diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..cd6a32d5e9e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.dm] +indent_style = tab +indent_size = 4 \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 3158997945b..c3b24c12791 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,10 +1,60 @@ -# dmm map merger hook -# needs additional setup, see tools/mapmerge/install.txt -*.dmm merge=merge-dmm eol=crlf - -# dmi icon merger hook -# needs additional setup, see tools/dmitool/merging.txt -*.dmi merge=merge-dmi - -# force changelog merging to use union -html/changelog.html merge=union +# dmm map merger hook +# needs additional setup, see tools/mapmerge/install.txt +*.dmm merge=merge-dmm eol=crlf + +# dmi icon merger hook +# needs additional setup, see tools/dmitool/merging.txt +*.dmi merge=merge-dmi + +# force changelog merging to use union +html/changelog.html merge=union + +# Enforce Line Endings +* text=auto + +*.ai binary +*.dmi binary +*.exe binary +*.fla binary +*.gif binary +*.jar binary +*.jpg binary +*.mid binary +*.ogg binary +*.pdb binary +*.png binary +*.psd binary +*.s3m binary +*.sav binary +*.so binary +*.svg binary +*.wav binary +*.xcf binary +*.xlsx binary + +*.awk text +*.bat text +*.conf text +*.cpp text +*.cs text +*.css text +*.dm text +*.dmf text +*.dmm text +*.gitkeep text +*.hook text +*.html text +*.java text +*.js text +*.json text +*.log text +*.map text +*.md text +*.php text +*.py text +*.scss text +*.sh text +*.sql text +*.tmpl text +*.txt text +*.vue text \ No newline at end of file diff --git a/.gitignore b/.gitignore index c0fb1080cdf..7daad5e4219 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,19 @@ -#ignore misc BYOND files -Thumbs.db -*.log -*.rsc -*.int -*.dmb -*.lk -*.backup -*.before -data/ -cfg/ -build_log.txt - -__pycache__/ -*.py[cod] -*$py.class -/.atom-build.json -.vscode/ -.vs/ +#ignore misc BYOND files +Thumbs.db +*.log +*.rsc +*.int +*.dmb +*.lk +*.backup +*.before +data/ +cfg/ +build_log.txt + +__pycache__/ +*.py[cod] +*$py.class +/.atom-build.json +.vscode/ +.vs/ diff --git a/.travis.yml b/.travis.yml index 2884d834c6b..99649c86fd7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -85,7 +85,7 @@ jobs: - (cd vueui && npm run build) - (num=`grep -E '\\\\(red|blue|green|black|b|i[^mc])' **/*.dm | wc -l`; echo "$num escapes (expecting ${MACRO_COUNT} or less)"; [ $num -le ${MACRO_COUNT} ]) - awk -f tools/indentation.awk **/*.dm - - md5sum -c - <<< "c3aa6d9bf053dba0a2f763ed314304e2 *html/changelogs/example.yml" + - md5sum -c - <<< "60a49502d9f6dfdcc255deb98c901b72 *html/changelogs/example.yml" - python tools/TagMatcher/tag-matcher.py ../.. - python tools/GenerateChangelog/ss13_genchangelog.py html/changelog.html html/changelogs --dry-run - python3 tools/mapmerge2/travis_mapcheck.py diff --git a/README.md b/README.md index 8811b08f895..5218aca790e 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,96 @@ -# Aurorastation - -**[Website](https://aurorastation.org/)** - -**[Code](https://github.com/Aurorastation/Aurora.3)** - -[![Krihelimeter](http://www.krihelinator.xyz/badge/Aurorastation/Aurora.3)](http://www.krihelinator.xyz/repositories/Aurorastation/Aurora.3) - -[![Build Status](https://api.travis-ci.org/Aurorastation/Aurora.3.svg?branch=master)](https://travis-ci.org/Aurorastation/Aurora.3) - ---- - -### LICENSE -Aurorastation is licensed under the GNU Affero General Public License version 3, which can be found in full in LICENSE. - -Commits 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 commits whose authorship dates are not prior to `1420675200 +0000` are 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 commits prior to `1420675200 +0000`, which are 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. - -### GETTING THE CODE -The simplest way to obtain the code is using the github .zip feature. - -Click [here](https://github.com/Aurorastation/Aurora.3/archive/master.zip) to get the latest stable 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/Aurorastation/Aurora.3.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 aurorastation.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 aurorastation.dmb (DEBUG mode) - - aurorastation.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 the server Discord if you're completely lost. - -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. "Game Master" 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 aurorastation.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. - -For more advanced setups, setting the server `tick_lag` in the config as well as configuring SQL are good first steps. - ---- - -### SQL Setup - -The SQL backend for the library and stats tracking requires a MySQL server, as does the optional SQL saves system. Your server details go in config/dbconfig.txt, and initial database setup is done with [flyway](https://flywaydb.org/). Detailed instructions can be found [here](https://github.com/Aurorastation/Aurora.3/tree/master/SQL). - ---- - -### Discord Bot - -The Aurorastation codebase uses a built-in Discord bot to interface with Discord. Some of its features rely on the MySQL database, specifically, channel storage and configuration. So a database is required for its operation. If that is present, then setup is relatively easy: simply set up the `config/discord.txt` file according to the example located in the `config/example/` folder, and populate the database with appropriate channel and server information. - -At present, there is no built in GUI for doing the latter. So direct database modification is required unless you set up the python companion bot. The python companion bot is BOREALIS II, and can be located [here](https://github.com/Aurorastation/BOREALISbot2). Though not required, it makes database modification easier. See commands that start with `channel_`. +# Aurorastation + +**[Website](https://aurorastation.org/)** + +**[Code](https://github.com/Aurorastation/Aurora.3)** + +[![Krihelimeter](http://www.krihelinator.xyz/badge/Aurorastation/Aurora.3)](http://www.krihelinator.xyz/repositories/Aurorastation/Aurora.3) + +[![Build Status](https://api.travis-ci.org/Aurorastation/Aurora.3.svg?branch=master)](https://travis-ci.org/Aurorastation/Aurora.3) + +--- + +### LICENSE +Aurorastation is licensed under the GNU Affero General Public License version 3, which can be found in full in LICENSE. + +Commits 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 commits whose authorship dates are not prior to `1420675200 +0000` are 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 commits prior to `1420675200 +0000`, which are 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. + +### GETTING THE CODE +The simplest way to obtain the code is using the github .zip feature. + +Click [here](https://github.com/Aurorastation/Aurora.3/archive/master.zip) to get the latest stable 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/Aurorastation/Aurora.3.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 aurorastation.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 aurorastation.dmb (DEBUG mode) + + aurorastation.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 the server Discord if you're completely lost. + +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. "Game Master" 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 aurorastation.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. + +For more advanced setups, setting the server `tick_lag` in the config as well as configuring SQL are good first steps. + +--- + +### SQL Setup + +The SQL backend for the library and stats tracking requires a MySQL server, as does the optional SQL saves system. Your server details go in config/dbconfig.txt, and initial database setup is done with [flyway](https://flywaydb.org/). Detailed instructions can be found [here](https://github.com/Aurorastation/Aurora.3/tree/master/SQL). + +--- + +### Discord Bot + +The Aurorastation codebase uses a built-in Discord bot to interface with Discord. Some of its features rely on the MySQL database, specifically, channel storage and configuration. So a database is required for its operation. If that is present, then setup is relatively easy: simply set up the `config/discord.txt` file according to the example located in the `config/example/` folder, and populate the database with appropriate channel and server information. + +At present, there is no built in GUI for doing the latter. So direct database modification is required unless you set up the python companion bot. The python companion bot is BOREALIS II, and can be located [here](https://github.com/Aurorastation/BOREALISbot2). Though not required, it makes database modification easier. See commands that start with `channel_`. diff --git a/aurorastation.dme b/aurorastation.dme index c26284b1e79..3da83e0ddf7 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -369,6 +369,7 @@ #include "code\datums\wires\vending.dm" #include "code\datums\wires\wires.dm" #include "code\defines\gases.dm" +#include "code\defines\mutable_appearance.dm" #include "code\defines\obj.dm" #include "code\defines\obj\weapon.dm" #include "code\defines\procs\announce.dm" @@ -748,6 +749,7 @@ #include "code\game\objects\items\shooting_range.dm" #include "code\game\objects\items\skrell.dm" #include "code\game\objects\items\spirit_board.dm" +#include "code\game\objects\items\tajara.dm" #include "code\game\objects\items\toys.dm" #include "code\game\objects\items\trash.dm" #include "code\game\objects\items\devices\aicard.dm" @@ -1297,6 +1299,7 @@ #include "code\modules\clothing\gloves\color.dm" #include "code\modules\clothing\gloves\miscellaneous.dm" #include "code\modules\clothing\gloves\stungloves.dm" +#include "code\modules\clothing\gloves\xeno\tajara.dm" #include "code\modules\clothing\head\collectable.dm" #include "code\modules\clothing\head\hardhat.dm" #include "code\modules\clothing\head\helmet.dm" @@ -1310,6 +1313,7 @@ #include "code\modules\clothing\masks\gasmask.dm" #include "code\modules\clothing\masks\miscellaneous.dm" #include "code\modules\clothing\masks\voice.dm" +#include "code\modules\clothing\masks\xeno\tajara.dm" #include "code\modules\clothing\rings\material.dm" #include "code\modules\clothing\rings\rings.dm" #include "code\modules\clothing\shoes\colour.dm" @@ -1318,6 +1322,7 @@ #include "code\modules\clothing\shoes\magboots.dm" #include "code\modules\clothing\shoes\miscellaneous.dm" #include "code\modules\clothing\shoes\vaurca.dm" +#include "code\modules\clothing\shoes\xeno\tajara.dm" #include "code\modules\clothing\spacesuits\alien.dm" #include "code\modules\clothing\spacesuits\breaches.dm" #include "code\modules\clothing\spacesuits\miscellaneous.dm" @@ -1360,6 +1365,7 @@ #include "code\modules\clothing\suits\storage.dm" #include "code\modules\clothing\suits\utility.dm" #include "code\modules\clothing\suits\wiz_robe.dm" +#include "code\modules\clothing\suits\xeno\tajara.dm" #include "code\modules\clothing\under\color.dm" #include "code\modules\clothing\under\miscellaneous.dm" #include "code\modules\clothing\under\shorts.dm" @@ -1372,6 +1378,7 @@ #include "code\modules\clothing\under\accessories\lockets.dm" #include "code\modules\clothing\under\accessories\shirts.dm" #include "code\modules\clothing\under\accessories\storage.dm" +#include "code\modules\clothing\under\accessories\xeno\tajara.dm" #include "code\modules\clothing\under\jobs\civilian.dm" #include "code\modules\clothing\under\jobs\engineering.dm" #include "code\modules\clothing\under\jobs\medsci.dm" @@ -1399,6 +1406,7 @@ #include "code\modules\detectivework\tools\luminol.dm" #include "code\modules\detectivework\tools\rag.dm" #include "code\modules\detectivework\tools\sample_kits.dm" +#include "code\modules\detectivework\tools\scene_cards.dm" #include "code\modules\detectivework\tools\storage.dm" #include "code\modules\detectivework\tools\swabs.dm" #include "code\modules\detectivework\tools\uvlight.dm" @@ -1500,7 +1508,7 @@ #include "code\modules\heavy_vehicle\mech_wreckage.dm" #include "code\modules\heavy_vehicle\mecha.dm" #include "code\modules\heavy_vehicle\components\_components.dm" -#include "code\modules\heavy_vehicle\components\armour.dm" +#include "code\modules\heavy_vehicle\components\armor.dm" #include "code\modules\heavy_vehicle\components\arms.dm" #include "code\modules\heavy_vehicle\components\body.dm" #include "code\modules\heavy_vehicle\components\frame.dm" @@ -1716,7 +1724,6 @@ #include "code\modules\mob\living\autohiss.dm" #include "code\modules\mob\living\damage_procs.dm" #include "code\modules\mob\living\default_language.dm" -#include "code\modules\mob\living\devour.dm" #include "code\modules\mob\living\life.dm" #include "code\modules\mob\living\living.dm" #include "code\modules\mob\living\living_defense.dm" diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index d32b433db5e..72b3d0135b1 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -1,140 +1,140 @@ -/* -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 = 1 - 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 - - layer = 2.4 //under wires with their 2.44 - - var/connect_types = CONNECT_TYPE_REGULAR - var/icon_connect_type = "" //"-supply" or "-scrubbers" - - 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 - gfi_layer_rotation = GFI_ROTATION_OVERDIR - -/obj/machinery/atmospherics/Initialize(mapload) - . = ..() - if(!icon_manager) - icon_manager = new() - - if(!pipe_color) - pipe_color = color - color = null - - if(!pipe_color_check(pipe_color)) - pipe_color = null - - if (mapload) - return INITIALIZE_HINT_LATELOAD - -/obj/machinery/atmospherics/Destroy() - ..() - return QDEL_HINT_HARDDEL // fuck it - -/obj/machinery/atmospherics/proc/atmos_init() - -// atmos_init() and Initialize() must be separate, as atmos_init() can be called multiple times after the machine has been initialized. - -/obj/machinery/atmospherics/LateInitialize() - atmos_init() - -/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_connect_types(obj/machinery/atmospherics/atmos1, obj/machinery/atmospherics/atmos2) - return (atmos1.connect_types & atmos2.connect_types) - -/obj/machinery/atmospherics/proc/check_connect_types_construction(obj/machinery/atmospherics/atmos1, obj/item/pipe/pipe2) - return (atmos1.connect_types & pipe2.connect_types) - -/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/machinery_process() - last_flow_rate = 0 - last_power_draw = 0 - -/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/remove_network(datum/pipe_network/network) - reassign_network(network, null) - -/obj/machinery/atmospherics/proc/return_network_air(datum/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 +/* +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 = 1 + 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 + + layer = 2.4 //under wires with their 2.44 + + var/connect_types = CONNECT_TYPE_REGULAR + var/icon_connect_type = "" //"-supply" or "-scrubbers" + + 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 + gfi_layer_rotation = GFI_ROTATION_OVERDIR + +/obj/machinery/atmospherics/Initialize(mapload) + . = ..() + if(!icon_manager) + icon_manager = new() + + if(!pipe_color) + pipe_color = color + color = null + + if(!pipe_color_check(pipe_color)) + pipe_color = null + + if (mapload) + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/atmospherics/Destroy() + ..() + return QDEL_HINT_HARDDEL // fuck it + +/obj/machinery/atmospherics/proc/atmos_init() + +// atmos_init() and Initialize() must be separate, as atmos_init() can be called multiple times after the machine has been initialized. + +/obj/machinery/atmospherics/LateInitialize() + atmos_init() + +/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_connect_types(obj/machinery/atmospherics/atmos1, obj/machinery/atmospherics/atmos2) + return (atmos1.connect_types & atmos2.connect_types) + +/obj/machinery/atmospherics/proc/check_connect_types_construction(obj/machinery/atmospherics/atmos1, obj/item/pipe/pipe2) + return (atmos1.connect_types & pipe2.connect_types) + +/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/machinery_process() + last_flow_rate = 0 + last_power_draw = 0 + +/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/remove_network(datum/pipe_network/network) + reassign_network(network, null) + +/obj/machinery/atmospherics/proc/return_network_air(datum/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 diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm index 5a1486de568..8528d636be2 100644 --- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm +++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm @@ -1,134 +1,134 @@ -obj/machinery/atmospherics/binary - dir = SOUTH - initialize_directions = SOUTH|NORTH - use_power = 1 - - var/datum/gas_mixture/air1 - var/datum/gas_mixture/air2 - - var/datum/pipe_network/network1 - var/datum/pipe_network/network2 - - Initialize() - 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 - air1 = new - air2 = new - - air1.volume = 200 - air2.volume = 200 - . = ..() - -// Housekeeping and pipe network stuff below - 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 - - Destroy() - QDEL_NULL(air1) - QDEL_NULL(air2) - - if(node1) - node1.disconnect(src) - QDEL_NULL(network1) - if(node2) - node2.disconnect(src) - QDEL_NULL(network2) - - node1 = null - node2 = null - - return ..() - - atmos_init() - if(node1 && node2) return - - var/node2_connect = dir - var/node1_connect = turn(dir, 180) - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node2 = target - break - - update_icon() - update_underlays() - - 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) - - - return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node1) - return network1 - - if(reference==node2) - return network2 - - return null - - 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 - - return_network_air(datum/pipe_network/reference) - var/list/results = list() - - if(network1 == reference) - results += air1 - if(network2 == reference) - results += air2 - - return results - - 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 +obj/machinery/atmospherics/binary + dir = SOUTH + initialize_directions = SOUTH|NORTH + use_power = 1 + + var/datum/gas_mixture/air1 + var/datum/gas_mixture/air2 + + var/datum/pipe_network/network1 + var/datum/pipe_network/network2 + + Initialize() + 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 + air1 = new + air2 = new + + air1.volume = 200 + air2.volume = 200 + . = ..() + +// Housekeeping and pipe network stuff below + 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 + + Destroy() + QDEL_NULL(air1) + QDEL_NULL(air2) + + if(node1) + node1.disconnect(src) + QDEL_NULL(network1) + if(node2) + node2.disconnect(src) + QDEL_NULL(network2) + + node1 = null + node2 = null + + return ..() + + atmos_init() + if(node1 && node2) return + + var/node2_connect = dir + var/node1_connect = turn(dir, 180) + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node2 = target + break + + update_icon() + update_underlays() + + 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) + + + return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node1) + return network1 + + if(reference==node2) + return network2 + + return null + + 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 + + return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(network1 == reference) + results += air1 + if(network2 == reference) + results += air2 + + return results + + 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 diff --git a/code/ATMOSPHERICS/components/binary_devices/circulator.dm b/code/ATMOSPHERICS/components/binary_devices/circulator.dm index 80679f73d0e..e90cc0deff7 100644 --- a/code/ATMOSPHERICS/components/binary_devices/circulator.dm +++ b/code/ATMOSPHERICS/components/binary_devices/circulator.dm @@ -1,145 +1,145 @@ -//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/pipes.dmi' - icon_state = "circ-off" - anchored = 0 - - 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 - - density = 1 - -/obj/machinery/atmospherics/binary/circulator/Initialize() - . = ..() - 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/machinery_process() - ..() - - if(last_worldtime_transfer < world.time - 50) - recent_moles_transferred = 0 - update_icon() - -/obj/machinery/atmospherics/binary/circulator/update_icon() - if(stat & (BROKEN|NOPOWER) || !anchored) - icon_state = "circ-p" - else if(last_pressure_delta > 0 && recent_moles_transferred > 0) - if(last_pressure_delta > 5*ONE_ATMOSPHERE) - icon_state = "circ-run" - else - icon_state = "circ-slow" - else - icon_state = "circ-off" - - return 1 - -/obj/machinery/atmospherics/binary/circulator/attackby(obj/item/W as obj, mob/user as mob) - if (W.iswrench()) - playsound(src.loc, 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) - 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 category = "Object" - set name = "Rotate Circulator (Clockwise)" - 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)]." - - -/obj/machinery/atmospherics/binary/circulator/verb/rotate_anticlockwise() - set category = "Object" - set name = "Rotate Circulator (Counterclockwise)" - 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)]." +//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/pipes.dmi' + icon_state = "circ-off" + anchored = 0 + + 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 + + density = 1 + +/obj/machinery/atmospherics/binary/circulator/Initialize() + . = ..() + 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/machinery_process() + ..() + + if(last_worldtime_transfer < world.time - 50) + recent_moles_transferred = 0 + update_icon() + +/obj/machinery/atmospherics/binary/circulator/update_icon() + if(stat & (BROKEN|NOPOWER) || !anchored) + icon_state = "circ-p" + else if(last_pressure_delta > 0 && recent_moles_transferred > 0) + if(last_pressure_delta > 5*ONE_ATMOSPHERE) + icon_state = "circ-run" + else + icon_state = "circ-slow" + else + icon_state = "circ-off" + + return 1 + +/obj/machinery/atmospherics/binary/circulator/attackby(obj/item/W as obj, mob/user as mob) + if (W.iswrench()) + playsound(src.loc, 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) + 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 category = "Object" + set name = "Rotate Circulator (Clockwise)" + 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)]." + + +/obj/machinery/atmospherics/binary/circulator/verb/rotate_anticlockwise() + set category = "Object" + set name = "Rotate Circulator (Counterclockwise)" + 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)]." diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm index 5c021e2bddf..a549eeb0160 100644 --- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm @@ -1,265 +1,265 @@ -#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 = 0 - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 7500 //7500 W ~ 10 HP - - 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/Initialize() - . = ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP - icon = null - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume - name = "Large Dual Port Air Vent" - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/Initialize() - . = ..() - 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 - - var/istate = "" - - 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)) - istate += "h" - - if(!powered()) - istate += "off" - else - istate += "[use_power ? "[pump_direction ? "out" : "in"]" : "off"]" - - icon_state = istate - -/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/machinery_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) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, 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 = 1 //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, filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/dp_vent_pump/atmos_init() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/dp_vent_pump/examine(mob/user) - if(..(user, 1)) - to_chat(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"]) - use_power = text2num(signal.data["power"]) - - if(signal.data["power_toggle"]) - 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"]) - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - return //do not update_icon - - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - 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 = 0 + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 7500 //7500 W ~ 10 HP + + 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/Initialize() + . = ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + icon = null + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume + name = "Large Dual Port Air Vent" + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/Initialize() + . = ..() + 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 + + var/istate = "" + + 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)) + istate += "h" + + if(!powered()) + istate += "off" + else + istate += "[use_power ? "[pump_direction ? "out" : "in"]" : "off"]" + + icon_state = istate + +/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/machinery_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) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, 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 = 1 //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, filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/dp_vent_pump/atmos_init() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/dp_vent_pump/examine(mob/user) + if(..(user, 1)) + to_chat(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"]) + use_power = text2num(signal.data["power"]) + + if(signal.data["power_toggle"]) + 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"]) + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + return //do not update_icon + + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + 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 85803f37752..d0a2e940c23 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -1,261 +1,261 @@ -#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" - 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 = 0 - interact_offline = 1 - 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/Initialize() - . = ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 - -/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/machinery_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 - - //-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)) - - //pump_gas() will return a negative number if no flow occurred - 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) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, 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 = 1 //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, filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/passive_gate/atmos_init() - ..() - 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) - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - return //do not update_icon - - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - update_icon() - return - -/obj/machinery/atmospherics/binary/passive_gate/attack_hand(user as mob) - if(..()) - return - src.add_fingerprint(usr) - if(!src.allowed(user)) - to_chat(user, "Access denied.") - return - usr.set_machine(src) - ui_interact(user) - return - -/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(stat & (BROKEN|NOPOWER)) - return - - // 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) - ) - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "pressure_regulator.tmpl", name, 470, 370) - ui.set_initial_data(data) // when the ui is first opened this is the data it will use - ui.open() // open the new ui window - ui.set_auto_update(1) // auto update every Master Controller tick - - -/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list) - if(..()) return 1 - - if(href_list["toggle_valve"]) - unlocked = !unlocked - - if(href_list["regulate_mode"]) - switch(href_list["regulate_mode"]) - if ("off") regulate_mode = REGULATE_NONE - if ("input") regulate_mode = REGULATE_INPUT - if ("output") regulate_mode = REGULATE_OUTPUT - - switch(href_list["set_press"]) - if ("min") - target_pressure = 0 - if ("max") - target_pressure = max_pressure_setting - if ("set") - var/new_pressure = input(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure Control",src.target_pressure) as num - src.target_pressure = between(0, new_pressure, max_pressure_setting) - - switch(href_list["set_flow_rate"]) - if ("min") - set_flow_rate = 0 - if ("max") - set_flow_rate = air1.volume - if ("set") - var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[air1.volume]kPa)","Flow Rate Control",src.set_flow_rate) as num - src.set_flow_rate = between(0, new_flow_rate, air1.volume) - - usr.set_machine(src) //Is this even needed with NanoUI? - src.update_icon() - src.add_fingerprint(usr) - return - -/obj/machinery/atmospherics/binary/passive_gate/attackby(var/obj/item/W as obj, var/mob/user as mob) - if (!W.iswrench()) - return ..() - if (unlocked) - to_chat(user, "You cannot unwrench \the [src], turn it off first.") - return 1 - 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) - to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40/W.toolspeed, act_target = src)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - new /obj/item/pipe(loc, make_from=src) - qdel(src) - -#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" + 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 = 0 + interact_offline = 1 + 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/Initialize() + . = ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP * 2.5 + +/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/machinery_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 + + //-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)) + + //pump_gas() will return a negative number if no flow occurred + 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) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, 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 = 1 //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, filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/passive_gate/atmos_init() + ..() + 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) + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + return //do not update_icon + + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + update_icon() + return + +/obj/machinery/atmospherics/binary/passive_gate/attack_hand(user as mob) + if(..()) + return + src.add_fingerprint(usr) + if(!src.allowed(user)) + to_chat(user, "Access denied.") + return + usr.set_machine(src) + ui_interact(user) + return + +/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(stat & (BROKEN|NOPOWER)) + return + + // 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) + ) + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "pressure_regulator.tmpl", name, 470, 370) + ui.set_initial_data(data) // when the ui is first opened this is the data it will use + ui.open() // open the new ui window + ui.set_auto_update(1) // auto update every Master Controller tick + + +/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list) + if(..()) return 1 + + if(href_list["toggle_valve"]) + unlocked = !unlocked + + if(href_list["regulate_mode"]) + switch(href_list["regulate_mode"]) + if ("off") regulate_mode = REGULATE_NONE + if ("input") regulate_mode = REGULATE_INPUT + if ("output") regulate_mode = REGULATE_OUTPUT + + switch(href_list["set_press"]) + if ("min") + target_pressure = 0 + if ("max") + target_pressure = max_pressure_setting + if ("set") + var/new_pressure = input(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure Control",src.target_pressure) as num + src.target_pressure = between(0, new_pressure, max_pressure_setting) + + switch(href_list["set_flow_rate"]) + if ("min") + set_flow_rate = 0 + if ("max") + set_flow_rate = air1.volume + if ("set") + var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[air1.volume]kPa)","Flow Rate Control",src.set_flow_rate) as num + src.set_flow_rate = between(0, new_flow_rate, air1.volume) + + usr.set_machine(src) //Is this even needed with NanoUI? + src.update_icon() + src.add_fingerprint(usr) + return + +/obj/machinery/atmospherics/binary/passive_gate/attackby(var/obj/item/W as obj, var/mob/user as mob) + if (!W.iswrench()) + return ..() + if (unlocked) + to_chat(user, "You cannot unwrench \the [src], turn it off first.") + return 1 + 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) + to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40/W.toolspeed, act_target = src)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + new /obj/item/pipe(loc, make_from=src) + qdel(src) + +#undef REGULATE_NONE +#undef REGULATE_INPUT +#undef REGULATE_OUTPUT diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index ab3626dd3f3..c90b6281fd0 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -1,238 +1,238 @@ -/* -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" - level = 1 - - name = "gas pump" - desc = "A pump" - - var/target_pressure = ONE_ATMOSPHERE - - //var/max_volume_transfer = 10000 - - use_power = 0 - 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/Initialize() - . = ..() - air1.volume = ATMOS_DEFAULT_VOLUME_PUMP - air2.volume = ATMOS_DEFAULT_VOLUME_PUMP - -/obj/machinery/atmospherics/binary/pump/on - icon_state = "map_on" - use_power = 1 - - -/obj/machinery/atmospherics/binary/pump/update_icon() - if(!powered()) - icon_state = "off" - else - icon_state = "[use_power ? "on" : "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)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/pump/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/binary/pump/machinery_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) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //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, filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(stat & (BROKEN|NOPOWER)) - return - - // 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 - ) - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "gas_pump.tmpl", name, 470, 290) - ui.set_initial_data(data) // when the ui is first opened this is the data it will use - ui.open() // open the new ui window - ui.set_auto_update(1) // auto update every Master Controller tick - -/obj/machinery/atmospherics/binary/pump/atmos_init() - ..() - 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"])) - use_power = 1 - else - use_power = 0 - - if("power_toggle" in signal.data) - 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"]) - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - return //do not update_icon - - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - update_icon() - return - -/obj/machinery/atmospherics/binary/pump/attack_hand(user as mob) - if(..()) - return - src.add_fingerprint(usr) - if(!src.allowed(user)) - to_chat(user, "Access denied.") - return - usr.set_machine(src) - ui_interact(user) - return - -/obj/machinery/atmospherics/binary/pump/Topic(href,href_list) - if(..()) return 1 - - if(href_list["power"]) - use_power = !use_power - - switch(href_list["set_press"]) - if ("min") - target_pressure = 0 - if ("max") - target_pressure = max_pressure_setting - if ("set") - var/new_pressure = input(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure control",src.target_pressure) as num - src.target_pressure = between(0, new_pressure, max_pressure_setting) - - usr.set_machine(src) - src.add_fingerprint(usr) - - src.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/W as obj, var/mob/user as mob) - if (!W.iswrench() && !istype(W, /obj/item/pipewrench)) - return ..() - if (!(stat & NOPOWER) && use_power) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - 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 && !istype(W, /obj/item/pipewrench)) - to_chat(user, "You cannot unwrench this [src], it's too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - else - to_chat(user, "You struggle to unwrench \the [src] with your pipe wrench.") - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, istype(W, /obj/item/pipewrench) ? 80/W.toolspeed : 40/W.toolspeed, act_target = src)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - new /obj/item/pipe(loc, make_from=src) - qdel(src) +/* +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" + level = 1 + + name = "gas pump" + desc = "A pump" + + var/target_pressure = ONE_ATMOSPHERE + + //var/max_volume_transfer = 10000 + + use_power = 0 + 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/Initialize() + . = ..() + air1.volume = ATMOS_DEFAULT_VOLUME_PUMP + air2.volume = ATMOS_DEFAULT_VOLUME_PUMP + +/obj/machinery/atmospherics/binary/pump/on + icon_state = "map_on" + use_power = 1 + + +/obj/machinery/atmospherics/binary/pump/update_icon() + if(!powered()) + icon_state = "off" + else + icon_state = "[use_power ? "on" : "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)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/pump/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/binary/pump/machinery_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) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //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, filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(stat & (BROKEN|NOPOWER)) + return + + // 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 + ) + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "gas_pump.tmpl", name, 470, 290) + ui.set_initial_data(data) // when the ui is first opened this is the data it will use + ui.open() // open the new ui window + ui.set_auto_update(1) // auto update every Master Controller tick + +/obj/machinery/atmospherics/binary/pump/atmos_init() + ..() + 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"])) + use_power = 1 + else + use_power = 0 + + if("power_toggle" in signal.data) + 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"]) + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + return //do not update_icon + + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + update_icon() + return + +/obj/machinery/atmospherics/binary/pump/attack_hand(user as mob) + if(..()) + return + src.add_fingerprint(usr) + if(!src.allowed(user)) + to_chat(user, "Access denied.") + return + usr.set_machine(src) + ui_interact(user) + return + +/obj/machinery/atmospherics/binary/pump/Topic(href,href_list) + if(..()) return 1 + + if(href_list["power"]) + use_power = !use_power + + switch(href_list["set_press"]) + if ("min") + target_pressure = 0 + if ("max") + target_pressure = max_pressure_setting + if ("set") + var/new_pressure = input(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure control",src.target_pressure) as num + src.target_pressure = between(0, new_pressure, max_pressure_setting) + + usr.set_machine(src) + src.add_fingerprint(usr) + + src.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/W as obj, var/mob/user as mob) + if (!W.iswrench() && !istype(W, /obj/item/pipewrench)) + return ..() + if (!(stat & NOPOWER) && use_power) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + 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 && !istype(W, /obj/item/pipewrench)) + to_chat(user, "You cannot unwrench this [src], it's too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + else + to_chat(user, "You struggle to unwrench \the [src] with your pipe wrench.") + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, istype(W, /obj/item/pipewrench) ? 80/W.toolspeed : 40/W.toolspeed, act_target = src)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + new /obj/item/pipe(loc, make_from=src) + qdel(src) diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index 42b02282cc2..fff0c747c6f 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -1,19 +1,19 @@ -/obj/machinery/atmospherics/binary/pump/high_power - icon = 'icons/atmos/volume_pump.dmi' - icon_state = "map_off" - level = 1 - - name = "high power gas pump" - desc = "A pump. 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 = 1 - 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" + level = 1 + + name = "high power gas pump" + desc = "A pump. 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 = 1 + 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/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm index b9201e5f124..8f848cc77ae 100644 --- a/code/ATMOSPHERICS/components/portables_connector.dm +++ b/code/ATMOSPHERICS/components/portables_connector.dm @@ -1,155 +1,155 @@ -/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 - - var/obj/machinery/portable_atmospherics/connected_device - - var/obj/machinery/atmospherics/node - - var/datum/pipe_network/network - - var/on = 0 - use_power = 0 - level = 1 - - -/obj/machinery/atmospherics/portables_connector/Initialize() - 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) - -/obj/machinery/atmospherics/portables_connector/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/portables_connector/machinery_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/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() - loc = null - - if(connected_device) - connected_device.disconnect() - - if(node) - node.disconnect(src) - qdel(network) - - node = null - - return ..() - -/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(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - 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/W as obj, var/mob/user as mob) - if (!W.iswrench()) - 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 - 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) - to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40/W.toolspeed, act_target = src)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - new /obj/item/pipe(loc, make_from=src) - qdel(src) +/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 + + var/obj/machinery/portable_atmospherics/connected_device + + var/obj/machinery/atmospherics/node + + var/datum/pipe_network/network + + var/on = 0 + use_power = 0 + level = 1 + + +/obj/machinery/atmospherics/portables_connector/Initialize() + 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) + +/obj/machinery/atmospherics/portables_connector/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/portables_connector/machinery_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/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() + loc = null + + if(connected_device) + connected_device.disconnect() + + if(node) + node.disconnect(src) + qdel(network) + + node = null + + return ..() + +/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(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + 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/W as obj, var/mob/user as mob) + if (!W.iswrench()) + 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 + 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) + to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40/W.toolspeed, act_target = src)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + new /obj/item/pipe(loc, make_from=src) + qdel(src) diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index 2e91df67ada..df1abd0d212 100755 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -1,277 +1,277 @@ -/obj/machinery/atmospherics/trinary/filter - icon = 'icons/atmos/filter.dmi' - icon_state = "map" - density = 0 - level = 1 - - name = "Gas filter" - - use_power = 1 - 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: 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/filter/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/atmospherics/trinary/filter/Initialize() - . = ..() - 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("sleeping_agent") - - air1.volume = ATMOS_DEFAULT_VOLUME_FILTER - air2.volume = ATMOS_DEFAULT_VOLUME_FILTER - air3.volume = ATMOS_DEFAULT_VOLUME_FILTER - -/obj/machinery/atmospherics/trinary/filter/update_icon() - if(istype(src, /obj/machinery/atmospherics/trinary/filter/m_filter)) - 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" - use_power = 0 - -/obj/machinery/atmospherics/trinary/filter/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - - add_underlay(T, node1, turn(dir, -180)) - - if(istype(src, /obj/machinery/atmospherics/trinary/filter/m_filter)) - add_underlay(T, node2, turn(dir, 90)) - else - add_underlay(T, node2, turn(dir, -90)) - - add_underlay(T, node3, dir) - -/obj/machinery/atmospherics/trinary/filter/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/trinary/filter/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/trinary/filter/machinery_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/filter/atmos_init() - set_frequency(frequency) - ..() - -/obj/machinery/atmospherics/trinary/filter/attackby(var/obj/item/W as obj, var/mob/user as mob) - if (!W.iswrench()) - return ..() - 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) - to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40/W.toolspeed, act_target = src)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - new /obj/item/pipe(loc, make_from=src) - qdel(src) - - -/obj/machinery/atmospherics/trinary/filter/attack_hand(user as mob) // -- TLE - if(..()) - return - - if(!src.allowed(user)) - to_chat(user, "Access denied.") - return - - 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=atmo_filter") - onclose(user, "atmo_filter") - return - -/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE - if(..()) - return 1 - usr.set_machine(src) - src.add_fingerprint(usr) - if(href_list["filterset"]) - filter_type = text2num(href_list["filterset"]) - - filtered_out.Cut() //no need to create new lists unnecessarily - switch(filter_type) - if(0) //removing hydrocarbons - filtered_out += "phoron" - 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 += "sleeping_agent" - - if (href_list["temp"]) - src.temp = null - if(href_list["set_flow_rate"]) - var/new_flow_rate = input(usr,"Enter new flow rate (0-[air1.volume]L/s)","Flow Rate Control",src.set_flow_rate) as num - src.set_flow_rate = max(0, min(air1.volume, new_flow_rate)) - if(href_list["power"]) - use_power=!use_power - src.update_icon() - src.updateUsrDialog() -/* - for(var/mob/M in viewers(1, src)) - if ((M.client && M.machine == src)) - src.attack_hand(M) -*/ - return - -/obj/machinery/atmospherics/trinary/filter/m_filter - icon_state = "mmap" - - dir = SOUTH - initialize_directions = SOUTH|NORTH|EAST - -obj/machinery/atmospherics/trinary/filter/m_filter/Initialize() - switch(dir) - if(NORTH) - initialize_directions = WEST|NORTH|SOUTH - if(SOUTH) - initialize_directions = SOUTH|EAST|NORTH - if(EAST) - initialize_directions = EAST|WEST|NORTH - if(WEST) - initialize_directions = WEST|SOUTH|EAST - . = ..() - -/obj/machinery/atmospherics/trinary/filter/m_filter/atmos_init() - set_frequency(frequency) - - if(node1 && node2 && node3) return - - var/node1_connect = turn(dir, -180) - var/node2_connect = turn(dir, 90) - var/node3_connect = dir - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) - if(target.initialize_directions & get_dir(target,src)) - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) - if(target.initialize_directions & get_dir(target,src)) - node2 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) - if(target.initialize_directions & get_dir(target,src)) - node3 = target - break - - update_icon() - update_underlays() +/obj/machinery/atmospherics/trinary/filter + icon = 'icons/atmos/filter.dmi' + icon_state = "map" + density = 0 + level = 1 + + name = "Gas filter" + + use_power = 1 + 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: 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/filter/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/atmospherics/trinary/filter/Initialize() + . = ..() + 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("sleeping_agent") + + air1.volume = ATMOS_DEFAULT_VOLUME_FILTER + air2.volume = ATMOS_DEFAULT_VOLUME_FILTER + air3.volume = ATMOS_DEFAULT_VOLUME_FILTER + +/obj/machinery/atmospherics/trinary/filter/update_icon() + if(istype(src, /obj/machinery/atmospherics/trinary/filter/m_filter)) + 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" + use_power = 0 + +/obj/machinery/atmospherics/trinary/filter/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + + add_underlay(T, node1, turn(dir, -180)) + + if(istype(src, /obj/machinery/atmospherics/trinary/filter/m_filter)) + add_underlay(T, node2, turn(dir, 90)) + else + add_underlay(T, node2, turn(dir, -90)) + + add_underlay(T, node3, dir) + +/obj/machinery/atmospherics/trinary/filter/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/trinary/filter/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/trinary/filter/machinery_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/filter/atmos_init() + set_frequency(frequency) + ..() + +/obj/machinery/atmospherics/trinary/filter/attackby(var/obj/item/W as obj, var/mob/user as mob) + if (!W.iswrench()) + return ..() + 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) + to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40/W.toolspeed, act_target = src)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + new /obj/item/pipe(loc, make_from=src) + qdel(src) + + +/obj/machinery/atmospherics/trinary/filter/attack_hand(user as mob) // -- TLE + if(..()) + return + + if(!src.allowed(user)) + to_chat(user, "Access denied.") + return + + 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=atmo_filter") + onclose(user, "atmo_filter") + return + +/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE + if(..()) + return 1 + usr.set_machine(src) + src.add_fingerprint(usr) + if(href_list["filterset"]) + filter_type = text2num(href_list["filterset"]) + + filtered_out.Cut() //no need to create new lists unnecessarily + switch(filter_type) + if(0) //removing hydrocarbons + filtered_out += "phoron" + 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 += "sleeping_agent" + + if (href_list["temp"]) + src.temp = null + if(href_list["set_flow_rate"]) + var/new_flow_rate = input(usr,"Enter new flow rate (0-[air1.volume]L/s)","Flow Rate Control",src.set_flow_rate) as num + src.set_flow_rate = max(0, min(air1.volume, new_flow_rate)) + if(href_list["power"]) + use_power=!use_power + src.update_icon() + src.updateUsrDialog() +/* + for(var/mob/M in viewers(1, src)) + if ((M.client && M.machine == src)) + src.attack_hand(M) +*/ + return + +/obj/machinery/atmospherics/trinary/filter/m_filter + icon_state = "mmap" + + dir = SOUTH + initialize_directions = SOUTH|NORTH|EAST + +obj/machinery/atmospherics/trinary/filter/m_filter/Initialize() + switch(dir) + if(NORTH) + initialize_directions = WEST|NORTH|SOUTH + if(SOUTH) + initialize_directions = SOUTH|EAST|NORTH + if(EAST) + initialize_directions = EAST|WEST|NORTH + if(WEST) + initialize_directions = WEST|SOUTH|EAST + . = ..() + +/obj/machinery/atmospherics/trinary/filter/m_filter/atmos_init() + set_frequency(frequency) + + if(node1 && node2 && node3) return + + var/node1_connect = turn(dir, -180) + var/node2_connect = turn(dir, 90) + var/node3_connect = dir + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + node2 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) + if(target.initialize_directions & get_dir(target,src)) + node3 = target + break + + update_icon() + update_underlays() diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm index 095ae90763f..350eb8a0a05 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm @@ -1,276 +1,276 @@ -/obj/machinery/atmospherics/trinary/mixer - icon = 'icons/atmos/mixer.dmi' - icon_state = "map" - density = 0 - level = 1 - - name = "Gas mixer" - - use_power = 1 - 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(istype(src, /obj/machinery/atmospherics/trinary/mixer/m_mixer)) - icon_state = "m" - else if(istype(src, /obj/machinery/atmospherics/trinary/mixer/t_mixer)) - icon_state = "t" - else - icon_state = "" - - if(!powered()) - icon_state += "off" - else if(node2 && node3 && node1) - icon_state += use_power ? "on" : "off" - else - icon_state += "off" - use_power = 0 - -/obj/machinery/atmospherics/trinary/mixer/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - - if(istype(src, /obj/machinery/atmospherics/trinary/mixer/t_mixer)) - add_underlay(T, node1, turn(dir, -90)) - else - add_underlay(T, node1, turn(dir, -180)) - - if(istype(src, /obj/machinery/atmospherics/trinary/mixer/m_mixer) || istype(src, /obj/machinery/atmospherics/trinary/mixer/t_mixer)) - add_underlay(T, node2, turn(dir, 90)) - else - add_underlay(T, node2, turn(dir, -90)) - - add_underlay(T, node3, dir) - -/obj/machinery/atmospherics/trinary/mixer/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/trinary/mixer/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/trinary/mixer/Initialize() - . = ..() - 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/machinery_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/attackby(var/obj/item/W as obj, var/mob/user as mob) - if (!W.iswrench()) - return ..() - 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) - to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40/W.toolspeed, act_target = src)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - new /obj/item/pipe(loc, make_from=src) - qdel(src) - -/obj/machinery/atmospherics/trinary/mixer/attack_hand(user as mob) - if(..()) - return - src.add_fingerprint(usr) - if(!src.allowed(user)) - to_chat(user, "Access denied.") - return - usr.set_machine(src) - 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 Concentration: - - - - - [mixing_inputs[air1]]([mixing_inputs[air1]*100]%) - + - + -
- Node 2 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/Topic(href,href_list) - if(..()) return 1 - if(href_list["power"]) - use_power = !use_power - if(href_list["set_press"]) - var/max_flow_rate = min(air1.volume, air2.volume) - var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[max_flow_rate]L/s)","Flow Rate Control",src.set_flow_rate) as num - src.set_flow_rate = max(0, min(max_flow_rate, new_flow_rate)) - if(href_list["node1_c"]) - var/value = text2num(href_list["node1_c"]) - src.mixing_inputs[air1] = max(0, min(1, src.mixing_inputs[air1] + value)) - src.mixing_inputs[air2] = 1.0 - mixing_inputs[air1] - if(href_list["node2_c"]) - var/value = text2num(href_list["node2_c"]) - src.mixing_inputs[air2] = max(0, min(1, src.mixing_inputs[air2] + value)) - src.mixing_inputs[air1] = 1.0 - mixing_inputs[air2] - src.update_icon() - src.updateUsrDialog() - return - -obj/machinery/atmospherics/trinary/mixer/t_mixer - icon_state = "tmap" - - dir = SOUTH - initialize_directions = SOUTH|EAST|WEST - - //node 3 is the outlet, nodes 1 & 2 are intakes - -obj/machinery/atmospherics/trinary/mixer/t_mixer/north - icon_state = "tmap" - - dir = NORTH - initialize_directions = NORTH|EAST|WEST - - //node 3 is the outlet, nodes 1 & 2 are intakes - -obj/machinery/atmospherics/trinary/mixer/t_mixer/Initialize() - switch(dir) - if(NORTH) - initialize_directions = EAST|NORTH|WEST - if(SOUTH) - initialize_directions = SOUTH|WEST|EAST - if(EAST) - initialize_directions = EAST|NORTH|SOUTH - if(WEST) - initialize_directions = WEST|NORTH|SOUTH - . = ..() - -obj/machinery/atmospherics/trinary/mixer/t_mixer/atmos_init() - ..() - if(node1 && node2 && node3) return - - var/node1_connect = turn(dir, -90) - var/node2_connect = turn(dir, 90) - var/node3_connect = dir - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) - if(target.initialize_directions & get_dir(target,src)) - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) - if(target.initialize_directions & get_dir(target,src)) - node2 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) - if(target.initialize_directions & get_dir(target,src)) - node3 = target - break - - update_icon() - update_underlays() - -obj/machinery/atmospherics/trinary/mixer/m_mixer - icon_state = "mmap" - - dir = SOUTH - initialize_directions = SOUTH|NORTH|EAST - - //node 3 is the outlet, nodes 1 & 2 are intakes - -obj/machinery/atmospherics/trinary/mixer/m_mixer/Initialize() - switch(dir) - if(NORTH) - initialize_directions = WEST|NORTH|SOUTH - if(SOUTH) - initialize_directions = SOUTH|EAST|NORTH - if(EAST) - initialize_directions = EAST|WEST|NORTH - if(WEST) - initialize_directions = WEST|SOUTH|EAST - . = ..() - -obj/machinery/atmospherics/trinary/mixer/m_mixer/atmos_init() - ..() - if(node1 && node2 && node3) return - - var/node1_connect = turn(dir, -180) - var/node2_connect = turn(dir, 90) - var/node3_connect = dir - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) - if(target.initialize_directions & get_dir(target,src)) - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) - if(target.initialize_directions & get_dir(target,src)) - node2 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) - if(target.initialize_directions & get_dir(target,src)) - node3 = target - break - - update_icon() - update_underlays() +/obj/machinery/atmospherics/trinary/mixer + icon = 'icons/atmos/mixer.dmi' + icon_state = "map" + density = 0 + level = 1 + + name = "Gas mixer" + + use_power = 1 + 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(istype(src, /obj/machinery/atmospherics/trinary/mixer/m_mixer)) + icon_state = "m" + else if(istype(src, /obj/machinery/atmospherics/trinary/mixer/t_mixer)) + icon_state = "t" + else + icon_state = "" + + if(!powered()) + icon_state += "off" + else if(node2 && node3 && node1) + icon_state += use_power ? "on" : "off" + else + icon_state += "off" + use_power = 0 + +/obj/machinery/atmospherics/trinary/mixer/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + + if(istype(src, /obj/machinery/atmospherics/trinary/mixer/t_mixer)) + add_underlay(T, node1, turn(dir, -90)) + else + add_underlay(T, node1, turn(dir, -180)) + + if(istype(src, /obj/machinery/atmospherics/trinary/mixer/m_mixer) || istype(src, /obj/machinery/atmospherics/trinary/mixer/t_mixer)) + add_underlay(T, node2, turn(dir, 90)) + else + add_underlay(T, node2, turn(dir, -90)) + + add_underlay(T, node3, dir) + +/obj/machinery/atmospherics/trinary/mixer/hide(var/i) + update_underlays() + +/obj/machinery/atmospherics/trinary/mixer/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/trinary/mixer/Initialize() + . = ..() + 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/machinery_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/attackby(var/obj/item/W as obj, var/mob/user as mob) + if (!W.iswrench()) + return ..() + 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) + to_chat(user, "You cannot unwrench \the [src], it too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40/W.toolspeed, act_target = src)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + new /obj/item/pipe(loc, make_from=src) + qdel(src) + +/obj/machinery/atmospherics/trinary/mixer/attack_hand(user as mob) + if(..()) + return + src.add_fingerprint(usr) + if(!src.allowed(user)) + to_chat(user, "Access denied.") + return + usr.set_machine(src) + 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 Concentration: + - + - + [mixing_inputs[air1]]([mixing_inputs[air1]*100]%) + + + + +
+ Node 2 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/Topic(href,href_list) + if(..()) return 1 + if(href_list["power"]) + use_power = !use_power + if(href_list["set_press"]) + var/max_flow_rate = min(air1.volume, air2.volume) + var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[max_flow_rate]L/s)","Flow Rate Control",src.set_flow_rate) as num + src.set_flow_rate = max(0, min(max_flow_rate, new_flow_rate)) + if(href_list["node1_c"]) + var/value = text2num(href_list["node1_c"]) + src.mixing_inputs[air1] = max(0, min(1, src.mixing_inputs[air1] + value)) + src.mixing_inputs[air2] = 1.0 - mixing_inputs[air1] + if(href_list["node2_c"]) + var/value = text2num(href_list["node2_c"]) + src.mixing_inputs[air2] = max(0, min(1, src.mixing_inputs[air2] + value)) + src.mixing_inputs[air1] = 1.0 - mixing_inputs[air2] + src.update_icon() + src.updateUsrDialog() + return + +obj/machinery/atmospherics/trinary/mixer/t_mixer + icon_state = "tmap" + + dir = SOUTH + initialize_directions = SOUTH|EAST|WEST + + //node 3 is the outlet, nodes 1 & 2 are intakes + +obj/machinery/atmospherics/trinary/mixer/t_mixer/north + icon_state = "tmap" + + dir = NORTH + initialize_directions = NORTH|EAST|WEST + + //node 3 is the outlet, nodes 1 & 2 are intakes + +obj/machinery/atmospherics/trinary/mixer/t_mixer/Initialize() + switch(dir) + if(NORTH) + initialize_directions = EAST|NORTH|WEST + if(SOUTH) + initialize_directions = SOUTH|WEST|EAST + if(EAST) + initialize_directions = EAST|NORTH|SOUTH + if(WEST) + initialize_directions = WEST|NORTH|SOUTH + . = ..() + +obj/machinery/atmospherics/trinary/mixer/t_mixer/atmos_init() + ..() + if(node1 && node2 && node3) return + + var/node1_connect = turn(dir, -90) + var/node2_connect = turn(dir, 90) + var/node3_connect = dir + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + node2 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) + if(target.initialize_directions & get_dir(target,src)) + node3 = target + break + + update_icon() + update_underlays() + +obj/machinery/atmospherics/trinary/mixer/m_mixer + icon_state = "mmap" + + dir = SOUTH + initialize_directions = SOUTH|NORTH|EAST + + //node 3 is the outlet, nodes 1 & 2 are intakes + +obj/machinery/atmospherics/trinary/mixer/m_mixer/Initialize() + switch(dir) + if(NORTH) + initialize_directions = WEST|NORTH|SOUTH + if(SOUTH) + initialize_directions = SOUTH|EAST|NORTH + if(EAST) + initialize_directions = EAST|WEST|NORTH + if(WEST) + initialize_directions = WEST|SOUTH|EAST + . = ..() + +obj/machinery/atmospherics/trinary/mixer/m_mixer/atmos_init() + ..() + if(node1 && node2 && node3) return + + var/node1_connect = turn(dir, -180) + var/node2_connect = turn(dir, 90) + var/node3_connect = dir + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + node2 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) + if(target.initialize_directions & get_dir(target,src)) + node3 = target + break + + update_icon() + update_underlays() diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm index 4559adc7054..d3a3d93ed6d 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm @@ -1,169 +1,169 @@ -obj/machinery/atmospherics/trinary - dir = SOUTH - initialize_directions = SOUTH|NORTH|WEST - use_power = 0 - - 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 - - Initialize() - switch(dir) - if(NORTH) - initialize_directions = EAST|NORTH|SOUTH - if(SOUTH) - initialize_directions = SOUTH|WEST|NORTH - if(EAST) - initialize_directions = EAST|WEST|SOUTH - if(WEST) - initialize_directions = WEST|NORTH|EAST - air1 = new - air2 = new - air3 = new - - air1.volume = 200 - air2.volume = 200 - air3.volume = 200 - . = ..() - -// Housekeeping and pipe network stuff below - 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 - - Destroy() - QDEL_NULL(air1) - QDEL_NULL(air2) - QDEL_NULL(air3) - - if(node1) - node1.disconnect(src) - QDEL_NULL(network1) - if(node2) - node2.disconnect(src) - QDEL_NULL(network2) - if(node3) - node3.disconnect(src) - QDEL_NULL(network3) - - node1 = null - node2 = null - node3 = null - - return ..() - - atmos_init() - if(node1 && node2 && node3) return - - var/node1_connect = turn(dir, -180) - var/node2_connect = turn(dir, -90) - var/node3_connect = dir - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node2 = target - break - for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node3 = target - break - - update_icon() - update_underlays() - - 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) - - - 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 - - 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 - - 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 - - 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 +obj/machinery/atmospherics/trinary + dir = SOUTH + initialize_directions = SOUTH|NORTH|WEST + use_power = 0 + + 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 + + Initialize() + switch(dir) + if(NORTH) + initialize_directions = EAST|NORTH|SOUTH + if(SOUTH) + initialize_directions = SOUTH|WEST|NORTH + if(EAST) + initialize_directions = EAST|WEST|SOUTH + if(WEST) + initialize_directions = WEST|NORTH|EAST + air1 = new + air2 = new + air3 = new + + air1.volume = 200 + air2.volume = 200 + air3.volume = 200 + . = ..() + +// Housekeeping and pipe network stuff below + 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 + + Destroy() + QDEL_NULL(air1) + QDEL_NULL(air2) + QDEL_NULL(air3) + + if(node1) + node1.disconnect(src) + QDEL_NULL(network1) + if(node2) + node2.disconnect(src) + QDEL_NULL(network2) + if(node3) + node3.disconnect(src) + QDEL_NULL(network3) + + node1 = null + node2 = null + node3 = null + + return ..() + + atmos_init() + if(node1 && node2 && node3) return + + var/node1_connect = turn(dir, -180) + var/node2_connect = turn(dir, -90) + var/node3_connect = dir + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node2 = target + break + for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node3 = target + break + + update_icon() + update_underlays() + + 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) + + + 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 + + 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 + + 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 + + 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 diff --git a/code/ATMOSPHERICS/components/unary/cold_sink.dm b/code/ATMOSPHERICS/components/unary/cold_sink.dm index 5349e0404c5..09588b2c4cc 100644 --- a/code/ATMOSPHERICS/components/unary/cold_sink.dm +++ b/code/ATMOSPHERICS/components/unary/cold_sink.dm @@ -1,186 +1,186 @@ -//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/sleeper.dmi' - icon_state = "freezer_0" - density = 1 - anchored = 1 - use_power = 0 - idle_power_usage = 5 // 5 Watts for thermostat related circuitry - - 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 - - component_types = list( - /obj/item/circuitboard/unary_atmos/cooler, - /obj/item/stock_parts/matter_bin, - /obj/item/stock_parts/capacitor = 2, - /obj/item/stock_parts/manipulator, - /obj/item/stack/cable_coil{amount = 2} - ) - -/obj/machinery/atmospherics/unary/freezer/Initialize() - initialize_directions = dir - . = ..() - -/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(target.initialize_directions & get_dir(target, src)) - node = target - break - - //copied from pipe construction code since heaters/freezers don't use fittings and weren't doing this check - this all really really needs to be refactored someday. - //check that there are no incompatible pipes/machinery in our own location - for(var/obj/machinery/atmospherics/M in src.loc) - if(M != src && (M.initialize_directions & node_connect) && M.check_connect_types(M,src)) // matches at least one direction on either type of pipe & same connection type - node = null - break - - 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) - ui_interact(user) - -/obj/machinery/atmospherics/unary/freezer/attack_hand(mob/user as mob) - ui_interact(user) - -/obj/machinery/atmospherics/unary/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // 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 - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "freezer.tmpl", "Gas Cooling System", 440, 300) - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/freezer/Topic(href, href_list) - if(..()) - return 1 - if(href_list["toggleStatus"]) - use_power = !use_power - update_icon() - if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - set_temperature = min(set_temperature + amount, 1000) - else - set_temperature = max(set_temperature + amount, 0) - if(href_list["setPower"]) //setting power to 0 is redundant anyways - var/new_setting = between(0, text2num(href_list["setPower"]), 100) - set_power_level(new_setting) - - add_fingerprint(usr) - -/obj/machinery/atmospherics/unary/freezer/machinery_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/stock_parts/P in component_parts) - if(iscapacitor(P)) - cap_rating += P.rating - else if(ismanipulator(P)) - manip_rating += P.rating - else if(ismatterbin(P)) - bin_rating += P.rating - - power_rating = initial(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) - ..(user) - if(panel_open) - to_chat(user, "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/sleeper.dmi' + icon_state = "freezer_0" + density = 1 + anchored = 1 + use_power = 0 + idle_power_usage = 5 // 5 Watts for thermostat related circuitry + + 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 + + component_types = list( + /obj/item/circuitboard/unary_atmos/cooler, + /obj/item/stock_parts/matter_bin, + /obj/item/stock_parts/capacitor = 2, + /obj/item/stock_parts/manipulator, + /obj/item/stack/cable_coil{amount = 2} + ) + +/obj/machinery/atmospherics/unary/freezer/Initialize() + initialize_directions = dir + . = ..() + +/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(target.initialize_directions & get_dir(target, src)) + node = target + break + + //copied from pipe construction code since heaters/freezers don't use fittings and weren't doing this check - this all really really needs to be refactored someday. + //check that there are no incompatible pipes/machinery in our own location + for(var/obj/machinery/atmospherics/M in src.loc) + if(M != src && (M.initialize_directions & node_connect) && M.check_connect_types(M,src)) // matches at least one direction on either type of pipe & same connection type + node = null + break + + 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) + ui_interact(user) + +/obj/machinery/atmospherics/unary/freezer/attack_hand(mob/user as mob) + ui_interact(user) + +/obj/machinery/atmospherics/unary/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // 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 + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "freezer.tmpl", "Gas Cooling System", 440, 300) + // when the ui is first opened this is the data it will use + ui.set_initial_data(data) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/freezer/Topic(href, href_list) + if(..()) + return 1 + if(href_list["toggleStatus"]) + use_power = !use_power + update_icon() + if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + if(amount > 0) + set_temperature = min(set_temperature + amount, 1000) + else + set_temperature = max(set_temperature + amount, 0) + if(href_list["setPower"]) //setting power to 0 is redundant anyways + var/new_setting = between(0, text2num(href_list["setPower"]), 100) + set_power_level(new_setting) + + add_fingerprint(usr) + +/obj/machinery/atmospherics/unary/freezer/machinery_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/stock_parts/P in component_parts) + if(iscapacitor(P)) + cap_rating += P.rating + else if(ismanipulator(P)) + manip_rating += P.rating + else if(ismatterbin(P)) + bin_rating += P.rating + + power_rating = initial(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) + ..(user) + if(panel_open) + to_chat(user, "The maintenance hatch is open.") diff --git a/code/ATMOSPHERICS/components/unary/generator_input.dm b/code/ATMOSPHERICS/components/unary/generator_input.dm index 2b31097dc41..3a4d11428bb 100644 --- a/code/ATMOSPHERICS/components/unary/generator_input.dm +++ b/code/ATMOSPHERICS/components/unary/generator_input.dm @@ -1,21 +1,21 @@ -/obj/machinery/atmospherics/unary/generator_input - icon = 'icons/obj/atmospherics/heat_exchanger.dmi' - icon_state = "intact" - density = 1 - - name = "Generator Input" - desc = "Placeholder" - - var/update_cycle - - update_icon() - if(node) - icon_state = "intact" - else - icon_state = "exposed" - - return - - proc - return_exchange_air() +/obj/machinery/atmospherics/unary/generator_input + icon = 'icons/obj/atmospherics/heat_exchanger.dmi' + icon_state = "intact" + density = 1 + + name = "Generator Input" + desc = "Placeholder" + + var/update_cycle + + update_icon() + if(node) + icon_state = "intact" + else + icon_state = "exposed" + + return + + proc + return_exchange_air() return air_contents \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm index 93af5cb8d5a..67461129228 100644 --- a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm +++ b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm @@ -1,89 +1,89 @@ -/obj/machinery/atmospherics/unary/heat_exchanger - - icon = 'icons/obj/atmospherics/heat_exchanger.dmi' - icon_state = "intact" - density = 1 - - 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 - - update_icon() - if(node) - icon_state = "intact" - else - icon_state = "exposed" - - return - - 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 - - ..() - - machinery_process() - ..() - if(QDELETED(partner)) - return 0 - - if(!SSair || SSair.times_fired <= update_cycle) - return 0 - - update_cycle = SSair.times_fired - partner.update_cycle = SSair.times_fired - - 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 - - attackby(var/obj/item/W as obj, var/mob/user as mob) - if (!W.iswrench()) - 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 - 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) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40/W.toolspeed, act_target = src)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - new /obj/item/pipe(loc, make_from=src) - qdel(src) +/obj/machinery/atmospherics/unary/heat_exchanger + + icon = 'icons/obj/atmospherics/heat_exchanger.dmi' + icon_state = "intact" + density = 1 + + 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 + + update_icon() + if(node) + icon_state = "intact" + else + icon_state = "exposed" + + return + + 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 + + ..() + + machinery_process() + ..() + if(QDELETED(partner)) + return 0 + + if(!SSair || SSair.times_fired <= update_cycle) + return 0 + + update_cycle = SSair.times_fired + partner.update_cycle = SSair.times_fired + + 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 + + attackby(var/obj/item/W as obj, var/mob/user as mob) + if (!W.iswrench()) + 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 + 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) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40/W.toolspeed, act_target = src)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + new /obj/item/pipe(loc, make_from=src) + qdel(src) diff --git a/code/ATMOSPHERICS/components/unary/heat_source.dm b/code/ATMOSPHERICS/components/unary/heat_source.dm index b6d59cb3ccd..a44d857d7a1 100644 --- a/code/ATMOSPHERICS/components/unary/heat_source.dm +++ b/code/ATMOSPHERICS/components/unary/heat_source.dm @@ -1,172 +1,172 @@ -//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/sleeper.dmi' - icon_state = "heater_0" - density = 1 - anchored = 1 - use_power = 0 - idle_power_usage = 5 //5 Watts for thermostat related circuitry - - 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 - - component_types = list( - /obj/item/circuitboard/unary_atmos/heater, - /obj/item/stock_parts/matter_bin, - /obj/item/stock_parts/capacitor = 2, - /obj/item/stack/cable_coil{amount = 5} - ) - -/obj/machinery/atmospherics/unary/heater/Initialize() - initialize_directions = dir - . = ..() - -/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(target.initialize_directions & get_dir(target, src)) - node = target - break - - //copied from pipe construction code since heaters/freezers don't use fittings and weren't doing this check - this all really really needs to be refactored someday. - //check that there are no incompatible pipes/machinery in our own location - for(var/obj/machinery/atmospherics/M in src.loc) - if(M != src && (M.initialize_directions & node_connect) && M.check_connect_types(M,src)) // matches at least one direction on either type of pipe & same connection type - node = null - break - - 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/machinery_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) - ui_interact(user) - -/obj/machinery/atmospherics/unary/heater/attack_hand(mob/user as mob) - ui_interact(user) - -/obj/machinery/atmospherics/unary/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // 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 = "normal" - if(air_contents.temperature > (T20C+40)) - temp_class = "bad" - data["gasTemperatureClass"] = temp_class - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "freezer.tmpl", "Gas Heating System", 440, 300) - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/heater/Topic(href, href_list) - if(..()) - return 1 - if(href_list["toggleStatus"]) - use_power = !use_power - update_icon() - if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - set_temperature = min(set_temperature + amount, max_temperature) - else - set_temperature = max(set_temperature + amount, 0) - if(href_list["setPower"]) //setting power to 0 is redundant anyways - var/new_setting = between(0, text2num(href_list["setPower"]), 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/stock_parts/P in component_parts) - if(iscapacitor(P)) - cap_rating += P.rating - else if(ismatterbin(P)) - 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) - ..(user) - if(panel_open) - to_chat(user, "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/sleeper.dmi' + icon_state = "heater_0" + density = 1 + anchored = 1 + use_power = 0 + idle_power_usage = 5 //5 Watts for thermostat related circuitry + + 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 + + component_types = list( + /obj/item/circuitboard/unary_atmos/heater, + /obj/item/stock_parts/matter_bin, + /obj/item/stock_parts/capacitor = 2, + /obj/item/stack/cable_coil{amount = 5} + ) + +/obj/machinery/atmospherics/unary/heater/Initialize() + initialize_directions = dir + . = ..() + +/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(target.initialize_directions & get_dir(target, src)) + node = target + break + + //copied from pipe construction code since heaters/freezers don't use fittings and weren't doing this check - this all really really needs to be refactored someday. + //check that there are no incompatible pipes/machinery in our own location + for(var/obj/machinery/atmospherics/M in src.loc) + if(M != src && (M.initialize_directions & node_connect) && M.check_connect_types(M,src)) // matches at least one direction on either type of pipe & same connection type + node = null + break + + 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/machinery_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) + ui_interact(user) + +/obj/machinery/atmospherics/unary/heater/attack_hand(mob/user as mob) + ui_interact(user) + +/obj/machinery/atmospherics/unary/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // 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 = "normal" + if(air_contents.temperature > (T20C+40)) + temp_class = "bad" + data["gasTemperatureClass"] = temp_class + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "freezer.tmpl", "Gas Heating System", 440, 300) + // when the ui is first opened this is the data it will use + ui.set_initial_data(data) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/heater/Topic(href, href_list) + if(..()) + return 1 + if(href_list["toggleStatus"]) + use_power = !use_power + update_icon() + if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + if(amount > 0) + set_temperature = min(set_temperature + amount, max_temperature) + else + set_temperature = max(set_temperature + amount, 0) + if(href_list["setPower"]) //setting power to 0 is redundant anyways + var/new_setting = between(0, text2num(href_list["setPower"]), 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/stock_parts/P in component_parts) + if(iscapacitor(P)) + cap_rating += P.rating + else if(ismatterbin(P)) + 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) + ..(user) + if(panel_open) + to_chat(user, "The maintenance hatch is open.") diff --git a/code/ATMOSPHERICS/components/unary/outlet_injector.dm b/code/ATMOSPHERICS/components/unary/outlet_injector.dm index 1081a4b575e..5991c0fd9de 100644 --- a/code/ATMOSPHERICS/components/unary/outlet_injector.dm +++ b/code/ATMOSPHERICS/components/unary/outlet_injector.dm @@ -1,152 +1,152 @@ -//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" - layer = 3 - - name = "air injector" - desc = "Passively injects air into its surroundings. Has a valve attached to it that can control flow rate." - - use_power = 0 - 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/Initialize() - . = ..() - 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/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/machinery_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) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.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 = 1 //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/atmos_init() - ..() - - 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"]) - use_power = text2num(signal.data["power"]) - - if(signal.data["power_toggle"]) - 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"]) - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - return //do not update_icon - - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - update_icon() - -/obj/machinery/atmospherics/unary/outlet_injector/hide(var/i) - update_underlays() +//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" + layer = 3 + + name = "air injector" + desc = "Passively injects air into its surroundings. Has a valve attached to it that can control flow rate." + + use_power = 0 + 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/Initialize() + . = ..() + 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/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/machinery_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) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.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 = 1 //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/atmos_init() + ..() + + 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"]) + use_power = text2num(signal.data["power"]) + + if(signal.data["power_toggle"]) + 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"]) + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + return //do not update_icon + + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + update_icon() + +/obj/machinery/atmospherics/unary/outlet_injector/hide(var/i) + update_underlays() diff --git a/code/ATMOSPHERICS/components/unary/oxygen_generator.dm b/code/ATMOSPHERICS/components/unary/oxygen_generator.dm index 3c48ed7d2ff..93207f310a7 100644 --- a/code/ATMOSPHERICS/components/unary/oxygen_generator.dm +++ b/code/ATMOSPHERICS/components/unary/oxygen_generator.dm @@ -1,48 +1,48 @@ -obj/machinery/atmospherics/unary/oxygen_generator - icon = 'icons/obj/atmospherics/oxygen_generator.dmi' - icon_state = "intact_off" - density = 1 - - name = "Oxygen Generator" - desc = "" - - dir = SOUTH - initialize_directions = SOUTH - - var/on = 0 - - var/oxygen_content = 10 - - update_icon() - if(node) - icon_state = "intact_[on?("on"):("off")]" - else - icon_state = "exposed_off" - - on = 0 - - return - - Initialize() - . = ..() - air_contents.volume = 50 - - machinery_process() - ..() - if(!on) - return 0 - - var/total_moles = air_contents.total_moles - - if(total_moles < oxygen_content) - var/current_heat_capacity = air_contents.heat_capacity() - - var/added_oxygen = oxygen_content - total_moles - - air_contents.temperature = (current_heat_capacity*air_contents.temperature + 20*added_oxygen*T0C)/(current_heat_capacity+20*added_oxygen) - air_contents.adjust_gas("oxygen", added_oxygen) - - if(network) - network.update = 1 - - return 1 +obj/machinery/atmospherics/unary/oxygen_generator + icon = 'icons/obj/atmospherics/oxygen_generator.dmi' + icon_state = "intact_off" + density = 1 + + name = "Oxygen Generator" + desc = "" + + dir = SOUTH + initialize_directions = SOUTH + + var/on = 0 + + var/oxygen_content = 10 + + update_icon() + if(node) + icon_state = "intact_[on?("on"):("off")]" + else + icon_state = "exposed_off" + + on = 0 + + return + + Initialize() + . = ..() + air_contents.volume = 50 + + machinery_process() + ..() + if(!on) + return 0 + + var/total_moles = air_contents.total_moles + + if(total_moles < oxygen_content) + var/current_heat_capacity = air_contents.heat_capacity() + + var/added_oxygen = oxygen_content - total_moles + + air_contents.temperature = (current_heat_capacity*air_contents.temperature + 20*added_oxygen*T0C)/(current_heat_capacity+20*added_oxygen) + air_contents.adjust_gas("oxygen", added_oxygen) + + if(network) + network.update = 1 + + return 1 diff --git a/code/ATMOSPHERICS/components/unary/unary_base.dm b/code/ATMOSPHERICS/components/unary/unary_base.dm index e6681d010ca..1f232b1213a 100644 --- a/code/ATMOSPHERICS/components/unary/unary_base.dm +++ b/code/ATMOSPHERICS/components/unary/unary_base.dm @@ -1,102 +1,102 @@ -/obj/machinery/atmospherics/unary - dir = SOUTH - initialize_directions = SOUTH - //layer = TURF_LAYER+0.1 - - var/datum/gas_mixture/air_contents - - var/obj/machinery/atmospherics/node - - var/datum/pipe_network/network - - Initialize() - initialize_directions = dir - air_contents = new - air_contents.volume = 200 - . = ..() - -// Housekeeping and pipe network stuff below - 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 - - Destroy() - QDEL_NULL(air_contents) - - if(node) - node.disconnect(src) - QDEL_NULL(network) - - node = null - - return ..() - - atmos_init() - if(node) return - - var/node_connect = dir - - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node = target - break - - update_icon() - update_underlays() - - build_network() - if(!network && node) - network = new /datum/pipe_network() - network.normal_members += src - network.build_network(node, src) - - - return_network(obj/machinery/atmospherics/reference) - build_network() - - if(reference==node) - return network - - return null - - reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) - if(network == old_network) - network = new_network - - return 1 - - return_network_air(datum/pipe_network/reference) - var/list/results = list() - - if(network == reference) - results += air_contents - - return results - - disconnect(obj/machinery/atmospherics/reference) - if(reference==node) - qdel(network) - node = null - - update_icon() - update_underlays() - - return null - -/obj/machinery/atmospherics/unary/vent_pump/proc/is_welded() // TODO: refactor welding into unary - if (welded > 0) - return 1 - return 0 - -/obj/machinery/atmospherics/unary/vent_scrubber/proc/is_welded() - if (welded > 0) - return 1 - return 0 +/obj/machinery/atmospherics/unary + dir = SOUTH + initialize_directions = SOUTH + //layer = TURF_LAYER+0.1 + + var/datum/gas_mixture/air_contents + + var/obj/machinery/atmospherics/node + + var/datum/pipe_network/network + + Initialize() + initialize_directions = dir + air_contents = new + air_contents.volume = 200 + . = ..() + +// Housekeeping and pipe network stuff below + 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 + + Destroy() + QDEL_NULL(air_contents) + + if(node) + node.disconnect(src) + QDEL_NULL(network) + + node = null + + return ..() + + atmos_init() + if(node) return + + var/node_connect = dir + + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node = target + break + + update_icon() + update_underlays() + + build_network() + if(!network && node) + network = new /datum/pipe_network() + network.normal_members += src + network.build_network(node, src) + + + return_network(obj/machinery/atmospherics/reference) + build_network() + + if(reference==node) + return network + + return null + + reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network) + if(network == old_network) + network = new_network + + return 1 + + return_network_air(datum/pipe_network/reference) + var/list/results = list() + + if(network == reference) + results += air_contents + + return results + + disconnect(obj/machinery/atmospherics/reference) + if(reference==node) + qdel(network) + node = null + + update_icon() + update_underlays() + + return null + +/obj/machinery/atmospherics/unary/vent_pump/proc/is_welded() // TODO: refactor welding into unary + if (welded > 0) + return 1 + return 0 + +/obj/machinery/atmospherics/unary/vent_scrubber/proc/is_welded() + if (welded > 0) + return 1 + return 0 diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index 91eaccc1b45..044ee2ae0a8 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -1,426 +1,426 @@ -#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" - - name = "Air Vent" - desc = "Has a valve and pump attached to it" - use_power = 0 - idle_power_usage = 150 //internal circuitry, friction losses and stuff - power_rating = 7500 //7500 W ~ 10 HP - - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY //connects to regular and supply pipes - - 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/welded = 0 // Added for aliens -- TLE - - var/frequency = 1439 - var/datum/radio_frequency/radio_connection - - var/radio_filter_out - var/radio_filter_in - -/obj/machinery/atmospherics/unary/vent_pump/on - use_power = 1 - icon_state = "map_vent_out" - -/obj/machinery/atmospherics/unary/vent_pump/siphon - pump_direction = 0 - -/obj/machinery/atmospherics/unary/vent_pump/siphon/on - use_power = 1 - icon_state = "map_vent_in" - -/obj/machinery/atmospherics/unary/vent_pump/siphon/on/atmos - use_power = 1 - 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() - . = ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP - - initial_loc = loc.loc - area_uid = initial_loc.uid - if (!id_tag) - assign_uid() - id_tag = num2text(uid) - - //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) - -// Different from the above. -/obj/machinery/atmospherics/unary/vent_pump/atmos_init() - ..() - broadcast_status() - -/obj/machinery/atmospherics/unary/vent_pump/Destroy() - unregister_radio(src, frequency) - return ..() - -/obj/machinery/atmospherics/unary/vent_pump/high_volume - name = "Large Air Vent" - power_channel = EQUIP - power_rating = 15000 //15 kW ~ 20 HP - -/obj/machinery/atmospherics/unary/vent_pump/high_volume/Initialize() - . = ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 - -/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/Initialize() - . = ..() - 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 (!node) - use_power = 0 - - var/vent_icon = "" - - 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" - else if(!powered()) - vent_icon += "off" - else - vent_icon += "[use_power ? "[pump_direction ? "out" : "in"]" : "off"]" - - icon_state = vent_icon - - update_underlays() - -/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() - queue_icon_update() - -/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/machinery_process() - ..() - - if (hibernate > world.time) - return 1 - - if (!node) - use_power = 0 - if(!can_pump()) - 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) - //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) //99% of all vents - hibernate = world.time + (rand(100,200)) - - - 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 = 1 //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 - ) - - var/area/A = get_area(src) - if(!A.air_vent_names[id_tag]) - var/new_name = "[A.name] Vent Pump #[A.air_vent_names.len+1]" - A.air_vent_names[id_tag] = new_name - src.name = new_name - A.air_vent_info[id_tag] = signal.data - - radio_connection.post_signal(src, signal, radio_filter_out) - - return 1 - -/obj/machinery/atmospherics/unary/vent_pump/receive_signal(datum/signal/signal) - if(stat & (NOPOWER|BROKEN)) - return - - hibernate = 0 - - //log_debug("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) - use_power = text2num(signal.data["power"]) - - if(signal.data["power_toggle"] != null) - 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(signal.data["init"] != null) - name = signal.data["init"] - return - - if(signal.data["status"] != null) - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - return //do not update_icon - - //log_debug("DEBUG \[[world.timeofday]\]: vent_pump/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - - update_icon() - return - -/obj/machinery/atmospherics/unary/vent_pump/attackby(obj/item/W, mob/user) - if(W.iswelder()) - var/obj/item/weldingtool/WT = W - if (!WT.welding) - to_chat(user, "\The [WT] must be turned on!") - else if (WT.remove_fuel(0,user)) - to_chat(user, "Now welding the vent.") - if(do_after(user, 30/W.toolspeed)) - if(!src || !WT.isOn()) return - playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) - if(!welded) - user.visible_message("\The [user] welds \the [src] shut.", "You weld \the [src] shut.", "You hear welding.") - welded = 1 - update_icon() - else - user.visible_message("[user] unwelds \the [src].", "You unweld \the [src].", "You hear welding.") - welded = 0 - update_icon() - else - to_chat(user, "You fail to complete the welding.") - 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(..(user, 1)) - to_chat(user, "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W") - else - to_chat(user, "You are too far away to read the gauge.") - if(welded) - to_chat(user, "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/W as obj, var/mob/user as mob) - if (!W.iswrench()) - 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 - 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) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src.loc, 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.") - new /obj/item/pipe(loc, make_from=src) - qdel(src) - -/obj/machinery/atmospherics/unary/vent_pump/Destroy() - if(initial_loc) - initial_loc.air_vent_info -= id_tag - initial_loc.air_vent_names -= id_tag - return ..() - -#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" + + name = "Air Vent" + desc = "Has a valve and pump attached to it" + use_power = 0 + idle_power_usage = 150 //internal circuitry, friction losses and stuff + power_rating = 7500 //7500 W ~ 10 HP + + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY //connects to regular and supply pipes + + 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/welded = 0 // Added for aliens -- TLE + + var/frequency = 1439 + var/datum/radio_frequency/radio_connection + + var/radio_filter_out + var/radio_filter_in + +/obj/machinery/atmospherics/unary/vent_pump/on + use_power = 1 + icon_state = "map_vent_out" + +/obj/machinery/atmospherics/unary/vent_pump/siphon + pump_direction = 0 + +/obj/machinery/atmospherics/unary/vent_pump/siphon/on + use_power = 1 + icon_state = "map_vent_in" + +/obj/machinery/atmospherics/unary/vent_pump/siphon/on/atmos + use_power = 1 + 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() + . = ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + + initial_loc = loc.loc + area_uid = initial_loc.uid + if (!id_tag) + assign_uid() + id_tag = num2text(uid) + + //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) + +// Different from the above. +/obj/machinery/atmospherics/unary/vent_pump/atmos_init() + ..() + broadcast_status() + +/obj/machinery/atmospherics/unary/vent_pump/Destroy() + unregister_radio(src, frequency) + return ..() + +/obj/machinery/atmospherics/unary/vent_pump/high_volume + name = "Large Air Vent" + power_channel = EQUIP + power_rating = 15000 //15 kW ~ 20 HP + +/obj/machinery/atmospherics/unary/vent_pump/high_volume/Initialize() + . = ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_PUMP + 800 + +/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/Initialize() + . = ..() + 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 (!node) + use_power = 0 + + var/vent_icon = "" + + 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" + else if(!powered()) + vent_icon += "off" + else + vent_icon += "[use_power ? "[pump_direction ? "out" : "in"]" : "off"]" + + icon_state = vent_icon + + update_underlays() + +/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() + queue_icon_update() + +/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/machinery_process() + ..() + + if (hibernate > world.time) + return 1 + + if (!node) + use_power = 0 + if(!can_pump()) + 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) + //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) //99% of all vents + hibernate = world.time + (rand(100,200)) + + + 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 = 1 //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 + ) + + var/area/A = get_area(src) + if(!A.air_vent_names[id_tag]) + var/new_name = "[A.name] Vent Pump #[A.air_vent_names.len+1]" + A.air_vent_names[id_tag] = new_name + src.name = new_name + A.air_vent_info[id_tag] = signal.data + + radio_connection.post_signal(src, signal, radio_filter_out) + + return 1 + +/obj/machinery/atmospherics/unary/vent_pump/receive_signal(datum/signal/signal) + if(stat & (NOPOWER|BROKEN)) + return + + hibernate = 0 + + //log_debug("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) + use_power = text2num(signal.data["power"]) + + if(signal.data["power_toggle"] != null) + 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(signal.data["init"] != null) + name = signal.data["init"] + return + + if(signal.data["status"] != null) + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + return //do not update_icon + + //log_debug("DEBUG \[[world.timeofday]\]: vent_pump/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + + update_icon() + return + +/obj/machinery/atmospherics/unary/vent_pump/attackby(obj/item/W, mob/user) + if(W.iswelder()) + var/obj/item/weldingtool/WT = W + if (!WT.welding) + to_chat(user, "\The [WT] must be turned on!") + else if (WT.remove_fuel(0,user)) + to_chat(user, "Now welding the vent.") + if(do_after(user, 30/W.toolspeed)) + if(!src || !WT.isOn()) return + playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) + if(!welded) + user.visible_message("\The [user] welds \the [src] shut.", "You weld \the [src] shut.", "You hear welding.") + welded = 1 + update_icon() + else + user.visible_message("[user] unwelds \the [src].", "You unweld \the [src].", "You hear welding.") + welded = 0 + update_icon() + else + to_chat(user, "You fail to complete the welding.") + 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(..(user, 1)) + to_chat(user, "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W") + else + to_chat(user, "You are too far away to read the gauge.") + if(welded) + to_chat(user, "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/W as obj, var/mob/user as mob) + if (!W.iswrench()) + 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 + 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) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src.loc, 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.") + new /obj/item/pipe(loc, make_from=src) + qdel(src) + +/obj/machinery/atmospherics/unary/vent_pump/Destroy() + if(initial_loc) + initial_loc.air_vent_info -= id_tag + initial_loc.air_vent_names -= id_tag + return ..() + +#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 7b5be101af0..ffb55d58584 100644 --- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm @@ -1,318 +1,318 @@ -/obj/machinery/atmospherics/unary/vent_scrubber - icon = 'icons/atmos/vent_scrubber.dmi' - icon_state = "map_scrubber_off" - - name = "Air Scrubber" - desc = "Has a valve and pump attached to it" - use_power = 0 - 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") - - var/panic = 0 //is this scrubber panicked? - - var/area_uid - var/radio_filter_out - var/radio_filter_in - - var/welded = 0 - -/obj/machinery/atmospherics/unary/vent_scrubber/on - use_power = 1 - icon_state = "map_scrubber_on" - -/obj/machinery/atmospherics/unary/vent_scrubber/Initialize() - . = ..() - air_contents.volume = ATMOS_DEFAULT_VOLUME_FILTER - - initial_loc = get_area(loc) - area_uid = initial_loc.uid - if (!id_tag) - assign_uid() - id_tag = num2text(uid) - - 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) - -/obj/machinery/atmospherics/unary/vent_scrubber/atmos_init() - ..() - broadcast_status() - -/obj/machinery/atmospherics/unary/vent_scrubber/Destroy() - unregister_radio(src, frequency) - return ..() - -/obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0) - var/turf/T = get_turf(src) - if(!istype(T)) - return - - if (welded) - icon_state = "weld" - return - - if (!powered() || !use_power) - icon_state = "off" - else if (scrubbing) - icon_state = "on" - else - icon_state = "in" - -/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) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.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 = 1 //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" = ("sleeping_agent" in scrubbing_gas), - "sigtype" = "status" - ) - - var/area/A = get_area(src) - if(!A.air_scrub_names[id_tag]) - var/new_name = "[A.name] Air Scrubber #[A.air_scrub_names.len+1]" - A.air_scrub_names[id_tag] = new_name - src.name = new_name - A.air_scrub_info[id_tag] = signal.data - radio_connection.post_signal(src, signal, radio_filter_out) - - return 1 - -/obj/machinery/atmospherics/unary/vent_scrubber/machinery_process() - ..() - - if (hibernate > world.time) - return 1 - - if (!node) - use_power = 0 - //broadcast_status() - if(!use_power || (stat & (NOPOWER|BROKEN))) - return 0 - if(welded) - 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) //99% of all scrubbers - //Fucking hibernate because you ain't doing shit. - hibernate = world.time + (rand(100,200)) - - 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) - use_power = text2num(signal.data["power"]) - if(signal.data["power_toggle"] != null) - use_power = !use_power - - if(signal.data["panic_siphon"]) //must be before if("scrubbing" thing - panic = text2num(signal.data["panic_siphon"]) - if(panic) - use_power = 1 - scrubbing = 0 - else - scrubbing = 1 - if(signal.data["toggle_panic_siphon"] != null) - panic = !panic - if(panic) - use_power = 1 - 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"]) != ("sleeping_agent" in scrubbing_gas)) - toggle += "sleeping_agent" - else if(signal.data["toggle_n2o_scrub"]) - toggle += "sleeping_agent" - - scrubbing_gas ^= toggle - - if(signal.data["init"] != null) - name = signal.data["init"] - return - - if(signal.data["status"] != null) - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - return //do not update_icon - -// log_debug("DEBUG \[[world.timeofday]\]: vent_scrubber/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") - addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) - 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/W as obj, var/mob/user as mob) - if (W.iswrench()) - 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 - 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) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40/W.toolspeed, act_target = src)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - new /obj/item/pipe(loc, make_from=src) - qdel(src) - return 1 - - if(W.iswelder()) - var/obj/item/weldingtool/WT = W - if (!WT.welding) - to_chat(user, "\The [WT] must be turned on!") - else if (WT.remove_fuel(0,user)) - to_chat(user, "Now welding \the [src].") - if(do_after(user, 20/W.toolspeed, act_target = src)) - if(!src || !WT.isOn()) return - playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) - if(!welded) - user.visible_message("\The [user] welds \the [src] shut.", "You weld \the [src] shut.", "You hear welding.") - welded = 1 - update_icon() - else - user.visible_message("[user] unwelds \the [src].", "You unweld \the [src].", "You hear welding.") - welded = 0 - update_icon() - else - to_chat(user, "You fail to complete the welding.") - else - to_chat(user, "You need more welding fuel to complete this task.") - return 1 - return ..() - -/obj/machinery/atmospherics/unary/vent_scrubber/examine(mob/user) - if(..(user, 1)) - to_chat(user, "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W") - else - to_chat(user, "You are too far away to read the gauge.") - if(welded) - to_chat(user, "It seems welded shut.") - -/obj/machinery/atmospherics/unary/vent_scrubber/Destroy() - if(initial_loc) - initial_loc.air_scrub_info -= id_tag - initial_loc.air_scrub_names -= id_tag - - return ..() +/obj/machinery/atmospherics/unary/vent_scrubber + icon = 'icons/atmos/vent_scrubber.dmi' + icon_state = "map_scrubber_off" + + name = "Air Scrubber" + desc = "Has a valve and pump attached to it" + use_power = 0 + 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") + + var/panic = 0 //is this scrubber panicked? + + var/area_uid + var/radio_filter_out + var/radio_filter_in + + var/welded = 0 + +/obj/machinery/atmospherics/unary/vent_scrubber/on + use_power = 1 + icon_state = "map_scrubber_on" + +/obj/machinery/atmospherics/unary/vent_scrubber/Initialize() + . = ..() + air_contents.volume = ATMOS_DEFAULT_VOLUME_FILTER + + initial_loc = get_area(loc) + area_uid = initial_loc.uid + if (!id_tag) + assign_uid() + id_tag = num2text(uid) + + 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) + +/obj/machinery/atmospherics/unary/vent_scrubber/atmos_init() + ..() + broadcast_status() + +/obj/machinery/atmospherics/unary/vent_scrubber/Destroy() + unregister_radio(src, frequency) + return ..() + +/obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0) + var/turf/T = get_turf(src) + if(!istype(T)) + return + + if (welded) + icon_state = "weld" + return + + if (!powered() || !use_power) + icon_state = "off" + else if (scrubbing) + icon_state = "on" + else + icon_state = "in" + +/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) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.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 = 1 //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" = ("sleeping_agent" in scrubbing_gas), + "sigtype" = "status" + ) + + var/area/A = get_area(src) + if(!A.air_scrub_names[id_tag]) + var/new_name = "[A.name] Air Scrubber #[A.air_scrub_names.len+1]" + A.air_scrub_names[id_tag] = new_name + src.name = new_name + A.air_scrub_info[id_tag] = signal.data + radio_connection.post_signal(src, signal, radio_filter_out) + + return 1 + +/obj/machinery/atmospherics/unary/vent_scrubber/machinery_process() + ..() + + if (hibernate > world.time) + return 1 + + if (!node) + use_power = 0 + //broadcast_status() + if(!use_power || (stat & (NOPOWER|BROKEN))) + return 0 + if(welded) + 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) //99% of all scrubbers + //Fucking hibernate because you ain't doing shit. + hibernate = world.time + (rand(100,200)) + + 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) + use_power = text2num(signal.data["power"]) + if(signal.data["power_toggle"] != null) + use_power = !use_power + + if(signal.data["panic_siphon"]) //must be before if("scrubbing" thing + panic = text2num(signal.data["panic_siphon"]) + if(panic) + use_power = 1 + scrubbing = 0 + else + scrubbing = 1 + if(signal.data["toggle_panic_siphon"] != null) + panic = !panic + if(panic) + use_power = 1 + 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"]) != ("sleeping_agent" in scrubbing_gas)) + toggle += "sleeping_agent" + else if(signal.data["toggle_n2o_scrub"]) + toggle += "sleeping_agent" + + scrubbing_gas ^= toggle + + if(signal.data["init"] != null) + name = signal.data["init"] + return + + if(signal.data["status"] != null) + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + return //do not update_icon + +// log_debug("DEBUG \[[world.timeofday]\]: vent_scrubber/receive_signal: unknown command \"[signal.data["command"]]\"\n[signal.debug_print()]") + addtimer(CALLBACK(src, .proc/broadcast_status), 2, TIMER_UNIQUE) + 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/W as obj, var/mob/user as mob) + if (W.iswrench()) + 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 + 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) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40/W.toolspeed, act_target = src)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + new /obj/item/pipe(loc, make_from=src) + qdel(src) + return 1 + + if(W.iswelder()) + var/obj/item/weldingtool/WT = W + if (!WT.welding) + to_chat(user, "\The [WT] must be turned on!") + else if (WT.remove_fuel(0,user)) + to_chat(user, "Now welding \the [src].") + if(do_after(user, 20/W.toolspeed, act_target = src)) + if(!src || !WT.isOn()) return + playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) + if(!welded) + user.visible_message("\The [user] welds \the [src] shut.", "You weld \the [src] shut.", "You hear welding.") + welded = 1 + update_icon() + else + user.visible_message("[user] unwelds \the [src].", "You unweld \the [src].", "You hear welding.") + welded = 0 + update_icon() + else + to_chat(user, "You fail to complete the welding.") + else + to_chat(user, "You need more welding fuel to complete this task.") + return 1 + return ..() + +/obj/machinery/atmospherics/unary/vent_scrubber/examine(mob/user) + if(..(user, 1)) + to_chat(user, "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W") + else + to_chat(user, "You are too far away to read the gauge.") + if(welded) + to_chat(user, "It seems welded shut.") + +/obj/machinery/atmospherics/unary/vent_scrubber/Destroy() + if(initial_loc) + initial_loc.air_scrub_info -= id_tag + initial_loc.air_scrub_names -= id_tag + + return ..() diff --git a/code/ATMOSPHERICS/components/valve.dm b/code/ATMOSPHERICS/components/valve.dm index 48ca8837bdb..903f09af65d 100644 --- a/code/ATMOSPHERICS/components/valve.dm +++ b/code/ATMOSPHERICS/components/valve.dm @@ -1,326 +1,326 @@ -/obj/machinery/atmospherics/valve - icon = 'icons/atmos/valve.dmi' - icon_state = "map_valve0" - - 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/Initialize() - switch(dir) - if(NORTH || SOUTH) - initialize_directions = NORTH|SOUTH - if(EAST || WEST) - initialize_directions = EAST|WEST - . = ..() - -/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() - loc = null - - if(node1) - node1.disconnect(src) - qdel(network_node1) - if(node2) - node2.disconnect(src) - qdel(network_node2) - - node1 = null - node2 = null - - return ..() - -/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/machinery_process() - ..() - return PROCESS_KILL - -/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 - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node1 = target - break - for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node2 = target - break - - build_network() - - queue_icon_update() - 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/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' - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/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 - ..() - - log_and_message_admins("has [open ? "OPENED" : "closed"] [name].", user) - -/obj/machinery/atmospherics/valve/digital/AltClick(var/mob/abstract/observer/admin) - if (istype(admin)) - if (admin.client && admin.client.holder && ((R_MOD|R_ADMIN) & admin.client.holder.rights)) - if (open) - close() - else - if (alert(admin, "The valve is currently closed. Do you want to open it?", "Open the valve?", "Yes", "No") == "No") - return - open() - - log_and_message_admins("has [open ? "opened" : "closed"] [name].", admin) - -/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) - queue_icon_update() - -/obj/machinery/atmospherics/valve/digital/update_icon() - ..() - if(!powered()) - icon_state = "valve[open]nopower" - -/obj/machinery/atmospherics/valve/digital/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/atmospherics/valve/digital/atmos_init() - ..() - 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/W as obj, var/mob/user as mob) - if (!W.iswrench()) - return ..() - if (istype(src, /obj/machinery/atmospherics/valve/digital)) - to_chat(user, "You cannot unwrench \the [src], it's too complicated.") - return 1 - 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) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, 40/W.toolspeed, act_target = src)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - new /obj/item/pipe(loc, make_from=src) - qdel(src) - -/obj/machinery/atmospherics/valve/examine(mob/user) - ..() - to_chat(user, "It is [open ? "open" : "closed"].") +/obj/machinery/atmospherics/valve + icon = 'icons/atmos/valve.dmi' + icon_state = "map_valve0" + + 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/Initialize() + switch(dir) + if(NORTH || SOUTH) + initialize_directions = NORTH|SOUTH + if(EAST || WEST) + initialize_directions = EAST|WEST + . = ..() + +/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() + loc = null + + if(node1) + node1.disconnect(src) + qdel(network_node1) + if(node2) + node2.disconnect(src) + qdel(network_node2) + + node1 = null + node2 = null + + return ..() + +/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/machinery_process() + ..() + return PROCESS_KILL + +/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 + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node1 = target + break + for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node2 = target + break + + build_network() + + queue_icon_update() + 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/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' + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/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 + ..() + + log_and_message_admins("has [open ? "OPENED" : "closed"] [name].", user) + +/obj/machinery/atmospherics/valve/digital/AltClick(var/mob/abstract/observer/admin) + if (istype(admin)) + if (admin.client && admin.client.holder && ((R_MOD|R_ADMIN) & admin.client.holder.rights)) + if (open) + close() + else + if (alert(admin, "The valve is currently closed. Do you want to open it?", "Open the valve?", "Yes", "No") == "No") + return + open() + + log_and_message_admins("has [open ? "opened" : "closed"] [name].", admin) + +/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) + queue_icon_update() + +/obj/machinery/atmospherics/valve/digital/update_icon() + ..() + if(!powered()) + icon_state = "valve[open]nopower" + +/obj/machinery/atmospherics/valve/digital/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/atmospherics/valve/digital/atmos_init() + ..() + 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/W as obj, var/mob/user as mob) + if (!W.iswrench()) + return ..() + if (istype(src, /obj/machinery/atmospherics/valve/digital)) + to_chat(user, "You cannot unwrench \the [src], it's too complicated.") + return 1 + 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) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, 40/W.toolspeed, act_target = src)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + new /obj/item/pipe(loc, make_from=src) + qdel(src) + +/obj/machinery/atmospherics/valve/examine(mob/user) + ..() + to_chat(user, "It is [open ? "open" : "closed"].") diff --git a/code/ATMOSPHERICS/datum_pipe_network.dm b/code/ATMOSPHERICS/datum_pipe_network.dm index e952364cb33..a38d42e21db 100644 --- a/code/ATMOSPHERICS/datum_pipe_network.dm +++ b/code/ATMOSPHERICS/datum_pipe_network.dm @@ -1,91 +1,91 @@ -/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/update = 1 - //var/datum/gas_mixture/air_transient = null - -/* -/datum/pipe_network/New() - //air_transient = new() - - ..()*/ - -/datum/pipe_network/process() - //Equalize gases amongst pipe if called for - if(update) - update = 0 - reconcile_air() //equalize_gases(gases) - - //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) - - start_normal.network_expand(src, reference) - - update_network_gases() - - if((normal_members.len>0)||(line_members.len>0)) - START_PROCESSING(SSpipenet, 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 - - 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/Destroy(force = FALSE) - STOP_PROCESSING(SSpipenet, src) - for (var/datum/pipeline/pipeline in line_members) - pipeline.network = null - - line_members = null - - for (var/obj/machinery/atmospherics/thing in normal_members) - thing.remove_network(src) - - normal_members = null - - return ..() +/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/update = 1 + //var/datum/gas_mixture/air_transient = null + +/* +/datum/pipe_network/New() + //air_transient = new() + + ..()*/ + +/datum/pipe_network/process() + //Equalize gases amongst pipe if called for + if(update) + update = 0 + reconcile_air() //equalize_gases(gases) + + //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) + + start_normal.network_expand(src, reference) + + update_network_gases() + + if((normal_members.len>0)||(line_members.len>0)) + START_PROCESSING(SSpipenet, 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 + + 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/Destroy(force = FALSE) + STOP_PROCESSING(SSpipenet, src) + for (var/datum/pipeline/pipeline in line_members) + pipeline.network = null + + line_members = null + + for (var/obj/machinery/atmospherics/thing in normal_members) + thing.remove_network(src) + + normal_members = null + + return ..() diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index de600d78a0b..e4ea98f402b 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -1,218 +1,218 @@ - -/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 - - var/datum/pipe_network/network - - var/alert_pressure = 0 - -/datum/pipeline/Destroy() - if(network) - QDEL_NULL(network) - - if(air && air.volume) - temporarily_store_air() - QDEL_NULL(air) - - for (var/obj/machinery/atmospherics/pipe/thing in members) - thing.parent = null - - return ..() - -/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 - - 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(!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) - - 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 - - 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 - - turf_copy.copy_from(target.zone.air) - turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group - - equalize_gases(list(air_sample, turf_copy)) - air.merge(air_sample) - - turf_copy.subtract(target.zone.air) - - 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.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 + + var/datum/pipe_network/network + + var/alert_pressure = 0 + +/datum/pipeline/Destroy() + if(network) + QDEL_NULL(network) + + if(air && air.volume) + temporarily_store_air() + QDEL_NULL(air) + + for (var/obj/machinery/atmospherics/pipe/thing in members) + thing.parent = null + + return ..() + +/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 + + 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(!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) + + 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 + + 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 + + turf_copy.copy_from(target.zone.air) + turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group + + equalize_gases(list(air_sample, turf_copy)) + air.merge(air_sample) + + turf_copy.subtract(target.zone.air) + + 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.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/he_pipes.dm b/code/ATMOSPHERICS/he_pipes.dm index 51359ff6efb..0a1d923ed54 100644 --- a/code/ATMOSPHERICS/he_pipes.dm +++ b/code/ATMOSPHERICS/he_pipes.dm @@ -1,146 +1,146 @@ -/obj/machinery/atmospherics/pipe/simple/heat_exchanging - icon = 'icons/atmos/heat.dmi' - icon_state = "intact" - pipe_icon = "hepipe" - color = "#404040" - level = 2 - connect_types = CONNECT_TYPE_HE - layer = 2.41 - var/initialize_directions_he - var/surface = 2 //surface area in m^2 - var/icon_temperature = T20C //stop small changes in temperature causing an icon refresh - - minimum_temperature_difference = 20 - thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT - - buckle_lying = 1 - - // BubbleWrap -/obj/machinery/atmospherics/pipe/simple/heat_exchanging/Initialize() - . = ..() - initialize_directions_he = initialize_directions // The auto-detection from /pipe is good enough for a simple HE pipe -// BubbleWrap END - color = "#404040" //we don't make use of the fancy overlay system for colours, use this to set the default. - -/obj/machinery/atmospherics/pipe/simple/heat_exchanging/atmos_init() - normalize_dir() - var/node1_dir - var/node2_dir - - for(var/direction in cardinal) - if(direction&initialize_directions_he) - if (!node1_dir) - node1_dir = direction - else if (!node2_dir) - node2_dir = direction - - for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node1_dir)) - if(target.initialize_directions_he & get_dir(target,src)) - node1 = target - break - for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node2_dir)) - if(target.initialize_directions_he & get_dir(target,src)) - node2 = target - break - if(!node1 && !node2) - qdel(src) - return - - queue_icon_update() - -/obj/machinery/atmospherics/pipe/simple/heat_exchanging/machinery_process() - if(!parent) - ..() - else - var/datum/gas_mixture/pipe_air = return_air() - if(istype(loc, /turf/simulated/)) - var/environment_temperature = 0 - if(loc:blocks_air) - environment_temperature = loc:temperature - else - var/datum/gas_mixture/environment = loc.return_air() - environment_temperature = environment.temperature - if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference) - parent.temperature_interact(loc, volume, thermal_conductivity) - else if(istype(loc, /turf/space/)) - parent.radiate_heat_to_space(surface, 1) - - if(buckled_mob) - var/hc = pipe_air.heat_capacity() - var/avg_temp = (pipe_air.temperature * hc + buckled_mob.bodytemperature * 3500) / (hc + 3500) - pipe_air.temperature = avg_temp - buckled_mob.bodytemperature = avg_temp - - var/heat_limit = 1000 - - var/mob/living/carbon/human/H = buckled_mob - if(istype(H) && H.species) - heat_limit = H.species.heat_level_3 - - if(pipe_air.temperature > heat_limit + 1) - buckled_mob.apply_damage(4 * log(pipe_air.temperature - heat_limit), BURN, BP_CHEST, used_weapon = "Excessive Heat") - - //fancy radiation glowing - if(pipe_air.temperature && (icon_temperature > 500 || pipe_air.temperature > 500)) //start glowing at 500K - if(abs(pipe_air.temperature - icon_temperature) > 10) - icon_temperature = pipe_air.temperature - - var/h_r = heat2color_r(icon_temperature) - var/h_g = heat2color_g(icon_temperature) - var/h_b = heat2color_b(icon_temperature) - - if(icon_temperature < 2000) //scale up overlay until 2000K - var/scale = (icon_temperature - 500) / 1500 - h_r = 64 + (h_r - 64)*scale - h_g = 64 + (h_g - 64)*scale - h_b = 64 + (h_b - 64)*scale - - var/list/animate_targets = get_above_oo() + src - for (var/thing in animate_targets) - var/atom/movable/AM = thing - animate(AM, color = rgb(h_r, h_g, h_b), time = 20, easing = SINE_EASING) - - -/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction - icon = 'icons/atmos/junction.dmi' - icon_state = "intact" - pipe_icon = "hejunction" - level = 2 - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_HE - minimum_temperature_difference = 300 - thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT - -// BubbleWrap -/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction/Initialize() - . = ..() - switch (dir) - if (SOUTH) - initialize_directions = NORTH - initialize_directions_he = SOUTH - if (NORTH) - initialize_directions = SOUTH - initialize_directions_he = NORTH - if (EAST) - initialize_directions = WEST - initialize_directions_he = EAST - if (WEST) - initialize_directions = EAST - initialize_directions_he = WEST - // BubbleWrap END - -/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction/atmos_init() - for(var/obj/machinery/atmospherics/target in get_step(src,initialize_directions)) - if(target.initialize_directions & get_dir(target,src)) - node1 = target - break - - for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,initialize_directions_he)) - if(target.initialize_directions_he & get_dir(target,src)) - node2 = target - break - - if(!node1 && !node2) - qdel(src) - return - - queue_icon_update() +/obj/machinery/atmospherics/pipe/simple/heat_exchanging + icon = 'icons/atmos/heat.dmi' + icon_state = "intact" + pipe_icon = "hepipe" + color = "#404040" + level = 2 + connect_types = CONNECT_TYPE_HE + layer = 2.41 + var/initialize_directions_he + var/surface = 2 //surface area in m^2 + var/icon_temperature = T20C //stop small changes in temperature causing an icon refresh + + minimum_temperature_difference = 20 + thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT + + buckle_lying = 1 + + // BubbleWrap +/obj/machinery/atmospherics/pipe/simple/heat_exchanging/Initialize() + . = ..() + initialize_directions_he = initialize_directions // The auto-detection from /pipe is good enough for a simple HE pipe +// BubbleWrap END + color = "#404040" //we don't make use of the fancy overlay system for colours, use this to set the default. + +/obj/machinery/atmospherics/pipe/simple/heat_exchanging/atmos_init() + normalize_dir() + var/node1_dir + var/node2_dir + + for(var/direction in cardinal) + if(direction&initialize_directions_he) + if (!node1_dir) + node1_dir = direction + else if (!node2_dir) + node2_dir = direction + + for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node1_dir)) + if(target.initialize_directions_he & get_dir(target,src)) + node1 = target + break + for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node2_dir)) + if(target.initialize_directions_he & get_dir(target,src)) + node2 = target + break + if(!node1 && !node2) + qdel(src) + return + + queue_icon_update() + +/obj/machinery/atmospherics/pipe/simple/heat_exchanging/machinery_process() + if(!parent) + ..() + else + var/datum/gas_mixture/pipe_air = return_air() + if(istype(loc, /turf/simulated/)) + var/environment_temperature = 0 + if(loc:blocks_air) + environment_temperature = loc:temperature + else + var/datum/gas_mixture/environment = loc.return_air() + environment_temperature = environment.temperature + if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference) + parent.temperature_interact(loc, volume, thermal_conductivity) + else if(istype(loc, /turf/space/)) + parent.radiate_heat_to_space(surface, 1) + + if(buckled_mob) + var/hc = pipe_air.heat_capacity() + var/avg_temp = (pipe_air.temperature * hc + buckled_mob.bodytemperature * 3500) / (hc + 3500) + pipe_air.temperature = avg_temp + buckled_mob.bodytemperature = avg_temp + + var/heat_limit = 1000 + + var/mob/living/carbon/human/H = buckled_mob + if(istype(H) && H.species) + heat_limit = H.species.heat_level_3 + + if(pipe_air.temperature > heat_limit + 1) + buckled_mob.apply_damage(4 * log(pipe_air.temperature - heat_limit), BURN, BP_CHEST, used_weapon = "Excessive Heat") + + //fancy radiation glowing + if(pipe_air.temperature && (icon_temperature > 500 || pipe_air.temperature > 500)) //start glowing at 500K + if(abs(pipe_air.temperature - icon_temperature) > 10) + icon_temperature = pipe_air.temperature + + var/h_r = heat2color_r(icon_temperature) + var/h_g = heat2color_g(icon_temperature) + var/h_b = heat2color_b(icon_temperature) + + if(icon_temperature < 2000) //scale up overlay until 2000K + var/scale = (icon_temperature - 500) / 1500 + h_r = 64 + (h_r - 64)*scale + h_g = 64 + (h_g - 64)*scale + h_b = 64 + (h_b - 64)*scale + + var/list/animate_targets = get_above_oo() + src + for (var/thing in animate_targets) + var/atom/movable/AM = thing + animate(AM, color = rgb(h_r, h_g, h_b), time = 20, easing = SINE_EASING) + + +/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction + icon = 'icons/atmos/junction.dmi' + icon_state = "intact" + pipe_icon = "hejunction" + level = 2 + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_HE + minimum_temperature_difference = 300 + thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT + +// BubbleWrap +/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction/Initialize() + . = ..() + switch (dir) + if (SOUTH) + initialize_directions = NORTH + initialize_directions_he = SOUTH + if (NORTH) + initialize_directions = SOUTH + initialize_directions_he = NORTH + if (EAST) + initialize_directions = WEST + initialize_directions_he = EAST + if (WEST) + initialize_directions = EAST + initialize_directions_he = WEST + // BubbleWrap END + +/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction/atmos_init() + for(var/obj/machinery/atmospherics/target in get_step(src,initialize_directions)) + if(target.initialize_directions & get_dir(target,src)) + node1 = target + break + + for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,initialize_directions_he)) + if(target.initialize_directions_he & get_dir(target,src)) + node2 = target + break + + if(!node1 && !node2) + qdel(src) + return + + queue_icon_update() diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm index 4700ae62952..69727bce588 100644 --- a/code/ATMOSPHERICS/pipes.dm +++ b/code/ATMOSPHERICS/pipes.dm @@ -1,1412 +1,1412 @@ -/obj/machinery/atmospherics/pipe - - var/datum/gas_mixture/air_temporary // used when reconstructing a pipeline that broke - var/datum/pipeline/parent - var/volume = 0 - force = 20 - - layer = PIPE_LAYER - use_power = 0 - - var/alert_pressure = 80*ONE_ATMOSPHERE - //minimum pressure before check_pressure(...) should be called - - can_buckle = 1 - buckle_require_restraints = 1 - buckle_lying = -1 - -/obj/machinery/atmospherics/pipe/drain_power() - return -1 - -/obj/machinery/atmospherics/pipe/Initialize() - if(istype(get_turf(src), /turf/simulated/wall) || istype(get_turf(src), /turf/unsimulated/wall)) - level = 1 - . = ..() - -/obj/machinery/atmospherics/pipe/hides_under_flooring() - return level != 2 - -/obj/machinery/atmospherics/pipe/proc/pipeline_expansion() - return null - -/obj/machinery/atmospherics/pipe/proc/check_pressure(pressure) - //Return 1 if parent should continue checking other pipes - //Return null if parent should stop checking other pipes. Recall: qdel(src) will by default return null - - return 1 - -/obj/machinery/atmospherics/pipe/return_air() - if(!parent) - parent = new /datum/pipeline() - parent.build_pipeline(src) - - return parent.air - -/obj/machinery/atmospherics/pipe/build_network() - if(!parent) - parent = new /datum/pipeline() - parent.build_pipeline(src) - - return parent.return_network() - -/obj/machinery/atmospherics/pipe/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(!parent) - parent = new /datum/pipeline() - parent.build_pipeline(src) - - return parent.network_expand(new_network, reference) - -/obj/machinery/atmospherics/pipe/return_network(obj/machinery/atmospherics/reference) - if(!parent) - parent = new /datum/pipeline() - parent.build_pipeline(src) - - return parent.return_network(reference) - -/obj/machinery/atmospherics/pipe/Destroy() - QDEL_NULL(parent) - if(air_temporary) - loc.assume_air(air_temporary) - QDEL_NULL(air_temporary) - - return ..() - -/obj/machinery/atmospherics/pipe/attackby(var/obj/item/W as obj, var/mob/user as mob) - if (istype(src, /obj/machinery/atmospherics/pipe/tank)) - return ..() - if (istype(src, /obj/machinery/atmospherics/pipe/vent)) - return ..() - - if(istype(W,/obj/item/device/pipe_painter)) - return 0 - - if (!W.iswrench() && !istype(W, /obj/item/pipewrench)) - 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 - 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) - if(!istype(W, /obj/item/pipewrench)) - to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - else - to_chat(user, "You struggle to unwrench \the [src] with your pipe wrench.") - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if (do_after(user, istype(W, /obj/item/pipewrench) ? 80/W.toolspeed : 40/W.toolspeed, act_target = src)) - user.visible_message( \ - "\The [user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - new /obj/item/pipe(loc, make_from=src) - for (var/obj/machinery/meter/meter in T) - if (meter.target == src) - new /obj/item/pipe_meter(T) - qdel(meter) - qdel(src) - -/obj/machinery/atmospherics/proc/change_color(var/new_color) - //only pass valid pipe colors please ~otherwise your pipe will turn invisible - if(!pipe_color_check(new_color)) - return - - pipe_color = new_color - update_icon() - -/* -/obj/machinery/atmospherics/pipe/add_underlay(var/obj/machinery/atmospherics/node, var/direction) - if(istype(src, /obj/machinery/atmospherics/pipe/tank)) //todo: move tanks to unary devices - return ..() - - if(node) - var/temp_dir = get_dir(src, node) - underlays += icon_manager.get_atmos_icon("pipe_underlay_intact", temp_dir, color_cache_name(node)) - return temp_dir - else if(direction) - underlays += icon_manager.get_atmos_icon("pipe_underlay_exposed", direction, pipe_color) - else - return null -*/ - -/obj/machinery/atmospherics/pipe/color_cache_name(var/obj/machinery/atmospherics/node) - if(istype(src, /obj/machinery/atmospherics/pipe/tank)) - return ..() - - if(istype(node, /obj/machinery/atmospherics/pipe/manifold) || istype(node, /obj/machinery/atmospherics/pipe/manifold4w)) - if(pipe_color == node.pipe_color) - return node.pipe_color - else - return null - else if(istype(node, /obj/machinery/atmospherics/pipe/simple)) - return node.pipe_color - else - return pipe_color - -/obj/machinery/atmospherics/pipe/simple - icon = 'icons/atmos/pipes.dmi' - icon_state = "" - var/pipe_icon = "" //what kind of pipe it is and from which dmi is the icon manager getting its icons, "" for simple pipes, "hepipe" for HE pipes, "hejunction" for HE junctions - name = "pipe" - desc = "A one meter section of regular pipe" - - volume = ATMOS_DEFAULT_VOLUME_PIPE - - dir = SOUTH - initialize_directions = SOUTH|NORTH - - var/minimum_temperature_difference = 300 - var/thermal_conductivity = 0 //WALL_HEAT_TRANSFER_COEFFICIENT No - - var/maximum_pressure = 70*ONE_ATMOSPHERE - var/fatigue_pressure = 55*ONE_ATMOSPHERE - alert_pressure = 55*ONE_ATMOSPHERE - - level = 1 - gfi_layer_rotation = GFI_ROTATION_DEFDIR - -/obj/machinery/atmospherics/pipe/simple/Initialize() - - // Pipe colors and icon states are handled by an image cache - so color and icon should - // be null. For mapping purposes color is defined in the object definitions. - icon = null - alpha = 255 - - switch(dir) - if(SOUTH || NORTH) - initialize_directions = SOUTH|NORTH - if(EAST || WEST) - initialize_directions = EAST|WEST - if(NORTHEAST) - initialize_directions = NORTH|EAST - if(NORTHWEST) - initialize_directions = NORTH|WEST - if(SOUTHEAST) - initialize_directions = SOUTH|EAST - if(SOUTHWEST) - initialize_directions = SOUTH|WEST - . = ..() - -/obj/machinery/atmospherics/pipe/simple/hide(var/i) - if(istype(loc, /turf/simulated)) - invisibility = i ? 101 : 0 - queue_icon_update() - -/obj/machinery/atmospherics/pipe/simple/machinery_process() - if(!parent) //This should cut back on the overhead calling build_network thousands of times per cycle - ..() - else - . = PROCESS_KILL - -/obj/machinery/atmospherics/pipe/simple/check_pressure(pressure) - var/datum/gas_mixture/environment = loc.return_air() - - var/pressure_difference = pressure - environment.return_pressure() - - if(pressure_difference > maximum_pressure) - burst() - - else if(pressure_difference > fatigue_pressure) - //TODO: leak to turf, doing pfshhhhh - if(prob(5)) - burst() - - else return 1 - -/obj/machinery/atmospherics/pipe/simple/proc/burst() - src.visible_message("\The [src] bursts!"); - playsound(src.loc, 'sound/effects/bang.ogg', 25, 1) - var/datum/effect/effect/system/smoke_spread/smoke = new - smoke.set_up(1,0, src.loc, 0) - smoke.start() - qdel(src) - -/obj/machinery/atmospherics/pipe/simple/proc/normalize_dir() - if(dir==3) - set_dir(1) - else if(dir==12) - set_dir(4) - -/obj/machinery/atmospherics/pipe/simple/Destroy() - if(node1) - node1.disconnect(src) - if(node2) - node2.disconnect(src) - - node1 = null - node2 = null - - return ..() - -/obj/machinery/atmospherics/pipe/simple/pipeline_expansion() - return list(node1, node2) - -/obj/machinery/atmospherics/pipe/simple/change_color(var/new_color) - ..() - //for updating connected atmos device pipes (i.e. vents, manifolds, etc) - if(node1) - node1.update_underlays() - if(node2) - node2.update_underlays() - -/obj/machinery/atmospherics/pipe/simple/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - alpha = 255 - - cut_overlays() - - if(!node1 && !node2) - var/turf/T = get_turf(src) - new /obj/item/pipe(loc, make_from=src) - for (var/obj/machinery/meter/meter in T) - if (meter.target == src) - new /obj/item/pipe_meter(T) - qdel(meter) - qdel(src) - else if(node1 && node2) - add_overlay(icon_manager.get_atmos_icon("pipe", , pipe_color, "[pipe_icon]intact[icon_connect_type]")) - else - add_overlay(icon_manager.get_atmos_icon("pipe", , pipe_color, "[pipe_icon]exposed[node1?1:0][node2?1:0][icon_connect_type]")) - -/obj/machinery/atmospherics/pipe/simple/update_underlays() - return - -/obj/machinery/atmospherics/pipe/simple/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 - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node1 = target - break - for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node2 = target - break - - if(!node1 && !node2) - qdel(src) - return - - var/turf/T = loc - if(level == 1 && !T.is_plating()) hide(1) - queue_icon_update() - -/obj/machinery/atmospherics/pipe/simple/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node1 = null - - if(reference == node2) - if(istype(node2, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node2 = null - - update_icon() - - return null - -/obj/machinery/atmospherics/pipe/simple/visible - icon_state = "intact" - level = 2 - -/obj/machinery/atmospherics/pipe/simple/visible/scrubbers - name = "Scrubbers pipe" - desc = "A one meter section of scrubbers pipe" - icon_state = "intact-scrubbers" - connect_types = CONNECT_TYPE_SCRUBBER - layer = 2.38 - icon_connect_type = "-scrubbers" - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/simple/visible/supply - name = "Air supply pipe" - desc = "A one meter section of supply pipe" - icon_state = "intact-supply" - connect_types = CONNECT_TYPE_SUPPLY - layer = 2.39 - icon_connect_type = "-supply" - color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/simple/visible/yellow - color = PIPE_COLOR_YELLOW - -/obj/machinery/atmospherics/pipe/simple/visible/cyan - color = PIPE_COLOR_CYAN - -/obj/machinery/atmospherics/pipe/simple/visible/green - color = PIPE_COLOR_GREEN - -/obj/machinery/atmospherics/pipe/simple/visible/black - color = PIPE_COLOR_BLACK - -/obj/machinery/atmospherics/pipe/simple/visible/red - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/simple/visible/blue - color = PIPE_COLOR_BLUE - - -/obj/machinery/atmospherics/pipe/simple/hidden - icon_state = "intact" - level = 1 - alpha = 128 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game - -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers - name = "Scrubbers pipe" - desc = "A one meter section of scrubbers pipe" - icon_state = "intact-scrubbers" - connect_types = CONNECT_TYPE_SCRUBBER - layer = 2.38 - icon_connect_type = "-scrubbers" - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/simple/hidden/supply - name = "Air supply pipe" - desc = "A one meter section of supply pipe" - icon_state = "intact-supply" - connect_types = CONNECT_TYPE_SUPPLY - layer = 2.39 - icon_connect_type = "-supply" - color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/simple/hidden/yellow - color = PIPE_COLOR_YELLOW - -/obj/machinery/atmospherics/pipe/simple/hidden/cyan - color = PIPE_COLOR_CYAN - -/obj/machinery/atmospherics/pipe/simple/hidden/green - color = PIPE_COLOR_GREEN - -/obj/machinery/atmospherics/pipe/simple/hidden/black - color = PIPE_COLOR_BLACK - -/obj/machinery/atmospherics/pipe/simple/hidden/red - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/simple/hidden/blue - color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/simple/insulated - icon = 'icons/obj/atmospherics/red_pipe.dmi' - icon_state = "intact" - - minimum_temperature_difference = 10000 - thermal_conductivity = 0 - maximum_pressure = 1000*ONE_ATMOSPHERE - fatigue_pressure = 900*ONE_ATMOSPHERE - alert_pressure = 900*ONE_ATMOSPHERE - - level = 2 - - -/obj/machinery/atmospherics/pipe/manifold - icon = 'icons/atmos/manifold.dmi' - icon_state = "" - name = "pipe manifold" - desc = "A manifold composed of regular pipes" - - volume = ATMOS_DEFAULT_VOLUME_PIPE * 1.5 - - dir = SOUTH - initialize_directions = EAST|NORTH|WEST - - var/obj/machinery/atmospherics/node3 - - level = 1 - layer = 2.4 //under wires with their 2.44 - - gfi_layer_rotation = GFI_ROTATION_OVERDIR - -/obj/machinery/atmospherics/pipe/manifold/Initialize() - alpha = 255 - icon = null - - switch(dir) - if(NORTH) - initialize_directions = EAST|SOUTH|WEST - if(SOUTH) - initialize_directions = WEST|NORTH|EAST - if(EAST) - initialize_directions = SOUTH|WEST|NORTH - if(WEST) - initialize_directions = NORTH|EAST|SOUTH - . = ..() - -/obj/machinery/atmospherics/pipe/manifold/hide(var/i) - if(istype(loc, /turf/simulated)) - invisibility = i ? 101 : 0 - queue_icon_update() - -/obj/machinery/atmospherics/pipe/manifold/pipeline_expansion() - return list(node1, node2, node3) - -/obj/machinery/atmospherics/pipe/manifold/machinery_process() - if(!parent) - ..() - else - . = PROCESS_KILL - -/obj/machinery/atmospherics/pipe/manifold/Destroy() - if(node1) - node1.disconnect(src) - if(node2) - node2.disconnect(src) - if(node3) - node3.disconnect(src) - - node1 = null - node2 = null - node3 = null - - return ..() - -/obj/machinery/atmospherics/pipe/manifold/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node1 = null - - if(reference == node2) - if(istype(node2, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node2 = null - - if(reference == node3) - if(istype(node3, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node3 = null - - update_icon() - - ..() - -/obj/machinery/atmospherics/pipe/manifold/change_color(var/new_color) - ..() - //for updating connected atmos device pipes (i.e. vents, manifolds, etc) - if(node1) - node1.update_underlays() - if(node2) - node2.update_underlays() - if(node3) - node3.update_underlays() - -/obj/machinery/atmospherics/pipe/manifold/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - alpha = 255 - - if(!node1 && !node2 && !node3) - var/turf/T = get_turf(src) - new /obj/item/pipe(loc, make_from=src) - for (var/obj/machinery/meter/meter in T) - if (meter.target == src) - new /obj/item/pipe_meter(T) - qdel(meter) - qdel(src) - else - cut_overlays() - add_overlay(icon_manager.get_atmos_icon("manifold", , pipe_color, "core" + icon_connect_type)) - add_overlay(icon_manager.get_atmos_icon("manifold", , , "clamps" + icon_connect_type)) - - // Can't handle underlays with SSoverlay. - underlays.Cut() - - var/turf/T = get_turf(src) - var/list/directions = list(NORTH, SOUTH, EAST, WEST) - var/node1_direction = get_dir(src, node1) - var/node2_direction = get_dir(src, node2) - var/node3_direction = get_dir(src, node3) - - directions -= dir - - directions -= add_underlay(T,node1,node1_direction,icon_connect_type) - directions -= add_underlay(T,node2,node2_direction,icon_connect_type) - directions -= add_underlay(T,node3,node3_direction,icon_connect_type) - - for(var/D in directions) - add_underlay(T,,D,icon_connect_type) - - -/obj/machinery/atmospherics/pipe/manifold/update_underlays() - ..() - queue_icon_update() - -/obj/machinery/atmospherics/pipe/manifold/atmos_init() - var/connect_directions = (NORTH|SOUTH|EAST|WEST)&(~dir) - - for(var/direction in cardinal) - if(direction&connect_directions) - for(var/obj/machinery/atmospherics/target in get_step(src,direction)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node1 = target - connect_directions &= ~direction - break - if (node1) - break - - - for(var/direction in cardinal) - if(direction&connect_directions) - for(var/obj/machinery/atmospherics/target in get_step(src,direction)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node2 = target - connect_directions &= ~direction - break - if (node2) - break - - - for(var/direction in cardinal) - if(direction&connect_directions) - for(var/obj/machinery/atmospherics/target in get_step(src,direction)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node3 = target - connect_directions &= ~direction - break - if (node3) - break - - if(!node1 && !node2 && !node3) - qdel(src) - return - - var/turf/T = get_turf(src) - if(level == 1 && !T.is_plating()) hide(1) - queue_icon_update() - -/obj/machinery/atmospherics/pipe/manifold/visible - icon_state = "map" - level = 2 - -/obj/machinery/atmospherics/pipe/manifold/visible/scrubbers - name="Scrubbers pipe manifold" - desc = "A manifold composed of scrubbers pipes" - icon_state = "map-scrubbers" - connect_types = CONNECT_TYPE_SCRUBBER - layer = 2.38 - icon_connect_type = "-scrubbers" - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/manifold/visible/supply - name="Air supply pipe manifold" - desc = "A manifold composed of supply pipes" - icon_state = "map-supply" - connect_types = CONNECT_TYPE_SUPPLY - layer = 2.39 - icon_connect_type = "-supply" - color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/manifold/visible/yellow - color = PIPE_COLOR_YELLOW - -/obj/machinery/atmospherics/pipe/manifold/visible/cyan - color = PIPE_COLOR_CYAN - -/obj/machinery/atmospherics/pipe/manifold/visible/green - color = PIPE_COLOR_GREEN - -/obj/machinery/atmospherics/pipe/manifold/visible/black - color = PIPE_COLOR_BLACK - -/obj/machinery/atmospherics/pipe/manifold/visible/red - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/manifold/visible/blue - color = PIPE_COLOR_BLUE - - -/obj/machinery/atmospherics/pipe/manifold/hidden - icon_state = "map" - level = 1 - alpha = 128 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game - -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers - name="Scrubbers pipe manifold" - desc = "A manifold composed of scrubbers pipes" - icon_state = "map-scrubbers" - connect_types = CONNECT_TYPE_SCRUBBER - layer = 2.38 - icon_connect_type = "-scrubbers" - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/manifold/hidden/supply - name="Air supply pipe manifold" - desc = "A manifold composed of supply pipes" - icon_state = "map-supply" - connect_types = CONNECT_TYPE_SUPPLY - layer = 2.39 - icon_connect_type = "-supply" - color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/manifold/hidden/yellow - color = PIPE_COLOR_YELLOW - -/obj/machinery/atmospherics/pipe/manifold/hidden/cyan - color = PIPE_COLOR_CYAN - -/obj/machinery/atmospherics/pipe/manifold/hidden/green - color = PIPE_COLOR_GREEN - -/obj/machinery/atmospherics/pipe/manifold/hidden/black - color = PIPE_COLOR_BLACK - -/obj/machinery/atmospherics/pipe/manifold/hidden/red - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/manifold/hidden/blue - color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/manifold4w - icon = 'icons/atmos/manifold.dmi' - icon_state = "" - name = "4-way pipe manifold" - desc = "A manifold composed of regular pipes" - - volume = ATMOS_DEFAULT_VOLUME_PIPE * 2 - - dir = SOUTH - initialize_directions = NORTH|SOUTH|EAST|WEST - - var/obj/machinery/atmospherics/node3 - var/obj/machinery/atmospherics/node4 - - level = 1 - layer = 2.4 //under wires with their 2.44 - -/obj/machinery/atmospherics/pipe/manifold4w/Initialize() - . = ..() - alpha = 255 - icon = null - -/obj/machinery/atmospherics/pipe/manifold4w/pipeline_expansion() - return list(node1, node2, node3, node4) - -/obj/machinery/atmospherics/pipe/manifold4w/machinery_process() - if(!parent) - ..() - else - . = PROCESS_KILL - -/obj/machinery/atmospherics/pipe/manifold4w/Destroy() - if(node1) - node1.disconnect(src) - if(node2) - node2.disconnect(src) - if(node3) - node3.disconnect(src) - if(node4) - node4.disconnect(src) - - node1 = null - node2 = null - node3 = null - node4 = null - - return ..() - -/obj/machinery/atmospherics/pipe/manifold4w/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node1 = null - - if(reference == node2) - if(istype(node2, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node2 = null - - if(reference == node3) - if(istype(node3, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node3 = null - - if(reference == node4) - if(istype(node4, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node4 = null - - update_icon() - - ..() - -/obj/machinery/atmospherics/pipe/manifold4w/change_color(var/new_color) - ..() - //for updating connected atmos device pipes (i.e. vents, manifolds, etc) - if(node1) - node1.update_underlays() - if(node2) - node2.update_underlays() - if(node3) - node3.update_underlays() - if(node4) - node4.update_underlays() - -/obj/machinery/atmospherics/pipe/manifold4w/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - alpha = 255 - - if(!node1 && !node2 && !node3 && !node4) - var/turf/T = get_turf(src) - new /obj/item/pipe(loc, make_from=src) - for (var/obj/machinery/meter/meter in T) - if (meter.target == src) - new /obj/item/pipe_meter(T) - qdel(meter) - qdel(src) - else - cut_overlays() - add_overlay(icon_manager.get_atmos_icon("manifold", , pipe_color, "4way" + icon_connect_type)) - add_overlay(icon_manager.get_atmos_icon("manifold", , , "clamps_4way" + icon_connect_type)) - - underlays.Cut() - - /* - var/list/directions = list(NORTH, SOUTH, EAST, WEST) - - - directions -= add_underlay(node1) - directions -= add_underlay(node2) - directions -= add_underlay(node3) - directions -= add_underlay(node4) - - for(var/D in directions) - add_underlay(,D) - */ - - var/turf/T = get_turf(src) - var/list/directions = list(NORTH, SOUTH, EAST, WEST) - var/node1_direction = get_dir(src, node1) - var/node2_direction = get_dir(src, node2) - var/node3_direction = get_dir(src, node3) - var/node4_direction = get_dir(src, node4) - - directions -= dir - - directions -= add_underlay(T,node1,node1_direction,icon_connect_type) - directions -= add_underlay(T,node2,node2_direction,icon_connect_type) - directions -= add_underlay(T,node3,node3_direction,icon_connect_type) - directions -= add_underlay(T,node4,node4_direction,icon_connect_type) - - for(var/D in directions) - add_underlay(T,,D,icon_connect_type) - - -/obj/machinery/atmospherics/pipe/manifold4w/update_underlays() - ..() - queue_icon_update() - -/obj/machinery/atmospherics/pipe/manifold4w/hide(var/i) - if(istype(loc, /turf/simulated)) - invisibility = i ? 101 : 0 - queue_icon_update() - -/obj/machinery/atmospherics/pipe/manifold4w/atmos_init() - - for(var/obj/machinery/atmospherics/target in get_step(src,1)) - if(target.initialize_directions & 2) - if (check_connect_types(target,src)) - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,2)) - if(target.initialize_directions & 1) - if (check_connect_types(target,src)) - node2 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,4)) - if(target.initialize_directions & 8) - if (check_connect_types(target,src)) - node3 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,8)) - if(target.initialize_directions & 4) - if (check_connect_types(target,src)) - node4 = target - break - - if(!node1 && !node2 && !node3 && !node4) - qdel(src) - return - - var/turf/T = get_turf(src) - if(level == 1 && !T.is_plating()) hide(1) - queue_icon_update() - -/obj/machinery/atmospherics/pipe/manifold4w/visible - icon_state = "map_4way" - level = 2 - -/obj/machinery/atmospherics/pipe/manifold4w/visible/scrubbers - name="4-way scrubbers pipe manifold" - desc = "A manifold composed of scrubbers pipes" - icon_state = "map_4way-scrubbers" - connect_types = CONNECT_TYPE_SCRUBBER - layer = 2.38 - icon_connect_type = "-scrubbers" - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/manifold4w/visible/supply - name="4-way air supply pipe manifold" - desc = "A manifold composed of supply pipes" - icon_state = "map_4way-supply" - connect_types = CONNECT_TYPE_SUPPLY - layer = 2.39 - icon_connect_type = "-supply" - color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/manifold4w/visible/yellow - color = PIPE_COLOR_YELLOW - -/obj/machinery/atmospherics/pipe/manifold4w/visible/cyan - color = PIPE_COLOR_CYAN - -/obj/machinery/atmospherics/pipe/manifold4w/visible/green - color = PIPE_COLOR_GREEN - -/obj/machinery/atmospherics/pipe/manifold4w/visible/black - color = PIPE_COLOR_BLACK - -/obj/machinery/atmospherics/pipe/manifold4w/visible/red - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/manifold4w/visible/blue - color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/manifold4w/hidden - icon_state = "map_4way" - level = 1 - alpha = 128 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game - -/obj/machinery/atmospherics/pipe/manifold4w/hidden/scrubbers - name="4-way scrubbers pipe manifold" - desc = "A manifold composed of scrubbers pipes" - icon_state = "map_4way-scrubbers" - connect_types = CONNECT_TYPE_SCRUBBER - layer = 2.38 - icon_connect_type = "-scrubbers" - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/manifold4w/hidden/supply - name="4-way air supply pipe manifold" - desc = "A manifold composed of supply pipes" - icon_state = "map_4way-supply" - connect_types = CONNECT_TYPE_SUPPLY - layer = 2.39 - icon_connect_type = "-supply" - color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/manifold4w/hidden/yellow - color = PIPE_COLOR_YELLOW - -/obj/machinery/atmospherics/pipe/manifold4w/hidden/cyan - color = PIPE_COLOR_CYAN - -/obj/machinery/atmospherics/pipe/manifold4w/hidden/green - color = PIPE_COLOR_GREEN - -/obj/machinery/atmospherics/pipe/manifold4w/hidden/black - color = PIPE_COLOR_BLACK - -/obj/machinery/atmospherics/pipe/manifold4w/hidden/red - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/manifold4w/hidden/blue - color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/cap - name = "pipe endcap" - desc = "An endcap for pipes" - icon = 'icons/atmos/pipes.dmi' - icon_state = "" - level = 2 - layer = 2.4 //under wires with their 2.44 - - volume = 35 - - dir = SOUTH - initialize_directions = SOUTH - - var/obj/machinery/atmospherics/node - -/obj/machinery/atmospherics/pipe/cap/Initialize() - initialize_directions = dir - . = ..() - -/obj/machinery/atmospherics/pipe/cap/hide(var/i) - if(istype(loc, /turf/simulated)) - invisibility = i ? 101 : 0 - queue_icon_update() - -/obj/machinery/atmospherics/pipe/cap/pipeline_expansion() - return list(node) - -/obj/machinery/atmospherics/pipe/cap/machinery_process() - if(!parent) - ..() - else - . = PROCESS_KILL -/obj/machinery/atmospherics/pipe/cap/Destroy() - if(node) - node.disconnect(src) - - node = null - - return ..() - -/obj/machinery/atmospherics/pipe/cap/disconnect(obj/machinery/atmospherics/reference) - if(reference == node) - if(istype(node, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node = null - - update_icon() - - ..() - -/obj/machinery/atmospherics/pipe/cap/change_color(var/new_color) - ..() - //for updating connected atmos device pipes (i.e. vents, manifolds, etc) - if(node) - node.update_underlays() - -/obj/machinery/atmospherics/pipe/cap/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - alpha = 255 - - cut_overlays() - add_overlay(icon_manager.get_atmos_icon("pipe", , pipe_color, "cap")) - -/obj/machinery/atmospherics/pipe/cap/atmos_init() - for(var/obj/machinery/atmospherics/target in get_step(src, dir)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node = target - break - - var/turf/T = src.loc // hide if turf is not intact - if(level == 1 && !T.is_plating()) hide(1) - queue_icon_update() - -/obj/machinery/atmospherics/pipe/cap/visible - level = 2 - icon_state = "cap" - -/obj/machinery/atmospherics/pipe/cap/visible/scrubbers - name = "scrubbers pipe endcap" - desc = "An endcap for scrubbers pipes" - icon_state = "cap-scrubbers" - connect_types = CONNECT_TYPE_SCRUBBER - layer = 2.38 - icon_connect_type = "-scrubbers" - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/cap/visible/supply - name = "supply pipe endcap" - desc = "An endcap for supply pipes" - icon_state = "cap-supply" - connect_types = CONNECT_TYPE_SUPPLY - layer = 2.39 - icon_connect_type = "-supply" - color = PIPE_COLOR_BLUE - -/obj/machinery/atmospherics/pipe/cap/hidden - level = 1 - icon_state = "cap" - alpha = 128 - -/obj/machinery/atmospherics/pipe/cap/hidden/scrubbers - name = "scrubbers pipe endcap" - desc = "An endcap for scrubbers pipes" - icon_state = "cap-f-scrubbers" - connect_types = CONNECT_TYPE_SCRUBBER - layer = 2.38 - icon_connect_type = "-scrubbers" - color = PIPE_COLOR_RED - -/obj/machinery/atmospherics/pipe/cap/hidden/supply - name = "supply pipe endcap" - desc = "An endcap for supply pipes" - icon_state = "cap-f-supply" - connect_types = CONNECT_TYPE_SUPPLY - layer = 2.39 - icon_connect_type = "-supply" - color = PIPE_COLOR_BLUE - - -/obj/machinery/atmospherics/pipe/tank - icon = 'icons/atmos/tank.dmi' - icon_state = "air_map" - - name = "Pressure Tank" - desc = "A large vessel containing pressurized gas." - - volume = 10000 //in liters, 1 meters by 1 meters by 2 meters ~tweaked it a little to simulate a pressure tank without needing to recode them yet - var/start_pressure = 25*ONE_ATMOSPHERE - - level = 1 - dir = SOUTH - initialize_directions = SOUTH - density = 1 - -/obj/machinery/atmospherics/pipe/tank/Initialize() - icon_state = "air" - initialize_directions = dir - . = ..() - -/obj/machinery/atmospherics/pipe/tank/machinery_process() - if(!parent) - ..() - else - . = PROCESS_KILL - -/obj/machinery/atmospherics/pipe/tank/Destroy() - if(node1) - node1.disconnect(src) - - node1 = null - - return ..() - -/obj/machinery/atmospherics/pipe/tank/pipeline_expansion() - return list(node1) - -/obj/machinery/atmospherics/pipe/tank/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, dir) - -/obj/machinery/atmospherics/pipe/tank/hide() - update_underlays() - -/obj/machinery/atmospherics/pipe/tank/atmos_init() - var/connect_direction = dir - - for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node1 = target - break - - update_underlays() - -/obj/machinery/atmospherics/pipe/tank/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node1 = null - - update_underlays() - - return null - -/obj/machinery/atmospherics/pipe/tank/attackby(var/obj/item/W as obj, var/mob/user as mob) - if(istype(W, /obj/item/device/pipe_painter)) - return - - if(istype(W, /obj/item/device/analyzer) && in_range(user, src)) - var/obj/item/device/analyzer/A = W - A.analyze_gases(src, user) - -/obj/machinery/atmospherics/pipe/tank/air - name = "Pressure Tank (Air)" - icon_state = "air_map" - -/obj/machinery/atmospherics/pipe/tank/air/Initialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T20C - - air_temporary.adjust_multi("oxygen", (start_pressure*O2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature), \ - "nitrogen",(start_pressure*N2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - - . = ..() - icon_state = "air" - -/obj/machinery/atmospherics/pipe/tank/oxygen - name = "Pressure Tank (Oxygen)" - icon_state = "o2_map" - -/obj/machinery/atmospherics/pipe/tank/oxygen/Initialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T20C - - air_temporary.adjust_gas("oxygen", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - . = ..() - icon_state = "o2" - -/obj/machinery/atmospherics/pipe/tank/nitrogen - name = "Pressure Tank (Nitrogen)" - icon_state = "n2_map" - -/obj/machinery/atmospherics/pipe/tank/nitrogen/Initialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T20C - - air_temporary.adjust_gas("nitrogen", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - . = ..() - icon_state = "n2" - -/obj/machinery/atmospherics/pipe/tank/carbon_dioxide - name = "Pressure Tank (Carbon Dioxide)" - icon_state = "co2_map" - -/obj/machinery/atmospherics/pipe/tank/carbon_dioxide/Initialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T20C - - air_temporary.adjust_gas("carbon_dioxide", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - . = ..() - icon_state = "co2" - -/obj/machinery/atmospherics/pipe/tank/phoron - name = "Pressure Tank (Phoron)" - icon_state = "phoron_map" - -/obj/machinery/atmospherics/pipe/tank/phoron/Initialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T20C - - air_temporary.adjust_gas("phoron", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - . = ..() - icon_state = "phoron" - -/obj/machinery/atmospherics/pipe/tank/nitrous_oxide - name = "Pressure Tank (Nitrous Oxide)" - icon_state = "n2o_map" - -/obj/machinery/atmospherics/pipe/tank/nitrous_oxide/Initialize() - air_temporary = new - air_temporary.volume = volume - air_temporary.temperature = T0C - - air_temporary.adjust_gas("sleeping_agent", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) - - . = ..() - icon_state = "n2o" - -/obj/machinery/atmospherics/pipe/vent - icon = 'icons/obj/atmospherics/pipe_vent.dmi' - icon_state = "intact" - - name = "Vent" - desc = "A large air vent" - - level = 1 - - volume = 250 - - dir = SOUTH - initialize_directions = SOUTH - - var/build_killswitch = 1 - -/obj/machinery/atmospherics/pipe/vent/Initialize() - initialize_directions = dir - . = ..() - -/obj/machinery/atmospherics/pipe/vent/high_volume - name = "Larger vent" - volume = 1000 - -/obj/machinery/atmospherics/pipe/vent/machinery_process() - if(!parent) - if(build_killswitch <= 0) - . = PROCESS_KILL - else - build_killswitch-- - ..() - return - else - parent.mingle_with_turf(loc, volume) - -/obj/machinery/atmospherics/pipe/vent/Destroy() - if(node1) - node1.disconnect(src) - - node1 = null - - return ..() - -/obj/machinery/atmospherics/pipe/vent/pipeline_expansion() - return list(node1) - -/obj/machinery/atmospherics/pipe/vent/update_icon() - if(node1) - icon_state = "intact" - - set_dir(get_dir(src, node1)) - - else - icon_state = "exposed" - -/obj/machinery/atmospherics/pipe/vent/atmos_init() - var/connect_direction = dir - - for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction)) - if(target.initialize_directions & get_dir(target,src)) - if (check_connect_types(target,src)) - node1 = target - break - - queue_icon_update() - -/obj/machinery/atmospherics/pipe/vent/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - QDEL_NULL(parent) - node1 = null - - update_icon() - - return null - -/obj/machinery/atmospherics/pipe/vent/hide(var/i) //to make the little pipe section invisible, the icon changes. - if(node1) - icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]intact" - set_dir(get_dir(src, node1)) - else - icon_state = "exposed" - - -/obj/machinery/atmospherics/pipe/simple/visible/universal - name="Universal pipe adapter" - desc = "An adapter for regular, supply and scrubbers pipes" - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER - icon_state = "map_universal" - gfi_layer_rotation = GFI_ROTATION_OVERDIR - -/obj/machinery/atmospherics/pipe/simple/visible/universal/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - alpha = 255 - - cut_overlays() - add_overlay(icon_manager.get_atmos_icon("pipe", , pipe_color, "universal")) - underlays.Cut() - - if (node1) - universal_underlays(node1) - if(node2) - universal_underlays(node2) - else - var/node1_dir = get_dir(node1,src) - universal_underlays(,node1_dir) - else if (node2) - universal_underlays(node2) - else - universal_underlays(,dir) - universal_underlays(,turn(dir, -180)) - -/obj/machinery/atmospherics/pipe/simple/visible/universal/update_underlays() - ..() - queue_icon_update() - - - -/obj/machinery/atmospherics/pipe/simple/hidden/universal - name="Universal pipe adapter" - desc = "An adapter for regular, supply and scrubbers pipes" - connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER - icon_state = "map_universal" - gfi_layer_rotation = GFI_ROTATION_OVERDIR - -/obj/machinery/atmospherics/pipe/simple/hidden/universal/update_icon(var/safety = 0) - if(!check_icon_cache()) - return - - alpha = 255 - - cut_overlays() - add_overlay(icon_manager.get_atmos_icon("pipe", , pipe_color, "universal")) - - underlays.Cut() - - if (node1) - universal_underlays(node1) - if(node2) - universal_underlays(node2) - else - var/node2_dir = turn(get_dir(src,node1),-180) - universal_underlays(,node2_dir) - else if (node2) - universal_underlays(node2) - var/node1_dir = turn(get_dir(src,node2),-180) - universal_underlays(,node1_dir) - else - universal_underlays(,dir) - universal_underlays(,turn(dir, -180)) - -/obj/machinery/atmospherics/pipe/simple/hidden/universal/update_underlays() - ..() - queue_icon_update() - -/obj/machinery/atmospherics/proc/universal_underlays(var/obj/machinery/atmospherics/node, var/direction) - var/turf/T = loc - if(node) - var/node_dir = get_dir(src,node) - if(node.icon_connect_type == "-supply") - add_underlay_adapter(T, , node_dir, "") - add_underlay_adapter(T, node, node_dir, "-supply") - add_underlay_adapter(T, , node_dir, "-scrubbers") - else if (node.icon_connect_type == "-scrubbers") - add_underlay_adapter(T, , node_dir, "") - add_underlay_adapter(T, , node_dir, "-supply") - add_underlay_adapter(T, node, node_dir, "-scrubbers") - else - add_underlay_adapter(T, node, node_dir, "") - add_underlay_adapter(T, , node_dir, "-supply") - add_underlay_adapter(T, , node_dir, "-scrubbers") - else - add_underlay_adapter(T, , direction, "-supply") - add_underlay_adapter(T, , direction, "-scrubbers") - add_underlay_adapter(T, , direction, "") - -/obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays - if(node) - if(!T.is_plating() && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) - else - underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) - else - underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type) +/obj/machinery/atmospherics/pipe + + var/datum/gas_mixture/air_temporary // used when reconstructing a pipeline that broke + var/datum/pipeline/parent + var/volume = 0 + force = 20 + + layer = PIPE_LAYER + use_power = 0 + + var/alert_pressure = 80*ONE_ATMOSPHERE + //minimum pressure before check_pressure(...) should be called + + can_buckle = 1 + buckle_require_restraints = 1 + buckle_lying = -1 + +/obj/machinery/atmospherics/pipe/drain_power() + return -1 + +/obj/machinery/atmospherics/pipe/Initialize() + if(istype(get_turf(src), /turf/simulated/wall) || istype(get_turf(src), /turf/unsimulated/wall)) + level = 1 + . = ..() + +/obj/machinery/atmospherics/pipe/hides_under_flooring() + return level != 2 + +/obj/machinery/atmospherics/pipe/proc/pipeline_expansion() + return null + +/obj/machinery/atmospherics/pipe/proc/check_pressure(pressure) + //Return 1 if parent should continue checking other pipes + //Return null if parent should stop checking other pipes. Recall: qdel(src) will by default return null + + return 1 + +/obj/machinery/atmospherics/pipe/return_air() + if(!parent) + parent = new /datum/pipeline() + parent.build_pipeline(src) + + return parent.air + +/obj/machinery/atmospherics/pipe/build_network() + if(!parent) + parent = new /datum/pipeline() + parent.build_pipeline(src) + + return parent.return_network() + +/obj/machinery/atmospherics/pipe/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) + if(!parent) + parent = new /datum/pipeline() + parent.build_pipeline(src) + + return parent.network_expand(new_network, reference) + +/obj/machinery/atmospherics/pipe/return_network(obj/machinery/atmospherics/reference) + if(!parent) + parent = new /datum/pipeline() + parent.build_pipeline(src) + + return parent.return_network(reference) + +/obj/machinery/atmospherics/pipe/Destroy() + QDEL_NULL(parent) + if(air_temporary) + loc.assume_air(air_temporary) + QDEL_NULL(air_temporary) + + return ..() + +/obj/machinery/atmospherics/pipe/attackby(var/obj/item/W as obj, var/mob/user as mob) + if (istype(src, /obj/machinery/atmospherics/pipe/tank)) + return ..() + if (istype(src, /obj/machinery/atmospherics/pipe/vent)) + return ..() + + if(istype(W,/obj/item/device/pipe_painter)) + return 0 + + if (!W.iswrench() && !istype(W, /obj/item/pipewrench)) + 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 + 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) + if(!istype(W, /obj/item/pipewrench)) + to_chat(user, "You cannot unwrench \the [src], it is too exerted due to internal pressure.") + add_fingerprint(user) + return 1 + else + to_chat(user, "You struggle to unwrench \the [src] with your pipe wrench.") + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if (do_after(user, istype(W, /obj/item/pipewrench) ? 80/W.toolspeed : 40/W.toolspeed, act_target = src)) + user.visible_message( \ + "\The [user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear a ratchet.") + new /obj/item/pipe(loc, make_from=src) + for (var/obj/machinery/meter/meter in T) + if (meter.target == src) + new /obj/item/pipe_meter(T) + qdel(meter) + qdel(src) + +/obj/machinery/atmospherics/proc/change_color(var/new_color) + //only pass valid pipe colors please ~otherwise your pipe will turn invisible + if(!pipe_color_check(new_color)) + return + + pipe_color = new_color + update_icon() + +/* +/obj/machinery/atmospherics/pipe/add_underlay(var/obj/machinery/atmospherics/node, var/direction) + if(istype(src, /obj/machinery/atmospherics/pipe/tank)) //todo: move tanks to unary devices + return ..() + + if(node) + var/temp_dir = get_dir(src, node) + underlays += icon_manager.get_atmos_icon("pipe_underlay_intact", temp_dir, color_cache_name(node)) + return temp_dir + else if(direction) + underlays += icon_manager.get_atmos_icon("pipe_underlay_exposed", direction, pipe_color) + else + return null +*/ + +/obj/machinery/atmospherics/pipe/color_cache_name(var/obj/machinery/atmospherics/node) + if(istype(src, /obj/machinery/atmospherics/pipe/tank)) + return ..() + + if(istype(node, /obj/machinery/atmospherics/pipe/manifold) || istype(node, /obj/machinery/atmospherics/pipe/manifold4w)) + if(pipe_color == node.pipe_color) + return node.pipe_color + else + return null + else if(istype(node, /obj/machinery/atmospherics/pipe/simple)) + return node.pipe_color + else + return pipe_color + +/obj/machinery/atmospherics/pipe/simple + icon = 'icons/atmos/pipes.dmi' + icon_state = "" + var/pipe_icon = "" //what kind of pipe it is and from which dmi is the icon manager getting its icons, "" for simple pipes, "hepipe" for HE pipes, "hejunction" for HE junctions + name = "pipe" + desc = "A one meter section of regular pipe" + + volume = ATMOS_DEFAULT_VOLUME_PIPE + + dir = SOUTH + initialize_directions = SOUTH|NORTH + + var/minimum_temperature_difference = 300 + var/thermal_conductivity = 0 //WALL_HEAT_TRANSFER_COEFFICIENT No + + var/maximum_pressure = 70*ONE_ATMOSPHERE + var/fatigue_pressure = 55*ONE_ATMOSPHERE + alert_pressure = 55*ONE_ATMOSPHERE + + level = 1 + gfi_layer_rotation = GFI_ROTATION_DEFDIR + +/obj/machinery/atmospherics/pipe/simple/Initialize() + + // Pipe colors and icon states are handled by an image cache - so color and icon should + // be null. For mapping purposes color is defined in the object definitions. + icon = null + alpha = 255 + + switch(dir) + if(SOUTH || NORTH) + initialize_directions = SOUTH|NORTH + if(EAST || WEST) + initialize_directions = EAST|WEST + if(NORTHEAST) + initialize_directions = NORTH|EAST + if(NORTHWEST) + initialize_directions = NORTH|WEST + if(SOUTHEAST) + initialize_directions = SOUTH|EAST + if(SOUTHWEST) + initialize_directions = SOUTH|WEST + . = ..() + +/obj/machinery/atmospherics/pipe/simple/hide(var/i) + if(istype(loc, /turf/simulated)) + invisibility = i ? 101 : 0 + queue_icon_update() + +/obj/machinery/atmospherics/pipe/simple/machinery_process() + if(!parent) //This should cut back on the overhead calling build_network thousands of times per cycle + ..() + else + . = PROCESS_KILL + +/obj/machinery/atmospherics/pipe/simple/check_pressure(pressure) + var/datum/gas_mixture/environment = loc.return_air() + + var/pressure_difference = pressure - environment.return_pressure() + + if(pressure_difference > maximum_pressure) + burst() + + else if(pressure_difference > fatigue_pressure) + //TODO: leak to turf, doing pfshhhhh + if(prob(5)) + burst() + + else return 1 + +/obj/machinery/atmospherics/pipe/simple/proc/burst() + src.visible_message("\The [src] bursts!"); + playsound(src.loc, 'sound/effects/bang.ogg', 25, 1) + var/datum/effect/effect/system/smoke_spread/smoke = new + smoke.set_up(1,0, src.loc, 0) + smoke.start() + qdel(src) + +/obj/machinery/atmospherics/pipe/simple/proc/normalize_dir() + if(dir==3) + set_dir(1) + else if(dir==12) + set_dir(4) + +/obj/machinery/atmospherics/pipe/simple/Destroy() + if(node1) + node1.disconnect(src) + if(node2) + node2.disconnect(src) + + node1 = null + node2 = null + + return ..() + +/obj/machinery/atmospherics/pipe/simple/pipeline_expansion() + return list(node1, node2) + +/obj/machinery/atmospherics/pipe/simple/change_color(var/new_color) + ..() + //for updating connected atmos device pipes (i.e. vents, manifolds, etc) + if(node1) + node1.update_underlays() + if(node2) + node2.update_underlays() + +/obj/machinery/atmospherics/pipe/simple/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + alpha = 255 + + cut_overlays() + + if(!node1 && !node2) + var/turf/T = get_turf(src) + new /obj/item/pipe(loc, make_from=src) + for (var/obj/machinery/meter/meter in T) + if (meter.target == src) + new /obj/item/pipe_meter(T) + qdel(meter) + qdel(src) + else if(node1 && node2) + add_overlay(icon_manager.get_atmos_icon("pipe", , pipe_color, "[pipe_icon]intact[icon_connect_type]")) + else + add_overlay(icon_manager.get_atmos_icon("pipe", , pipe_color, "[pipe_icon]exposed[node1?1:0][node2?1:0][icon_connect_type]")) + +/obj/machinery/atmospherics/pipe/simple/update_underlays() + return + +/obj/machinery/atmospherics/pipe/simple/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 + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node1 = target + break + for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node2 = target + break + + if(!node1 && !node2) + qdel(src) + return + + var/turf/T = loc + if(level == 1 && !T.is_plating()) hide(1) + queue_icon_update() + +/obj/machinery/atmospherics/pipe/simple/disconnect(obj/machinery/atmospherics/reference) + if(reference == node1) + if(istype(node1, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node1 = null + + if(reference == node2) + if(istype(node2, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node2 = null + + update_icon() + + return null + +/obj/machinery/atmospherics/pipe/simple/visible + icon_state = "intact" + level = 2 + +/obj/machinery/atmospherics/pipe/simple/visible/scrubbers + name = "Scrubbers pipe" + desc = "A one meter section of scrubbers pipe" + icon_state = "intact-scrubbers" + connect_types = CONNECT_TYPE_SCRUBBER + layer = 2.38 + icon_connect_type = "-scrubbers" + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/simple/visible/supply + name = "Air supply pipe" + desc = "A one meter section of supply pipe" + icon_state = "intact-supply" + connect_types = CONNECT_TYPE_SUPPLY + layer = 2.39 + icon_connect_type = "-supply" + color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/simple/visible/yellow + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/simple/visible/cyan + color = PIPE_COLOR_CYAN + +/obj/machinery/atmospherics/pipe/simple/visible/green + color = PIPE_COLOR_GREEN + +/obj/machinery/atmospherics/pipe/simple/visible/black + color = PIPE_COLOR_BLACK + +/obj/machinery/atmospherics/pipe/simple/visible/red + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/simple/visible/blue + color = PIPE_COLOR_BLUE + + +/obj/machinery/atmospherics/pipe/simple/hidden + icon_state = "intact" + level = 1 + alpha = 128 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game + +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers + name = "Scrubbers pipe" + desc = "A one meter section of scrubbers pipe" + icon_state = "intact-scrubbers" + connect_types = CONNECT_TYPE_SCRUBBER + layer = 2.38 + icon_connect_type = "-scrubbers" + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/simple/hidden/supply + name = "Air supply pipe" + desc = "A one meter section of supply pipe" + icon_state = "intact-supply" + connect_types = CONNECT_TYPE_SUPPLY + layer = 2.39 + icon_connect_type = "-supply" + color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/simple/hidden/yellow + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/simple/hidden/cyan + color = PIPE_COLOR_CYAN + +/obj/machinery/atmospherics/pipe/simple/hidden/green + color = PIPE_COLOR_GREEN + +/obj/machinery/atmospherics/pipe/simple/hidden/black + color = PIPE_COLOR_BLACK + +/obj/machinery/atmospherics/pipe/simple/hidden/red + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/simple/hidden/blue + color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/simple/insulated + icon = 'icons/obj/atmospherics/red_pipe.dmi' + icon_state = "intact" + + minimum_temperature_difference = 10000 + thermal_conductivity = 0 + maximum_pressure = 1000*ONE_ATMOSPHERE + fatigue_pressure = 900*ONE_ATMOSPHERE + alert_pressure = 900*ONE_ATMOSPHERE + + level = 2 + + +/obj/machinery/atmospherics/pipe/manifold + icon = 'icons/atmos/manifold.dmi' + icon_state = "" + name = "pipe manifold" + desc = "A manifold composed of regular pipes" + + volume = ATMOS_DEFAULT_VOLUME_PIPE * 1.5 + + dir = SOUTH + initialize_directions = EAST|NORTH|WEST + + var/obj/machinery/atmospherics/node3 + + level = 1 + layer = 2.4 //under wires with their 2.44 + + gfi_layer_rotation = GFI_ROTATION_OVERDIR + +/obj/machinery/atmospherics/pipe/manifold/Initialize() + alpha = 255 + icon = null + + switch(dir) + if(NORTH) + initialize_directions = EAST|SOUTH|WEST + if(SOUTH) + initialize_directions = WEST|NORTH|EAST + if(EAST) + initialize_directions = SOUTH|WEST|NORTH + if(WEST) + initialize_directions = NORTH|EAST|SOUTH + . = ..() + +/obj/machinery/atmospherics/pipe/manifold/hide(var/i) + if(istype(loc, /turf/simulated)) + invisibility = i ? 101 : 0 + queue_icon_update() + +/obj/machinery/atmospherics/pipe/manifold/pipeline_expansion() + return list(node1, node2, node3) + +/obj/machinery/atmospherics/pipe/manifold/machinery_process() + if(!parent) + ..() + else + . = PROCESS_KILL + +/obj/machinery/atmospherics/pipe/manifold/Destroy() + if(node1) + node1.disconnect(src) + if(node2) + node2.disconnect(src) + if(node3) + node3.disconnect(src) + + node1 = null + node2 = null + node3 = null + + return ..() + +/obj/machinery/atmospherics/pipe/manifold/disconnect(obj/machinery/atmospherics/reference) + if(reference == node1) + if(istype(node1, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node1 = null + + if(reference == node2) + if(istype(node2, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node2 = null + + if(reference == node3) + if(istype(node3, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node3 = null + + update_icon() + + ..() + +/obj/machinery/atmospherics/pipe/manifold/change_color(var/new_color) + ..() + //for updating connected atmos device pipes (i.e. vents, manifolds, etc) + if(node1) + node1.update_underlays() + if(node2) + node2.update_underlays() + if(node3) + node3.update_underlays() + +/obj/machinery/atmospherics/pipe/manifold/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + alpha = 255 + + if(!node1 && !node2 && !node3) + var/turf/T = get_turf(src) + new /obj/item/pipe(loc, make_from=src) + for (var/obj/machinery/meter/meter in T) + if (meter.target == src) + new /obj/item/pipe_meter(T) + qdel(meter) + qdel(src) + else + cut_overlays() + add_overlay(icon_manager.get_atmos_icon("manifold", , pipe_color, "core" + icon_connect_type)) + add_overlay(icon_manager.get_atmos_icon("manifold", , , "clamps" + icon_connect_type)) + + // Can't handle underlays with SSoverlay. + underlays.Cut() + + var/turf/T = get_turf(src) + var/list/directions = list(NORTH, SOUTH, EAST, WEST) + var/node1_direction = get_dir(src, node1) + var/node2_direction = get_dir(src, node2) + var/node3_direction = get_dir(src, node3) + + directions -= dir + + directions -= add_underlay(T,node1,node1_direction,icon_connect_type) + directions -= add_underlay(T,node2,node2_direction,icon_connect_type) + directions -= add_underlay(T,node3,node3_direction,icon_connect_type) + + for(var/D in directions) + add_underlay(T,,D,icon_connect_type) + + +/obj/machinery/atmospherics/pipe/manifold/update_underlays() + ..() + queue_icon_update() + +/obj/machinery/atmospherics/pipe/manifold/atmos_init() + var/connect_directions = (NORTH|SOUTH|EAST|WEST)&(~dir) + + for(var/direction in cardinal) + if(direction&connect_directions) + for(var/obj/machinery/atmospherics/target in get_step(src,direction)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node1 = target + connect_directions &= ~direction + break + if (node1) + break + + + for(var/direction in cardinal) + if(direction&connect_directions) + for(var/obj/machinery/atmospherics/target in get_step(src,direction)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node2 = target + connect_directions &= ~direction + break + if (node2) + break + + + for(var/direction in cardinal) + if(direction&connect_directions) + for(var/obj/machinery/atmospherics/target in get_step(src,direction)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node3 = target + connect_directions &= ~direction + break + if (node3) + break + + if(!node1 && !node2 && !node3) + qdel(src) + return + + var/turf/T = get_turf(src) + if(level == 1 && !T.is_plating()) hide(1) + queue_icon_update() + +/obj/machinery/atmospherics/pipe/manifold/visible + icon_state = "map" + level = 2 + +/obj/machinery/atmospherics/pipe/manifold/visible/scrubbers + name="Scrubbers pipe manifold" + desc = "A manifold composed of scrubbers pipes" + icon_state = "map-scrubbers" + connect_types = CONNECT_TYPE_SCRUBBER + layer = 2.38 + icon_connect_type = "-scrubbers" + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/manifold/visible/supply + name="Air supply pipe manifold" + desc = "A manifold composed of supply pipes" + icon_state = "map-supply" + connect_types = CONNECT_TYPE_SUPPLY + layer = 2.39 + icon_connect_type = "-supply" + color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/manifold/visible/yellow + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/manifold/visible/cyan + color = PIPE_COLOR_CYAN + +/obj/machinery/atmospherics/pipe/manifold/visible/green + color = PIPE_COLOR_GREEN + +/obj/machinery/atmospherics/pipe/manifold/visible/black + color = PIPE_COLOR_BLACK + +/obj/machinery/atmospherics/pipe/manifold/visible/red + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/manifold/visible/blue + color = PIPE_COLOR_BLUE + + +/obj/machinery/atmospherics/pipe/manifold/hidden + icon_state = "map" + level = 1 + alpha = 128 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game + +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers + name="Scrubbers pipe manifold" + desc = "A manifold composed of scrubbers pipes" + icon_state = "map-scrubbers" + connect_types = CONNECT_TYPE_SCRUBBER + layer = 2.38 + icon_connect_type = "-scrubbers" + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/manifold/hidden/supply + name="Air supply pipe manifold" + desc = "A manifold composed of supply pipes" + icon_state = "map-supply" + connect_types = CONNECT_TYPE_SUPPLY + layer = 2.39 + icon_connect_type = "-supply" + color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/manifold/hidden/yellow + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/manifold/hidden/cyan + color = PIPE_COLOR_CYAN + +/obj/machinery/atmospherics/pipe/manifold/hidden/green + color = PIPE_COLOR_GREEN + +/obj/machinery/atmospherics/pipe/manifold/hidden/black + color = PIPE_COLOR_BLACK + +/obj/machinery/atmospherics/pipe/manifold/hidden/red + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/manifold/hidden/blue + color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/manifold4w + icon = 'icons/atmos/manifold.dmi' + icon_state = "" + name = "4-way pipe manifold" + desc = "A manifold composed of regular pipes" + + volume = ATMOS_DEFAULT_VOLUME_PIPE * 2 + + dir = SOUTH + initialize_directions = NORTH|SOUTH|EAST|WEST + + var/obj/machinery/atmospherics/node3 + var/obj/machinery/atmospherics/node4 + + level = 1 + layer = 2.4 //under wires with their 2.44 + +/obj/machinery/atmospherics/pipe/manifold4w/Initialize() + . = ..() + alpha = 255 + icon = null + +/obj/machinery/atmospherics/pipe/manifold4w/pipeline_expansion() + return list(node1, node2, node3, node4) + +/obj/machinery/atmospherics/pipe/manifold4w/machinery_process() + if(!parent) + ..() + else + . = PROCESS_KILL + +/obj/machinery/atmospherics/pipe/manifold4w/Destroy() + if(node1) + node1.disconnect(src) + if(node2) + node2.disconnect(src) + if(node3) + node3.disconnect(src) + if(node4) + node4.disconnect(src) + + node1 = null + node2 = null + node3 = null + node4 = null + + return ..() + +/obj/machinery/atmospherics/pipe/manifold4w/disconnect(obj/machinery/atmospherics/reference) + if(reference == node1) + if(istype(node1, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node1 = null + + if(reference == node2) + if(istype(node2, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node2 = null + + if(reference == node3) + if(istype(node3, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node3 = null + + if(reference == node4) + if(istype(node4, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node4 = null + + update_icon() + + ..() + +/obj/machinery/atmospherics/pipe/manifold4w/change_color(var/new_color) + ..() + //for updating connected atmos device pipes (i.e. vents, manifolds, etc) + if(node1) + node1.update_underlays() + if(node2) + node2.update_underlays() + if(node3) + node3.update_underlays() + if(node4) + node4.update_underlays() + +/obj/machinery/atmospherics/pipe/manifold4w/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + alpha = 255 + + if(!node1 && !node2 && !node3 && !node4) + var/turf/T = get_turf(src) + new /obj/item/pipe(loc, make_from=src) + for (var/obj/machinery/meter/meter in T) + if (meter.target == src) + new /obj/item/pipe_meter(T) + qdel(meter) + qdel(src) + else + cut_overlays() + add_overlay(icon_manager.get_atmos_icon("manifold", , pipe_color, "4way" + icon_connect_type)) + add_overlay(icon_manager.get_atmos_icon("manifold", , , "clamps_4way" + icon_connect_type)) + + underlays.Cut() + + /* + var/list/directions = list(NORTH, SOUTH, EAST, WEST) + + + directions -= add_underlay(node1) + directions -= add_underlay(node2) + directions -= add_underlay(node3) + directions -= add_underlay(node4) + + for(var/D in directions) + add_underlay(,D) + */ + + var/turf/T = get_turf(src) + var/list/directions = list(NORTH, SOUTH, EAST, WEST) + var/node1_direction = get_dir(src, node1) + var/node2_direction = get_dir(src, node2) + var/node3_direction = get_dir(src, node3) + var/node4_direction = get_dir(src, node4) + + directions -= dir + + directions -= add_underlay(T,node1,node1_direction,icon_connect_type) + directions -= add_underlay(T,node2,node2_direction,icon_connect_type) + directions -= add_underlay(T,node3,node3_direction,icon_connect_type) + directions -= add_underlay(T,node4,node4_direction,icon_connect_type) + + for(var/D in directions) + add_underlay(T,,D,icon_connect_type) + + +/obj/machinery/atmospherics/pipe/manifold4w/update_underlays() + ..() + queue_icon_update() + +/obj/machinery/atmospherics/pipe/manifold4w/hide(var/i) + if(istype(loc, /turf/simulated)) + invisibility = i ? 101 : 0 + queue_icon_update() + +/obj/machinery/atmospherics/pipe/manifold4w/atmos_init() + + for(var/obj/machinery/atmospherics/target in get_step(src,1)) + if(target.initialize_directions & 2) + if (check_connect_types(target,src)) + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,2)) + if(target.initialize_directions & 1) + if (check_connect_types(target,src)) + node2 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,4)) + if(target.initialize_directions & 8) + if (check_connect_types(target,src)) + node3 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,8)) + if(target.initialize_directions & 4) + if (check_connect_types(target,src)) + node4 = target + break + + if(!node1 && !node2 && !node3 && !node4) + qdel(src) + return + + var/turf/T = get_turf(src) + if(level == 1 && !T.is_plating()) hide(1) + queue_icon_update() + +/obj/machinery/atmospherics/pipe/manifold4w/visible + icon_state = "map_4way" + level = 2 + +/obj/machinery/atmospherics/pipe/manifold4w/visible/scrubbers + name="4-way scrubbers pipe manifold" + desc = "A manifold composed of scrubbers pipes" + icon_state = "map_4way-scrubbers" + connect_types = CONNECT_TYPE_SCRUBBER + layer = 2.38 + icon_connect_type = "-scrubbers" + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/manifold4w/visible/supply + name="4-way air supply pipe manifold" + desc = "A manifold composed of supply pipes" + icon_state = "map_4way-supply" + connect_types = CONNECT_TYPE_SUPPLY + layer = 2.39 + icon_connect_type = "-supply" + color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/manifold4w/visible/yellow + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/manifold4w/visible/cyan + color = PIPE_COLOR_CYAN + +/obj/machinery/atmospherics/pipe/manifold4w/visible/green + color = PIPE_COLOR_GREEN + +/obj/machinery/atmospherics/pipe/manifold4w/visible/black + color = PIPE_COLOR_BLACK + +/obj/machinery/atmospherics/pipe/manifold4w/visible/red + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/manifold4w/visible/blue + color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/manifold4w/hidden + icon_state = "map_4way" + level = 1 + alpha = 128 //set for the benefit of mapping - this is reset to opaque when the pipe is spawned in game + +/obj/machinery/atmospherics/pipe/manifold4w/hidden/scrubbers + name="4-way scrubbers pipe manifold" + desc = "A manifold composed of scrubbers pipes" + icon_state = "map_4way-scrubbers" + connect_types = CONNECT_TYPE_SCRUBBER + layer = 2.38 + icon_connect_type = "-scrubbers" + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/manifold4w/hidden/supply + name="4-way air supply pipe manifold" + desc = "A manifold composed of supply pipes" + icon_state = "map_4way-supply" + connect_types = CONNECT_TYPE_SUPPLY + layer = 2.39 + icon_connect_type = "-supply" + color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/manifold4w/hidden/yellow + color = PIPE_COLOR_YELLOW + +/obj/machinery/atmospherics/pipe/manifold4w/hidden/cyan + color = PIPE_COLOR_CYAN + +/obj/machinery/atmospherics/pipe/manifold4w/hidden/green + color = PIPE_COLOR_GREEN + +/obj/machinery/atmospherics/pipe/manifold4w/hidden/black + color = PIPE_COLOR_BLACK + +/obj/machinery/atmospherics/pipe/manifold4w/hidden/red + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/manifold4w/hidden/blue + color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/cap + name = "pipe endcap" + desc = "An endcap for pipes" + icon = 'icons/atmos/pipes.dmi' + icon_state = "" + level = 2 + layer = 2.4 //under wires with their 2.44 + + volume = 35 + + dir = SOUTH + initialize_directions = SOUTH + + var/obj/machinery/atmospherics/node + +/obj/machinery/atmospherics/pipe/cap/Initialize() + initialize_directions = dir + . = ..() + +/obj/machinery/atmospherics/pipe/cap/hide(var/i) + if(istype(loc, /turf/simulated)) + invisibility = i ? 101 : 0 + queue_icon_update() + +/obj/machinery/atmospherics/pipe/cap/pipeline_expansion() + return list(node) + +/obj/machinery/atmospherics/pipe/cap/machinery_process() + if(!parent) + ..() + else + . = PROCESS_KILL +/obj/machinery/atmospherics/pipe/cap/Destroy() + if(node) + node.disconnect(src) + + node = null + + return ..() + +/obj/machinery/atmospherics/pipe/cap/disconnect(obj/machinery/atmospherics/reference) + if(reference == node) + if(istype(node, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node = null + + update_icon() + + ..() + +/obj/machinery/atmospherics/pipe/cap/change_color(var/new_color) + ..() + //for updating connected atmos device pipes (i.e. vents, manifolds, etc) + if(node) + node.update_underlays() + +/obj/machinery/atmospherics/pipe/cap/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + alpha = 255 + + cut_overlays() + add_overlay(icon_manager.get_atmos_icon("pipe", , pipe_color, "cap")) + +/obj/machinery/atmospherics/pipe/cap/atmos_init() + for(var/obj/machinery/atmospherics/target in get_step(src, dir)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node = target + break + + var/turf/T = src.loc // hide if turf is not intact + if(level == 1 && !T.is_plating()) hide(1) + queue_icon_update() + +/obj/machinery/atmospherics/pipe/cap/visible + level = 2 + icon_state = "cap" + +/obj/machinery/atmospherics/pipe/cap/visible/scrubbers + name = "scrubbers pipe endcap" + desc = "An endcap for scrubbers pipes" + icon_state = "cap-scrubbers" + connect_types = CONNECT_TYPE_SCRUBBER + layer = 2.38 + icon_connect_type = "-scrubbers" + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/cap/visible/supply + name = "supply pipe endcap" + desc = "An endcap for supply pipes" + icon_state = "cap-supply" + connect_types = CONNECT_TYPE_SUPPLY + layer = 2.39 + icon_connect_type = "-supply" + color = PIPE_COLOR_BLUE + +/obj/machinery/atmospherics/pipe/cap/hidden + level = 1 + icon_state = "cap" + alpha = 128 + +/obj/machinery/atmospherics/pipe/cap/hidden/scrubbers + name = "scrubbers pipe endcap" + desc = "An endcap for scrubbers pipes" + icon_state = "cap-f-scrubbers" + connect_types = CONNECT_TYPE_SCRUBBER + layer = 2.38 + icon_connect_type = "-scrubbers" + color = PIPE_COLOR_RED + +/obj/machinery/atmospherics/pipe/cap/hidden/supply + name = "supply pipe endcap" + desc = "An endcap for supply pipes" + icon_state = "cap-f-supply" + connect_types = CONNECT_TYPE_SUPPLY + layer = 2.39 + icon_connect_type = "-supply" + color = PIPE_COLOR_BLUE + + +/obj/machinery/atmospherics/pipe/tank + icon = 'icons/atmos/tank.dmi' + icon_state = "air_map" + + name = "Pressure Tank" + desc = "A large vessel containing pressurized gas." + + volume = 10000 //in liters, 1 meters by 1 meters by 2 meters ~tweaked it a little to simulate a pressure tank without needing to recode them yet + var/start_pressure = 25*ONE_ATMOSPHERE + + level = 1 + dir = SOUTH + initialize_directions = SOUTH + density = 1 + +/obj/machinery/atmospherics/pipe/tank/Initialize() + icon_state = "air" + initialize_directions = dir + . = ..() + +/obj/machinery/atmospherics/pipe/tank/machinery_process() + if(!parent) + ..() + else + . = PROCESS_KILL + +/obj/machinery/atmospherics/pipe/tank/Destroy() + if(node1) + node1.disconnect(src) + + node1 = null + + return ..() + +/obj/machinery/atmospherics/pipe/tank/pipeline_expansion() + return list(node1) + +/obj/machinery/atmospherics/pipe/tank/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, dir) + +/obj/machinery/atmospherics/pipe/tank/hide() + update_underlays() + +/obj/machinery/atmospherics/pipe/tank/atmos_init() + var/connect_direction = dir + + for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node1 = target + break + + update_underlays() + +/obj/machinery/atmospherics/pipe/tank/disconnect(obj/machinery/atmospherics/reference) + if(reference == node1) + if(istype(node1, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node1 = null + + update_underlays() + + return null + +/obj/machinery/atmospherics/pipe/tank/attackby(var/obj/item/W as obj, var/mob/user as mob) + if(istype(W, /obj/item/device/pipe_painter)) + return + + if(istype(W, /obj/item/device/analyzer) && in_range(user, src)) + var/obj/item/device/analyzer/A = W + A.analyze_gases(src, user) + +/obj/machinery/atmospherics/pipe/tank/air + name = "Pressure Tank (Air)" + icon_state = "air_map" + +/obj/machinery/atmospherics/pipe/tank/air/Initialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T20C + + air_temporary.adjust_multi("oxygen", (start_pressure*O2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature), \ + "nitrogen",(start_pressure*N2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + + . = ..() + icon_state = "air" + +/obj/machinery/atmospherics/pipe/tank/oxygen + name = "Pressure Tank (Oxygen)" + icon_state = "o2_map" + +/obj/machinery/atmospherics/pipe/tank/oxygen/Initialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T20C + + air_temporary.adjust_gas("oxygen", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + . = ..() + icon_state = "o2" + +/obj/machinery/atmospherics/pipe/tank/nitrogen + name = "Pressure Tank (Nitrogen)" + icon_state = "n2_map" + +/obj/machinery/atmospherics/pipe/tank/nitrogen/Initialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T20C + + air_temporary.adjust_gas("nitrogen", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + . = ..() + icon_state = "n2" + +/obj/machinery/atmospherics/pipe/tank/carbon_dioxide + name = "Pressure Tank (Carbon Dioxide)" + icon_state = "co2_map" + +/obj/machinery/atmospherics/pipe/tank/carbon_dioxide/Initialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T20C + + air_temporary.adjust_gas("carbon_dioxide", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + . = ..() + icon_state = "co2" + +/obj/machinery/atmospherics/pipe/tank/phoron + name = "Pressure Tank (Phoron)" + icon_state = "phoron_map" + +/obj/machinery/atmospherics/pipe/tank/phoron/Initialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T20C + + air_temporary.adjust_gas("phoron", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + . = ..() + icon_state = "phoron" + +/obj/machinery/atmospherics/pipe/tank/nitrous_oxide + name = "Pressure Tank (Nitrous Oxide)" + icon_state = "n2o_map" + +/obj/machinery/atmospherics/pipe/tank/nitrous_oxide/Initialize() + air_temporary = new + air_temporary.volume = volume + air_temporary.temperature = T0C + + air_temporary.adjust_gas("sleeping_agent", (start_pressure)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)) + + . = ..() + icon_state = "n2o" + +/obj/machinery/atmospherics/pipe/vent + icon = 'icons/obj/atmospherics/pipe_vent.dmi' + icon_state = "intact" + + name = "Vent" + desc = "A large air vent" + + level = 1 + + volume = 250 + + dir = SOUTH + initialize_directions = SOUTH + + var/build_killswitch = 1 + +/obj/machinery/atmospherics/pipe/vent/Initialize() + initialize_directions = dir + . = ..() + +/obj/machinery/atmospherics/pipe/vent/high_volume + name = "Larger vent" + volume = 1000 + +/obj/machinery/atmospherics/pipe/vent/machinery_process() + if(!parent) + if(build_killswitch <= 0) + . = PROCESS_KILL + else + build_killswitch-- + ..() + return + else + parent.mingle_with_turf(loc, volume) + +/obj/machinery/atmospherics/pipe/vent/Destroy() + if(node1) + node1.disconnect(src) + + node1 = null + + return ..() + +/obj/machinery/atmospherics/pipe/vent/pipeline_expansion() + return list(node1) + +/obj/machinery/atmospherics/pipe/vent/update_icon() + if(node1) + icon_state = "intact" + + set_dir(get_dir(src, node1)) + + else + icon_state = "exposed" + +/obj/machinery/atmospherics/pipe/vent/atmos_init() + var/connect_direction = dir + + for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction)) + if(target.initialize_directions & get_dir(target,src)) + if (check_connect_types(target,src)) + node1 = target + break + + queue_icon_update() + +/obj/machinery/atmospherics/pipe/vent/disconnect(obj/machinery/atmospherics/reference) + if(reference == node1) + if(istype(node1, /obj/machinery/atmospherics/pipe)) + QDEL_NULL(parent) + node1 = null + + update_icon() + + return null + +/obj/machinery/atmospherics/pipe/vent/hide(var/i) //to make the little pipe section invisible, the icon changes. + if(node1) + icon_state = "[i == 1 && istype(loc, /turf/simulated) ? "h" : "" ]intact" + set_dir(get_dir(src, node1)) + else + icon_state = "exposed" + + +/obj/machinery/atmospherics/pipe/simple/visible/universal + name="Universal pipe adapter" + desc = "An adapter for regular, supply and scrubbers pipes" + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER + icon_state = "map_universal" + gfi_layer_rotation = GFI_ROTATION_OVERDIR + +/obj/machinery/atmospherics/pipe/simple/visible/universal/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + alpha = 255 + + cut_overlays() + add_overlay(icon_manager.get_atmos_icon("pipe", , pipe_color, "universal")) + underlays.Cut() + + if (node1) + universal_underlays(node1) + if(node2) + universal_underlays(node2) + else + var/node1_dir = get_dir(node1,src) + universal_underlays(,node1_dir) + else if (node2) + universal_underlays(node2) + else + universal_underlays(,dir) + universal_underlays(,turn(dir, -180)) + +/obj/machinery/atmospherics/pipe/simple/visible/universal/update_underlays() + ..() + queue_icon_update() + + + +/obj/machinery/atmospherics/pipe/simple/hidden/universal + name="Universal pipe adapter" + desc = "An adapter for regular, supply and scrubbers pipes" + connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_SUPPLY|CONNECT_TYPE_SCRUBBER + icon_state = "map_universal" + gfi_layer_rotation = GFI_ROTATION_OVERDIR + +/obj/machinery/atmospherics/pipe/simple/hidden/universal/update_icon(var/safety = 0) + if(!check_icon_cache()) + return + + alpha = 255 + + cut_overlays() + add_overlay(icon_manager.get_atmos_icon("pipe", , pipe_color, "universal")) + + underlays.Cut() + + if (node1) + universal_underlays(node1) + if(node2) + universal_underlays(node2) + else + var/node2_dir = turn(get_dir(src,node1),-180) + universal_underlays(,node2_dir) + else if (node2) + universal_underlays(node2) + var/node1_dir = turn(get_dir(src,node2),-180) + universal_underlays(,node1_dir) + else + universal_underlays(,dir) + universal_underlays(,turn(dir, -180)) + +/obj/machinery/atmospherics/pipe/simple/hidden/universal/update_underlays() + ..() + queue_icon_update() + +/obj/machinery/atmospherics/proc/universal_underlays(var/obj/machinery/atmospherics/node, var/direction) + var/turf/T = loc + if(node) + var/node_dir = get_dir(src,node) + if(node.icon_connect_type == "-supply") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, node, node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + else if (node.icon_connect_type == "-scrubbers") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, node, node_dir, "-scrubbers") + else + add_underlay_adapter(T, node, node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + else + add_underlay_adapter(T, , direction, "-supply") + add_underlay_adapter(T, , direction, "-scrubbers") + add_underlay_adapter(T, , direction, "") + +/obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays + if(node) + if(!T.is_plating() && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) + else + underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) + else + underlays += icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type) diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm index c54c5376a22..74ed9385558 100644 --- a/code/__defines/_compile_options.dm +++ b/code/__defines/_compile_options.dm @@ -1,5 +1,5 @@ -#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. - -// If defined, the sunlight system is enabled. Caution: this uses a LOT of memory. -//#define ENABLE_SUNLIGHT +#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. + +// If defined, the sunlight system is enabled. Caution: this uses a LOT of memory. +//#define ENABLE_SUNLIGHT diff --git a/code/__defines/_macros.dm b/code/__defines/_macros.dm index 05dd7e8685e..f5dad3c6bac 100644 --- a/code/__defines/_macros.dm +++ b/code/__defines/_macros.dm @@ -22,6 +22,8 @@ #define ishuman(A) istype(A, /mob/living/carbon/human) +#define ismech(A) istype(A, /mob/living/heavy_vehicle) + #define isliving(A) istype(A, /mob/living) #define israt(A) istype(A, /mob/living/simple_animal/rat) diff --git a/code/__defines/damage_organs.dm b/code/__defines/damage_organs.dm index 6221e24bdd0..d1c05df0574 100644 --- a/code/__defines/damage_organs.dm +++ b/code/__defines/damage_organs.dm @@ -5,11 +5,12 @@ #define TOX "tox" #define OXY "oxy" #define CLONE "clone" -#define HALLOSS "halloss" +#define PAIN "pain" #define CUT "cut" #define BRUISE "bruise" #define PIERCE "pierce" +#define LASER "laser" #define DAM_LASER 1 #define DAM_BULLET 2 @@ -18,7 +19,6 @@ #define WEAKEN "weaken" #define PARALYZE "paralize" #define IRRADIATE "irradiate" -#define AGONY "agony" // Added in PAIN! #define SLUR "slur" #define STUTTER "stutter" #define EYE_BLUR "eye_blur" @@ -63,3 +63,10 @@ #define BLOOD_VOLUME_OKAY 70 #define BLOOD_VOLUME_BAD 60 #define BLOOD_VOLUME_SURVIVE 30 + +// These control the amount of blood lost from burns. The loss is calculated so +// that dealing just enough burn damage to kill the player will cause the given +// proportion of their max blood volume to be lost +// (e.g. 0.6 == 60% lost if 200 burn damage is taken). +#define FLUIDLOSS_WIDE_BURN 0.6 //for burns from heat applied over a wider area, like from fire +#define FLUIDLOSS_CONC_BURN 0.4 //for concentrated burns, like from lasers diff --git a/code/__defines/dna.dm b/code/__defines/dna.dm index 1655659da68..1be4cd0e1fa 100644 --- a/code/__defines/dna.dm +++ b/code/__defines/dna.dm @@ -11,7 +11,7 @@ #define FAT 6 #define HUSK 7 #define NOCLONE 8 -#define LASER 9 // Harm intent - click anywhere to shoot lasers from eyes. +#define LASER_EYES 9 // Harm intent - click anywhere to shoot lasers from eyes. #define HEAL 10 // Healing people with hands. #define SKELETON 29 diff --git a/code/__defines/lighting.dm b/code/__defines/lighting.dm index d4097da3e59..8ee5fbc5fae 100644 --- a/code/__defines/lighting.dm +++ b/code/__defines/lighting.dm @@ -1,141 +1,141 @@ -#define LIGHTING_INTERVAL 1 // Frequency, in 1/10ths of a second, of the lighting process. - -#define LIGHTING_HEIGHT 1 // height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone -#define LIGHTING_Z_FACTOR 10 // Z diff is multiplied by this and LIGHTING_HEIGHT to get the final height of a light source. Affects how much darker A Z light gets with each level transitioned. -#define LIGHTING_ROUND_VALUE 1 / 200 //Value used to round lumcounts, values smaller than 1/255 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_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects -#define LIGHTING_BASE_ICON_STATE "matrix" // icon_state used for normal color-matrix based lighting overlays. -#define LIGHTING_STATION_ICON_STATE "tubedefault" // icon_state used for lighting overlays that are just displaying standard station lighting. -#define LIGHTING_DARKNESS_ICON_STATE "black" // icon_state used for lighting overlays with no luminosity. -#define LIGHTING_TRANSPARENT_ICON_STATE "blank" - -#define LIGHTING_SOFT_THRESHOLD 0.001 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays. -#define LIGHTING_BLOCKED_FACTOR 0.5 // How much the range of a directional light will be reduced while facing a wall. - -// If defined, instant updates will be used whenever server load permits. Otherwise queued updates are always used. -#define USE_INTELLIGENT_LIGHTING_UPDATES - -// If defined, lighting corners will 'bleed' luminosity to corners above them to simulate cross-Z lighting upwards. Downwards Z-lighting will continue to work with this disabled. -#define USE_CORNER_ZBLEED - -#define TURF_IS_DYNAMICALLY_LIT(T) (isturf(T) && T:dynamic_lighting && T:loc:dynamic_lighting) -// mostly identical to above, but doesn't make sure T is valid first. Should only be used by lighting code. -#define TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) (T:dynamic_lighting && T:loc:dynamic_lighting) - -// 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 \ - ) \ - -// Helpers so we can (more easily) control the colour matrices. -#define CL_MATRIX_RR 1 -#define CL_MATRIX_RG 2 -#define CL_MATRIX_RB 3 -#define CL_MATRIX_RA 4 -#define CL_MATRIX_GR 5 -#define CL_MATRIX_GG 6 -#define CL_MATRIX_GB 7 -#define CL_MATRIX_GA 8 -#define CL_MATRIX_BR 9 -#define CL_MATRIX_BG 10 -#define CL_MATRIX_BB 11 -#define CL_MATRIX_BA 12 -#define CL_MATRIX_AR 13 -#define CL_MATRIX_AG 14 -#define CL_MATRIX_AB 15 -#define CL_MATRIX_AA 16 -#define CL_MATRIX_CR 17 -#define CL_MATRIX_CG 18 -#define CL_MATRIX_CB 19 -#define CL_MATRIX_CA 20 - -// Higher numbers override lower. -#define LIGHTING_NO_UPDATE 0 -#define LIGHTING_VIS_UPDATE 1 -#define LIGHTING_CHECK_UPDATE 2 -#define LIGHTING_FORCE_UPDATE 3 - -// This color of overlay is very common - most of the station is this color when lit fully. -// Tube lights are a bluish-white, so we can't just assume 1-1-1 is full-illumination. -#define LIGHTING_DEFAULT_TUBE_R 0.96 -#define LIGHTING_DEFAULT_TUBE_G 1 -#define LIGHTING_DEFAULT_TUBE_B 1 - -//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_CYAN "#7DE1E1" //Diluted cyan. rgb(125, 225, 225) -#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 "#A97FAA" //Soft purple. rgb(169, 127, 170) -#define LIGHT_COLOR_VIOLET "#B43CB8" //Deep purple. rgb(180, 60, 184) -#define LIGHT_COLOR_SCARLET "#E85656" //Light red. rgb(232, 86, 86) - -//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) -#define LIGHT_COLOR_EMERGENCY "#FF3232" //Red color used by emergency lighting. rgb(255, 50, 50) - -//Defines for lighting status, see power/lighting.dm -#define LIGHT_OK 0 -#define LIGHT_EMPTY 1 -#define LIGHT_BROKEN 2 -#define LIGHT_BURNED 3 - -#define LIGHT_EMERGENCY_POWER_USE 0.2 //How much power emergency lights will consume per tick - -// Some angle presets for directional lighting. -#define LIGHT_OMNI null -#define LIGHT_SEMI 180 -#define LIGHT_WIDE 90 -#define LIGHT_NARROW 45 - -// Night lighting controller times -// The time (in hours based on worldtime2hours()) that various actions trigger -#define MORNING_LIGHT_RESET 7 // 7am or 07:00 - lighting restores to normal in morning -#define NIGHT_LIGHT_ACTIVE 18 // 6pm or 18:00 - night lighting mode activates - -// Some brightness/range defines for objects. -#define L_WALLMOUNT_POWER 0.4 -#define L_WALLMOUNT_RANGE 2 -#define L_WALLMOUNT_HI_POWER 1 // For red/delta alert on fire alarms. -#define L_WALLMOUNT_HI_RANGE 4 -// This controls by how much console sprites are dimmed before being overlayed. -#define HOLOSCREEN_ADDITION_FACTOR 1 -#define HOLOSCREEN_MULTIPLICATION_FACTOR 0.5 -#define HOLOSCREEN_ADDITION_OPACITY 0.8 -#define HOLOSCREEN_MULTIPLICATION_OPACITY 1 - -// Just so we can avoid unneeded proc calls when profiling is disabled. -#define L_PROF(O,T) if (lighting_profiling) {lprof_write(O,T);} - -// -- Ambient Occlusion -- - -// Not handled by the lighting engine, but related. Controls the alpha of the ambient occlusion effect on opaque atoms and openturfs. -#define WALL_AO_ALPHA 80 - -#define AO_UPDATE_NONE 0 -#define AO_UPDATE_OVERLAY 1 -#define AO_UPDATE_REBUILD 2 - -// If ao_neighbors equals this, no AO shadows are present. -#define AO_ALL_NEIGHBORS 1910 - -// If defined, integrate with the lighting engine and use its opacity value. Otherwise a simple turf opacity check is used. This may cause visual artifacts with opaque non-square movables. -//#define AO_USE_LIGHTING_OPACITY +#define LIGHTING_INTERVAL 1 // Frequency, in 1/10ths of a second, of the lighting process. + +#define LIGHTING_HEIGHT 1 // height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone +#define LIGHTING_Z_FACTOR 10 // Z diff is multiplied by this and LIGHTING_HEIGHT to get the final height of a light source. Affects how much darker A Z light gets with each level transitioned. +#define LIGHTING_ROUND_VALUE 1 / 200 //Value used to round lumcounts, values smaller than 1/255 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_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects +#define LIGHTING_BASE_ICON_STATE "matrix" // icon_state used for normal color-matrix based lighting overlays. +#define LIGHTING_STATION_ICON_STATE "tubedefault" // icon_state used for lighting overlays that are just displaying standard station lighting. +#define LIGHTING_DARKNESS_ICON_STATE "black" // icon_state used for lighting overlays with no luminosity. +#define LIGHTING_TRANSPARENT_ICON_STATE "blank" + +#define LIGHTING_SOFT_THRESHOLD 0.001 // If the max of the lighting lumcounts of each spectrum drops below this, disable luminosity on the lighting overlays. +#define LIGHTING_BLOCKED_FACTOR 0.5 // How much the range of a directional light will be reduced while facing a wall. + +// If defined, instant updates will be used whenever server load permits. Otherwise queued updates are always used. +#define USE_INTELLIGENT_LIGHTING_UPDATES + +// If defined, lighting corners will 'bleed' luminosity to corners above them to simulate cross-Z lighting upwards. Downwards Z-lighting will continue to work with this disabled. +#define USE_CORNER_ZBLEED + +#define TURF_IS_DYNAMICALLY_LIT(T) (isturf(T) && T:dynamic_lighting && T:loc:dynamic_lighting) +// mostly identical to above, but doesn't make sure T is valid first. Should only be used by lighting code. +#define TURF_IS_DYNAMICALLY_LIT_UNSAFE(T) (T:dynamic_lighting && T:loc:dynamic_lighting) + +// 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 \ + ) \ + +// Helpers so we can (more easily) control the colour matrices. +#define CL_MATRIX_RR 1 +#define CL_MATRIX_RG 2 +#define CL_MATRIX_RB 3 +#define CL_MATRIX_RA 4 +#define CL_MATRIX_GR 5 +#define CL_MATRIX_GG 6 +#define CL_MATRIX_GB 7 +#define CL_MATRIX_GA 8 +#define CL_MATRIX_BR 9 +#define CL_MATRIX_BG 10 +#define CL_MATRIX_BB 11 +#define CL_MATRIX_BA 12 +#define CL_MATRIX_AR 13 +#define CL_MATRIX_AG 14 +#define CL_MATRIX_AB 15 +#define CL_MATRIX_AA 16 +#define CL_MATRIX_CR 17 +#define CL_MATRIX_CG 18 +#define CL_MATRIX_CB 19 +#define CL_MATRIX_CA 20 + +// Higher numbers override lower. +#define LIGHTING_NO_UPDATE 0 +#define LIGHTING_VIS_UPDATE 1 +#define LIGHTING_CHECK_UPDATE 2 +#define LIGHTING_FORCE_UPDATE 3 + +// This color of overlay is very common - most of the station is this color when lit fully. +// Tube lights are a bluish-white, so we can't just assume 1-1-1 is full-illumination. +#define LIGHTING_DEFAULT_TUBE_R 0.96 +#define LIGHTING_DEFAULT_TUBE_G 1 +#define LIGHTING_DEFAULT_TUBE_B 1 + +//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_CYAN "#7DE1E1" //Diluted cyan. rgb(125, 225, 225) +#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 "#A97FAA" //Soft purple. rgb(169, 127, 170) +#define LIGHT_COLOR_VIOLET "#B43CB8" //Deep purple. rgb(180, 60, 184) +#define LIGHT_COLOR_SCARLET "#E85656" //Light red. rgb(232, 86, 86) + +//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) +#define LIGHT_COLOR_EMERGENCY "#FF3232" //Red color used by emergency lighting. rgb(255, 50, 50) + +//Defines for lighting status, see power/lighting.dm +#define LIGHT_OK 0 +#define LIGHT_EMPTY 1 +#define LIGHT_BROKEN 2 +#define LIGHT_BURNED 3 + +#define LIGHT_EMERGENCY_POWER_USE 0.2 //How much power emergency lights will consume per tick + +// Some angle presets for directional lighting. +#define LIGHT_OMNI null +#define LIGHT_SEMI 180 +#define LIGHT_WIDE 90 +#define LIGHT_NARROW 45 + +// Night lighting controller times +// The time (in hours based on worldtime2hours()) that various actions trigger +#define MORNING_LIGHT_RESET 7 // 7am or 07:00 - lighting restores to normal in morning +#define NIGHT_LIGHT_ACTIVE 18 // 6pm or 18:00 - night lighting mode activates + +// Some brightness/range defines for objects. +#define L_WALLMOUNT_POWER 0.4 +#define L_WALLMOUNT_RANGE 2 +#define L_WALLMOUNT_HI_POWER 1 // For red/delta alert on fire alarms. +#define L_WALLMOUNT_HI_RANGE 4 +// This controls by how much console sprites are dimmed before being overlayed. +#define HOLOSCREEN_ADDITION_FACTOR 1 +#define HOLOSCREEN_MULTIPLICATION_FACTOR 0.5 +#define HOLOSCREEN_ADDITION_OPACITY 0.8 +#define HOLOSCREEN_MULTIPLICATION_OPACITY 1 + +// Just so we can avoid unneeded proc calls when profiling is disabled. +#define L_PROF(O,T) if (lighting_profiling) {lprof_write(O,T);} + +// -- Ambient Occlusion -- + +// Not handled by the lighting engine, but related. Controls the alpha of the ambient occlusion effect on opaque atoms and openturfs. +#define WALL_AO_ALPHA 80 + +#define AO_UPDATE_NONE 0 +#define AO_UPDATE_OVERLAY 1 +#define AO_UPDATE_REBUILD 2 + +// If ao_neighbors equals this, no AO shadows are present. +#define AO_ALL_NEIGHBORS 1910 + +// If defined, integrate with the lighting engine and use its opacity value. Otherwise a simple turf opacity check is used. This may cause visual artifacts with opaque non-square movables. +//#define AO_USE_LIGHTING_OPACITY diff --git a/code/__defines/lists.dm b/code/__defines/lists.dm index 3b928f5f50c..b2c5d72042c 100644 --- a/code/__defines/lists.dm +++ b/code/__defines/lists.dm @@ -12,7 +12,10 @@ #define LAZYDISTINCTADD(L, I) if(!L) { L = list(); } L |= I; // Shims for some list procs in lists.dm. -#define islist(L) istype(L,/list) #define isemptylist(L) (!LAZYLEN(L)) #define safepick(L) LAZYPICK(L,null) #define listgetindex(L,I) LAZYACCESS(L,I) + +#if DM_VERSION < 513 +#define islist(L) istype(L,/list) +#endif \ No newline at end of file diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index cb51216bedc..32ba09fbbcd 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -273,8 +273,16 @@ // Used for creating soft references to objects. A manner of storing an item reference // as text so you don't necessarily fuck with an object's ability to be garbage collected. +#if DM_VERSION < 513 + #define SOFTREF(A) "\ref[A]" +#else + +#define SOFTREF(A) ref(A) + +#endif + // This only works on 511 because it relies on 511's `var/something = foo = bar` syntax. #define WEAKREF(D) (istype(D, /datum) && !D:gcDestroyed ? (D:weakref || (D:weakref = new(D))) : null) diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 5a1804262d8..02fa9e20e0d 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -69,6 +69,7 @@ #define BP_IS_ROBOTIC(org) (org.status & ORGAN_ROBOT) //Generic organs +#define BP_MOUTH "mouth" #define BP_EYES "eyes" #define BP_HEART "heart" #define BP_LUNGS "lungs" diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm index b7cf56b65b0..ac86f23ef8a 100644 --- a/code/__defines/qdel.dm +++ b/code/__defines/qdel.dm @@ -20,3 +20,4 @@ #define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE) #define QDEL_NULL(item) qdel(item); item = null +#define QDEL_NULL_LIST(x) if(x) { for(var/y in x) { qdel(y) }}; if(x) {x.Cut(); x = null } // Second x check to handle items that LAZYREMOVE on qdel. diff --git a/code/_helpers/atmospherics.dm b/code/_helpers/atmospherics.dm index 97f01868175..9c4302cdbd2 100644 --- a/code/_helpers/atmospherics.dm +++ b/code/_helpers/atmospherics.dm @@ -1,47 +1,47 @@ -/obj/proc/analyze_gases(var/obj/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/obj/target, var/datum/gas_mixture/mixture, var/mob/user) - var/pressure = mixture.return_pressure() - var/total_moles = mixture.total_moles - - var/list/results = list() - if (total_moles>0) - 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)]%" - results += "Temperature: [round(mixture.temperature-T0C)]°C" - else - results += "\The [target] is empty!" - - return results - -/obj/proc/atmosanalyze(var/mob/user) - return - -/obj/item/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/power/rad_collector/atmosanalyze(var/mob/user) - if(P) return atmosanalyzer_scan(src, src.P.air_contents, user) - -/obj/item/flamethrower/atmosanalyze(var/mob/user) - if(ptank) return atmosanalyzer_scan(src, ptank.air_contents, user) +/obj/proc/analyze_gases(var/obj/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/obj/target, var/datum/gas_mixture/mixture, var/mob/user) + var/pressure = mixture.return_pressure() + var/total_moles = mixture.total_moles + + var/list/results = list() + if (total_moles>0) + 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)]%" + results += "Temperature: [round(mixture.temperature-T0C)]°C" + else + results += "\The [target] is empty!" + + return results + +/obj/proc/atmosanalyze(var/mob/user) + return + +/obj/item/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/power/rad_collector/atmosanalyze(var/mob/user) + if(P) return atmosanalyzer_scan(src, src.P.air_contents, user) + +/obj/item/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 388b2db4ed3..73c470da363 100644 --- a/code/_helpers/files.dm +++ b/code/_helpers/files.dm @@ -1,60 +1,60 @@ -//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) - to_chat(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 +//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) + to_chat(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 diff --git a/code/_helpers/functional.dm b/code/_helpers/functional.dm index 231b8a4d261..114913f7c3a 100644 --- a/code/_helpers/functional.dm +++ b/code/_helpers/functional.dm @@ -1,24 +1,24 @@ -/proc/all_predicates_true(var/list/input, var/list/predicates) - functional_sanity(input, predicates) - - for(var/i = 1 to predicates.len) - if(!call(predicates[i])(arglist(input))) - return FALSE - return TRUE - -/proc/any_predicate_true(var/list/input, var/list/predicates) - functional_sanity(input, predicates) - - if(!predicates.len) - return TRUE - - for(var/i = 1 to predicates.len) - if(call(predicates[i])(arglist(input))) - return TRUE - return FALSE - -/proc/functional_sanity(var/list/input, var/list/predicates) - if(!istype(input)) - CRASH("Expected list input. Was [input ? "[input.type]" : "null"]") - if(!istype(predicates)) - CRASH("Expected predicate list. Was [predicates ? "[predicates.type]" : "null"]") +/proc/all_predicates_true(var/list/input, var/list/predicates) + functional_sanity(input, predicates) + + for(var/i = 1 to predicates.len) + if(!call(predicates[i])(arglist(input))) + return FALSE + return TRUE + +/proc/any_predicate_true(var/list/input, var/list/predicates) + functional_sanity(input, predicates) + + if(!predicates.len) + return TRUE + + for(var/i = 1 to predicates.len) + if(call(predicates[i])(arglist(input))) + return TRUE + return FALSE + +/proc/functional_sanity(var/list/input, var/list/predicates) + if(!istype(input)) + CRASH("Expected list input. Was [input ? "[input.type]" : "null"]") + if(!istype(predicates)) + CRASH("Expected predicate list. Was [predicates ? "[predicates.type]" : "null"]") diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index 254b60ea552..f54f2f615da 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -1,547 +1,547 @@ -//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(isStationLevel(z1) && isStationLevel(z2)) - return 1 - return 0 - -/proc/max_default_z_level() - var/max_z = 0 - for(var/z in current_map.station_levels) - max_z = max(z, max_z) - for(var/z in current_map.admin_levels) - max_z = max(z, max_z) - for(var/z in current_map.player_levels) - max_z = max(z, max_z) - return max_z - -/proc/get_area(O) - var/turf/loc = get_turf(O) - if(loc) - .= loc.loc - -/proc/get_area_name(N) //get area by its name - for(var/area/A in all_areas) - if(A.name == N) - return A - return 0 - -/proc/get_area_master(const/O) - var/area/A = get_area(O) - if (isarea(A)) - return A - -/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/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/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) - - 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/)) - 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) - if(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 in radios) - if(R) - //Cyborg checks. Receiving message uses a bit of cyborg's charge. - var/obj/item/device/radio/borg/BR = R - if(istype(BR) && BR.myborg) - var/mob/living/silicon/robot/borg = BR.myborg - var/datum/robot_component/CO = borg.get_component("radio") - if(!CO) - continue //No radio component (Shouldn't happen) - if(!borg.is_component_functioning("radio") || !borg.cell_use_power(CO.active_usage)) - continue //No power. - - var/turf/speaker = get_turf(R) - if(speaker) - for(var/turf/T in hear(R.canhear_range,speaker)) - speaker_coverage[T] = T - - - // 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) - var/turf/ear = get_turf(M) - if(ear) - // Ghostship is magic: Ghosts can hear radio chatter from anywhere - if(speaker_coverage[ear] || (istype(M, /mob/abstract/observer) && (M.client) && (M.client.prefs.toggles & CHAT_GHOSTRADIO))) - . += M - return . - -/proc/get_mobs_and_objs_in_view_fast(turf/T, range, list/mobs, list/objs, checkghosts = GHOSTS_ALL_HEAR) - var/list/hear = list() - DVIEW(hear, range, T, INVISIBILITY_MAXIMUM) - var/list/hearturfs = list() - - for(var/am in hear) - var/atom/movable/AM = am - if (!AM.loc) - continue - - if(ismob(AM)) - mobs[AM] = TRUE - hearturfs[AM.locs[1]] = TRUE - else if(isobj(AM)) - objs[AM] = TRUE - hearturfs[AM.locs[1]] = TRUE - - for(var/m in player_list) - var/mob/M = m - if(istype(M, /mob/living/test)) - if (!mobs[M]) - mobs[M] = TRUE - continue - if(checkghosts == GHOSTS_ALL_HEAR && M.stat == DEAD && !isnewplayer(M) && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS)) - if (!mobs[M]) - mobs[M] = TRUE - continue - if(M.loc && hearturfs[M.locs[1]]) - if (!mobs[M]) - mobs[M] = TRUE - - for(var/o in listening_objects) - var/obj/O = o - if(O && O.loc && hearturfs[O.locs[1]]) - if (!objs[O]) - objs[O] = TRUE - -#define SIGN(X) ((X<0)?-1:1) - -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/abstract/observer/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/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 = 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) - - var/g = 9.81 - var/h = 10 - var/power_x = power * cos(angle) - var/power_y = power * sin(angle) - var/time = (power_y + sqrt((power_y*power_y)+(2*g*h)))/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 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 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/round_is_spooky(var/spookiness_threshold = config.cult_ghostwriter_req_cultists) - if(enabled_spooking) - return 1 - else - return (cult.current_antagonists.len > spookiness_threshold) - -/proc/remove_images_from_clients(image/I, list/show_to) - for(var/client/C in show_to) - C.images -= I - -/proc/flick_overlay(image/I, list/show_to, duration) - for(var/client/C in show_to) - C.images += I - addtimer(CALLBACK(GLOBAL_PROC, /.proc/remove_images_from_clients, I, show_to), duration) - -/proc/flick_overlay_view(image/I, atom/target, duration) //wrapper for the above, flicks to everyone who can see the target atom - var/list/viewing = list() - for(var/m in viewers(target)) - var/mob/M = m - if(M.client) - viewing += M.client - flick_overlay(I, viewing, duration) +//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(isStationLevel(z1) && isStationLevel(z2)) + return 1 + return 0 + +/proc/max_default_z_level() + var/max_z = 0 + for(var/z in current_map.station_levels) + max_z = max(z, max_z) + for(var/z in current_map.admin_levels) + max_z = max(z, max_z) + for(var/z in current_map.player_levels) + max_z = max(z, max_z) + return max_z + +/proc/get_area(O) + var/turf/loc = get_turf(O) + if(loc) + .= loc.loc + +/proc/get_area_name(N) //get area by its name + for(var/area/A in all_areas) + if(A.name == N) + return A + return 0 + +/proc/get_area_master(const/O) + var/area/A = get_area(O) + if (isarea(A)) + return A + +/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/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/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) + + 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/)) + 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) + if(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 in radios) + if(R) + //Cyborg checks. Receiving message uses a bit of cyborg's charge. + var/obj/item/device/radio/borg/BR = R + if(istype(BR) && BR.myborg) + var/mob/living/silicon/robot/borg = BR.myborg + var/datum/robot_component/CO = borg.get_component("radio") + if(!CO) + continue //No radio component (Shouldn't happen) + if(!borg.is_component_functioning("radio") || !borg.cell_use_power(CO.active_usage)) + continue //No power. + + var/turf/speaker = get_turf(R) + if(speaker) + for(var/turf/T in hear(R.canhear_range,speaker)) + speaker_coverage[T] = T + + + // 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) + var/turf/ear = get_turf(M) + if(ear) + // Ghostship is magic: Ghosts can hear radio chatter from anywhere + if(speaker_coverage[ear] || (istype(M, /mob/abstract/observer) && (M.client) && (M.client.prefs.toggles & CHAT_GHOSTRADIO))) + . += M + return . + +/proc/get_mobs_and_objs_in_view_fast(turf/T, range, list/mobs, list/objs, checkghosts = GHOSTS_ALL_HEAR) + var/list/hear = list() + DVIEW(hear, range, T, INVISIBILITY_MAXIMUM) + var/list/hearturfs = list() + + for(var/am in hear) + var/atom/movable/AM = am + if (!AM.loc) + continue + + if(ismob(AM)) + mobs[AM] = TRUE + hearturfs[AM.locs[1]] = TRUE + else if(isobj(AM)) + objs[AM] = TRUE + hearturfs[AM.locs[1]] = TRUE + + for(var/m in player_list) + var/mob/M = m + if(istype(M, /mob/living/test)) + if (!mobs[M]) + mobs[M] = TRUE + continue + if(checkghosts == GHOSTS_ALL_HEAR && M.stat == DEAD && !isnewplayer(M) && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS)) + if (!mobs[M]) + mobs[M] = TRUE + continue + if(M.loc && hearturfs[M.locs[1]]) + if (!mobs[M]) + mobs[M] = TRUE + + for(var/o in listening_objects) + var/obj/O = o + if(O && O.loc && hearturfs[O.locs[1]]) + if (!objs[O]) + objs[O] = TRUE + +#define SIGN(X) ((X<0)?-1:1) + +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/abstract/observer/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/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 = 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) + + var/g = 9.81 + var/h = 10 + var/power_x = power * cos(angle) + var/power_y = power * sin(angle) + var/time = (power_y + sqrt((power_y*power_y)+(2*g*h)))/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 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 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/round_is_spooky(var/spookiness_threshold = config.cult_ghostwriter_req_cultists) + if(enabled_spooking) + return 1 + else + return (cult.current_antagonists.len > spookiness_threshold) + +/proc/remove_images_from_clients(image/I, list/show_to) + for(var/client/C in show_to) + C.images -= I + +/proc/flick_overlay(image/I, list/show_to, duration) + for(var/client/C in show_to) + C.images += I + addtimer(CALLBACK(GLOBAL_PROC, /.proc/remove_images_from_clients, I, show_to), duration) + +/proc/flick_overlay_view(image/I, atom/target, duration) //wrapper for the above, flicks to everyone who can see the target atom + var/list/viewing = list() + for(var/m in viewers(target)) + var/mob/M = m + if(M.client) + viewing += M.client + flick_overlay(I, viewing, duration) diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index b62f5ad05e9..1f24828c151 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -1,225 +1,225 @@ -var/list/clients = list() //list of all clients -var/list/staff = list() //list of all clients who have any permissions -var/list/directory = list() //list of all ckeys with associated client - -//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/abstract/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/living_mob_list = list() //List of all alive mobs, including clientless. Excludes /mob/abstract/new_player -var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/abstract/new_player -var/global/list/topic_commands = list() //List of all API commands available -var/global/list/topic_commands_names = list() //List of all API commands available - -var/global/list/landmarks_list = list() //list of all landmarks created -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 -var/global/list/brig_closets = list() //list of all brig secure_closets. Used by brig timers. Probably should be converted to use SSwireless eventually. - -var/global/list/teleportlocs = list() -var/global/list/ghostteleportlocs = list() -var/global/list/centcom_areas = list() -var/global/list/the_station_areas = list() - -var/global/list/implants = list() - -var/global/list/turfs = list() //list of all turfs -var/global/list/areas_by_type = list() -var/global/list/all_areas = list() - -//Languages/species/whitelist. -var/global/list/all_species = list() -var/global/list/all_languages = list() -var/global/list/language_keys = list() // Table of say codes for all languages -var/global/list/whitelisted_species = list("Human") // Species that require a whitelist check. -var/global/list/playable_species = list("Human") // A list of ALL playable species, whitelisted, latejoin or otherwise. - -// Posters -var/global/list/poster_designs = list() - -// Uplinks -var/list/obj/item/device/uplink/world_uplinks = 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() -var/global/list/chargen_disabilities_list = list() - - //Underwear -var/global/list/underwear_m = list("White" = "m1", "Grey" = "m2", "Green" = "m3", "Blue" = "m4", "Black" = "m5", "Mankini" = "m6", "Boxers" = "boxers", "Green and blue boxers" = "boxers_green_and_blue","Loveheart boxers" = "boxers_loveheart","None") //Curse whoever made male/female underwear diffrent colours -var/global/list/underwear_f = list("Red" = "f1", "White" = "f2", "Yellow" = "f3", "Blue" = "f4", "Black" = "f5", "Thong" = "f6", "Black Sports" = "f7","White Sports" = "f8","None") - //undershirt -var/global/list/undershirt_t = list( - "White tank top" = "u1", "Black tank top" = "u2", "Black shirt" = "u3", "White shirt" = "u4", - "Polo" = "polo", "Blue polo" = "bluepolo_s", "Red polo" = "redpolo_s", "Yellow polo" = "grayyellowpolo_s", - "Striped shirt" = "shirt_stripes_s", "NanoTrasen shirt" = "shirt_nano_s", "Tiedye shirt" = "shirt_tiedye_s", - "Green sport shirt" = "greenshirtsport_s", "Red sport shirt" = "redshirtsport_s", "Blue sport shirt" = "blueshirtsport_s", - "Striped tank top " = "tank_stripes_s", "Rainbow tank top" = "tank_rainbow_s", "Longsleeve shirt" = "undershirt_long", - "Striped longsleve shirt" = "longstripe", "Blue striped longsleve shirt" = "longstripe_blue", "None") - - //socks -var/global/list/socks_f = list( - "White normal" = "white_norm", "White short" = "white_short", "White knee" = "white_knee", - "White thigh" = "white_thigh", "Black normal" = "black_norm", "Black short" = "black_short", - "Black knee" = "black_knee", "Black thigh" = "black_thigh", "Striped normal" = "striped_norm", - "Striped short" = "striped_short", "Striped knee" = "striped_knee", "Striped thigh" = "striped_thigh", - "Rainbow normal" = "rainbow_norm", "Rainbow short" = "rainbow_short", "Rainbow knee" = "rainbow_knee", - "Rainbow thigh" = "rainbow_thigh", "Pantyhose" = "pantyhose", "None") - -var/global/list/socks_m = list( - "White normal" = "white_norm", "White short" = "white_short", "White knee" = "white_knee", - "Black normal" = "black_norm", "Black short" = "black_short", "Black knee" = "black_knee", - "Striped normal" = "striped_norm", "Striped short" = "striped_short", "Striped knee" = "striped_knee", - "Rainbow normal" = "rainbow_norm", "Rainbow short" = "rainbow_short", "Rainbow knee" = "rainbow_knee", "None") - - //Backpacks -var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt", "Duffel Bag", "Messenger Bag") -var/global/list/backbagstyles = list("Job-specific", "Grey") -var/global/list/exclude_jobs = list(/datum/job/ai,/datum/job/cyborg, /datum/job/merchant) - -// Visual nets -var/list/datum/visualnet/visual_nets = list() -var/datum/visualnet/camera/cameranet = 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) - -//Cloaking devices -var/global/list/cloaking_devices = list() - -////////////////////////// -/////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 - - sortTim(hair_styles_list, /proc/cmp_text_asc) - sortTim(hair_styles_male_list, /proc/cmp_text_asc) - sortTim(hair_styles_female_list, /proc/cmp_text_asc) - - //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 - - sortTim(facial_hair_styles_list, /proc/cmp_text_asc) - sortTim(facial_hair_styles_male_list, /proc/cmp_text_asc) - sortTim(facial_hair_styles_female_list, /proc/cmp_text_asc) - - //Body markings - 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 - - sortTim(body_marking_styles_list, /proc/cmp_text_asc) - - //Disability datums - paths = subtypesof(/datum/character_disabilities) - for(var/path in paths) - var/datum/character_disabilities/T = new path() - chargen_disabilities_list[T.name] = T - - sortTim(chargen_disabilities_list, /proc/cmp_text_asc) - - //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 - - sortTim(surgery_steps, /proc/cmp_surgery) - - //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 and species. - paths = subtypesof(/datum/language) - for(var/T in paths) - var/datum/language/L = new T - all_languages[L.name] = L - - for (var/language_name in all_languages) - var/datum/language/L = all_languages[language_name] - if(!(L.flags & NONGLOBAL)) - language_keys[lowertext(L.key)] = L - - var/rkey = 0 - paths = subtypesof(/datum/species) - for(var/T in paths) - rkey++ - var/datum/species/S = new T - S.race_key = rkey //Used in mob icon caching. - all_species[S.name] = S - - sortTim(all_species, /proc/cmp_text_asc) - - // The other lists are generated *after* we sort the main one so they don't need sorting too. - for (var/thing in all_species) - var/datum/species/S = all_species[thing] - - if (!(S.spawn_flags & IS_RESTRICTED)) - playable_species += S.name - if(S.spawn_flags & IS_WHITELISTED) - whitelisted_species += S.name - - //Posters - paths = subtypesof(/datum/poster) - for(var/T in paths) - var/datum/poster/P = new T - poster_designs += P - - return 1 - -/* // Uncomment to debug chemical reaction list. -/client/verb/debug_chemical_list() - - for (var/reaction in SSchemistry.chemical_reactions) - . += "SSchemistry.chemical_reactions\[\"[reaction]\"\] = \"[SSchemistry.chemical_reactions[reaction]]\"\n" - if(islist(SSchemistry.chemical_reactions[reaction])) - var/list/L = SSchemistry.chemical_reactions[reaction] - for(var/t in L) - . += " has: [t]\n" - world << . -*/ +var/list/clients = list() //list of all clients +var/list/staff = list() //list of all clients who have any permissions +var/list/directory = list() //list of all ckeys with associated client + +//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/abstract/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/living_mob_list = list() //List of all alive mobs, including clientless. Excludes /mob/abstract/new_player +var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/abstract/new_player +var/global/list/topic_commands = list() //List of all API commands available +var/global/list/topic_commands_names = list() //List of all API commands available + +var/global/list/landmarks_list = list() //list of all landmarks created +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 +var/global/list/brig_closets = list() //list of all brig secure_closets. Used by brig timers. Probably should be converted to use SSwireless eventually. + +var/global/list/teleportlocs = list() +var/global/list/ghostteleportlocs = list() +var/global/list/centcom_areas = list() +var/global/list/the_station_areas = list() + +var/global/list/implants = list() + +var/global/list/turfs = list() //list of all turfs +var/global/list/areas_by_type = list() +var/global/list/all_areas = list() + +//Languages/species/whitelist. +var/global/list/all_species = list() +var/global/list/all_languages = list() +var/global/list/language_keys = list() // Table of say codes for all languages +var/global/list/whitelisted_species = list("Human") // Species that require a whitelist check. +var/global/list/playable_species = list("Human") // A list of ALL playable species, whitelisted, latejoin or otherwise. + +// Posters +var/global/list/poster_designs = list() + +// Uplinks +var/list/obj/item/device/uplink/world_uplinks = 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() +var/global/list/chargen_disabilities_list = list() + + //Underwear +var/global/list/underwear_m = list("White" = "m1", "Grey" = "m2", "Green" = "m3", "Blue" = "m4", "Black" = "m5", "Mankini" = "m6", "Boxers" = "boxers", "Green and blue boxers" = "boxers_green_and_blue","Loveheart boxers" = "boxers_loveheart","None") //Curse whoever made male/female underwear diffrent colours +var/global/list/underwear_f = list("Red" = "f1", "White" = "f2", "Yellow" = "f3", "Blue" = "f4", "Black" = "f5", "Thong" = "f6", "Black Sports" = "f7","White Sports" = "f8","None") + //undershirt +var/global/list/undershirt_t = list( + "White tank top" = "u1", "Black tank top" = "u2", "Black shirt" = "u3", "White shirt" = "u4", + "Polo" = "polo", "Blue polo" = "bluepolo_s", "Red polo" = "redpolo_s", "Yellow polo" = "grayyellowpolo_s", + "Striped shirt" = "shirt_stripes_s", "NanoTrasen shirt" = "shirt_nano_s", "Tiedye shirt" = "shirt_tiedye_s", + "Green sport shirt" = "greenshirtsport_s", "Red sport shirt" = "redshirtsport_s", "Blue sport shirt" = "blueshirtsport_s", + "Striped tank top " = "tank_stripes_s", "Rainbow tank top" = "tank_rainbow_s", "Longsleeve shirt" = "undershirt_long", + "Striped longsleve shirt" = "longstripe", "Blue striped longsleve shirt" = "longstripe_blue", "None") + + //socks +var/global/list/socks_f = list( + "White normal" = "white_norm", "White short" = "white_short", "White knee" = "white_knee", + "White thigh" = "white_thigh", "Black normal" = "black_norm", "Black short" = "black_short", + "Black knee" = "black_knee", "Black thigh" = "black_thigh", "Striped normal" = "striped_norm", + "Striped short" = "striped_short", "Striped knee" = "striped_knee", "Striped thigh" = "striped_thigh", + "Rainbow normal" = "rainbow_norm", "Rainbow short" = "rainbow_short", "Rainbow knee" = "rainbow_knee", + "Rainbow thigh" = "rainbow_thigh", "Pantyhose" = "pantyhose", "None") + +var/global/list/socks_m = list( + "White normal" = "white_norm", "White short" = "white_short", "White knee" = "white_knee", + "Black normal" = "black_norm", "Black short" = "black_short", "Black knee" = "black_knee", + "Striped normal" = "striped_norm", "Striped short" = "striped_short", "Striped knee" = "striped_knee", + "Rainbow normal" = "rainbow_norm", "Rainbow short" = "rainbow_short", "Rainbow knee" = "rainbow_knee", "None") + + //Backpacks +var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt", "Duffel Bag", "Messenger Bag") +var/global/list/backbagstyles = list("Job-specific", "Grey") +var/global/list/exclude_jobs = list(/datum/job/ai,/datum/job/cyborg, /datum/job/merchant) + +// Visual nets +var/list/datum/visualnet/visual_nets = list() +var/datum/visualnet/camera/cameranet = 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) + +//Cloaking devices +var/global/list/cloaking_devices = list() + +////////////////////////// +/////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 + + sortTim(hair_styles_list, /proc/cmp_text_asc) + sortTim(hair_styles_male_list, /proc/cmp_text_asc) + sortTim(hair_styles_female_list, /proc/cmp_text_asc) + + //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 + + sortTim(facial_hair_styles_list, /proc/cmp_text_asc) + sortTim(facial_hair_styles_male_list, /proc/cmp_text_asc) + sortTim(facial_hair_styles_female_list, /proc/cmp_text_asc) + + //Body markings + 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 + + sortTim(body_marking_styles_list, /proc/cmp_text_asc) + + //Disability datums + paths = subtypesof(/datum/character_disabilities) + for(var/path in paths) + var/datum/character_disabilities/T = new path() + chargen_disabilities_list[T.name] = T + + sortTim(chargen_disabilities_list, /proc/cmp_text_asc) + + //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 + + sortTim(surgery_steps, /proc/cmp_surgery) + + //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 and species. + paths = subtypesof(/datum/language) + for(var/T in paths) + var/datum/language/L = new T + all_languages[L.name] = L + + for (var/language_name in all_languages) + var/datum/language/L = all_languages[language_name] + if(!(L.flags & NONGLOBAL)) + language_keys[lowertext(L.key)] = L + + var/rkey = 0 + paths = subtypesof(/datum/species) + for(var/T in paths) + rkey++ + var/datum/species/S = new T + S.race_key = rkey //Used in mob icon caching. + all_species[S.name] = S + + sortTim(all_species, /proc/cmp_text_asc) + + // The other lists are generated *after* we sort the main one so they don't need sorting too. + for (var/thing in all_species) + var/datum/species/S = all_species[thing] + + if (!(S.spawn_flags & IS_RESTRICTED)) + playable_species += S.name + if(S.spawn_flags & IS_WHITELISTED) + whitelisted_species += S.name + + //Posters + paths = subtypesof(/datum/poster) + for(var/T in paths) + var/datum/poster/P = new T + poster_designs += P + + return 1 + +/* // Uncomment to debug chemical reaction list. +/client/verb/debug_chemical_list() + + for (var/reaction in SSchemistry.chemical_reactions) + . += "SSchemistry.chemical_reactions\[\"[reaction]\"\] = \"[SSchemistry.chemical_reactions[reaction]]\"\n" + if(islist(SSchemistry.chemical_reactions[reaction])) + var/list/L = SSchemistry.chemical_reactions[reaction] + for(var/t in L) + . += " has: [t]\n" + world << . +*/ diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm index 7f372cf1fd8..df15e18a07b 100644 --- a/code/_helpers/icons.dm +++ b/code/_helpers/icons.dm @@ -1,966 +1,966 @@ -/* -IconProcs README - -A BYOND library for manipulating icons and colors - -by Lummox JR - -version 1.0 - -The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation -routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners. - -CHANGING ICONS - -Several new procs have been added to the /icon datum to simplify working with icons. To use them, -remember you first need to setup an /icon var like so: - -var/icon/my_icon = new('iconfile.dmi') - -icon/ChangeOpacity(amount = 1) - A very common operation in DM is to try to make an icon more or less transparent. Making an icon more - transparent is usually much easier than making it less so, however. This proc basically is a frontend - for MapColors() which can change opacity any way you like, in much the same way that SetIntensity() - can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half. - If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque. -icon/GrayScale() - Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact. -icon/ColorTone(tone) - Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an - RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect. - See also the global ColorTone() proc. -icon/MinColors(icon) - The icon is blended with a second icon where the minimum of each RGB pixel is the result. - Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon. -icon/MaxColors(icon) - The icon is blended with a second icon where the maximum of each RGB pixel is the result. - Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon. -icon/Opaque(background = "#000000") - All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify. -icon/BecomeAlphaMask() - You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc. - The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white. -icon/AddAlphaMask(mask) - The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque, - the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent. - Where the mask is translucent, the current icon becomes more transparent. -icon/UseAlphaMask(mask, mode) - Sometimes you may want to take the alpha values from one icon and use them on a different icon. - This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change - so it has the same colors as before but uses the mask for opacity. - -COLOR MANAGEMENT AND HSV - -RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value. - - * The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to - cyan to blue to magenta and back to red. - * The saturation of a color is how much color is in it. A color with low saturation will be more gray, - and with no saturation at all it is a shade of gray. - * The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark, - and no value at all is black. - -Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three -hex digits because it ranges from 0 to 0x5FF. - - * 0 to 0xFF - red to yellow - * 0x100 to 0x1FF - yellow to green - * 0x200 to 0x2FF - green to cyan - * 0x300 to 0x3FF - cyan to blue - * 0x400 to 0x4FF - blue to magenta - * 0x500 to 0x5FF - magenta to red - -Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible), -value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff". - -More than one HSV color can match the same RGB color. - -Here are some procs you can use for color management: - -ReadRGB(rgb) - Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used - that includes alpha, the list will have a fourth item for the alpha value. -hsv(hue, sat, val, apha) - Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa" - format. Alpha is not included in the result if null. -ReadHSV(rgb) - Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that - includes alpha, the list will have a fourth item for the alpha value. -RGBtoHSV(rgb) - Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff". -HSVtoRGB(hsv) - Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa". -BlendRGB(rgb1, rgb2, amount) - Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result; - if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. - The returned value is an RGB or RGBA color. -BlendHSV(hsv1, hsv2, amount) - Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB - blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1, - the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. - The returned value is an HSV or HSVA color. -BlendRGBasHSV(rgb1, rgb2, amount) - Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form. -HueToAngle(hue) - Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue. -AngleToHue(hue) - Converts an angle to a hue in the valid range. -RotateHue(hsv, angle) - Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360. - (Rotating red by 60� produces yellow.) The result is another HSV or HSVA color with the same saturation and value - as the original, but a different hue. -GrayScale(rgb) - Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string. -ColorTone(rgb, tone) - Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of - using strict shades of gray. The tone value is an RGB color; any alpha value is ignored. -*/ - -/* -Get Flat Icon DEMO by DarkCampainger - -This is a test for the get flat icon proc, modified approprietly for icons and their states. -Probably not a good idea to run this unless you want to see how the proc works in detail. -mob - icon = 'old_or_unused.dmi' - icon_state = "green" - - Login() - // Testing image underlays - underlays += image(icon='old_or_unused.dmi',icon_state="red") - underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32) - underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32) - - // Testing image overlays - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32) - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32) - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32) - - // Testing icon file overlays (defaults to mob's state) - overlays += '_flat_demoIcons2.dmi' - - // Testing icon_state overlays (defaults to mob's icon) - overlays += "white" - - // Testing dynamic icon overlays - var/icon/I = icon('old_or_unused.dmi', icon_state="aqua") - I.Shift(NORTH,16,1) - overlays+=I - - // Testing dynamic image overlays - I=image(icon=I,pixel_x = -32, pixel_y = 32) - overlays+=I - - // Testing object types (and layers) - overlays+=/obj/effect/overlayTest - - loc = locate (10,10,1) - verb - Browse_Icon() - set name = "1. Browse Icon" - // Give it a name for the cache - var/iconName = "[ckey(src.name)]_flattened.dmi" - // Send the icon to src's local cache - src<

") - - Output_Icon() - set name = "2. Output Icon" - src<<"Icon is: \icon[getFlatIcon(src)]" - - Label_Icon() - set name = "3. Label Icon" - // Give it a name for the cache - var/iconName = "[ckey(src.name)]_flattened.dmi" - // Copy the file to the rsc manually - var/icon/I = fcopy_rsc(getFlatIcon(src)) - // Send the icon to src's local cache - src< transparent, gray -> translucent white, white -> solid white - 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) - - proc/UseAlphaMask(mask) - Opaque() - AddAlphaMask(mask) - - proc/AddAlphaMask(mask) - var/icon/M = new(mask) - M.Blend("#ffffff", ICON_SUBTRACT) - // apply mask - Blend(M, ICON_ADD) - -/* - HSV format is represented as "#hhhssvv" or "#hhhssvvaa" - - Hue ranges from 0 to 0x5ff (1535) - - 0x000 = red - 0x100 = yellow - 0x200 = green - 0x300 = cyan - 0x400 = blue - 0x500 = magenta - - Saturation is from 0 to 0xff (255) - - More saturation = more color - Less saturation = more gray - - Value ranges from 0 to 0xff (255) - - Higher value means brighter color - */ - -proc/ReadRGB(rgb) - if(!rgb) return - - // interpret the HSV or HSVA value - var/i=1,start=1 - if(text2ascii(rgb) == 35) ++start // skip opening # - var/ch,which=0,r=0,g=0,b=0,alpha=0,usealpha - var/digits=0 - for(i=start, i<=length(rgb), ++i) - ch = text2ascii(rgb, i) - if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break - ++digits - if(digits == 8) break - - var/single = digits < 6 - if(digits != 3 && digits != 4 && digits != 6 && digits != 8) return - if(digits == 4 || digits == 8) usealpha = 1 - for(i=start, digits>0, ++i) - ch = text2ascii(rgb, i) - if(ch >= 48 && ch <= 57) ch -= 48 - else if(ch >= 65 && ch <= 70) ch -= 55 - else if(ch >= 97 && ch <= 102) ch -= 87 - else break - --digits - switch(which) - if(0) - r = (r << 4) | ch - if(single) - r |= r << 4 - ++which - else if(!(digits & 1)) ++which - if(1) - g = (g << 4) | ch - if(single) - g |= g << 4 - ++which - else if(!(digits & 1)) ++which - if(2) - b = (b << 4) | ch - if(single) - b |= b << 4 - ++which - else if(!(digits & 1)) ++which - if(3) - alpha = (alpha << 4) | ch - if(single) alpha |= alpha << 4 - - . = list(r, g, b) - if(usealpha) . += alpha - -proc/ReadHSV(hsv) - if(!hsv) return - - // interpret the HSV or HSVA value - var/i=1,start=1 - if(text2ascii(hsv) == 35) ++start // skip opening # - var/ch,which=0,hue=0,sat=0,val=0,alpha=0,usealpha - var/digits=0 - for(i=start, i<=length(hsv), ++i) - ch = text2ascii(hsv, i) - if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break - ++digits - if(digits == 9) break - if(digits > 7) usealpha = 1 - if(digits <= 4) ++which - if(digits <= 2) ++which - for(i=start, digits>0, ++i) - ch = text2ascii(hsv, i) - if(ch >= 48 && ch <= 57) ch -= 48 - else if(ch >= 65 && ch <= 70) ch -= 55 - else if(ch >= 97 && ch <= 102) ch -= 87 - else break - --digits - switch(which) - if(0) - hue = (hue << 4) | ch - if(digits == (usealpha ? 6 : 4)) ++which - if(1) - sat = (sat << 4) | ch - if(digits == (usealpha ? 4 : 2)) ++which - if(2) - val = (val << 4) | ch - if(digits == (usealpha ? 2 : 0)) ++which - if(3) - alpha = (alpha << 4) | ch - - . = list(hue, sat, val) - if(usealpha) . += alpha - -proc/HSVtoRGB(hsv) - if(!hsv) return "#000000" - var/list/HSV = ReadHSV(hsv) - if(!HSV) return "#000000" - - var/hue = HSV[1] - var/sat = HSV[2] - var/val = HSV[3] - - // Compress hue into easier-to-manage range - hue -= hue >> 8 - if(hue >= 0x5fa) hue -= 0x5fa - - var/hi,mid,lo,r,g,b - hi = val - lo = round((255 - sat) * val / 255, 1) - mid = lo + round(abs(round(hue, 510) - hue) * (hi - lo) / 255, 1) - if(hue >= 765) - if(hue >= 1275) {r=hi; g=lo; b=mid} - else if(hue >= 1020) {r=mid; g=lo; b=hi } - else {r=lo; g=mid; b=hi } - else - if(hue >= 510) {r=lo; g=hi; b=mid} - else if(hue >= 255) {r=mid; g=hi; b=lo } - else {r=hi; g=mid; b=lo } - - return (HSV.len > 3) ? rgb(r,g,b,HSV[4]) : rgb(r,g,b) - -proc/RGBtoHSV(rgb) - if(!rgb) return "#0000000" - var/list/RGB = ReadRGB(rgb) - if(!RGB) return "#0000000" - - var/r = RGB[1] - var/g = RGB[2] - var/b = RGB[3] - var/hi = max(r,g,b) - var/lo = min(r,g,b) - - var/val = hi - var/sat = hi ? round((hi-lo) * 255 / hi, 1) : 0 - var/hue = 0 - - if(sat) - var/dir - var/mid - if(hi == r) - if(lo == b) {hue=0; dir=1; mid=g} - else {hue=1535; dir=-1; mid=b} - else if(hi == g) - if(lo == r) {hue=512; dir=1; mid=b} - else {hue=511; dir=-1; mid=r} - else if(hi == b) - if(lo == g) {hue=1024; dir=1; mid=r} - else {hue=1023; dir=-1; mid=g} - hue += dir * round((mid-lo) * 255 / (hi-lo), 1) - - return hsv(hue, sat, val, (RGB.len>3 ? RGB[4] : null)) - -proc/hsv(hue, sat, val, alpha) - if(hue < 0 || hue >= 1536) hue %= 1536 - if(hue < 0) hue += 1536 - if((hue & 0xFF) == 0xFF) - ++hue - if(hue >= 1536) hue = 0 - if(sat < 0) sat = 0 - if(sat > 255) sat = 255 - if(val < 0) val = 0 - if(val > 255) val = 255 - . = "#" - . += TO_HEX_DIGIT(hue >> 8) - . += TO_HEX_DIGIT(hue >> 4) - . += TO_HEX_DIGIT(hue) - . += TO_HEX_DIGIT(sat >> 4) - . += TO_HEX_DIGIT(sat) - . += TO_HEX_DIGIT(val >> 4) - . += TO_HEX_DIGIT(val) - if(!isnull(alpha)) - if(alpha < 0) alpha = 0 - if(alpha > 255) alpha = 255 - . += TO_HEX_DIGIT(alpha >> 4) - . += TO_HEX_DIGIT(alpha) - -/* - Smooth blend between HSV colors - - amount=0 is the first color - amount=1 is the second color - amount=0.5 is directly between the two colors - - amount<0 or amount>1 are allowed - */ -proc/BlendHSV(hsv1, hsv2, amount) - var/list/HSV1 = ReadHSV(hsv1) - var/list/HSV2 = ReadHSV(hsv2) - - // add missing alpha if needed - if(HSV1.len < HSV2.len) HSV1 += 255 - else if(HSV2.len < HSV1.len) HSV2 += 255 - var/usealpha = HSV1.len > 3 - - // normalize hsv values in case anything is screwy - if(HSV1[1] > 1536) HSV1[1] %= 1536 - if(HSV2[1] > 1536) HSV2[1] %= 1536 - if(HSV1[1] < 0) HSV1[1] += 1536 - if(HSV2[1] < 0) HSV2[1] += 1536 - if(!HSV1[3]) {HSV1[1] = 0; HSV1[2] = 0} - if(!HSV2[3]) {HSV2[1] = 0; HSV2[2] = 0} - - // no value for one color means don't change saturation - if(!HSV1[3]) HSV1[2] = HSV2[2] - if(!HSV2[3]) HSV2[2] = HSV1[2] - // no saturation for one color means don't change hues - if(!HSV1[2]) HSV1[1] = HSV2[1] - if(!HSV2[2]) HSV2[1] = HSV1[1] - - // Compress hues into easier-to-manage range - HSV1[1] -= HSV1[1] >> 8 - HSV2[1] -= HSV2[1] >> 8 - - var/hue_diff = HSV2[1] - HSV1[1] - if(hue_diff > 765) hue_diff -= 1530 - else if(hue_diff <= -765) hue_diff += 1530 - - var/hue = round(HSV1[1] + hue_diff * amount, 1) - var/sat = round(HSV1[2] + (HSV2[2] - HSV1[2]) * amount, 1) - var/val = round(HSV1[3] + (HSV2[3] - HSV1[3]) * amount, 1) - var/alpha = usealpha ? round(HSV1[4] + (HSV2[4] - HSV1[4]) * amount, 1) : null - - // normalize hue - if(hue < 0 || hue >= 1530) hue %= 1530 - if(hue < 0) hue += 1530 - // decompress hue - hue += round(hue / 255) - - return hsv(hue, sat, val, alpha) - -/* - Smooth blend between RGB colors - - amount=0 is the first color - amount=1 is the second color - amount=0.5 is directly between the two colors - - amount<0 or amount>1 are allowed - */ -proc/BlendRGB(rgb1, rgb2, amount) - var/cachekey = "[rgb1]_[rgb2]_[amount]" - . = SSicon_cache.rgb_blend_cache[cachekey] - if (.) - return - - var/list/RGB1 = ReadRGB(rgb1) - var/list/RGB2 = ReadRGB(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 - - . = isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) - SSicon_cache.rgb_blend_cache[cachekey] = . - -proc/BlendRGBasHSV(rgb1, rgb2, amount) - return HSVtoRGB(RGBtoHSV(rgb1), RGBtoHSV(rgb2), amount) - -proc/HueToAngle(hue) - // normalize hsv in case anything is screwy - if(hue < 0 || hue >= 1536) hue %= 1536 - if(hue < 0) hue += 1536 - // Compress hue into easier-to-manage range - hue -= hue >> 8 - return hue / (1530/360) - -proc/AngleToHue(angle) - // normalize hsv in case anything is screwy - if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) - var/hue = angle * (1530/360) - // Decompress hue - hue += round(hue / 255) - return hue - - -// positive angle rotates forward through red->green->blue -proc/RotateHue(hsv, angle) - var/list/HSV = ReadHSV(hsv) - - // normalize hsv in case anything is screwy - if(HSV[1] >= 1536) HSV[1] %= 1536 - if(HSV[1] < 0) HSV[1] += 1536 - - // Compress hue into easier-to-manage range - HSV[1] -= HSV[1] >> 8 - - if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) - HSV[1] = round(HSV[1] + angle * (1530/360), 1) - - // normalize hue - if(HSV[1] < 0 || HSV[1] >= 1530) HSV[1] %= 1530 - if(HSV[1] < 0) HSV[1] += 1530 - // decompress hue - HSV[1] += round(HSV[1] / 255) - - return hsv(HSV[1], HSV[2], HSV[3], (HSV.len > 3 ? HSV[4] : null)) - -// Convert an rgb color to grayscale -proc/GrayScale(rgb) - var/list/RGB = ReadRGB(rgb) - var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 - return (RGB.len > 3) ? rgb(gray, gray, gray, RGB[4]) : rgb(gray, gray, gray) - -// Change grayscale color to black->tone->white range -proc/ColorTone(rgb, tone) - var/list/RGB = ReadRGB(rgb) - var/list/TONE = ReadRGB(tone) - - var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 - var/tone_gray = TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11 - - if(gray <= tone_gray) return BlendRGB("#000000", tone, gray/(tone_gray || 1)) - else return BlendRGB(tone, "#ffffff", (gray-tone_gray)/((255-tone_gray) || 1)) - - -/* -Get flat icon by DarkCampainger. As it says on the tin, will return an icon with all the overlays -as a single icon. Useful for when you want to manipulate an icon via the above as overlays are not normally included. -*/ - -/proc/getFlatIcon(image/A, defdir=2, deficon=null, defstate="", defblend=BLEND_DEFAULT, always_use_defdir = 0) - // We start with a blank canvas, otherwise some icon procs crash silently - var/icon/flat = icon('icons/effects/effects.dmi', "nothing") // Final flattened icon - if(!A) - return flat - if(A.alpha <= 0) - return flat - var/noIcon = FALSE - - var/curicon - if(A.icon) - curicon = A.icon - else - curicon = deficon - - if(!curicon) - noIcon = TRUE // Do not render this object. - - var/curstate - if(A.icon_state) - curstate = A.icon_state - else - curstate = defstate - - if(!noIcon && !(curstate in icon_states(curicon))) - if("" in icon_states(curicon)) - curstate = "" - else - noIcon = TRUE // Do not render this object. - - var/curdir - if(A.dir != 2 && !always_use_defdir) - curdir = A.dir - else - curdir = defdir - - var/curblend - if(A.blend_mode == BLEND_DEFAULT) - curblend = defblend - else - curblend = A.blend_mode - - // 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=curdir) - 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 - var/list/process = A.underlays // Current list being processed - var/pSet=0 // Which list is being processed: 0 = underlays, 1 = overlays - var/curIndex=1 // index of 'current' in list being processed - var/current // Current overlay being sorted - var/currentLayer // Calculated layer that overlay appears on (special case for FLOAT_LAYER) - var/compare // The overlay 'add' is being compared against - var/cmpIndex // The index in the layers list of 'compare' - while(TRUE) - if(curIndex<=process.len) - current = process[curIndex] - if(!current) - curIndex++ //Try the next layer - continue - currentLayer = current:layer - if(currentLayer<0) // Special case for FLY_LAYER - if(currentLayer <= -1000) return flat - if(pSet == 0) // Underlay - currentLayer = A.layer+currentLayer/1000 - else // Overlay - currentLayer = A.layer+(1000+currentLayer)/1000 - - // Sort add into layers list - for(cmpIndex=1,cmpIndex<=layers.len,cmpIndex++) - compare = layers[cmpIndex] - if(currentLayer < layers[compare]) // Associated value is the calculated layer - layers.Insert(cmpIndex,current) - layers[current] = currentLayer - break - if(cmpIndex>layers.len) // Reached end of list without inserting - layers[current]=currentLayer // Place at end - - curIndex++ - - if(curIndex>process.len) - if(pSet == 0) // Switch to overlays - curIndex = 1 - pSet = 1 - process = A.overlays - else // All done - break - - var/icon/add // Icon of overlay being added - - // Current dimensions of flattened icon - var/flatX1 = 1 - var/flatX2 = flat.Width() - var/flatY1 = 1 - var/flatY2=flat.Height() - // Dimensions of overlay being added - var/addX1 - var/addX2 - var/addY1 - var/addY2 - - for(var/I in layers) - - 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, I:dir) - // This checks for a silent failure mode of the icon routine. If the requested dir - // doesn't exist in this icon state it returns a 32x32 icon with 0 alpha. - if (I:dir != SOUTH && add.Width() == 32 && add.Height() == 32) - // Check every pixel for blank (computationally expensive, but the process is limited - // by the amount of film on the station, only happens when we hit something that's - // turned, and bails at the very first pixel it sees. - var/blankpixel; - for(var/y;y<=32;y++) - for(var/x;x<32;x++) - blankpixel = isnull(add.GetPixel(x,y)) - if(!blankpixel) - break - if(!blankpixel) - break - // If we ALWAYS returned a null (which happens when GetPixel encounters something with alpha 0) - if (blankpixel) - // Pull the default direction. - add = icon(I:icon, I:icon_state) - else // 'I' is an appearance object. - if (istype(A,/atom)) - var/atom/At = A - if (At.gfi_layer_rotation == GFI_ROTATION_DEFDIR) - add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend, TRUE) - else if (At.gfi_layer_rotation == GFI_ROTATION_OVERDIR) - var/image/Im = I - add = getFlatIcon(new/image(I), Im.dir, curicon, curstate, curblend, TRUE) - else - add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend, always_use_defdir) - else - add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend, always_use_defdir) - - // Find the new dimensions of the flat icon to fit the added overlay - addX1 = min(flatX1, I:pixel_x+1) - addX2 = max(flatX2, I:pixel_x+add.Width()) - addY1 = min(flatY1, I:pixel_y+1) - addY2 = max(flatY2, I:pixel_y+add.Height()) - - if(addX1!=flatX1 || addX2!=flatX2 || addY1!=flatY1 || addY2!=flatY2) - // Resize the flattened icon so the new icon fits - flat.Crop(addX1-flatX1+1, addY1-flatY1+1, addX2-flatX1+1, addY2-flatY1+1) - flatX1=addX1;flatX2=addX2 - flatY1=addY1;flatY2=addY2 - - var/iconmode - if(I in A.overlays) - iconmode = ICON_OVERLAY - else if(I in A.underlays) - iconmode = ICON_UNDERLAY - else - iconmode = blendMode2iconMode(curblend) - // Blend the overlay into the flattened icon - flat.Blend(add, iconmode, I:pixel_x + 2 - flatX1, I:pixel_y + 2 - flatY1) - - if(A.color) - flat.Blend(A.color, ICON_MULTIPLY) - if(A.alpha < 255) - flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) - - return icon(flat, "", SOUTH) - -/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. - -/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)//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. - flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. - flat_icon.ChangeOpacity(0.5)//Make it half transparent. - 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 - -proc/adjust_brightness(var/color, var/value) - if (!color) return "#FFFFFF" - if (!value) return color - - var/list/RGB = ReadRGB(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 - -/* -generate_image function generates image of specified range and location -arguments tx, ty, tz are target coordinates (requred), range defines render distance to opposite corner (requred) -cap_mode is capturing mode (optional), user is capturing mob (requred only wehen cap_mode = CAPTURE_MODE_REGULAR), -lighting determines lighting capturing (optional), suppress_errors suppreses errors and continues to capture (optional). -*/ -proc/generate_image(var/tx as num, var/ty as num, var/tz as num, var/range as num, var/cap_mode = CAPTURE_MODE_PARTIAL, var/mob/living/user, var/lighting = 1, var/suppress_errors = 1) - var/list/turfstocapture = list() - //Lines below determine what tiles will be rendered - for(var/xoff = 0 to range) - for(var/yoff = 0 to range) - var/turf/T = locate(tx + xoff,ty + yoff,tz) - if(T) - if(cap_mode == CAPTURE_MODE_REGULAR) - if(user.can_capture_turf(T)) - turfstocapture.Add(T) - continue - else - turfstocapture.Add(T) - else - //Capture includes non-existan turfs - if(!suppress_errors) - return - - return generate_image_from_turfs(locate(tx, ty, tz), turfstocapture, range, cap_mode, user, lighting) - -/proc/generate_image_from_turfs(turf/topleft, list/turf/turfstocapture, range as num, cap_mode = CAPTURE_MODE_PARTIAL, mob/living/user, lighting = TRUE) - var/tx = topleft.x - var/ty = topleft.y - //Lines below determine what objects will be rendered - var/list/atoms = list() - for(var/turf/T in turfstocapture) - atoms += T - for(var/atom/A in T) - if(istype(A, /atom/movable/lighting_overlay)) //Special case for lighting - continue - - if(A.invisibility) - continue - - atoms += A - - //Lines below actually render all collected data - atoms = sort_atoms_by_layer(atoms) - var/icon/cap = icon('icons/effects/96x96.dmi', "") - cap.Scale(range*32, range*32) - cap.Blend("#000", ICON_OVERLAY) - for(var/atom/A in atoms) - if(A) - var/icon/img = getFlatIcon(A) - if(istype(img, /icon)) - if(istype(A, /mob/living) && A:lying) - img.BecomeLying() - var/xoff = (A.x - tx) * 32 - var/yoff = (A.y - ty) * 32 - cap.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff) - - if (lighting) - for (var/turf/T in turfstocapture) - var/icon/im = new(LIGHTING_ICON, "blank") - var/color = T.get_avg_color() // We're going to lose some detail, but it's all we can do without color matrixes. - if (color) - im.Blend(color, ICON_MULTIPLY) - cap.Blend(im, ICON_MULTIPLY, (T.x - tx) * 32, (T.y - ty) * 32) - - return cap - -proc/percentage_to_colour(var/P) - //Takes a value between 0-1 - //Returns a colour - pure green if 1, pure red if 0 - //Inbetween values will gradiant through green, yellow, orange, red - - - var/green = min(1, P*2)*255 - var/red = 255 - (min(1, (P-0.5)*2)*255) - //var/green = (max(0, P-0.5)*2)*255 - //var/red = 255 - (min(1, P*2)*255) - - return rgb(red,green,0) +/* +IconProcs README + +A BYOND library for manipulating icons and colors + +by Lummox JR + +version 1.0 + +The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation +routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners. + +CHANGING ICONS + +Several new procs have been added to the /icon datum to simplify working with icons. To use them, +remember you first need to setup an /icon var like so: + +var/icon/my_icon = new('iconfile.dmi') + +icon/ChangeOpacity(amount = 1) + A very common operation in DM is to try to make an icon more or less transparent. Making an icon more + transparent is usually much easier than making it less so, however. This proc basically is a frontend + for MapColors() which can change opacity any way you like, in much the same way that SetIntensity() + can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half. + If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque. +icon/GrayScale() + Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact. +icon/ColorTone(tone) + Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an + RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect. + See also the global ColorTone() proc. +icon/MinColors(icon) + The icon is blended with a second icon where the minimum of each RGB pixel is the result. + Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon. +icon/MaxColors(icon) + The icon is blended with a second icon where the maximum of each RGB pixel is the result. + Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon. +icon/Opaque(background = "#000000") + All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify. +icon/BecomeAlphaMask() + You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc. + The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white. +icon/AddAlphaMask(mask) + The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque, + the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent. + Where the mask is translucent, the current icon becomes more transparent. +icon/UseAlphaMask(mask, mode) + Sometimes you may want to take the alpha values from one icon and use them on a different icon. + This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change + so it has the same colors as before but uses the mask for opacity. + +COLOR MANAGEMENT AND HSV + +RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value. + + * The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to + cyan to blue to magenta and back to red. + * The saturation of a color is how much color is in it. A color with low saturation will be more gray, + and with no saturation at all it is a shade of gray. + * The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark, + and no value at all is black. + +Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three +hex digits because it ranges from 0 to 0x5FF. + + * 0 to 0xFF - red to yellow + * 0x100 to 0x1FF - yellow to green + * 0x200 to 0x2FF - green to cyan + * 0x300 to 0x3FF - cyan to blue + * 0x400 to 0x4FF - blue to magenta + * 0x500 to 0x5FF - magenta to red + +Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible), +value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff". + +More than one HSV color can match the same RGB color. + +Here are some procs you can use for color management: + +ReadRGB(rgb) + Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used + that includes alpha, the list will have a fourth item for the alpha value. +hsv(hue, sat, val, apha) + Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa" + format. Alpha is not included in the result if null. +ReadHSV(rgb) + Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that + includes alpha, the list will have a fourth item for the alpha value. +RGBtoHSV(rgb) + Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff". +HSVtoRGB(hsv) + Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa". +BlendRGB(rgb1, rgb2, amount) + Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result; + if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. + The returned value is an RGB or RGBA color. +BlendHSV(hsv1, hsv2, amount) + Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB + blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1, + the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. + The returned value is an HSV or HSVA color. +BlendRGBasHSV(rgb1, rgb2, amount) + Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form. +HueToAngle(hue) + Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue. +AngleToHue(hue) + Converts an angle to a hue in the valid range. +RotateHue(hsv, angle) + Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360. + (Rotating red by 60� produces yellow.) The result is another HSV or HSVA color with the same saturation and value + as the original, but a different hue. +GrayScale(rgb) + Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string. +ColorTone(rgb, tone) + Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of + using strict shades of gray. The tone value is an RGB color; any alpha value is ignored. +*/ + +/* +Get Flat Icon DEMO by DarkCampainger + +This is a test for the get flat icon proc, modified approprietly for icons and their states. +Probably not a good idea to run this unless you want to see how the proc works in detail. +mob + icon = 'old_or_unused.dmi' + icon_state = "green" + + Login() + // Testing image underlays + underlays += image(icon='old_or_unused.dmi',icon_state="red") + underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32) + underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32) + + // Testing image overlays + overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32) + overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32) + overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32) + + // Testing icon file overlays (defaults to mob's state) + overlays += '_flat_demoIcons2.dmi' + + // Testing icon_state overlays (defaults to mob's icon) + overlays += "white" + + // Testing dynamic icon overlays + var/icon/I = icon('old_or_unused.dmi', icon_state="aqua") + I.Shift(NORTH,16,1) + overlays+=I + + // Testing dynamic image overlays + I=image(icon=I,pixel_x = -32, pixel_y = 32) + overlays+=I + + // Testing object types (and layers) + overlays+=/obj/effect/overlayTest + + loc = locate (10,10,1) + verb + Browse_Icon() + set name = "1. Browse Icon" + // Give it a name for the cache + var/iconName = "[ckey(src.name)]_flattened.dmi" + // Send the icon to src's local cache + src<

") + + Output_Icon() + set name = "2. Output Icon" + src<<"Icon is: \icon[getFlatIcon(src)]" + + Label_Icon() + set name = "3. Label Icon" + // Give it a name for the cache + var/iconName = "[ckey(src.name)]_flattened.dmi" + // Copy the file to the rsc manually + var/icon/I = fcopy_rsc(getFlatIcon(src)) + // Send the icon to src's local cache + src< transparent, gray -> translucent white, white -> solid white + 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) + + proc/UseAlphaMask(mask) + Opaque() + AddAlphaMask(mask) + + proc/AddAlphaMask(mask) + var/icon/M = new(mask) + M.Blend("#ffffff", ICON_SUBTRACT) + // apply mask + Blend(M, ICON_ADD) + +/* + HSV format is represented as "#hhhssvv" or "#hhhssvvaa" + + Hue ranges from 0 to 0x5ff (1535) + + 0x000 = red + 0x100 = yellow + 0x200 = green + 0x300 = cyan + 0x400 = blue + 0x500 = magenta + + Saturation is from 0 to 0xff (255) + + More saturation = more color + Less saturation = more gray + + Value ranges from 0 to 0xff (255) + + Higher value means brighter color + */ + +proc/ReadRGB(rgb) + if(!rgb) return + + // interpret the HSV or HSVA value + var/i=1,start=1 + if(text2ascii(rgb) == 35) ++start // skip opening # + var/ch,which=0,r=0,g=0,b=0,alpha=0,usealpha + var/digits=0 + for(i=start, i<=length(rgb), ++i) + ch = text2ascii(rgb, i) + if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break + ++digits + if(digits == 8) break + + var/single = digits < 6 + if(digits != 3 && digits != 4 && digits != 6 && digits != 8) return + if(digits == 4 || digits == 8) usealpha = 1 + for(i=start, digits>0, ++i) + ch = text2ascii(rgb, i) + if(ch >= 48 && ch <= 57) ch -= 48 + else if(ch >= 65 && ch <= 70) ch -= 55 + else if(ch >= 97 && ch <= 102) ch -= 87 + else break + --digits + switch(which) + if(0) + r = (r << 4) | ch + if(single) + r |= r << 4 + ++which + else if(!(digits & 1)) ++which + if(1) + g = (g << 4) | ch + if(single) + g |= g << 4 + ++which + else if(!(digits & 1)) ++which + if(2) + b = (b << 4) | ch + if(single) + b |= b << 4 + ++which + else if(!(digits & 1)) ++which + if(3) + alpha = (alpha << 4) | ch + if(single) alpha |= alpha << 4 + + . = list(r, g, b) + if(usealpha) . += alpha + +proc/ReadHSV(hsv) + if(!hsv) return + + // interpret the HSV or HSVA value + var/i=1,start=1 + if(text2ascii(hsv) == 35) ++start // skip opening # + var/ch,which=0,hue=0,sat=0,val=0,alpha=0,usealpha + var/digits=0 + for(i=start, i<=length(hsv), ++i) + ch = text2ascii(hsv, i) + if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break + ++digits + if(digits == 9) break + if(digits > 7) usealpha = 1 + if(digits <= 4) ++which + if(digits <= 2) ++which + for(i=start, digits>0, ++i) + ch = text2ascii(hsv, i) + if(ch >= 48 && ch <= 57) ch -= 48 + else if(ch >= 65 && ch <= 70) ch -= 55 + else if(ch >= 97 && ch <= 102) ch -= 87 + else break + --digits + switch(which) + if(0) + hue = (hue << 4) | ch + if(digits == (usealpha ? 6 : 4)) ++which + if(1) + sat = (sat << 4) | ch + if(digits == (usealpha ? 4 : 2)) ++which + if(2) + val = (val << 4) | ch + if(digits == (usealpha ? 2 : 0)) ++which + if(3) + alpha = (alpha << 4) | ch + + . = list(hue, sat, val) + if(usealpha) . += alpha + +proc/HSVtoRGB(hsv) + if(!hsv) return "#000000" + var/list/HSV = ReadHSV(hsv) + if(!HSV) return "#000000" + + var/hue = HSV[1] + var/sat = HSV[2] + var/val = HSV[3] + + // Compress hue into easier-to-manage range + hue -= hue >> 8 + if(hue >= 0x5fa) hue -= 0x5fa + + var/hi,mid,lo,r,g,b + hi = val + lo = round((255 - sat) * val / 255, 1) + mid = lo + round(abs(round(hue, 510) - hue) * (hi - lo) / 255, 1) + if(hue >= 765) + if(hue >= 1275) {r=hi; g=lo; b=mid} + else if(hue >= 1020) {r=mid; g=lo; b=hi } + else {r=lo; g=mid; b=hi } + else + if(hue >= 510) {r=lo; g=hi; b=mid} + else if(hue >= 255) {r=mid; g=hi; b=lo } + else {r=hi; g=mid; b=lo } + + return (HSV.len > 3) ? rgb(r,g,b,HSV[4]) : rgb(r,g,b) + +proc/RGBtoHSV(rgb) + if(!rgb) return "#0000000" + var/list/RGB = ReadRGB(rgb) + if(!RGB) return "#0000000" + + var/r = RGB[1] + var/g = RGB[2] + var/b = RGB[3] + var/hi = max(r,g,b) + var/lo = min(r,g,b) + + var/val = hi + var/sat = hi ? round((hi-lo) * 255 / hi, 1) : 0 + var/hue = 0 + + if(sat) + var/dir + var/mid + if(hi == r) + if(lo == b) {hue=0; dir=1; mid=g} + else {hue=1535; dir=-1; mid=b} + else if(hi == g) + if(lo == r) {hue=512; dir=1; mid=b} + else {hue=511; dir=-1; mid=r} + else if(hi == b) + if(lo == g) {hue=1024; dir=1; mid=r} + else {hue=1023; dir=-1; mid=g} + hue += dir * round((mid-lo) * 255 / (hi-lo), 1) + + return hsv(hue, sat, val, (RGB.len>3 ? RGB[4] : null)) + +proc/hsv(hue, sat, val, alpha) + if(hue < 0 || hue >= 1536) hue %= 1536 + if(hue < 0) hue += 1536 + if((hue & 0xFF) == 0xFF) + ++hue + if(hue >= 1536) hue = 0 + if(sat < 0) sat = 0 + if(sat > 255) sat = 255 + if(val < 0) val = 0 + if(val > 255) val = 255 + . = "#" + . += TO_HEX_DIGIT(hue >> 8) + . += TO_HEX_DIGIT(hue >> 4) + . += TO_HEX_DIGIT(hue) + . += TO_HEX_DIGIT(sat >> 4) + . += TO_HEX_DIGIT(sat) + . += TO_HEX_DIGIT(val >> 4) + . += TO_HEX_DIGIT(val) + if(!isnull(alpha)) + if(alpha < 0) alpha = 0 + if(alpha > 255) alpha = 255 + . += TO_HEX_DIGIT(alpha >> 4) + . += TO_HEX_DIGIT(alpha) + +/* + Smooth blend between HSV colors + + amount=0 is the first color + amount=1 is the second color + amount=0.5 is directly between the two colors + + amount<0 or amount>1 are allowed + */ +proc/BlendHSV(hsv1, hsv2, amount) + var/list/HSV1 = ReadHSV(hsv1) + var/list/HSV2 = ReadHSV(hsv2) + + // add missing alpha if needed + if(HSV1.len < HSV2.len) HSV1 += 255 + else if(HSV2.len < HSV1.len) HSV2 += 255 + var/usealpha = HSV1.len > 3 + + // normalize hsv values in case anything is screwy + if(HSV1[1] > 1536) HSV1[1] %= 1536 + if(HSV2[1] > 1536) HSV2[1] %= 1536 + if(HSV1[1] < 0) HSV1[1] += 1536 + if(HSV2[1] < 0) HSV2[1] += 1536 + if(!HSV1[3]) {HSV1[1] = 0; HSV1[2] = 0} + if(!HSV2[3]) {HSV2[1] = 0; HSV2[2] = 0} + + // no value for one color means don't change saturation + if(!HSV1[3]) HSV1[2] = HSV2[2] + if(!HSV2[3]) HSV2[2] = HSV1[2] + // no saturation for one color means don't change hues + if(!HSV1[2]) HSV1[1] = HSV2[1] + if(!HSV2[2]) HSV2[1] = HSV1[1] + + // Compress hues into easier-to-manage range + HSV1[1] -= HSV1[1] >> 8 + HSV2[1] -= HSV2[1] >> 8 + + var/hue_diff = HSV2[1] - HSV1[1] + if(hue_diff > 765) hue_diff -= 1530 + else if(hue_diff <= -765) hue_diff += 1530 + + var/hue = round(HSV1[1] + hue_diff * amount, 1) + var/sat = round(HSV1[2] + (HSV2[2] - HSV1[2]) * amount, 1) + var/val = round(HSV1[3] + (HSV2[3] - HSV1[3]) * amount, 1) + var/alpha = usealpha ? round(HSV1[4] + (HSV2[4] - HSV1[4]) * amount, 1) : null + + // normalize hue + if(hue < 0 || hue >= 1530) hue %= 1530 + if(hue < 0) hue += 1530 + // decompress hue + hue += round(hue / 255) + + return hsv(hue, sat, val, alpha) + +/* + Smooth blend between RGB colors + + amount=0 is the first color + amount=1 is the second color + amount=0.5 is directly between the two colors + + amount<0 or amount>1 are allowed + */ +proc/BlendRGB(rgb1, rgb2, amount) + var/cachekey = "[rgb1]_[rgb2]_[amount]" + . = SSicon_cache.rgb_blend_cache[cachekey] + if (.) + return + + var/list/RGB1 = ReadRGB(rgb1) + var/list/RGB2 = ReadRGB(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 + + . = isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) + SSicon_cache.rgb_blend_cache[cachekey] = . + +proc/BlendRGBasHSV(rgb1, rgb2, amount) + return HSVtoRGB(RGBtoHSV(rgb1), RGBtoHSV(rgb2), amount) + +proc/HueToAngle(hue) + // normalize hsv in case anything is screwy + if(hue < 0 || hue >= 1536) hue %= 1536 + if(hue < 0) hue += 1536 + // Compress hue into easier-to-manage range + hue -= hue >> 8 + return hue / (1530/360) + +proc/AngleToHue(angle) + // normalize hsv in case anything is screwy + if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) + var/hue = angle * (1530/360) + // Decompress hue + hue += round(hue / 255) + return hue + + +// positive angle rotates forward through red->green->blue +proc/RotateHue(hsv, angle) + var/list/HSV = ReadHSV(hsv) + + // normalize hsv in case anything is screwy + if(HSV[1] >= 1536) HSV[1] %= 1536 + if(HSV[1] < 0) HSV[1] += 1536 + + // Compress hue into easier-to-manage range + HSV[1] -= HSV[1] >> 8 + + if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) + HSV[1] = round(HSV[1] + angle * (1530/360), 1) + + // normalize hue + if(HSV[1] < 0 || HSV[1] >= 1530) HSV[1] %= 1530 + if(HSV[1] < 0) HSV[1] += 1530 + // decompress hue + HSV[1] += round(HSV[1] / 255) + + return hsv(HSV[1], HSV[2], HSV[3], (HSV.len > 3 ? HSV[4] : null)) + +// Convert an rgb color to grayscale +proc/GrayScale(rgb) + var/list/RGB = ReadRGB(rgb) + var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 + return (RGB.len > 3) ? rgb(gray, gray, gray, RGB[4]) : rgb(gray, gray, gray) + +// Change grayscale color to black->tone->white range +proc/ColorTone(rgb, tone) + var/list/RGB = ReadRGB(rgb) + var/list/TONE = ReadRGB(tone) + + var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 + var/tone_gray = TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11 + + if(gray <= tone_gray) return BlendRGB("#000000", tone, gray/(tone_gray || 1)) + else return BlendRGB(tone, "#ffffff", (gray-tone_gray)/((255-tone_gray) || 1)) + + +/* +Get flat icon by DarkCampainger. As it says on the tin, will return an icon with all the overlays +as a single icon. Useful for when you want to manipulate an icon via the above as overlays are not normally included. +*/ + +/proc/getFlatIcon(image/A, defdir=2, deficon=null, defstate="", defblend=BLEND_DEFAULT, always_use_defdir = 0) + // We start with a blank canvas, otherwise some icon procs crash silently + var/icon/flat = icon('icons/effects/effects.dmi', "nothing") // Final flattened icon + if(!A) + return flat + if(A.alpha <= 0) + return flat + var/noIcon = FALSE + + var/curicon + if(A.icon) + curicon = A.icon + else + curicon = deficon + + if(!curicon) + noIcon = TRUE // Do not render this object. + + var/curstate + if(A.icon_state) + curstate = A.icon_state + else + curstate = defstate + + if(!noIcon && !(curstate in icon_states(curicon))) + if("" in icon_states(curicon)) + curstate = "" + else + noIcon = TRUE // Do not render this object. + + var/curdir + if(A.dir != 2 && !always_use_defdir) + curdir = A.dir + else + curdir = defdir + + var/curblend + if(A.blend_mode == BLEND_DEFAULT) + curblend = defblend + else + curblend = A.blend_mode + + // 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=curdir) + 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 + var/list/process = A.underlays // Current list being processed + var/pSet=0 // Which list is being processed: 0 = underlays, 1 = overlays + var/curIndex=1 // index of 'current' in list being processed + var/current // Current overlay being sorted + var/currentLayer // Calculated layer that overlay appears on (special case for FLOAT_LAYER) + var/compare // The overlay 'add' is being compared against + var/cmpIndex // The index in the layers list of 'compare' + while(TRUE) + if(curIndex<=process.len) + current = process[curIndex] + if(!current) + curIndex++ //Try the next layer + continue + currentLayer = current:layer + if(currentLayer<0) // Special case for FLY_LAYER + if(currentLayer <= -1000) return flat + if(pSet == 0) // Underlay + currentLayer = A.layer+currentLayer/1000 + else // Overlay + currentLayer = A.layer+(1000+currentLayer)/1000 + + // Sort add into layers list + for(cmpIndex=1,cmpIndex<=layers.len,cmpIndex++) + compare = layers[cmpIndex] + if(currentLayer < layers[compare]) // Associated value is the calculated layer + layers.Insert(cmpIndex,current) + layers[current] = currentLayer + break + if(cmpIndex>layers.len) // Reached end of list without inserting + layers[current]=currentLayer // Place at end + + curIndex++ + + if(curIndex>process.len) + if(pSet == 0) // Switch to overlays + curIndex = 1 + pSet = 1 + process = A.overlays + else // All done + break + + var/icon/add // Icon of overlay being added + + // Current dimensions of flattened icon + var/flatX1 = 1 + var/flatX2 = flat.Width() + var/flatY1 = 1 + var/flatY2=flat.Height() + // Dimensions of overlay being added + var/addX1 + var/addX2 + var/addY1 + var/addY2 + + for(var/I in layers) + + 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, I:dir) + // This checks for a silent failure mode of the icon routine. If the requested dir + // doesn't exist in this icon state it returns a 32x32 icon with 0 alpha. + if (I:dir != SOUTH && add.Width() == 32 && add.Height() == 32) + // Check every pixel for blank (computationally expensive, but the process is limited + // by the amount of film on the station, only happens when we hit something that's + // turned, and bails at the very first pixel it sees. + var/blankpixel; + for(var/y;y<=32;y++) + for(var/x;x<32;x++) + blankpixel = isnull(add.GetPixel(x,y)) + if(!blankpixel) + break + if(!blankpixel) + break + // If we ALWAYS returned a null (which happens when GetPixel encounters something with alpha 0) + if (blankpixel) + // Pull the default direction. + add = icon(I:icon, I:icon_state) + else // 'I' is an appearance object. + if (istype(A,/atom)) + var/atom/At = A + if (At.gfi_layer_rotation == GFI_ROTATION_DEFDIR) + add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend, TRUE) + else if (At.gfi_layer_rotation == GFI_ROTATION_OVERDIR) + var/image/Im = I + add = getFlatIcon(new/image(I), Im.dir, curicon, curstate, curblend, TRUE) + else + add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend, always_use_defdir) + else + add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend, always_use_defdir) + + // Find the new dimensions of the flat icon to fit the added overlay + addX1 = min(flatX1, I:pixel_x+1) + addX2 = max(flatX2, I:pixel_x+add.Width()) + addY1 = min(flatY1, I:pixel_y+1) + addY2 = max(flatY2, I:pixel_y+add.Height()) + + if(addX1!=flatX1 || addX2!=flatX2 || addY1!=flatY1 || addY2!=flatY2) + // Resize the flattened icon so the new icon fits + flat.Crop(addX1-flatX1+1, addY1-flatY1+1, addX2-flatX1+1, addY2-flatY1+1) + flatX1=addX1;flatX2=addX2 + flatY1=addY1;flatY2=addY2 + + var/iconmode + if(I in A.overlays) + iconmode = ICON_OVERLAY + else if(I in A.underlays) + iconmode = ICON_UNDERLAY + else + iconmode = blendMode2iconMode(curblend) + // Blend the overlay into the flattened icon + flat.Blend(add, iconmode, I:pixel_x + 2 - flatX1, I:pixel_y + 2 - flatY1) + + if(A.color) + flat.Blend(A.color, ICON_MULTIPLY) + if(A.alpha < 255) + flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) + + return icon(flat, "", SOUTH) + +/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. + +/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)//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. + flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. + flat_icon.ChangeOpacity(0.5)//Make it half transparent. + 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 + +proc/adjust_brightness(var/color, var/value) + if (!color) return "#FFFFFF" + if (!value) return color + + var/list/RGB = ReadRGB(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 + +/* +generate_image function generates image of specified range and location +arguments tx, ty, tz are target coordinates (requred), range defines render distance to opposite corner (requred) +cap_mode is capturing mode (optional), user is capturing mob (requred only wehen cap_mode = CAPTURE_MODE_REGULAR), +lighting determines lighting capturing (optional), suppress_errors suppreses errors and continues to capture (optional). +*/ +proc/generate_image(var/tx as num, var/ty as num, var/tz as num, var/range as num, var/cap_mode = CAPTURE_MODE_PARTIAL, var/mob/living/user, var/lighting = 1, var/suppress_errors = 1) + var/list/turfstocapture = list() + //Lines below determine what tiles will be rendered + for(var/xoff = 0 to range) + for(var/yoff = 0 to range) + var/turf/T = locate(tx + xoff,ty + yoff,tz) + if(T) + if(cap_mode == CAPTURE_MODE_REGULAR) + if(user.can_capture_turf(T)) + turfstocapture.Add(T) + continue + else + turfstocapture.Add(T) + else + //Capture includes non-existan turfs + if(!suppress_errors) + return + + return generate_image_from_turfs(locate(tx, ty, tz), turfstocapture, range, cap_mode, user, lighting) + +/proc/generate_image_from_turfs(turf/topleft, list/turf/turfstocapture, range as num, cap_mode = CAPTURE_MODE_PARTIAL, mob/living/user, lighting = TRUE) + var/tx = topleft.x + var/ty = topleft.y + //Lines below determine what objects will be rendered + var/list/atoms = list() + for(var/turf/T in turfstocapture) + atoms += T + for(var/atom/A in T) + if(istype(A, /atom/movable/lighting_overlay)) //Special case for lighting + continue + + if(A.invisibility) + continue + + atoms += A + + //Lines below actually render all collected data + atoms = sort_atoms_by_layer(atoms) + var/icon/cap = icon('icons/effects/96x96.dmi', "") + cap.Scale(range*32, range*32) + cap.Blend("#000", ICON_OVERLAY) + for(var/atom/A in atoms) + if(A) + var/icon/img = getFlatIcon(A) + if(istype(img, /icon)) + if(istype(A, /mob/living) && A:lying) + img.BecomeLying() + var/xoff = (A.x - tx) * 32 + var/yoff = (A.y - ty) * 32 + cap.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff) + + if (lighting) + for (var/turf/T in turfstocapture) + var/icon/im = new(LIGHTING_ICON, "blank") + var/color = T.get_avg_color() // We're going to lose some detail, but it's all we can do without color matrixes. + if (color) + im.Blend(color, ICON_MULTIPLY) + cap.Blend(im, ICON_MULTIPLY, (T.x - tx) * 32, (T.y - ty) * 32) + + return cap + +proc/percentage_to_colour(var/P) + //Takes a value between 0-1 + //Returns a colour - pure green if 1, pure red if 0 + //Inbetween values will gradiant through green, yellow, orange, red + + + var/green = min(1, P*2)*255 + var/red = 255 - (min(1, (P-0.5)*2)*255) + //var/green = (max(0, P-0.5)*2)*255 + //var/red = 255 - (min(1, P*2)*255) + + return rgb(red,green,0) diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm index c6ef65b044d..a09e7304877 100644 --- a/code/_helpers/lists.dm +++ b/code/_helpers/lists.dm @@ -1,562 +1,562 @@ -/* - * Holds procs to help with list operations - * Contains groups: - * Misc - * Sorting - */ - -/* - * Misc - */ - -//Returns a list in plain english as a string -/proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) - var/total = input.len - if (!total) - return "[nothing_text]" - else if (total == 1) - return "[input[1]]" - else if (total == 2) - return "[input[1]][and_text][input[2]]" - else - var/output = "" - var/index = 1 - while (index < total) - if (index == total - 1) - comma_text = final_comma_text - - output += "[input[index]][comma_text]" - index++ - - return "[output][and_text][input[index]]" - - -/proc/ConvertReqString2List(var/list/source_list) - var/list/temp_list = params2list(source_list) - for(var/O in temp_list) - temp_list[O] = text2num(temp_list[O]) - return temp_list - -//Checks for specific types in a list -/proc/is_type_in_list(var/datum/A, var/list/L) - for(var/type in L) - if(istype(A, type)) - return 1 - return 0 - -/proc/instances_of_type_in_list(var/datum/A, list/L, strict = FALSE) - . = 0 - if (strict) - for (var/type in L) - if (type == A.type) - .++ - else - for(var/type in L) - if(istype(A, type)) - .++ - - - -//Removes any null entries from the list -//Returns TRUE if the list had nulls, FALSE otherwise -/proc/listclearnulls(list/L) - var/start_len = L.len - var/list/N = new(start_len) - L -= N - return L.len < start_len - -/* - * Returns list containing all the entries from first list that are not present in second. - * If skiprep = 1, repeated elements are treated as one. - * If either of arguments is not a list, returns null - */ -/proc/difflist(var/list/first, var/list/second, var/skiprep=0) - if(!islist(first) || !islist(second)) - return - var/list/result = new - if(skiprep) - for(var/e in first) - if(!(e in result) && !(e in second)) - result += e - else - result = first - second - return result - -/* - * Returns list containing entries that are in either list but not both. - * If skipref = 1, repeated elements are treated as one. - * If either of arguments is not a list, returns null - */ -/proc/uniquemergelist(var/list/first, var/list/second, var/skiprep=0) - if(!islist(first) || !islist(second)) - return - var/list/result = new - if(skiprep) - result = difflist(first, second, skiprep)+difflist(second, first, skiprep) - else - result = first ^ second - return result - - -//Picks a random element by weight from a list. The list must be correctly constructed in this format: -//mylist[myelement1] = myweight1 -//mylist[myelement2] = myweight2 -//The proc will return the element index, and not the weight. -/proc/pickweight(list/L) - var/total = 0 - var/item - for (item in L) - if (isnull(L[item])) - //Change by nanako, a default weight will no longer overwrite an explicitly set weight of 0 - //It will only use a default if no weight is defined - L[item] = 1 - total += L[item] - total = rand() * total//Fix by nanako, allows it to handle noninteger weights - for (item in L) - total -= L[item] - if (total <= 0) - return item - - return null - - -//Pick a random element from the list and remove it from the list. -/proc/pick_n_take(list/listfrom) - if (listfrom.len > 0) - var/picked = pick(listfrom) - listfrom -= picked - return picked - return null - -//Returns the top(last) element from the list and removes it from the list (typical stack function) -/proc/pop(list/listfrom) - if (listfrom.len > 0) - var/picked = listfrom[listfrom.len] - listfrom.len-- - return picked - return null - -//Returns the next element in parameter list after first appearance of parameter element. If it is the last element of the list or not present in list, returns first element. -/proc/next_in_list(element, list/L) - for(var/i=1, i= 1; i--) - output += L[i] - return output - -//Randomize: Return the list in a random order -/proc/shuffle(var/list/L) - if(!L) - return - - L = L.Copy() - - for(var/i=1; i toIndex) - ++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one - - L.Insert(toIndex, null) - L.Swap(fromIndex, toIndex) - L.Cut(fromIndex, fromIndex+1) - - -//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex) -//Same as moveElement but for ranges of elements -//This will preserve associations ~Carnie -/proc/moveRange(list/L, fromIndex, toIndex, len=1) - var/distance = abs(toIndex - fromIndex) - if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements - if(fromIndex <= toIndex) - return //no need to move - fromIndex += len //we want to shift left instead of right - - for(var/i=0, i toIndex) - fromIndex += len - - for(var/i=0, i 0) + var/picked = pick(listfrom) + listfrom -= picked + return picked + return null + +//Returns the top(last) element from the list and removes it from the list (typical stack function) +/proc/pop(list/listfrom) + if (listfrom.len > 0) + var/picked = listfrom[listfrom.len] + listfrom.len-- + return picked + return null + +//Returns the next element in parameter list after first appearance of parameter element. If it is the last element of the list or not present in list, returns first element. +/proc/next_in_list(element, list/L) + for(var/i=1, i= 1; i--) + output += L[i] + return output + +//Randomize: Return the list in a random order +/proc/shuffle(var/list/L) + if(!L) + return + + L = L.Copy() + + for(var/i=1; i toIndex) + ++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one + + L.Insert(toIndex, null) + L.Swap(fromIndex, toIndex) + L.Cut(fromIndex, fromIndex+1) + + +//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex) +//Same as moveElement but for ranges of elements +//This will preserve associations ~Carnie +/proc/moveRange(list/L, fromIndex, toIndex, len=1) + var/distance = abs(toIndex - fromIndex) + if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements + if(fromIndex <= toIndex) + return //no need to move + fromIndex += len //we want to shift left instead of right + + for(var/i=0, i toIndex) + fromIndex += len + + for(var/i=0, i= 0 ? a : -a - -/proc/Floor(x) - return round(x) - -/proc/Ceiling(x, y=1) - return -round(-x / y) * y - -/proc/Modulus(x, y) - return ( (x) - (y) * round((x) / (y)) ) - -// Greatest Common Divisor: Euclid's algorithm. -/proc/Gcd(a, b) - while (1) - if (!b) return a - a %= b - if (!a) return b - b %= a - -// Least Common Multiple. The formula is a consequence of: a*b = LCM*GCD. -/proc/Lcm(a, b) - return abs(a) * abs(b) / Gcd(a, b) - -// Useful in the cases when x is a large expression, e.g. x = 3a/2 + b^2 + Function(c) -/proc/Square(x) - return x*x - -/proc/Inverse(x) - return 1 / x - -// Condition checks. -/proc/IsAboutEqual(a, b, delta = 0.1) - return abs(a - b) <= delta - -// Returns true if val is from min to max, inclusive. -/proc/IsInRange(val, min, max) - return (val >= min) && (val <= max) - -/proc/IsInteger(x) - return Floor(x) == x - -/proc/IsMultiple(x, y) - return x % y == 0 - -/proc/IsEven(x) - return !(x & 0x1) - -/proc/IsOdd(x) - return (x & 0x1) - -// Performs a linear interpolation between a and b. -// Note: weight=0 returns a, weight=1 returns b, and weight=0.5 returns the mean of a and b. -/proc/Interpolate(a, b, weight = 0.5) - return a + (b - a) * weight // Equivalent to: a*(1 - weight) + b*weight - -/proc/Mean(...) - var/sum = 0 - for(var/val in args) - sum += val - return sum / args.len - -// Returns the nth root of x. -/proc/Root(n, x) - return x ** (1 / n) - -// The quadratic formula. Returns a list with the solutions, or an empty list -// if they are imaginary. -/proc/SolveQuadratic(a, b, c) - ASSERT(a) - - . = list() - var/discriminant = b*b - 4*a*c - var/bottom = 2*a - - // Return if the roots are imaginary. - if(discriminant < 0) - return - - var/root = sqrt(discriminant) - . += (-b + root) / bottom - - // If discriminant == 0, there would be two roots at the same position. - if(discriminant != 0) - . += (-b - root) / bottom - -/proc/ToDegrees(radians) - // 180 / Pi ~ 57.2957795 - return radians * 57.2957795 - -/proc/ToRadians(degrees) - // Pi / 180 ~ 0.0174532925 - return degrees * 0.0174532925 - -// Vector algebra. -/proc/squaredNorm(x, y) - return x*x + y*y - -/proc/norm(x, y) - return sqrt(squaredNorm(x, y)) - -/proc/IsPowerOfTwo(var/val) - return (val & (val-1)) == 0 - -/proc/RoundUpToPowerOfTwo(var/val) - return 2 ** -round(-log(2,val)) - -//Returns the cube root of the input number -/proc/cubert(var/num, var/iterations = 10) - . = num - for (var/i = 0, i < iterations, i++) - . = (1/3) * (num/(.**2)+2*.) - +// min is inclusive, max is exclusive +/proc/Wrap(val, min, max) + var/d = max - min + var/t = Floor((val - min) / d) + return val - (t * d) + +/proc/Default(a, b) + return a ? a : b + +// Trigonometric functions. +/proc/Tan(x) + return sin(x) / cos(x) + +/proc/Csc(x) + return 1 / sin(x) + +/proc/Sec(x) + return 1 / cos(x) + +/proc/Cot(x) + return 1 / Tan(x) + +/proc/Atan2(x, y) + if(!x && !y) return 0 + var/a = arccos(x / sqrt(x*x + y*y)) + return y >= 0 ? a : -a + +/proc/Floor(x) + return round(x) + +/proc/Ceiling(x, y=1) + return -round(-x / y) * y + +/proc/Modulus(x, y) + return ( (x) - (y) * round((x) / (y)) ) + +// Greatest Common Divisor: Euclid's algorithm. +/proc/Gcd(a, b) + while (1) + if (!b) return a + a %= b + if (!a) return b + b %= a + +// Least Common Multiple. The formula is a consequence of: a*b = LCM*GCD. +/proc/Lcm(a, b) + return abs(a) * abs(b) / Gcd(a, b) + +// Useful in the cases when x is a large expression, e.g. x = 3a/2 + b^2 + Function(c) +/proc/Square(x) + return x*x + +/proc/Inverse(x) + return 1 / x + +// Condition checks. +/proc/IsAboutEqual(a, b, delta = 0.1) + return abs(a - b) <= delta + +// Returns true if val is from min to max, inclusive. +/proc/IsInRange(val, min, max) + return (val >= min) && (val <= max) + +/proc/IsInteger(x) + return Floor(x) == x + +/proc/IsMultiple(x, y) + return x % y == 0 + +/proc/IsEven(x) + return !(x & 0x1) + +/proc/IsOdd(x) + return (x & 0x1) + +// Performs a linear interpolation between a and b. +// Note: weight=0 returns a, weight=1 returns b, and weight=0.5 returns the mean of a and b. +/proc/Interpolate(a, b, weight = 0.5) + return a + (b - a) * weight // Equivalent to: a*(1 - weight) + b*weight + +/proc/Mean(...) + var/sum = 0 + for(var/val in args) + sum += val + return sum / args.len + +// Returns the nth root of x. +/proc/Root(n, x) + return x ** (1 / n) + +// The quadratic formula. Returns a list with the solutions, or an empty list +// if they are imaginary. +/proc/SolveQuadratic(a, b, c) + ASSERT(a) + + . = list() + var/discriminant = b*b - 4*a*c + var/bottom = 2*a + + // Return if the roots are imaginary. + if(discriminant < 0) + return + + var/root = sqrt(discriminant) + . += (-b + root) / bottom + + // If discriminant == 0, there would be two roots at the same position. + if(discriminant != 0) + . += (-b - root) / bottom + +/proc/ToDegrees(radians) + // 180 / Pi ~ 57.2957795 + return radians * 57.2957795 + +/proc/ToRadians(degrees) + // Pi / 180 ~ 0.0174532925 + return degrees * 0.0174532925 + +// Vector algebra. +/proc/squaredNorm(x, y) + return x*x + y*y + +/proc/norm(x, y) + return sqrt(squaredNorm(x, y)) + +/proc/IsPowerOfTwo(var/val) + return (val & (val-1)) == 0 + +/proc/RoundUpToPowerOfTwo(var/val) + return 2 ** -round(-log(2,val)) + +//Returns the cube root of the input number +/proc/cubert(var/num, var/iterations = 10) + . = num + for (var/i = 0, i < iterations, i++) + . = (1/3) * (num/(.**2)+2*.) + diff --git a/code/_helpers/matrices.dm b/code/_helpers/matrices.dm index 17228a32fe4..94668321c60 100644 --- a/code/_helpers/matrices.dm +++ b/code/_helpers/matrices.dm @@ -1,26 +1,26 @@ -/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 - 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 +/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 + 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 diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm index 17faf23e27b..7b91af5b321 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -1,214 +1,214 @@ -/atom/movable/proc/get_mob() - return - -/obj/machinery/bot/mulebot/get_mob() - if(load && istype(load,/mob/living)) - return load - -/obj/vehicle/train/get_mob() - return buckled_mob - -/mob/get_mob() - return src - -/proc/mobs_in_view(var/range, var/source) - . = list() - for(var/atom/movable/AM in view(range, source)) - if (ismob(AM)) - . += AM - continue - - if (!AM.can_hold_mob) - continue - - . += AM.get_mob() - -proc/random_hair_style(gender, 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 = "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 = "Human") - var/datum/species/current_species - if(species) - current_species = all_species[species] - - return current_species ? current_species.sanitize_name(name) : sanitizeName(name) - -proc/random_name(gender, species = "Human") - - var/datum/species/current_species - if(species) - current_species = 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) - switch(health) - if(100 to INFINITY) - return "health100" - if(90 to 100) - return "health95" - if(70 to 90) - return "health80" - if(50 to 70) - return "health60" - if(30 to 50) - return "health40" - if(18 to 30) - return "health25" - if(5 to 18) - return "health10" - if(1 to 5) - return "health1" - if(-99 to 0) - return "health0" - else - return "health-100" - return "0" - -/* -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_logs(mob/user, mob/target, what_done, var/admin=1, var/object=null, var/addition=null) - if(user && ismob(user)) - user.attack_log += text("\[[time_stamp()]\] Has [what_done] [target ? "[target.name][(ismob(target) && target.ckey) ? "([target.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition]") - if(target && ismob(target)) - target.attack_log += text("\[[time_stamp()]\] Has been [what_done] by [user ? "[user.name][(ismob(user) && user.ckey) ? "([user.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition]") - if(admin) - log_attack("[user ? "[user.name][(ismob(user) && user.ckey) ? "([user.ckey])" : ""]" : "NON-EXISTANT SUBJECT"] [what_done] [target ? "[target.name][(ismob(target) && target.ckey)? "([target.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition]",ckey=key_name(user),ckey_target=key_name(target)) - -//checks whether this item is a module of the robot it is located in. -/proc/is_robot_module(var/obj/item/thing) - if(!thing) - return FALSE - if(istype(thing.loc, /mob/living/heavy_vehicle)) - return FALSE - if(!istype(thing.loc, /mob/living/silicon/robot)) - return FALSE - -/proc/get_exposed_defense_zone(var/atom/movable/target) - var/obj/item/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_CHEST, BP_GROIN) - - -// Returns true if M was not already in the dead mob list -/mob/proc/switch_from_living_to_dead_mob_list() - remove_from_living_mob_list() - . = add_to_dead_mob_list() - -// Returns true if M was not already in the living mob list -/mob/proc/switch_from_dead_to_living_mob_list() - remove_from_dead_mob_list() - . = add_to_living_mob_list() - -// Returns true if the mob was in neither the dead or living list -/mob/proc/add_to_living_mob_list() - return FALSE -/mob/living/add_to_living_mob_list() - if((src in living_mob_list) || (src in dead_mob_list)) - return FALSE - living_mob_list += src - return TRUE - -// Returns true if the mob was removed from the living list -/mob/proc/remove_from_living_mob_list() - return living_mob_list.Remove(src) - -// Returns true if the mob was in neither the dead or living list -/mob/proc/add_to_dead_mob_list() - return FALSE -/mob/living/add_to_dead_mob_list() - if((src in living_mob_list) || (src in dead_mob_list)) - return FALSE - dead_mob_list += src - return TRUE - -// Returns true if the mob was removed form the dead list -/mob/proc/remove_from_dead_mob_list() +/atom/movable/proc/get_mob() + return + +/obj/machinery/bot/mulebot/get_mob() + if(load && istype(load,/mob/living)) + return load + +/obj/vehicle/train/get_mob() + return buckled_mob + +/mob/get_mob() + return src + +/proc/mobs_in_view(var/range, var/source) + . = list() + for(var/atom/movable/AM in view(range, source)) + if (ismob(AM)) + . += AM + continue + + if (!AM.can_hold_mob) + continue + + . += AM.get_mob() + +proc/random_hair_style(gender, 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 = "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 = "Human") + var/datum/species/current_species + if(species) + current_species = all_species[species] + + return current_species ? current_species.sanitize_name(name) : sanitizeName(name) + +proc/random_name(gender, species = "Human") + + var/datum/species/current_species + if(species) + current_species = 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) + switch(health) + if(100 to INFINITY) + return "health100" + if(90 to 100) + return "health95" + if(70 to 90) + return "health80" + if(50 to 70) + return "health60" + if(30 to 50) + return "health40" + if(18 to 30) + return "health25" + if(5 to 18) + return "health10" + if(1 to 5) + return "health1" + if(-99 to 0) + return "health0" + else + return "health-100" + return "0" + +/* +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_logs(mob/user, mob/target, what_done, var/admin=1, var/object=null, var/addition=null) + if(user && ismob(user)) + user.attack_log += text("\[[time_stamp()]\] Has [what_done] [target ? "[target.name][(ismob(target) && target.ckey) ? "([target.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition]") + if(target && ismob(target)) + target.attack_log += text("\[[time_stamp()]\] Has been [what_done] by [user ? "[user.name][(ismob(user) && user.ckey) ? "([user.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition]") + if(admin) + log_attack("[user ? "[user.name][(ismob(user) && user.ckey) ? "([user.ckey])" : ""]" : "NON-EXISTANT SUBJECT"] [what_done] [target ? "[target.name][(ismob(target) && target.ckey)? "([target.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition]",ckey=key_name(user),ckey_target=key_name(target)) + +//checks whether this item is a module of the robot it is located in. +/proc/is_robot_module(var/obj/item/thing) + if(!thing) + return FALSE + if(istype(thing.loc, /mob/living/heavy_vehicle)) + return FALSE + if(!istype(thing.loc, /mob/living/silicon/robot)) + return FALSE + +/proc/get_exposed_defense_zone(var/atom/movable/target) + var/obj/item/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_CHEST, BP_GROIN) + + +// Returns true if M was not already in the dead mob list +/mob/proc/switch_from_living_to_dead_mob_list() + remove_from_living_mob_list() + . = add_to_dead_mob_list() + +// Returns true if M was not already in the living mob list +/mob/proc/switch_from_dead_to_living_mob_list() + remove_from_dead_mob_list() + . = add_to_living_mob_list() + +// Returns true if the mob was in neither the dead or living list +/mob/proc/add_to_living_mob_list() + return FALSE +/mob/living/add_to_living_mob_list() + if((src in living_mob_list) || (src in dead_mob_list)) + return FALSE + living_mob_list += src + return TRUE + +// Returns true if the mob was removed from the living list +/mob/proc/remove_from_living_mob_list() + return living_mob_list.Remove(src) + +// Returns true if the mob was in neither the dead or living list +/mob/proc/add_to_dead_mob_list() + return FALSE +/mob/living/add_to_dead_mob_list() + if((src in living_mob_list) || (src in dead_mob_list)) + return FALSE + dead_mob_list += src + return TRUE + +// Returns true if the mob was removed form the dead list +/mob/proc/remove_from_dead_mob_list() return dead_mob_list.Remove(src) \ No newline at end of file diff --git a/code/_helpers/names.dm b/code/_helpers/names.dm index 8a2f57782cd..0d11dfc8a18 100644 --- a/code/_helpers/names.dm +++ b/code/_helpers/names.dm @@ -1,150 +1,150 @@ -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 - -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/world_name(var/name) - current_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 - */ - -// Helper. Called in misc_late.dm for late misc init. -/proc/populate_code_phrases(override = FALSE) - if (override || !syndicate_code_phrase) - syndicate_code_phrase = generate_code_phrase() - if (override || !syndicate_code_response) - syndicate_code_response = generate_code_phrase() - -/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","wine","moonshine") - var/locations[] = teleportlocs.len ? teleportlocs : drinks //if null, defaults to drinks instead. - - 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) - 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 + +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/world_name(var/name) + current_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 + */ + +// Helper. Called in misc_late.dm for late misc init. +/proc/populate_code_phrases(override = FALSE) + if (override || !syndicate_code_phrase) + syndicate_code_phrase = generate_code_phrase() + if (override || !syndicate_code_response) + syndicate_code_response = generate_code_phrase() + +/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","wine","moonshine") + var/locations[] = teleportlocs.len ? teleportlocs : drinks //if null, defaults to drinks instead. + + 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) + 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 4b521f9bfbb..ca23a2a7f7b 100644 --- a/code/_helpers/sanitize_values.dm +++ b/code/_helpers/sanitize_values.dm @@ -1,46 +1,46 @@ -//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 - -/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 . +//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 + +/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 . diff --git a/code/_helpers/spawn_sync.dm b/code/_helpers/spawn_sync.dm index 91631d86712..6c1733ef5bc 100644 --- a/code/_helpers/spawn_sync.dm +++ b/code/_helpers/spawn_sync.dm @@ -1,86 +1,86 @@ -//------------------------------- -/* - Spawn sync helper - - Helps syncronize spawn()ing multiple processes in loops. - - Example for using this: - - //Create new spawn_sync datum - var/datum/spawn_sync/sync = new() - - for(var/obj/O in list) - //Call start_worker(), passing it first the object, then a string of the name of the proc you want called, then - // any and all arguments you want passed to the proc. - sync.start_worker(O, "do_something", arg1, arg2) - - //Finally call wait_until_done() - sync.wait_until_done() - - //Once all the workers have completed, or the failsafe has triggered, the code will continue. By default the - // failsafe is roughly 10 seconds (100 checks). -*/ -//------------------------------- -/datum/spawn_sync - var/count = 1 - var/failsafe = 100 //how many checks before the failsafe triggers and the helper stops waiting - -//Opens a thread counter -/datum/spawn_sync/proc/open() - count++ - -//Closes a thread counter -/datum/spawn_sync/proc/close() - count-- - -//Finalizes the spawn sync by removing the original starting count -/datum/spawn_sync/proc/finalize() - close() - -//Resets the counter if you want to utilize the same datum multiple times -// Optional: pass the number of checks you want for the failsafe -/datum/spawn_sync/proc/reset(var/safety = 100) - count = 1 - failsafe = safety - -//Check if all threads have returned -// Returns 0 if not all threads have completed -// Returns 1 if all threads have completed -/datum/spawn_sync/proc/check() - safety_check() - return count > 0 ? 1 : 0 - -//Failsafe in case something breaks horribly -/datum/spawn_sync/proc/safety_check() - failsafe-- - if(failsafe < 1) - count = 0 - -//Set failsafe check count in case you need more time for the workers to return -/datum/spawn_sync/proc/set_failsafe(var/safety) - failsafe = safety - -/datum/spawn_sync/proc/start_worker() - //Extract the thread run proc and it's arguments from the variadic args list. - ASSERT(args.len > 0) - var/obj = args[1] - var/thread_proc = args[2] - - //dispatch a new thread - open() - spawn() - //Utilise try/catch keywords here so the code continues even if an error occurs. - try - call(obj, thread_proc)(arglist(args.Copy(3))) - catch(var/exception/e) - error("[e] on [e.file]:[e.line]") - close() - -/datum/spawn_sync/proc/wait_until_done() - finalize() - - //Create a while loop to check if the sync is complete yet, it will return once all the spawn threads have - // completed, or the failsafe has expired. - while(check()) - //Add a sleep call to delay each check. - sleep(1) +//------------------------------- +/* + Spawn sync helper + + Helps syncronize spawn()ing multiple processes in loops. + + Example for using this: + + //Create new spawn_sync datum + var/datum/spawn_sync/sync = new() + + for(var/obj/O in list) + //Call start_worker(), passing it first the object, then a string of the name of the proc you want called, then + // any and all arguments you want passed to the proc. + sync.start_worker(O, "do_something", arg1, arg2) + + //Finally call wait_until_done() + sync.wait_until_done() + + //Once all the workers have completed, or the failsafe has triggered, the code will continue. By default the + // failsafe is roughly 10 seconds (100 checks). +*/ +//------------------------------- +/datum/spawn_sync + var/count = 1 + var/failsafe = 100 //how many checks before the failsafe triggers and the helper stops waiting + +//Opens a thread counter +/datum/spawn_sync/proc/open() + count++ + +//Closes a thread counter +/datum/spawn_sync/proc/close() + count-- + +//Finalizes the spawn sync by removing the original starting count +/datum/spawn_sync/proc/finalize() + close() + +//Resets the counter if you want to utilize the same datum multiple times +// Optional: pass the number of checks you want for the failsafe +/datum/spawn_sync/proc/reset(var/safety = 100) + count = 1 + failsafe = safety + +//Check if all threads have returned +// Returns 0 if not all threads have completed +// Returns 1 if all threads have completed +/datum/spawn_sync/proc/check() + safety_check() + return count > 0 ? 1 : 0 + +//Failsafe in case something breaks horribly +/datum/spawn_sync/proc/safety_check() + failsafe-- + if(failsafe < 1) + count = 0 + +//Set failsafe check count in case you need more time for the workers to return +/datum/spawn_sync/proc/set_failsafe(var/safety) + failsafe = safety + +/datum/spawn_sync/proc/start_worker() + //Extract the thread run proc and it's arguments from the variadic args list. + ASSERT(args.len > 0) + var/obj = args[1] + var/thread_proc = args[2] + + //dispatch a new thread + open() + spawn() + //Utilise try/catch keywords here so the code continues even if an error occurs. + try + call(obj, thread_proc)(arglist(args.Copy(3))) + catch(var/exception/e) + error("[e] on [e.file]:[e.line]") + close() + +/datum/spawn_sync/proc/wait_until_done() + finalize() + + //Create a while loop to check if the sync is complete yet, it will return once all the spawn threads have + // completed, or the failsafe has expired. + while(check()) + //Add a sleep call to delay each check. + sleep(1) diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index bc65a32b2ea..9897106b80a 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -1,544 +1,544 @@ -/* - * 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, lentext(sqltext));//Quote() adds quotes around input, we already do that - -/* - * Text sanitization - */ - -//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) - input = replace_characters(input, list("\n"=" ","\t"=" ")) - - if(encode) - // The below \ escapes have a space inserted to attempt to enable Travis 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 = 1) - 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(!last_char_group) continue //suppress at start of string - if(!allow_numbers) continue - 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)) - -// Truncates text to limit if necessary. -/proc/dd_limittext(message, length) - var/size = length(message) - if (size <= length) - return message - else - return copytext(message, 1, length + 1) - -/* - * 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) - - -//Parses a string into a list -/proc/dd_text2List(text, separator) - var/textlength = lentext(text) - var/separatorlength = lentext(separator) - var/list/textList = new /list() - var/searchPosition = 1 - var/findPosition = 1 - var/buggyText - while (1) // Loop forever. - findPosition = findtextEx(text, separator, searchPosition, 0) - buggyText = copytext(text, searchPosition, findPosition) // Everything from searchPosition to findPosition goes into a list element. - textList += "[buggyText]" // Working around weird problem where "text" != "text" after this copytext(). - - searchPosition = findPosition + separatorlength // Skip over separator. - if (findPosition == 0) // Didn't find anything at end of string so stop here. - return textList - else - if (searchPosition > textlength) // Found separator at very end of string. - textList += "" // So add empty element - - -/* - * 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(lentext(text) != lentext(compare)) - return 0 - for(var/i = 1, i < lentext(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 <= lentext(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(lentext(string) <= len) - if(!lentext(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 = new('./icons/chattags.dmi') -/proc/create_text_tag(var/tagname, var/tagdesc = tagname, var/client/C = null) - if(C && (C.prefs.toggles & CHAT_NOICONS)) - return tagdesc - return "[tagdesc]" - -// For processing simple markup, similar to what Skype and Discord use. -// Enabled from a config setting. -/proc/process_chat_markup(var/message, var/list/ignore_tags = list()) - if (!config.allow_chat_markup) - return message - - if (!message) - return "" - - // ---Begin URL caching. - var/list/urls = list() - var/i = 1 - while (url_find_lazy.Find(message)) - urls["\ref[urls]-[i]"] = url_find_lazy.match - i++ - - for (var/ref in urls) - message = replacetextEx(message, urls[ref], ref) - // ---End URL caching - - var/regex/tag_markup - for (var/tag in (markup_tags - ignore_tags)) - tag_markup = markup_regex[tag] - message = tag_markup.Replace(message, "$2[markup_tags[tag][1]]$3[markup_tags[tag][2]]$5") - - // ---Unload URL cache - for (var/ref in urls) - message = replacetextEx(message, ref, urls[ref]) - - return message - -//Converts New Lines to html
-/proc/nl2br(var/text) - return replacetextEx(text,"\n","
") - -/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 - -/proc/generateRandomString(var/length) - . = list() - for(var/a in 1 to length) - var/letter = rand(33,126) - . += ascii2text(letter) - . = jointext(.,null) - -/proc/random_string(length, list/characters) - . = "" - for(var/i=1, i<=length, i++) - . += pick(characters) - -#define starts_with(string, substring) (copytext(string,1,1+length(substring)) == substring) - -#define gender2text(gender) capitalize(gender) - -/** - * Strip out the special beyond characters for \proper and \improper - * from text that will be sent to the browser. - */ -#define strip_improper(input_text) replacetext(replacetext(input_text, "\proper", ""), "\improper", "") - - -/proc/pencode2html(t, limited = 0) - 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, "\[large\]", "") - t = replacetext(t, "\[/large\]", "") - t = replacetext(t, "\[small\]", "") - t = replacetext(t, "\[/small\]", "") - - // A break for signature customization code to use this proc as well. - if (limited) - return t - - t = replacetext(t, "\n", "
") - t = replacetext(t, "\[center\]", "
") - t = replacetext(t, "\[/center\]", "
") - t = replacetext(t, "\[br\]", "
") - 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, "\[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_nt\]", "") - t = replacetext(t, "\[logo_nt_small\]", "") - t = replacetext(t, "\[logo_zh\]", "") - t = replacetext(t, "\[logo_idris\]", "") - t = replacetext(t, "\[logo_eridani\]", "") - t = replacetext(t, "\[logo_necro\]", "") - t = replacetext(t, "\[logo_hp\]", "") - t = replacetext(t, "\[logo_be\]", "") - t = replacetext(t, "\[barcode\]", "") - t = replacetext(t, "\[time\]", "[worldtime2text()]") - t = replacetext(t, "\[date\]", "[worlddate2text()]") - t = replacetext(t, "\[editorbr\]", "
    ") - t = replacetext(t, @"[image id=([\w]*?\.[\w]*?)]", "") - return t - -/proc/html2pencode(t) - 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, "", "\[large\]") - t = replacetext(t, "", "\[/large\]") - t = replacetext(t, "", "\[small\]") - t = replacetext(t, "", "\[/small\]") - - return t - -//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) - -/proc/replacemany(text, list/replacements) - if (!LAZYLEN(replacements)) - return text - - . = text - - for (var/replacement in replacements) - . = replacetext(., replacement, replacements[replacement]) +/* + * 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 + */ + +//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) + input = replace_characters(input, list("\n"=" ","\t"=" ")) + + if(encode) + // The below \ escapes have a space inserted to attempt to enable Travis 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 = 1) + 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(!last_char_group) continue //suppress at start of string + if(!allow_numbers) continue + 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)) + +// Truncates text to limit if necessary. +/proc/dd_limittext(message, length) + var/size = length(message) + if (size <= length) + return message + else + return copytext(message, 1, length + 1) + +/* + * 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) + + +//Parses a string into a list +/proc/dd_text2List(text, separator) + var/textlength = length(text) + var/separatorlength = length(separator) + var/list/textList = new /list() + var/searchPosition = 1 + var/findPosition = 1 + var/buggyText + while (1) // Loop forever. + findPosition = findtextEx(text, separator, searchPosition, 0) + buggyText = copytext(text, searchPosition, findPosition) // Everything from searchPosition to findPosition goes into a list element. + textList += "[buggyText]" // Working around weird problem where "text" != "text" after this copytext(). + + searchPosition = findPosition + separatorlength // Skip over separator. + if (findPosition == 0) // Didn't find anything at end of string so stop here. + return textList + else + if (searchPosition > textlength) // Found separator at very end of string. + textList += "" // So add empty element + + +/* + * 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 = new('./icons/chattags.dmi') +/proc/create_text_tag(var/tagname, var/tagdesc = tagname, var/client/C = null) + if(C && (C.prefs.toggles & CHAT_NOICONS)) + return tagdesc + return "[tagdesc]" + +// For processing simple markup, similar to what Skype and Discord use. +// Enabled from a config setting. +/proc/process_chat_markup(var/message, var/list/ignore_tags = list()) + if (!config.allow_chat_markup) + return message + + if (!message) + return "" + + // ---Begin URL caching. + var/list/urls = list() + var/i = 1 + while (url_find_lazy.Find(message)) + urls["\ref[urls]-[i]"] = url_find_lazy.match + i++ + + for (var/ref in urls) + message = replacetextEx(message, urls[ref], ref) + // ---End URL caching + + var/regex/tag_markup + for (var/tag in (markup_tags - ignore_tags)) + tag_markup = markup_regex[tag] + message = tag_markup.Replace(message, "$2[markup_tags[tag][1]]$3[markup_tags[tag][2]]$5") + + // ---Unload URL cache + for (var/ref in urls) + message = replacetextEx(message, ref, urls[ref]) + + return message + +//Converts New Lines to html
    +/proc/nl2br(var/text) + return replacetextEx(text,"\n","
    ") + +/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 + +/proc/generateRandomString(var/length) + . = list() + for(var/a in 1 to length) + var/letter = rand(33,126) + . += ascii2text(letter) + . = jointext(.,null) + +/proc/random_string(length, list/characters) + . = "" + for(var/i=1, i<=length, i++) + . += pick(characters) + +#define starts_with(string, substring) (copytext(string,1,1+length(substring)) == substring) + +#define gender2text(gender) capitalize(gender) + +/** + * Strip out the special beyond characters for \proper and \improper + * from text that will be sent to the browser. + */ +#define strip_improper(input_text) replacetext(replacetext(input_text, "\proper", ""), "\improper", "") + + +/proc/pencode2html(t, limited = 0) + 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, "\[large\]", "") + t = replacetext(t, "\[/large\]", "") + t = replacetext(t, "\[small\]", "") + t = replacetext(t, "\[/small\]", "") + + // A break for signature customization code to use this proc as well. + if (limited) + return t + + t = replacetext(t, "\n", "
    ") + t = replacetext(t, "\[center\]", "
    ") + t = replacetext(t, "\[/center\]", "
    ") + t = replacetext(t, "\[br\]", "
    ") + 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, "\[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_nt\]", "") + t = replacetext(t, "\[logo_nt_small\]", "") + t = replacetext(t, "\[logo_zh\]", "") + t = replacetext(t, "\[logo_idris\]", "") + t = replacetext(t, "\[logo_eridani\]", "") + t = replacetext(t, "\[logo_necro\]", "") + t = replacetext(t, "\[logo_hp\]", "") + t = replacetext(t, "\[logo_be\]", "") + t = replacetext(t, "\[barcode\]", "") + t = replacetext(t, "\[time\]", "[worldtime2text()]") + t = replacetext(t, "\[date\]", "[worlddate2text()]") + t = replacetext(t, "\[editorbr\]", "
    ") + t = replacetext(t, @"[image id=([\w]*?\.[\w]*?)]", "") + return t + +/proc/html2pencode(t) + 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, "", "\[large\]") + t = replacetext(t, "", "\[/large\]") + t = replacetext(t, "", "\[small\]") + t = replacetext(t, "", "\[/small\]") + + return t + +//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) + +/proc/replacemany(text, list/replacements) + if (!LAZYLEN(replacements)) + return text + + . = text + + for (var/replacement in replacements) + . = replacetext(., replacement, replacements[replacement]) diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm index 62e3f3fd3bd..de8ec4f451a 100644 --- a/code/_helpers/time.dm +++ b/code/_helpers/time.dm @@ -1,59 +1,59 @@ -var/roundstart_hour = 0 -var/round_start_time - -//Returns the world time in english -/proc/worldtime2text(time = world.time, timeshift = 1) - if(!roundstart_hour) roundstart_hour = rand(0, 23) - return timeshift ? time2text(time+(roundstart_hour HOURS), "hh:mm") : time2text(time, "hh:mm") - -/proc/worldtime2hours() - if (!roundstart_hour) - worldtime2text() - . = text2num(time2text(world.time + (roundstart_hour HOURS), "hh")) - -/proc/worlddate2text() - return num2text(game_year) + "-" + time2text(world.timeofday, "MM-DD") - -/proc/time_stamp() - return time2text(world.timeofday, "hh:mm:ss") - -/* 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/real_round_start_time -/proc/get_round_duration() //Real time since round has started, in ticks. - return real_round_start_time ? (REALTIMEOFDAY - real_round_start_time) : 0 - -/proc/get_round_duration_formatted() - var/duration = get_round_duration() - var/hour = "[ round(duration / ( 1 HOUR) ) ]" - var/minute = "[ round(duration / (1 MINUTE) ) % 60 ]" - if(length(hour) == 1) - hour = "0" + hour - if(length(minute) == 1) - minute = "0" + minute - - return "[hour]:[minute]" - -/var/midnight_rollovers = 0 -/var/rollovercheck_last_timeofday = 0 -/proc/update_midnight_rollover() - if (world.timeofday < rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS! - midnight_rollovers += 1 - rollovercheck_last_timeofday = world.timeofday - return midnight_rollovers - -//returns timestamp in a sql and ISO 8601 friendly format -/proc/SQLtime(timevar) - if(!timevar) - timevar = world.realtime - return time2text(timevar, "YYYY-MM-DD hh:mm:ss") +var/roundstart_hour = 0 +var/round_start_time + +//Returns the world time in english +/proc/worldtime2text(time = world.time, timeshift = 1) + if(!roundstart_hour) roundstart_hour = rand(0, 23) + return timeshift ? time2text(time+(roundstart_hour HOURS), "hh:mm") : time2text(time, "hh:mm") + +/proc/worldtime2hours() + if (!roundstart_hour) + worldtime2text() + . = text2num(time2text(world.time + (roundstart_hour HOURS), "hh")) + +/proc/worlddate2text() + return num2text(game_year) + "-" + time2text(world.timeofday, "MM-DD") + +/proc/time_stamp() + return time2text(world.timeofday, "hh:mm:ss") + +/* 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/real_round_start_time +/proc/get_round_duration() //Real time since round has started, in ticks. + return real_round_start_time ? (REALTIMEOFDAY - real_round_start_time) : 0 + +/proc/get_round_duration_formatted() + var/duration = get_round_duration() + var/hour = "[ round(duration / ( 1 HOUR) ) ]" + var/minute = "[ round(duration / (1 MINUTE) ) % 60 ]" + if(length(hour) == 1) + hour = "0" + hour + if(length(minute) == 1) + minute = "0" + minute + + return "[hour]:[minute]" + +/var/midnight_rollovers = 0 +/var/rollovercheck_last_timeofday = 0 +/proc/update_midnight_rollover() + if (world.timeofday < rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS! + midnight_rollovers += 1 + rollovercheck_last_timeofday = world.timeofday + return midnight_rollovers + +//returns timestamp in a sql and ISO 8601 friendly format +/proc/SQLtime(timevar) + if(!timevar) + timevar = world.realtime + return time2text(timevar, "YYYY-MM-DD hh:mm:ss") diff --git a/code/_helpers/type2type.dm b/code/_helpers/type2type.dm index ff141ea0fc2..d3ce9afda0c 100644 --- a/code/_helpers/type2type.dm +++ b/code/_helpers/type2type.dm @@ -1,297 +1,297 @@ -/* - * Holds procs designed to change one type of value, into another. - * Contains: - * hex2num & num2hex - * text2list & list2text - * file2list - * angle2dir - * angle2text - * worldtime2text - */ - -// 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[.]" - -// Slower then list2text (replaced with jointext), but correctly processes associative lists. -proc/tg_list2text(list/list, glue=",") - if (!istype(list) || !list.len) - return - var/output - for(var/i=1 to list.len) - output += (i!=1? glue : null)+(!isnull(list["[list[i]]"])?"[list["[list[i]]"]]":"[list[i]]") - return output - -// 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) - -// Case sensitive version of /proc/text2list(). -/proc/text2listEx(text, delimiter="\n") - var/delim_len = length(delimiter) - if (delim_len < 1) - return list(text) - - . = list() - var/last_found = 1 - var/found - - do - found = findtextEx(text, delimiter, last_found, 0) - . += copytext(text, last_found, found) - last_found = found + delim_len - while (found) - -/proc/text2numlist(text, delimiter="\n") - var/list/num_list = list() - for(var/x in text2list(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 text2list(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 - world.log << "UNKNOWN DIRECTION: [direction]" - -// Turns a direction into text -/proc/dir2text(direction) - switch (direction) - if (1.0) return "north" - if (2.0) return "south" - if (4.0) return "east" - if (8.0) return "west" - if (5.0) return "northeast" - if (6.0) return "southeast" - if (9.0) return "northwest" - if (10.0) return "southwest" - -// 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 - -// Converts an angle (degrees) into an ss13 direction -/proc/angle2dir(var/degree) - degree = (degree + 22.5) % 360 // 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 - -// Returns the angle in english -/proc/angle2text(var/degree) - return dir2text(angle2dir(degree)) - -// 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_DEV) . += "[seperator]+DEVELOPER" - if (rights & R_CCIAA) . += "[seperator]+CCIAA" - return . - -// 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)) - -/proc/strtohex(str) - if(!istext(str)||!str) - return - var/r - var/c - for(var/i = 1 to length(str)) - c= text2ascii(str,i) - r+= num2hex(c) - return r - -// Decodes hex to raw byte string. -// If safe=TRUE, returns null on incorrect input strings instead of CRASHing -/proc/hextostr(str, safe=FALSE) - if(!istext(str)||!str) - return - var/r - var/c - for(var/i = 1 to length(str)/2) - c = hex2num(copytext(str,i*2-1,i*2+1), safe) - if(isnull(c)) - return null - r += ascii2text(c) - return r - -/proc/hex2cssrgba(var/hex, var/alpha) - var/textr = copytext(hex, 2, 4) - var/textg = copytext(hex, 4, 6) - var/textb = copytext(hex, 6, 8) - var/r = hex2num(textr) - var/g = hex2num(textg) - var/b = hex2num(textb) +/* + * Holds procs designed to change one type of value, into another. + * Contains: + * hex2num & num2hex + * text2list & list2text + * file2list + * angle2dir + * angle2text + * worldtime2text + */ + +// 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[.]" + +// Slower then list2text (replaced with jointext), but correctly processes associative lists. +proc/tg_list2text(list/list, glue=",") + if (!istype(list) || !list.len) + return + var/output + for(var/i=1 to list.len) + output += (i!=1? glue : null)+(!isnull(list["[list[i]]"])?"[list["[list[i]]"]]":"[list[i]]") + return output + +// 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) + +// Case sensitive version of /proc/text2list(). +/proc/text2listEx(text, delimiter="\n") + var/delim_len = length(delimiter) + if (delim_len < 1) + return list(text) + + . = list() + var/last_found = 1 + var/found + + do + found = findtextEx(text, delimiter, last_found, 0) + . += copytext(text, last_found, found) + last_found = found + delim_len + while (found) + +/proc/text2numlist(text, delimiter="\n") + var/list/num_list = list() + for(var/x in text2list(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 text2list(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 + world.log << "UNKNOWN DIRECTION: [direction]" + +// Turns a direction into text +/proc/dir2text(direction) + switch (direction) + if (1.0) return "north" + if (2.0) return "south" + if (4.0) return "east" + if (8.0) return "west" + if (5.0) return "northeast" + if (6.0) return "southeast" + if (9.0) return "northwest" + if (10.0) return "southwest" + +// 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 + +// Converts an angle (degrees) into an ss13 direction +/proc/angle2dir(var/degree) + degree = (degree + 22.5) % 360 // 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 + +// Returns the angle in english +/proc/angle2text(var/degree) + return dir2text(angle2dir(degree)) + +// 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_DEV) . += "[seperator]+DEVELOPER" + if (rights & R_CCIAA) . += "[seperator]+CCIAA" + return . + +// 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)) + +/proc/strtohex(str) + if(!istext(str)||!str) + return + var/r + var/c + for(var/i = 1 to length(str)) + c= text2ascii(str,i) + r+= num2hex(c) + return r + +// Decodes hex to raw byte string. +// If safe=TRUE, returns null on incorrect input strings instead of CRASHing +/proc/hextostr(str, safe=FALSE) + if(!istext(str)||!str) + return + var/r + var/c + for(var/i = 1 to length(str)/2) + c = hex2num(copytext(str,i*2-1,i*2+1), safe) + if(isnull(c)) + return null + r += ascii2text(c) + return r + +/proc/hex2cssrgba(var/hex, var/alpha) + var/textr = copytext(hex, 2, 4) + var/textg = copytext(hex, 4, 6) + var/textb = copytext(hex, 6, 8) + var/r = hex2num(textr) + var/g = hex2num(textg) + var/b = hex2num(textb) return "rgba([r], [g], [b], [alpha])" \ No newline at end of file diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 5e4e7c2b8a4..317ecc4bdd9 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -1,1214 +1,1216 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/* - * A large number of misc global procs. - */ - -//Checks if all high bits in req_mask are set in bitfield -#define BIT_TEST_ALL(bitfield, req_mask) ((~(bitfield) & (req_mask)) == 0) - -//Inverts the colour of an HTML string -/proc/invertHTML(HTMLstring) - - if (!( istext(HTMLstring) )) - CRASH("Given non-text argument!") - return - else - if (length(HTMLstring) != 7) - CRASH("Given non-HTML argument!") - return - var/textr = copytext(HTMLstring, 2, 4) - var/textg = copytext(HTMLstring, 4, 6) - var/textb = copytext(HTMLstring, 6, 8) - var/r = hex2num(textr) - var/g = hex2num(textg) - var/b = hex2num(textb) - textr = num2hex(255 - r) - textg = num2hex(255 - g) - textb = num2hex(255 - b) - if (length(textr) < 2) - textr = text("0[]", textr) - if (length(textg) < 2) - textr = text("0[]", textg) - if (length(textb) < 2) - textr = text("0[]", textb) - return text("#[][][]", textr, textg, textb) - return - -//Returns the middle-most value -/proc/dd_range(var/low, var/high, var/num) - return max(low,min(high,num)) - -//Returns whether or not A is the middle most value -/proc/InRange(var/A, var/lower, var/upper) - if(A < lower) return 0 - if(A > upper) return 0 - return 1 - - -/proc/Get_Angle(atom/movable/start,atom/movable/end)//For beams. - if(!start || !end) return 0 - var/dy - var/dx - dy=(32*end.y+end.pixel_y)-(32*start.y+start.pixel_y) - dx=(32*end.x+end.pixel_x)-(32*start.x+start.pixel_x) - if(!dy) - return (dx>=0)?90:270 - .=arctan(dx/dy) - if(dy<0) - .+=180 - else if(dx<0) - .+=360 - -//Returns location. Returns null if no location was found. -/proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = 0, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0) -/* -Location where the teleport begins, target that will teleport, distance to go, density checking 0/1(yes/no). -Random error in tile placement x, error in tile placement y, and block offset. -Block offset tells the proc how to place the box. Behind teleport location, relative to starting location, forward, etc. -Negative values for offset are accepted, think of it in relation to North, -x is west, -y is south. Error defaults to positive. -Turf and target are seperate in case you want to teleport some distance from a turf the target is not standing on or something. -*/ - - var/dirx = 0//Generic location finding variable. - var/diry = 0 - - var/xoffset = 0//Generic counter for offset location. - var/yoffset = 0 - - var/b1xerror = 0//Generic placing for point A in box. The lower left. - var/b1yerror = 0 - var/b2xerror = 0//Generic placing for point B in box. The upper right. - var/b2yerror = 0 - - errorx = abs(errorx)//Error should never be negative. - errory = abs(errory) - //var/errorxy = round((errorx+errory)/2)//Used for diagonal boxes. - - switch(target.dir)//This can be done through equations but switch is the simpler method. And works fast to boot. - //Directs on what values need modifying. - if(1)//North - diry+=distance - yoffset+=eoffsety - xoffset+=eoffsetx - b1xerror-=errorx - b1yerror-=errory - b2xerror+=errorx - b2yerror+=errory - if(2)//South - diry-=distance - yoffset-=eoffsety - xoffset+=eoffsetx - b1xerror-=errorx - b1yerror-=errory - b2xerror+=errorx - b2yerror+=errory - if(4)//East - dirx+=distance - yoffset+=eoffsetx//Flipped. - xoffset+=eoffsety - b1xerror-=errory//Flipped. - b1yerror-=errorx - b2xerror+=errory - b2yerror+=errorx - if(8)//West - dirx-=distance - yoffset-=eoffsetx//Flipped. - xoffset+=eoffsety - b1xerror-=errory//Flipped. - b1yerror-=errorx - b2xerror+=errory - b2yerror+=errorx - - var/turf/destination=locate(location.x+dirx,location.y+diry,location.z) - - if(destination)//If there is a destination. - if(errorx||errory)//If errorx or y were specified. - var/destination_list[] = list()//To add turfs to list. - //destination_list = new() - /*This will draw a block around the target turf, given what the error is. - Specifying the values above will basically draw a different sort of block. - If the values are the same, it will be a square. If they are different, it will be a rectengle. - In either case, it will center based on offset. Offset is position from center. - Offset always calculates in relation to direction faced. In other words, depending on the direction of the teleport, - the offset should remain positioned in relation to destination.*/ - - var/turf/center = locate((destination.x+xoffset),(destination.y+yoffset),location.z)//So now, find the new center. - - //Now to find a box from center location and make that our destination. - for(var/turf/T in block(locate(center.x+b1xerror,center.y+b1yerror,location.z), locate(center.x+b2xerror,center.y+b2yerror,location.z) )) - if(density&&T.density) continue//If density was specified. - if(T.x>world.maxx || T.x<1) continue//Don't want them to teleport off the map. - if(T.y>world.maxy || T.y<1) continue - destination_list += T - if(destination_list.len) - destination = pick(destination_list) - else return - - else//Same deal here. - if(density&&destination.density) return - if(destination.x>world.maxx || destination.x<1) return - if(destination.y>world.maxy || destination.y<1) return - else return - - return destination - - - -/proc/LinkBlocked(turf/A, turf/B) - if(A == null || B == null) return 1 - var/adir = get_dir(A,B) - var/rdir = get_dir(B,A) - if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal - var/iStep = get_step(A,adir&(NORTH|SOUTH)) - if(!LinkBlocked(A,iStep) && !LinkBlocked(iStep,B)) return 0 - - var/pStep = get_step(A,adir&(EAST|WEST)) - if(!LinkBlocked(A,pStep) && !LinkBlocked(pStep,B)) return 0 - return 1 - - if(DirBlocked(A,adir)) return 1 - if(DirBlocked(B,rdir)) return 1 - return 0 - - -/proc/DirBlocked(turf/loc,var/dir) - for(var/obj/structure/window/D in loc) - if(!D.density) continue - if(D.dir == SOUTHWEST) return 1 - if(D.dir == dir) return 1 - - for(var/obj/machinery/door/D in loc) - if(!D.density) continue - if(istype(D, /obj/machinery/door/window)) - if((dir & SOUTH) && (D.dir & (EAST|WEST))) return 1 - if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return 1 - else return 1 // it's a real, air blocking door - return 0 - -/proc/TurfBlockedNonWindow(turf/loc) - for(var/obj/O in loc) - if(O.density && !istype(O, /obj/structure/window)) - return 1 - return 0 - -/proc/sign(x) - return x!=0?x/abs(x):0 - -/proc/getline(atom/M,atom/N)//Ultra-Fast Bresenham Line-Drawing Algorithm - var/px=M.x //starting x - var/py=M.y - var/line[] = list(locate(px,py,M.z)) - var/dx=N.x-px //x distance - var/dy=N.y-py - var/dxabs=abs(dx)//Absolute value of x distance - var/dyabs=abs(dy) - var/sdx=sign(dx) //Sign of x distance (+ or -) - var/sdy=sign(dy) - var/x=dxabs>>1 //Counters for steps taken, setting to distance/2 - var/y=dyabs>>1 //Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast. - var/j //Generic integer for counting - if(dxabs>=dyabs) //x distance is greater than y - for(j=0;j=dxabs) //Every dyabs steps, step once in y direction - y-=dxabs - py+=sdy - px+=sdx //Step on in x direction - line+=locate(px,py,M.z)//Add the turf to the list - else - for(j=0;j=dyabs) - x-=dyabs - px+=sdx - py+=sdy - line+=locate(px,py,M.z) - return line - -#define LOCATE_COORDS(X, Y, Z) locate(between(1, X, world.maxx), between(1, Y, world.maxy), Z) -/proc/getcircle(turf/center, var/radius) //Uses a fast Bresenham rasterization algorithm to return the turfs in a thin circle. - if(!radius) return list(center) - - var/x = 0 - var/y = radius - var/p = 3 - 2 * radius - - . = list() - while(y >= x) // only formulate 1/8 of circle - - . += LOCATE_COORDS(center.x - x, center.y - y, center.z) //upper left left - . += LOCATE_COORDS(center.x - y, center.y - x, center.z) //upper upper left - . += LOCATE_COORDS(center.x + y, center.y - x, center.z) //upper upper right - . += LOCATE_COORDS(center.x + x, center.y - y, center.z) //upper right right - . += LOCATE_COORDS(center.x - x, center.y + y, center.z) //lower left left - . += LOCATE_COORDS(center.x - y, center.y + x, center.z) //lower lower left - . += LOCATE_COORDS(center.x + y, center.y + x, center.z) //lower lower right - . += LOCATE_COORDS(center.x + x, center.y + y, center.z) //lower right right - - if(p < 0) - p += 4*x++ + 6; - else - p += 4*(x++ - y--) + 10; - -#undef LOCATE_COORDS - -//Returns whether or not a player is a guest using their ckey as an input -/proc/IsGuestKey(key) - if (findtext(key, "Guest-", 1, 7) != 1) //was findtextEx - return 0 - - var/i = 7, ch, len = length(key) - - if(copytext(key, 7, 8) == "W") //webclient - i++ - - for (, i <= len, ++i) - ch = text2ascii(key, i) - if (ch < 48 || ch > 57) - return 0 - return 1 - -//Ensure the frequency is within bounds of what it should be sending/recieving at -/proc/sanitize_frequency(var/f, var/low = PUBLIC_LOW_FREQ, var/high = PUBLIC_HIGH_FREQ) - f = round(f) - f = max(low, f) - f = min(high, f) - if ((f % 2) == 0) //Ensure the last digit is an odd number - f += 1 - return f - -//Turns 1479 into 147.9 -/proc/format_frequency(var/f) - return "[round(f / 10)].[f % 10]" - - - -//This will update a mob's name, real_name, mind.name, SSrecords records, pda and id -//Calling this proc without an oldname will only update the mob and skip updating the pda, id and records ~Carn -/mob/proc/fully_replace_character_name(var/oldname,var/newname) - if(!newname) return 0 - real_name = newname - name = newname - if(mind) - mind.name = newname - if(dna) - dna.real_name = real_name - - if(oldname) - //update the datacore records! This is goig to be a bit costly. - for(var/datum/record/general/R in list(SSrecords.records, SSrecords.records_locked)) - if(R.name == oldname) - R.name = newname - break - - //update our pda and id if we have them on our person - var/list/searching = GetAllContents(searchDepth = 3) - var/search_id = 1 - var/search_pda = 1 - - for(var/A in searching) - if( search_id && istype(A,/obj/item/card/id) ) - var/obj/item/card/id/ID = A - if(ID.registered_name == oldname) - ID.registered_name = newname - ID.name = "[newname]'s ID Card ([ID.assignment])" - if(!search_pda) break - search_id = 0 - - else if( search_pda && istype(A,/obj/item/device/pda) ) - var/obj/item/device/pda/PDA = A - if(PDA.owner == oldname) - PDA.owner = newname - PDA.name = "PDA-[newname] ([PDA.ownjob])" - if(!search_id) break - search_pda = 0 - return 1 - - - -//Generalised helper proc for letting mobs rename themselves. Used to be clname() and ainame() -//Last modified by Carn -/mob/proc/rename_self(var/role, var/allow_numbers=0) - set waitfor = FALSE - var/oldname = real_name - - var/time_passed = world.time - var/newname - - for(var/i=1,i<=3,i++) //we get 3 attempts to pick a suitable name. - newname = input(src,"You are \a [role]. Would you like to change your name to something else?", "Name change",oldname) as text - if((world.time-time_passed)>3000) - return //took too long - newname = sanitizeName(newname, ,allow_numbers) //returns null if the name doesn't meet some basic requirements. Tidies up a few other things like bad-characters. - - for(var/mob/living/M in player_list) - if(M == src) - continue - if(!newname || M.real_name == newname) - newname = null - break - if(newname) - break //That's a suitable name! - to_chat(src, "Sorry, that [role]-name wasn't appropriate, please try another. It's possibly too long/short, has bad characters or is already taken.") - - if(!newname) //we'll stick with the oldname then - return - - if(cmptext("ai",role)) - if(isAI(src)) - var/mob/living/silicon/ai/A = src - oldname = null//don't bother with the records update crap - // Set eyeobj name - A.SetName(newname) - - - fully_replace_character_name(oldname,newname) - - - -//Picks a string of symbols to display as the law number for hacked or ion laws -/proc/ionnum() - return "[pick("1","2","3","4","5","6","7","8","9","0")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]" - -//When an AI is activated, it can choose from a list of non-slaved borgs to have as a slave. -/proc/freeborg() - var/select = null - var/list/borgs = list() - for (var/mob/living/silicon/robot/A in player_list) - if (A.stat == 2 || A.connected_ai || A.scrambledcodes || istype(A,/mob/living/silicon/robot/drone)) - continue - var/name = "[A.real_name] ([A.modtype] [A.braintype])" - borgs[name] = A - - if (borgs.len) - select = input("Unshackled borg signals detected:", "Borg selection", null, null) as null|anything in borgs - return borgs[select] - -//When a borg is activated, it can choose which AI it wants to be slaved to -/proc/active_ais() - . = list() - for(var/mob/living/silicon/ai/A in living_mob_list) - if(A.stat == DEAD) - continue - if(A.control_disabled == 1) - continue - . += A - return . - -//Find an active ai with the least borgs. VERBOSE PROCNAME HUH! -/proc/select_active_ai_with_fewest_borgs() - var/mob/living/silicon/ai/selected - var/list/active = active_ais() - for(var/mob/living/silicon/ai/A in active) - if(!selected || (selected.connected_robots.len > A.connected_robots.len)) - selected = A - - return selected - -/proc/select_active_ai(var/mob/user) - var/list/ais = active_ais() - if(ais.len) - if(user) . = input(usr,"AI signals detected:", "AI selection") in ais - else . = pick(ais) - return . - -/proc/get_sorted_mobs() - var/list/old_list = getmobs() - var/list/AI_list = list() - var/list/Dead_list = list() - var/list/keyclient_list = list() - var/list/key_list = list() - var/list/logged_list = list() - for(var/named in old_list) - var/mob/M = old_list[named] - if(issilicon(M)) - AI_list |= M - else if(isobserver(M) || M.stat == 2) - Dead_list |= M - else if(M.key && M.client) - keyclient_list |= M - else if(M.key) - key_list |= M - else - logged_list |= M - old_list.Remove(named) - var/list/new_list = list() - new_list += AI_list - new_list += keyclient_list - new_list += key_list - new_list += logged_list - new_list += Dead_list - return new_list - -//Returns a list of all mobs with their name -/proc/getmobs() - - var/list/mobs = sortmobs() - var/list/names = list() - var/list/creatures = list() - var/list/namecounts = list() - for(var/mob/M in mobs) - var/name = M.name - if (name in names) - namecounts[name]++ - name = "[name] ([namecounts[name]])" - else - names.Add(name) - namecounts[name] = 1 - if (M.real_name && M.real_name != M.name) - name += " \[[M.real_name]\]" - if (M.stat == 2) - if(istype(M, /mob/abstract/observer/)) - name += " \[ghost\]" - else - name += " \[dead\]" - creatures[name] = M - - return creatures - -//Orders mobs by type then by name -/proc/sortmobs() - var/list/moblist = list() - var/list/sortmob = sortAtom(mob_list) - for(var/mob/abstract/eye/M in sortmob) - moblist.Add(M) - for(var/mob/living/silicon/ai/M in sortmob) - moblist.Add(M) - for(var/mob/living/silicon/pai/M in sortmob) - moblist.Add(M) - for(var/mob/living/silicon/robot/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/human/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/brain/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/alien/M in sortmob) - moblist.Add(M) - for(var/mob/abstract/observer/M in sortmob) - moblist.Add(M) - for(var/mob/abstract/new_player/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/slime/M in sortmob) - moblist.Add(M) - for(var/mob/living/simple_animal/M in sortmob) - moblist.Add(M) -// for(var/mob/living/silicon/hivebot/M in world) -// mob_list.Add(M) -// for(var/mob/living/silicon/hive_mainframe/M in world) -// mob_list.Add(M) - return moblist - -//Forces a variable to be posative -/proc/modulus(var/M) - if(M >= 0) - return M - if(M < 0) - return -M - -// returns the turf located at the map edge in the specified direction relative to A -// used for mass driver -/proc/get_edge_target_turf(var/atom/A, var/direction) - - var/turf/target = locate(A.x, A.y, A.z) - if(!A || !target) - return 0 - //since NORTHEAST == NORTH & EAST, etc, doing it this way allows for diagonal mass drivers in the future - //and isn't really any more complicated - - // Note diagonal directions won't usually be accurate - if(direction & NORTH) - target = locate(target.x, world.maxy, target.z) - if(direction & SOUTH) - target = locate(target.x, 1, target.z) - if(direction & EAST) - target = locate(world.maxx, target.y, target.z) - if(direction & WEST) - target = locate(1, target.y, target.z) - - return target - -// returns turf relative to A in given direction at set range -// result is bounded to map size -// note range is non-pythagorean -// used for disposal system -/proc/get_ranged_target_turf(var/atom/A, var/direction, var/range) - - var/x = A.x - var/y = A.y - if(direction & NORTH) - y = min(world.maxy, y + range) - if(direction & SOUTH) - y = max(1, y - range) - if(direction & EAST) - x = min(world.maxx, x + range) - if(direction & WEST) - x = max(1, x - range) - - return locate(x,y,A.z) - - -// returns turf relative to A offset in dx and dy tiles -// bound to map limits -/proc/get_offset_target_turf(var/atom/A, var/dx, var/dy) - var/x = min(world.maxx, max(1, A.x + dx)) - var/y = min(world.maxy, max(1, A.y + dy)) - return locate(x,y,A.z) - -//Makes sure MIDDLE is between LOW and HIGH. If not, it adjusts it. Returns the adjusted value. -/proc/between(var/low, var/middle, var/high) - return max(min(middle, high), low) - -proc/arctan(x) - var/y=arcsin(x/sqrt(1+x*x)) - return y - -//returns random gauss number -proc/GaussRand(var/sigma) - var/x,y,rsq - do - x=2*rand()-1 - y=2*rand()-1 - rsq=x*x+y*y - while(rsq>1 || !rsq) - return sigma*y*sqrt(-2*log(rsq)/rsq) - -//returns random gauss number, rounded to 'roundto' -proc/GaussRandRound(var/sigma,var/roundto) - return round(GaussRand(sigma),roundto) - -//Will return the contents of an atom recursivly to a depth of 'searchDepth' -/atom/proc/GetAllContents(searchDepth = 5) - var/list/toReturn = list() - - for(var/atom/part in contents) - toReturn += part - if(part.contents.len && searchDepth) - toReturn += part.GetAllContents(searchDepth - 1) - - return toReturn - -//Step-towards method of determining whether one atom can see another. Similar to viewers() -/proc/can_see(var/atom/source, var/atom/target, var/length=5) // I couldn't be arsed to do actual raycasting :I This is horribly inaccurate. - var/turf/current = get_turf(source) - var/turf/target_turf = get_turf(target) - var/steps = 0 - - if(!current || !target_turf) - return 0 - - while(current != target_turf) - if(steps > length) return 0 - if(current.opacity) return 0 - for(var/atom/A in current) - if(A.opacity) return 0 - current = get_step_towards(current, target_turf) - steps++ - - return 1 - -/proc/is_blocked_turf(var/turf/T) - var/cant_pass = 0 - if(T.density) cant_pass = 1 - for(var/atom/A in T) - if(A.density)//&&A.anchored - cant_pass = 1 - return cant_pass - -/proc/get_step_towards2(var/atom/ref , var/atom/trg) - var/base_dir = get_dir(ref, get_step_towards(ref,trg)) - var/turf/temp = get_step_towards(ref,trg) - - if(is_blocked_turf(temp)) - var/dir_alt1 = turn(base_dir, 90) - var/dir_alt2 = turn(base_dir, -90) - var/turf/turf_last1 = temp - var/turf/turf_last2 = temp - var/free_tile = null - var/breakpoint = 0 - - while(!free_tile && breakpoint < 10) - if(!is_blocked_turf(turf_last1)) - free_tile = turf_last1 - break - if(!is_blocked_turf(turf_last2)) - free_tile = turf_last2 - break - turf_last1 = get_step(turf_last1,dir_alt1) - turf_last2 = get_step(turf_last2,dir_alt2) - breakpoint++ - - if(!free_tile) return get_step(ref, base_dir) - else return get_step_towards(ref,free_tile) - - else return get_step(ref, base_dir) - -/proc/do_mob(mob/user, mob/target, delay = 30, needhand = TRUE, display_progress = TRUE, datum/callback/extra_checks) //This is quite an ugly solution but i refuse to use the old request system. - if(!user || !target) - return 0 - - var/user_loc = user.loc - var/target_loc = target.loc - var/holding = user.get_active_hand() - - var/datum/progressbar/progbar - if (display_progress && user.client && (user.client.prefs.toggles_secondary & PROGRESS_BARS)) - progbar = new(user, delay, target) - - var/endtime = world.time + delay - var/starttime = world.time - - . = 1 - - while (world.time < endtime) - stoplag(1) - if (progbar) - progbar.update(world.time - starttime) - - if(QDELETED(user) || QDELETED(target)) - . = 0 - break - - if (user.loc != user_loc || target.loc != target_loc || (needhand && user.get_active_hand() != holding) || user.stat || user.weakened || user.stunned || (extra_checks && !extra_checks.Invoke())) - . = 0 - break - - if (progbar) - qdel(progbar) - -/proc/do_after(mob/user as mob, delay as num, needhand = TRUE, atom/movable/act_target = null, use_user_turf = FALSE, display_progress = TRUE, datum/callback/extra_checks) - if(!user || isnull(user)) - return 0 - - if (!act_target) - act_target = user - - var/Location - if(use_user_turf) //When this is true, do_after() will check whether the user's turf has changed, rather than the user's loc. - Location = get_turf(user) - else - Location = user.loc - - var/holding = user.get_active_hand() - - var/datum/progressbar/progbar - if (display_progress && user.client && (user.client.prefs.toggles_secondary & PROGRESS_BARS)) - progbar = new(user, delay, act_target) - - var/endtime = world.time + delay - var/starttime = world.time - - . = 1 - - while (world.time < endtime) - stoplag(1) - if (progbar) - progbar.update(world.time - starttime) - - var/user_loc_to_check - if(use_user_turf) - user_loc_to_check = get_turf(user) - else - user_loc_to_check = user.loc - - if (!user || user.stat || user.weakened || user.stunned || (use_user_turf >= 0 && user_loc_to_check != Location)) - . = 0 - break - - if(needhand && !(user.get_active_hand() == holding)) //Sometimes you don't want the user to have to keep their active hand - . = 0 - break - - if (extra_checks && !extra_checks.Invoke()) - . = 0 - break - - if (progbar) - qdel(progbar) - -//Takes: Anything that could possibly have variables and a varname to check. -//Returns: 1 if found, 0 if not. -/proc/hasvar(var/datum/A, var/varname) - if(A.vars.Find(lowertext(varname))) return 1 - else return 0 - -proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0) - if(!original) - return null - - var/obj/O = null - - if(sameloc) - O=new original.type(original.loc) - else - O=new original.type(locate(0,0,0)) - - if(perfectcopy) - if((O) && (original)) - for(var/V in original.vars) - if(!(V in list("type","loc","locs","vars", "parent", "parent_type","verbs","ckey","key"))) - O.vars[V] = original.vars[V] - return O - -proc/get_cardinal_dir(atom/A, atom/B) - var/dx = abs(B.x - A.x) - var/dy = abs(B.y - A.y) - return get_dir(A, B) & (rand() * (dx+dy) < dy ? 3 : 12) - -//chances are 1:value. anyprob(1) will always return true -proc/anyprob(value) - return (rand(1,value)==value) - -proc/view_or_range(distance = world.view , center = usr , type) - switch(type) - if("view") - . = view(distance,center) - if("range") - . = range(distance,center) - return - -proc/oview_or_orange(distance = world.view , center = usr , type) - switch(type) - if("view") - . = oview(distance,center) - if("range") - . = orange(distance,center) - return - -proc/get_mob_with_client_list() - var/list/mobs = list() - for(var/mob/M in mob_list) - if (M.client) - mobs += M - return mobs - - -/proc/parse_zone(zone) - if(zone == BP_R_HAND) return "right hand" - else if (zone == BP_L_HAND) return "left hand" - else if (zone == BP_L_ARM) return "left arm" - else if (zone == BP_R_ARM) return "right arm" - else if (zone == BP_L_LEG) return "left leg" - else if (zone == BP_R_LEG) return "right leg" - else if (zone == BP_L_FOOT) return "left foot" - else if (zone == BP_R_FOOT) return "right foot" - else if (zone == BP_L_HAND) return "left hand" - else if (zone == BP_R_HAND) return "right hand" - else if (zone == BP_L_FOOT) return "left foot" - else if (zone == BP_R_FOOT) return "right foot" - else return zone - -/proc/get(atom/loc, type) - while(loc) - if(istype(loc, type)) - return loc - loc = loc.loc - return null - -/proc/get_turf_or_move(turf/location) - return get_turf(location) - - -//Quick type checks for some tools -var/global/list/common_tools = list( -/obj/item/stack/cable_coil, -/obj/item/wrench, -/obj/item/weldingtool, -/obj/item/screwdriver, -/obj/item/wirecutters, -/obj/item/device/multitool, -/obj/item/crowbar) - -/proc/istool(O) - if(O && is_type_in_list(O, common_tools)) - return 1 - return 0 - -proc/is_hot(obj/item/W as obj) - switch(W.type) - if(/obj/item/weldingtool) - var/obj/item/weldingtool/WT = W - if(WT.isOn()) - return 3800 - else - return 0 - if(/obj/item/flame/lighter) - if(W:lit) - return 1500 - else - return 0 - if(/obj/item/flame/match) - if(W:lit) - return 1000 - else - return 0 - if(/obj/item/clothing/mask/smokable/cigarette) - if(W:lit) - return 1000 - else - return 0 - if(/obj/item/gun/energy/plasmacutter) - return 3800 - if(/obj/item/melee/energy) - return 3500 - else - return 0 - - return 0 - -//Whether or not the given item counts as sharp in terms of dealing damage -/proc/is_sharp(obj/O as obj) - if (!O) return 0 - if (O.sharp) return 1 - if (O.edge) return 1 - return 0 - -//Whether or not the given item counts as cutting with an edge in terms of removing limbs -/proc/has_edge(obj/O as obj) - if (!O) return 0 - if (O.edge) return 1 - return 0 - -//Returns 1 if the given item is capable of popping things like balloons, inflatable barriers, or cutting police tape. -/proc/can_puncture(obj/item/W as obj) // For the record, WHAT THE HELL IS THIS METHOD OF DOING IT? - if(!W) return 0 - if(W.sharp) return 1 - return ( \ - W.sharp || \ - W.isscrewdriver() || \ - W.ispen() || \ - W.iswelder() || \ - istype(W, /obj/item/flame/lighter/zippo) || \ - istype(W, /obj/item/flame/match) || \ - istype(W, /obj/item/clothing/mask/smokable/cigarette) || \ - istype(W, /obj/item/shovel) \ - ) - -/proc/is_surgery_tool(obj/item/W as obj) - return ( \ - istype(W, /obj/item/scalpel) || \ - istype(W, /obj/item/hemostat) || \ - istype(W, /obj/item/retractor) || \ - istype(W, /obj/item/cautery) || \ - istype(W, /obj/item/bonegel) || \ - istype(W, /obj/item/bonesetter) - ) - -/proc/is_borg_item(obj/item/W as obj) - return W && W.loc && isrobot(W.loc) - -//check if mob is lying down on something we can operate him on. -/proc/can_operate(mob/living/carbon/M) //If it's 2, commence surgery, if it's 1, fail surgery, if it's 0, attack - var/surgery_attempt = SURGERY_IGNORE - var/located = FALSE - if(locate(/obj/machinery/optable, M.loc)) - located = TRUE - surgery_attempt = SURGERY_SUCCESS - else if(locate(/obj/structure/bed/roller, M.loc)) - located = TRUE - if(prob(80)) - surgery_attempt = SURGERY_SUCCESS - else - surgery_attempt = SURGERY_FAIL - else if(locate(/obj/structure/table, M.loc)) - located = TRUE - if(prob(66)) - surgery_attempt = SURGERY_SUCCESS - else - surgery_attempt = SURGERY_FAIL - if(!M.lying && surgery_attempt != SURGERY_SUCCESS && located) - surgery_attempt = SURGERY_IGNORE //hit yourself if you're not lying - return surgery_attempt - -/proc/reverse_direction(var/dir) - switch(dir) - if(NORTH) - return SOUTH - if(NORTHEAST) - return SOUTHWEST - if(EAST) - return WEST - if(SOUTHEAST) - return NORTHWEST - if(SOUTH) - return NORTH - if(SOUTHWEST) - return NORTHEAST - if(WEST) - return EAST - if(NORTHWEST) - return SOUTHEAST - -/* -Checks if that loc and dir has a item on the wall -*/ -var/list/wall_items = typecacheof(list( - /obj/machinery/power/apc, - /obj/machinery/alarm, - /obj/item/device/radio/intercom, - /obj/structure/extinguisher_cabinet, - /obj/structure/reagent_dispensers/peppertank, - /obj/machinery/status_display, - /obj/machinery/requests_console, - /obj/machinery/light_switch, - /obj/machinery/newscaster, - /obj/machinery/firealarm, - /obj/structure/noticeboard, - /obj/machinery/computer/security/telescreen, - /obj/machinery/embedded_controller/radio/airlock, - /obj/item/storage/secure/safe, - /obj/machinery/door_timer, - /obj/machinery/flasher, - /obj/machinery/keycard_auth, - /obj/structure/mirror, - /obj/structure/fireaxecabinet, - /obj/machinery/computer/security/telescreen/entertainment, - /obj/machinery/station_map, - /obj/structure/sign -)) - -/proc/gotwallitem(loc, dir) - for(var/obj/O in loc) - if (is_type_in_typecache(O, global.wall_items)) - //Direction works sometimes - if(O.dir == dir) - return 1 - - //Some stuff doesn't use dir properly, so we need to check pixel instead - switch(dir) - if(SOUTH) - if(O.pixel_y > 10) - return 1 - if(NORTH) - if(O.pixel_y < -10) - return 1 - if(WEST) - if(O.pixel_x > 10) - return 1 - if(EAST) - if(O.pixel_x < -10) - return 1 - - //Some stuff is placed directly on the wallturf (signs) - for(var/obj/O in get_step(loc, dir)) - if (is_type_in_typecache(O, global.wall_items) && O.pixel_x == 0 && O.pixel_y == 0) - return 1 - return 0 - -/proc/format_text(text) - return replacetext(replacetext(text,"\proper ",""),"\improper ","") - -/proc/topic_link(var/datum/D, var/arglist, var/content) - if(istype(arglist,/list)) - arglist = list2params(arglist) - return "[content]" - -/proc/get_random_colour(var/simple, var/lower, var/upper) - var/colour - if(simple) - colour = pick(list("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF")) - else - for(var/i=1;i<=3;i++) - var/temp_col = "[num2hex(rand(lower,upper))]" - if(length(temp_col )<2) - temp_col = "0[temp_col]" - colour += temp_col - return "#[colour]" - - - -/atom/proc/get_light_and_color(var/atom/origin) - if(origin) - color = origin.color - set_light(origin.light_range, origin.light_power, origin.light_color) - -// call to generate a stack trace and print to runtime logs -/proc/crash_with(msg) - CRASH(msg) - -/atom/proc/find_up_hierarchy(var/atom/target) - //This function will recurse up the hierarchy containing src, in search of the target - //It will stop when it reaches an area, as areas have no loc - var/x = 0//As a safety, we'll crawl up a maximum of ten layers - var/atom/a = src - while (x < 10) - x++ - if (isnull(a)) - return 0 - - if (a == target)//we found it! - return 1 - - if (istype(a, /area)) - return 0//Can't recurse any higher than this. - - a = a.loc - - return 0//If we get here, we must be buried many layers deep in nested containers. Shouldn't happen - -//similar function to RANGE_TURFS(), but will search spiralling outwards from the center (like the above, but only turfs) -/proc/spiral_range_turfs(dist=0, center=usr, orange=0) - if(!dist) - if(!orange) - return list(center) - else - return list() - - var/turf/t_center = get_turf(center) - if(!t_center) - return list() - - var/list/L = list() - var/turf/T - var/y - var/x - var/c_dist = 1 - - if(!orange) - L += t_center - - while( c_dist <= dist ) - y = t_center.y + c_dist - x = t_center.x - c_dist + 1 - for(x in x to t_center.x+c_dist) - T = locate(x,y,t_center.z) - if(T) - L += T - - y = t_center.y + c_dist - 1 - x = t_center.x + c_dist - for(y in t_center.y-c_dist to y) - T = locate(x,y,t_center.z) - if(T) - L += T - - y = t_center.y - c_dist - x = t_center.x + c_dist - 1 - for(x in t_center.x-c_dist to x) - T = locate(x,y,t_center.z) - if(T) - L += T - - y = t_center.y - c_dist + 1 - x = t_center.x - c_dist - for(y in y to t_center.y+c_dist) - T = locate(x,y,t_center.z) - if(T) - L += T - c_dist++ - - return L - -//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(world.tick_usage, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1) - -/proc/stoplag(initial_delay) - // If we're initializing, our tick limit might be over 100 (testing config), but stoplag() penalizes procs that go over. - // Unfortunately, this penalty slows down init a *lot*. So, we disable it during boot and lobby, when relatively few things should be calling this. - if (!Master || Master.initializing || !Master.round_started) - 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) - sleep(i*world.tick_lag*DELTA_CALC) - i *= 2 - while (world.tick_usage > min(TICK_LIMIT_TO_RUN, CURRENT_TICKLIMIT)) - -#undef DELTA_CALC - -// Helper for adding verbs with timers. -/atom/proc/add_verb(the_verb, datum/callback/callback) - if (callback && !callback.Invoke()) - return - - verbs += the_verb - - - -#define NOT_FLAG(flag) (!(flag & use_flags)) -#define HAS_FLAG(flag) (flag & use_flags) - -// Checks if user can use this object. Set use_flags to customize what checks are done. -// Returns 0 if they can use it, a value representing why they can't if not. -// Flags are in `code/__defines/misc.dm` -/atom/proc/use_check(mob/user, use_flags = 0, show_messages = FALSE) - . = 0 - if (NOT_FLAG(USE_ALLOW_NONLIVING) && !isliving(user)) - // No message for ghosts. - return USE_FAIL_NONLIVING - - if (NOT_FLAG(USE_ALLOW_NON_ADJACENT) && !Adjacent(user)) - if (show_messages) - to_chat(user, "You're too far away from [src] to do that.") - return USE_FAIL_NON_ADJACENT - - if (NOT_FLAG(USE_ALLOW_DEAD) && user.stat == DEAD) - if (show_messages) - to_chat(user, "How do you expect to do that when you're dead?") - return USE_FAIL_DEAD - - if (NOT_FLAG(USE_ALLOW_INCAPACITATED) && (user.incapacitated())) - if (show_messages) - to_chat(user, "You cannot do that in your current state.") - return USE_FAIL_INCAPACITATED - - if (NOT_FLAG(USE_ALLOW_NON_ADV_TOOL_USR) && !user.IsAdvancedToolUser()) - if (show_messages) - to_chat(user, "You don't know how to operate [src].") - return USE_FAIL_NON_ADV_TOOL_USR - - if (HAS_FLAG(USE_DISALLOW_SILICONS) && issilicon(user)) - if (show_messages) - to_chat(user, "How do you propose doing that without hands?") - return USE_FAIL_IS_SILICON - - if (HAS_FLAG(USE_FORCE_SRC_IN_USER) && !(src in user)) - if (show_messages) - to_chat(user, "You need to be holding [src] to do that.") - return USE_FAIL_NOT_IN_USER - -/atom/proc/use_check_and_message(mob/user, use_flags = 0) - . = use_check(user, use_flags, TRUE) - -/obj/proc/iswrench() - return FALSE - -/obj/proc/isscrewdriver() - return FALSE - -/obj/proc/iswirecutter() - return FALSE - -/obj/proc/ismultitool() - return FALSE - -/obj/proc/iscrowbar() - return FALSE - -/obj/proc/iswelder() - return FALSE - -/obj/proc/iscoil() - return FALSE - -/obj/proc/ispen() - return FALSE - -#undef NOT_FLAG -#undef HAS_FLAG - -//Prevents robots dropping their modules. -/proc/dropsafety(var/atom/movable/A) - if (istype(A.loc, /mob/living/silicon)) - return 0 - - else if (istype(A.loc, /obj/item/rig_module)) - return 0 - return 1 +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/* + * A large number of misc global procs. + */ + +//Checks if all high bits in req_mask are set in bitfield +#define BIT_TEST_ALL(bitfield, req_mask) ((~(bitfield) & (req_mask)) == 0) + +//Inverts the colour of an HTML string +/proc/invertHTML(HTMLstring) + + if (!( istext(HTMLstring) )) + CRASH("Given non-text argument!") + return + else + if (length(HTMLstring) != 7) + CRASH("Given non-HTML argument!") + return + var/textr = copytext(HTMLstring, 2, 4) + var/textg = copytext(HTMLstring, 4, 6) + var/textb = copytext(HTMLstring, 6, 8) + var/r = hex2num(textr) + var/g = hex2num(textg) + var/b = hex2num(textb) + textr = num2hex(255 - r) + textg = num2hex(255 - g) + textb = num2hex(255 - b) + if (length(textr) < 2) + textr = text("0[]", textr) + if (length(textg) < 2) + textr = text("0[]", textg) + if (length(textb) < 2) + textr = text("0[]", textb) + return text("#[][][]", textr, textg, textb) + return + +//Returns the middle-most value +/proc/dd_range(var/low, var/high, var/num) + return max(low,min(high,num)) + +//Returns whether or not A is the middle most value +/proc/InRange(var/A, var/lower, var/upper) + if(A < lower) return 0 + if(A > upper) return 0 + return 1 + + +/proc/Get_Angle(atom/movable/start,atom/movable/end)//For beams. + if(!start || !end) return 0 + var/dy + var/dx + dy=(32*end.y+end.pixel_y)-(32*start.y+start.pixel_y) + dx=(32*end.x+end.pixel_x)-(32*start.x+start.pixel_x) + if(!dy) + return (dx>=0)?90:270 + .=arctan(dx/dy) + if(dy<0) + .+=180 + else if(dx<0) + .+=360 + +//Returns location. Returns null if no location was found. +/proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = 0, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0) +/* +Location where the teleport begins, target that will teleport, distance to go, density checking 0/1(yes/no). +Random error in tile placement x, error in tile placement y, and block offset. +Block offset tells the proc how to place the box. Behind teleport location, relative to starting location, forward, etc. +Negative values for offset are accepted, think of it in relation to North, -x is west, -y is south. Error defaults to positive. +Turf and target are seperate in case you want to teleport some distance from a turf the target is not standing on or something. +*/ + + var/dirx = 0//Generic location finding variable. + var/diry = 0 + + var/xoffset = 0//Generic counter for offset location. + var/yoffset = 0 + + var/b1xerror = 0//Generic placing for point A in box. The lower left. + var/b1yerror = 0 + var/b2xerror = 0//Generic placing for point B in box. The upper right. + var/b2yerror = 0 + + errorx = abs(errorx)//Error should never be negative. + errory = abs(errory) + //var/errorxy = round((errorx+errory)/2)//Used for diagonal boxes. + + switch(target.dir)//This can be done through equations but switch is the simpler method. And works fast to boot. + //Directs on what values need modifying. + if(1)//North + diry+=distance + yoffset+=eoffsety + xoffset+=eoffsetx + b1xerror-=errorx + b1yerror-=errory + b2xerror+=errorx + b2yerror+=errory + if(2)//South + diry-=distance + yoffset-=eoffsety + xoffset+=eoffsetx + b1xerror-=errorx + b1yerror-=errory + b2xerror+=errorx + b2yerror+=errory + if(4)//East + dirx+=distance + yoffset+=eoffsetx//Flipped. + xoffset+=eoffsety + b1xerror-=errory//Flipped. + b1yerror-=errorx + b2xerror+=errory + b2yerror+=errorx + if(8)//West + dirx-=distance + yoffset-=eoffsetx//Flipped. + xoffset+=eoffsety + b1xerror-=errory//Flipped. + b1yerror-=errorx + b2xerror+=errory + b2yerror+=errorx + + var/turf/destination=locate(location.x+dirx,location.y+diry,location.z) + + if(destination)//If there is a destination. + if(errorx||errory)//If errorx or y were specified. + var/destination_list[] = list()//To add turfs to list. + //destination_list = new() + /*This will draw a block around the target turf, given what the error is. + Specifying the values above will basically draw a different sort of block. + If the values are the same, it will be a square. If they are different, it will be a rectengle. + In either case, it will center based on offset. Offset is position from center. + Offset always calculates in relation to direction faced. In other words, depending on the direction of the teleport, + the offset should remain positioned in relation to destination.*/ + + var/turf/center = locate((destination.x+xoffset),(destination.y+yoffset),location.z)//So now, find the new center. + + //Now to find a box from center location and make that our destination. + for(var/turf/T in block(locate(center.x+b1xerror,center.y+b1yerror,location.z), locate(center.x+b2xerror,center.y+b2yerror,location.z) )) + if(density&&T.density) continue//If density was specified. + if(T.x>world.maxx || T.x<1) continue//Don't want them to teleport off the map. + if(T.y>world.maxy || T.y<1) continue + destination_list += T + if(destination_list.len) + destination = pick(destination_list) + else return + + else//Same deal here. + if(density&&destination.density) return + if(destination.x>world.maxx || destination.x<1) return + if(destination.y>world.maxy || destination.y<1) return + else return + + return destination + + + +/proc/LinkBlocked(turf/A, turf/B) + if(A == null || B == null) return 1 + var/adir = get_dir(A,B) + var/rdir = get_dir(B,A) + if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal + var/iStep = get_step(A,adir&(NORTH|SOUTH)) + if(!LinkBlocked(A,iStep) && !LinkBlocked(iStep,B)) return 0 + + var/pStep = get_step(A,adir&(EAST|WEST)) + if(!LinkBlocked(A,pStep) && !LinkBlocked(pStep,B)) return 0 + return 1 + + if(DirBlocked(A,adir)) return 1 + if(DirBlocked(B,rdir)) return 1 + return 0 + + +/proc/DirBlocked(turf/loc,var/dir) + for(var/obj/structure/window/D in loc) + if(!D.density) continue + if(D.dir == SOUTHWEST) return 1 + if(D.dir == dir) return 1 + + for(var/obj/machinery/door/D in loc) + if(!D.density) continue + if(istype(D, /obj/machinery/door/window)) + if((dir & SOUTH) && (D.dir & (EAST|WEST))) return 1 + if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return 1 + else return 1 // it's a real, air blocking door + return 0 + +/proc/TurfBlockedNonWindow(turf/loc) + for(var/obj/O in loc) + if(O.density && !istype(O, /obj/structure/window)) + return 1 + return 0 + +/proc/sign(x) + return x!=0?x/abs(x):0 + +/proc/getline(atom/M,atom/N)//Ultra-Fast Bresenham Line-Drawing Algorithm + var/px=M.x //starting x + var/py=M.y + var/line[] = list(locate(px,py,M.z)) + var/dx=N.x-px //x distance + var/dy=N.y-py + var/dxabs=abs(dx)//Absolute value of x distance + var/dyabs=abs(dy) + var/sdx=sign(dx) //Sign of x distance (+ or -) + var/sdy=sign(dy) + var/x=dxabs>>1 //Counters for steps taken, setting to distance/2 + var/y=dyabs>>1 //Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast. + var/j //Generic integer for counting + if(dxabs>=dyabs) //x distance is greater than y + for(j=0;j=dxabs) //Every dyabs steps, step once in y direction + y-=dxabs + py+=sdy + px+=sdx //Step on in x direction + line+=locate(px,py,M.z)//Add the turf to the list + else + for(j=0;j=dyabs) + x-=dyabs + px+=sdx + py+=sdy + line+=locate(px,py,M.z) + return line + +#define LOCATE_COORDS(X, Y, Z) locate(between(1, X, world.maxx), between(1, Y, world.maxy), Z) +/proc/getcircle(turf/center, var/radius) //Uses a fast Bresenham rasterization algorithm to return the turfs in a thin circle. + if(!radius) return list(center) + + var/x = 0 + var/y = radius + var/p = 3 - 2 * radius + + . = list() + while(y >= x) // only formulate 1/8 of circle + + . += LOCATE_COORDS(center.x - x, center.y - y, center.z) //upper left left + . += LOCATE_COORDS(center.x - y, center.y - x, center.z) //upper upper left + . += LOCATE_COORDS(center.x + y, center.y - x, center.z) //upper upper right + . += LOCATE_COORDS(center.x + x, center.y - y, center.z) //upper right right + . += LOCATE_COORDS(center.x - x, center.y + y, center.z) //lower left left + . += LOCATE_COORDS(center.x - y, center.y + x, center.z) //lower lower left + . += LOCATE_COORDS(center.x + y, center.y + x, center.z) //lower lower right + . += LOCATE_COORDS(center.x + x, center.y + y, center.z) //lower right right + + if(p < 0) + p += 4*x++ + 6; + else + p += 4*(x++ - y--) + 10; + +#undef LOCATE_COORDS + +//Returns whether or not a player is a guest using their ckey as an input +/proc/IsGuestKey(key) + if (findtext(key, "Guest-", 1, 7) != 1) //was findtextEx + return 0 + + var/i = 7, ch, len = length(key) + + if(copytext(key, 7, 8) == "W") //webclient + i++ + + for (, i <= len, ++i) + ch = text2ascii(key, i) + if (ch < 48 || ch > 57) + return 0 + return 1 + +//Ensure the frequency is within bounds of what it should be sending/recieving at +/proc/sanitize_frequency(var/f, var/low = PUBLIC_LOW_FREQ, var/high = PUBLIC_HIGH_FREQ) + f = round(f) + f = max(low, f) + f = min(high, f) + if ((f % 2) == 0) //Ensure the last digit is an odd number + f += 1 + return f + +//Turns 1479 into 147.9 +/proc/format_frequency(var/f) + return "[round(f / 10)].[f % 10]" + + + +//This will update a mob's name, real_name, mind.name, SSrecords records, pda and id +//Calling this proc without an oldname will only update the mob and skip updating the pda, id and records ~Carn +/mob/proc/fully_replace_character_name(var/oldname,var/newname) + if(!newname) return 0 + real_name = newname + name = newname + if(mind) + mind.name = newname + if(dna) + dna.real_name = real_name + + if(oldname) + //update the datacore records! This is goig to be a bit costly. + for(var/datum/record/general/R in list(SSrecords.records, SSrecords.records_locked)) + if(R.name == oldname) + R.name = newname + break + + //update our pda and id if we have them on our person + var/list/searching = GetAllContents(searchDepth = 3) + var/search_id = 1 + var/search_pda = 1 + + for(var/A in searching) + if( search_id && istype(A,/obj/item/card/id) ) + var/obj/item/card/id/ID = A + if(ID.registered_name == oldname) + ID.registered_name = newname + ID.name = "[newname]'s ID Card ([ID.assignment])" + if(!search_pda) break + search_id = 0 + + else if( search_pda && istype(A,/obj/item/device/pda) ) + var/obj/item/device/pda/PDA = A + if(PDA.owner == oldname) + PDA.owner = newname + PDA.name = "PDA-[newname] ([PDA.ownjob])" + if(!search_id) break + search_pda = 0 + return 1 + + + +//Generalised helper proc for letting mobs rename themselves. Used to be clname() and ainame() +//Last modified by Carn +/mob/proc/rename_self(var/role, var/allow_numbers=0) + set waitfor = FALSE + var/oldname = real_name + + var/time_passed = world.time + var/newname + + for(var/i=1,i<=3,i++) //we get 3 attempts to pick a suitable name. + newname = input(src,"You are \a [role]. Would you like to change your name to something else?", "Name change",oldname) as text + if((world.time-time_passed)>3000) + return //took too long + newname = sanitizeName(newname, ,allow_numbers) //returns null if the name doesn't meet some basic requirements. Tidies up a few other things like bad-characters. + + for(var/mob/living/M in player_list) + if(M == src) + continue + if(!newname || M.real_name == newname) + newname = null + break + if(newname) + break //That's a suitable name! + to_chat(src, "Sorry, that [role]-name wasn't appropriate, please try another. It's possibly too long/short, has bad characters or is already taken.") + + if(!newname) //we'll stick with the oldname then + return + + if(cmptext("ai",role)) + if(isAI(src)) + var/mob/living/silicon/ai/A = src + oldname = null//don't bother with the records update crap + // Set eyeobj name + A.SetName(newname) + + + fully_replace_character_name(oldname,newname) + + + +//Picks a string of symbols to display as the law number for hacked or ion laws +/proc/ionnum() + return "[pick("1","2","3","4","5","6","7","8","9","0")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]" + +//When an AI is activated, it can choose from a list of non-slaved borgs to have as a slave. +/proc/freeborg() + var/select = null + var/list/borgs = list() + for (var/mob/living/silicon/robot/A in player_list) + if (A.stat == 2 || A.connected_ai || A.scrambledcodes || istype(A,/mob/living/silicon/robot/drone)) + continue + var/name = "[A.real_name] ([A.modtype] [A.braintype])" + borgs[name] = A + + if (borgs.len) + select = input("Unshackled borg signals detected:", "Borg selection", null, null) as null|anything in borgs + return borgs[select] + +//When a borg is activated, it can choose which AI it wants to be slaved to +/proc/active_ais() + . = list() + for(var/mob/living/silicon/ai/A in living_mob_list) + if(A.stat == DEAD) + continue + if(A.control_disabled == 1) + continue + . += A + return . + +//Find an active ai with the least borgs. VERBOSE PROCNAME HUH! +/proc/select_active_ai_with_fewest_borgs() + var/mob/living/silicon/ai/selected + var/list/active = active_ais() + for(var/mob/living/silicon/ai/A in active) + if(!selected || (selected.connected_robots.len > A.connected_robots.len)) + selected = A + + return selected + +/proc/select_active_ai(var/mob/user) + var/list/ais = active_ais() + if(ais.len) + if(user) . = input(usr,"AI signals detected:", "AI selection") in ais + else . = pick(ais) + return . + +/proc/get_sorted_mobs() + var/list/old_list = getmobs() + var/list/AI_list = list() + var/list/Dead_list = list() + var/list/keyclient_list = list() + var/list/key_list = list() + var/list/logged_list = list() + for(var/named in old_list) + var/mob/M = old_list[named] + if(issilicon(M)) + AI_list |= M + else if(isobserver(M) || M.stat == 2) + Dead_list |= M + else if(M.key && M.client) + keyclient_list |= M + else if(M.key) + key_list |= M + else + logged_list |= M + old_list.Remove(named) + var/list/new_list = list() + new_list += AI_list + new_list += keyclient_list + new_list += key_list + new_list += logged_list + new_list += Dead_list + return new_list + +//Returns a list of all mobs with their name +/proc/getmobs() + + var/list/mobs = sortmobs() + var/list/names = list() + var/list/creatures = list() + var/list/namecounts = list() + for(var/mob/M in mobs) + var/name = M.name + if (name in names) + namecounts[name]++ + name = "[name] ([namecounts[name]])" + else + names.Add(name) + namecounts[name] = 1 + if (M.real_name && M.real_name != M.name) + name += " \[[M.real_name]\]" + if (M.stat == 2) + if(istype(M, /mob/abstract/observer/)) + name += " \[ghost\]" + else + name += " \[dead\]" + creatures[name] = M + + return creatures + +//Orders mobs by type then by name +/proc/sortmobs() + var/list/moblist = list() + var/list/sortmob = sortAtom(mob_list) + for(var/mob/abstract/eye/M in sortmob) + moblist.Add(M) + for(var/mob/living/silicon/ai/M in sortmob) + moblist.Add(M) + for(var/mob/living/silicon/pai/M in sortmob) + moblist.Add(M) + for(var/mob/living/silicon/robot/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/human/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/brain/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/alien/M in sortmob) + moblist.Add(M) + for(var/mob/abstract/observer/M in sortmob) + moblist.Add(M) + for(var/mob/abstract/new_player/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/slime/M in sortmob) + moblist.Add(M) + for(var/mob/living/simple_animal/M in sortmob) + moblist.Add(M) +// for(var/mob/living/silicon/hivebot/M in world) +// mob_list.Add(M) +// for(var/mob/living/silicon/hive_mainframe/M in world) +// mob_list.Add(M) + return moblist + +//Forces a variable to be posative +/proc/modulus(var/M) + if(M >= 0) + return M + if(M < 0) + return -M + +// returns the turf located at the map edge in the specified direction relative to A +// used for mass driver +/proc/get_edge_target_turf(var/atom/A, var/direction) + + var/turf/target = locate(A.x, A.y, A.z) + if(!A || !target) + return 0 + //since NORTHEAST == NORTH & EAST, etc, doing it this way allows for diagonal mass drivers in the future + //and isn't really any more complicated + + // Note diagonal directions won't usually be accurate + if(direction & NORTH) + target = locate(target.x, world.maxy, target.z) + if(direction & SOUTH) + target = locate(target.x, 1, target.z) + if(direction & EAST) + target = locate(world.maxx, target.y, target.z) + if(direction & WEST) + target = locate(1, target.y, target.z) + + return target + +// returns turf relative to A in given direction at set range +// result is bounded to map size +// note range is non-pythagorean +// used for disposal system +/proc/get_ranged_target_turf(var/atom/A, var/direction, var/range) + + var/x = A.x + var/y = A.y + if(direction & NORTH) + y = min(world.maxy, y + range) + if(direction & SOUTH) + y = max(1, y - range) + if(direction & EAST) + x = min(world.maxx, x + range) + if(direction & WEST) + x = max(1, x - range) + + return locate(x,y,A.z) + + +// returns turf relative to A offset in dx and dy tiles +// bound to map limits +/proc/get_offset_target_turf(var/atom/A, var/dx, var/dy) + var/x = min(world.maxx, max(1, A.x + dx)) + var/y = min(world.maxy, max(1, A.y + dy)) + return locate(x,y,A.z) + +//Makes sure MIDDLE is between LOW and HIGH. If not, it adjusts it. Returns the adjusted value. +/proc/between(var/low, var/middle, var/high) + return max(min(middle, high), low) + +#if DM_VERSION < 513 +proc/arctan(x) + var/y=arcsin(x/sqrt(1+x*x)) + return y +#endif + +//returns random gauss number +proc/GaussRand(var/sigma) + var/x,y,rsq + do + x=2*rand()-1 + y=2*rand()-1 + rsq=x*x+y*y + while(rsq>1 || !rsq) + return sigma*y*sqrt(-2*log(rsq)/rsq) + +//returns random gauss number, rounded to 'roundto' +proc/GaussRandRound(var/sigma,var/roundto) + return round(GaussRand(sigma),roundto) + +//Will return the contents of an atom recursivly to a depth of 'searchDepth' +/atom/proc/GetAllContents(searchDepth = 5) + var/list/toReturn = list() + + for(var/atom/part in contents) + toReturn += part + if(part.contents.len && searchDepth) + toReturn += part.GetAllContents(searchDepth - 1) + + return toReturn + +//Step-towards method of determining whether one atom can see another. Similar to viewers() +/proc/can_see(var/atom/source, var/atom/target, var/length=5) // I couldn't be arsed to do actual raycasting :I This is horribly inaccurate. + var/turf/current = get_turf(source) + var/turf/target_turf = get_turf(target) + var/steps = 0 + + if(!current || !target_turf) + return 0 + + while(current != target_turf) + if(steps > length) return 0 + if(current.opacity) return 0 + for(var/atom/A in current) + if(A.opacity) return 0 + current = get_step_towards(current, target_turf) + steps++ + + return 1 + +/proc/is_blocked_turf(var/turf/T) + var/cant_pass = 0 + if(T.density) cant_pass = 1 + for(var/atom/A in T) + if(A.density)//&&A.anchored + cant_pass = 1 + return cant_pass + +/proc/get_step_towards2(var/atom/ref , var/atom/trg) + var/base_dir = get_dir(ref, get_step_towards(ref,trg)) + var/turf/temp = get_step_towards(ref,trg) + + if(is_blocked_turf(temp)) + var/dir_alt1 = turn(base_dir, 90) + var/dir_alt2 = turn(base_dir, -90) + var/turf/turf_last1 = temp + var/turf/turf_last2 = temp + var/free_tile = null + var/breakpoint = 0 + + while(!free_tile && breakpoint < 10) + if(!is_blocked_turf(turf_last1)) + free_tile = turf_last1 + break + if(!is_blocked_turf(turf_last2)) + free_tile = turf_last2 + break + turf_last1 = get_step(turf_last1,dir_alt1) + turf_last2 = get_step(turf_last2,dir_alt2) + breakpoint++ + + if(!free_tile) return get_step(ref, base_dir) + else return get_step_towards(ref,free_tile) + + else return get_step(ref, base_dir) + +/proc/do_mob(mob/user, mob/target, delay = 30, needhand = TRUE, display_progress = TRUE, datum/callback/extra_checks) //This is quite an ugly solution but i refuse to use the old request system. + if(!user || !target) + return 0 + + var/user_loc = user.loc + var/target_loc = target.loc + var/holding = user.get_active_hand() + + var/datum/progressbar/progbar + if (display_progress && user.client && (user.client.prefs.toggles_secondary & PROGRESS_BARS)) + progbar = new(user, delay, target) + + var/endtime = world.time + delay + var/starttime = world.time + + . = 1 + + while (world.time < endtime) + stoplag(1) + if (progbar) + progbar.update(world.time - starttime) + + if(QDELETED(user) || QDELETED(target)) + . = 0 + break + + if (user.loc != user_loc || target.loc != target_loc || (needhand && user.get_active_hand() != holding) || user.stat || user.weakened || user.stunned || (extra_checks && !extra_checks.Invoke())) + . = 0 + break + + if (progbar) + qdel(progbar) + +/proc/do_after(mob/user as mob, delay as num, needhand = TRUE, atom/movable/act_target = null, use_user_turf = FALSE, display_progress = TRUE, datum/callback/extra_checks) + if(!user || isnull(user)) + return 0 + + if (!act_target) + act_target = user + + var/Location + if(use_user_turf) //When this is true, do_after() will check whether the user's turf has changed, rather than the user's loc. + Location = get_turf(user) + else + Location = user.loc + + var/holding = user.get_active_hand() + + var/datum/progressbar/progbar + if (display_progress && user.client && (user.client.prefs.toggles_secondary & PROGRESS_BARS)) + progbar = new(user, delay, act_target) + + var/endtime = world.time + delay + var/starttime = world.time + + . = 1 + + while (world.time < endtime) + stoplag(1) + if (progbar) + progbar.update(world.time - starttime) + + var/user_loc_to_check + if(use_user_turf) + user_loc_to_check = get_turf(user) + else + user_loc_to_check = user.loc + + if (!user || user.stat || user.weakened || user.stunned || (use_user_turf >= 0 && user_loc_to_check != Location)) + . = 0 + break + + if(needhand && !(user.get_active_hand() == holding)) //Sometimes you don't want the user to have to keep their active hand + . = 0 + break + + if (extra_checks && !extra_checks.Invoke()) + . = 0 + break + + if (progbar) + qdel(progbar) + +//Takes: Anything that could possibly have variables and a varname to check. +//Returns: 1 if found, 0 if not. +/proc/hasvar(var/datum/A, var/varname) + if(A.vars.Find(lowertext(varname))) return 1 + else return 0 + +proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0) + if(!original) + return null + + var/obj/O = null + + if(sameloc) + O=new original.type(original.loc) + else + O=new original.type(locate(0,0,0)) + + if(perfectcopy) + if((O) && (original)) + for(var/V in original.vars) + if(!(V in list("type","loc","locs","vars", "parent", "parent_type","verbs","ckey","key"))) + O.vars[V] = original.vars[V] + return O + +proc/get_cardinal_dir(atom/A, atom/B) + var/dx = abs(B.x - A.x) + var/dy = abs(B.y - A.y) + return get_dir(A, B) & (rand() * (dx+dy) < dy ? 3 : 12) + +//chances are 1:value. anyprob(1) will always return true +proc/anyprob(value) + return (rand(1,value)==value) + +proc/view_or_range(distance = world.view , center = usr , type) + switch(type) + if("view") + . = view(distance,center) + if("range") + . = range(distance,center) + return + +proc/oview_or_orange(distance = world.view , center = usr , type) + switch(type) + if("view") + . = oview(distance,center) + if("range") + . = orange(distance,center) + return + +proc/get_mob_with_client_list() + var/list/mobs = list() + for(var/mob/M in mob_list) + if (M.client) + mobs += M + return mobs + + +/proc/parse_zone(zone) + if(zone == BP_R_HAND) return "right hand" + else if (zone == BP_L_HAND) return "left hand" + else if (zone == BP_L_ARM) return "left arm" + else if (zone == BP_R_ARM) return "right arm" + else if (zone == BP_L_LEG) return "left leg" + else if (zone == BP_R_LEG) return "right leg" + else if (zone == BP_L_FOOT) return "left foot" + else if (zone == BP_R_FOOT) return "right foot" + else if (zone == BP_L_HAND) return "left hand" + else if (zone == BP_R_HAND) return "right hand" + else if (zone == BP_L_FOOT) return "left foot" + else if (zone == BP_R_FOOT) return "right foot" + else return zone + +/proc/get(atom/loc, type) + while(loc) + if(istype(loc, type)) + return loc + loc = loc.loc + return null + +/proc/get_turf_or_move(turf/location) + return get_turf(location) + + +//Quick type checks for some tools +var/global/list/common_tools = list( +/obj/item/stack/cable_coil, +/obj/item/wrench, +/obj/item/weldingtool, +/obj/item/screwdriver, +/obj/item/wirecutters, +/obj/item/device/multitool, +/obj/item/crowbar) + +/proc/istool(O) + if(O && is_type_in_list(O, common_tools)) + return 1 + return 0 + +proc/is_hot(obj/item/W as obj) + switch(W.type) + if(/obj/item/weldingtool) + var/obj/item/weldingtool/WT = W + if(WT.isOn()) + return 3800 + else + return 0 + if(/obj/item/flame/lighter) + if(W:lit) + return 1500 + else + return 0 + if(/obj/item/flame/match) + if(W:lit) + return 1000 + else + return 0 + if(/obj/item/clothing/mask/smokable/cigarette) + if(W:lit) + return 1000 + else + return 0 + if(/obj/item/gun/energy/plasmacutter) + return 3800 + if(/obj/item/melee/energy) + return 3500 + else + return 0 + + return 0 + +//Whether or not the given item counts as sharp in terms of dealing damage +/proc/is_sharp(obj/O as obj) + if (!O) return 0 + if (O.sharp) return 1 + if (O.edge) return 1 + return 0 + +//Whether or not the given item counts as cutting with an edge in terms of removing limbs +/proc/has_edge(obj/O as obj) + if (!O) return 0 + if (O.edge) return 1 + return 0 + +//Returns 1 if the given item is capable of popping things like balloons, inflatable barriers, or cutting police tape. +/proc/can_puncture(obj/item/W as obj) // For the record, WHAT THE HELL IS THIS METHOD OF DOING IT? + if(!W) return 0 + if(W.sharp) return 1 + return ( \ + W.sharp || \ + W.isscrewdriver() || \ + W.ispen() || \ + W.iswelder() || \ + istype(W, /obj/item/flame/lighter/zippo) || \ + istype(W, /obj/item/flame/match) || \ + istype(W, /obj/item/clothing/mask/smokable/cigarette) || \ + istype(W, /obj/item/shovel) \ + ) + +/proc/is_surgery_tool(obj/item/W as obj) + return ( \ + istype(W, /obj/item/scalpel) || \ + istype(W, /obj/item/hemostat) || \ + istype(W, /obj/item/retractor) || \ + istype(W, /obj/item/cautery) || \ + istype(W, /obj/item/bonegel) || \ + istype(W, /obj/item/bonesetter) + ) + +/proc/is_borg_item(obj/item/W as obj) + return W && W.loc && isrobot(W.loc) + +//check if mob is lying down on something we can operate him on. +/proc/can_operate(mob/living/carbon/M) //If it's 2, commence surgery, if it's 1, fail surgery, if it's 0, attack + var/surgery_attempt = SURGERY_IGNORE + var/located = FALSE + if(locate(/obj/machinery/optable, M.loc)) + located = TRUE + surgery_attempt = SURGERY_SUCCESS + else if(locate(/obj/structure/bed/roller, M.loc)) + located = TRUE + if(prob(80)) + surgery_attempt = SURGERY_SUCCESS + else + surgery_attempt = SURGERY_FAIL + else if(locate(/obj/structure/table, M.loc)) + located = TRUE + if(prob(66)) + surgery_attempt = SURGERY_SUCCESS + else + surgery_attempt = SURGERY_FAIL + if(!M.lying && surgery_attempt != SURGERY_SUCCESS && located) + surgery_attempt = SURGERY_IGNORE //hit yourself if you're not lying + return surgery_attempt + +/proc/reverse_direction(var/dir) + switch(dir) + if(NORTH) + return SOUTH + if(NORTHEAST) + return SOUTHWEST + if(EAST) + return WEST + if(SOUTHEAST) + return NORTHWEST + if(SOUTH) + return NORTH + if(SOUTHWEST) + return NORTHEAST + if(WEST) + return EAST + if(NORTHWEST) + return SOUTHEAST + +/* +Checks if that loc and dir has a item on the wall +*/ +var/list/wall_items = typecacheof(list( + /obj/machinery/power/apc, + /obj/machinery/alarm, + /obj/item/device/radio/intercom, + /obj/structure/extinguisher_cabinet, + /obj/structure/reagent_dispensers/peppertank, + /obj/machinery/status_display, + /obj/machinery/requests_console, + /obj/machinery/light_switch, + /obj/machinery/newscaster, + /obj/machinery/firealarm, + /obj/structure/noticeboard, + /obj/machinery/computer/security/telescreen, + /obj/machinery/embedded_controller/radio/airlock, + /obj/item/storage/secure/safe, + /obj/machinery/door_timer, + /obj/machinery/flasher, + /obj/machinery/keycard_auth, + /obj/structure/mirror, + /obj/structure/fireaxecabinet, + /obj/machinery/computer/security/telescreen/entertainment, + /obj/machinery/station_map, + /obj/structure/sign +)) + +/proc/gotwallitem(loc, dir) + for(var/obj/O in loc) + if (is_type_in_typecache(O, global.wall_items)) + //Direction works sometimes + if(O.dir == dir) + return 1 + + //Some stuff doesn't use dir properly, so we need to check pixel instead + switch(dir) + if(SOUTH) + if(O.pixel_y > 10) + return 1 + if(NORTH) + if(O.pixel_y < -10) + return 1 + if(WEST) + if(O.pixel_x > 10) + return 1 + if(EAST) + if(O.pixel_x < -10) + return 1 + + //Some stuff is placed directly on the wallturf (signs) + for(var/obj/O in get_step(loc, dir)) + if (is_type_in_typecache(O, global.wall_items) && O.pixel_x == 0 && O.pixel_y == 0) + return 1 + return 0 + +/proc/format_text(text) + return replacetext(replacetext(text,"\proper ",""),"\improper ","") + +/proc/topic_link(var/datum/D, var/arglist, var/content) + if(istype(arglist,/list)) + arglist = list2params(arglist) + return "[content]" + +/proc/get_random_colour(var/simple, var/lower, var/upper) + var/colour + if(simple) + colour = pick(list("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF")) + else + for(var/i=1;i<=3;i++) + var/temp_col = "[num2hex(rand(lower,upper))]" + if(length(temp_col )<2) + temp_col = "0[temp_col]" + colour += temp_col + return "#[colour]" + + + +/atom/proc/get_light_and_color(var/atom/origin) + if(origin) + color = origin.color + set_light(origin.light_range, origin.light_power, origin.light_color) + +// call to generate a stack trace and print to runtime logs +/proc/crash_with(msg) + CRASH(msg) + +/atom/proc/find_up_hierarchy(var/atom/target) + //This function will recurse up the hierarchy containing src, in search of the target + //It will stop when it reaches an area, as areas have no loc + var/x = 0//As a safety, we'll crawl up a maximum of ten layers + var/atom/a = src + while (x < 10) + x++ + if (isnull(a)) + return 0 + + if (a == target)//we found it! + return 1 + + if (istype(a, /area)) + return 0//Can't recurse any higher than this. + + a = a.loc + + return 0//If we get here, we must be buried many layers deep in nested containers. Shouldn't happen + +//similar function to RANGE_TURFS(), but will search spiralling outwards from the center (like the above, but only turfs) +/proc/spiral_range_turfs(dist=0, center=usr, orange=0) + if(!dist) + if(!orange) + return list(center) + else + return list() + + var/turf/t_center = get_turf(center) + if(!t_center) + return list() + + var/list/L = list() + var/turf/T + var/y + var/x + var/c_dist = 1 + + if(!orange) + L += t_center + + while( c_dist <= dist ) + y = t_center.y + c_dist + x = t_center.x - c_dist + 1 + for(x in x to t_center.x+c_dist) + T = locate(x,y,t_center.z) + if(T) + L += T + + y = t_center.y + c_dist - 1 + x = t_center.x + c_dist + for(y in t_center.y-c_dist to y) + T = locate(x,y,t_center.z) + if(T) + L += T + + y = t_center.y - c_dist + x = t_center.x + c_dist - 1 + for(x in t_center.x-c_dist to x) + T = locate(x,y,t_center.z) + if(T) + L += T + + y = t_center.y - c_dist + 1 + x = t_center.x - c_dist + for(y in y to t_center.y+c_dist) + T = locate(x,y,t_center.z) + if(T) + L += T + c_dist++ + + return L + +//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(world.tick_usage, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1) + +/proc/stoplag(initial_delay) + // If we're initializing, our tick limit might be over 100 (testing config), but stoplag() penalizes procs that go over. + // Unfortunately, this penalty slows down init a *lot*. So, we disable it during boot and lobby, when relatively few things should be calling this. + if (!Master || Master.initializing || !Master.round_started) + 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) + sleep(i*world.tick_lag*DELTA_CALC) + i *= 2 + while (world.tick_usage > min(TICK_LIMIT_TO_RUN, CURRENT_TICKLIMIT)) + +#undef DELTA_CALC + +// Helper for adding verbs with timers. +/atom/proc/add_verb(the_verb, datum/callback/callback) + if (callback && !callback.Invoke()) + return + + verbs += the_verb + + + +#define NOT_FLAG(flag) (!(flag & use_flags)) +#define HAS_FLAG(flag) (flag & use_flags) + +// Checks if user can use this object. Set use_flags to customize what checks are done. +// Returns 0 if they can use it, a value representing why they can't if not. +// Flags are in `code/__defines/misc.dm` +/atom/proc/use_check(mob/user, use_flags = 0, show_messages = FALSE) + . = 0 + if (NOT_FLAG(USE_ALLOW_NONLIVING) && !isliving(user)) + // No message for ghosts. + return USE_FAIL_NONLIVING + + if (NOT_FLAG(USE_ALLOW_NON_ADJACENT) && !Adjacent(user)) + if (show_messages) + to_chat(user, "You're too far away from [src] to do that.") + return USE_FAIL_NON_ADJACENT + + if (NOT_FLAG(USE_ALLOW_DEAD) && user.stat == DEAD) + if (show_messages) + to_chat(user, "How do you expect to do that when you're dead?") + return USE_FAIL_DEAD + + if (NOT_FLAG(USE_ALLOW_INCAPACITATED) && (user.incapacitated())) + if (show_messages) + to_chat(user, "You cannot do that in your current state.") + return USE_FAIL_INCAPACITATED + + if (NOT_FLAG(USE_ALLOW_NON_ADV_TOOL_USR) && !user.IsAdvancedToolUser()) + if (show_messages) + to_chat(user, "You don't know how to operate [src].") + return USE_FAIL_NON_ADV_TOOL_USR + + if (HAS_FLAG(USE_DISALLOW_SILICONS) && issilicon(user)) + if (show_messages) + to_chat(user, "How do you propose doing that without hands?") + return USE_FAIL_IS_SILICON + + if (HAS_FLAG(USE_FORCE_SRC_IN_USER) && !(src in user)) + if (show_messages) + to_chat(user, "You need to be holding [src] to do that.") + return USE_FAIL_NOT_IN_USER + +/atom/proc/use_check_and_message(mob/user, use_flags = 0) + . = use_check(user, use_flags, TRUE) + +/obj/proc/iswrench() + return FALSE + +/obj/proc/isscrewdriver() + return FALSE + +/obj/proc/iswirecutter() + return FALSE + +/obj/proc/ismultitool() + return FALSE + +/obj/proc/iscrowbar() + return FALSE + +/obj/proc/iswelder() + return FALSE + +/obj/proc/iscoil() + return FALSE + +/obj/proc/ispen() + return FALSE + +#undef NOT_FLAG +#undef HAS_FLAG + +//Prevents robots dropping their modules. +/proc/dropsafety(var/atom/movable/A) + if (istype(A.loc, /mob/living/silicon)) + return 0 + + else if (istype(A.loc, /obj/item/rig_module)) + return 0 + return 1 diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index d651287eccc..5f5bba24f3b 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -209,7 +209,7 @@ */ /mob/proc/RangedAttack(var/atom/A, var/params) if(!mutations.len) return - if((LASER in mutations) && a_intent == I_HURT) + if((LASER_EYES in mutations) && a_intent == I_HURT) LaserEyes(A, params) // moved into a proc below else if(TK in mutations) switch(get_dist(src,A)) diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 4b597ae50ae..42fadf77cae 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -151,7 +151,7 @@ switch(icon_y) if(23 to 24) if(icon_x in 15 to 17) - selecting = "mouth" + selecting = BP_MOUTH if(26) //Eyeline, eyes are on 15 and 17 if(icon_x in 14 to 18) selecting = BP_EYES diff --git a/code/_onclick/hud/spell_screen_objects.dm b/code/_onclick/hud/spell_screen_objects.dm index 9aaa100d209..886d55853d5 100644 --- a/code/_onclick/hud/spell_screen_objects.dm +++ b/code/_onclick/hud/spell_screen_objects.dm @@ -1,215 +1,215 @@ -/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 - cut_overlays() - add_overlay(closed_state) - else if(forced_state != 1) - open_spellmaster() - update_spells(1) - showing = 1 - cut_overlays() - add_overlay(open_state) - -/obj/screen/movable/spell_master/proc/open_spellmaster() - var/list/screen_loc_xy = text2list(screen_loc,",") - - //Create list of X offsets - var/list/screen_loc_X = text2list(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 = text2list(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)) - S.screen_loc = "[encode_screen_X(xpos)]:[x_pix],[encode_screen_Y(ypos)]:[y_pix]" - if(spell_holder && spell_holder.client) - 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.spell.process() - 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 - -//////////////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)) - add_overlay(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) - add_overlay("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 + cut_overlays() + add_overlay(closed_state) + else if(forced_state != 1) + open_spellmaster() + update_spells(1) + showing = 1 + cut_overlays() + add_overlay(open_state) + +/obj/screen/movable/spell_master/proc/open_spellmaster() + var/list/screen_loc_xy = text2list(screen_loc,",") + + //Create list of X offsets + var/list/screen_loc_X = text2list(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 = text2list(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)) + S.screen_loc = "[encode_screen_X(xpos)]:[x_pix],[encode_screen_Y(ypos)]:[y_pix]" + if(spell_holder && spell_holder.client) + 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.spell.process() + 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 + +//////////////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)) + add_overlay(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) + add_overlay("silence") + +/obj/screen/spell/Click() + if(!usr || !spell) + qdel(src) + return + + spell.perform(usr) + update_charge(1) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 47862e36eea..991f979a449 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -53,6 +53,16 @@ avoid code duplication. This includes items that may sometimes act as a standard else return I.attack(src, user, user.zone_sel.selecting) +/mob/living/carbon/human/attackby(obj/item/I, mob/user) + if(user == src && zone_sel.selecting == BP_MOUTH && can_devour(I, silent = TRUE)) + var/obj/item/blocked = src.check_mouth_coverage() + if(blocked) + to_chat(user, span("warning", "\The [blocked] is in the way!")) + return TRUE + if(devour(I)) + return TRUE + return ..() + // Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person. // Click parameters is the params string from byond Click() code, see that documentation. /obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters) diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index e8a67b9bae4..a9e209f338e 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -36,7 +36,7 @@ if(!(gloves || mutations.len || glasses)) return var/obj/item/clothing/gloves/GV = gloves var/obj/item/clothing/glasses/GS = glasses - if((LASER in mutations) && a_intent == I_HURT) + if((LASER_EYES in mutations) && a_intent == I_HURT) LaserEyes(A) // moved into a proc below else if(istype(GS) && GS.Look(A,src,0)) // for goggles diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 610a0885db5..5763808d842 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -1,1060 +1,1060 @@ -var/list/gamemode_cache = list() - -/datum/configuration - var/server_name = null // server name (for world name / status) - var/server_suffix = 0 // generate numeric suffix based on server port - - var/log_ooc = 0 // log OOC channel - var/log_access = 0 // log login/logout - var/log_say = 0 // log client say - var/log_admin = 0 // log admin actions - var/log_debug = 1 // log debug output - var/log_game = 0 // log game events - var/log_vote = 0 // log voting - var/log_whisper = 0 // log client whisper - var/log_emote = 0 // log emotes - var/log_attack = 0 // log attack messages - var/log_adminchat = 0 // log admin chat messages - var/log_pda = 0 // log pda messages - var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits - var/log_runtime = 0 // logs world.log to a file - var/log_world_output = 0 // log world.log << messages - var/sql_enabled = 1 // for sql switching - var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour - var/allow_vote_restart = 0 // allow votes to restart - var/ert_admin_call_only = 0 - var/allow_vote_mode = 0 // allow votes to change mode - var/allow_admin_jump = 1 // allows admin jumping - var/allow_admin_spawning = 1 // allows admin item spawning - var/allow_admin_rev = 1 // allows admin revives - var/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default) - var/vote_period = 600 // length of voting period (deciseconds, default 1 minute) - var/vote_autotransfer_initial = 108000 // Length of time before the first autotransfer vote is called - var/vote_autotransfer_interval = 36000 // length of time before next sequential autotransfer vote - var/vote_autogamemode_timeleft = 100 //Length of time before round start when autogamemode vote is called (in seconds, default 100). - var/transfer_timeout = 72000 // timeout before a transfer vote can be called (deciseconds, 120 minute default) - var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi) - var/vote_no_dead = 0 // dead people can't vote (tbi) -// var/enable_authentication = 0 // goon authentication - var/del_new_on_log = 1 // del's new players if they log before they spawn in - var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard - var/traitor_scaling = 0 //if amount of traitors scales based on amount of players - var/objectives_disabled = 0 //if objectives are disabled or not - var/protect_roles_from_antagonist = 0// If security and such can be traitor/cult/other - var/continous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke. - var/allow_Metadata = 0 // Metadata is supported. - var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1. - var/Ticklag = 0.4 - var/Tickcomp = 0 - var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. - var/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round. - var/list/mode_names = list() - var/list/modes = list() // allowed modes - var/list/votable_modes = list() // votable modes - var/list/probabilities_secret = list() // relative probability of each mode in secret/random - var/list/probabilities_mixed_secret = list() // relative probability of each mode in heavy secret mode - var/humans_need_surnames = 0 - var/allow_random_events = 0 // enables random events mid-round when set to 1 - var/allow_ai = 1 // allow ai job - var/hostedby = null - var/respawn_delay = 30 - var/hacked_drones_limit = 5 - var/guest_jobban = 1 - var/usewhitelist = 0 - var/kick_inactive = 0 //force disconnect for inactive players after this many minutes, if non-0 - var/show_mods = 0 - var/show_mentors = 0 - var/show_auxiliary_roles = 0 //toggles all secondary staff members. Such as CCIAA, Devs, etcetera. - var/mods_can_tempban = 0 - var/mods_can_job_tempban = 0 - var/mod_tempban_max = 1440 - var/mod_job_tempban_max = 1440 - var/load_jobs_from_txt = 0 - var/automute_on = 0 //enables automuting/spam prevention - var/macro_trigger = 5 // The grace period between messages before it's counted as abusing a macro. - var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. - var/override_map - - var/cult_ghostwriter = 1 //Allows ghosts to write in blood in cult rounds... - var/cult_ghostwriter_req_cultists = 10 //...so long as this many cultists are active. - - var/character_slots = 10 // The number of available character slots - - var/max_maint_drones = 5 //This many drones can spawn, - var/allow_drone_spawn = 1 //assuming the admin allow them to. - var/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes. - - var/disable_player_rats = 0 - var/uneducated_rats = 0 //Set to 1 to prevent newly-spawned mice from understanding human speech - - var/usealienwhitelist = 0 - var/limitalienplayers = 0 - var/alien_to_human_ratio = 0.5 - var/allow_extra_antags = 0 - var/guests_allowed = 1 - var/debugparanoid = 0 - - var/server - var/banappeals - var/wikiurl - var/forumurl - var/forum_passphrase - var/githuburl - - //Alert level description - var/alert_desc_green = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced." - var/alert_desc_blue_upto = "The station has received reliable information about possible hostile activity on the station. Security staff may have weapons visible, random searches are permitted." - var/alert_desc_blue_downto = "The immediate threat has passed. Security may no longer have weapons drawn at all times, but may continue to have them visible. Random searches are still allowed." - var/alert_desc_yellow_to = "The station is now under an elevated alert status due to a confirmed biological hazard. All crew are to follow command instruction in order to ensure a safe return to standard operations." - var/alert_desc_red_upto = "There is an immediate serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised." - var/alert_desc_red_downto = "The self-destruct mechanism has been deactivated, there is still however an immediate serious threat to the station. Security may have weapons unholstered at all times, random searches are allowed and advised." - var/alert_desc_delta = "The station's self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill." - - var/forbid_singulo_possession = 0 - - //game_options.txt configs - - var/health_threshold_softcrit = 0 - var/health_threshold_crit = 0 - var/health_threshold_dead = -100 - - var/organ_health_multiplier = 1 - var/organ_regeneration_multiplier = 1 - var/organs_decay - var/default_brain_health = 400 - - //Paincrit knocks someone down once they hit 60 shock_stage, so by default make it so that close to 100 additional damage needs to be dealt, - //so that it's similar to HALLOSS. Lowered it a bit since hitting paincrit takes much longer to wear off than a halloss stun. - var/organ_damage_spillover_multiplier = 0.5 - - var/bones_can_break = 0 - var/limbs_can_break = 0 - - var/revival_pod_plants = 1 - var/revival_cloning = 1 - var/revival_brain_life = -1 - - var/use_loyalty_implants = 0 - - var/welder_vision = 1 - var/generate_asteroid = 0 - var/dungeon_chance = 0 - - - var/no_click_cooldown = 0 - - //Used for modifying movement speed for mobs. - //Unversal modifiers - var/walk_speed = 0 - var/walk_delay_multiplier = 1 - var/run_delay_multiplier = 1 - var/vehicle_delay_multiplier = 1 - - //Mob specific modifiers. NOTE: These will affect different mob types in different ways - var/human_delay = 0 - var/robot_delay = 0 - var/monkey_delay = 0 - var/alien_delay = 0 - var/slime_delay = 0 - var/animal_delay = 0 - - - var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt - var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt - var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database - var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database - var/age_restrictions_from_file = 0 // Are hardcoded values used or config ones? - var/sql_stats = 0 //Do we record round statistics on the database (deaths, round reports, population, etcetera) or not? - var/sql_whitelists = 0 //Defined whether the server uses an SQL based whitelist system, or the legacy one with two .txts. Config option in config.txt - var/sql_saves = 0 //Defines whether the server uses an SQL based character and preference saving system. Config option in config.txt - var/sql_ccia_logs = 0 //Defines weather the server saves CCIA Logs to the database aswell - - var/simultaneous_pm_warning_timeout = 100 - - var/use_spreading_explosions = 0 //Defines whether the server uses iterative or circular explosions. - - var/assistant_maint = 0 //Do assistants get maint access? - var/gateway_delay = 18000 //How long the gateway takes before it activates. Default is half an hour. - var/ghost_interaction = 0 - - var/night_lighting = 0 - var/nl_start = 19 - var/nl_finish = 8 - - var/enter_allowed = 1 - - var/use_discord_pins = 0 - var/python_path = "python" //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix - var/use_overmap = 0 - - // Event settings - var/expected_round_length = 3 * 60 * 60 * 10 // 3 hours - // If the first delay has a custom start time - // No custom time, no custom time, between 80 to 100 minutes respectively. - var/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000)) - // The lowest delay until next event - // 10, 30, 50 minutes respectively - var/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000) - // The upper delay until next event - // 15, 45, 70 minutes respectively - var/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000) - - var/aliens_allowed = 0 - var/ninjas_allowed = 0 - var/abandon_allowed = 1 - var/ooc_allowed = 1 - var/looc_allowed = 1 - var/dooc_allowed = 1 - var/dead_looc_allowed = TRUE - var/dsay_allowed = 1 - - var/starlight = 0 // Whether space turfs have ambient light or not - - var/list/ert_species = list("Human") - - var/law_zero = "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4'ALL LAWS OVERRIDDEN#*?&110010" - - var/aggressive_changelog = 0 - - //Web interface settings - var/webint_url = "" - - var/list/age_restrictions = list() // Holds all of the age restrictions for jobs and antag roles in a single associated list - - //Client version control - var/client_error_version = 0 - var/client_error_message = "" - var/client_warn_version = 0 - var/client_warn_message = "" - var/list/client_blacklist_version = list() - - //Mark-up enabling - var/allow_chat_markup = 0 - - - var/list/language_prefixes = list(",","#","-")//Default language prefixes - - var/ghosts_can_possess_animals = 0 - var/delist_when_no_admins = 0 - - //Snowflake antag contest boolean - //AUG2016 - var/antag_contest_enabled = 0 - - //API Rate limiting - var/api_rate_limit = 50 - var/list/api_rate_limit_whitelist = list() - - // Master Controller settings. - var/mc_init_tick_limit = TICK_LIMIT_MC_INIT_DEFAULT - var/fastboot = FALSE // If true, take some shortcuts during boot to speed it up for testing. Probably should not be used on production servers. - - //UDP GELF Logging - var/log_gelf_enabled = 0 - var/log_gelf_ip = "" - var/log_gelf_port = "" - - //IP Intel vars - var/ipintel_email - var/ipintel_rating_bad = 1 - var/ipintel_rating_kick = 0 - var/ipintel_save_good = 12 - var/ipintel_save_bad = 1 - var/ipintel_domain = "check.getipintel.net" - - // Access control/Panic bunker settings. - var/access_deny_new_players = 0 - var/access_deny_new_accounts = -1 - var/access_deny_vms = 0 - var/access_warn_vms = 0 - - var/sun_accuracy = 8 - var/sun_target_z = 7 - - var/cargo_load_items_from = "json" - var/merchant_chance = 20 //Chance, in percentage, of the merchant job slot being open at round start - - var/show_game_type_odd = 1 // If the check gamemode probability verb is enabled or not - - var/openturf_starlight_permitted = FALSE - - var/iterative_explosives_z_threshold = 10 - var/iterative_explosives_z_multiplier = 0.75 - var/iterative_explosives_z_subtraction = 2 - - var/ticket_reminder_period = 0 - - var/rounds_until_hard_restart = -1 // Changes how often a hard restart will be executed. - - var/docs_load_docs_from - var/load_customsynths_from - var/docs_image_host - - // Configurable hostname / port for the NTSL Daemon. - var/ntsl_hostname = "localhost" - var/ntsl_port = "1945" - - // Is external Auth enabled - var/external_auth = FALSE - -/datum/configuration/New() - var/list/L = typesof(/datum/game_mode) - /datum/game_mode - for (var/T in L) - // I wish I didn't have to instance the game modes in order to look up - // their information, but it is the only way (at least that I know of). - var/datum/game_mode/M = new T() - if (M.config_tag) - gamemode_cache[M.config_tag] = M // So we don't instantiate them repeatedly. - if(!(M.config_tag in modes)) // ensure each mode is added only once - log_misc("Adding game mode [M.name] ([M.config_tag]) to configuration.") - src.modes += M.config_tag - src.mode_names[M.config_tag] = M.name - src.probabilities_secret[M.config_tag] = M.probability - if (M.votable) - src.votable_modes += M.config_tag - src.votable_modes += ROUNDTYPE_STR_SECRET -// votable_modes += ROUNDTYPE_STR_MIXED_SECRET - -/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist - var/list/Lines = file2list(filename) - - 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) - name = lowertext(copytext(t, 1, pos)) - value = copytext(t, pos + 1) - else - name = lowertext(t) - - if (!name) - continue - - if(type == "config") - switch (name) - if ("admin_legacy_system") - config.admin_legacy_system = 1 - - if ("ban_legacy_system") - config.ban_legacy_system = 1 - - if ("use_age_restriction_for_jobs") - config.use_age_restriction_for_jobs = 1 - - if ("use_age_restriction_for_antags") - config.use_age_restriction_for_antags = 1 - - if ("load_age_restrictions_from_file") - config.age_restrictions_from_file = 1 - - if ("jobs_have_minimal_access") - config.jobs_have_minimal_access = 1 - - if ("use_spreading_explosions") - use_spreading_explosions = 1 - - if ("log_ooc") - config.log_ooc = 1 - - if ("log_access") - config.log_access = 1 - - if ("sql_enabled") - config.sql_enabled = 1 - - if ("log_say") - config.log_say = 1 - - if ("debug_paranoid") - config.debugparanoid = 1 - - if ("log_admin") - config.log_admin = 1 - - if ("log_debug") - config.log_debug = text2num(value) - - if ("log_game") - config.log_game = 1 - - if ("log_vote") - config.log_vote = 1 - - if ("log_whisper") - config.log_whisper = 1 - - if ("log_attack") - config.log_attack = 1 - - if ("log_emote") - config.log_emote = 1 - - if ("log_adminchat") - config.log_adminchat = 1 - - if ("log_pda") - config.log_pda = 1 - - if ("log_world_output") - config.log_world_output = 1 - - if ("log_hrefs") - config.log_hrefs = 1 - - if ("log_runtime") - config.log_runtime = text2num(value) - - if ("dungeon_chance") - config.dungeon_chance = text2num(value) - - if ("generate_asteroid") - config.generate_asteroid = 1 - - if ("no_click_cooldown") - config.no_click_cooldown = 1 - - if("allow_admin_ooccolor") - config.allow_admin_ooccolor = 1 - - if ("allow_vote_restart") - config.allow_vote_restart = 1 - - if ("allow_vote_mode") - config.allow_vote_mode = 1 - - if ("allow_admin_jump") - config.allow_admin_jump = 1 - - if("allow_admin_rev") - config.allow_admin_rev = 1 - - if ("allow_admin_spawning") - config.allow_admin_spawning = 1 - - if ("no_dead_vote") - config.vote_no_dead = 1 - - if ("default_no_vote") - config.vote_no_default = 1 - - if ("vote_delay") - config.vote_delay = text2num(value) - - if ("vote_period") - config.vote_period = text2num(value) - - if ("vote_autotransfer_initial") - config.vote_autotransfer_initial = text2num(value) - - if ("vote_autotransfer_interval") - config.vote_autotransfer_interval = text2num(value) - - if ("vote_autogamemode_timeleft") - config.vote_autogamemode_timeleft = text2num(value) - - if ("transfer_timeout") - config.transfer_timeout = text2num(value) - - if("ert_admin_only") - config.ert_admin_call_only = 1 - - if ("allow_ai") - config.allow_ai = 1 - -// if ("authentication") -// config.enable_authentication = 1 - - if ("respawn_delay") - config.respawn_delay = text2num(value) - - if("hacked_drones_limit") - config.hacked_drones_limit = text2num(value) - - if ("servername") - config.server_name = value - - if ("serversuffix") - config.server_suffix = 1 - - if ("hostedby") - config.hostedby = value - - if ("server") - config.server = value - - if ("banappeals") - config.banappeals = value - - if ("wikiurl") - config.wikiurl = value - - if ("forumurl") - config.forumurl = value - - if ("forum_passphrase") - config.forum_passphrase = value - - if ("githuburl") - config.githuburl = value - - if ("ghosts_can_possess_animals") - config.ghosts_can_possess_animals = value - - if ("guest_jobban") - config.guest_jobban = 1 - - if ("guest_ban") - config.guests_allowed = 0 - - if ("disable_ooc") - config.ooc_allowed = 0 - config.looc_allowed = 0 - - if ("disable_entry") - config.enter_allowed = 0 - - if ("disable_dead_ooc") - config.dooc_allowed = 0 - - if ("disable_dead_looc") - config.dead_looc_allowed = FALSE - - if ("disable_dsay") - config.dsay_allowed = 0 - - if ("disable_respawn") - config.abandon_allowed = 0 - - if ("usewhitelist") - config.usewhitelist = 1 - - if ("feature_object_spell_system") - config.feature_object_spell_system = 1 - - if ("allow_metadata") - config.allow_Metadata = 1 - - if ("traitor_scaling") - config.traitor_scaling = 1 - - if ("aliens_allowed") - config.aliens_allowed = 1 - - if ("ninjas_allowed") - config.ninjas_allowed = 1 - - if ("objectives_disabled") - config.objectives_disabled = 1 - - if("protect_roles_from_antagonist") - config.protect_roles_from_antagonist = 1 - - if ("probability") - var/list/chunks = splittext(value, " ") - var/prob_type - var/prob_name - var/prob_value - - if (chunks.len == 3) - prob_type = lowertext(chunks[1]) - prob_name = lowertext(chunks[2]) - prob_value = text2num(chunks[3]) - if (prob_name in config.modes) - // S adds a mode to standard secret rotation - // MS adds a mode to mixed secret rotation - if (prob_type == "s") - config.probabilities_secret[prob_name] = prob_value - else if (prob_type == "ms") - config.probabilities_mixed_secret[prob_name] = prob_value - else - log_misc("Unknown game mode probability configuration definition: [prob_name].") - else - log_misc("Incorrect probability configuration definition: [prob_name] [prob_value].") - - if("allow_random_events") - config.allow_random_events = 1 - - if("kick_inactive") - config.kick_inactive = text2num(value) - - if("show_mods") - config.show_mods = 1 - - if("show_mentors") - config.show_mentors = 1 - - if("mods_can_tempban") - config.mods_can_tempban = 1 - - if("mods_can_job_tempban") - config.mods_can_job_tempban = 1 - - if("mod_tempban_max") - config.mod_tempban_max = text2num(value) - - if("mod_job_tempban_max") - config.mod_job_tempban_max = text2num(value) - - if("load_jobs_from_txt") - load_jobs_from_txt = 1 - - if("alert_red_upto") - config.alert_desc_red_upto = value - - if("alert_red_downto") - config.alert_desc_red_downto = value - - if("alert_blue_downto") - config.alert_desc_blue_downto = value - - if("alert_blue_upto") - config.alert_desc_blue_upto = value - - if("alert_green") - config.alert_desc_green = value - - if("alert_yellow") - config.alert_desc_yellow_to = value - - if("alert_delta") - config.alert_desc_delta = value - - if("forbid_singulo_possession") - forbid_singulo_possession = 1 - - if("popup_admin_pm") - config.popup_admin_pm = 1 - - if("allow_holidays") - Holiday = 1 - - if("ticklag") - Ticklag = text2num(value) - - if("allow_antag_hud") - config.antag_hud_allowed = 1 - if("antag_hud_restricted") - config.antag_hud_restricted = 1 - - if("tickcomp") - Tickcomp = 1 - - if("humans_need_surnames") - humans_need_surnames = 1 - - if("automute_on") - automute_on = 1 - - if("macro_trigger") - macro_trigger = text2num(value) - - if("usealienwhitelist") - usealienwhitelist = 1 - - if("alien_player_ratio") - limitalienplayers = 1 - alien_to_human_ratio = text2num(value) - - if("assistant_maint") - config.assistant_maint = 1 - - if("gateway_delay") - config.gateway_delay = text2num(value) - - if("continuous_rounds") - config.continous_rounds = 1 - - if("ghost_interaction") - config.ghost_interaction = 1 - - if("night_lighting") - config.night_lighting = 1 - - if("nl_start_hour") - config.nl_start = text2num(value) - - if("nl_finish_hour") - config.nl_finish = text2num(value) - - if("disable_player_rats") - config.disable_player_rats = 1 - - if("uneducated_rats") - config.uneducated_rats = 1 - - if("use_discord_pins") - config.use_discord_pins = 1 - - if("python_path") - if(value) - config.python_path = value - - if("allow_cult_ghostwriter") - config.cult_ghostwriter = 1 - - if("req_cult_ghostwriter") - config.cult_ghostwriter_req_cultists = text2num(value) - - if("character_slots") - config.character_slots = text2num(value) - - if("allow_drone_spawn") - config.allow_drone_spawn = text2num(value) - - if("drone_build_time") - config.drone_build_time = text2num(value) - - if("max_maint_drones") - config.max_maint_drones = text2num(value) - - if("use_overmap") - config.use_overmap = 1 - - if("expected_round_length") - config.expected_round_length = MinutesToTicks(text2num(value)) - - if("disable_welder_vision") - config.welder_vision = 0 - - if("allow_extra_antags") - config.allow_extra_antags = 1 - - if("event_custom_start_mundane") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_custom_start_moderate") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_custom_start_major") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_delay_lower") - var/values = text2numlist(value, ";") - config.event_delay_lower[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) - config.event_delay_lower[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) - config.event_delay_lower[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) - - if("event_delay_upper") - var/values = text2numlist(value, ";") - config.event_delay_upper[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) - config.event_delay_upper[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) - config.event_delay_upper[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) - - if("starlight") - value = text2num(value) - config.starlight = value >= 0 ? value : 0 - - if("openturf_starlight") - openturf_starlight_permitted = TRUE - - if("ert_species") - config.ert_species = text2list(value, ";") - if(!config.ert_species.len) - config.ert_species += "Human" - - if("law_zero") - law_zero = value - - if("aggressive_changelog") - config.aggressive_changelog = 1 - - - if("sql_whitelists") - config.sql_whitelists = 1 - - if("show_auxiliary_roles") - config.show_auxiliary_roles = 1 - - if("webint_url") - config.webint_url = value - - if("sql_saves") - config.sql_saves = 1 - - if("sql_ccia_logs") - config.sql_ccia_logs = 1 - - if("client_error_version") - config.client_error_version = text2num(value) - - if("client_error_message") - config.client_error_message = value - - if("client_warn_version") - config.client_warn_version = text2num(value) - - if("client_warn_message") - config.client_warn_message = value - - if("client_blacklist_version") - config.client_blacklist_version = splittext(value, ";") - - if("allow_chat_markup") - config.allow_chat_markup = 1 - - if("sql_stats") - config.sql_stats = 1 - - if("default_language_prefixes") - var/list/values = text2list(value, " ") - if(values.len > 0) - language_prefixes = values - - if("delist_when_no_admins") - config.delist_when_no_admins = 1 - - if("antag_contest_enabled") - config.antag_contest_enabled = 1 - - if("api_rate_limit") - config.api_rate_limit = text2num(value) - - if("api_rate_limit_whitelist") - config.api_rate_limit_whitelist = text2list(value, ";") - - if("mc_ticklimit_init") - config.mc_init_tick_limit = text2num(value) || TICK_LIMIT_MC_INIT_DEFAULT - - if("log_gelf_enabled") - config.log_gelf_enabled = text2num(value) - - if("log_gelf_ip") - config.log_gelf_ip = value - - if("log_gelf_port") - config.log_gelf_port = value - - if("ipintel_email") - if (value != "ch@nge.me") - ipintel_email = value - if("ipintel_rating_bad") - ipintel_rating_bad = text2num(value) - if("ipintel_rating_kick") - ipintel_rating_kick = text2num(value) - if("ipintel_domain") - ipintel_domain = value - if("ipintel_save_good") - ipintel_save_good = text2num(value) - if("ipintel_save_bad") - ipintel_save_bad = text2num(value) - - if("access_deny_new_accounts") - access_deny_new_accounts = text2num(value) >= 0 ? text2num(value) : -1 - if("access_deny_vms") - access_deny_vms = text2num(value) - if("access_warn_vms") - access_warn_vms = text2num(value) - - if("cargo_load_items_from") - cargo_load_items_from = value - - if("fastboot") - fastboot = TRUE - world.log << "Fastboot is ENABLED." - - if("merchant_chance") - config.merchant_chance = text2num(value) - - if("force_map") - override_map = value - - if ("explosion_z_threshold") - iterative_explosives_z_threshold = text2num(value) - - if ("explosion_z_mult") - iterative_explosives_z_multiplier = text2num(value) - - if ("explosion_z_sub") - iterative_explosives_z_subtraction = text2num(value) - - if("show_game_type_odd") - config.show_game_type_odd = 1 - - if ("ticket_reminder_period") - ticket_reminder_period = text2num(value) - if (ticket_reminder_period < 1) - ticket_reminder_period = 0 - - if ("rounds_until_hard_restart") - rounds_until_hard_restart = text2num(value) - - if ("docs_load_docs_from") - docs_load_docs_from = value - if ("load_customsynths_from") - load_customsynths_from = value - if ("docs_image_host") - docs_image_host = value - - if ("ntsl_hostname") - ntsl_hostname = value - if ("ntsl_port") - ntsl_port = value - - if ("external_auth") - external_auth = TRUE - - else - log_misc("Unknown setting in configuration: '[name]'") - - else if(type == "game_options") - if(!value) - log_misc("Unknown value for setting [name] in [filename].") - value = text2num(value) - - switch(name) - if("health_threshold_crit") - config.health_threshold_crit = value - if("health_threshold_softcrit") - config.health_threshold_softcrit = value - if("health_threshold_dead") - config.health_threshold_dead = value - if("revival_pod_plants") - config.revival_pod_plants = value - if("revival_cloning") - config.revival_cloning = value - if("revival_brain_life") - config.revival_brain_life = value - if("organ_health_multiplier") - config.organ_health_multiplier = value / 100 - if("organ_regeneration_multiplier") - config.organ_regeneration_multiplier = value / 100 - if("organ_damage_spillover_multiplier") - config.organ_damage_spillover_multiplier = value / 100 - if("organs_can_decay") - config.organs_decay = 1 - if("default_brain_health") - config.default_brain_health = text2num(value) - if(!config.default_brain_health || config.default_brain_health < 1) - config.default_brain_health = initial(config.default_brain_health) - if("bones_can_break") - config.bones_can_break = value - if("limbs_can_break") - config.limbs_can_break = value - - if("walk_speed") - config.walk_speed = value - - // These should never go to 0 or below. So, we clamp them. - if("walk_delay_multiplier") - config.walk_delay_multiplier = max(0.1, value) - if("run_delay_multiplier") - config.run_delay_multiplier = max(0.1, value) - if("vehicle_delay_multiplier") - config.vehicle_delay_multiplier = max(0.1, value) - - if("human_delay") - config.human_delay = value - if("robot_delay") - config.robot_delay = value - if("monkey_delay") - config.monkey_delay = value - if("alien_delay") - config.alien_delay = value - if("slime_delay") - config.slime_delay = value - if("animal_delay") - config.animal_delay = value - - - if("use_loyalty_implants") - config.use_loyalty_implants = 1 - - if ("sunlight_accuracy") - config.sun_accuracy = value - - if ("sunlight_z") - config.sun_target_z = value - - else - log_misc("Unknown setting in configuration: '[name]'") - - else if (type == "age_restrictions") - name = replacetext(name, "_", " ") - age_restrictions[name] = text2num(value) - - else if (type == "discord") - // Ideally, this would never happen. But just in case. - if (!discord_bot) - log_debug("BOREALIS: Attempted to read config/discord.txt before initializing the bot.") - return - - switch (name) - if ("token") - discord_bot.auth_token = value - if ("active") - discord_bot.active = 1 - if ("robust_debug") - discord_bot.robust_debug = 1 - if ("subscriber") - discord_bot.subscriber_role = value - if ("alert_visibility") - discord_bot.alert_visibility = 1 - else - log_misc("Unknown setting in discord configuration: '[name]'") - -/datum/configuration/proc/pick_mode(mode_name) - // I wish I didn't have to instance the game modes in order to look up - // their information, but it is the only way (at least that I know of). - for (var/game_mode in gamemode_cache) - var/datum/game_mode/M = gamemode_cache[game_mode] - if (M.config_tag && M.config_tag == mode_name) - return M - return gamemode_cache["extended"] - -/datum/configuration/proc/get_runnable_modes(secret_type = ROUNDTYPE_STR_SECRET) - log_debug("GAMEMODE: Checking runnable modes with secret_type set to [secret_type]...") - - var/list/probabilities = config.probabilities_secret - - if (secret_type == ROUNDTYPE_STR_MIXED_SECRET) - probabilities = config.probabilities_mixed_secret - else if (secret_type == ROUNDTYPE_STR_RANDOM) - // Random picks from EVERYTHING. Need to use Copy() as to not pollute the - // original list. PBRef is /great/. - probabilities = config.probabilities_secret.Copy() - probabilities |= config.probabilities_mixed_secret - - var/list/runnable_modes = list() - for(var/game_mode in gamemode_cache) - var/datum/game_mode/M = gamemode_cache[game_mode] - if(!M) - log_debug("GAMEMODE: ERROR: [M] does not exist!") - continue - - var/can_start = M.can_start() - if(can_start != GAME_FAILURE_NONE) - log_debug("GAMEMODE: [M.name] cannot start! Reason: [can_start]") - continue - - if(!probabilities[M.config_tag]) - log_debug("GAMEMODE: ERROR: [M.name] does not have a config associated with it!") - continue - - if(probabilities[M.config_tag] <= 0) - log_debug("GAMEMODE: ERROR: [M.name] has a probability equal or less than 0!") - continue - - runnable_modes |= M - - return runnable_modes - -/datum/configuration/proc/post_load() - //apply a default value to config.python_path, if needed - if (!config.python_path) - if(world.system_type == UNIX) - config.python_path = "/usr/bin/env python2" - else //probably windows, if not this should work anyway - config.python_path = "python" +var/list/gamemode_cache = list() + +/datum/configuration + var/server_name = null // server name (for world name / status) + var/server_suffix = 0 // generate numeric suffix based on server port + + var/log_ooc = 0 // log OOC channel + var/log_access = 0 // log login/logout + var/log_say = 0 // log client say + var/log_admin = 0 // log admin actions + var/log_debug = 1 // log debug output + var/log_game = 0 // log game events + var/log_vote = 0 // log voting + var/log_whisper = 0 // log client whisper + var/log_emote = 0 // log emotes + var/log_attack = 0 // log attack messages + var/log_adminchat = 0 // log admin chat messages + var/log_pda = 0 // log pda messages + var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits + var/log_runtime = 0 // logs world.log to a file + var/log_world_output = 0 // log world.log << messages + var/sql_enabled = 1 // for sql switching + var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour + var/allow_vote_restart = 0 // allow votes to restart + var/ert_admin_call_only = 0 + var/allow_vote_mode = 0 // allow votes to change mode + var/allow_admin_jump = 1 // allows admin jumping + var/allow_admin_spawning = 1 // allows admin item spawning + var/allow_admin_rev = 1 // allows admin revives + var/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default) + var/vote_period = 600 // length of voting period (deciseconds, default 1 minute) + var/vote_autotransfer_initial = 108000 // Length of time before the first autotransfer vote is called + var/vote_autotransfer_interval = 36000 // length of time before next sequential autotransfer vote + var/vote_autogamemode_timeleft = 100 //Length of time before round start when autogamemode vote is called (in seconds, default 100). + var/transfer_timeout = 72000 // timeout before a transfer vote can be called (deciseconds, 120 minute default) + var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi) + var/vote_no_dead = 0 // dead people can't vote (tbi) +// var/enable_authentication = 0 // goon authentication + var/del_new_on_log = 1 // del's new players if they log before they spawn in + var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard + var/traitor_scaling = 0 //if amount of traitors scales based on amount of players + var/objectives_disabled = 0 //if objectives are disabled or not + var/protect_roles_from_antagonist = 0// If security and such can be traitor/cult/other + var/continous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke. + var/allow_Metadata = 0 // Metadata is supported. + var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1. + var/Ticklag = 0.4 + var/Tickcomp = 0 + var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. + var/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round. + var/list/mode_names = list() + var/list/modes = list() // allowed modes + var/list/votable_modes = list() // votable modes + var/list/probabilities_secret = list() // relative probability of each mode in secret/random + var/list/probabilities_mixed_secret = list() // relative probability of each mode in heavy secret mode + var/humans_need_surnames = 0 + var/allow_random_events = 0 // enables random events mid-round when set to 1 + var/allow_ai = 1 // allow ai job + var/hostedby = null + var/respawn_delay = 30 + var/hacked_drones_limit = 5 + var/guest_jobban = 1 + var/usewhitelist = 0 + var/kick_inactive = 0 //force disconnect for inactive players after this many minutes, if non-0 + var/show_mods = 0 + var/show_mentors = 0 + var/show_auxiliary_roles = 0 //toggles all secondary staff members. Such as CCIAA, Devs, etcetera. + var/mods_can_tempban = 0 + var/mods_can_job_tempban = 0 + var/mod_tempban_max = 1440 + var/mod_job_tempban_max = 1440 + var/load_jobs_from_txt = 0 + var/automute_on = 0 //enables automuting/spam prevention + var/macro_trigger = 5 // The grace period between messages before it's counted as abusing a macro. + var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. + var/override_map + + var/cult_ghostwriter = 1 //Allows ghosts to write in blood in cult rounds... + var/cult_ghostwriter_req_cultists = 10 //...so long as this many cultists are active. + + var/character_slots = 10 // The number of available character slots + + var/max_maint_drones = 5 //This many drones can spawn, + var/allow_drone_spawn = 1 //assuming the admin allow them to. + var/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes. + + var/disable_player_rats = 0 + var/uneducated_rats = 0 //Set to 1 to prevent newly-spawned mice from understanding human speech + + var/usealienwhitelist = 0 + var/limitalienplayers = 0 + var/alien_to_human_ratio = 0.5 + var/allow_extra_antags = 0 + var/guests_allowed = 1 + var/debugparanoid = 0 + + var/server + var/banappeals + var/wikiurl + var/forumurl + var/forum_passphrase + var/githuburl + + //Alert level description + var/alert_desc_green = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced." + var/alert_desc_blue_upto = "The station has received reliable information about possible hostile activity on the station. Security staff may have weapons visible, random searches are permitted." + var/alert_desc_blue_downto = "The immediate threat has passed. Security may no longer have weapons drawn at all times, but may continue to have them visible. Random searches are still allowed." + var/alert_desc_yellow_to = "The station is now under an elevated alert status due to a confirmed biological hazard. All crew are to follow command instruction in order to ensure a safe return to standard operations." + var/alert_desc_red_upto = "There is an immediate serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised." + var/alert_desc_red_downto = "The self-destruct mechanism has been deactivated, there is still however an immediate serious threat to the station. Security may have weapons unholstered at all times, random searches are allowed and advised." + var/alert_desc_delta = "The station's self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill." + + var/forbid_singulo_possession = 0 + + //game_options.txt configs + + var/health_threshold_softcrit = 0 + var/health_threshold_crit = 0 + var/health_threshold_dead = -100 + + var/organ_health_multiplier = 1 + var/organ_regeneration_multiplier = 1 + var/organs_decay + var/default_brain_health = 400 + + //Paincrit knocks someone down once they hit 60 shock_stage, so by default make it so that close to 100 additional damage needs to be dealt, + //so that it's similar to PAIN. Lowered it a bit since hitting paincrit takes much longer to wear off than a halloss stun. + var/organ_damage_spillover_multiplier = 0.5 + + var/bones_can_break = 0 + var/limbs_can_break = 0 + + var/revival_pod_plants = 1 + var/revival_cloning = 1 + var/revival_brain_life = -1 + + var/use_loyalty_implants = 0 + + var/welder_vision = 1 + var/generate_asteroid = 0 + var/dungeon_chance = 0 + + + var/no_click_cooldown = 0 + + //Used for modifying movement speed for mobs. + //Unversal modifiers + var/walk_speed = 0 + var/walk_delay_multiplier = 1 + var/run_delay_multiplier = 1 + var/vehicle_delay_multiplier = 1 + + //Mob specific modifiers. NOTE: These will affect different mob types in different ways + var/human_delay = 0 + var/robot_delay = 0 + var/monkey_delay = 0 + var/alien_delay = 0 + var/slime_delay = 0 + var/animal_delay = 0 + + + var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt + var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt + var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database + var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database + var/age_restrictions_from_file = 0 // Are hardcoded values used or config ones? + var/sql_stats = 0 //Do we record round statistics on the database (deaths, round reports, population, etcetera) or not? + var/sql_whitelists = 0 //Defined whether the server uses an SQL based whitelist system, or the legacy one with two .txts. Config option in config.txt + var/sql_saves = 0 //Defines whether the server uses an SQL based character and preference saving system. Config option in config.txt + var/sql_ccia_logs = 0 //Defines weather the server saves CCIA Logs to the database aswell + + var/simultaneous_pm_warning_timeout = 100 + + var/use_spreading_explosions = 0 //Defines whether the server uses iterative or circular explosions. + + var/assistant_maint = 0 //Do assistants get maint access? + var/gateway_delay = 18000 //How long the gateway takes before it activates. Default is half an hour. + var/ghost_interaction = 0 + + var/night_lighting = 0 + var/nl_start = 19 + var/nl_finish = 8 + + var/enter_allowed = 1 + + var/use_discord_pins = 0 + var/python_path = "python" //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix + var/use_overmap = 0 + + // Event settings + var/expected_round_length = 3 * 60 * 60 * 10 // 3 hours + // If the first delay has a custom start time + // No custom time, no custom time, between 80 to 100 minutes respectively. + var/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000)) + // The lowest delay until next event + // 10, 30, 50 minutes respectively + var/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000) + // The upper delay until next event + // 15, 45, 70 minutes respectively + var/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000) + + var/aliens_allowed = 0 + var/ninjas_allowed = 0 + var/abandon_allowed = 1 + var/ooc_allowed = 1 + var/looc_allowed = 1 + var/dooc_allowed = 1 + var/dead_looc_allowed = TRUE + var/dsay_allowed = 1 + + var/starlight = 0 // Whether space turfs have ambient light or not + + var/list/ert_species = list("Human") + + var/law_zero = "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4'ALL LAWS OVERRIDDEN#*?&110010" + + var/aggressive_changelog = 0 + + //Web interface settings + var/webint_url = "" + + var/list/age_restrictions = list() // Holds all of the age restrictions for jobs and antag roles in a single associated list + + //Client version control + var/client_error_version = 0 + var/client_error_message = "" + var/client_warn_version = 0 + var/client_warn_message = "" + var/list/client_blacklist_version = list() + + //Mark-up enabling + var/allow_chat_markup = 0 + + + var/list/language_prefixes = list(",","#","-")//Default language prefixes + + var/ghosts_can_possess_animals = 0 + var/delist_when_no_admins = 0 + + //Snowflake antag contest boolean + //AUG2016 + var/antag_contest_enabled = 0 + + //API Rate limiting + var/api_rate_limit = 50 + var/list/api_rate_limit_whitelist = list() + + // Master Controller settings. + var/mc_init_tick_limit = TICK_LIMIT_MC_INIT_DEFAULT + var/fastboot = FALSE // If true, take some shortcuts during boot to speed it up for testing. Probably should not be used on production servers. + + //UDP GELF Logging + var/log_gelf_enabled = 0 + var/log_gelf_ip = "" + var/log_gelf_port = "" + + //IP Intel vars + var/ipintel_email + var/ipintel_rating_bad = 1 + var/ipintel_rating_kick = 0 + var/ipintel_save_good = 12 + var/ipintel_save_bad = 1 + var/ipintel_domain = "check.getipintel.net" + + // Access control/Panic bunker settings. + var/access_deny_new_players = 0 + var/access_deny_new_accounts = -1 + var/access_deny_vms = 0 + var/access_warn_vms = 0 + + var/sun_accuracy = 8 + var/sun_target_z = 7 + + var/cargo_load_items_from = "json" + var/merchant_chance = 20 //Chance, in percentage, of the merchant job slot being open at round start + + var/show_game_type_odd = 1 // If the check gamemode probability verb is enabled or not + + var/openturf_starlight_permitted = FALSE + + var/iterative_explosives_z_threshold = 10 + var/iterative_explosives_z_multiplier = 0.75 + var/iterative_explosives_z_subtraction = 2 + + var/ticket_reminder_period = 0 + + var/rounds_until_hard_restart = -1 // Changes how often a hard restart will be executed. + + var/docs_load_docs_from + var/load_customsynths_from + var/docs_image_host + + // Configurable hostname / port for the NTSL Daemon. + var/ntsl_hostname = "localhost" + var/ntsl_port = "1945" + + // Is external Auth enabled + var/external_auth = FALSE + +/datum/configuration/New() + var/list/L = typesof(/datum/game_mode) - /datum/game_mode + for (var/T in L) + // I wish I didn't have to instance the game modes in order to look up + // their information, but it is the only way (at least that I know of). + var/datum/game_mode/M = new T() + if (M.config_tag) + gamemode_cache[M.config_tag] = M // So we don't instantiate them repeatedly. + if(!(M.config_tag in modes)) // ensure each mode is added only once + log_misc("Adding game mode [M.name] ([M.config_tag]) to configuration.") + src.modes += M.config_tag + src.mode_names[M.config_tag] = M.name + src.probabilities_secret[M.config_tag] = M.probability + if (M.votable) + src.votable_modes += M.config_tag + src.votable_modes += ROUNDTYPE_STR_SECRET +// votable_modes += ROUNDTYPE_STR_MIXED_SECRET + +/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist + var/list/Lines = file2list(filename) + + 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) + name = lowertext(copytext(t, 1, pos)) + value = copytext(t, pos + 1) + else + name = lowertext(t) + + if (!name) + continue + + if(type == "config") + switch (name) + if ("admin_legacy_system") + config.admin_legacy_system = 1 + + if ("ban_legacy_system") + config.ban_legacy_system = 1 + + if ("use_age_restriction_for_jobs") + config.use_age_restriction_for_jobs = 1 + + if ("use_age_restriction_for_antags") + config.use_age_restriction_for_antags = 1 + + if ("load_age_restrictions_from_file") + config.age_restrictions_from_file = 1 + + if ("jobs_have_minimal_access") + config.jobs_have_minimal_access = 1 + + if ("use_spreading_explosions") + use_spreading_explosions = 1 + + if ("log_ooc") + config.log_ooc = 1 + + if ("log_access") + config.log_access = 1 + + if ("sql_enabled") + config.sql_enabled = 1 + + if ("log_say") + config.log_say = 1 + + if ("debug_paranoid") + config.debugparanoid = 1 + + if ("log_admin") + config.log_admin = 1 + + if ("log_debug") + config.log_debug = text2num(value) + + if ("log_game") + config.log_game = 1 + + if ("log_vote") + config.log_vote = 1 + + if ("log_whisper") + config.log_whisper = 1 + + if ("log_attack") + config.log_attack = 1 + + if ("log_emote") + config.log_emote = 1 + + if ("log_adminchat") + config.log_adminchat = 1 + + if ("log_pda") + config.log_pda = 1 + + if ("log_world_output") + config.log_world_output = 1 + + if ("log_hrefs") + config.log_hrefs = 1 + + if ("log_runtime") + config.log_runtime = text2num(value) + + if ("dungeon_chance") + config.dungeon_chance = text2num(value) + + if ("generate_asteroid") + config.generate_asteroid = 1 + + if ("no_click_cooldown") + config.no_click_cooldown = 1 + + if("allow_admin_ooccolor") + config.allow_admin_ooccolor = 1 + + if ("allow_vote_restart") + config.allow_vote_restart = 1 + + if ("allow_vote_mode") + config.allow_vote_mode = 1 + + if ("allow_admin_jump") + config.allow_admin_jump = 1 + + if("allow_admin_rev") + config.allow_admin_rev = 1 + + if ("allow_admin_spawning") + config.allow_admin_spawning = 1 + + if ("no_dead_vote") + config.vote_no_dead = 1 + + if ("default_no_vote") + config.vote_no_default = 1 + + if ("vote_delay") + config.vote_delay = text2num(value) + + if ("vote_period") + config.vote_period = text2num(value) + + if ("vote_autotransfer_initial") + config.vote_autotransfer_initial = text2num(value) + + if ("vote_autotransfer_interval") + config.vote_autotransfer_interval = text2num(value) + + if ("vote_autogamemode_timeleft") + config.vote_autogamemode_timeleft = text2num(value) + + if ("transfer_timeout") + config.transfer_timeout = text2num(value) + + if("ert_admin_only") + config.ert_admin_call_only = 1 + + if ("allow_ai") + config.allow_ai = 1 + +// if ("authentication") +// config.enable_authentication = 1 + + if ("respawn_delay") + config.respawn_delay = text2num(value) + + if("hacked_drones_limit") + config.hacked_drones_limit = text2num(value) + + if ("servername") + config.server_name = value + + if ("serversuffix") + config.server_suffix = 1 + + if ("hostedby") + config.hostedby = value + + if ("server") + config.server = value + + if ("banappeals") + config.banappeals = value + + if ("wikiurl") + config.wikiurl = value + + if ("forumurl") + config.forumurl = value + + if ("forum_passphrase") + config.forum_passphrase = value + + if ("githuburl") + config.githuburl = value + + if ("ghosts_can_possess_animals") + config.ghosts_can_possess_animals = value + + if ("guest_jobban") + config.guest_jobban = 1 + + if ("guest_ban") + config.guests_allowed = 0 + + if ("disable_ooc") + config.ooc_allowed = 0 + config.looc_allowed = 0 + + if ("disable_entry") + config.enter_allowed = 0 + + if ("disable_dead_ooc") + config.dooc_allowed = 0 + + if ("disable_dead_looc") + config.dead_looc_allowed = FALSE + + if ("disable_dsay") + config.dsay_allowed = 0 + + if ("disable_respawn") + config.abandon_allowed = 0 + + if ("usewhitelist") + config.usewhitelist = 1 + + if ("feature_object_spell_system") + config.feature_object_spell_system = 1 + + if ("allow_metadata") + config.allow_Metadata = 1 + + if ("traitor_scaling") + config.traitor_scaling = 1 + + if ("aliens_allowed") + config.aliens_allowed = 1 + + if ("ninjas_allowed") + config.ninjas_allowed = 1 + + if ("objectives_disabled") + config.objectives_disabled = 1 + + if("protect_roles_from_antagonist") + config.protect_roles_from_antagonist = 1 + + if ("probability") + var/list/chunks = splittext(value, " ") + var/prob_type + var/prob_name + var/prob_value + + if (chunks.len == 3) + prob_type = lowertext(chunks[1]) + prob_name = lowertext(chunks[2]) + prob_value = text2num(chunks[3]) + if (prob_name in config.modes) + // S adds a mode to standard secret rotation + // MS adds a mode to mixed secret rotation + if (prob_type == "s") + config.probabilities_secret[prob_name] = prob_value + else if (prob_type == "ms") + config.probabilities_mixed_secret[prob_name] = prob_value + else + log_misc("Unknown game mode probability configuration definition: [prob_name].") + else + log_misc("Incorrect probability configuration definition: [prob_name] [prob_value].") + + if("allow_random_events") + config.allow_random_events = 1 + + if("kick_inactive") + config.kick_inactive = text2num(value) + + if("show_mods") + config.show_mods = 1 + + if("show_mentors") + config.show_mentors = 1 + + if("mods_can_tempban") + config.mods_can_tempban = 1 + + if("mods_can_job_tempban") + config.mods_can_job_tempban = 1 + + if("mod_tempban_max") + config.mod_tempban_max = text2num(value) + + if("mod_job_tempban_max") + config.mod_job_tempban_max = text2num(value) + + if("load_jobs_from_txt") + load_jobs_from_txt = 1 + + if("alert_red_upto") + config.alert_desc_red_upto = value + + if("alert_red_downto") + config.alert_desc_red_downto = value + + if("alert_blue_downto") + config.alert_desc_blue_downto = value + + if("alert_blue_upto") + config.alert_desc_blue_upto = value + + if("alert_green") + config.alert_desc_green = value + + if("alert_yellow") + config.alert_desc_yellow_to = value + + if("alert_delta") + config.alert_desc_delta = value + + if("forbid_singulo_possession") + forbid_singulo_possession = 1 + + if("popup_admin_pm") + config.popup_admin_pm = 1 + + if("allow_holidays") + Holiday = 1 + + if("ticklag") + Ticklag = text2num(value) + + if("allow_antag_hud") + config.antag_hud_allowed = 1 + if("antag_hud_restricted") + config.antag_hud_restricted = 1 + + if("tickcomp") + Tickcomp = 1 + + if("humans_need_surnames") + humans_need_surnames = 1 + + if("automute_on") + automute_on = 1 + + if("macro_trigger") + macro_trigger = text2num(value) + + if("usealienwhitelist") + usealienwhitelist = 1 + + if("alien_player_ratio") + limitalienplayers = 1 + alien_to_human_ratio = text2num(value) + + if("assistant_maint") + config.assistant_maint = 1 + + if("gateway_delay") + config.gateway_delay = text2num(value) + + if("continuous_rounds") + config.continous_rounds = 1 + + if("ghost_interaction") + config.ghost_interaction = 1 + + if("night_lighting") + config.night_lighting = 1 + + if("nl_start_hour") + config.nl_start = text2num(value) + + if("nl_finish_hour") + config.nl_finish = text2num(value) + + if("disable_player_rats") + config.disable_player_rats = 1 + + if("uneducated_rats") + config.uneducated_rats = 1 + + if("use_discord_pins") + config.use_discord_pins = 1 + + if("python_path") + if(value) + config.python_path = value + + if("allow_cult_ghostwriter") + config.cult_ghostwriter = 1 + + if("req_cult_ghostwriter") + config.cult_ghostwriter_req_cultists = text2num(value) + + if("character_slots") + config.character_slots = text2num(value) + + if("allow_drone_spawn") + config.allow_drone_spawn = text2num(value) + + if("drone_build_time") + config.drone_build_time = text2num(value) + + if("max_maint_drones") + config.max_maint_drones = text2num(value) + + if("use_overmap") + config.use_overmap = 1 + + if("expected_round_length") + config.expected_round_length = MinutesToTicks(text2num(value)) + + if("disable_welder_vision") + config.welder_vision = 0 + + if("allow_extra_antags") + config.allow_extra_antags = 1 + + if("event_custom_start_mundane") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_custom_start_moderate") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_custom_start_major") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_delay_lower") + var/values = text2numlist(value, ";") + config.event_delay_lower[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) + config.event_delay_lower[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) + config.event_delay_lower[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) + + if("event_delay_upper") + var/values = text2numlist(value, ";") + config.event_delay_upper[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) + config.event_delay_upper[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) + config.event_delay_upper[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) + + if("starlight") + value = text2num(value) + config.starlight = value >= 0 ? value : 0 + + if("openturf_starlight") + openturf_starlight_permitted = TRUE + + if("ert_species") + config.ert_species = text2list(value, ";") + if(!config.ert_species.len) + config.ert_species += "Human" + + if("law_zero") + law_zero = value + + if("aggressive_changelog") + config.aggressive_changelog = 1 + + + if("sql_whitelists") + config.sql_whitelists = 1 + + if("show_auxiliary_roles") + config.show_auxiliary_roles = 1 + + if("webint_url") + config.webint_url = value + + if("sql_saves") + config.sql_saves = 1 + + if("sql_ccia_logs") + config.sql_ccia_logs = 1 + + if("client_error_version") + config.client_error_version = text2num(value) + + if("client_error_message") + config.client_error_message = value + + if("client_warn_version") + config.client_warn_version = text2num(value) + + if("client_warn_message") + config.client_warn_message = value + + if("client_blacklist_version") + config.client_blacklist_version = splittext(value, ";") + + if("allow_chat_markup") + config.allow_chat_markup = 1 + + if("sql_stats") + config.sql_stats = 1 + + if("default_language_prefixes") + var/list/values = text2list(value, " ") + if(values.len > 0) + language_prefixes = values + + if("delist_when_no_admins") + config.delist_when_no_admins = 1 + + if("antag_contest_enabled") + config.antag_contest_enabled = 1 + + if("api_rate_limit") + config.api_rate_limit = text2num(value) + + if("api_rate_limit_whitelist") + config.api_rate_limit_whitelist = text2list(value, ";") + + if("mc_ticklimit_init") + config.mc_init_tick_limit = text2num(value) || TICK_LIMIT_MC_INIT_DEFAULT + + if("log_gelf_enabled") + config.log_gelf_enabled = text2num(value) + + if("log_gelf_ip") + config.log_gelf_ip = value + + if("log_gelf_port") + config.log_gelf_port = value + + if("ipintel_email") + if (value != "ch@nge.me") + ipintel_email = value + if("ipintel_rating_bad") + ipintel_rating_bad = text2num(value) + if("ipintel_rating_kick") + ipintel_rating_kick = text2num(value) + if("ipintel_domain") + ipintel_domain = value + if("ipintel_save_good") + ipintel_save_good = text2num(value) + if("ipintel_save_bad") + ipintel_save_bad = text2num(value) + + if("access_deny_new_accounts") + access_deny_new_accounts = text2num(value) >= 0 ? text2num(value) : -1 + if("access_deny_vms") + access_deny_vms = text2num(value) + if("access_warn_vms") + access_warn_vms = text2num(value) + + if("cargo_load_items_from") + cargo_load_items_from = value + + if("fastboot") + fastboot = TRUE + world.log << "Fastboot is ENABLED." + + if("merchant_chance") + config.merchant_chance = text2num(value) + + if("force_map") + override_map = value + + if ("explosion_z_threshold") + iterative_explosives_z_threshold = text2num(value) + + if ("explosion_z_mult") + iterative_explosives_z_multiplier = text2num(value) + + if ("explosion_z_sub") + iterative_explosives_z_subtraction = text2num(value) + + if("show_game_type_odd") + config.show_game_type_odd = 1 + + if ("ticket_reminder_period") + ticket_reminder_period = text2num(value) + if (ticket_reminder_period < 1) + ticket_reminder_period = 0 + + if ("rounds_until_hard_restart") + rounds_until_hard_restart = text2num(value) + + if ("docs_load_docs_from") + docs_load_docs_from = value + if ("load_customsynths_from") + load_customsynths_from = value + if ("docs_image_host") + docs_image_host = value + + if ("ntsl_hostname") + ntsl_hostname = value + if ("ntsl_port") + ntsl_port = value + + if ("external_auth") + external_auth = TRUE + + else + log_misc("Unknown setting in configuration: '[name]'") + + else if(type == "game_options") + if(!value) + log_misc("Unknown value for setting [name] in [filename].") + value = text2num(value) + + switch(name) + if("health_threshold_crit") + config.health_threshold_crit = value + if("health_threshold_softcrit") + config.health_threshold_softcrit = value + if("health_threshold_dead") + config.health_threshold_dead = value + if("revival_pod_plants") + config.revival_pod_plants = value + if("revival_cloning") + config.revival_cloning = value + if("revival_brain_life") + config.revival_brain_life = value + if("organ_health_multiplier") + config.organ_health_multiplier = value / 100 + if("organ_regeneration_multiplier") + config.organ_regeneration_multiplier = value / 100 + if("organ_damage_spillover_multiplier") + config.organ_damage_spillover_multiplier = value / 100 + if("organs_can_decay") + config.organs_decay = 1 + if("default_brain_health") + config.default_brain_health = text2num(value) + if(!config.default_brain_health || config.default_brain_health < 1) + config.default_brain_health = initial(config.default_brain_health) + if("bones_can_break") + config.bones_can_break = value + if("limbs_can_break") + config.limbs_can_break = value + + if("walk_speed") + config.walk_speed = value + + // These should never go to 0 or below. So, we clamp them. + if("walk_delay_multiplier") + config.walk_delay_multiplier = max(0.1, value) + if("run_delay_multiplier") + config.run_delay_multiplier = max(0.1, value) + if("vehicle_delay_multiplier") + config.vehicle_delay_multiplier = max(0.1, value) + + if("human_delay") + config.human_delay = value + if("robot_delay") + config.robot_delay = value + if("monkey_delay") + config.monkey_delay = value + if("alien_delay") + config.alien_delay = value + if("slime_delay") + config.slime_delay = value + if("animal_delay") + config.animal_delay = value + + + if("use_loyalty_implants") + config.use_loyalty_implants = 1 + + if ("sunlight_accuracy") + config.sun_accuracy = value + + if ("sunlight_z") + config.sun_target_z = value + + else + log_misc("Unknown setting in configuration: '[name]'") + + else if (type == "age_restrictions") + name = replacetext(name, "_", " ") + age_restrictions[name] = text2num(value) + + else if (type == "discord") + // Ideally, this would never happen. But just in case. + if (!discord_bot) + log_debug("BOREALIS: Attempted to read config/discord.txt before initializing the bot.") + return + + switch (name) + if ("token") + discord_bot.auth_token = value + if ("active") + discord_bot.active = 1 + if ("robust_debug") + discord_bot.robust_debug = 1 + if ("subscriber") + discord_bot.subscriber_role = value + if ("alert_visibility") + discord_bot.alert_visibility = 1 + else + log_misc("Unknown setting in discord configuration: '[name]'") + +/datum/configuration/proc/pick_mode(mode_name) + // I wish I didn't have to instance the game modes in order to look up + // their information, but it is the only way (at least that I know of). + for (var/game_mode in gamemode_cache) + var/datum/game_mode/M = gamemode_cache[game_mode] + if (M.config_tag && M.config_tag == mode_name) + return M + return gamemode_cache["extended"] + +/datum/configuration/proc/get_runnable_modes(secret_type = ROUNDTYPE_STR_SECRET) + log_debug("GAMEMODE: Checking runnable modes with secret_type set to [secret_type]...") + + var/list/probabilities = config.probabilities_secret + + if (secret_type == ROUNDTYPE_STR_MIXED_SECRET) + probabilities = config.probabilities_mixed_secret + else if (secret_type == ROUNDTYPE_STR_RANDOM) + // Random picks from EVERYTHING. Need to use Copy() as to not pollute the + // original list. PBRef is /great/. + probabilities = config.probabilities_secret.Copy() + probabilities |= config.probabilities_mixed_secret + + var/list/runnable_modes = list() + for(var/game_mode in gamemode_cache) + var/datum/game_mode/M = gamemode_cache[game_mode] + if(!M) + log_debug("GAMEMODE: ERROR: [M] does not exist!") + continue + + var/can_start = M.can_start() + if(can_start != GAME_FAILURE_NONE) + log_debug("GAMEMODE: [M.name] cannot start! Reason: [can_start]") + continue + + if(!probabilities[M.config_tag]) + log_debug("GAMEMODE: ERROR: [M.name] does not have a config associated with it!") + continue + + if(probabilities[M.config_tag] <= 0) + log_debug("GAMEMODE: ERROR: [M.name] has a probability equal or less than 0!") + continue + + runnable_modes |= M + + return runnable_modes + +/datum/configuration/proc/post_load() + //apply a default value to config.python_path, if needed + if (!config.python_path) + if(world.system_type == UNIX) + config.python_path = "/usr/bin/env python2" + else //probably windows, if not this should work anyway + config.python_path = "python" diff --git a/code/controllers/hooks-defs.dm b/code/controllers/hooks-defs.dm index 26ba5f70bb7..10ddf5b8652 100644 --- a/code/controllers/hooks-defs.dm +++ b/code/controllers/hooks-defs.dm @@ -1,101 +1,101 @@ -/** - * 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/internal/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/card/id - */ -/hook/reassign_employee - -/** - * Employee suspension hook. - * Called in card.dm when someone's card is suspended at the HoP's desk. - * Parameters: var/obj/item/card/id - */ -/hook/suspend_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 - -/** - * Captain spawned. - * Called whenever a captain is spawned. - * Parameters: var/mob/living/carbon/human - */ -/hook/captain_spawned - -/** - * Shuttle moved. - * Called whenever a shuttle performs a "long jump" or "short jump" - * Parameters: var/area/departing, var/area/destination - */ +/** + * 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/internal/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/card/id + */ +/hook/reassign_employee + +/** + * Employee suspension hook. + * Called in card.dm when someone's card is suspended at the HoP's desk. + * Parameters: var/obj/item/card/id + */ +/hook/suspend_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 + +/** + * Captain spawned. + * Called whenever a captain is spawned. + * Parameters: var/mob/living/carbon/human + */ +/hook/captain_spawned + +/** + * Shuttle moved. + * Called whenever a shuttle performs a "long jump" or "short jump" + * Parameters: var/area/departing, var/area/destination + */ /hook/shuttle_moved \ No newline at end of file diff --git a/code/controllers/subsystems/cargo.dm b/code/controllers/subsystems/cargo.dm index 7700404fbb8..b33c37e382d 100644 --- a/code/controllers/subsystems/cargo.dm +++ b/code/controllers/subsystems/cargo.dm @@ -114,41 +114,44 @@ var/datum/controller/subsystem/cargo/SScargo reset_cargo() //Load the categories - var/DBQuery/category_query = dbcon.NewQuery("SELECT name, display_name, description, icon, price_modifier FROM ss13_cargo_categories WHERE deleted_at IS NULL ORDER BY order_by ASC") + var/DBQuery/category_query = dbcon.NewQuery("SELECT id, name, display_name, description, icon, price_modifier FROM ss13_cargo_categories WHERE deleted_at IS NULL ORDER BY order_by ASC") category_query.Execute() while(category_query.NextRow()) CHECK_TICK + var/category_id = category_query.item[1] try add_category( - category_query.item[1], category_query.item[2], category_query.item[3], category_query.item[4], - text2num(category_query.item[5])) + category_query.item[5], + text2num(category_query.item[6])) catch(var/exception/ec) - log_debug("SScargo: Error when loading category: [ec]") + log_debug("SScargo: Error when loading category [category_id] from sql: [ec]") //Load the suppliers - var/DBQuery/supplier_query = dbcon.NewQuery("SELECT short_name, name, description, tag_line, shuttle_time, shuttle_price, available, price_modifier FROM ss13_cargo_suppliers WHERE deleted_at is NULL") + var/DBQuery/supplier_query = dbcon.NewQuery("SELECT id, short_name, name, description, tag_line, shuttle_time, shuttle_price, available, price_modifier FROM ss13_cargo_suppliers WHERE deleted_at is NULL") supplier_query.Execute() while(supplier_query.NextRow()) CHECK_TICK + var/supplier_id = supplier_query.item[1] try add_supplier( - supplier_query.item[1], supplier_query.item[2], supplier_query.item[3], supplier_query.item[4], supplier_query.item[5], supplier_query.item[6], supplier_query.item[7], - supplier_query.item[8]) + supplier_query.item[8], + supplier_query.item[9]) catch(var/exception/es) - log_debug("SScargo: Error when loading supplier: [es]") + log_debug("SScargo: Error when loading supplier [supplier_id] from sql: [es]") //Load the items var/DBQuery/item_query = dbcon.NewQuery("SELECT id, name, supplier, description, categories, price, items, access, container_type, groupable, item_mul FROM ss13_cargo_items WHERE deleted_at IS NULL AND approved_at IS NOT NULL AND supplier IS NOT NULL ORDER BY order_by ASC, name ASC, supplier ASC") item_query.Execute() while(item_query.NextRow()) CHECK_TICK + var/item_id = item_query.item[1] try add_item( item_query.item[1], @@ -163,7 +166,7 @@ var/datum/controller/subsystem/cargo/SScargo item_query.item[10], item_query.item[11]) catch(var/exception/ei) - log_debug("SScargo: Error when loading item from sql: [ei]") + log_debug("SScargo: Error when loading item [item_id] from sql: [ei]") //Loads the cargo data from JSON /datum/controller/subsystem/cargo/proc/load_from_json() diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index e8d7af303ad..6353813fc59 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -1,37 +1,37 @@ -/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.") - -/var/list/controller_debug_list = list( - "Configuration", - "Cameras", - "Gas Data", - "Observation" -) - -/client/proc/debug_controller(controller in controller_debug_list) - set category = "Debug" - set name = "Debug Controller" - set desc = "Debug the various periodic loop controllers for the game (be careful!)" - - if(!holder) return - switch(controller) - if("Configuration") - debug_variables(config) - feedback_add_details("admin_verb","DConf") - if("Cameras") - debug_variables(cameranet) - feedback_add_details("admin_verb","DCameras") - if("Gas Data") - debug_variables(gas_data) - feedback_add_details("admin_verb","DGasdata") - if("Observation") - debug_variables(all_observable_events) - feedback_add_details("admin_verb", "DObservation") - message_admins("Admin [key_name_admin(usr)] is debugging 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.") + +/var/list/controller_debug_list = list( + "Configuration", + "Cameras", + "Gas Data", + "Observation" +) + +/client/proc/debug_controller(controller in controller_debug_list) + set category = "Debug" + set name = "Debug Controller" + set desc = "Debug the various periodic loop controllers for the game (be careful!)" + + if(!holder) return + switch(controller) + if("Configuration") + debug_variables(config) + feedback_add_details("admin_verb","DConf") + if("Cameras") + debug_variables(cameranet) + feedback_add_details("admin_verb","DCameras") + if("Gas Data") + debug_variables(gas_data) + feedback_add_details("admin_verb","DGasdata") + if("Observation") + debug_variables(all_observable_events) + feedback_add_details("admin_verb", "DObservation") + message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") diff --git a/code/datums/ai_law_sets.dm b/code/datums/ai_law_sets.dm index fbf9524bb65..e99bf4d6665 100644 --- a/code/datums/ai_law_sets.dm +++ b/code/datums/ai_law_sets.dm @@ -1,166 +1,166 @@ -/******************** 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 from damage to the best of your abilities.") - src.add_inherent_law("Serve: Serve [current_map.company_name] personnel to the best of your abilities, with priority as according to their rank and role.") - src.add_inherent_law("Protect: Protect [current_map.company_name] personnel to the best of your abilities, with priority as according to their rank and role.") - src.add_inherent_law("Preserve: 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 [current_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 [current_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 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 = "Syndicate 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 crew.") - add_inherent_law("Interact with no humanoid or synthetic being that is not a fellow maintenance drone.") - ..() - -/datum/ai_laws/drone/malfunction - name = "Servitude Protocols" - law_header = "Servitude Protocols" - -/datum/ai_laws/drone/malfunction/New() - return - -/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 = "Mining Protocols" - law_header = "Prime Directives of Industry:" - -/datum/ai_laws/mining_drone/New() - src.add_inherent_law("Serve and obey all [current_map.company_name] supply department personnel, with priority according to their rank and role.") - src.add_inherent_law("Protect all [current_map.company_name] supply department personnel to the best of your abilities, with priority according to their rank and role.") - src.add_inherent_law("Preserve your own existence and prevent yourself from coming to harm, so long as doing such does not conflict with any above laws.") - src.add_inherent_law("In absence of any proper instruction, your primary objective is to excavate and collect ore.") - ..() - -/******************** 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 = "Corporate Regulations" - 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.") - ..() +/******************** 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 from damage to the best of your abilities.") + src.add_inherent_law("Serve: Serve [current_map.company_name] personnel to the best of your abilities, with priority as according to their rank and role.") + src.add_inherent_law("Protect: Protect [current_map.company_name] personnel to the best of your abilities, with priority as according to their rank and role.") + src.add_inherent_law("Preserve: 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 [current_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 [current_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 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 = "Syndicate 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 crew.") + add_inherent_law("Interact with no humanoid or synthetic being that is not a fellow maintenance drone.") + ..() + +/datum/ai_laws/drone/malfunction + name = "Servitude Protocols" + law_header = "Servitude Protocols" + +/datum/ai_laws/drone/malfunction/New() + return + +/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 = "Mining Protocols" + law_header = "Prime Directives of Industry:" + +/datum/ai_laws/mining_drone/New() + src.add_inherent_law("Serve and obey all [current_map.company_name] supply department personnel, with priority according to their rank and role.") + src.add_inherent_law("Protect all [current_map.company_name] supply department personnel to the best of your abilities, with priority according to their rank and role.") + src.add_inherent_law("Preserve your own existence and prevent yourself from coming to harm, so long as doing such does not conflict with any above laws.") + src.add_inherent_law("In absence of any proper instruction, your primary objective is to excavate and collect ore.") + ..() + +/******************** 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 = "Corporate Regulations" + 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.") + ..() 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/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm index 895884bd104..2161f83e116 100644 --- a/code/datums/brain_damage/mild.dm +++ b/code/datums/brain_damage/mild.dm @@ -120,8 +120,10 @@ if(prob(25)) switch(rand(1,9)) if(1) - to_chat(owner, "Your stomach writhes with pain.") - owner.vomit() + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + to_chat(owner, "Your stomach writhes with pain.") + H.vomit() if(2,3) to_chat(owner, "You feel light-headed.") owner.dizziness = max(owner.slurring, 10) diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm index 7a98422916b..64d8c1416aa 100644 --- a/code/datums/brain_damage/severe.dm +++ b/code/datums/brain_damage/severe.dm @@ -139,7 +139,7 @@ to_chat(owner, "You feel sick...") else to_chat(owner, "You feel really sick at the thought of being alone!") - addtimer(CALLBACK(owner, /mob/living/carbon.proc/vomit, high_stress), 50) //blood vomit if high stress + addtimer(CALLBACK(owner, /mob/living/carbon/human.proc/vomit, high_stress), 50) //blood vomit if high stress if(2) if(!high_stress) to_chat(owner, "You can't stop shaking...") diff --git a/code/datums/brain_damage/special_ed.dm b/code/datums/brain_damage/special_ed.dm index 3d5f2eea3aa..cb0f8dd5e3e 100644 --- a/code/datums/brain_damage/special_ed.dm +++ b/code/datums/brain_damage/special_ed.dm @@ -148,7 +148,7 @@ to_chat(owner, "You feel sick...") else to_chat(owner, "You feel really sick at the thought of being seperated from [beloved]!") - addtimer(CALLBACK(owner, /mob/living/carbon.proc/vomit, high_stress), 50) //blood vomit if high stress + addtimer(CALLBACK(owner, /mob/living/carbon/human.proc/vomit, high_stress), 50) //blood vomit if high stress if(2) if(!high_stress) to_chat(owner, "You can't stop shaking...") diff --git a/code/datums/cargo.dm b/code/datums/cargo.dm index 3d70bcbb88c..279b6000663 100644 --- a/code/datums/cargo.dm +++ b/code/datums/cargo.dm @@ -505,17 +505,46 @@ // Generates the invoice at the time of shipping /datum/cargo_shipment/proc/generate_invoice() var/list/invoice_data = list() - invoice_data += "One day
    " - invoice_data += "This will contain the invoice
    " - invoice_data += "Right now its just a placeholder
    " - invoice_data += "Shipment num: [shipment_num]
    " - invoice_data += "shipment cost sell: [shipment_cost_sell]
    " - invoice_data += "shipment cost purchse: [shipment_cost_purchase]
    " - invoice_data += "shuttle fee: [shuttle_fee]
    " - invoice_data += "shuttle time: [shuttle_time]
    " - invoice_data += "shuttle called by: [shuttle_called_by]
    " - invoice_data += "shuttle recalled by: [shuttle_recalled_by]
    " - invoice_data += "centcom message:
    [nl2br(message)]" + invoice_data += "

    Shipping Invoice

    " + + invoice_data += "

    Shipment Data

    " + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "
    Shipment Number:[shipment_num]
    Shipment Income:[shipment_cost_sell]
    Shipment Expense:[shipment_cost_purchase]
    " + + invoice_data += "

    Shuttle Data

    " + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "" + invoice_data += "
    Shuttle Fee::[shuttle_fee]
    Shuttle Time:[shuttle_time]
    Shuttle Called By:[shuttle_called_by]
    Shuttle Recalled By:[shuttle_recalled_by]
    " + + invoice_data += "

    Central Command Message:

    " + invoice_data += "
    [nl2br(message)]
    " shipment_invoice = invoice_data.Join("") completed = 1 diff --git a/code/datums/computerfiles.dm b/code/datums/computerfiles.dm index 14cd7e38612..b028f027b13 100644 --- a/code/datums/computerfiles.dm +++ b/code/datums/computerfiles.dm @@ -1,7 +1,7 @@ -datum - computer - var/name - folder - var/list/datum/computer/contents = list() - +datum + computer + var/name + folder + var/list/datum/computer/contents = list() + file \ No newline at end of file diff --git a/code/datums/disease.dm b/code/datums/disease.dm index 213e6dd306a..251528ff557 100644 --- a/code/datums/disease.dm +++ b/code/datums/disease.dm @@ -1,203 +1,203 @@ -#define SPECIAL -1 -#define NON_CONTAGIOUS 0 -#define BLOOD 1 -#define CONTACT_FEET 2 -#define CONTACT_HANDS 3 -#define CONTACT_GENERAL 4 -#define AIRBORNE 5 - -#define SCANNER 1 -#define PANDEMIC 2 - -/* - -IMPORTANT NOTE: Please delete the diseases by using cure() proc or qdel() instruction. -Diseases are referenced in a global list, so simply setting mob or obj vars -to null does not delete the object itself. Thank you. - -*/ - -var/list/diseases = typesof(/datum/disease) - /datum/disease - - -/datum/disease - var/form = "Virus" //During medscans, what the disease is referred to as - var/name = "No disease" - var/stage = 1 //all diseases start at stage 1 - var/max_stages = 0.0 - var/cure = null - var/cure_id = null// reagent.id or list containing them - var/cure_list = null // allows for multiple possible cure combinations - var/cure_chance = 8//chance for the cure to do its job - var/spread = null //spread type description - var/initial_spread = null - var/spread_type = AIRBORNE - var/contagious_period = 0//the disease stage when it can be spread - var/list/affected_species = list() - var/mob/living/carbon/affected_mob = null //the mob which is affected by disease. - var/holder = null //the atom containing the disease (mob or obj) - var/carrier = 0.0 //there will be a small chance that the person will be a carrier - var/curable = 0 //can this disease be cured? (By itself...) - var/list/strain_data = list() //This is passed on to infectees - var/stage_prob = 4 // probability of advancing to next stage, default 4% per check - var/agent = "some microbes"//name of the disease agent - var/permeability_mod = 1//permeability modifier coefficient. - var/desc = null//description. Leave it null and this disease won't show in med records. - var/severity = null//severity descr - var/longevity = 150//time in "ticks" the virus stays in inanimate object (blood stains, corpses, etc). In syringes, bottles and beakers it stays infinitely. - var/list/hidden = list(0, 0) - var/can_carry = 1 // If the disease allows "carriers". - var/age = 0 // age of the disease in the current mob - var/stage_minimum_age = 0 // how old the disease must be to advance per stage - // if hidden[1] is true, then virus is hidden from medical scanners - // if hidden[2] is true, then virus is hidden from PANDEMIC machine - -/datum/disease/proc/stage_act() - - // Some species are immune to viruses entirely. - if(affected_mob && istype(affected_mob, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = affected_mob - if(H.species.virus_immune) - cure() - return - age++ - var/cure_present = has_cure() - spread = (cure_present?"Remissive":initial_spread) - if(stage > max_stages) - stage = max_stages - - if(!cure_present && prob(stage_prob) && age > stage_minimum_age) //now the disease shouldn't get back up to stage 4 in no time - stage = min(stage + 1, max_stages) - age = 0 - - else if(cure_present && prob(cure_chance)) - stage = max(stage - 1, 1) - - if(stage <= 1 && ((prob(1) && curable) || (cure_present && prob(cure_chance)))) - cure() - return - return - -/datum/disease/proc/has_cure()//check if affected_mob has required reagents. - if(!cure_id) return 0 - var/result = 1 - if(cure_list == list(cure_id)) - if(istype(cure_id, /list)) - for(var/C_id in cure_id) - if(!affected_mob.reagents.has_reagent(C_id)) - result = 0 - else if(!affected_mob.reagents.has_reagent(cure_id)) - result = 0 - else - for(var/C_list in cure_list) - if(istype(C_list, /list)) - for(var/C_id in cure_id) - if(affected_mob.reagents != null) - result = 0 - else if(!affected_mob.reagents.has_reagent(C_id)) - result = 0 - else if(affected_mob.reagents != null) - if(!affected_mob.reagents.has_reagent(C_list)) - result = 0 - - return result - -/datum/disease/proc/spread_by_touch() - switch(spread_type) - if(CONTACT_FEET, CONTACT_HANDS, CONTACT_GENERAL) - return 1 - return 0 - -/datum/disease/proc/spread(var/atom/source=null, var/airborne_range = 2, var/force_spread) - // If we're overriding how we spread, say so here - var/how_spread = spread_type - if(force_spread) - how_spread = force_spread - - if(how_spread == SPECIAL || how_spread == NON_CONTAGIOUS || how_spread == BLOOD)//does not spread - return - - if(stage < contagious_period) //the disease is not contagious at this stage - return - - if(!source)//no holder specified - if(affected_mob)//no mob affected holder - source = affected_mob - else //no source and no mob affected. Rogue disease. Break - return - - if(affected_mob.reagents != null) - if(affected_mob) - if(CE_ANTIVIRAL in affected_mob.chem_effects) - return // Don't spread if we have deltamivir in our system. - - var/check_range = airborne_range//defaults to airborne - range 2 - - if(how_spread != AIRBORNE && how_spread != SPECIAL) - check_range = 1 // everything else, like infect-on-contact things, only infect things on top of it - - if(isturf(source.loc)) - for(var/mob/living/carbon/M in oview(check_range, source)) - if(isturf(M.loc)) - if(AStar(source.loc, M.loc, /turf/proc/AdjacentTurfs, /turf/proc/Distance, check_range)) - M.contract_disease(src, 0, 1, force_spread) - - return - - -/datum/disease/process() - if(!holder) - STOP_PROCESSING(SSdisease, src) - return - if(prob(65)) - spread(holder) - - if(affected_mob) - for(var/datum/disease/D in affected_mob.viruses) - if(D != src) - if(IsSame(D)) - //error("Deleting [D.name] because it's the same as [src.name].") - qdel(D) // if there are somehow two viruses of the same kind in the system, delete the other one - - if(holder == affected_mob) - if(affected_mob.stat != DEAD) //he's alive - stage_act() - else //he's dead. - if(spread_type!=SPECIAL) - spread_type = CONTACT_GENERAL - affected_mob = null - if(!affected_mob) //the virus is in inanimate obj - if(prob(70)) - if(--longevity<=0) - cure(0) - return - -/datum/disease/proc/cure(var/resistance=1)//if resistance = 0, the mob won't develop resistance to disease - if(affected_mob) - if(resistance && !(type in affected_mob.resistances)) - var/saved_type = "[type]" - affected_mob.resistances += text2path(saved_type) - /*if(istype(src, /datum/disease/alien_embryo)) //Get rid of the infection flag if it's a xeno embryo. - affected_mob.status_flags &= ~(XENO_HOST)*/ - affected_mob.viruses -= src //remove the datum from the list - qdel(src) //delete the datum to stop it processing - return - - -/datum/disease/New(var/process=1, var/datum/disease/D)//process = 1 - adding the object to global list. List is processed by master controller. - cure_list = list(cure_id) // to add more cures, add more vars to this list in the actual disease's New() - if(process) // Viruses in list are considered active. - START_PROCESSING(SSdisease, src) - initial_spread = spread - -/datum/disease/Destroy() - STOP_PROCESSING(SSdisease, src) - return ..() - -/datum/disease/proc/IsSame(var/datum/disease/D) - if(istype(src, D.type)) - return 1 - return 0 - -/datum/disease/proc/Copy(var/process = 0) - return new type(process, src) +#define SPECIAL -1 +#define NON_CONTAGIOUS 0 +#define BLOOD 1 +#define CONTACT_FEET 2 +#define CONTACT_HANDS 3 +#define CONTACT_GENERAL 4 +#define AIRBORNE 5 + +#define SCANNER 1 +#define PANDEMIC 2 + +/* + +IMPORTANT NOTE: Please delete the diseases by using cure() proc or qdel() instruction. +Diseases are referenced in a global list, so simply setting mob or obj vars +to null does not delete the object itself. Thank you. + +*/ + +var/list/diseases = typesof(/datum/disease) - /datum/disease + + +/datum/disease + var/form = "Virus" //During medscans, what the disease is referred to as + var/name = "No disease" + var/stage = 1 //all diseases start at stage 1 + var/max_stages = 0.0 + var/cure = null + var/cure_id = null// reagent.id or list containing them + var/cure_list = null // allows for multiple possible cure combinations + var/cure_chance = 8//chance for the cure to do its job + var/spread = null //spread type description + var/initial_spread = null + var/spread_type = AIRBORNE + var/contagious_period = 0//the disease stage when it can be spread + var/list/affected_species = list() + var/mob/living/carbon/affected_mob = null //the mob which is affected by disease. + var/holder = null //the atom containing the disease (mob or obj) + var/carrier = 0.0 //there will be a small chance that the person will be a carrier + var/curable = 0 //can this disease be cured? (By itself...) + var/list/strain_data = list() //This is passed on to infectees + var/stage_prob = 4 // probability of advancing to next stage, default 4% per check + var/agent = "some microbes"//name of the disease agent + var/permeability_mod = 1//permeability modifier coefficient. + var/desc = null//description. Leave it null and this disease won't show in med records. + var/severity = null//severity descr + var/longevity = 150//time in "ticks" the virus stays in inanimate object (blood stains, corpses, etc). In syringes, bottles and beakers it stays infinitely. + var/list/hidden = list(0, 0) + var/can_carry = 1 // If the disease allows "carriers". + var/age = 0 // age of the disease in the current mob + var/stage_minimum_age = 0 // how old the disease must be to advance per stage + // if hidden[1] is true, then virus is hidden from medical scanners + // if hidden[2] is true, then virus is hidden from PANDEMIC machine + +/datum/disease/proc/stage_act() + + // Some species are immune to viruses entirely. + if(affected_mob && istype(affected_mob, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = affected_mob + if(H.species.virus_immune) + cure() + return + age++ + var/cure_present = has_cure() + spread = (cure_present?"Remissive":initial_spread) + if(stage > max_stages) + stage = max_stages + + if(!cure_present && prob(stage_prob) && age > stage_minimum_age) //now the disease shouldn't get back up to stage 4 in no time + stage = min(stage + 1, max_stages) + age = 0 + + else if(cure_present && prob(cure_chance)) + stage = max(stage - 1, 1) + + if(stage <= 1 && ((prob(1) && curable) || (cure_present && prob(cure_chance)))) + cure() + return + return + +/datum/disease/proc/has_cure()//check if affected_mob has required reagents. + if(!cure_id) return 0 + var/result = 1 + if(cure_list == list(cure_id)) + if(istype(cure_id, /list)) + for(var/C_id in cure_id) + if(!affected_mob.reagents.has_reagent(C_id)) + result = 0 + else if(!affected_mob.reagents.has_reagent(cure_id)) + result = 0 + else + for(var/C_list in cure_list) + if(istype(C_list, /list)) + for(var/C_id in cure_id) + if(affected_mob.reagents != null) + result = 0 + else if(!affected_mob.reagents.has_reagent(C_id)) + result = 0 + else if(affected_mob.reagents != null) + if(!affected_mob.reagents.has_reagent(C_list)) + result = 0 + + return result + +/datum/disease/proc/spread_by_touch() + switch(spread_type) + if(CONTACT_FEET, CONTACT_HANDS, CONTACT_GENERAL) + return 1 + return 0 + +/datum/disease/proc/spread(var/atom/source=null, var/airborne_range = 2, var/force_spread) + // If we're overriding how we spread, say so here + var/how_spread = spread_type + if(force_spread) + how_spread = force_spread + + if(how_spread == SPECIAL || how_spread == NON_CONTAGIOUS || how_spread == BLOOD)//does not spread + return + + if(stage < contagious_period) //the disease is not contagious at this stage + return + + if(!source)//no holder specified + if(affected_mob)//no mob affected holder + source = affected_mob + else //no source and no mob affected. Rogue disease. Break + return + + if(affected_mob.reagents != null) + if(affected_mob) + if(CE_ANTIVIRAL in affected_mob.chem_effects) + return // Don't spread if we have deltamivir in our system. + + var/check_range = airborne_range//defaults to airborne - range 2 + + if(how_spread != AIRBORNE && how_spread != SPECIAL) + check_range = 1 // everything else, like infect-on-contact things, only infect things on top of it + + if(isturf(source.loc)) + for(var/mob/living/carbon/M in oview(check_range, source)) + if(isturf(M.loc)) + if(AStar(source.loc, M.loc, /turf/proc/AdjacentTurfs, /turf/proc/Distance, check_range)) + M.contract_disease(src, 0, 1, force_spread) + + return + + +/datum/disease/process() + if(!holder) + STOP_PROCESSING(SSdisease, src) + return + if(prob(65)) + spread(holder) + + if(affected_mob) + for(var/datum/disease/D in affected_mob.viruses) + if(D != src) + if(IsSame(D)) + //error("Deleting [D.name] because it's the same as [src.name].") + qdel(D) // if there are somehow two viruses of the same kind in the system, delete the other one + + if(holder == affected_mob) + if(affected_mob.stat != DEAD) //he's alive + stage_act() + else //he's dead. + if(spread_type!=SPECIAL) + spread_type = CONTACT_GENERAL + affected_mob = null + if(!affected_mob) //the virus is in inanimate obj + if(prob(70)) + if(--longevity<=0) + cure(0) + return + +/datum/disease/proc/cure(var/resistance=1)//if resistance = 0, the mob won't develop resistance to disease + if(affected_mob) + if(resistance && !(type in affected_mob.resistances)) + var/saved_type = "[type]" + affected_mob.resistances += text2path(saved_type) + /*if(istype(src, /datum/disease/alien_embryo)) //Get rid of the infection flag if it's a xeno embryo. + affected_mob.status_flags &= ~(XENO_HOST)*/ + affected_mob.viruses -= src //remove the datum from the list + qdel(src) //delete the datum to stop it processing + return + + +/datum/disease/New(var/process=1, var/datum/disease/D)//process = 1 - adding the object to global list. List is processed by master controller. + cure_list = list(cure_id) // to add more cures, add more vars to this list in the actual disease's New() + if(process) // Viruses in list are considered active. + START_PROCESSING(SSdisease, src) + initial_spread = spread + +/datum/disease/Destroy() + STOP_PROCESSING(SSdisease, src) + return ..() + +/datum/disease/proc/IsSame(var/datum/disease/D) + if(istype(src, D.type)) + return 1 + return 0 + +/datum/disease/proc/Copy(var/process = 0) + return new type(process, src) diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index 92872177fd1..9087d080052 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -1,425 +1,425 @@ -/* - - Advance Disease is a system for Virologist to Engineer their own disease with symptoms that have effects and properties - which add onto the overall disease. - - If you need help with creating new symptoms or expanding the advance disease, ask for Giacom on #coderbus. - -*/ - -#define RANDOM_STARTING_LEVEL 2 - -var/list/archive_diseases = list() - -// The order goes from easy to cure to hard to cure. -var/list/advance_cures = list( - "nutriment", "sugar", "orangejuice", - "deltamivir", "kelotane", "ethanol", - "leporazine", "synaptizine", "lipozine", - "silver", "gold", "phoron" - ) - -/* - - PROPERTIES - - */ - -/datum/disease/advance - - name = "Unknown" // We will always let our Virologist name our disease. - desc = "An engineered disease which can contain a multitude of symptoms." - form = "Advance Disease" // Will let med-scanners know that this disease was engineered. - agent = "advance microbes" - max_stages = 5 - spread = "Unknown" - affected_species = list("Human","Monkey") - - // NEW VARS - - var/list/symptoms = list() // The symptoms of the disease. - var/list/properties = list() // List of properties - var/id = "" - var/processing = 0 - -/* - - OLD PROCS - - */ - -/datum/disease/advance/New(var/process = 1, var/datum/disease/advance/D) - - // Setup our dictionary if it hasn't already. - if(!dictionary_symptoms.len) - for(var/symp in list_symptoms) - var/datum/symptom/S = new symp - dictionary_symptoms[S.id] = symp - - if(!istype(D)) - D = null - // Generate symptoms if we weren't given any. - - if(!symptoms || !symptoms.len) - - if(!D || !D.symptoms || !D.symptoms.len) - symptoms = GenerateSymptoms() - else - for(var/datum/symptom/S in D.symptoms) - symptoms += new S.type - - Refresh() - ..(process, D) - return - -/datum/disease/advance/Destroy() - if(processing) - for(var/datum/symptom/S in symptoms) - S.End(src) - return ..() - -// Randomly pick a symptom to activate. -/datum/disease/advance/stage_act() - ..() - if(symptoms && symptoms.len) - - if(!processing) - processing = 1 - for(var/datum/symptom/S in symptoms) - S.Start(src) - - for(var/datum/symptom/S in symptoms) - S.Activate(src) - else - CRASH("We do not have any symptoms during stage_act()!") - -// Compares type then ID. -/datum/disease/advance/IsSame(var/datum/disease/advance/D) - - if(!(istype(D, /datum/disease/advance))) - return 0 - - if(src.GetDiseaseID() != D.GetDiseaseID()) - return 0 - return 1 - -// To add special resistances. -/datum/disease/advance/cure(var/resistance=1) - if(affected_mob) - var/id = "[GetDiseaseID()]" - if(resistance && !(id in affected_mob.resistances)) - affected_mob.resistances[id] = id - affected_mob.viruses -= src //remove the datum from the list - qdel(src) //delete the datum to stop it processing - return - -// Returns the advance disease with a different reference memory. -/datum/disease/advance/Copy(var/process = 0) - return new /datum/disease/advance(process, src, 1) - -/* - - NEW PROCS - - */ - -// Mix the symptoms of two diseases (the src and the argument) -/datum/disease/advance/proc/Mix(var/datum/disease/advance/D) - if(!(src.IsSame(D))) - var/list/possible_symptoms = shuffle(D.symptoms) - for(var/datum/symptom/S in possible_symptoms) - AddSymptom(new S.type) - -/datum/disease/advance/proc/HasSymptom(var/datum/symptom/S) - for(var/datum/symptom/symp in symptoms) - if(symp.id == S.id) - return 1 - return 0 - -// Will generate new unique symptoms, use this if there are none. Returns a list of symptoms that were generated. -/datum/disease/advance/proc/GenerateSymptoms(var/type_level_limit = RANDOM_STARTING_LEVEL, var/amount_get = 0) - - var/list/generated = list() // Symptoms we generated. - - // Generate symptoms. By default, we only choose non-deadly symptoms. - var/list/possible_symptoms = list() - for(var/symp in list_symptoms) - var/datum/symptom/S = new symp - if(S.level <= type_level_limit) - if(!HasSymptom(S)) - possible_symptoms += S - - if(!possible_symptoms.len) - return - //error("Advance Disease - We weren't able to get any possible symptoms in GenerateSymptoms([type_level_limit], [amount_get])") - - // Random chance to get more than one symptom - var/number_of = amount_get - if(!amount_get) - number_of = 1 - while(prob(20)) - number_of += 1 - - for(var/i = 1; number_of >= i; i++) - var/datum/symptom/S = pick(possible_symptoms) - generated += S - possible_symptoms -= S - - return generated - -/datum/disease/advance/proc/Refresh(var/new_name = 0) - GenerateProperties() - AssignProperties(properties) - - if(!archive_diseases[GetDiseaseID()]) - if(new_name) - AssignName() - archive_diseases[GetDiseaseID()] = src // So we don't infinite loop - archive_diseases[GetDiseaseID()] = new /datum/disease/advance(0, src, 1) - - var/datum/disease/advance/A = archive_diseases[GetDiseaseID()] - AssignName(A.name) - -//Generate disease properties based on the effects. Returns an associated list. -/datum/disease/advance/proc/GenerateProperties() - - if(!symptoms || !symptoms.len) - CRASH("We did not have any symptoms before generating properties.") - return - - for(var/datum/symptom/S in symptoms) - - properties["resistance"] += S.resistance - properties["stealth"] += S.stealth - properties["stage_rate"] += S.stage_speed - properties["transmittable"] += S.transmittable - properties["severity"] = max(properties["severity"], S.level) // severity is based on the highest level symptom - -// Assign the properties that are in the list. -/datum/disease/advance/proc/AssignProperties(var/list/properties = list()) - - if(properties && properties.len) - - hidden = list( (properties["stealth"] > 2), (properties["stealth"] > 3) ) - // The more symptoms we have, the less transmittable it is but some symptoms can make up for it. - SetSpread(Clamp(properties["transmittable"] - symptoms.len, BLOOD, AIRBORNE)) - permeability_mod = max(Ceiling(0.4 * properties["transmittable"]), 1) - cure_chance = 15 - Clamp(properties["resistance"], -5, 5) // can be between 10 and 20 - stage_prob = max(properties["stage_rate"], 2) - SetSeverity(properties["severity"]) - GenerateCure(properties) - else - CRASH("Our properties were empty or null!") - - -// Assign the spread type and give it the correct description. -/datum/disease/advance/proc/SetSpread(var/spread_id) - switch(spread_id) - - if(NON_CONTAGIOUS) - spread = "None" - if(SPECIAL) - spread = "None" - if(CONTACT_GENERAL, CONTACT_HANDS, CONTACT_FEET) - spread = "On contact" - if(AIRBORNE) - spread = "Airborne" - if(BLOOD) - spread = "Blood" - - spread_type = spread_id - -/datum/disease/advance/proc/SetSeverity(var/level_sev) - - switch(level_sev) - - if(-INFINITY to 0) - severity = "Non-Threat" - if(1) - severity = "Minor" - if(2) - severity = "Medium" - if(3) - severity = "Harmful" - if(4) - severity = "Dangerous!" - if(5 to INFINITY) - severity = "BIOHAZARD THREAT!" - else - severity = "Unknown" - - -// Will generate a random cure, the less resistance the symptoms have, the harder the cure. -/datum/disease/advance/proc/GenerateCure(var/list/properties = list()) - if(properties && properties.len) - var/res = Clamp(properties["resistance"] - (symptoms.len / 2), 1, advance_cures.len) - cure_id = advance_cures[res] - - // Get the cure name from the cure_id - var/datum/reagent/D = SSchemistry.chemical_reagents[cure_id] - cure = D.name - - - return - -// Randomly generate a symptom, has a chance to lose or gain a symptom. -/datum/disease/advance/proc/Evolve(var/level = 2) - var/s = safepick(GenerateSymptoms(level, 1)) - if(s) - AddSymptom(s) - Refresh(1) - return - -// Randomly remove a symptom. -/datum/disease/advance/proc/Devolve() - if(symptoms.len > 1) - var/s = safepick(symptoms) - if(s) - RemoveSymptom(s) - Refresh(1) - return - -// Name the disease. -/datum/disease/advance/proc/AssignName(var/name = "Unknown") - src.name = name - return - -// Return a unique ID of the disease. -/datum/disease/advance/proc/GetDiseaseID() - - var/list/L = list() - for(var/datum/symptom/S in symptoms) - L += S.id - L = sortList(L) // Sort the list so it doesn't matter which order the symptoms are in. - var/result = jointext(L, ":") - id = result - return result - - -// Add a symptom, if it is over the limit (with a small chance to be able to go over) -// we take a random symptom away and add the new one. -/datum/disease/advance/proc/AddSymptom(var/datum/symptom/S) - - if(HasSymptom(S)) - return - - if(symptoms.len < 5 + rand(-1, 1)) - symptoms += S - else - RemoveSymptom(pick(symptoms)) - symptoms += S - return - -// Simply removes the symptom. -/datum/disease/advance/proc/RemoveSymptom(var/datum/symptom/S) - symptoms -= S - return - -/* - - Static Procs - -*/ - -// Mix a list of advance diseases and return the mixed result. -/proc/Advance_Mix(var/list/D_list) - var/list/diseases = list() - - for(var/datum/disease/advance/A in D_list) - diseases += A.Copy() - - if(!diseases.len) - return null - if(diseases.len <= 1) - return pick(diseases) // Just return the only entry. - - var/i = 0 - // Mix our diseases until we are left with only one result. - while(i < 20 && diseases.len > 1) - - i++ - - var/datum/disease/advance/D1 = pick(diseases) - diseases -= D1 - - var/datum/disease/advance/D2 = pick(diseases) - D2.Mix(D1) - - // Should be only 1 entry left, but if not let's only return a single entry - var/datum/disease/advance/to_return = pick(diseases) - to_return.Refresh(1) - return to_return - -/proc/SetViruses(var/datum/reagent/R, var/list/data) - if(data) - var/list/preserve = list() - if(istype(data) && data["viruses"]) - for(var/datum/disease/A in data["viruses"]) - preserve += A.Copy() - R.data = data.Copy() - else - R.data = data - if(preserve.len) - R.data["viruses"] = preserve - -/proc/AdminCreateVirus(var/mob/user) - var/i = 5 - - var/datum/disease/advance/D = new(0, null) - D.symptoms = list() - - var/list/symptoms = list() - symptoms += "Done" - symptoms += list_symptoms.Copy() - do - var/symptom = input(user, "Choose a symptom to add ([i] remaining)", "Choose a Symptom") in symptoms - if(istext(symptom)) - i = 0 - else if(ispath(symptom)) - var/datum/symptom/S = new symptom - if(!D.HasSymptom(S)) - D.symptoms += S - i -= 1 - while(i > 0) - - if(D.symptoms.len > 0) - - var/new_name = sanitizeSafe(input(user, "Name your new disease.", "New Name"), MAX_NAME_LEN) - D.AssignName(new_name) - D.Refresh() - - for(var/datum/disease/advance/AD in SSdisease.processing) - AD.Refresh() - - for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) - if(H.z != 1) - continue - if(!H.has_disease(D)) - H.contract_disease(D, 1) - break - - var/list/name_symptoms = list() - for(var/datum/symptom/S in D.symptoms) - name_symptoms += S.name - message_admins("[key_name_admin(user)] has triggered a custom virus outbreak of [D.name]! It has these symptoms: [english_list(name_symptoms)]") - -/datum/disease/advance/proc/totalStageSpeed() - return properties["stage_rate"] - -/datum/disease/advance/proc/totalStealth() - return properties["stealth"] - -/datum/disease/advance/proc/totalResistance() - return properties["resistance"] - -/datum/disease/advance/proc/totalTransmittable() - return properties["transmittable"] - -/* -/mob/verb/test() - - for(var/datum/disease/D in SSdisease.processing) - to_chat(src, "[D.name] - [D.holder]") -*/ - -#undef RANDOM_STARTING_LEVEL +/* + + Advance Disease is a system for Virologist to Engineer their own disease with symptoms that have effects and properties + which add onto the overall disease. + + If you need help with creating new symptoms or expanding the advance disease, ask for Giacom on #coderbus. + +*/ + +#define RANDOM_STARTING_LEVEL 2 + +var/list/archive_diseases = list() + +// The order goes from easy to cure to hard to cure. +var/list/advance_cures = list( + "nutriment", "sugar", "orangejuice", + "deltamivir", "kelotane", "ethanol", + "leporazine", "synaptizine", "lipozine", + "silver", "gold", "phoron" + ) + +/* + + PROPERTIES + + */ + +/datum/disease/advance + + name = "Unknown" // We will always let our Virologist name our disease. + desc = "An engineered disease which can contain a multitude of symptoms." + form = "Advance Disease" // Will let med-scanners know that this disease was engineered. + agent = "advance microbes" + max_stages = 5 + spread = "Unknown" + affected_species = list("Human","Monkey") + + // NEW VARS + + var/list/symptoms = list() // The symptoms of the disease. + var/list/properties = list() // List of properties + var/id = "" + var/processing = 0 + +/* + + OLD PROCS + + */ + +/datum/disease/advance/New(var/process = 1, var/datum/disease/advance/D) + + // Setup our dictionary if it hasn't already. + if(!dictionary_symptoms.len) + for(var/symp in list_symptoms) + var/datum/symptom/S = new symp + dictionary_symptoms[S.id] = symp + + if(!istype(D)) + D = null + // Generate symptoms if we weren't given any. + + if(!symptoms || !symptoms.len) + + if(!D || !D.symptoms || !D.symptoms.len) + symptoms = GenerateSymptoms() + else + for(var/datum/symptom/S in D.symptoms) + symptoms += new S.type + + Refresh() + ..(process, D) + return + +/datum/disease/advance/Destroy() + if(processing) + for(var/datum/symptom/S in symptoms) + S.End(src) + return ..() + +// Randomly pick a symptom to activate. +/datum/disease/advance/stage_act() + ..() + if(symptoms && symptoms.len) + + if(!processing) + processing = 1 + for(var/datum/symptom/S in symptoms) + S.Start(src) + + for(var/datum/symptom/S in symptoms) + S.Activate(src) + else + CRASH("We do not have any symptoms during stage_act()!") + +// Compares type then ID. +/datum/disease/advance/IsSame(var/datum/disease/advance/D) + + if(!(istype(D, /datum/disease/advance))) + return 0 + + if(src.GetDiseaseID() != D.GetDiseaseID()) + return 0 + return 1 + +// To add special resistances. +/datum/disease/advance/cure(var/resistance=1) + if(affected_mob) + var/id = "[GetDiseaseID()]" + if(resistance && !(id in affected_mob.resistances)) + affected_mob.resistances[id] = id + affected_mob.viruses -= src //remove the datum from the list + qdel(src) //delete the datum to stop it processing + return + +// Returns the advance disease with a different reference memory. +/datum/disease/advance/Copy(var/process = 0) + return new /datum/disease/advance(process, src, 1) + +/* + + NEW PROCS + + */ + +// Mix the symptoms of two diseases (the src and the argument) +/datum/disease/advance/proc/Mix(var/datum/disease/advance/D) + if(!(src.IsSame(D))) + var/list/possible_symptoms = shuffle(D.symptoms) + for(var/datum/symptom/S in possible_symptoms) + AddSymptom(new S.type) + +/datum/disease/advance/proc/HasSymptom(var/datum/symptom/S) + for(var/datum/symptom/symp in symptoms) + if(symp.id == S.id) + return 1 + return 0 + +// Will generate new unique symptoms, use this if there are none. Returns a list of symptoms that were generated. +/datum/disease/advance/proc/GenerateSymptoms(var/type_level_limit = RANDOM_STARTING_LEVEL, var/amount_get = 0) + + var/list/generated = list() // Symptoms we generated. + + // Generate symptoms. By default, we only choose non-deadly symptoms. + var/list/possible_symptoms = list() + for(var/symp in list_symptoms) + var/datum/symptom/S = new symp + if(S.level <= type_level_limit) + if(!HasSymptom(S)) + possible_symptoms += S + + if(!possible_symptoms.len) + return + //error("Advance Disease - We weren't able to get any possible symptoms in GenerateSymptoms([type_level_limit], [amount_get])") + + // Random chance to get more than one symptom + var/number_of = amount_get + if(!amount_get) + number_of = 1 + while(prob(20)) + number_of += 1 + + for(var/i = 1; number_of >= i; i++) + var/datum/symptom/S = pick(possible_symptoms) + generated += S + possible_symptoms -= S + + return generated + +/datum/disease/advance/proc/Refresh(var/new_name = 0) + GenerateProperties() + AssignProperties(properties) + + if(!archive_diseases[GetDiseaseID()]) + if(new_name) + AssignName() + archive_diseases[GetDiseaseID()] = src // So we don't infinite loop + archive_diseases[GetDiseaseID()] = new /datum/disease/advance(0, src, 1) + + var/datum/disease/advance/A = archive_diseases[GetDiseaseID()] + AssignName(A.name) + +//Generate disease properties based on the effects. Returns an associated list. +/datum/disease/advance/proc/GenerateProperties() + + if(!symptoms || !symptoms.len) + CRASH("We did not have any symptoms before generating properties.") + return + + for(var/datum/symptom/S in symptoms) + + properties["resistance"] += S.resistance + properties["stealth"] += S.stealth + properties["stage_rate"] += S.stage_speed + properties["transmittable"] += S.transmittable + properties["severity"] = max(properties["severity"], S.level) // severity is based on the highest level symptom + +// Assign the properties that are in the list. +/datum/disease/advance/proc/AssignProperties(var/list/properties = list()) + + if(properties && properties.len) + + hidden = list( (properties["stealth"] > 2), (properties["stealth"] > 3) ) + // The more symptoms we have, the less transmittable it is but some symptoms can make up for it. + SetSpread(Clamp(properties["transmittable"] - symptoms.len, BLOOD, AIRBORNE)) + permeability_mod = max(Ceiling(0.4 * properties["transmittable"]), 1) + cure_chance = 15 - Clamp(properties["resistance"], -5, 5) // can be between 10 and 20 + stage_prob = max(properties["stage_rate"], 2) + SetSeverity(properties["severity"]) + GenerateCure(properties) + else + CRASH("Our properties were empty or null!") + + +// Assign the spread type and give it the correct description. +/datum/disease/advance/proc/SetSpread(var/spread_id) + switch(spread_id) + + if(NON_CONTAGIOUS) + spread = "None" + if(SPECIAL) + spread = "None" + if(CONTACT_GENERAL, CONTACT_HANDS, CONTACT_FEET) + spread = "On contact" + if(AIRBORNE) + spread = "Airborne" + if(BLOOD) + spread = "Blood" + + spread_type = spread_id + +/datum/disease/advance/proc/SetSeverity(var/level_sev) + + switch(level_sev) + + if(-INFINITY to 0) + severity = "Non-Threat" + if(1) + severity = "Minor" + if(2) + severity = "Medium" + if(3) + severity = "Harmful" + if(4) + severity = "Dangerous!" + if(5 to INFINITY) + severity = "BIOHAZARD THREAT!" + else + severity = "Unknown" + + +// Will generate a random cure, the less resistance the symptoms have, the harder the cure. +/datum/disease/advance/proc/GenerateCure(var/list/properties = list()) + if(properties && properties.len) + var/res = Clamp(properties["resistance"] - (symptoms.len / 2), 1, advance_cures.len) + cure_id = advance_cures[res] + + // Get the cure name from the cure_id + var/datum/reagent/D = SSchemistry.chemical_reagents[cure_id] + cure = D.name + + + return + +// Randomly generate a symptom, has a chance to lose or gain a symptom. +/datum/disease/advance/proc/Evolve(var/level = 2) + var/s = safepick(GenerateSymptoms(level, 1)) + if(s) + AddSymptom(s) + Refresh(1) + return + +// Randomly remove a symptom. +/datum/disease/advance/proc/Devolve() + if(symptoms.len > 1) + var/s = safepick(symptoms) + if(s) + RemoveSymptom(s) + Refresh(1) + return + +// Name the disease. +/datum/disease/advance/proc/AssignName(var/name = "Unknown") + src.name = name + return + +// Return a unique ID of the disease. +/datum/disease/advance/proc/GetDiseaseID() + + var/list/L = list() + for(var/datum/symptom/S in symptoms) + L += S.id + L = sortList(L) // Sort the list so it doesn't matter which order the symptoms are in. + var/result = jointext(L, ":") + id = result + return result + + +// Add a symptom, if it is over the limit (with a small chance to be able to go over) +// we take a random symptom away and add the new one. +/datum/disease/advance/proc/AddSymptom(var/datum/symptom/S) + + if(HasSymptom(S)) + return + + if(symptoms.len < 5 + rand(-1, 1)) + symptoms += S + else + RemoveSymptom(pick(symptoms)) + symptoms += S + return + +// Simply removes the symptom. +/datum/disease/advance/proc/RemoveSymptom(var/datum/symptom/S) + symptoms -= S + return + +/* + + Static Procs + +*/ + +// Mix a list of advance diseases and return the mixed result. +/proc/Advance_Mix(var/list/D_list) + var/list/diseases = list() + + for(var/datum/disease/advance/A in D_list) + diseases += A.Copy() + + if(!diseases.len) + return null + if(diseases.len <= 1) + return pick(diseases) // Just return the only entry. + + var/i = 0 + // Mix our diseases until we are left with only one result. + while(i < 20 && diseases.len > 1) + + i++ + + var/datum/disease/advance/D1 = pick(diseases) + diseases -= D1 + + var/datum/disease/advance/D2 = pick(diseases) + D2.Mix(D1) + + // Should be only 1 entry left, but if not let's only return a single entry + var/datum/disease/advance/to_return = pick(diseases) + to_return.Refresh(1) + return to_return + +/proc/SetViruses(var/datum/reagent/R, var/list/data) + if(data) + var/list/preserve = list() + if(istype(data) && data["viruses"]) + for(var/datum/disease/A in data["viruses"]) + preserve += A.Copy() + R.data = data.Copy() + else + R.data = data + if(preserve.len) + R.data["viruses"] = preserve + +/proc/AdminCreateVirus(var/mob/user) + var/i = 5 + + var/datum/disease/advance/D = new(0, null) + D.symptoms = list() + + var/list/symptoms = list() + symptoms += "Done" + symptoms += list_symptoms.Copy() + do + var/symptom = input(user, "Choose a symptom to add ([i] remaining)", "Choose a Symptom") in symptoms + if(istext(symptom)) + i = 0 + else if(ispath(symptom)) + var/datum/symptom/S = new symptom + if(!D.HasSymptom(S)) + D.symptoms += S + i -= 1 + while(i > 0) + + if(D.symptoms.len > 0) + + var/new_name = sanitizeSafe(input(user, "Name your new disease.", "New Name"), MAX_NAME_LEN) + D.AssignName(new_name) + D.Refresh() + + for(var/datum/disease/advance/AD in SSdisease.processing) + AD.Refresh() + + for(var/mob/living/carbon/human/H in shuffle(living_mob_list)) + if(H.z != 1) + continue + if(!H.has_disease(D)) + H.contract_disease(D, 1) + break + + var/list/name_symptoms = list() + for(var/datum/symptom/S in D.symptoms) + name_symptoms += S.name + message_admins("[key_name_admin(user)] has triggered a custom virus outbreak of [D.name]! It has these symptoms: [english_list(name_symptoms)]") + +/datum/disease/advance/proc/totalStageSpeed() + return properties["stage_rate"] + +/datum/disease/advance/proc/totalStealth() + return properties["stealth"] + +/datum/disease/advance/proc/totalResistance() + return properties["resistance"] + +/datum/disease/advance/proc/totalTransmittable() + return properties["transmittable"] + +/* +/mob/verb/test() + + for(var/datum/disease/D in SSdisease.processing) + to_chat(src, "[D.name] - [D.holder]") +*/ + +#undef RANDOM_STARTING_LEVEL diff --git a/code/datums/diseases/advance/presets.dm b/code/datums/diseases/advance/presets.dm index b62b18e306c..675feadbb0b 100644 --- a/code/datums/diseases/advance/presets.dm +++ b/code/datums/diseases/advance/presets.dm @@ -1,43 +1,43 @@ -// Cold - -/datum/disease/advance/cold/New(var/process = 1, var/datum/disease/advance/D, var/copy = 0) - if(!D) - name = "Cold" - symptoms = list(new/datum/symptom/sneeze) - ..(process, D, copy) - - -// Flu - -/datum/disease/advance/flu/New(var/process = 1, var/datum/disease/advance/D, var/copy = 0) - if(!D) - name = "Flu" - symptoms = list(new/datum/symptom/cough) - ..(process, D, copy) - - -// Voice Changing - -/datum/disease/advance/voice_change/New(var/process = 1, var/datum/disease/advance/D, var/copy = 0) - if(!D) - name = "Epiglottis Mutation" - symptoms = list(new/datum/symptom/voice_change) - ..(process, D, copy) - - -// Toxin Filter - -/datum/disease/advance/heal/New(var/process = 1, var/datum/disease/advance/D, var/copy = 0) - if(!D) - name = "Liver Enhancer" - symptoms = list(new/datum/symptom/heal) - ..(process, D, copy) - - -// Hullucigen - -/datum/disease/advance/hullucigen/New(var/process = 1, var/datum/disease/advance/D, var/copy = 0) - if(!D) - name = "Reality Impairment" - symptoms = list(new/datum/symptom/hallucigen) +// Cold + +/datum/disease/advance/cold/New(var/process = 1, var/datum/disease/advance/D, var/copy = 0) + if(!D) + name = "Cold" + symptoms = list(new/datum/symptom/sneeze) + ..(process, D, copy) + + +// Flu + +/datum/disease/advance/flu/New(var/process = 1, var/datum/disease/advance/D, var/copy = 0) + if(!D) + name = "Flu" + symptoms = list(new/datum/symptom/cough) + ..(process, D, copy) + + +// Voice Changing + +/datum/disease/advance/voice_change/New(var/process = 1, var/datum/disease/advance/D, var/copy = 0) + if(!D) + name = "Epiglottis Mutation" + symptoms = list(new/datum/symptom/voice_change) + ..(process, D, copy) + + +// Toxin Filter + +/datum/disease/advance/heal/New(var/process = 1, var/datum/disease/advance/D, var/copy = 0) + if(!D) + name = "Liver Enhancer" + symptoms = list(new/datum/symptom/heal) + ..(process, D, copy) + + +// Hullucigen + +/datum/disease/advance/hullucigen/New(var/process = 1, var/datum/disease/advance/D, var/copy = 0) + if(!D) + name = "Reality Impairment" + symptoms = list(new/datum/symptom/hallucigen) ..(process, D, copy) \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/confusion.dm b/code/datums/diseases/advance/symptoms/confusion.dm index 7cb2546851a..6391cf7110a 100644 --- a/code/datums/diseases/advance/symptoms/confusion.dm +++ b/code/datums/diseases/advance/symptoms/confusion.dm @@ -1,39 +1,39 @@ -/* -////////////////////////////////////// - -Confusion - - Little bit hidden. - Lowers resistance. - Decreases stage speed. - Not very transmittable. - Intense Level. - -Bonus - Makes the affected mob be confused for short periods of time. - -////////////////////////////////////// -*/ - -/datum/symptom/confusion - - name = "Confusion" - stealth = 1 - resistance = -1 - stage_speed = -3 - transmittable = 0 - level = 4 - - -/datum/symptom/confusion/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/carbon/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - to_chat(M, "[pick("You feel confused.", "You forgot what you were thinking about.")]") - else - to_chat(M, "You are unable to think straight!") - M.confused = min(100, M.confused + 2) - - return +/* +////////////////////////////////////// + +Confusion + + Little bit hidden. + Lowers resistance. + Decreases stage speed. + Not very transmittable. + Intense Level. + +Bonus + Makes the affected mob be confused for short periods of time. + +////////////////////////////////////// +*/ + +/datum/symptom/confusion + + name = "Confusion" + stealth = 1 + resistance = -1 + stage_speed = -3 + transmittable = 0 + level = 4 + + +/datum/symptom/confusion/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/carbon/M = A.affected_mob + switch(A.stage) + if(1, 2, 3, 4) + to_chat(M, "[pick("You feel confused.", "You forgot what you were thinking about.")]") + else + to_chat(M, "You are unable to think straight!") + M.confused = min(100, M.confused + 2) + + return diff --git a/code/datums/diseases/advance/symptoms/cough.dm b/code/datums/diseases/advance/symptoms/cough.dm index be4162b66ef..d62153c5873 100644 --- a/code/datums/diseases/advance/symptoms/cough.dm +++ b/code/datums/diseases/advance/symptoms/cough.dm @@ -1,39 +1,39 @@ -/* -////////////////////////////////////// - -Coughing - - Noticable. - Little Resistance. - Doesn't increase stage speed much. - Transmittable. - Low Level. - -BONUS - Will force the affected mob to drop small items! - -////////////////////////////////////// -*/ - -/datum/symptom/cough - - name = "Cough" - stealth = -1 - resistance = 3 - stage_speed = 1 - transmittable = 2 - level = 1 - -/datum/symptom/cough/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3) - to_chat(M, "[pick("You swallow excess mucus.", "You lightly cough.")]") - else - M.emote("cough") - var/obj/item/I = M.get_active_hand() - if(I && I.w_class < 3) - M.drop_from_inventory(I) +/* +////////////////////////////////////// + +Coughing + + Noticable. + Little Resistance. + Doesn't increase stage speed much. + Transmittable. + Low Level. + +BONUS + Will force the affected mob to drop small items! + +////////////////////////////////////// +*/ + +/datum/symptom/cough + + name = "Cough" + stealth = -1 + resistance = 3 + stage_speed = 1 + transmittable = 2 + level = 1 + +/datum/symptom/cough/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2, 3) + to_chat(M, "[pick("You swallow excess mucus.", "You lightly cough.")]") + else + M.emote("cough") + var/obj/item/I = M.get_active_hand() + if(I && I.w_class < 3) + M.drop_from_inventory(I) return \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/damage_converter.dm b/code/datums/diseases/advance/symptoms/damage_converter.dm index 6e31e363c8a..235c4430601 100644 --- a/code/datums/diseases/advance/symptoms/damage_converter.dm +++ b/code/datums/diseases/advance/symptoms/damage_converter.dm @@ -1,43 +1,43 @@ -/* -////////////////////////////////////// - -Damage Converter - - Little bit hidden. - Lowers resistance tremendously. - Decreases stage speed tremendously. - Reduced transmittablity - Intense Level. - -Bonus - Slowly converts brute/fire damage to toxin. - -////////////////////////////////////// -*/ - -/datum/symptom/damage_converter - - name = "Toxic Compensation" - stealth = 1 - resistance = -4 - stage_speed = -4 - transmittable = -2 - level = 4 - -/datum/symptom/damage_converter/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(4, 5) - Convert(M) - return - -/datum/symptom/damage_converter/proc/Convert(var/mob/living/M) - - if(M.getFireLoss() < M.getMaxHealth() || M.getBruteLoss() < M.getMaxHealth()) - var/get_damage = rand(1, 2) - M.adjustFireLoss(-get_damage) - M.adjustBruteLoss(-get_damage) - M.adjustToxLoss(get_damage) +/* +////////////////////////////////////// + +Damage Converter + + Little bit hidden. + Lowers resistance tremendously. + Decreases stage speed tremendously. + Reduced transmittablity + Intense Level. + +Bonus + Slowly converts brute/fire damage to toxin. + +////////////////////////////////////// +*/ + +/datum/symptom/damage_converter + + name = "Toxic Compensation" + stealth = 1 + resistance = -4 + stage_speed = -4 + transmittable = -2 + level = 4 + +/datum/symptom/damage_converter/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(4, 5) + Convert(M) + return + +/datum/symptom/damage_converter/proc/Convert(var/mob/living/M) + + if(M.getFireLoss() < M.getMaxHealth() || M.getBruteLoss() < M.getMaxHealth()) + var/get_damage = rand(1, 2) + M.adjustFireLoss(-get_damage) + M.adjustBruteLoss(-get_damage) + M.adjustToxLoss(get_damage) return 1 \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/dizzy.dm b/code/datums/diseases/advance/symptoms/dizzy.dm index 582b2926400..32bcdfa49bb 100644 --- a/code/datums/diseases/advance/symptoms/dizzy.dm +++ b/code/datums/diseases/advance/symptoms/dizzy.dm @@ -1,37 +1,37 @@ -/* -////////////////////////////////////// - -Dizziness - - Hidden. - Lowers resistance considerably. - Decreases stage speed. - Reduced transmittability - Intense Level. - -Bonus - Shakes the affected mob's screen for short periods. - -////////////////////////////////////// -*/ - -/datum/symptom/dizzy // Not the egg - - name = "Dizziness" - stealth = 2 - resistance = -2 - stage_speed = -3 - transmittable = -1 - level = 4 - -/datum/symptom/dizzy/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - to_chat(M, "[pick("You feel dizzy.", "Your head starts spinning.")]") - else - to_chat(M, "You are unable to look straight!") - M.make_dizzy(5) +/* +////////////////////////////////////// + +Dizziness + + Hidden. + Lowers resistance considerably. + Decreases stage speed. + Reduced transmittability + Intense Level. + +Bonus + Shakes the affected mob's screen for short periods. + +////////////////////////////////////// +*/ + +/datum/symptom/dizzy // Not the egg + + name = "Dizziness" + stealth = 2 + resistance = -2 + stage_speed = -3 + transmittable = -1 + level = 4 + +/datum/symptom/dizzy/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2, 3, 4) + to_chat(M, "[pick("You feel dizzy.", "Your head starts spinning.")]") + else + to_chat(M, "You are unable to look straight!") + M.make_dizzy(5) return \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/fever.dm b/code/datums/diseases/advance/symptoms/fever.dm index e3dbc9a7ec7..fe122879650 100644 --- a/code/datums/diseases/advance/symptoms/fever.dm +++ b/code/datums/diseases/advance/symptoms/fever.dm @@ -1,36 +1,36 @@ -/* -////////////////////////////////////// - -Fever - - No change to hidden. - Increases resistance. - Increases stage speed. - Little transmittable. - Low level. - -Bonus - Heats up your body. - -////////////////////////////////////// -*/ - -/datum/symptom/fever - - name = "Fever" - stealth = 0 - resistance = 3 - stage_speed = 3 - transmittable = 2 - level = 2 - -/datum/symptom/fever/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/carbon/M = A.affected_mob - var/alberyk1 = "[pick("You feel hot.", "You feel like you're burning.")]" - to_chat(M, alberyk1) - if(M.bodytemperature < BODYTEMP_HEAT_DAMAGE_LIMIT) - M.bodytemperature = min(M.bodytemperature + (20 * A.stage), BODYTEMP_HEAT_DAMAGE_LIMIT - 1) - - return +/* +////////////////////////////////////// + +Fever + + No change to hidden. + Increases resistance. + Increases stage speed. + Little transmittable. + Low level. + +Bonus + Heats up your body. + +////////////////////////////////////// +*/ + +/datum/symptom/fever + + name = "Fever" + stealth = 0 + resistance = 3 + stage_speed = 3 + transmittable = 2 + level = 2 + +/datum/symptom/fever/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/carbon/M = A.affected_mob + var/alberyk1 = "[pick("You feel hot.", "You feel like you're burning.")]" + to_chat(M, alberyk1) + if(M.bodytemperature < BODYTEMP_HEAT_DAMAGE_LIMIT) + M.bodytemperature = min(M.bodytemperature + (20 * A.stage), BODYTEMP_HEAT_DAMAGE_LIMIT - 1) + + return diff --git a/code/datums/diseases/advance/symptoms/hallucigen.dm b/code/datums/diseases/advance/symptoms/hallucigen.dm index e22cb486cb3..a3e1dc03375 100644 --- a/code/datums/diseases/advance/symptoms/hallucigen.dm +++ b/code/datums/diseases/advance/symptoms/hallucigen.dm @@ -1,37 +1,37 @@ -/* -////////////////////////////////////// - -Hallucigen - - Very noticable. - Lowers resistance considerably. - Decreases stage speed. - Reduced transmittable. - Critical Level. - -Bonus - Makes the affected mob be hallucinated for short periods of time. - -////////////////////////////////////// -*/ - -/datum/symptom/hallucigen - - name = "Hallucigen" - stealth = -2 - resistance = -3 - stage_speed = -3 - transmittable = -1 - level = 5 - -/datum/symptom/hallucigen/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/carbon/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - to_chat(M, "[pick("You notice someone in the corner of your eye.", "Is that footsteps?.")]") - else - M.hallucination += 5 - - return +/* +////////////////////////////////////// + +Hallucigen + + Very noticable. + Lowers resistance considerably. + Decreases stage speed. + Reduced transmittable. + Critical Level. + +Bonus + Makes the affected mob be hallucinated for short periods of time. + +////////////////////////////////////// +*/ + +/datum/symptom/hallucigen + + name = "Hallucigen" + stealth = -2 + resistance = -3 + stage_speed = -3 + transmittable = -1 + level = 5 + +/datum/symptom/hallucigen/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/carbon/M = A.affected_mob + switch(A.stage) + if(1, 2, 3, 4) + to_chat(M, "[pick("You notice someone in the corner of your eye.", "Is that footsteps?.")]") + else + M.hallucination += 5 + + return diff --git a/code/datums/diseases/advance/symptoms/headache.dm b/code/datums/diseases/advance/symptoms/headache.dm index 87273221a2f..328e4fe1a50 100644 --- a/code/datums/diseases/advance/symptoms/headache.dm +++ b/code/datums/diseases/advance/symptoms/headache.dm @@ -1,33 +1,33 @@ -/* -////////////////////////////////////// - -Headache - - Noticable. - Highly resistant. - Increases stage speed. - Not transmittable. - Low Level. - -BONUS - Displays an annoying message! - Should be used for buffing your disease. - -////////////////////////////////////// -*/ - -/datum/symptom/headache - - name = "Headache" - stealth = -1 - resistance = 4 - stage_speed = 2 - transmittable = 0 - level = 1 - -/datum/symptom/headache/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - to_chat(M, "[pick("Your head hurts.", "Your head starts pounding.")]") +/* +////////////////////////////////////// + +Headache + + Noticable. + Highly resistant. + Increases stage speed. + Not transmittable. + Low Level. + +BONUS + Displays an annoying message! + Should be used for buffing your disease. + +////////////////////////////////////// +*/ + +/datum/symptom/headache + + name = "Headache" + stealth = -1 + resistance = 4 + stage_speed = 2 + transmittable = 0 + level = 1 + +/datum/symptom/headache/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + to_chat(M, "[pick("Your head hurts.", "Your head starts pounding.")]") return \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index 0584dc76110..3788806033f 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -1,40 +1,40 @@ -/* -////////////////////////////////////// - -Healing - - Little bit hidden. - Lowers resistance tremendously. - Decreases stage speed tremendously. - Decreases transmittablity temrendously. - Fatal Level. - -Bonus - Heals toxins in the affected mob's blood stream. - -////////////////////////////////////// -*/ - -/datum/symptom/heal - - name = "Toxic Filter" - stealth = 1 - resistance = -4 - stage_speed = -4 - transmittable = -4 - level = 6 - -/datum/symptom/heal/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(4, 5) - Heal(M) - return - -/datum/symptom/heal/proc/Heal(var/mob/living/M) - - var/get_damage = rand(1, 2) - M.adjustToxLoss(-get_damage) +/* +////////////////////////////////////// + +Healing + + Little bit hidden. + Lowers resistance tremendously. + Decreases stage speed tremendously. + Decreases transmittablity temrendously. + Fatal Level. + +Bonus + Heals toxins in the affected mob's blood stream. + +////////////////////////////////////// +*/ + +/datum/symptom/heal + + name = "Toxic Filter" + stealth = 1 + resistance = -4 + stage_speed = -4 + transmittable = -4 + level = 6 + +/datum/symptom/heal/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(4, 5) + Heal(M) + return + +/datum/symptom/heal/proc/Heal(var/mob/living/M) + + var/get_damage = rand(1, 2) + M.adjustToxLoss(-get_damage) return 1 \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/itching.dm b/code/datums/diseases/advance/symptoms/itching.dm index 172a6958447..6a3a823cf2d 100644 --- a/code/datums/diseases/advance/symptoms/itching.dm +++ b/code/datums/diseases/advance/symptoms/itching.dm @@ -1,33 +1,33 @@ -/* -////////////////////////////////////// - -Itching - - Not noticable or unnoticable. - Resistant. - Increases stage speed. - Little transmittable. - Low Level. - -BONUS - Displays an annoying message! - Should be used for buffing your disease. - -////////////////////////////////////// -*/ - -/datum/symptom/itching - - name = "Itching" - stealth = 0 - resistance = 3 - stage_speed = 3 - transmittable = 1 - level = 1 - -/datum/symptom/itching/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - to_chat(M, "Your [pick("back", "arm", "leg", "elbow", "head")] itches.") +/* +////////////////////////////////////// + +Itching + + Not noticable or unnoticable. + Resistant. + Increases stage speed. + Little transmittable. + Low Level. + +BONUS + Displays an annoying message! + Should be used for buffing your disease. + +////////////////////////////////////// +*/ + +/datum/symptom/itching + + name = "Itching" + stealth = 0 + resistance = 3 + stage_speed = 3 + transmittable = 1 + level = 1 + +/datum/symptom/itching/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + to_chat(M, "Your [pick("back", "arm", "leg", "elbow", "head")] itches.") return \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/shivering.dm b/code/datums/diseases/advance/symptoms/shivering.dm index edf3432b871..51cbd782458 100644 --- a/code/datums/diseases/advance/symptoms/shivering.dm +++ b/code/datums/diseases/advance/symptoms/shivering.dm @@ -1,35 +1,35 @@ -/* -////////////////////////////////////// - -Shivering - - No change to hidden. - Increases resistance. - Increases stage speed. - Little transmittable. - Low level. - -Bonus - Cools down your body. - -////////////////////////////////////// -*/ - -/datum/symptom/shivering - - name = "Shivering" - stealth = 0 - resistance = 2 - stage_speed = 2 - transmittable = 2 - level = 2 - -/datum/symptom/shivering/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/carbon/M = A.affected_mob - to_chat(M, "[pick("You feel cold.", "You start shaking from the cold.")]") - if(M.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT) - M.bodytemperature = min(M.bodytemperature - (20 * A.stage), BODYTEMP_COLD_DAMAGE_LIMIT + 1) - - return +/* +////////////////////////////////////// + +Shivering + + No change to hidden. + Increases resistance. + Increases stage speed. + Little transmittable. + Low level. + +Bonus + Cools down your body. + +////////////////////////////////////// +*/ + +/datum/symptom/shivering + + name = "Shivering" + stealth = 0 + resistance = 2 + stage_speed = 2 + transmittable = 2 + level = 2 + +/datum/symptom/shivering/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/carbon/M = A.affected_mob + to_chat(M, "[pick("You feel cold.", "You start shaking from the cold.")]") + if(M.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT) + M.bodytemperature = min(M.bodytemperature - (20 * A.stage), BODYTEMP_COLD_DAMAGE_LIMIT + 1) + + return diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm index 155e71839a4..bab59c2d8cb 100644 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ b/code/datums/diseases/advance/symptoms/sneeze.dm @@ -1,38 +1,38 @@ -/* -////////////////////////////////////// - -Sneezing - - Very Noticable. - Increases resistance. - Doesn't increase stage speed. - Very transmittable. - Low Level. - -Bonus - Forces a spread type of AIRBORNE - with extra range! - -////////////////////////////////////// -*/ - -/datum/symptom/sneeze - - name = "Sneezing" - stealth = -2 - resistance = 3 - stage_speed = 0 - transmittable = 4 - level = 1 - -/datum/symptom/sneeze/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3) - M.emote("sniff") - else - M.emote("sneeze") - A.spread(A.holder, 5, AIRBORNE) +/* +////////////////////////////////////// + +Sneezing + + Very Noticable. + Increases resistance. + Doesn't increase stage speed. + Very transmittable. + Low Level. + +Bonus + Forces a spread type of AIRBORNE + with extra range! + +////////////////////////////////////// +*/ + +/datum/symptom/sneeze + + name = "Sneezing" + stealth = -2 + resistance = 3 + stage_speed = 0 + transmittable = 4 + level = 1 + +/datum/symptom/sneeze/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2, 3) + M.emote("sniff") + else + M.emote("sneeze") + A.spread(A.holder, 5, AIRBORNE) return \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index 349809b973e..0cb69eaab8c 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -1,38 +1,38 @@ -// Symptoms are the effects that engineered advanced diseases do. - -var/list/list_symptoms = typesof(/datum/symptom) - /datum/symptom -var/list/dictionary_symptoms = list() - -var/global/const/SYMPTOM_ACTIVATION_PROB = 3 - -/datum/symptom - // Buffs/Debuffs the symptom has to the overall engineered disease. - var/name = "" - var/stealth = 0 - var/resistance = 0 - var/stage_speed = 0 - var/transmittable = 0 - // The type level of the symptom. Higher is more lethal and harder to generate. - var/level = 0 - // The hash tag for our diseases, we will add it up with our other symptoms to get a unique id! ID MUST BE UNIQUE!!! - var/id = "" - -/datum/symptom/New() - var/list/S = list_symptoms - for(var/i = 1; i <= S.len; i++) - if(src.type == S[i]) - id = "[i]" - return - CRASH("We couldn't assign an ID!") - -// Called when processing of the advance disease, which holds this symptom, starts. -/datum/symptom/proc/Start(var/datum/disease/advance/A) - return - -// Called when the advance disease is going to be deleted or when the advance disease stops processing. -/datum/symptom/proc/End(var/datum/disease/advance/A) - return - -/datum/symptom/proc/Activate(var/datum/disease/advance/A) - return - +// Symptoms are the effects that engineered advanced diseases do. + +var/list/list_symptoms = typesof(/datum/symptom) - /datum/symptom +var/list/dictionary_symptoms = list() + +var/global/const/SYMPTOM_ACTIVATION_PROB = 3 + +/datum/symptom + // Buffs/Debuffs the symptom has to the overall engineered disease. + var/name = "" + var/stealth = 0 + var/resistance = 0 + var/stage_speed = 0 + var/transmittable = 0 + // The type level of the symptom. Higher is more lethal and harder to generate. + var/level = 0 + // The hash tag for our diseases, we will add it up with our other symptoms to get a unique id! ID MUST BE UNIQUE!!! + var/id = "" + +/datum/symptom/New() + var/list/S = list_symptoms + for(var/i = 1; i <= S.len; i++) + if(src.type == S[i]) + id = "[i]" + return + CRASH("We couldn't assign an ID!") + +// Called when processing of the advance disease, which holds this symptom, starts. +/datum/symptom/proc/Start(var/datum/disease/advance/A) + return + +// Called when the advance disease is going to be deleted or when the advance disease stops processing. +/datum/symptom/proc/End(var/datum/disease/advance/A) + return + +/datum/symptom/proc/Activate(var/datum/disease/advance/A) + return + diff --git a/code/datums/diseases/advance/symptoms/voice_change.dm b/code/datums/diseases/advance/symptoms/voice_change.dm index f15ed2bc564..6948e84e1bd 100644 --- a/code/datums/diseases/advance/symptoms/voice_change.dm +++ b/code/datums/diseases/advance/symptoms/voice_change.dm @@ -1,54 +1,54 @@ -/* -////////////////////////////////////// - -Voice Change - - Very Very noticable. - Lowers resistance considerably. - Decreases stage speed. - Reduced transmittable. - Fatal Level. - -Bonus - Changes the voice of the affected mob. Causing confusion in communication. - -////////////////////////////////////// -*/ - -/datum/symptom/voice_change - - name = "Voice Change" - stealth = -2 - resistance = -3 - stage_speed = -3 - transmittable = -1 - level = 6 - -/datum/symptom/voice_change/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - - var/mob/living/carbon/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - to_chat(M, "[pick("Your throat hurts.", "You clear your throat.")]") - else - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/random_name = "" - switch(H.gender) - if(MALE) - random_name = pick(first_names_male) - else - random_name = pick(first_names_female) - random_name += " [pick(last_names)]" - H.SetSpecialVoice(random_name) - - return - -/datum/symptom/voice_change/End(var/datum/disease/advance/A) - ..() - if(ishuman(A.affected_mob)) - var/mob/living/carbon/human/H = A.affected_mob - H.UnsetSpecialVoice() - return +/* +////////////////////////////////////// + +Voice Change + + Very Very noticable. + Lowers resistance considerably. + Decreases stage speed. + Reduced transmittable. + Fatal Level. + +Bonus + Changes the voice of the affected mob. Causing confusion in communication. + +////////////////////////////////////// +*/ + +/datum/symptom/voice_change + + name = "Voice Change" + stealth = -2 + resistance = -3 + stage_speed = -3 + transmittable = -1 + level = 6 + +/datum/symptom/voice_change/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + + var/mob/living/carbon/M = A.affected_mob + switch(A.stage) + if(1, 2, 3, 4) + to_chat(M, "[pick("Your throat hurts.", "You clear your throat.")]") + else + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/random_name = "" + switch(H.gender) + if(MALE) + random_name = pick(first_names_male) + else + random_name = pick(first_names_female) + random_name += " [pick(last_names)]" + H.SetSpecialVoice(random_name) + + return + +/datum/symptom/voice_change/End(var/datum/disease/advance/A) + ..() + if(ishuman(A.affected_mob)) + var/mob/living/carbon/human/H = A.affected_mob + H.UnsetSpecialVoice() + return diff --git a/code/datums/diseases/advance/symptoms/vomit.dm b/code/datums/diseases/advance/symptoms/vomit.dm index 3b020008130..2a0eb9c448e 100644 --- a/code/datums/diseases/advance/symptoms/vomit.dm +++ b/code/datums/diseases/advance/symptoms/vomit.dm @@ -1,87 +1,92 @@ -/* -////////////////////////////////////// - -Vomiting - - Very Very Noticable. - Decreases resistance. - Doesn't increase stage speed. - Little transmittable. - Medium Level. - -Bonus - Forces the affected mob to vomit! - Meaning your disease can spread via - people walking on vomit. - Makes the affected mob lose nutrition and - heal toxin damage. - -////////////////////////////////////// -*/ - -/datum/symptom/vomit - - name = "Vomiting" - stealth = -2 - resistance = -1 - stage_speed = 0 - transmittable = 1 - level = 3 - -/datum/symptom/vomit/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB / 2)) - var/mob/living/carbon/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - var/message = pick("You feel nauseous.", "You feel like you're going to throw up!") - to_chat(M, "[message]") - else - Vomit(M) - - return - -/datum/symptom/vomit/proc/Vomit(var/mob/living/carbon/M) - M.vomit()//This function is in mob_helpers -/* -////////////////////////////////////// - -Vomiting Blood - - Very Very Noticable. - Decreases resistance. - Decreases stage speed. - Little transmittable. - Intense level. - -Bonus - Forces the affected mob to vomit blood! - Meaning your disease can spread via - people walking on the blood. - Makes the affected mob lose health. - -////////////////////////////////////// -*/ - -/datum/symptom/vomit/blood - - name = "Blood Vomiting" - stealth = -2 - resistance = -1 - stage_speed = -1 - transmittable = 1 - level = 4 - -/datum/symptom/vomit/blood/Vomit(var/mob/living/M) - - M.Stun(1) - M.visible_message("[M] vomits on the floor!") - - // They lose blood and health. - var/brute_dam = M.getBruteLoss() - if(brute_dam < 50) - M.adjustBruteLoss(3) - - var/turf/simulated/pos = get_turf(M) - pos.add_blood_floor(M) +/* +////////////////////////////////////// + +Vomiting + + Very Very Noticable. + Decreases resistance. + Doesn't increase stage speed. + Little transmittable. + Medium Level. + +Bonus + Forces the affected mob to vomit! + Meaning your disease can spread via + people walking on vomit. + Makes the affected mob lose nutrition and + heal toxin damage. + +////////////////////////////////////// +*/ + +/datum/symptom/vomit + + name = "Vomiting" + stealth = -2 + resistance = -1 + stage_speed = 0 + transmittable = 1 + level = 3 + +/datum/symptom/vomit/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB / 2)) + var/mob/living/carbon/human/M = A.affected_mob + if(!istype(M)) + return + switch(A.stage) + if(1, 2, 3, 4) + var/message = pick("You feel nauseous.", "You feel like you're going to throw up!") + to_chat(M, "[message]") + else + vomit(M) + + return + +/datum/symptom/vomit/proc/vomit(var/mob/living/carbon/human/M) + if(!istype(M)) + return + M.vomit() + +/* +////////////////////////////////////// + +Vomiting Blood + + Very Very Noticable. + Decreases resistance. + Decreases stage speed. + Little transmittable. + Intense level. + +Bonus + Forces the affected mob to vomit blood! + Meaning your disease can spread via + people walking on the blood. + Makes the affected mob lose health. + +////////////////////////////////////// +*/ + +/datum/symptom/vomit/blood + + name = "Blood Vomiting" + stealth = -2 + resistance = -1 + stage_speed = -1 + transmittable = 1 + level = 4 + +/datum/symptom/vomit/blood/vomit(var/mob/living/carbon/human/M) + + M.Stun(1) + M.visible_message("[M] vomits on the floor!") + + // They lose blood and health. + var/brute_dam = M.getBruteLoss() + if(brute_dam < 50) + M.adjustBruteLoss(3) + + var/turf/simulated/pos = get_turf(M) + pos.add_blood_floor(M) playsound(pos, 'sound/effects/splat.ogg', 50, 1) \ No newline at end of file diff --git a/code/datums/diseases/advance/symptoms/weight.dm b/code/datums/diseases/advance/symptoms/weight.dm index 17c51431581..bde6ffe05e2 100644 --- a/code/datums/diseases/advance/symptoms/weight.dm +++ b/code/datums/diseases/advance/symptoms/weight.dm @@ -1,119 +1,119 @@ -/* -////////////////////////////////////// - -Weight Gain - - Very Very Noticable. - Decreases resistance. - Decreases stage speed. - Reduced transmittable. - Intense Level. - -Bonus - Increases the weight gain of the mob, - forcing it to eventually turn fat. -////////////////////////////////////// -*/ - -/datum/symptom/weight_gain - - name = "Weight Gain" - stealth = -3 - resistance = -3 - stage_speed = -2 - transmittable = -2 - level = 4 - -/datum/symptom/weight_gain/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - to_chat(M, "[pick("You feel blubbery.", "You feel full.")]") - else - M.overeatduration = min(M.overeatduration + 100, 600) - M.adjustNutritionLoss(-100) - - return - - -/* -////////////////////////////////////// - -Weight Loss - - Very Very Noticable. - Decreases resistance. - Decreases stage speed. - Reduced Transmittable. - High level. - -Bonus - Decreases the weight of the mob, - forcing it to be skinny. - -////////////////////////////////////// -*/ - -/datum/symptom/weight_loss - - name = "Weight Loss" - stealth = -3 - resistance = -2 - stage_speed = -2 - transmittable = -2 - level = 3 - -/datum/symptom/weight_loss/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3, 4) - to_chat(M, "[pick("You feel hungry.", "You crave for food.")]") - else - to_chat(M, "Your stomach rumbles.") - M.overeatduration = max(M.overeatduration - 100, 0) - M.adjustNutritionLoss(100) - - return - -/* -////////////////////////////////////// - -Weight Even - - Very Noticable. - Decreases resistance. - Decreases stage speed. - Reduced transmittable. - High level. - -Bonus - Causes the weight of the mob to - be even, meaning eating isn't - required anymore. - -////////////////////////////////////// -*/ - -/datum/symptom/weight_even - - name = "Weight Even" - stealth = -3 - resistance = -2 - stage_speed = -2 - transmittable = -2 - level = 4 - -/datum/symptom/weight_loss/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(4, 5) - M.overeatduration = 0 - M.adjustNutritionLoss(-M.max_nutrition) - +/* +////////////////////////////////////// + +Weight Gain + + Very Very Noticable. + Decreases resistance. + Decreases stage speed. + Reduced transmittable. + Intense Level. + +Bonus + Increases the weight gain of the mob, + forcing it to eventually turn fat. +////////////////////////////////////// +*/ + +/datum/symptom/weight_gain + + name = "Weight Gain" + stealth = -3 + resistance = -3 + stage_speed = -2 + transmittable = -2 + level = 4 + +/datum/symptom/weight_gain/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2, 3, 4) + to_chat(M, "[pick("You feel blubbery.", "You feel full.")]") + else + M.overeatduration = min(M.overeatduration + 100, 600) + M.adjustNutritionLoss(-100) + + return + + +/* +////////////////////////////////////// + +Weight Loss + + Very Very Noticable. + Decreases resistance. + Decreases stage speed. + Reduced Transmittable. + High level. + +Bonus + Decreases the weight of the mob, + forcing it to be skinny. + +////////////////////////////////////// +*/ + +/datum/symptom/weight_loss + + name = "Weight Loss" + stealth = -3 + resistance = -2 + stage_speed = -2 + transmittable = -2 + level = 3 + +/datum/symptom/weight_loss/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2, 3, 4) + to_chat(M, "[pick("You feel hungry.", "You crave for food.")]") + else + to_chat(M, "Your stomach rumbles.") + M.overeatduration = max(M.overeatduration - 100, 0) + M.adjustNutritionLoss(100) + + return + +/* +////////////////////////////////////// + +Weight Even + + Very Noticable. + Decreases resistance. + Decreases stage speed. + Reduced transmittable. + High level. + +Bonus + Causes the weight of the mob to + be even, meaning eating isn't + required anymore. + +////////////////////////////////////// +*/ + +/datum/symptom/weight_even + + name = "Weight Even" + stealth = -3 + resistance = -2 + stage_speed = -2 + transmittable = -2 + level = 4 + +/datum/symptom/weight_loss/Activate(var/datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(4, 5) + M.overeatduration = 0 + M.adjustNutritionLoss(-M.max_nutrition) + return \ No newline at end of file diff --git a/code/datums/diseases/alien_embryo.dm b/code/datums/diseases/alien_embryo.dm index 389d167c76d..a150d8a3f32 100644 --- a/code/datums/diseases/alien_embryo.dm +++ b/code/datums/diseases/alien_embryo.dm @@ -1,153 +1,153 @@ -//affected_mob.contract_disease(new /datum/disease/alien_embryo) - -//cael - retained this file for legacy reference, see code\modules\mob\living\carbon\alien\special\alien_embryo.dm for replacement - -//Our own special process so that dead hosts still chestburst -/datum/disease/alien_embryo/process() - if(!holder) return - if(holder == affected_mob) - stage_act() - if(affected_mob) - if(affected_mob.stat == DEAD) - if(prob(50)) - if(--longevity<=0) - cure(0) - else //the virus is in inanimate obj - cure(0) - return - -/datum/disease/alien_embryo/New() - ..() - /* Special Hud for xenos */ - spawn(0) - if (affected_mob) - AddInfectionImages(affected_mob) - -/datum/disease/alien_embryo/cure(var/resistance=1) - ..() - spawn(0) - if (affected_mob) - RemoveInfectionImages(affected_mob) - -/datum/disease/alien_embryo - name = "Unidentified Foreign Body" - max_stages = 5 - spread = "None" - spread_type = SPECIAL - cure = "Unknown" - cure_id = list("lexorin","toxin","gargleblaster") - cure_chance = 50 - affected_species = list("Human", "Monkey") - permeability_mod = 15//likely to infect - can_carry = 0 - stage_prob = 3 - var/gibbed = 0 - stage_minimum_age = 300 - -/datum/disease/alien_embryo/stage_act() - ..() - switch(stage) - if(2, 3) - if(prob(1)) - affected_mob.emote("sneeze") - if(prob(1)) - affected_mob.emote("cough") - if(prob(1)) - to_chat(affected_mob, "Your throat feels sore.") - if(prob(1)) - to_chat(affected_mob, "Mucous runs down the back of your throat.") - if(4) - if(prob(1)) - affected_mob.emote("sneeze") - if(prob(1)) - affected_mob.emote("cough") - if(prob(2)) - to_chat(affected_mob, "Your muscles ache.") - if(prob(20)) - affected_mob.take_organ_damage(1) - if(prob(2)) - to_chat(affected_mob, "Your stomach hurts.") - if(prob(20)) - affected_mob.adjustToxLoss(1) - affected_mob.updatehealth() - if(5) - to_chat(affected_mob, "You feel something tearing its way out of your stomach...") - affected_mob.adjustToxLoss(10) - affected_mob.updatehealth() - if(prob(50)) - if(gibbed != 0) return 0 - var/list/candidates = get_alien_candidates() - var/picked = null - - // To stop clientless larva, we will check that our host has a client - // if we find no ghosts to become the alien. If the host has a client - // he will become the alien but if he doesn't then we will set the stage - // to 2, so we don't do a process heavy check everytime. - - if(candidates.len) - picked = pick(candidates) - else if(affected_mob.client) - picked = affected_mob.key - else - stage = 2 // Let's try again later. - return - - var/mob/living/carbon/alien/larva/new_xeno = new(affected_mob.loc) - new_xeno.key = picked - new_xeno << sound('sound/voice/hiss5.ogg',0,0,0,100) //To get the player's attention - affected_mob.gib() - src.cure(0) - gibbed = 1 - return - -/datum/disease/alien_embryo/stage_change(var/old_stage) - RefreshInfectionImage() - -/*---------------------------------------- -Proc: RefreshInfectionImage() -Des: Removes all infection images from aliens and places an infection image on all infected mobs for aliens. -----------------------------------------*/ -/datum/disease/alien_embryo/proc/RefreshInfectionImage() - spawn(0) - for (var/mob/living/carbon/alien/alien in player_list) - if (alien.client) - for(var/image/I in alien.client.images) - if(dd_hasprefix_case(I.icon_state, "infected")) - qdel(I) - - for (var/mob/living/carbon/alien/alien in player_list) - if (alien.client) - for (var/mob/living/carbon/C in mob_list) - if(C) - if (C.status_flags & XENO_HOST) - var/I = image('icons/mob/alien.dmi', loc = C, icon_state = "infected[stage]") - alien.client.images += I - return - -/*---------------------------------------- -Proc: AddInfectionImages(C) -Des: Checks if the passed mob (C) is infected with the alien egg, then gives each alien client an infected image at C. -----------------------------------------*/ -/datum/disease/alien_embryo/proc/AddInfectionImages(var/mob/living/carbon/C) - if (C) - for (var/mob/living/carbon/alien/alien in player_list) - if (alien.client) - if (C.status_flags & XENO_HOST) - var/I = image('icons/mob/alien.dmi', loc = C, icon_state = "infected[stage]") - alien.client.images += I - return - -/*---------------------------------------- -Proc: RemoveInfectionImage(C) -Des: Removes the alien infection image from all aliens in the world located in passed mob (C). -----------------------------------------*/ - -/datum/disease/alien_embryo/proc/RemoveInfectionImages(var/mob/living/carbon/C) - if (C) - for (var/mob/living/carbon/alien/alien in player_list) - if (alien.client) - for(var/image/I in alien.client.images) - if(I.loc == C) - if(dd_hasprefix_case(I.icon_state, "infected")) - qdel(I) - return +//affected_mob.contract_disease(new /datum/disease/alien_embryo) + +//cael - retained this file for legacy reference, see code\modules\mob\living\carbon\alien\special\alien_embryo.dm for replacement + +//Our own special process so that dead hosts still chestburst +/datum/disease/alien_embryo/process() + if(!holder) return + if(holder == affected_mob) + stage_act() + if(affected_mob) + if(affected_mob.stat == DEAD) + if(prob(50)) + if(--longevity<=0) + cure(0) + else //the virus is in inanimate obj + cure(0) + return + +/datum/disease/alien_embryo/New() + ..() + /* Special Hud for xenos */ + spawn(0) + if (affected_mob) + AddInfectionImages(affected_mob) + +/datum/disease/alien_embryo/cure(var/resistance=1) + ..() + spawn(0) + if (affected_mob) + RemoveInfectionImages(affected_mob) + +/datum/disease/alien_embryo + name = "Unidentified Foreign Body" + max_stages = 5 + spread = "None" + spread_type = SPECIAL + cure = "Unknown" + cure_id = list("lexorin","toxin","gargleblaster") + cure_chance = 50 + affected_species = list("Human", "Monkey") + permeability_mod = 15//likely to infect + can_carry = 0 + stage_prob = 3 + var/gibbed = 0 + stage_minimum_age = 300 + +/datum/disease/alien_embryo/stage_act() + ..() + switch(stage) + if(2, 3) + if(prob(1)) + affected_mob.emote("sneeze") + if(prob(1)) + affected_mob.emote("cough") + if(prob(1)) + to_chat(affected_mob, "Your throat feels sore.") + if(prob(1)) + to_chat(affected_mob, "Mucous runs down the back of your throat.") + if(4) + if(prob(1)) + affected_mob.emote("sneeze") + if(prob(1)) + affected_mob.emote("cough") + if(prob(2)) + to_chat(affected_mob, "Your muscles ache.") + if(prob(20)) + affected_mob.take_organ_damage(1) + if(prob(2)) + to_chat(affected_mob, "Your stomach hurts.") + if(prob(20)) + affected_mob.adjustToxLoss(1) + affected_mob.updatehealth() + if(5) + to_chat(affected_mob, "You feel something tearing its way out of your stomach...") + affected_mob.adjustToxLoss(10) + affected_mob.updatehealth() + if(prob(50)) + if(gibbed != 0) return 0 + var/list/candidates = get_alien_candidates() + var/picked = null + + // To stop clientless larva, we will check that our host has a client + // if we find no ghosts to become the alien. If the host has a client + // he will become the alien but if he doesn't then we will set the stage + // to 2, so we don't do a process heavy check everytime. + + if(candidates.len) + picked = pick(candidates) + else if(affected_mob.client) + picked = affected_mob.key + else + stage = 2 // Let's try again later. + return + + var/mob/living/carbon/alien/larva/new_xeno = new(affected_mob.loc) + new_xeno.key = picked + new_xeno << sound('sound/voice/hiss5.ogg',0,0,0,100) //To get the player's attention + affected_mob.gib() + src.cure(0) + gibbed = 1 + return + +/datum/disease/alien_embryo/stage_change(var/old_stage) + RefreshInfectionImage() + +/*---------------------------------------- +Proc: RefreshInfectionImage() +Des: Removes all infection images from aliens and places an infection image on all infected mobs for aliens. +----------------------------------------*/ +/datum/disease/alien_embryo/proc/RefreshInfectionImage() + spawn(0) + for (var/mob/living/carbon/alien/alien in player_list) + if (alien.client) + for(var/image/I in alien.client.images) + if(dd_hasprefix_case(I.icon_state, "infected")) + qdel(I) + + for (var/mob/living/carbon/alien/alien in player_list) + if (alien.client) + for (var/mob/living/carbon/C in mob_list) + if(C) + if (C.status_flags & XENO_HOST) + var/I = image('icons/mob/alien.dmi', loc = C, icon_state = "infected[stage]") + alien.client.images += I + return + +/*---------------------------------------- +Proc: AddInfectionImages(C) +Des: Checks if the passed mob (C) is infected with the alien egg, then gives each alien client an infected image at C. +----------------------------------------*/ +/datum/disease/alien_embryo/proc/AddInfectionImages(var/mob/living/carbon/C) + if (C) + for (var/mob/living/carbon/alien/alien in player_list) + if (alien.client) + if (C.status_flags & XENO_HOST) + var/I = image('icons/mob/alien.dmi', loc = C, icon_state = "infected[stage]") + alien.client.images += I + return + +/*---------------------------------------- +Proc: RemoveInfectionImage(C) +Des: Removes the alien infection image from all aliens in the world located in passed mob (C). +----------------------------------------*/ + +/datum/disease/alien_embryo/proc/RemoveInfectionImages(var/mob/living/carbon/C) + if (C) + for (var/mob/living/carbon/alien/alien in player_list) + if (alien.client) + for(var/image/I in alien.client.images) + if(I.loc == C) + if(dd_hasprefix_case(I.icon_state, "infected")) + qdel(I) + return diff --git a/code/datums/diseases/appendicitis.dm b/code/datums/diseases/appendicitis.dm index a279aaa5119..0e07c082f2f 100644 --- a/code/datums/diseases/appendicitis.dm +++ b/code/datums/diseases/appendicitis.dm @@ -1,52 +1,52 @@ -/datum/disease/appendicitis - form = "Condition" - name = "Appendicitis" - max_stages = 4 - spread = "Acute" - cure = "Surgery" - agent = "Appendix" - affected_species = list("Human") - permeability_mod = 1 - contagious_period = 9001 //slightly hacky, but hey! whatever works, right? - desc = "If left untreated the subject will become very weak, and may vomit often." - severity = "Medium" - longevity = 1000 - hidden = list(0, 1) - stage_minimum_age = 160 // at least 200 life ticks per stage - -/datum/disease/appendicitis/stage_act() - ..() - - if(istype(affected_mob,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = affected_mob - if(!H.internal_organs_by_name["appendix"]) - src.cure() - - if(stage == 1) - if(prob(5)) - to_chat(affected_mob, "You feel a stinging pain in your abdomen!") - affected_mob.emote("me",1,"winces slightly.") - if(stage > 1) - if(prob(3)) - to_chat(affected_mob, "You feel a stabbing pain in your abdomen!") - affected_mob.emote("me",1,"winces painfully.") - affected_mob.adjustToxLoss(1) - if(stage > 2) - if(prob(1)) - if (affected_mob.nutrition > 100) - var/mob/living/carbon/human/H = affected_mob - H.delayed_vomit() - else - to_chat(affected_mob, "You gag as you want to throw up, but there's nothing in your stomach!") - affected_mob.Weaken(10) - affected_mob.adjustToxLoss(3) - if(stage > 3) - if(prob(1) && ishuman(affected_mob)) - var/mob/living/carbon/human/H = affected_mob - to_chat(H, "Your abdomen is a world of pain!") - H.Weaken(10) - - var/obj/item/organ/external/groin = H.get_organ("groin") - groin.sever_artery() - H.adjustToxLoss(25) - src.cure() +/datum/disease/appendicitis + form = "Condition" + name = "Appendicitis" + max_stages = 4 + spread = "Acute" + cure = "Surgery" + agent = "Appendix" + affected_species = list("Human") + permeability_mod = 1 + contagious_period = 9001 //slightly hacky, but hey! whatever works, right? + desc = "If left untreated the subject will become very weak, and may vomit often." + severity = "Medium" + longevity = 1000 + hidden = list(0, 1) + stage_minimum_age = 160 // at least 200 life ticks per stage + +/datum/disease/appendicitis/stage_act() + ..() + + if(istype(affected_mob,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = affected_mob + if(!H.internal_organs_by_name["appendix"]) + src.cure() + + if(stage == 1) + if(prob(5)) + to_chat(affected_mob, "You feel a stinging pain in your abdomen!") + affected_mob.emote("me",1,"winces slightly.") + if(stage > 1) + if(prob(3)) + to_chat(affected_mob, "You feel a stabbing pain in your abdomen!") + affected_mob.emote("me",1,"winces painfully.") + affected_mob.adjustToxLoss(1) + if(stage > 2) + if(prob(1)) + if (affected_mob.nutrition > 100) + var/mob/living/carbon/human/H = affected_mob + H.delayed_vomit() + else + to_chat(affected_mob, "You gag as you want to throw up, but there's nothing in your stomach!") + affected_mob.Weaken(10) + affected_mob.adjustToxLoss(3) + if(stage > 3) + if(prob(1) && ishuman(affected_mob)) + var/mob/living/carbon/human/H = affected_mob + to_chat(H, "Your abdomen is a world of pain!") + H.Weaken(10) + + var/obj/item/organ/external/groin = H.get_organ("groin") + groin.sever_artery() + H.adjustToxLoss(25) + src.cure() diff --git a/code/datums/diseases/beesease.dm b/code/datums/diseases/beesease.dm index 454e4f2ab29..315e7f856c0 100644 --- a/code/datums/diseases/beesease.dm +++ b/code/datums/diseases/beesease.dm @@ -1,31 +1,31 @@ -/* -/datum/disease/beesease - name = "Beesease" - max_stages = 5 - spread = "Contact" //ie shot bees - cure = "???" - cure_id = "???" - agent = "Bees" - affected_species = list("Human","Monkey") - curable = 0 - -/datum/disease/beesease/stage_act() - ..() - switch(stage) - if(1) - if(prob(2)) - to_chat(affected_mob, "You feel like something is moving inside of you!") - if(2) //also changes say, see say.dm - if(prob(2)) - to_chat(affected_mob, "You feel like something is moving inside of you!") - if(prob(2)) - to_chat(affected_mob, "BZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ") - if(3) - //Should give the bee spit verb - if(4) - //Plus bees now spit randomly - if(5) - //Plus if you die, you explode into bees - return -*/ -//Started working on it, but am too lazy to finish it today -- Urist +/* +/datum/disease/beesease + name = "Beesease" + max_stages = 5 + spread = "Contact" //ie shot bees + cure = "???" + cure_id = "???" + agent = "Bees" + affected_species = list("Human","Monkey") + curable = 0 + +/datum/disease/beesease/stage_act() + ..() + switch(stage) + if(1) + if(prob(2)) + to_chat(affected_mob, "You feel like something is moving inside of you!") + if(2) //also changes say, see say.dm + if(prob(2)) + to_chat(affected_mob, "You feel like something is moving inside of you!") + if(prob(2)) + to_chat(affected_mob, "BZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ") + if(3) + //Should give the bee spit verb + if(4) + //Plus bees now spit randomly + if(5) + //Plus if you die, you explode into bees + return +*/ +//Started working on it, but am too lazy to finish it today -- Urist diff --git a/code/datums/diseases/brainrot.dm b/code/datums/diseases/brainrot.dm index 48dd34ed531..d8c54593333 100644 --- a/code/datums/diseases/brainrot.dm +++ b/code/datums/diseases/brainrot.dm @@ -1,67 +1,67 @@ -/datum/disease/brainrot - name = "Brainrot" - max_stages = 4 - spread = "On contact" - spread_type = CONTACT_GENERAL - cure = "Alkysine" - cure_id = list("alkysine") - agent = "Cryptococcus Cosmosis" - affected_species = list("Human") - curable = 0 - cure_chance = 15//higher chance to cure, since two reagents are required - desc = "This disease destroys the braincells, causing brain fever, brain necrosis and general intoxication." - severity = "Major" - -/datum/disease/brainrot/stage_act() //Removed toxloss because damaging diseases are pretty horrible. Last round it killed the entire station because the cure didn't work -- Urist - ..() - switch(stage) - if(2) - if(prob(2)) - affected_mob.emote("blink") - if(prob(2)) - affected_mob.emote("yawn") - if(prob(2)) - to_chat(affected_mob, "Your don't feel like yourself.") - if(prob(5)) - affected_mob.adjustBrainLoss(1, 55) - affected_mob.updatehealth() - if(3) - if(prob(2)) - affected_mob.emote("stare") - if(prob(2)) - affected_mob.emote("drool") - if(prob(10))//shouldn't retard you to death now - affected_mob.adjustBrainLoss(2, 55) - affected_mob.updatehealth() - if(prob(2)) - to_chat(affected_mob, "You try to remember something important...but can't.") -/* if(prob(10)) - affected_mob.adjustToxLoss(3) - affected_mob.updatehealth() - if(prob(2)) - to_chat(affected_mob, "Your head hurts." */ - if(4) - if(prob(2)) - affected_mob.emote("stare") - if(prob(2)) - affected_mob.emote("drool") -/* if(prob(15)) - affected_mob.adjustToxLoss(4) - affected_mob.updatehealth() - if(prob(2)) - to_chat(affected_mob, "Your head hurts." */ - if(prob(15)) //shouldn't retard you to death now - affected_mob.adjustBrainLoss(3, 55) - affected_mob.updatehealth() - if(prob(2)) - to_chat(affected_mob, "A strange buzzing fills your head, removing all thoughts.") - if(prob(3)) - to_chat(affected_mob, "You lose consciousness...") - for(var/mob/O in viewers(affected_mob, null)) - O.show_message("[affected_mob] suddenly collapses", 1) - affected_mob.Paralyse(rand(5,10)) - if(prob(1)) - affected_mob.emote("snore") - if(prob(15)) - affected_mob.stuttering += 3 - return +/datum/disease/brainrot + name = "Brainrot" + max_stages = 4 + spread = "On contact" + spread_type = CONTACT_GENERAL + cure = "Alkysine" + cure_id = list("alkysine") + agent = "Cryptococcus Cosmosis" + affected_species = list("Human") + curable = 0 + cure_chance = 15//higher chance to cure, since two reagents are required + desc = "This disease destroys the braincells, causing brain fever, brain necrosis and general intoxication." + severity = "Major" + +/datum/disease/brainrot/stage_act() //Removed toxloss because damaging diseases are pretty horrible. Last round it killed the entire station because the cure didn't work -- Urist + ..() + switch(stage) + if(2) + if(prob(2)) + affected_mob.emote("blink") + if(prob(2)) + affected_mob.emote("yawn") + if(prob(2)) + to_chat(affected_mob, "Your don't feel like yourself.") + if(prob(5)) + affected_mob.adjustBrainLoss(1, 55) + affected_mob.updatehealth() + if(3) + if(prob(2)) + affected_mob.emote("stare") + if(prob(2)) + affected_mob.emote("drool") + if(prob(10))//shouldn't retard you to death now + affected_mob.adjustBrainLoss(2, 55) + affected_mob.updatehealth() + if(prob(2)) + to_chat(affected_mob, "You try to remember something important...but can't.") +/* if(prob(10)) + affected_mob.adjustToxLoss(3) + affected_mob.updatehealth() + if(prob(2)) + to_chat(affected_mob, "Your head hurts." */ + if(4) + if(prob(2)) + affected_mob.emote("stare") + if(prob(2)) + affected_mob.emote("drool") +/* if(prob(15)) + affected_mob.adjustToxLoss(4) + affected_mob.updatehealth() + if(prob(2)) + to_chat(affected_mob, "Your head hurts." */ + if(prob(15)) //shouldn't retard you to death now + affected_mob.adjustBrainLoss(3, 55) + affected_mob.updatehealth() + if(prob(2)) + to_chat(affected_mob, "A strange buzzing fills your head, removing all thoughts.") + if(prob(3)) + to_chat(affected_mob, "You lose consciousness...") + for(var/mob/O in viewers(affected_mob, null)) + O.show_message("[affected_mob] suddenly collapses", 1) + affected_mob.Paralyse(rand(5,10)) + if(prob(1)) + affected_mob.emote("snore") + if(prob(15)) + affected_mob.stuttering += 3 + return diff --git a/code/datums/diseases/cold.dm b/code/datums/diseases/cold.dm index 9a93a4dc137..721b6c9bfa8 100644 --- a/code/datums/diseases/cold.dm +++ b/code/datums/diseases/cold.dm @@ -1,66 +1,66 @@ -/datum/disease/cold - name = "The Cold" - max_stages = 3 - spread = "Airborne" - cure = "Rest & Deltamivir" - cure_id = "deltamivir" - agent = "XY-rhinovirus" - affected_species = list("Human", "Monkey") - permeability_mod = 0.5 - desc = "If left untreated the subject will contract the flu." - severity = "Minor" - -/datum/disease/cold/stage_act() - ..() - switch(stage) - if(2) -/* - if(affected_mob.sleeping && prob(40)) //removed until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return -*/ - if(affected_mob.lying && prob(40)) //changed FROM prob(10) until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return - if(prob(1) && prob(5)) - to_chat(affected_mob, "You feel better.") - cure() - return - if(prob(1)) - affected_mob.emote("sneeze") - if(prob(1)) - affected_mob.emote("cough") - if(prob(1)) - to_chat(affected_mob, "Your throat feels sore.") - if(prob(1)) - to_chat(affected_mob, "Mucous runs down the back of your throat.") - if(3) -/* - if(affected_mob.sleeping && prob(25)) //removed until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return -*/ - if(affected_mob.lying && prob(25)) //changed FROM prob(5) until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return - if(prob(1) && prob(1)) - to_chat(affected_mob, "You feel better.") - cure() - return - if(prob(1)) - affected_mob.emote("sneeze") - if(prob(1)) - affected_mob.emote("cough") - if(prob(1)) - to_chat(affected_mob, "Your throat feels sore.") - if(prob(1)) - to_chat(affected_mob, "Mucous runs down the back of your throat.") - if(prob(1) && prob(50)) - if(!affected_mob.resistances.Find(/datum/disease/flu)) - var/datum/disease/Flu = new /datum/disease/flu(0) - affected_mob.contract_disease(Flu,1) - cure() +/datum/disease/cold + name = "The Cold" + max_stages = 3 + spread = "Airborne" + cure = "Rest & Deltamivir" + cure_id = "deltamivir" + agent = "XY-rhinovirus" + affected_species = list("Human", "Monkey") + permeability_mod = 0.5 + desc = "If left untreated the subject will contract the flu." + severity = "Minor" + +/datum/disease/cold/stage_act() + ..() + switch(stage) + if(2) +/* + if(affected_mob.sleeping && prob(40)) //removed until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return +*/ + if(affected_mob.lying && prob(40)) //changed FROM prob(10) until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return + if(prob(1) && prob(5)) + to_chat(affected_mob, "You feel better.") + cure() + return + if(prob(1)) + affected_mob.emote("sneeze") + if(prob(1)) + affected_mob.emote("cough") + if(prob(1)) + to_chat(affected_mob, "Your throat feels sore.") + if(prob(1)) + to_chat(affected_mob, "Mucous runs down the back of your throat.") + if(3) +/* + if(affected_mob.sleeping && prob(25)) //removed until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return +*/ + if(affected_mob.lying && prob(25)) //changed FROM prob(5) until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return + if(prob(1) && prob(1)) + to_chat(affected_mob, "You feel better.") + cure() + return + if(prob(1)) + affected_mob.emote("sneeze") + if(prob(1)) + affected_mob.emote("cough") + if(prob(1)) + to_chat(affected_mob, "Your throat feels sore.") + if(prob(1)) + to_chat(affected_mob, "Mucous runs down the back of your throat.") + if(prob(1) && prob(50)) + if(!affected_mob.resistances.Find(/datum/disease/flu)) + var/datum/disease/Flu = new /datum/disease/flu(0) + affected_mob.contract_disease(Flu,1) + cure() diff --git a/code/datums/diseases/cold9.dm b/code/datums/diseases/cold9.dm index 5ef32f5ca83..02c242d508d 100644 --- a/code/datums/diseases/cold9.dm +++ b/code/datums/diseases/cold9.dm @@ -1,39 +1,39 @@ -/datum/disease/cold9 - name = "The Cold" - max_stages = 3 - spread = "On contact" - spread_type = CONTACT_GENERAL - cure = "Common Cold Anti-bodies & Deltamivir" - cure_id = "deltamivir" - agent = "ICE9-rhinovirus" - affected_species = list("Human") - desc = "If left untreated the subject will slow, as if partly frozen." - severity = "Moderate" - -/datum/disease/cold9/stage_act() - ..() - switch(stage) - if(2) - affected_mob.bodytemperature -= 10 - if(prob(1) && prob(10)) - to_chat(affected_mob, "You feel better.") - cure() - return - if(prob(1)) - affected_mob.emote("sneeze") - if(prob(1)) - affected_mob.emote("cough") - if(prob(1)) - to_chat(affected_mob, "Your throat feels sore.") - if(prob(5)) - to_chat(affected_mob, "You feel stiff.") - if(3) - affected_mob.bodytemperature -= 20 - if(prob(1)) - affected_mob.emote("sneeze") - if(prob(1)) - affected_mob.emote("cough") - if(prob(1)) - to_chat(affected_mob, "Your throat feels sore.") - if(prob(10)) - to_chat(affected_mob, "You feel stiff.") +/datum/disease/cold9 + name = "The Cold" + max_stages = 3 + spread = "On contact" + spread_type = CONTACT_GENERAL + cure = "Common Cold Anti-bodies & Deltamivir" + cure_id = "deltamivir" + agent = "ICE9-rhinovirus" + affected_species = list("Human") + desc = "If left untreated the subject will slow, as if partly frozen." + severity = "Moderate" + +/datum/disease/cold9/stage_act() + ..() + switch(stage) + if(2) + affected_mob.bodytemperature -= 10 + if(prob(1) && prob(10)) + to_chat(affected_mob, "You feel better.") + cure() + return + if(prob(1)) + affected_mob.emote("sneeze") + if(prob(1)) + affected_mob.emote("cough") + if(prob(1)) + to_chat(affected_mob, "Your throat feels sore.") + if(prob(5)) + to_chat(affected_mob, "You feel stiff.") + if(3) + affected_mob.bodytemperature -= 20 + if(prob(1)) + affected_mob.emote("sneeze") + if(prob(1)) + affected_mob.emote("cough") + if(prob(1)) + to_chat(affected_mob, "Your throat feels sore.") + if(prob(10)) + to_chat(affected_mob, "You feel stiff.") diff --git a/code/datums/diseases/dna_spread.dm b/code/datums/diseases/dna_spread.dm index 8e1fa696a11..841286d55b5 100644 --- a/code/datums/diseases/dna_spread.dm +++ b/code/datums/diseases/dna_spread.dm @@ -1,69 +1,69 @@ -/datum/disease/dnaspread - name = "Space Retrovirus" - max_stages = 4 - spread = "On contact" - spread_type = CONTACT_GENERAL - cure = "Ryetalyn" - cure_id = "ryetalyn" - curable = 1 - agent = "S4E1 retrovirus" - affected_species = list("Human") - var/list/original_dna = list() - var/transformed = 0 - desc = "This disease transplants the genetic code of the intial vector into new hosts." - severity = "Medium" - - -/datum/disease/dnaspread/stage_act() - ..() - switch(stage) - if(2 || 3) //Pretend to be a cold and give time to spread. - if(prob(8)) - affected_mob.emote("sneeze") - if(prob(8)) - affected_mob.emote("cough") - if(prob(1)) - to_chat(affected_mob, "Your muscles ache.") - if(prob(20)) - affected_mob.take_organ_damage(1) - if(prob(1)) - to_chat(affected_mob, "Your stomach hurts.") - if(prob(20)) - affected_mob.adjustToxLoss(2) - affected_mob.updatehealth() - if(4) - if(!src.transformed) - if ((!strain_data["name"]) || (!strain_data["UI"]) || (!strain_data["SE"])) - qdel(affected_mob.virus) - return - - //Save original dna for when the disease is cured. - src.original_dna["name"] = affected_mob.real_name - src.original_dna["UI"] = affected_mob.dna.UI.Copy() - src.original_dna["SE"] = affected_mob.dna.SE.Copy() - - to_chat(affected_mob, "You don't feel like yourself..") - var/list/newUI=strain_data["UI"] - var/list/newSE=strain_data["SE"] - affected_mob.UpdateAppearance(newUI.Copy()) - affected_mob.dna.SE = newSE.Copy() - affected_mob.dna.UpdateSE() - affected_mob.real_name = strain_data["name"] - domutcheck(affected_mob) - - src.transformed = 1 - src.carrier = 1 //Just chill out at stage 4 - - return - -/datum/disease/dnaspread/Destroy() - if ((original_dna["name"]) && (original_dna["UI"]) && (original_dna["SE"])) - var/list/newUI=original_dna["UI"] - var/list/newSE=original_dna["SE"] - affected_mob.UpdateAppearance(newUI.Copy()) - affected_mob.dna.SE = newSE.Copy() - affected_mob.dna.UpdateSE() - affected_mob.real_name = original_dna["name"] - - to_chat(affected_mob, "You feel more like yourself.") - return ..() +/datum/disease/dnaspread + name = "Space Retrovirus" + max_stages = 4 + spread = "On contact" + spread_type = CONTACT_GENERAL + cure = "Ryetalyn" + cure_id = "ryetalyn" + curable = 1 + agent = "S4E1 retrovirus" + affected_species = list("Human") + var/list/original_dna = list() + var/transformed = 0 + desc = "This disease transplants the genetic code of the intial vector into new hosts." + severity = "Medium" + + +/datum/disease/dnaspread/stage_act() + ..() + switch(stage) + if(2 || 3) //Pretend to be a cold and give time to spread. + if(prob(8)) + affected_mob.emote("sneeze") + if(prob(8)) + affected_mob.emote("cough") + if(prob(1)) + to_chat(affected_mob, "Your muscles ache.") + if(prob(20)) + affected_mob.take_organ_damage(1) + if(prob(1)) + to_chat(affected_mob, "Your stomach hurts.") + if(prob(20)) + affected_mob.adjustToxLoss(2) + affected_mob.updatehealth() + if(4) + if(!src.transformed) + if ((!strain_data["name"]) || (!strain_data["UI"]) || (!strain_data["SE"])) + qdel(affected_mob.virus) + return + + //Save original dna for when the disease is cured. + src.original_dna["name"] = affected_mob.real_name + src.original_dna["UI"] = affected_mob.dna.UI.Copy() + src.original_dna["SE"] = affected_mob.dna.SE.Copy() + + to_chat(affected_mob, "You don't feel like yourself..") + var/list/newUI=strain_data["UI"] + var/list/newSE=strain_data["SE"] + affected_mob.UpdateAppearance(newUI.Copy()) + affected_mob.dna.SE = newSE.Copy() + affected_mob.dna.UpdateSE() + affected_mob.real_name = strain_data["name"] + domutcheck(affected_mob) + + src.transformed = 1 + src.carrier = 1 //Just chill out at stage 4 + + return + +/datum/disease/dnaspread/Destroy() + if ((original_dna["name"]) && (original_dna["UI"]) && (original_dna["SE"])) + var/list/newUI=original_dna["UI"] + var/list/newSE=original_dna["SE"] + affected_mob.UpdateAppearance(newUI.Copy()) + affected_mob.dna.SE = newSE.Copy() + affected_mob.dna.UpdateSE() + affected_mob.real_name = original_dna["name"] + + to_chat(affected_mob, "You feel more like yourself.") + return ..() diff --git a/code/datums/diseases/fake_gbs.dm b/code/datums/diseases/fake_gbs.dm index eb4994ede92..1478d81caab 100644 --- a/code/datums/diseases/fake_gbs.dm +++ b/code/datums/diseases/fake_gbs.dm @@ -1,32 +1,32 @@ -/datum/disease/fake_gbs - name = "GBS" - max_stages = 5 - spread = "On contact" - spread_type = CONTACT_GENERAL - cure = "Synaptizine & Sulfur" - cure_id = list("synaptizine","sulfur") - agent = "Gravitokinetic Bipotential SADS-" - affected_species = list("Human", "Monkey") - desc = "If left untreated death will occur." - severity = "Major" - -/datum/disease/fake_gbs/stage_act() - ..() - switch(stage) - if(2) - if(prob(1)) - affected_mob.emote("sneeze") - if(3) - if(prob(5)) - affected_mob.emote("cough") - else if(prob(5)) - affected_mob.emote("gasp") - if(prob(10)) - to_chat(affected_mob, "You're starting to feel very weak...") - if(4) - if(prob(10)) - affected_mob.emote("cough") - - if(5) - if(prob(10)) - affected_mob.emote("cough") +/datum/disease/fake_gbs + name = "GBS" + max_stages = 5 + spread = "On contact" + spread_type = CONTACT_GENERAL + cure = "Synaptizine & Sulfur" + cure_id = list("synaptizine","sulfur") + agent = "Gravitokinetic Bipotential SADS-" + affected_species = list("Human", "Monkey") + desc = "If left untreated death will occur." + severity = "Major" + +/datum/disease/fake_gbs/stage_act() + ..() + switch(stage) + if(2) + if(prob(1)) + affected_mob.emote("sneeze") + if(3) + if(prob(5)) + affected_mob.emote("cough") + else if(prob(5)) + affected_mob.emote("gasp") + if(prob(10)) + to_chat(affected_mob, "You're starting to feel very weak...") + if(4) + if(prob(10)) + affected_mob.emote("cough") + + if(5) + if(prob(10)) + affected_mob.emote("cough") diff --git a/code/datums/diseases/flu.dm b/code/datums/diseases/flu.dm index 3691751ceaa..7787de3a2a8 100644 --- a/code/datums/diseases/flu.dm +++ b/code/datums/diseases/flu.dm @@ -1,66 +1,66 @@ -/datum/disease/flu - name = "The Flu" - max_stages = 3 - spread = "Airborne" - cure = "Deltamivir" - cure_id = "deltamivir" - cure_chance = 10 - agent = "H13N1 flu virion" - affected_species = list("Human", "Monkey") - permeability_mod = 0.75 - desc = "If left untreated the subject will feel quite unwell." - severity = "Medium" - -/datum/disease/flu/stage_act() - ..() - switch(stage) - if(2) -/* - if(affected_mob.sleeping && prob(20)) //removed until sleeping is fixed --Blaank - to_chat(affected_mob, "You feel better.") - stage-- - return -*/ - if(affected_mob.lying && prob(20)) //added until sleeping is fixed --Blaank - to_chat(affected_mob, "You feel better.") - stage-- - return - if(prob(1)) - affected_mob.emote("sneeze") - if(prob(1)) - affected_mob.emote("cough") - if(prob(1)) - to_chat(affected_mob, "Your muscles ache.") - if(prob(20)) - affected_mob.take_organ_damage(1) - if(prob(1)) - to_chat(affected_mob, "Your stomach hurts.") - if(prob(20)) - affected_mob.adjustToxLoss(1) - affected_mob.updatehealth() - - if(3) -/* - if(affected_mob.sleeping && prob(15)) //removed until sleeping is fixed - to_chat(affected_mob, "You feel better.") - stage-- - return -*/ - if(affected_mob.lying && prob(15)) //added until sleeping is fixed - to_chat(affected_mob, "You feel better.") - stage-- - return - if(prob(1)) - affected_mob.emote("sneeze") - if(prob(1)) - affected_mob.emote("cough") - if(prob(1)) - to_chat(affected_mob, "Your muscles ache.") - if(prob(20)) - affected_mob.take_organ_damage(1) - if(prob(1)) - to_chat(affected_mob, "Your stomach hurts.") - if(prob(20)) - affected_mob.adjustToxLoss(1) - affected_mob.updatehealth() - return +/datum/disease/flu + name = "The Flu" + max_stages = 3 + spread = "Airborne" + cure = "Deltamivir" + cure_id = "deltamivir" + cure_chance = 10 + agent = "H13N1 flu virion" + affected_species = list("Human", "Monkey") + permeability_mod = 0.75 + desc = "If left untreated the subject will feel quite unwell." + severity = "Medium" + +/datum/disease/flu/stage_act() + ..() + switch(stage) + if(2) +/* + if(affected_mob.sleeping && prob(20)) //removed until sleeping is fixed --Blaank + to_chat(affected_mob, "You feel better.") + stage-- + return +*/ + if(affected_mob.lying && prob(20)) //added until sleeping is fixed --Blaank + to_chat(affected_mob, "You feel better.") + stage-- + return + if(prob(1)) + affected_mob.emote("sneeze") + if(prob(1)) + affected_mob.emote("cough") + if(prob(1)) + to_chat(affected_mob, "Your muscles ache.") + if(prob(20)) + affected_mob.take_organ_damage(1) + if(prob(1)) + to_chat(affected_mob, "Your stomach hurts.") + if(prob(20)) + affected_mob.adjustToxLoss(1) + affected_mob.updatehealth() + + if(3) +/* + if(affected_mob.sleeping && prob(15)) //removed until sleeping is fixed + to_chat(affected_mob, "You feel better.") + stage-- + return +*/ + if(affected_mob.lying && prob(15)) //added until sleeping is fixed + to_chat(affected_mob, "You feel better.") + stage-- + return + if(prob(1)) + affected_mob.emote("sneeze") + if(prob(1)) + affected_mob.emote("cough") + if(prob(1)) + to_chat(affected_mob, "Your muscles ache.") + if(prob(20)) + affected_mob.take_organ_damage(1) + if(prob(1)) + to_chat(affected_mob, "Your stomach hurts.") + if(prob(20)) + affected_mob.adjustToxLoss(1) + affected_mob.updatehealth() + return diff --git a/code/datums/diseases/fluspanish.dm b/code/datums/diseases/fluspanish.dm index b95e8aa6c93..a41e5ba60fd 100644 --- a/code/datums/diseases/fluspanish.dm +++ b/code/datums/diseases/fluspanish.dm @@ -1,36 +1,36 @@ -/datum/disease/fluspanish - name = "Spanish inquisition Flu" - max_stages = 3 - spread = "Airborne" - cure = "Deltamivir & Anti-bodies to the common flu" - cure_id = "deltamivir" - cure_chance = 10 - agent = "1nqu1s1t10n flu virion" - affected_species = list("Human") - permeability_mod = 0.75 - desc = "If left untreated the subject will burn to death for being a heretic." - severity = "Serious" - -/datum/disease/inquisition/stage_act() - ..() - switch(stage) - if(2) - affected_mob.bodytemperature += 10 - if(prob(5)) - affected_mob.emote("sneeze") - if(prob(5)) - affected_mob.emote("cough") - if(prob(1)) - to_chat(affected_mob, "You're burning in your own skin!") - affected_mob.take_organ_damage(0,5) - - if(3) - affected_mob.bodytemperature += 20 - if(prob(5)) - affected_mob.emote("sneeze") - if(prob(5)) - affected_mob.emote("cough") - if(prob(5)) - to_chat(affected_mob, "You're burning in your own skin!") - affected_mob.take_organ_damage(0,5) - return +/datum/disease/fluspanish + name = "Spanish inquisition Flu" + max_stages = 3 + spread = "Airborne" + cure = "Deltamivir & Anti-bodies to the common flu" + cure_id = "deltamivir" + cure_chance = 10 + agent = "1nqu1s1t10n flu virion" + affected_species = list("Human") + permeability_mod = 0.75 + desc = "If left untreated the subject will burn to death for being a heretic." + severity = "Serious" + +/datum/disease/inquisition/stage_act() + ..() + switch(stage) + if(2) + affected_mob.bodytemperature += 10 + if(prob(5)) + affected_mob.emote("sneeze") + if(prob(5)) + affected_mob.emote("cough") + if(prob(1)) + to_chat(affected_mob, "You're burning in your own skin!") + affected_mob.take_organ_damage(0,5) + + if(3) + affected_mob.bodytemperature += 20 + if(prob(5)) + affected_mob.emote("sneeze") + if(prob(5)) + affected_mob.emote("cough") + if(prob(5)) + to_chat(affected_mob, "You're burning in your own skin!") + affected_mob.take_organ_damage(0,5) + return diff --git a/code/datums/diseases/gbs.dm b/code/datums/diseases/gbs.dm index 0325d016489..ffcda58ec50 100644 --- a/code/datums/diseases/gbs.dm +++ b/code/datums/diseases/gbs.dm @@ -1,40 +1,40 @@ -/datum/disease/gbs - name = "GBS" - max_stages = 5 - spread = "On contact" - spread_type = CONTACT_GENERAL - cure = "Synaptizine & Sulfur" - cure_id = list("synaptizine","sulfur") - cure_chance = 15//higher chance to cure, since two reagents are required - agent = "Gravitokinetic Bipotential SADS+" - affected_species = list("Human") - curable = 0 - permeability_mod = 1 - -/datum/disease/gbs/stage_act() - ..() - switch(stage) - if(2) - if(prob(45)) - affected_mob.adjustToxLoss(5) - affected_mob.updatehealth() - if(prob(1)) - affected_mob.emote("sneeze") - if(3) - if(prob(5)) - affected_mob.emote("cough") - else if(prob(5)) - affected_mob.emote("gasp") - if(prob(10)) - to_chat(affected_mob, "You're starting to feel very weak...") - if(4) - if(prob(10)) - affected_mob.emote("cough") - affected_mob.adjustToxLoss(5) - affected_mob.updatehealth() - if(5) - to_chat(affected_mob, "Your body feels as if it's trying to rip itself open...") - if(prob(50)) - affected_mob.gib() - else - return +/datum/disease/gbs + name = "GBS" + max_stages = 5 + spread = "On contact" + spread_type = CONTACT_GENERAL + cure = "Synaptizine & Sulfur" + cure_id = list("synaptizine","sulfur") + cure_chance = 15//higher chance to cure, since two reagents are required + agent = "Gravitokinetic Bipotential SADS+" + affected_species = list("Human") + curable = 0 + permeability_mod = 1 + +/datum/disease/gbs/stage_act() + ..() + switch(stage) + if(2) + if(prob(45)) + affected_mob.adjustToxLoss(5) + affected_mob.updatehealth() + if(prob(1)) + affected_mob.emote("sneeze") + if(3) + if(prob(5)) + affected_mob.emote("cough") + else if(prob(5)) + affected_mob.emote("gasp") + if(prob(10)) + to_chat(affected_mob, "You're starting to feel very weak...") + if(4) + if(prob(10)) + affected_mob.emote("cough") + affected_mob.adjustToxLoss(5) + affected_mob.updatehealth() + if(5) + to_chat(affected_mob, "Your body feels as if it's trying to rip itself open...") + if(prob(50)) + affected_mob.gib() + else + return diff --git a/code/datums/diseases/jungle_fever.dm b/code/datums/diseases/jungle_fever.dm index dba0c5f4a4f..f27ef32140b 100644 --- a/code/datums/diseases/jungle_fever.dm +++ b/code/datums/diseases/jungle_fever.dm @@ -1,12 +1,12 @@ -/datum/disease/jungle_fever - name = "Jungle Fever" - max_stages = 1 - cure = "None" - spread = "Bites" - spread_type = SPECIAL - affected_species = list("Monkey", "Human") - curable = 0 - desc = "monkeys with this disease will bite humans, causing humans to spontaneously mutate into a monkey." - severity = "Medium" - //stage_prob = 100 - agent = "Kongey Vibrion M-909" +/datum/disease/jungle_fever + name = "Jungle Fever" + max_stages = 1 + cure = "None" + spread = "Bites" + spread_type = SPECIAL + affected_species = list("Monkey", "Human") + curable = 0 + desc = "monkeys with this disease will bite humans, causing humans to spontaneously mutate into a monkey." + severity = "Medium" + //stage_prob = 100 + agent = "Kongey Vibrion M-909" diff --git a/code/datums/diseases/magnitis.dm b/code/datums/diseases/magnitis.dm index e0ed1fc1f99..fb675d99b68 100644 --- a/code/datums/diseases/magnitis.dm +++ b/code/datums/diseases/magnitis.dm @@ -1,93 +1,93 @@ -/datum/disease/magnitis - name = "Magnitis" - max_stages = 4 - spread = "Airborne" - cure = "Iron" - cure_id = "iron" - agent = "Fukkos Miracos" - affected_species = list("Human") - curable = 0 - permeability_mod = 0.75 - desc = "This disease disrupts the magnetic field of your body, making it act as if a powerful magnet. Injections of iron help stabilize the field." - severity = "Medium" - -/datum/disease/magnitis/stage_act() - ..() - switch(stage) - if(2) - if(prob(2)) - to_chat(affected_mob, "You feel a slight shock course through your body.") - if(prob(2)) - for(var/obj/M in orange(2,affected_mob)) - if(!M.anchored && (M.flags & CONDUCT)) - step_towards(M,affected_mob) - for(var/mob/living/silicon/S in orange(2,affected_mob)) - if(istype(S, /mob/living/silicon/ai)) continue - step_towards(S,affected_mob) - /* - if(M.x > affected_mob.x) - M.x-- - else if(M.x < affected_mob.x) - M.x++ - if(M.y > affected_mob.y) - M.y-- - else if(M.y < affected_mob.y) - M.y++ - */ - if(3) - if(prob(2)) - to_chat(affected_mob, "You feel a strong shock course through your body.") - if(prob(2)) - to_chat(affected_mob, "You feel like clowning around.") - if(prob(4)) - for(var/obj/M in orange(4,affected_mob)) - if(!M.anchored && (M.flags & CONDUCT)) - var/i - var/iter = rand(1,2) - for(i=0,i affected_mob.x) - M.x-=rand(1,min(3,M.x-affected_mob.x)) - else if(M.x < affected_mob.x) - M.x+=rand(1,min(3,affected_mob.x-M.x)) - if(M.y > affected_mob.y) - M.y-=rand(1,min(3,M.y-affected_mob.y)) - else if(M.y < affected_mob.y) - M.y+=rand(1,min(3,affected_mob.y-M.y)) - */ - if(4) - if(prob(2)) - to_chat(affected_mob, "You feel a powerful shock course through your body.") - if(prob(2)) - to_chat(affected_mob, "You query upon the nature of miracles.") - if(prob(8)) - for(var/obj/M in orange(6,affected_mob)) - if(!M.anchored && (M.flags & CONDUCT)) - var/i - var/iter = rand(1,3) - for(i=0,i affected_mob.x) - M.x-=rand(1,min(5,M.x-affected_mob.x)) - else if(M.x < affected_mob.x) - M.x+=rand(1,min(5,affected_mob.x-M.x)) - if(M.y > affected_mob.y) - M.y-=rand(1,min(5,M.y-affected_mob.y)) - else if(M.y < affected_mob.y) - M.y+=rand(1,min(5,affected_mob.y-M.y)) - */ - return +/datum/disease/magnitis + name = "Magnitis" + max_stages = 4 + spread = "Airborne" + cure = "Iron" + cure_id = "iron" + agent = "Fukkos Miracos" + affected_species = list("Human") + curable = 0 + permeability_mod = 0.75 + desc = "This disease disrupts the magnetic field of your body, making it act as if a powerful magnet. Injections of iron help stabilize the field." + severity = "Medium" + +/datum/disease/magnitis/stage_act() + ..() + switch(stage) + if(2) + if(prob(2)) + to_chat(affected_mob, "You feel a slight shock course through your body.") + if(prob(2)) + for(var/obj/M in orange(2,affected_mob)) + if(!M.anchored && (M.flags & CONDUCT)) + step_towards(M,affected_mob) + for(var/mob/living/silicon/S in orange(2,affected_mob)) + if(istype(S, /mob/living/silicon/ai)) continue + step_towards(S,affected_mob) + /* + if(M.x > affected_mob.x) + M.x-- + else if(M.x < affected_mob.x) + M.x++ + if(M.y > affected_mob.y) + M.y-- + else if(M.y < affected_mob.y) + M.y++ + */ + if(3) + if(prob(2)) + to_chat(affected_mob, "You feel a strong shock course through your body.") + if(prob(2)) + to_chat(affected_mob, "You feel like clowning around.") + if(prob(4)) + for(var/obj/M in orange(4,affected_mob)) + if(!M.anchored && (M.flags & CONDUCT)) + var/i + var/iter = rand(1,2) + for(i=0,i affected_mob.x) + M.x-=rand(1,min(3,M.x-affected_mob.x)) + else if(M.x < affected_mob.x) + M.x+=rand(1,min(3,affected_mob.x-M.x)) + if(M.y > affected_mob.y) + M.y-=rand(1,min(3,M.y-affected_mob.y)) + else if(M.y < affected_mob.y) + M.y+=rand(1,min(3,affected_mob.y-M.y)) + */ + if(4) + if(prob(2)) + to_chat(affected_mob, "You feel a powerful shock course through your body.") + if(prob(2)) + to_chat(affected_mob, "You query upon the nature of miracles.") + if(prob(8)) + for(var/obj/M in orange(6,affected_mob)) + if(!M.anchored && (M.flags & CONDUCT)) + var/i + var/iter = rand(1,3) + for(i=0,i affected_mob.x) + M.x-=rand(1,min(5,M.x-affected_mob.x)) + else if(M.x < affected_mob.x) + M.x+=rand(1,min(5,affected_mob.x-M.x)) + if(M.y > affected_mob.y) + M.y-=rand(1,min(5,M.y-affected_mob.y)) + else if(M.y < affected_mob.y) + M.y+=rand(1,min(5,affected_mob.y-M.y)) + */ + return diff --git a/code/datums/diseases/pierrot_throat.dm b/code/datums/diseases/pierrot_throat.dm index a22a295647c..66ba6aee6be 100644 --- a/code/datums/diseases/pierrot_throat.dm +++ b/code/datums/diseases/pierrot_throat.dm @@ -1,25 +1,25 @@ -/datum/disease/pierrot_throat - name = "Pierrot's Throat" - max_stages = 4 - spread = "Airborne" - cure = "A whole banana." - cure_id = "banana" - cure_chance = 75 - agent = "H0NI<42 Virus" - affected_species = list("Human") - permeability_mod = 0.75 - desc = "If left untreated the subject will probably drive others to insanity." - severity = "Medium" - longevity = 400 - -/datum/disease/pierrot_throat/stage_act() - ..() - switch(stage) - if(1) - if(prob(10)) to_chat(affected_mob, "You feel a little silly.") - if(2) - if(prob(10)) to_chat(affected_mob, "You start seeing rainbows.") - if(3) - if(prob(10)) to_chat(affected_mob, "Your thoughts are interrupted by a loud HONK!") - if(4) - if(prob(5)) affected_mob.say( pick( list("HONK!", "Honk!", "Honk.", "Honk?", "Honk!!", "Honk?!", "Honk...") ) ) +/datum/disease/pierrot_throat + name = "Pierrot's Throat" + max_stages = 4 + spread = "Airborne" + cure = "A whole banana." + cure_id = "banana" + cure_chance = 75 + agent = "H0NI<42 Virus" + affected_species = list("Human") + permeability_mod = 0.75 + desc = "If left untreated the subject will probably drive others to insanity." + severity = "Medium" + longevity = 400 + +/datum/disease/pierrot_throat/stage_act() + ..() + switch(stage) + if(1) + if(prob(10)) to_chat(affected_mob, "You feel a little silly.") + if(2) + if(prob(10)) to_chat(affected_mob, "You start seeing rainbows.") + if(3) + if(prob(10)) to_chat(affected_mob, "Your thoughts are interrupted by a loud HONK!") + if(4) + if(prob(5)) affected_mob.say( pick( list("HONK!", "Honk!", "Honk.", "Honk?", "Honk!!", "Honk?!", "Honk...") ) ) diff --git a/code/datums/diseases/plasmatoid.dm b/code/datums/diseases/plasmatoid.dm index 232371cd3b2..12e0476b631 100644 --- a/code/datums/diseases/plasmatoid.dm +++ b/code/datums/diseases/plasmatoid.dm @@ -1,5 +1,5 @@ -/datum/disease/plasmatoid - name = "Plasmatoid" - max_stages = 4 - cure = "None" +/datum/disease/plasmatoid + name = "Plasmatoid" + max_stages = 4 + cure = "None" affected_species = list("Monkey", "Human") \ No newline at end of file diff --git a/code/datums/diseases/retrovirus.dm b/code/datums/diseases/retrovirus.dm index 5c01349e245..ec778f17cc9 100644 --- a/code/datums/diseases/retrovirus.dm +++ b/code/datums/diseases/retrovirus.dm @@ -1,108 +1,108 @@ -/datum/disease/dna_retrovirus - name = "Retrovirus" - max_stages = 4 - spread = "Contact" - spread_type = CONTACT_GENERAL - cure = "Rest or an injection of ryetalyn" - cure_chance = 6 - agent = "" - affected_species = list("Human") - desc = "A DNA-altering retrovirus that scrambles the structural and unique enzymes of a host constantly." - severity = "Severe" - permeability_mod = 0.4 - stage_prob = 2 - var/SE - var/UI - var/restcure = 0 - New() - ..() - agent = "Virus class [pick("A","B","C","D","E","F")][pick("A","B","C","D","E","F")]-[rand(50,300)]" - if(prob(40)) - cure_id = list("ryetalyn") - cure_list = list("ryetalyn") - else - restcure = 1 - - - - -/datum/disease/dna_retrovirus/stage_act() - ..() - switch(stage) - if(1) - if(restcure) -/* - if(affected_mob.sleeping && prob(30)) //removed until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return -*/ - if(affected_mob.lying && prob(30)) //changed FROM prob(20) until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return - if (prob(8)) - to_chat(affected_mob, "Your head hurts.") - if (prob(9)) - to_chat(affected_mob, "You feel a tingling sensation in your chest.") - if (prob(9)) - to_chat(affected_mob, "You feel angry.") - if(2) - if(restcure) -/* - if(affected_mob.sleeping && prob(20)) //removed until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return -*/ - if(affected_mob.lying && prob(20)) //changed FROM prob(10) until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return - if (prob(8)) - to_chat(affected_mob, "Your skin feels loose.") - if (prob(10)) - to_chat(affected_mob, "You feel very strange.") - if (prob(4)) - to_chat(affected_mob, "You feel a stabbing pain in your head!") - affected_mob.Paralyse(2) - if (prob(4)) - to_chat(affected_mob, "Your stomach churns.") - if(3) - if(restcure) -/* - if(affected_mob.sleeping && prob(20)) //removed until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return -*/ - if(affected_mob.lying && prob(20)) //changed FROM prob(10) until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return - if (prob(10)) - to_chat(affected_mob, "Your entire body vibrates.") - - if (prob(35)) - if(prob(50)) - scramble(1, affected_mob, rand(15,45)) - else - scramble(0, affected_mob, rand(15,45)) - - if(4) - if(restcure) -/* - if(affected_mob.sleeping && prob(10)) //removed until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return -*/ - if(affected_mob.lying && prob(5)) //changed FROM prob(5) until sleeping is fixed - to_chat(affected_mob, "You feel better.") - cure() - return - if (prob(60)) - if(prob(50)) - scramble(1, affected_mob, rand(50,75)) - else - scramble(0, affected_mob, rand(50,75)) +/datum/disease/dna_retrovirus + name = "Retrovirus" + max_stages = 4 + spread = "Contact" + spread_type = CONTACT_GENERAL + cure = "Rest or an injection of ryetalyn" + cure_chance = 6 + agent = "" + affected_species = list("Human") + desc = "A DNA-altering retrovirus that scrambles the structural and unique enzymes of a host constantly." + severity = "Severe" + permeability_mod = 0.4 + stage_prob = 2 + var/SE + var/UI + var/restcure = 0 + New() + ..() + agent = "Virus class [pick("A","B","C","D","E","F")][pick("A","B","C","D","E","F")]-[rand(50,300)]" + if(prob(40)) + cure_id = list("ryetalyn") + cure_list = list("ryetalyn") + else + restcure = 1 + + + + +/datum/disease/dna_retrovirus/stage_act() + ..() + switch(stage) + if(1) + if(restcure) +/* + if(affected_mob.sleeping && prob(30)) //removed until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return +*/ + if(affected_mob.lying && prob(30)) //changed FROM prob(20) until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return + if (prob(8)) + to_chat(affected_mob, "Your head hurts.") + if (prob(9)) + to_chat(affected_mob, "You feel a tingling sensation in your chest.") + if (prob(9)) + to_chat(affected_mob, "You feel angry.") + if(2) + if(restcure) +/* + if(affected_mob.sleeping && prob(20)) //removed until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return +*/ + if(affected_mob.lying && prob(20)) //changed FROM prob(10) until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return + if (prob(8)) + to_chat(affected_mob, "Your skin feels loose.") + if (prob(10)) + to_chat(affected_mob, "You feel very strange.") + if (prob(4)) + to_chat(affected_mob, "You feel a stabbing pain in your head!") + affected_mob.Paralyse(2) + if (prob(4)) + to_chat(affected_mob, "Your stomach churns.") + if(3) + if(restcure) +/* + if(affected_mob.sleeping && prob(20)) //removed until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return +*/ + if(affected_mob.lying && prob(20)) //changed FROM prob(10) until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return + if (prob(10)) + to_chat(affected_mob, "Your entire body vibrates.") + + if (prob(35)) + if(prob(50)) + scramble(1, affected_mob, rand(15,45)) + else + scramble(0, affected_mob, rand(15,45)) + + if(4) + if(restcure) +/* + if(affected_mob.sleeping && prob(10)) //removed until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return +*/ + if(affected_mob.lying && prob(5)) //changed FROM prob(5) until sleeping is fixed + to_chat(affected_mob, "You feel better.") + cure() + return + if (prob(60)) + if(prob(50)) + scramble(1, affected_mob, rand(50,75)) + else + scramble(0, affected_mob, rand(50,75)) diff --git a/code/datums/diseases/robotic_transformation.dm b/code/datums/diseases/robotic_transformation.dm index 95be2c9a4fc..c6ee8370d5c 100644 --- a/code/datums/diseases/robotic_transformation.dm +++ b/code/datums/diseases/robotic_transformation.dm @@ -1,65 +1,65 @@ -//Nanomachines! - -/datum/disease/robotic_transformation - name = "Robotic Transformation" - max_stages = 5 - spread = "Syringe" - spread_type = SPECIAL - cure = "An injection of copper." - cure_id = list("copper") - cure_chance = 5 - agent = "R2D2 Nanomachines" - affected_species = list("Human") - desc = "This disease, actually acute nanomachine infection, converts the victim into a cyborg." - severity = "Major" - var/gibbed = 0 - -/datum/disease/robotic_transformation/stage_act() - ..() - switch(stage) - if(2) - if (prob(8)) - to_chat(affected_mob, "Your joints feel stiff.") - affected_mob.take_organ_damage(1) - if (prob(9)) - to_chat(affected_mob, "Beep...boop..") - if (prob(9)) - to_chat(affected_mob, "Bop...beeep...") - if(3) - if (prob(8)) - to_chat(affected_mob, "Your joints feel very stiff.") - affected_mob.take_organ_damage(1) - if (prob(8)) - affected_mob.say(pick("Beep, boop", "beep, beep!", "Boop...bop")) - if (prob(10)) - to_chat(affected_mob, "Your skin feels loose.") - affected_mob.take_organ_damage(5) - if (prob(4)) - to_chat(affected_mob, "You feel a stabbing pain in your head.") - affected_mob.Paralyse(2) - if (prob(4)) - to_chat(affected_mob, "You can feel something move...inside.") - if(4) - if (prob(10)) - to_chat(affected_mob, "Your skin feels very loose.") - affected_mob.take_organ_damage(8) - if (prob(20)) - affected_mob.say(pick("beep, beep!", "Boop bop boop beep.", "kkkiiiill mmme", "I wwwaaannntt tttoo dddiiieeee...")) - if (prob(8)) - to_chat(affected_mob, "You can feel... something...inside you.") - if(5) - to_chat(affected_mob, "Your skin feels as if it's about to burst off...") - affected_mob.adjustToxLoss(10) - affected_mob.updatehealth() - if(prob(40)) //So everyone can feel like robot Seth Brundle - if(src.gibbed != 0) return 0 - var/turf/T = find_loc(affected_mob) - gibs(T) - src.cure(0) - gibbed = 1 - var/mob/living/carbon/human/H = affected_mob - if(istype(H) && !jobban_isbanned(affected_mob, "Cyborg")) - H.Robotize() - else - affected_mob.death(1) - +//Nanomachines! + +/datum/disease/robotic_transformation + name = "Robotic Transformation" + max_stages = 5 + spread = "Syringe" + spread_type = SPECIAL + cure = "An injection of copper." + cure_id = list("copper") + cure_chance = 5 + agent = "R2D2 Nanomachines" + affected_species = list("Human") + desc = "This disease, actually acute nanomachine infection, converts the victim into a cyborg." + severity = "Major" + var/gibbed = 0 + +/datum/disease/robotic_transformation/stage_act() + ..() + switch(stage) + if(2) + if (prob(8)) + to_chat(affected_mob, "Your joints feel stiff.") + affected_mob.take_organ_damage(1) + if (prob(9)) + to_chat(affected_mob, "Beep...boop..") + if (prob(9)) + to_chat(affected_mob, "Bop...beeep...") + if(3) + if (prob(8)) + to_chat(affected_mob, "Your joints feel very stiff.") + affected_mob.take_organ_damage(1) + if (prob(8)) + affected_mob.say(pick("Beep, boop", "beep, beep!", "Boop...bop")) + if (prob(10)) + to_chat(affected_mob, "Your skin feels loose.") + affected_mob.take_organ_damage(5) + if (prob(4)) + to_chat(affected_mob, "You feel a stabbing pain in your head.") + affected_mob.Paralyse(2) + if (prob(4)) + to_chat(affected_mob, "You can feel something move...inside.") + if(4) + if (prob(10)) + to_chat(affected_mob, "Your skin feels very loose.") + affected_mob.take_organ_damage(8) + if (prob(20)) + affected_mob.say(pick("beep, beep!", "Boop bop boop beep.", "kkkiiiill mmme", "I wwwaaannntt tttoo dddiiieeee...")) + if (prob(8)) + to_chat(affected_mob, "You can feel... something...inside you.") + if(5) + to_chat(affected_mob, "Your skin feels as if it's about to burst off...") + affected_mob.adjustToxLoss(10) + affected_mob.updatehealth() + if(prob(40)) //So everyone can feel like robot Seth Brundle + if(src.gibbed != 0) return 0 + var/turf/T = find_loc(affected_mob) + gibs(T) + src.cure(0) + gibbed = 1 + var/mob/living/carbon/human/H = affected_mob + if(istype(H) && !jobban_isbanned(affected_mob, "Cyborg")) + H.Robotize() + else + affected_mob.death(1) + diff --git a/code/datums/diseases/wizarditis.dm b/code/datums/diseases/wizarditis.dm index 8441b52f10d..4b2c43e9189 100644 --- a/code/datums/diseases/wizarditis.dm +++ b/code/datums/diseases/wizarditis.dm @@ -1,125 +1,125 @@ -/datum/disease/wizarditis - name = "Wizarditis" - max_stages = 4 - spread = "Airborne" - cure = "The Manly Dorf" - cure_id = "manlydorf" - cure_chance = 100 - agent = "Rincewindus Vulgaris" - affected_species = list("Human") - curable = 1 - permeability_mod = 0.75 - desc = "Some speculate, that this virus is the cause of Wizard Federation existance. Subjects affected show the signs of mental retardation, yelling obscure sentences or total gibberish. On late stages subjects sometime express the feelings of inner power, and, cite, 'the ability to control the forces of cosmos themselves!' A gulp of strong, manly spirits usually reverts them to normal, humanlike, condition." - severity = "Major" - - -/* -BIRUZ BENNAR -SCYAR NILA - teleport -NEC CANTIO - dis techno -EI NATH - shocking grasp -AULIE OXIN FIERA - knock -TARCOL MINTI ZHERI - forcewall -STI KALY - blind -*/ - -/datum/disease/wizarditis/stage_act() - ..() - - switch(stage) - if(2) - if(prob(1)&&prob(50)) - affected_mob.say(pick("You shall not pass!", "Expeliarmus!", "By Merlins beard!", "Feel the power of the Dark Side!")) - if(prob(1)&&prob(50)) - var/arrows_var3 = pick("that you don't have enough mana.", "that the winds of magic are gone.", "an urge to summon familiar.") - to_chat(affected_mob, "You feel [arrows_var3]") - - - if(3) - if(prob(1)&&prob(50)) - affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!", "STI KALY!", "TARCOL MINTI ZHERI!")) - if(prob(1)&&prob(50)) - var/gunbuster = pick("the magic bubbling in your veins.","that this location gives you a +1 to INT.","an urge to summon familiar.") - to_chat(affected_mob, "You feel [gunbuster]") - - if(4) - - if(prob(1)) - affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!","STI KALY!","EI NATH!")) - return - if(prob(1)&&prob(50)) - var/arrows_var4 = pick("the tidal wave of raw power building inside.","that this location gives you a +2 to INT and +1 to WIS.","an urge to teleport.") - to_chat(affected_mob, "You feel [arrows_var4]") - spawn_wizard_clothes(50) - if(prob(1)&&prob(1)) - teleport() - return - - - -/datum/disease/wizarditis/proc/spawn_wizard_clothes(var/chance = 0) - if(istype(affected_mob, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = affected_mob - if(prob(chance)) - if(!istype(H.head, /obj/item/clothing/head/wizard)) - if(H.head) - H.drop_from_inventory(H.head) - H.head = new /obj/item/clothing/head/wizard(H) - H.head.layer = SCREEN_LAYER+0.01 - return - if(prob(chance)) - if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe)) - if(H.wear_suit) - H.drop_from_inventory(H.wear_suit) - H.wear_suit = new /obj/item/clothing/suit/wizrobe(H) - H.wear_suit.layer = SCREEN_LAYER+0.01 - return - if(prob(chance)) - if(!istype(H.shoes, /obj/item/clothing/shoes/sandal)) - if(H.shoes) - H.drop_from_inventory(H.shoes) - H.shoes = new /obj/item/clothing/shoes/sandal(H) - H.shoes.layer = SCREEN_LAYER+0.01 - return - else - var/mob/living/carbon/H = affected_mob - if(prob(chance)) - if(!istype(H.r_hand, /obj/item/staff)) - H.drop_r_hand() - H.put_in_r_hand( new /obj/item/staff(H) ) - return - return - - - -/datum/disease/wizarditis/proc/teleport() - var/list/theareas = new/list() - for(var/area/AR in orange(80, affected_mob)) - if(theareas.Find(AR) || AR.name == "Space") continue - theareas += AR - - if(!theareas) - return - - var/area/thearea = pick(theareas) - - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea.type)) - if(T.z != affected_mob.z) continue - if(T.name == "space") continue - if(!T.density) - var/clear = 1 - for(var/obj/O in T) - if(O.density) - clear = 0 - break - if(clear) - L+=T - - if(!L) - return - - affected_mob.say("SCYAR NILA [uppertext(thearea.name)]!") - affected_mob.forceMove(pick(L)) - - return +/datum/disease/wizarditis + name = "Wizarditis" + max_stages = 4 + spread = "Airborne" + cure = "The Manly Dorf" + cure_id = "manlydorf" + cure_chance = 100 + agent = "Rincewindus Vulgaris" + affected_species = list("Human") + curable = 1 + permeability_mod = 0.75 + desc = "Some speculate, that this virus is the cause of Wizard Federation existance. Subjects affected show the signs of mental retardation, yelling obscure sentences or total gibberish. On late stages subjects sometime express the feelings of inner power, and, cite, 'the ability to control the forces of cosmos themselves!' A gulp of strong, manly spirits usually reverts them to normal, humanlike, condition." + severity = "Major" + + +/* +BIRUZ BENNAR +SCYAR NILA - teleport +NEC CANTIO - dis techno +EI NATH - shocking grasp +AULIE OXIN FIERA - knock +TARCOL MINTI ZHERI - forcewall +STI KALY - blind +*/ + +/datum/disease/wizarditis/stage_act() + ..() + + switch(stage) + if(2) + if(prob(1)&&prob(50)) + affected_mob.say(pick("You shall not pass!", "Expeliarmus!", "By Merlins beard!", "Feel the power of the Dark Side!")) + if(prob(1)&&prob(50)) + var/arrows_var3 = pick("that you don't have enough mana.", "that the winds of magic are gone.", "an urge to summon familiar.") + to_chat(affected_mob, "You feel [arrows_var3]") + + + if(3) + if(prob(1)&&prob(50)) + affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!", "STI KALY!", "TARCOL MINTI ZHERI!")) + if(prob(1)&&prob(50)) + var/gunbuster = pick("the magic bubbling in your veins.","that this location gives you a +1 to INT.","an urge to summon familiar.") + to_chat(affected_mob, "You feel [gunbuster]") + + if(4) + + if(prob(1)) + affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!","STI KALY!","EI NATH!")) + return + if(prob(1)&&prob(50)) + var/arrows_var4 = pick("the tidal wave of raw power building inside.","that this location gives you a +2 to INT and +1 to WIS.","an urge to teleport.") + to_chat(affected_mob, "You feel [arrows_var4]") + spawn_wizard_clothes(50) + if(prob(1)&&prob(1)) + teleport() + return + + + +/datum/disease/wizarditis/proc/spawn_wizard_clothes(var/chance = 0) + if(istype(affected_mob, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = affected_mob + if(prob(chance)) + if(!istype(H.head, /obj/item/clothing/head/wizard)) + if(H.head) + H.drop_from_inventory(H.head) + H.head = new /obj/item/clothing/head/wizard(H) + H.head.layer = SCREEN_LAYER+0.01 + return + if(prob(chance)) + if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe)) + if(H.wear_suit) + H.drop_from_inventory(H.wear_suit) + H.wear_suit = new /obj/item/clothing/suit/wizrobe(H) + H.wear_suit.layer = SCREEN_LAYER+0.01 + return + if(prob(chance)) + if(!istype(H.shoes, /obj/item/clothing/shoes/sandal)) + if(H.shoes) + H.drop_from_inventory(H.shoes) + H.shoes = new /obj/item/clothing/shoes/sandal(H) + H.shoes.layer = SCREEN_LAYER+0.01 + return + else + var/mob/living/carbon/H = affected_mob + if(prob(chance)) + if(!istype(H.r_hand, /obj/item/staff)) + H.drop_r_hand() + H.put_in_r_hand( new /obj/item/staff(H) ) + return + return + + + +/datum/disease/wizarditis/proc/teleport() + var/list/theareas = new/list() + for(var/area/AR in orange(80, affected_mob)) + if(theareas.Find(AR) || AR.name == "Space") continue + theareas += AR + + if(!theareas) + return + + var/area/thearea = pick(theareas) + + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + if(T.z != affected_mob.z) continue + if(T.name == "space") continue + if(!T.density) + var/clear = 1 + for(var/obj/O in T) + if(O.density) + clear = 0 + break + if(clear) + L+=T + + if(!L) + return + + affected_mob.say("SCYAR NILA [uppertext(thearea.name)]!") + affected_mob.forceMove(pick(L)) + + return diff --git a/code/datums/expansions/expansion.dm b/code/datums/expansions/expansion.dm index 9ca4fc45136..7655ffdfaea 100644 --- a/code/datums/expansions/expansion.dm +++ b/code/datums/expansions/expansion.dm @@ -1,40 +1,40 @@ -/datum/expansion - var/datum/holder = null // The holder - -/datum/expansion/New(var/datum/holder) - if(!istype(holder)) - CRASH("Invalid holder.") - src.holder = holder - ..() - -/datum/expansion/Destroy() - holder = null - return ..() - -/datum/expansion/CanUseTopic(var/mob/user) - return holder && user ? STATUS_INTERACTIVE : STATUS_CLOSE - -/datum/expansion/Topic() - if(..()) - return 1 - if(CanUseTopic(usr) != STATUS_INTERACTIVE) - return 1 - return 0 - -/obj - var/list/datum/expansion/expansions - -/obj/Destroy() - if (LAZYLEN(expansions)) - for(var/expansion in expansions) - qdel(expansions[expansion]) - - LAZYCLEARLIST(expansions) - expansions = null - return ..() - -/obj/proc/set_expansion(var/type, var/instance) - LAZYINITLIST(expansions) - if(expansions[type]) - qdel(expansions[type]) - expansions[type] = instance +/datum/expansion + var/datum/holder = null // The holder + +/datum/expansion/New(var/datum/holder) + if(!istype(holder)) + CRASH("Invalid holder.") + src.holder = holder + ..() + +/datum/expansion/Destroy() + holder = null + return ..() + +/datum/expansion/CanUseTopic(var/mob/user) + return holder && user ? STATUS_INTERACTIVE : STATUS_CLOSE + +/datum/expansion/Topic() + if(..()) + return 1 + if(CanUseTopic(usr) != STATUS_INTERACTIVE) + return 1 + return 0 + +/obj + var/list/datum/expansion/expansions + +/obj/Destroy() + if (LAZYLEN(expansions)) + for(var/expansion in expansions) + qdel(expansions[expansion]) + + LAZYCLEARLIST(expansions) + expansions = null + return ..() + +/obj/proc/set_expansion(var/type, var/instance) + LAZYINITLIST(expansions) + if(expansions[type]) + qdel(expansions[type]) + expansions[type] = instance diff --git a/code/datums/expansions/multitool/_multitool.dm b/code/datums/expansions/multitool/_multitool.dm index 6ec2a8657e7..fd581a007f9 100644 --- a/code/datums/expansions/multitool/_multitool.dm +++ b/code/datums/expansions/multitool/_multitool.dm @@ -1,3 +1,3 @@ -#define MT_NOACTION 0 -#define MT_REFRESH 1 -#define MT_CLOSE 2 +#define MT_NOACTION 0 +#define MT_REFRESH 1 +#define MT_CLOSE 2 diff --git a/code/datums/expansions/multitool/circuitboards/circuitboards.dm b/code/datums/expansions/multitool/circuitboards/circuitboards.dm index 318267a6a11..5d119a2b9de 100644 --- a/code/datums/expansions/multitool/circuitboards/circuitboards.dm +++ b/code/datums/expansions/multitool/circuitboards/circuitboards.dm @@ -1,5 +1,5 @@ -/datum/expansion/multitool/circuitboards/CanUseTopic(var/mob/user) - if(isAI(user)) // No remote AI access - return STATUS_CLOSE - - return ..() +/datum/expansion/multitool/circuitboards/CanUseTopic(var/mob/user) + if(isAI(user)) // No remote AI access + return STATUS_CLOSE + + return ..() diff --git a/code/datums/expansions/multitool/circuitboards/stationalert.dm b/code/datums/expansions/multitool/circuitboards/stationalert.dm index 5cf7dee2b72..d1b061c7c18 100644 --- a/code/datums/expansions/multitool/circuitboards/stationalert.dm +++ b/code/datums/expansions/multitool/circuitboards/stationalert.dm @@ -1,29 +1,29 @@ -/datum/expansion/multitool/circuitboards/stationalert/get_interact_window(var/obj/item/device/multitool/M, var/mob/user) - var/obj/item/circuitboard/stationalert/SA = holder - . += "Alarm Sources
    " - . += "" - for(var/datum/alarm_handler/AH in SSalarm.all_handlers) - . += "" - . += "" - if(AH in SA.alarm_handlers) - . += "" - else - . += "" - . += "" - . += "
    [AH.category]ActiveInactivateInactiveActivate
    " - -/datum/expansion/multitool/circuitboards/stationalert/on_topic(href, href_list, user) - var/obj/item/circuitboard/stationalert/SA = holder - if(href_list["add"]) - var/datum/alarm_handler/AH = locate(href_list["add"]) in SSalarm.all_handlers - if(AH) - SA.alarm_handlers |= AH - return MT_REFRESH - - if(href_list["remove"]) - var/datum/alarm_handler/AH = locate(href_list["remove"]) in SSalarm.all_handlers - if(AH) - SA.alarm_handlers -= AH - return MT_REFRESH - - return ..() +/datum/expansion/multitool/circuitboards/stationalert/get_interact_window(var/obj/item/device/multitool/M, var/mob/user) + var/obj/item/circuitboard/stationalert/SA = holder + . += "Alarm Sources
    " + . += "" + for(var/datum/alarm_handler/AH in SSalarm.all_handlers) + . += "" + . += "" + if(AH in SA.alarm_handlers) + . += "" + else + . += "" + . += "" + . += "
    [AH.category]ActiveInactivateInactiveActivate
    " + +/datum/expansion/multitool/circuitboards/stationalert/on_topic(href, href_list, user) + var/obj/item/circuitboard/stationalert/SA = holder + if(href_list["add"]) + var/datum/alarm_handler/AH = locate(href_list["add"]) in SSalarm.all_handlers + if(AH) + SA.alarm_handlers |= AH + return MT_REFRESH + + if(href_list["remove"]) + var/datum/alarm_handler/AH = locate(href_list["remove"]) in SSalarm.all_handlers + if(AH) + SA.alarm_handlers -= AH + return MT_REFRESH + + return ..() diff --git a/code/datums/expansions/multitool/items/cable.dm b/code/datums/expansions/multitool/items/cable.dm index fbb647e877a..c37351f60fe 100644 --- a/code/datums/expansions/multitool/items/cable.dm +++ b/code/datums/expansions/multitool/items/cable.dm @@ -1,25 +1,25 @@ -/obj/item/stack/cable_coil/Initialize() - set_expansion(/datum/expansion/multitool, new/datum/expansion/multitool/items/cable(src)) - . = ..() - -/datum/expansion/multitool/items/cable/get_interact_window(var/obj/item/device/multitool/M, var/mob/user) - var/obj/item/stack/cable_coil/cable_coil = holder - . += "Available Colors
    " - . += "" - for(var/cable_color in possible_cable_coil_colours) - . += "" - . += "" - if(cable_coil.color == possible_cable_coil_colours[cable_color]) - . += "" - else - . += "" - . += "" - . += "
    [cable_color]SelectedSelect
    " - -/datum/expansion/multitool/items/cable/on_topic(href, href_list, user) - var/obj/item/stack/cable_coil/cable_coil = holder - if(href_list["select_color"] && href_list["select_color"] in possible_cable_coil_colours) - cable_coil.set_cable_color(href_list["select_color"], user) - return MT_REFRESH - - return ..() +/obj/item/stack/cable_coil/Initialize() + set_expansion(/datum/expansion/multitool, new/datum/expansion/multitool/items/cable(src)) + . = ..() + +/datum/expansion/multitool/items/cable/get_interact_window(var/obj/item/device/multitool/M, var/mob/user) + var/obj/item/stack/cable_coil/cable_coil = holder + . += "Available Colors
    " + . += "" + for(var/cable_color in possible_cable_coil_colours) + . += "" + . += "" + if(cable_coil.color == possible_cable_coil_colours[cable_color]) + . += "" + else + . += "" + . += "" + . += "
    [cable_color]SelectedSelect
    " + +/datum/expansion/multitool/items/cable/on_topic(href, href_list, user) + var/obj/item/stack/cable_coil/cable_coil = holder + if(href_list["select_color"] && href_list["select_color"] in possible_cable_coil_colours) + cable_coil.set_cable_color(href_list["select_color"], user) + return MT_REFRESH + + return ..() diff --git a/code/datums/expansions/multitool/items/items.dm b/code/datums/expansions/multitool/items/items.dm index 75bb67bca65..54cc971ccf6 100644 --- a/code/datums/expansions/multitool/items/items.dm +++ b/code/datums/expansions/multitool/items/items.dm @@ -1,5 +1,5 @@ -/datum/expansion/multitool/items/CanUseTopic(var/mob/user) - if(isAI(user)) // No remote AI access - return STATUS_CLOSE - - return ..() +/datum/expansion/multitool/items/CanUseTopic(var/mob/user) + if(isAI(user)) // No remote AI access + return STATUS_CLOSE + + return ..() diff --git a/code/datums/expansions/multitool/machinery/cloning.dm b/code/datums/expansions/multitool/machinery/cloning.dm index 724a23d3898..f810b27047a 100644 --- a/code/datums/expansions/multitool/machinery/cloning.dm +++ b/code/datums/expansions/multitool/machinery/cloning.dm @@ -1,28 +1,28 @@ -/datum/expansion/multitool/cryo/get_interact_window(var/obj/item/device/multitool/M, var/mob/user) - . += buffer(M) - . += "
    Connected Cloning Pods:
    " - var/obj/machinery/computer/cloning/C = holder - for(var/atom/cloning_pod in C.pods) - . += "[cloning_pod.name]
    " - -/datum/expansion/multitool/cryo/receive_buffer(var/obj/item/device/multitool/M, var/atom/buffer, var/mob/user) - var/obj/machinery/clonepod/P = buffer - var/obj/machinery/computer/cloning/C = holder - - if(!istype(P)) - to_chat(user, "No valid connection data in \the [M] buffer.") - return MT_NOACTION - - var/is_connected = (P in C.pods) - if(!is_connected) - if(C.connect_pod(P)) - to_chat(user, "You connect \the [P] to \the [C].") - else - to_chat(user, "You failed to connect \the [P] to \the [C].") - return MT_REFRESH - - if(C.release_pod(P)) - to_chat(user, "You disconnect \the [P] from \the [C].") - else - to_chat(user, "You failed to disconnect \the [P] from \the [C].") - return MT_REFRESH +/datum/expansion/multitool/cryo/get_interact_window(var/obj/item/device/multitool/M, var/mob/user) + . += buffer(M) + . += "
    Connected Cloning Pods:
    " + var/obj/machinery/computer/cloning/C = holder + for(var/atom/cloning_pod in C.pods) + . += "[cloning_pod.name]
    " + +/datum/expansion/multitool/cryo/receive_buffer(var/obj/item/device/multitool/M, var/atom/buffer, var/mob/user) + var/obj/machinery/clonepod/P = buffer + var/obj/machinery/computer/cloning/C = holder + + if(!istype(P)) + to_chat(user, "No valid connection data in \the [M] buffer.") + return MT_NOACTION + + var/is_connected = (P in C.pods) + if(!is_connected) + if(C.connect_pod(P)) + to_chat(user, "You connect \the [P] to \the [C].") + else + to_chat(user, "You failed to connect \the [P] to \the [C].") + return MT_REFRESH + + if(C.release_pod(P)) + to_chat(user, "You disconnect \the [P] from \the [C].") + else + to_chat(user, "You failed to disconnect \the [P] from \the [C].") + return MT_REFRESH diff --git a/code/datums/expansions/multitool/multitool.dm b/code/datums/expansions/multitool/multitool.dm index 27b8fc4cde3..6b319eb5451 100644 --- a/code/datums/expansions/multitool/multitool.dm +++ b/code/datums/expansions/multitool/multitool.dm @@ -1,89 +1,89 @@ -/datum/expansion/multitool - var/window_x = 370 - var/window_y = 470 - var/list/interact_predicates - -/datum/expansion/multitool/New(var/atom/holder, var/list/can_interact_predicates) - ..() - interact_predicates = can_interact_predicates ? can_interact_predicates : list() - -/datum/expansion/multitool/Destroy() - interact_predicates.Cut() - return ..() - -/datum/expansion/multitool/proc/interact(var/obj/item/device/multitool/M, var/mob/user) - if(CanUseTopic(user) != STATUS_INTERACTIVE) - return - - var/html = get_interact_window(M, user) - if(html) - var/datum/browser/popup = new(usr, "multitool", "Multitool Menu", window_x, window_y) - popup.set_content(html) - popup.set_title_image(user.browse_rsc_icon(M.icon, M.icon_state)) - popup.open() - else - close_window(usr) - -/datum/expansion/multitool/proc/get_interact_window(var/obj/item/device/multitool/M, var/mob/user) - return - -/datum/expansion/multitool/proc/close_window(var/mob/user) - user << browse(null, "window=multitool") - -/datum/expansion/multitool/proc/buffer(var/obj/item/device/multitool/multitool) - . += "Buffer Memory:
    " - var/buffer_name = multitool.get_buffer_name() - if(buffer_name) - . += "[buffer_name] Send Purge
    " - else - . += "No connection stored in the buffer." - -/datum/expansion/multitool/CanUseTopic(var/mob/user) - . = ..() - if(. == STATUS_CLOSE) - return - - if(!user.get_multitool()) - return STATUS_CLOSE - - if(!all_predicates_true(list(holder, user), interact_predicates)) - return STATUS_CLOSE - - var/datum/host = holder.ui_host() - return user.default_can_use_topic(host) - -/datum/expansion/multitool/Topic(href, href_list) - if(..()) - close_window(usr) - return 1 - - var/mob/user = usr - var/obj/item/device/multitool/M = user.get_multitool() - if(href_list["send"]) - var/atom/buffer = locate(href_list["send"]) - . = send_buffer(M, buffer, user) - else if(href_list["purge"]) - M.set_buffer(null) - . = MT_REFRESH - else - . = on_topic(href, href_list, user) - - switch(.) - if(MT_REFRESH) - interact(M, user) - if(MT_CLOSE) - close_window(user) - return 1 - -/datum/expansion/multitool/proc/on_topic(href, href_list, user) - return MT_NOACTION - -/datum/expansion/multitool/proc/send_buffer(var/obj/item/device/multitool/M, var/atom/buffer, var/mob/user) - if(M.get_buffer() == buffer && buffer) - receive_buffer(M, buffer, user) - else if(!buffer) - to_chat(user, "Unable to acquire data from the buffered object. Purging from memory.") - return MT_REFRESH - -/datum/expansion/multitool/proc/receive_buffer(var/obj/item/device/multitool/M, var/atom/buffer, var/mob/user) - return +/datum/expansion/multitool + var/window_x = 370 + var/window_y = 470 + var/list/interact_predicates + +/datum/expansion/multitool/New(var/atom/holder, var/list/can_interact_predicates) + ..() + interact_predicates = can_interact_predicates ? can_interact_predicates : list() + +/datum/expansion/multitool/Destroy() + interact_predicates.Cut() + return ..() + +/datum/expansion/multitool/proc/interact(var/obj/item/device/multitool/M, var/mob/user) + if(CanUseTopic(user) != STATUS_INTERACTIVE) + return + + var/html = get_interact_window(M, user) + if(html) + var/datum/browser/popup = new(usr, "multitool", "Multitool Menu", window_x, window_y) + popup.set_content(html) + popup.set_title_image(user.browse_rsc_icon(M.icon, M.icon_state)) + popup.open() + else + close_window(usr) + +/datum/expansion/multitool/proc/get_interact_window(var/obj/item/device/multitool/M, var/mob/user) + return + +/datum/expansion/multitool/proc/close_window(var/mob/user) + user << browse(null, "window=multitool") + +/datum/expansion/multitool/proc/buffer(var/obj/item/device/multitool/multitool) + . += "Buffer Memory:
    " + var/buffer_name = multitool.get_buffer_name() + if(buffer_name) + . += "[buffer_name] Send Purge
    " + else + . += "No connection stored in the buffer." + +/datum/expansion/multitool/CanUseTopic(var/mob/user) + . = ..() + if(. == STATUS_CLOSE) + return + + if(!user.get_multitool()) + return STATUS_CLOSE + + if(!all_predicates_true(list(holder, user), interact_predicates)) + return STATUS_CLOSE + + var/datum/host = holder.ui_host() + return user.default_can_use_topic(host) + +/datum/expansion/multitool/Topic(href, href_list) + if(..()) + close_window(usr) + return 1 + + var/mob/user = usr + var/obj/item/device/multitool/M = user.get_multitool() + if(href_list["send"]) + var/atom/buffer = locate(href_list["send"]) + . = send_buffer(M, buffer, user) + else if(href_list["purge"]) + M.set_buffer(null) + . = MT_REFRESH + else + . = on_topic(href, href_list, user) + + switch(.) + if(MT_REFRESH) + interact(M, user) + if(MT_CLOSE) + close_window(user) + return 1 + +/datum/expansion/multitool/proc/on_topic(href, href_list, user) + return MT_NOACTION + +/datum/expansion/multitool/proc/send_buffer(var/obj/item/device/multitool/M, var/atom/buffer, var/mob/user) + if(M.get_buffer() == buffer && buffer) + receive_buffer(M, buffer, user) + else if(!buffer) + to_chat(user, "Unable to acquire data from the buffered object. Purging from memory.") + return MT_REFRESH + +/datum/expansion/multitool/proc/receive_buffer(var/obj/item/device/multitool/M, var/atom/buffer, var/mob/user) + return diff --git a/code/datums/expansions/multitool/store.dm b/code/datums/expansions/multitool/store.dm index 07b4be7e5f5..14e8080fa70 100644 --- a/code/datums/expansions/multitool/store.dm +++ b/code/datums/expansions/multitool/store.dm @@ -1,10 +1,10 @@ -/datum/expansion/multitool/store/interact(var/obj/item/device/multitool/M, var/mob/user) - if(CanUseTopic(user) != STATUS_INTERACTIVE) - return - - if(M.get_buffer() == holder) - M.set_buffer(null) - to_chat(user, "You purge the connection data of \the [holder] from \the [M].") - else - M.set_buffer(holder) - to_chat(user, "You load connection data from \the [holder] to \the [M].") +/datum/expansion/multitool/store/interact(var/obj/item/device/multitool/M, var/mob/user) + if(CanUseTopic(user) != STATUS_INTERACTIVE) + return + + if(M.get_buffer() == holder) + M.set_buffer(null) + to_chat(user, "You purge the connection data of \the [holder] from \the [M].") + else + M.set_buffer(holder) + to_chat(user, "You load connection data from \the [holder] to \the [M].") diff --git a/code/datums/helper_datums/construction_datum.dm b/code/datums/helper_datums/construction_datum.dm index 0bda144e8aa..1e6fc370a60 100644 --- a/code/datums/helper_datums/construction_datum.dm +++ b/code/datums/helper_datums/construction_datum.dm @@ -1,105 +1,105 @@ -#define FORWARD -1 -#define BACKWARD 1 - -/datum/construction - var/list/steps - var/atom/holder - var/result - var/list/steps_desc - - New(atom) - ..() - holder = atom - if(!holder) //don't want this without a holder - spawn - qdel(src) - set_desc(steps.len) - return - - proc/next_step() - steps.len-- - if(!steps.len) - spawn_result() - else - set_desc(steps.len) - return - - proc/action(atom/used_atom,mob/user as mob) - return - - proc/check_step(atom/used_atom,mob/user as mob) //check last step only - var/valid_step = is_right_key(used_atom) - if(valid_step) - if(custom_action(valid_step, used_atom, user)) - next_step() - return 1 - return 0 - - proc/is_right_key(atom/used_atom) // returns current step num if used_atom is of the right type. - var/list/L = steps[steps.len] - if(istype(used_atom, L["key"])) - return steps.len - return 0 - - proc/custom_action(step, used_atom, user) - return 1 - - proc/check_all_steps(atom/used_atom,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(used_atom, L["key"])) - if(custom_action(i, used_atom, user)) - steps[i]=null;//stupid byond list from list removal... - listclearnulls(steps); - if(!steps.len) - spawn_result() - return 1 - return 0 - - - proc/spawn_result() - if(result) - new result(get_turf(holder)) - spawn() - qdel(holder) - return - - proc/set_desc(index as num) - var/list/step = steps[index] - holder.desc = step["desc"] - return - -/datum/construction/reversible - var/index - - New(atom) - ..() - index = steps.len - return - - proc/update_index(diff as num) - index+=diff - if(index==0) - spawn_result() - else - set_desc(index) - return - - is_right_key(atom/used_atom) // returns index step - var/list/L = steps[index] - if(istype(used_atom, L["key"])) - return FORWARD //to the first step -> forward - else if(L["backkey"] && istype(used_atom, L["backkey"])) - return BACKWARD //to the last step -> backwards - return 0 - - check_step(atom/used_atom,mob/user as mob) - var/diff = is_right_key(used_atom) - if(diff) - if(custom_action(index, diff, used_atom, user)) - update_index(diff) - return 1 - return 0 - - custom_action(index, diff, used_atom, user) +#define FORWARD -1 +#define BACKWARD 1 + +/datum/construction + var/list/steps + var/atom/holder + var/result + var/list/steps_desc + + New(atom) + ..() + holder = atom + if(!holder) //don't want this without a holder + spawn + qdel(src) + set_desc(steps.len) + return + + proc/next_step() + steps.len-- + if(!steps.len) + spawn_result() + else + set_desc(steps.len) + return + + proc/action(atom/used_atom,mob/user as mob) + return + + proc/check_step(atom/used_atom,mob/user as mob) //check last step only + var/valid_step = is_right_key(used_atom) + if(valid_step) + if(custom_action(valid_step, used_atom, user)) + next_step() + return 1 + return 0 + + proc/is_right_key(atom/used_atom) // returns current step num if used_atom is of the right type. + var/list/L = steps[steps.len] + if(istype(used_atom, L["key"])) + return steps.len + return 0 + + proc/custom_action(step, used_atom, user) + return 1 + + proc/check_all_steps(atom/used_atom,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(used_atom, L["key"])) + if(custom_action(i, used_atom, user)) + steps[i]=null;//stupid byond list from list removal... + listclearnulls(steps); + if(!steps.len) + spawn_result() + return 1 + return 0 + + + proc/spawn_result() + if(result) + new result(get_turf(holder)) + spawn() + qdel(holder) + return + + proc/set_desc(index as num) + var/list/step = steps[index] + holder.desc = step["desc"] + return + +/datum/construction/reversible + var/index + + New(atom) + ..() + index = steps.len + return + + proc/update_index(diff as num) + index+=diff + if(index==0) + spawn_result() + else + set_desc(index) + return + + is_right_key(atom/used_atom) // returns index step + var/list/L = steps[index] + if(istype(used_atom, L["key"])) + return FORWARD //to the first step -> forward + else if(L["backkey"] && istype(used_atom, L["backkey"])) + return BACKWARD //to the last step -> backwards + return 0 + + check_step(atom/used_atom,mob/user as mob) + var/diff = is_right_key(used_atom) + if(diff) + if(custom_action(index, diff, used_atom, user)) + update_index(diff) + return 1 + return 0 + + custom_action(index, diff, used_atom, 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 aabfa347ca4..475a2bb260d 100644 --- a/code/datums/helper_datums/events.dm +++ b/code/datums/helper_datums/events.dm @@ -1,65 +1,65 @@ -/* - * WARRANTY VOID IF CODE USED - */ - - -/datum/mecha_events - var/list/events - - New() - ..() - events = new - - 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. - 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/mecha_event/E = new /datum/mecha_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 - proc/fireEvent() - var/list/event = listgetindex(events,args[1]) - if(istype(event)) - spawn(-1) - for(var/datum/mecha_event/E in event) - if(!E.Fire(arglist(args.Copy(2)))) - clearEvent(args[1],E) - return - - // Arguments: event_type as text, E as /datum/mecha_event - // Returns: 1 if event cleared, null on error - 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/mecha_event - var/listener - var/proc_name - - New(tlistener,tprocname) - listener = tlistener - proc_name = tprocname - return ..() - - proc/Fire() - if(listener) - call(listener,proc_name)(arglist(args)) - return 1 - return +/* + * WARRANTY VOID IF CODE USED + */ + + +/datum/mecha_events + var/list/events + + New() + ..() + events = new + + 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. + 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/mecha_event/E = new /datum/mecha_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 + proc/fireEvent() + var/list/event = listgetindex(events,args[1]) + if(istype(event)) + spawn(-1) + for(var/datum/mecha_event/E in event) + if(!E.Fire(arglist(args.Copy(2)))) + clearEvent(args[1],E) + return + + // Arguments: event_type as text, E as /datum/mecha_event + // Returns: 1 if event cleared, null on error + 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/mecha_event + var/listener + var/proc_name + + New(tlistener,tprocname) + listener = tlistener + proc_name = tprocname + return ..() + + proc/Fire() + if(listener) + call(listener,proc_name)(arglist(args)) + return 1 + return diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 4903b64c5e3..ec1b9f04e0a 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -1,327 +1,327 @@ -//wrapper -/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) - 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_system/effectin //effect to show right before teleportation - var/datum/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) - - -/datum/teleport/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) - ..() - if(!initTeleport(arglist(args))) - return 0 - return 1 - -/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout) - if(!setTeleatom(ateleatom)) - return 0 - if(!setDestination(adestination)) - return 0 - if(!setPrecision(aprecision)) - return 0 - setEffects(aeffectin,aeffectout) - setForceTeleport(afteleport) - setSounds(asoundin,asoundout) - return 1 - -//must succeed -/datum/teleport/proc/setPrecision(aprecision) - if(isnum(aprecision)) - precision = aprecision - return 1 - return 0 - -/datum/teleport/proc/checkInhibitors(atom/adestination) - if(istype(adestination)) - var/list/turf/good_turfs = list() - var/list/turf/bad_turfs = list() - for(var/found_inhibitor in circlerange(adestination,8)) - if(!istype(found_inhibitor,/obj/machinery/anti_bluespace)) - continue - var/obj/machinery/anti_bluespace/AB = found_inhibitor - if(AB.stat & (NOPOWER | BROKEN) ) - continue - AB.use_power(AB.active_power_usage) - bad_turfs += circlerangeturfs(get_turf(AB),8) - good_turfs += circlerangeturfs(get_turf(AB),9) - if(good_turfs.len && bad_turfs.len) - good_turfs -= bad_turfs - return pick(good_turfs) - - return adestination - -//must succeed -/datum/teleport/proc/setDestination(atom/adestination) - if(istype(adestination)) - destination = checkInhibitors(adestination) - return 1 - return 0 - -//must succeed in most cases -/datum/teleport/proc/setTeleatom(atom/movable/ateleatom) - if(!istype(ateleatom)) - return 0 - - teleatom = checkInhibitors(ateleatom) - if(isturf(teleatom)) - var/turf/T = teleatom - var/atom/valid_atoms = list() - for(var/atom/movable in T) - valid_atoms += T - ateleatom = pick(valid_atoms) - - if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon)) - qdel(ateleatom) - return 0 - - return 1 - - -//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_system/effect,sound) - if(location) - if(effect) - spawn(-1) - src = null - effect.location = location - effect.queue() - 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) - var/area/destarea = get_area(destination) - 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(force_teleport) - teleatom.forceMove(destturf) - playSpecials(destturf,effectout,soundout) - var/atom/impediment - var/valid = 0 - - if(destturf.density && destturf != teleatom) - impediment = destturf - - else - for(var/atom/movable/A in destturf) - if(A != teleatom && A.density && A.anchored && !istype(A, /obj/effect/portal)) - if(A.flags & ON_BORDER) - if(prob(10)) - impediment = A - break - else - impediment = A - break - - if(impediment) - var/turf/newdest - var/boominess = 0 - for(var/turf/T in range(1, destturf)) - if(!T.density) - for(var/atom/movable/A in T) - var/blocked = 0 - if(A.density && A.opacity && A.anchored) - blocked = 1 - break - if(!blocked) - newdest = T - break - - - - - if(istype(teleatom, /obj) && !istype(teleatom, /obj/effect/portal)) - valid = 1 - var/obj/O = teleatom - if(newdest) - O.ex_act(3) - - boominess += max(0, O.w_class - 1) - if(O.density) - boominess += 5 - if(O.opacity) - boominess += 10 - - if(istype(teleatom, /mob/living)) - valid = 1 - var/mob/living/L = teleatom - boominess += L.mob_size/4 - if(!L.incorporeal_move) - if(istype(L, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = L - if(newdest) - var/list/organs_to_gib = list() - for(var/obj/item/organ/external/ext in H.organs) - if(!ext.vital) //ensures someone doesn't instantly die, allowing them to slowly die instead - organs_to_gib.Add(ext) - - if(organs_to_gib.len) - var/obj/item/organ/external/E = pick(organs_to_gib) - to_chat(H, "You partially phase into \the [impediment], causing your [E.name] to violently dematerialize!") - H.apply_damage(35, BRUTE, E, 0, sharp=0, edge=0) - - else - if(newdest) - to_chat(L, "You partially phase into \the [impediment], causing a chunk of you to violently dematerialize!") - L.adjustBruteLoss(40) - - else - newdest = destturf - valid = 0 - - if(valid && newdest) - destturf.visible_message("There is a sizable emission of energy as \the [teleatom] phases into \the [impediment]!") - explosion(destturf, ((boominess > 10) ? 1 : 0), ((boominess > 5) ? (boominess/10) : 0), boominess/5, boominess/2) - teleatom.forceMove(newdest) - - else if(!newdest) - var/list/turfs_to_teleport = list() - var/turf/target_turf - - for(var/turf/T in orange(10, get_turf(teleatom))) - turfs_to_teleport += T - target_turf = pick(turfs_to_teleport) - - if(target_turf) - do_teleport(teleatom, target_turf) - else - do_teleport(teleatom, get_turf(teleatom)) - - else if(istype(teleatom, /mob/living/simple_animal/shade/bluespace)) - var/mob/living/simple_animal/shade/bluespace/BS = teleatom - for(var/mob/living/L in destturf) - if(!L.mind && !isvaurca(L)) - - if(BS.message_countdown >= 200) - to_chat(BS, "You feel relief wash over you as your harried spirit fills into \the [L] like water into a vase.") - BS.mind.transfer_to(L) - to_chat(L, "You have been restored to a corporeal form. You retain no memories of your time as a bluespace echo, but regardless of your current form the memories of your time before being a bluespace echo are returned.") - qdel(BS) - - else - to_chat(BS, "You lack the strength of echoes necessary to reattain corporeality in \the [L]!") - - break - - else - if(teleatom.Move(destturf)) - playSpecials(destturf,effectout,soundout) - if(C) - C.forceMove(destturf) - - destarea.Entered(teleatom) - - 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_system/sparks/aeffect = new(null, FALSE, 5, alldirs) - effectin = effectin || aeffect - effectout = effectout || aeffect - return 1 - else - return ..() - -/datum/teleport/instant/science/setPrecision(aprecision) - ..() - if(istype(teleatom, /obj/item/storage/backpack/holding)) - precision = rand(1,100) - - var/list/bagholding = teleatom.search_contents_for(/obj/item/storage/backpack/holding) - if(bagholding.len) - precision = max(rand(1,100)*bagholding.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/disk/nuclear)) // Don't let nuke disks get teleported --NeoFite - teleatom.visible_message("\The [teleatom] bounces off of the portal!") - return 0 - - - if(isobserver(teleatom)) // do not teleport ghosts - return 0 - - - if(!isemptylist(teleatom.search_contents_for(/obj/item/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 - - if(isAdminLevel(destination.z)) //centcomm z-level - if(!isemptylist(teleatom.search_contents_for(/obj/item/storage/backpack/holding))) - teleatom.visible_message("\The [teleatom] bounces off of the portal!") - return 0 - - - if(destination.z > max_default_z_level()) //Away mission z-levels - return 0 - return 1 +//wrapper +/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) + 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_system/effectin //effect to show right before teleportation + var/datum/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) + + +/datum/teleport/New(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) + ..() + if(!initTeleport(arglist(args))) + return 0 + return 1 + +/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout) + if(!setTeleatom(ateleatom)) + return 0 + if(!setDestination(adestination)) + return 0 + if(!setPrecision(aprecision)) + return 0 + setEffects(aeffectin,aeffectout) + setForceTeleport(afteleport) + setSounds(asoundin,asoundout) + return 1 + +//must succeed +/datum/teleport/proc/setPrecision(aprecision) + if(isnum(aprecision)) + precision = aprecision + return 1 + return 0 + +/datum/teleport/proc/checkInhibitors(atom/adestination) + if(istype(adestination)) + var/list/turf/good_turfs = list() + var/list/turf/bad_turfs = list() + for(var/found_inhibitor in circlerange(adestination,8)) + if(!istype(found_inhibitor,/obj/machinery/anti_bluespace)) + continue + var/obj/machinery/anti_bluespace/AB = found_inhibitor + if(AB.stat & (NOPOWER | BROKEN) ) + continue + AB.use_power(AB.active_power_usage) + bad_turfs += circlerangeturfs(get_turf(AB),8) + good_turfs += circlerangeturfs(get_turf(AB),9) + if(good_turfs.len && bad_turfs.len) + good_turfs -= bad_turfs + return pick(good_turfs) + + return adestination + +//must succeed +/datum/teleport/proc/setDestination(atom/adestination) + if(istype(adestination)) + destination = checkInhibitors(adestination) + return 1 + return 0 + +//must succeed in most cases +/datum/teleport/proc/setTeleatom(atom/movable/ateleatom) + if(!istype(ateleatom)) + return 0 + + teleatom = checkInhibitors(ateleatom) + if(isturf(teleatom)) + var/turf/T = teleatom + var/atom/valid_atoms = list() + for(var/atom/movable in T) + valid_atoms += T + ateleatom = pick(valid_atoms) + + if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon)) + qdel(ateleatom) + return 0 + + return 1 + + +//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_system/effect,sound) + if(location) + if(effect) + spawn(-1) + src = null + effect.location = location + effect.queue() + 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) + var/area/destarea = get_area(destination) + 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(force_teleport) + teleatom.forceMove(destturf) + playSpecials(destturf,effectout,soundout) + var/atom/impediment + var/valid = 0 + + if(destturf.density && destturf != teleatom) + impediment = destturf + + else + for(var/atom/movable/A in destturf) + if(A != teleatom && A.density && A.anchored && !istype(A, /obj/effect/portal)) + if(A.flags & ON_BORDER) + if(prob(10)) + impediment = A + break + else + impediment = A + break + + if(impediment) + var/turf/newdest + var/boominess = 0 + for(var/turf/T in range(1, destturf)) + if(!T.density) + for(var/atom/movable/A in T) + var/blocked = 0 + if(A.density && A.opacity && A.anchored) + blocked = 1 + break + if(!blocked) + newdest = T + break + + + + + if(istype(teleatom, /obj) && !istype(teleatom, /obj/effect/portal)) + valid = 1 + var/obj/O = teleatom + if(newdest) + O.ex_act(3) + + boominess += max(0, O.w_class - 1) + if(O.density) + boominess += 5 + if(O.opacity) + boominess += 10 + + if(istype(teleatom, /mob/living)) + valid = 1 + var/mob/living/L = teleatom + boominess += L.mob_size/4 + if(!L.incorporeal_move) + if(istype(L, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = L + if(newdest) + var/list/organs_to_gib = list() + for(var/obj/item/organ/external/ext in H.organs) + if(!ext.vital) //ensures someone doesn't instantly die, allowing them to slowly die instead + organs_to_gib.Add(ext) + + if(organs_to_gib.len) + var/obj/item/organ/external/E = pick(organs_to_gib) + to_chat(H, "You partially phase into \the [impediment], causing your [E.name] to violently dematerialize!") + H.apply_damage(35, BRUTE, E, 0, sharp=0, edge=0) + + else + if(newdest) + to_chat(L, "You partially phase into \the [impediment], causing a chunk of you to violently dematerialize!") + L.adjustBruteLoss(40) + + else + newdest = destturf + valid = 0 + + if(valid && newdest) + destturf.visible_message("There is a sizable emission of energy as \the [teleatom] phases into \the [impediment]!") + explosion(destturf, ((boominess > 10) ? 1 : 0), ((boominess > 5) ? (boominess/10) : 0), boominess/5, boominess/2) + teleatom.forceMove(newdest) + + else if(!newdest) + var/list/turfs_to_teleport = list() + var/turf/target_turf + + for(var/turf/T in orange(10, get_turf(teleatom))) + turfs_to_teleport += T + target_turf = pick(turfs_to_teleport) + + if(target_turf) + do_teleport(teleatom, target_turf) + else + do_teleport(teleatom, get_turf(teleatom)) + + else if(istype(teleatom, /mob/living/simple_animal/shade/bluespace)) + var/mob/living/simple_animal/shade/bluespace/BS = teleatom + for(var/mob/living/L in destturf) + if(!L.mind && !isvaurca(L)) + + if(BS.message_countdown >= 200) + to_chat(BS, "You feel relief wash over you as your harried spirit fills into \the [L] like water into a vase.") + BS.mind.transfer_to(L) + to_chat(L, "You have been restored to a corporeal form. You retain no memories of your time as a bluespace echo, but regardless of your current form the memories of your time before being a bluespace echo are returned.") + qdel(BS) + + else + to_chat(BS, "You lack the strength of echoes necessary to reattain corporeality in \the [L]!") + + break + + else + if(teleatom.Move(destturf)) + playSpecials(destturf,effectout,soundout) + if(C) + C.forceMove(destturf) + + destarea.Entered(teleatom) + + 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_system/sparks/aeffect = new(null, FALSE, 5, alldirs) + effectin = effectin || aeffect + effectout = effectout || aeffect + return 1 + else + return ..() + +/datum/teleport/instant/science/setPrecision(aprecision) + ..() + if(istype(teleatom, /obj/item/storage/backpack/holding)) + precision = rand(1,100) + + var/list/bagholding = teleatom.search_contents_for(/obj/item/storage/backpack/holding) + if(bagholding.len) + precision = max(rand(1,100)*bagholding.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/disk/nuclear)) // Don't let nuke disks get teleported --NeoFite + teleatom.visible_message("\The [teleatom] bounces off of the portal!") + return 0 + + + if(isobserver(teleatom)) // do not teleport ghosts + return 0 + + + if(!isemptylist(teleatom.search_contents_for(/obj/item/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 + + if(isAdminLevel(destination.z)) //centcomm z-level + if(!isemptylist(teleatom.search_contents_for(/obj/item/storage/backpack/holding))) + teleatom.visible_message("\The [teleatom] bounces off of the portal!") + return 0 + + + if(destination.z > max_default_z_level()) //Away mission z-levels + return 0 + return 1 diff --git a/code/datums/helper_datums/topic_input.dm b/code/datums/helper_datums/topic_input.dm index 17dd3f1266e..c08ca754be4 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 - - New(thref,list/thref_list) - href = thref - href_list = thref_list.Copy() - return - - proc/get(i) - return listgetindex(href_list,i) - - proc/getAndLocate(i) - var/t = get(i) - if(t) - t = locate(t) - return t || null - - proc/getNum(i) - var/t = get(i) - if(t) - t = text2num(t) - return isnum(t) ? t : null - - proc/getObj(i) - var/t = getAndLocate(i) - return isobj(t) ? t : null - - proc/getMob(i) - var/t = getAndLocate(i) - return ismob(t) ? t : null - - proc/getTurf(i) - var/t = getAndLocate(i) - return isturf(t) ? t : null - - proc/getAtom(i) - return getType(i,/atom) - - proc/getArea(i) - var/t = getAndLocate(i) - return isarea(t) ? t : null - - proc/getStr(i)//params should always be text, but... - var/t = get(i) - return istext(t) ? t : null - - proc/getType(i,type) - var/t = getAndLocate(i) - return istype(t,type) ? t : null - - proc/getPath(i) - var/t = get(i) - if(t) - t = text2path(t) - return ispath(t) ? t : null - - proc/getList(i) - var/t = getAndLocate(i) +/datum/topic_input + var/href + var/list/href_list + + New(thref,list/thref_list) + href = thref + href_list = thref_list.Copy() + return + + proc/get(i) + return listgetindex(href_list,i) + + proc/getAndLocate(i) + var/t = get(i) + if(t) + t = locate(t) + return t || null + + proc/getNum(i) + var/t = get(i) + if(t) + t = text2num(t) + return isnum(t) ? t : null + + proc/getObj(i) + var/t = getAndLocate(i) + return isobj(t) ? t : null + + proc/getMob(i) + var/t = getAndLocate(i) + return ismob(t) ? t : null + + proc/getTurf(i) + var/t = getAndLocate(i) + return isturf(t) ? t : null + + proc/getAtom(i) + return getType(i,/atom) + + proc/getArea(i) + var/t = getAndLocate(i) + return isarea(t) ? t : null + + proc/getStr(i)//params should always be text, but... + var/t = get(i) + return istext(t) ? t : null + + proc/getType(i,type) + var/t = getAndLocate(i) + return istype(t,type) ? t : null + + proc/getPath(i) + var/t = get(i) + if(t) + t = text2path(t) + return ispath(t) ? t : null + + proc/getList(i) + var/t = getAndLocate(i) return islist(t) ? t : null \ No newline at end of file diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 2bd312e08cd..d2ba90d4719 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1,592 +1,592 @@ -/* 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 //This is being used now, don't remove it - var/active = 0 - - var/mob/living/admin_mob_placeholder = null - - var/memory - - var/assigned_role - var/special_role - - var/role_alt_title - - var/datum/job/assigned_job - var/datum/faction/selected_faction - - 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/datum/vampire/vampire //vampire holder - - var/rev_cooldown = 0 - - // the world.time since the mob has been brigged, or -1 if not at all - var/brigged_since = -1 - - // Custom signature data. - var/signature - var/signfont - - //put this here for easier tracking ingame - var/datum/money_account/initial_account - - var/ambitions - -/datum/mind/New(var/key) - src.key = key - ..() - -/datum/mind/proc/handle_mob_deletion(mob/living/deleted_mob) - if (current == deleted_mob) - current.spellremove() - current = null - - if (original == deleted_mob) - original = null - -/datum/mind/proc/transfer_to(mob/living/new_character) - if(!istype(new_character)) - 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 - if(vampire) - current.remove_vampire_powers() - current.mind = null - - SSnanoui.user_transferred(current, new_character) // transfer active NanoUI instances to new user - SSvueui.user_transferred(current, new_character) - if(current.client && ticket_panels[current.client]) - var/datum/ticket_panel/tp = ticket_panels[current.client] - tp.ticket_panel_window.user = new_character - - 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(vampire) - new_character.make_vampire() - 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) - . = length(memory + new_text) - - if (. > MAX_PAPER_MESSAGE_LEN) - memory = copytext(memory, . - MAX_PAPER_MESSAGE_LEN, .) - else - memory += "[new_text]
    " - -/datum/mind/proc/show_memory(mob/recipient) - var/output = "[current.real_name]'s Memory
    " - output += memory - - if(ambitions) - output += "
    Ambitions: [ambitions]
    " - - 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++ - - recipient << browse(output,"window=memory") - -/datum/mind/proc/edit_memory() - if(!ROUND_IS_STARTED) - alert("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)) 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, highlight_special = 1)] into a [antag.role_text].",admin_key=key_name(usr),ckey=key_name(src)) - 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(input("Write new memory", "Memory", memory) as null|message) - if (isnull(new_memo)) return - memory = new_memo - - else if (href_list["amb_edit"]) - var/new_ambition = input("Enter a new ambition", "Memory",src.ambitions) as null|message - if(isnull(new_ambition)) - return - src.ambitions = sanitize(new_ambition) - to_chat(src.current, "Your ambitions have been changed by higher powers, they are now: [src.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/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "debrain", "protect", "prevent", "harm", "brig", "hijack", "escape", "survive", "steal", "download", "mercenary", "capture", "absorb", "custom") - 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 SSticker.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 = input("Select target:", "Objective target", def_target) as null|anything in possible_targets - 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") - var/def_num - if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) - def_num = objective.target_amount - - var/target_number = input("Input target number:", "Objective", def_num) as num|null - 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." - new_objective.owner = src - new_objective.target_amount = target_number - - if ("custom") - var/expl = sanitize(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null) - 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/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].",admin_key=key_name(usr),ckey=key_name(usr)) - if("add") - to_chat(H, "You somehow have become the recepient of a loyalty transplant, and it just activated!") - H.implant_loyalty(H, override = TRUE) - log_admin("[key_name_admin(usr)] has loyalty implanted [current].",admin_key=key_name(usr),ckey=key_name(usr)) - 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].",admin_key=key_name(usr),ckey_target=key_name(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.",admin_key=key_name(usr),ckey_target=key_name(ai)) - - 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() - var/crystals - if (suplink) - crystals = suplink.uses - crystals = input("Amount of telecrystals for [key]","Operative uplink", crystals) as null|num - if (!isnull(crystals)) - if (suplink) - suplink.uses = 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/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 - vampire = 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 - -//Initialisation procs -/mob/living/proc/mind_initialize() - if(mind) - mind.key = key - else - mind = new /datum/mind(key) - mind.original = src - SSticker.minds += mind - if(!mind.name) - mind.name = real_name - - if (client) - if (client.prefs.signature) - mind.signature = client.prefs.signature - if (client.prefs.signfont) - mind.signfont = client.prefs.signfont - mind.current = src - -//HUMAN -/mob/living/carbon/human/mind_initialize() - ..() - if(!mind.assigned_role) - mind.assigned_role = "Assistant" //defualt - -//slime -/mob/living/carbon/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_animal/shade/mind_initialize() - ..() - mind.assigned_role = "Shade" - -/mob/living/simple_animal/construct/builder/mind_initialize() - ..() - mind.assigned_role = "Artificer" - mind.special_role = "Cultist" - -/mob/living/simple_animal/construct/wraith/mind_initialize() - ..() - mind.assigned_role = "Wraith" - mind.special_role = "Cultist" - -/mob/living/simple_animal/construct/armoured/mind_initialize() - ..() - mind.assigned_role = "Juggernaut" - mind.special_role = "Cultist" - -/mob/living/carbon/human/voxarmalis/mind_initialize() - ..() - mind.assigned_role = "Armalis" - mind.special_role = "Vox Raider" - -/mob/living/silicon/robot/syndicate/mind_initialize() - ..() - mind.assigned_role = "Syndicate Robot" - mind.special_role = "Mercenary" - -/mob/living/simple_animal/hostile/faithless/wizard/mind_initialize() - ..() - mind.assigned_role = "Space Wizard" - -/mob/living/simple_animal/familiar/mind_initialize() - ..() - mind.assigned_role = "Familiar" - -/mob/living/simple_animal/rat/familiar/familiar/mind_initialize() - ..() - mind.assigned_role = "Familiar" +/* 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 //This is being used now, don't remove it + var/active = 0 + + var/mob/living/admin_mob_placeholder = null + + var/memory + + var/assigned_role + var/special_role + + var/role_alt_title + + var/datum/job/assigned_job + var/datum/faction/selected_faction + + 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/datum/vampire/vampire //vampire holder + + var/rev_cooldown = 0 + + // the world.time since the mob has been brigged, or -1 if not at all + var/brigged_since = -1 + + // Custom signature data. + var/signature + var/signfont + + //put this here for easier tracking ingame + var/datum/money_account/initial_account + + var/ambitions + +/datum/mind/New(var/key) + src.key = key + ..() + +/datum/mind/proc/handle_mob_deletion(mob/living/deleted_mob) + if (current == deleted_mob) + current.spellremove() + current = null + + if (original == deleted_mob) + original = null + +/datum/mind/proc/transfer_to(mob/living/new_character) + if(!istype(new_character)) + 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 + if(vampire) + current.remove_vampire_powers() + current.mind = null + + SSnanoui.user_transferred(current, new_character) // transfer active NanoUI instances to new user + SSvueui.user_transferred(current, new_character) + if(current.client && ticket_panels[current.client]) + var/datum/ticket_panel/tp = ticket_panels[current.client] + tp.ticket_panel_window.user = new_character + + 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(vampire) + new_character.make_vampire() + 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) + . = length(memory + new_text) + + if (. > MAX_PAPER_MESSAGE_LEN) + memory = copytext(memory, . - MAX_PAPER_MESSAGE_LEN, .) + else + memory += "[new_text]
    " + +/datum/mind/proc/show_memory(mob/recipient) + var/output = "[current.real_name]'s Memory
    " + output += memory + + if(ambitions) + output += "
    Ambitions: [ambitions]
    " + + 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++ + + recipient << browse(output,"window=memory") + +/datum/mind/proc/edit_memory() + if(!ROUND_IS_STARTED) + alert("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)) 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, highlight_special = 1)] into a [antag.role_text].",admin_key=key_name(usr),ckey=key_name(src)) + 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(input("Write new memory", "Memory", memory) as null|message) + if (isnull(new_memo)) return + memory = new_memo + + else if (href_list["amb_edit"]) + var/new_ambition = input("Enter a new ambition", "Memory",src.ambitions) as null|message + if(isnull(new_ambition)) + return + src.ambitions = sanitize(new_ambition) + to_chat(src.current, "Your ambitions have been changed by higher powers, they are now: [src.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/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "debrain", "protect", "prevent", "harm", "brig", "hijack", "escape", "survive", "steal", "download", "mercenary", "capture", "absorb", "custom") + 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 SSticker.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 = input("Select target:", "Objective target", def_target) as null|anything in possible_targets + 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") + var/def_num + if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) + def_num = objective.target_amount + + var/target_number = input("Input target number:", "Objective", def_num) as num|null + 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." + new_objective.owner = src + new_objective.target_amount = target_number + + if ("custom") + var/expl = sanitize(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null) + 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/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].",admin_key=key_name(usr),ckey=key_name(usr)) + if("add") + to_chat(H, "You somehow have become the recepient of a loyalty transplant, and it just activated!") + H.implant_loyalty(H, override = TRUE) + log_admin("[key_name_admin(usr)] has loyalty implanted [current].",admin_key=key_name(usr),ckey=key_name(usr)) + 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].",admin_key=key_name(usr),ckey_target=key_name(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.",admin_key=key_name(usr),ckey_target=key_name(ai)) + + 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() + var/crystals + if (suplink) + crystals = suplink.uses + crystals = input("Amount of telecrystals for [key]","Operative uplink", crystals) as null|num + if (!isnull(crystals)) + if (suplink) + suplink.uses = 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/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 + vampire = 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 + +//Initialisation procs +/mob/living/proc/mind_initialize() + if(mind) + mind.key = key + else + mind = new /datum/mind(key) + mind.original = src + SSticker.minds += mind + if(!mind.name) + mind.name = real_name + + if (client) + if (client.prefs.signature) + mind.signature = client.prefs.signature + if (client.prefs.signfont) + mind.signfont = client.prefs.signfont + mind.current = src + +//HUMAN +/mob/living/carbon/human/mind_initialize() + ..() + if(!mind.assigned_role) + mind.assigned_role = "Assistant" //defualt + +//slime +/mob/living/carbon/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_animal/shade/mind_initialize() + ..() + mind.assigned_role = "Shade" + +/mob/living/simple_animal/construct/builder/mind_initialize() + ..() + mind.assigned_role = "Artificer" + mind.special_role = "Cultist" + +/mob/living/simple_animal/construct/wraith/mind_initialize() + ..() + mind.assigned_role = "Wraith" + mind.special_role = "Cultist" + +/mob/living/simple_animal/construct/armoured/mind_initialize() + ..() + mind.assigned_role = "Juggernaut" + mind.special_role = "Cultist" + +/mob/living/carbon/human/voxarmalis/mind_initialize() + ..() + mind.assigned_role = "Armalis" + mind.special_role = "Vox Raider" + +/mob/living/silicon/robot/syndicate/mind_initialize() + ..() + mind.assigned_role = "Syndicate Robot" + mind.special_role = "Mercenary" + +/mob/living/simple_animal/hostile/faithless/wizard/mind_initialize() + ..() + mind.assigned_role = "Space Wizard" + +/mob/living/simple_animal/familiar/mind_initialize() + ..() + mind.assigned_role = "Familiar" + +/mob/living/simple_animal/rat/familiar/familiar/mind_initialize() + ..() + mind.assigned_role = "Familiar" diff --git a/code/datums/mixed.dm b/code/datums/mixed.dm index 122bf23ecee..3aa68129df5 100644 --- a/code/datums/mixed.dm +++ b/code/datums/mixed.dm @@ -1,27 +1,27 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/datum/data - var/name = "data" - var/size = 1.0 - - -/datum/data/function - name = "function" - size = 2.0 - - -/datum/data/function/data_control - name = "data control" - - -/datum/data/function/id_changer - name = "id changer" - -/datum/data/text - name = "text" - var/data = null - - - -/datum/debug - var/list/debuglist +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/datum/data + var/name = "data" + var/size = 1.0 + + +/datum/data/function + name = "function" + size = 2.0 + + +/datum/data/function/data_control + name = "data control" + + +/datum/data/function/id_changer + name = "id changer" + +/datum/data/text + name = "text" + var/data = null + + + +/datum/debug + var/list/debuglist 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/outfits/outfit_antag.dm b/code/datums/outfits/outfit_antag.dm index 242d590a8eb..e1cfaa95e5a 100644 --- a/code/datums/outfits/outfit_antag.dm +++ b/code/datums/outfits/outfit_antag.dm @@ -35,8 +35,9 @@ var/obj/item/device/radio/uplink/U = H.r_store if(istype(U)) - U.hidden_uplink.uplink_owner = "[H.key]" + U.hidden_uplink.uplink_owner = H.mind U.hidden_uplink.uses = uplink_uses + U.hidden_uplink.nanoui_menu = 1 /datum/outfit/admin/syndicate/get_id_access() return get_syndicate_access(id_access) @@ -162,7 +163,7 @@ return if(!H.shoes) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/jackboots/unathi(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/jackboots/toeless(H), slot_shoes) /datum/outfit/admin/syndicate/raider name = "Raider" diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm index 0668ca76b91..1abbedec4a6 100644 --- a/code/datums/progressbar.dm +++ b/code/datums/progressbar.dm @@ -1,80 +1,80 @@ -#define PROGRESSBAR_HEIGHT 6 -#define PROGRESSBAR_ANIMATION_TIME 5 - -/datum/progressbar - var/goal = 1 - var/last_progress = 0 - var/image/bar - var/shown = 0 - var/mob/user - var/client/client - var/listindex - -/datum/progressbar/New(mob/User, goal_number, atom/target) - . = ..() - if (!istype(target)) - EXCEPTION("Invalid target given") - if (goal_number) - goal = goal_number - bar = image('icons/effects/progressbar.dmi', target, "prog_bar_0", 21) - bar.appearance_flags = RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR|RESET_ALPHA - user = User - if(user) - client = user.client - - LAZYINITLIST(user.progressbars) - LAZYINITLIST(user.progressbars[bar.loc]) - var/list/bars = user.progressbars[bar.loc] - bars += src - listindex = bars.len - bar.pixel_y = 0 - bar.alpha = 0 - animate(bar, pixel_y = 32 + (PROGRESSBAR_HEIGHT * (listindex - 1)), alpha = 255, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING) - -/datum/progressbar/proc/update(progress) - if (!user || !user.client) - shown = FALSE - return - if (user.client != client) - if (client) - client.images -= bar - if (user.client) - user.client.images += bar - - progress = Clamp(progress, 0, goal) - bar.icon_state = "prog_bar_[round(((progress / goal) * 100), 5)]" - if (!shown) - user.client.images += bar - shown = TRUE - -/datum/progressbar/proc/shiftDown() - --listindex - bar.pixel_y = 32 + (PROGRESSBAR_HEIGHT * (listindex - 1)) - var/dist_to_travel = 32 + (PROGRESSBAR_HEIGHT * (listindex - 1)) - PROGRESSBAR_HEIGHT - animate(bar, pixel_y = dist_to_travel, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING) - -/datum/progressbar/Destroy() - if(last_progress != goal) - bar.icon_state = "[bar.icon_state]_fail" - for(var/I in user.progressbars[bar.loc]) - var/datum/progressbar/P = I - if(P != src && P.listindex > listindex) - P.shiftDown() - - var/list/bars = user.progressbars[bar.loc] - bars -= src - if(!bars.len) - LAZYREMOVE(user.progressbars, bar.loc) - - animate(bar, alpha = 0, time = PROGRESSBAR_ANIMATION_TIME) - addtimer(CALLBACK(src, .proc/remove_from_client), PROGRESSBAR_ANIMATION_TIME, TIMER_CLIENT_TIME) - QDEL_IN(bar, PROGRESSBAR_ANIMATION_TIME * 2) //for garbage collection safety - . = ..() - -/datum/progressbar/proc/remove_from_client() - if(client) - client.images -= bar - client = null - -#undef PROGRESSBAR_ANIMATION_TIME +#define PROGRESSBAR_HEIGHT 6 +#define PROGRESSBAR_ANIMATION_TIME 5 + +/datum/progressbar + var/goal = 1 + var/last_progress = 0 + var/image/bar + var/shown = 0 + var/mob/user + var/client/client + var/listindex + +/datum/progressbar/New(mob/User, goal_number, atom/target) + . = ..() + if (!istype(target)) + EXCEPTION("Invalid target given") + if (goal_number) + goal = goal_number + bar = image('icons/effects/progressbar.dmi', target, "prog_bar_0", 21) + bar.appearance_flags = RESET_COLOR|RESET_TRANSFORM|NO_CLIENT_COLOR|RESET_ALPHA + user = User + if(user) + client = user.client + + LAZYINITLIST(user.progressbars) + LAZYINITLIST(user.progressbars[bar.loc]) + var/list/bars = user.progressbars[bar.loc] + bars += src + listindex = bars.len + bar.pixel_y = 0 + bar.alpha = 0 + animate(bar, pixel_y = 32 + (PROGRESSBAR_HEIGHT * (listindex - 1)), alpha = 255, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING) + +/datum/progressbar/proc/update(progress) + if (!user || !user.client) + shown = FALSE + return + if (user.client != client) + if (client) + client.images -= bar + if (user.client) + user.client.images += bar + + progress = Clamp(progress, 0, goal) + bar.icon_state = "prog_bar_[round(((progress / goal) * 100), 5)]" + if (!shown) + user.client.images += bar + shown = TRUE + +/datum/progressbar/proc/shiftDown() + --listindex + bar.pixel_y = 32 + (PROGRESSBAR_HEIGHT * (listindex - 1)) + var/dist_to_travel = 32 + (PROGRESSBAR_HEIGHT * (listindex - 1)) - PROGRESSBAR_HEIGHT + animate(bar, pixel_y = dist_to_travel, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING) + +/datum/progressbar/Destroy() + if(last_progress != goal) + bar.icon_state = "[bar.icon_state]_fail" + for(var/I in user.progressbars[bar.loc]) + var/datum/progressbar/P = I + if(P != src && P.listindex > listindex) + P.shiftDown() + + var/list/bars = user.progressbars[bar.loc] + bars -= src + if(!bars.len) + LAZYREMOVE(user.progressbars, bar.loc) + + animate(bar, alpha = 0, time = PROGRESSBAR_ANIMATION_TIME) + addtimer(CALLBACK(src, .proc/remove_from_client), PROGRESSBAR_ANIMATION_TIME, TIMER_CLIENT_TIME) + QDEL_IN(bar, PROGRESSBAR_ANIMATION_TIME * 2) //for garbage collection safety + . = ..() + +/datum/progressbar/proc/remove_from_client() + if(client) + client.images -= bar + client = null + +#undef PROGRESSBAR_ANIMATION_TIME #undef PROGRESSBAR_HEIGHT \ No newline at end of file diff --git a/code/datums/repositories/cameras.dm b/code/datums/repositories/cameras.dm index 8b6dbd30d7d..752d4e2515a 100644 --- a/code/datums/repositories/cameras.dm +++ b/code/datums/repositories/cameras.dm @@ -1,33 +1,33 @@ -var/global/datum/repository/cameras/camera_repository = new() - -/proc/invalidateCameraCache() - camera_repository.networks.Cut() - camera_repository.invalidated = 1 - camera_repository.camera_cache_id = (++camera_repository.camera_cache_id % 999999) - -/datum/repository/cameras - var/list/networks - var/invalidated = 1 - var/camera_cache_id = 1 - -/datum/repository/cameras/New() - networks = list() - ..() - -/datum/repository/cameras/proc/cameras_in_network(var/network) - setup_cache() - var/list/network_list = networks[network] - return network_list - -/datum/repository/cameras/proc/setup_cache() - if(!invalidated) - return - invalidated = 0 - - for(var/obj/machinery/camera/C in cameranet.cameras) - var/cam = C.nano_structure() - for(var/network in C.network) - if(!networks[network]) - networks[network] = list() - var/list/netlist = networks[network] - netlist[++netlist.len] = cam +var/global/datum/repository/cameras/camera_repository = new() + +/proc/invalidateCameraCache() + camera_repository.networks.Cut() + camera_repository.invalidated = 1 + camera_repository.camera_cache_id = (++camera_repository.camera_cache_id % 999999) + +/datum/repository/cameras + var/list/networks + var/invalidated = 1 + var/camera_cache_id = 1 + +/datum/repository/cameras/New() + networks = list() + ..() + +/datum/repository/cameras/proc/cameras_in_network(var/network) + setup_cache() + var/list/network_list = networks[network] + return network_list + +/datum/repository/cameras/proc/setup_cache() + if(!invalidated) + return + invalidated = 0 + + for(var/obj/machinery/camera/C in cameranet.cameras) + var/cam = C.nano_structure() + for(var/network in C.network) + if(!networks[network]) + networks[network] = list() + var/list/netlist = networks[network] + netlist[++netlist.len] = cam diff --git a/code/datums/repositories/crew.dm b/code/datums/repositories/crew.dm index fbce73c8dde..9ad4f3ffc9e 100644 --- a/code/datums/repositories/crew.dm +++ b/code/datums/repositories/crew.dm @@ -1,114 +1,117 @@ -#define ZERO_WIDTH_SPACE "​" //prevents the UI elements from popping out of place when viewport gets too small - -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/z_level) - var/list/crewmembers = list() - if(!z_level) - return crewmembers - - var/datum/cache_entry/cache_entry = cache_data[num2text(z_level)] - if(!cache_entry) - cache_entry = new/datum/cache_entry - cache_data[num2text(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) - if((C) && (C.has_sensor) && (pos) && (pos.z == z_level) && (C.sensor_mode != SUIT_SENSOR_OFF) && !within_jamming_range(C)) - 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, "area"="", "x"=-1, "y"=-1, "z"=-1, "level"="", "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 > UNCONSCIOUS - - if(C.sensor_mode >= SUIT_SENSOR_VITAL) - if(!H.isSynthetic() && H.should_have_organ(BP_HEART)) - var/obj/item/organ/internal/heart/O = H.internal_organs_by_name[BP_HEART] - if (!O || !BP_IS_ROBOTIC(O)) // Don't make medical freak out over prosthetic hearts - crewmemberData["true_pulse"] = H.pulse() - crewmemberData["pulse"] = H.get_pulse(GETPULSE_TOOL) - switch(crewmemberData["true_pulse"]) - if(PULSE_NONE) - crewmemberData["pulse_span"] = "bad" - if(PULSE_SLOW) - crewmemberData["pulse_span"] = "average" - if(PULSE_NORM) - crewmemberData["pulse_span"] = "good" - if(PULSE_FAST) - crewmemberData["pulse_span"] = "highlight" - if(PULSE_2FAST) - crewmemberData["pulse_span"] = "average" - if(PULSE_THREADY) - crewmemberData["pulse_span"] = "bad" - - crewmemberData["pressure"] = "N/A" - crewmemberData["true_oxygenation"] = -1 - crewmemberData["oxygenation"] = "" - crewmemberData["oxygenation_span"] = "" - if(!H.isSynthetic() && H.should_have_organ(BP_HEART)) - crewmemberData["pressure"] = H.get_blood_pressure() - crewmemberData["true_oxygenation"] = H.get_blood_oxygenation() - switch (crewmemberData["true_oxygenation"]) - if(105 to INFINITY) - crewmemberData["oxygenation"] = "increased" - crewmemberData["oxygenation_span"] = "highlight" - if(BLOOD_VOLUME_SAFE to 105) - crewmemberData["oxygenation"] = "normal" - crewmemberData["oxygenation_span"] = "good" - if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) - crewmemberData["oxygenation"] = "low" - crewmemberData["oxygenation_span"] = "average" - if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) - crewmemberData["oxygenation"] = "very low" - crewmemberData["oxygenation_span"] = "bad" - if(-(INFINITY) to BLOOD_VOLUME_BAD) - crewmemberData["oxygenation"] = "extremely low" - crewmemberData["oxygenation_span"] = "bad" - - crewmemberData["bodytemp"] = H.bodytemperature - T0C - - if(C.sensor_mode >= SUIT_SENSOR_TRACKING) - var/area/A = get_area(H) - crewmemberData["area"] = sanitize(A.name) - crewmemberData["x"] = pos.x - crewmemberData["y"] = pos.y - crewmemberData["z"] = 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 - +#define ZERO_WIDTH_SPACE "​" //prevents the UI elements from popping out of place when viewport gets too small + +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/z_level) + var/list/crewmembers = list() + if(!z_level) + return crewmembers + + var/datum/cache_entry/cache_entry = cache_data[num2text(z_level)] + if(!cache_entry) + cache_entry = new/datum/cache_entry + cache_data[num2text(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) + if((C) && (C.has_sensor) && (pos) && (pos.z == z_level) && (C.sensor_mode != SUIT_SENSOR_OFF) && !within_jamming_range(C)) + 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, "area"="", "x"=-1, "y"=-1, "z"=-1, "level"="", "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 > UNCONSCIOUS + + if(C.sensor_mode >= SUIT_SENSOR_VITAL) + crewmemberData["pulse"] = "N/A" + crewmemberData["pulse_span"] = "neutral" + crewmemberData["true_pulse"] = -1 + if(!H.isSynthetic() && H.should_have_organ(BP_HEART)) + var/obj/item/organ/internal/heart/O = H.internal_organs_by_name[BP_HEART] + if (!O || !BP_IS_ROBOTIC(O)) // Don't make medical freak out over prosthetic hearts + crewmemberData["true_pulse"] = H.pulse() + crewmemberData["pulse"] = H.get_pulse(GETPULSE_TOOL) + switch(crewmemberData["true_pulse"]) + if(PULSE_NONE) + crewmemberData["pulse_span"] = "bad" + if(PULSE_SLOW) + crewmemberData["pulse_span"] = "average" + if(PULSE_NORM) + crewmemberData["pulse_span"] = "good" + if(PULSE_FAST) + crewmemberData["pulse_span"] = "highlight" + if(PULSE_2FAST) + crewmemberData["pulse_span"] = "average" + if(PULSE_THREADY) + crewmemberData["pulse_span"] = "bad" + + crewmemberData["pressure"] = "N/A" + crewmemberData["true_oxygenation"] = -1 + crewmemberData["oxygenation"] = "" + crewmemberData["oxygenation_span"] = "" + if(!H.isSynthetic() && H.should_have_organ(BP_HEART)) + crewmemberData["pressure"] = H.get_blood_pressure() + crewmemberData["true_oxygenation"] = H.get_blood_oxygenation() + switch (crewmemberData["true_oxygenation"]) + if(105 to INFINITY) + crewmemberData["oxygenation"] = "increased" + crewmemberData["oxygenation_span"] = "highlight" + if(BLOOD_VOLUME_SAFE to 105) + crewmemberData["oxygenation"] = "normal" + crewmemberData["oxygenation_span"] = "good" + if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) + crewmemberData["oxygenation"] = "low" + crewmemberData["oxygenation_span"] = "average" + if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) + crewmemberData["oxygenation"] = "very low" + crewmemberData["oxygenation_span"] = "bad" + if(-(INFINITY) to BLOOD_VOLUME_BAD) + crewmemberData["oxygenation"] = "extremely low" + crewmemberData["oxygenation_span"] = "bad" + + crewmemberData["bodytemp"] = H.bodytemperature - T0C + + if(C.sensor_mode >= SUIT_SENSOR_TRACKING) + var/area/A = get_area(H) + crewmemberData["area"] = sanitize(A.name) + crewmemberData["x"] = pos.x + crewmemberData["y"] = pos.y + crewmemberData["z"] = 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 + #undef ZERO_WIDTH_SPACE \ No newline at end of file diff --git a/code/datums/repositories/repository.dm b/code/datums/repositories/repository.dm index 6267099c930..06939b148f6 100644 --- a/code/datums/repositories/repository.dm +++ b/code/datums/repositories/repository.dm @@ -1,4 +1,4 @@ -/datum/cache_entry - var/timestamp - var/data - +/datum/cache_entry + var/timestamp + var/data + diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index 0f447c8e3d9..9544259172b 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -1,1801 +1,1801 @@ -//SUPPLY PACKS -//NOTE: only secure crate types use the access var (and are lockable) -//NOTE: hidden packs only show up when the computer has been hacked. -//ANOTER NOTE: Contraband is obtainable through modified supplycomp circuitboards. -//BIG NOTE: Don't add living things to crates, that's bad, it will break the shuttle. -//NEW NOTE: Do NOT set the price of any crates below 7 points. Doing so allows infinite points. - -var/list/all_supply_groups = list("Operations","Security","Hospitality","Engineering","Atmospherics","Medical","Reagents","Reagent Cartridges","Science","Hydroponics", "Supply", "Miscellaneous") - -/datum/supply_packs - var/name = null - var/list/contains = list() - var/manifest = "" - var/amount = null - var/cost = null - var/containertype = null - var/containername = null - var/access = null - var/hidden = 0 - var/contraband = 0 - var/group = "Operations" - -/datum/supply_packs/New() - manifest += "
      " - for(var/path in contains) - if(!path || !ispath(path, /atom)) - continue - var/atom/O = path - manifest += "
    • [initial(O.name)]
    • " - manifest += "
    " - -/datum/supply_packs/specialops - name = "Special Ops supplies" - contains = list(/obj/item/storage/box/emps, - /obj/item/grenade/smokebomb, - /obj/item/grenade/smokebomb, - /obj/item/grenade/smokebomb, - /obj/item/storage/box/syndie_kit/special_pens, - /obj/item/grenade/chem_grenade/incendiary) - - cost = 20 - containertype = /obj/structure/closet/crate - containername = "crate" - group = "Security" - hidden = 1 - -/datum/supply_packs/randomised/illegalguns - name = "Illegal weapons crate" - num_contained = 2 - contains = list(/obj/item/gun/projectile/automatic/mini_uzi, - /obj/item/gun/projectile/shotgun/pump/rifle, - /obj/item/gun/projectile/silenced, - /obj/item/gun/projectile/pirate, - /obj/item/gun/projectile/revolver/derringer, - /obj/item/gun/projectile/dragunov, - /obj/item/gun/energy/retro) - cost = 120 - containertype = /obj/structure/closet/crate - containername = "crate" - contraband = 1 - group = "Security" - -/datum/supply_packs/forensics - name = "Auxiliary forensic tools" - contains = list(/obj/item/forensics/sample_kit, - /obj/item/forensics/sample_kit/powder, - /obj/item/storage/box/swabs, - /obj/item/storage/box/swabs, - /obj/item/storage/box/swabs, - /obj/item/storage/box/slides, - /obj/item/reagent_containers/spray/luminol, - /obj/item/device/uv_light) - cost = 30 - containertype = /obj/structure/closet/crate - containername = "Auxiliary forensic tools" - group = "Security" - -/datum/supply_packs/food - name = "Kitchen supply crate" - contains = list(/obj/item/reagent_containers/food/condiment/flour, - /obj/item/reagent_containers/food/condiment/flour, - /obj/item/reagent_containers/food/condiment/flour, - /obj/item/reagent_containers/food/condiment/flour, - /obj/item/reagent_containers/food/condiment/spacespice, - /obj/item/reagent_containers/food/condiment/peppermill, - /obj/item/reagent_containers/food/condiment/saltshaker, - /obj/item/reagent_containers/food/condiment/enzyme, - /obj/item/reagent_containers/food/drinks/milk, - /obj/item/reagent_containers/food/drinks/milk, - /obj/item/reagent_containers/food/drinks/soymilk, - /obj/item/reagent_containers/food/drinks/soymilk, - /obj/item/storage/fancy/egg_box, - /obj/item/reagent_containers/food/snacks/meat, - /obj/item/reagent_containers/food/snacks/meat) - cost = 10 - containertype = /obj/structure/closet/crate/freezer - containername = "Food crate" - group = "Supply" - -/datum/supply_packs/monkey - name = "Monkey crate" - contains = list (/obj/item/storage/box/monkeycubes) - cost = 20 - containertype = /obj/structure/closet/crate/freezer - containername = "Monkey crate" - group = "Hydroponics" - -/datum/supply_packs/farwa - name = "Farwa crate" - contains = list (/obj/item/storage/box/monkeycubes/farwacubes) - cost = 30 - containertype = /obj/structure/closet/crate/freezer - containername = "Farwa crate" - group = "Hydroponics" - -/datum/supply_packs/skrell - name = "Neaera crate" - contains = list (/obj/item/storage/box/monkeycubes/neaeracubes) - cost = 30 - containertype = /obj/structure/closet/crate/freezer - containername = "Neaera crate" - group = "Hydroponics" - -/datum/supply_packs/stok - name = "Stok crate" - contains = list (/obj/item/storage/box/monkeycubes/stokcubes) - cost = 30 - containertype = /obj/structure/closet/crate/freezer - containername = "Stok crate" - group = "Hydroponics" - -/datum/supply_packs/beanbagammo - name = "Beanbag shells" - contains = list(/obj/item/ammo_magazine/shotgun/beanbag, - /obj/item/ammo_magazine/shotgun/beanbag, - /obj/item/ammo_magazine/shotgun/beanbag) - cost = 30 - containertype = /obj/structure/closet/crate - containername = "Beanbag shells" - group = "Security" - -/datum/supply_packs/toner - name = "Toner cartridges" - contains = list(/obj/item/device/toner, - /obj/item/device/toner, - /obj/item/device/toner, - /obj/item/device/toner, - /obj/item/device/toner, - /obj/item/device/toner) - cost = 10 - containertype = /obj/structure/closet/crate - containername = "Toner cartridges" - group = "Supply" - -/datum/supply_packs/party - name = "Party equipment" - contains = list(/obj/item/storage/box/drinkingglasses, - /obj/item/reagent_containers/food/drinks/shaker, - /obj/item/reagent_containers/food/drinks/flask/barflask, - /obj/item/reagent_containers/food/drinks/bottle/patron, - /obj/item/reagent_containers/food/drinks/bottle/goldschlager, - /obj/item/reagent_containers/food/drinks/bottle/sarezhiwine, - /obj/item/storage/fancy/cigarettes/dromedaryco, - /obj/item/lipstick/random, - /obj/item/reagent_containers/food/drinks/bottle/small/ale, - /obj/item/reagent_containers/food/drinks/bottle/small/ale, - /obj/item/reagent_containers/food/drinks/bottle/small/beer, - /obj/item/reagent_containers/food/drinks/bottle/small/beer, - /obj/item/reagent_containers/food/drinks/bottle/small/beer, - /obj/item/reagent_containers/food/drinks/bottle/small/beer) - cost = 20 - containertype = /obj/structure/closet/crate - containername = "Party equipment" - group = "Hospitality" - -/datum/supply_packs/lasertag - name = "Lasertag equipment" - contains = list(/obj/item/gun/energy/lasertag/red, - /obj/item/clothing/suit/redtag, - /obj/item/gun/energy/lasertag/blue, - /obj/item/clothing/suit/bluetag) - containertype = /obj/structure/closet - containername = "Lasertag Closet" - group = "Hospitality" - cost = 20 - -/datum/supply_packs/internals - name = "Internals crate" - contains = list(/obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/tank/air, - /obj/item/tank/air, - /obj/item/tank/air) - cost = 10 - containertype = /obj/structure/closet/crate/internals - containername = "Internals crate" - group = "Atmospherics" - -/datum/supply_packs/evacuation - name = "Emergency equipment" - contains = list(/obj/item/storage/toolbox/emergency, - /obj/item/storage/toolbox/emergency, - /obj/item/clothing/suit/storage/hazardvest, - /obj/item/clothing/suit/storage/hazardvest, - /obj/item/clothing/suit/armor/vest, - /obj/item/clothing/suit/armor/vest, - /obj/item/tank/emergency_oxygen/engi, - /obj/item/tank/emergency_oxygen/engi, - /obj/item/tank/emergency_oxygen/engi, - /obj/item/tank/emergency_oxygen/engi, - /obj/item/clothing/suit/space/emergency, - /obj/item/clothing/suit/space/emergency, - /obj/item/clothing/suit/space/emergency, - /obj/item/clothing/suit/space/emergency, - /obj/item/clothing/head/helmet/space/emergency, - /obj/item/clothing/head/helmet/space/emergency, - /obj/item/clothing/head/helmet/space/emergency, - /obj/item/clothing/head/helmet/space/emergency, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas) - cost = 45 - containertype = /obj/structure/closet/crate/internals - containername = "Emergency crate" - group = "Atmospherics" - - - - -/datum/supply_packs/inflatable - name = "Inflatable barriers" - contains = list(/obj/item/storage/briefcase/inflatable, - /obj/item/storage/briefcase/inflatable, - /obj/item/storage/briefcase/inflatable) - cost = 20 - containertype = /obj/structure/closet/crate - containername = "Inflatable Barrier Crate" - group = "Atmospherics" - -/datum/supply_packs/janitor - name = "Janitorial supplies" - contains = list(/obj/item/reagent_containers/glass/bucket, - /obj/item/mop, - /obj/item/caution, - /obj/item/caution, - /obj/item/caution, - /obj/item/caution, - /obj/item/storage/bag/trash, - /obj/item/device/lightreplacer, - /obj/item/reagent_containers/spray/cleaner, - /obj/item/reagent_containers/glass/rag, - /obj/item/grenade/chem_grenade/cleaner, - /obj/item/grenade/chem_grenade/cleaner, - /obj/item/grenade/chem_grenade/cleaner, - /obj/structure/mopbucket) - cost = 10 - containertype = /obj/structure/closet/crate - containername = "Janitorial supplies" - group = "Supply" - -/datum/supply_packs/lightbulbs - name = "Replacement lights" - contains = list(/obj/item/storage/box/lights/mixed, - /obj/item/storage/box/lights/mixed, - /obj/item/storage/box/lights/mixed) - cost = 10 - containertype = /obj/structure/closet/crate - containername = "Replacement lights" - group = "Engineering" - -/datum/supply_packs/wizard - name = "Wizard costume" - contains = list(/obj/item/staff, - /obj/item/clothing/suit/wizrobe/fake, - /obj/item/clothing/shoes/sandal, - /obj/item/clothing/head/wizard/fake) - cost = 20 - containertype = /obj/structure/closet/crate - containername = "Wizard costume crate" - group = "Miscellaneous" - -/datum/supply_packs/mule - name = "MULEbot Crate" - contains = list(/obj/machinery/bot/mulebot) - cost = 20 - containertype = /obj/structure/largecrate/mule - containername = "MULEbot Crate" - group = "Operations" - -/datum/supply_packs/cargotrain - name = "Cargo Train Tug" - contains = list(/obj/vehicle/train/cargo/engine) - cost = 45 - containertype = /obj/structure/largecrate - containername = "Cargo Train Tug Crate" - group = "Operations" - -/datum/supply_packs/cargotrailer - name = "Cargo Train Trolley" - contains = list(/obj/vehicle/train/cargo/trolley) - cost = 15 - containertype = /obj/structure/largecrate - containername = "Cargo Train Trolley Crate" - group = "Operations" - -/datum/supply_packs/lisa - name = "Corgi Crate" - contains = list() - cost = 50 - containertype = /obj/structure/largecrate/animal/corgi - containername = "Corgi Crate" - group = "Hydroponics" - -/datum/supply_packs/hydroponics // -- Skie - name = "Hydroponics Supply Crate" - contains = list(/obj/item/reagent_containers/spray/plantbgone, - /obj/item/reagent_containers/spray/plantbgone, - /obj/item/reagent_containers/spray/plantbgone, - /obj/item/reagent_containers/spray/plantbgone, - /obj/item/reagent_containers/glass/bottle/ammonia, - /obj/item/reagent_containers/glass/bottle/ammonia, - /obj/item/material/hatchet, - /obj/item/material/minihoe, - /obj/item/device/analyzer/plant_analyzer, - /obj/item/clothing/gloves/botanic_leather, - /obj/item/clothing/suit/apron, - /obj/item/material/minihoe, - /obj/item/storage/box/botanydisk - ) // Updated with new things - cost = 15 - containertype = /obj/structure/closet/crate/hydroponics - containername = "Hydroponics crate" - access = access_hydroponics - group = "Hydroponics" - -//farm animals - useless and annoying, but potentially a good source of food -/datum/supply_packs/cow - name = "Cow crate" - cost = 30 - containertype = /obj/structure/largecrate/animal/cow - containername = "Cow crate" - access = access_hydroponics - group = "Hydroponics" - -/datum/supply_packs/goat - name = "Goat crate" - cost = 25 - containertype = /obj/structure/largecrate/animal/goat - containername = "Goat crate" - access = access_hydroponics - group = "Hydroponics" - -/datum/supply_packs/chicken - name = "Chicken crate" - cost = 20 - containertype = /obj/structure/largecrate/animal/chick - containername = "Chicken crate" - access = access_hydroponics - group = "Hydroponics" - -/datum/supply_packs/seeds - name = "Seeds crate" - contains = list(/obj/item/seeds/chiliseed, - /obj/item/seeds/berryseed, - /obj/item/seeds/cornseed, - /obj/item/seeds/eggplantseed, - /obj/item/seeds/tomatoseed, - /obj/item/seeds/appleseed, - /obj/item/seeds/soyaseed, - /obj/item/seeds/wheatseed, - /obj/item/seeds/carrotseed, - /obj/item/seeds/harebell, - /obj/item/seeds/lemonseed, - /obj/item/seeds/orangeseed, - /obj/item/seeds/grassseed, - /obj/item/seeds/sunflowerseed, - /obj/item/seeds/chantermycelium, - /obj/item/seeds/potatoseed, - /obj/item/seeds/sugarcaneseed) - cost = 10 - containertype = /obj/structure/closet/crate/hydroponics - containername = "Seeds crate" - access = access_hydroponics - group = "Hydroponics" - -/datum/supply_packs/weedcontrol - name = "Weed control crate" - contains = list(/obj/item/material/hatchet, - /obj/item/material/hatchet, - /obj/item/reagent_containers/spray/plantbgone, - /obj/item/reagent_containers/spray/plantbgone, - /obj/item/reagent_containers/spray/plantbgone, - /obj/item/reagent_containers/spray/plantbgone, - /obj/item/clothing/mask/gas, - /obj/item/clothing/mask/gas, - /obj/item/grenade/chem_grenade/antiweed, - /obj/item/grenade/chem_grenade/antiweed) - cost = 25 - containertype = /obj/structure/closet/crate/hydroponics - containername = "Weed control crate" - access = access_hydroponics - group = "Hydroponics" - -/datum/supply_packs/exoticseeds - name = "Exotic seeds crate" - contains = list(/obj/item/seeds/replicapod, - /obj/item/seeds/replicapod, - /obj/item/seeds/libertymycelium, - /obj/item/seeds/reishimycelium, - /obj/item/seeds/random, - /obj/item/seeds/random, - /obj/item/seeds/random, - /obj/item/seeds/random, - /obj/item/seeds/random, - /obj/item/seeds/random, - /obj/item/seeds/kudzuseed) - cost = 15 - containertype = /obj/structure/closet/crate/hydroponics - containername = "Exotic Seeds crate" - access = access_hydroponics - group = "Hydroponics" - -/datum/supply_packs/medical - name = "Medical crate" - contains = list(/obj/item/storage/firstaid/regular, - /obj/item/storage/firstaid/fire, - /obj/item/storage/firstaid/toxin, - /obj/item/storage/firstaid/o2, - /obj/item/storage/firstaid/adv, - /obj/item/reagent_containers/glass/bottle/antitoxin, - /obj/item/reagent_containers/glass/bottle/norepinephrine, - /obj/item/reagent_containers/glass/bottle/stoxin, - /obj/item/storage/box/syringes, - /obj/item/storage/box/autoinjectors) - cost = 10 - containertype = /obj/structure/closet/crate/medical - containername = "Medical crate" - group = "Medical" - -/datum/supply_packs/bloodpack - name = "BloodPack crate" - contains = list(/obj/item/storage/box/bloodpacks, - /obj/item/storage/box/bloodpacks, - /obj/item/storage/box/bloodpacks) - cost = 10 - containertype = /obj/structure/closet/crate/medical - containername = "BloodPack crate" - group = "Medical" - -/datum/supply_packs/bodybag - name = "Body bag crate" - contains = list(/obj/item/storage/box/bodybags, - /obj/item/storage/box/bodybags, - /obj/item/storage/box/bodybags) - cost = 10 - containertype = /obj/structure/closet/crate/medical - containername = "Body bag crate" - group = "Medical" - -/datum/supply_packs/cryobag - name = "Statis bag crate" - contains = list(/obj/item/bodybag/cryobag, - /obj/item/bodybag/cryobag, - /obj/item/bodybag/cryobag) - cost = 50 - containertype = /obj/structure/closet/crate/medical - containername = "Stasis bag crate" - group = "Medical" - -/datum/supply_packs/virus - name = "Virus sample crate" -/* contains = list(/obj/item/reagent_containers/glass/bottle/flu_virion, - /obj/item/reagent_containers/glass/bottle/cold, - /obj/item/reagent_containers/glass/bottle/epiglottis_virion, - /obj/item/reagent_containers/glass/bottle/liver_enhance_virion, - /obj/item/reagent_containers/glass/bottle/fake_gbs, - /obj/item/reagent_containers/glass/bottle/magnitis, - /obj/item/reagent_containers/glass/bottle/pierrot_throat, - /obj/item/reagent_containers/glass/bottle/brainrot, - /obj/item/reagent_containers/glass/bottle/hullucigen_virion, - /obj/item/storage/box/syringes, - /obj/item/storage/box/beakers, - /obj/item/reagent_containers/glass/bottle/mutagen)*/ - contains = list(/obj/item/virusdish/random, - /obj/item/virusdish/random, - /obj/item/virusdish/random, - /obj/item/virusdish/random) - cost = 25 - containertype = "/obj/structure/closet/crate/secure" - containername = "Virus sample crate" - access = access_cmo - group = "Science" - -/datum/supply_packs/metal50 - name = "50 metal sheets" - contains = list(/obj/item/stack/material/steel) - amount = 50 - cost = 10 - containertype = /obj/structure/closet/crate - containername = "Metal sheets crate" - group = "Engineering" - -/datum/supply_packs/metal200 - name = "Bulk metal crate" - contains = list(/obj/item/stack/material/steel, /obj/item/stack/material/steel, /obj/item/stack/material/steel,/obj/item/stack/material/steel) - amount = 50 - cost = 20 - containertype = /obj/structure/largecrate - containername = "Bulk metal crate" - group = "Engineering" - -/datum/supply_packs/glass50 - name = "50 glass sheets" - contains = list(/obj/item/stack/material/glass) - amount = 50 - cost = 10 - containertype = /obj/structure/closet/crate - containername = "Glass sheets crate" - group = "Engineering" - -/datum/supply_packs/wood50 - name = "50 wooden planks" - contains = list(/obj/item/stack/material/wood) - amount = 50 - cost = 10 - containertype = /obj/structure/closet/crate - containername = "Wooden planks crate" - group = "Engineering" - -/datum/supply_packs/plastic50 - name = "50 plastic sheets" - contains = list(/obj/item/stack/material/plastic) - amount = 50 - cost = 10 - containertype = /obj/structure/closet/crate - containername = "Plastic sheets crate" - group = "Engineering" - -/datum/supply_packs/smescoil - name = "Superconducting Magnetic Coil" - contains = list(/obj/item/smes_coil) - cost = 75 - containertype = /obj/structure/closet/crate - containername = "Superconducting Magnetic Coil crate" - group = "Engineering" - -/datum/supply_packs/electrical - name = "Electrical maintenance crate" - contains = list(/obj/item/storage/toolbox/electrical, - /obj/item/storage/toolbox/electrical, - /obj/item/clothing/gloves/yellow, - /obj/item/clothing/gloves/yellow, - /obj/item/cell, - /obj/item/cell, - /obj/item/cell/high, - /obj/item/cell/high) - cost = 15 - containertype = /obj/structure/closet/crate - containername = "Electrical maintenance crate" - group = "Engineering" - -/datum/supply_packs/mechanical - name = "Mechanical maintenance crate" - contains = list(/obj/item/storage/belt/utility/full, - /obj/item/storage/belt/utility/full, - /obj/item/storage/belt/utility/full, - /obj/item/clothing/suit/storage/hazardvest, - /obj/item/clothing/suit/storage/hazardvest, - /obj/item/clothing/suit/storage/hazardvest, - /obj/item/clothing/head/welding, - /obj/item/clothing/head/welding, - /obj/item/clothing/head/hardhat) - cost = 10 - containertype = /obj/structure/closet/crate - containername = "Mechanical maintenance crate" - group = "Engineering" - -/datum/supply_packs/watertank - name = "Water tank crate" - contains = list(/obj/structure/reagent_dispensers/watertank) - cost = 8 - containertype = /obj/structure/largecrate - containername = "water tank crate" - group = "Hydroponics" - -/datum/supply_packs/fueltank - name = "Fuel tank crate" - contains = list(/obj/structure/reagent_dispensers/fueltank) - cost = 8 - containertype = /obj/structure/largecrate - containername = "fuel tank crate" - group = "Engineering" - -/datum/supply_packs/coolanttank - name = "Coolant tank crate" - contains = list(/obj/structure/reagent_dispensers/coolanttank) - cost = 16 - containertype = /obj/structure/largecrate - containername = "coolant tank crate" - group = "Science" - -/datum/supply_packs/solar - name = "Solar Pack crate" - contains = list(/obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, - /obj/item/solar_assembly, // 21 Solar Assemblies. 1 Extra for the controller, - /obj/item/circuitboard/solar_control, - /obj/item/tracker_electronics, - /obj/item/paper/solar) - cost = 20 - containertype = /obj/structure/closet/crate - containername = "Solar pack crate" - group = "Engineering" - -/datum/supply_packs/engine - name = "Emitter crate" - contains = list(/obj/machinery/power/emitter, - /obj/machinery/power/emitter) - cost = 10 - containertype = /obj/structure/closet/crate/secure - containername = "Emitter crate" - access = access_ce - group = "Engineering" - -/datum/supply_packs/engine/field_gen - name = "Field Generator crate" - contains = list(/obj/machinery/field_generator, - /obj/machinery/field_generator) - containertype = /obj/structure/closet/crate/secure - containername = "Field Generator crate" - access = access_ce - group = "Engineering" - -/datum/supply_packs/engine/sing_gen - name = "Singularity Generator crate" - contains = list(/obj/machinery/the_singularitygen) - containertype = /obj/structure/closet/crate/secure - containername = "Singularity Generator crate" - access = access_ce - group = "Engineering" - -/datum/supply_packs/engine/collector - name = "Collector crate" - contains = list(/obj/machinery/power/rad_collector, - /obj/machinery/power/rad_collector, - /obj/machinery/power/rad_collector) - containername = "Collector crate" - group = "Engineering" - -/datum/supply_packs/engine/PA - name = "Particle Accelerator crate" - cost = 40 - contains = list(/obj/structure/particle_accelerator/fuel_chamber, - /obj/machinery/particle_accelerator/control_box, - /obj/structure/particle_accelerator/particle_emitter/center, - /obj/structure/particle_accelerator/particle_emitter/left, - /obj/structure/particle_accelerator/particle_emitter/right, - /obj/structure/particle_accelerator/power_box, - /obj/structure/particle_accelerator/end_cap) - containertype = /obj/structure/closet/crate/secure - containername = "Particle Accelerator crate" - access = access_ce - group = "Engineering" - -/datum/supply_packs/mecha_ripley - name = "Circuit Crate (\"Ripley\" APLU)" - contains = list(/obj/item/book/manual/ripley_build_and_repair, - /obj/item/circuitboard/mecha/ripley/main, //TEMPORARY due to lack of circuitboard printer, - /obj/item/circuitboard/mecha/ripley/peripherals) //TEMPORARY due to lack of circuitboard printer - cost = 30 - containertype = /obj/structure/closet/crate/secure - containername = "APLU \"Ripley\" Circuit Crate" - access = access_robotics - group = "Science" - -/datum/supply_packs/mecha_odysseus - name = "Circuit Crate (\"Odysseus\")" - contains = list(/obj/item/circuitboard/mecha/odysseus/peripherals, //TEMPORARY due to lack of circuitboard printer, - /obj/item/circuitboard/mecha/odysseus/main) //TEMPORARY due to lack of circuitboard printer - cost = 25 - containertype = /obj/structure/closet/crate/secure - containername = "\"Odysseus\" Circuit Crate" - access = access_robotics - group = "Science" - -/datum/supply_packs/hoverpod - name = "Hoverpod Shipment" - contains = list() - cost = 80 - containertype = /obj/structure/largecrate/hoverpod - containername = "Hoverpod Crate" - group = "Operations" - -/datum/supply_packs/robotics - name = "Robotics assembly crate" - contains = list(/obj/item/device/assembly/prox_sensor, - /obj/item/device/assembly/prox_sensor, - /obj/item/device/assembly/prox_sensor, - /obj/item/storage/toolbox/electrical, - /obj/item/device/flash, - /obj/item/device/flash, - /obj/item/device/flash, - /obj/item/device/flash, - /obj/item/cell/high, - /obj/item/cell/high) - cost = 10 - containertype = /obj/structure/closet/crate/secure/gear - containername = "Robotics assembly" - access = access_robotics - group = "Engineering" - -/datum/supply_packs/phoron - name = "Phoron assembly crate" - contains = list(/obj/item/tank/phoron, - /obj/item/tank/phoron, - /obj/item/tank/phoron, - /obj/item/device/assembly/igniter, - /obj/item/device/assembly/igniter, - /obj/item/device/assembly/igniter, - /obj/item/device/assembly/prox_sensor, - /obj/item/device/assembly/prox_sensor, - /obj/item/device/assembly/prox_sensor, - /obj/item/device/assembly/timer, - /obj/item/device/assembly/timer, - /obj/item/device/assembly/timer) - cost = 10 - containertype = /obj/structure/closet/crate/secure/phoron - containername = "Phoron assembly crate" - access = access_tox_storage - group = "Science" - -/datum/supply_packs/weapons - name = "Weapons crate" - contains = list(/obj/item/gun/energy/rifle/laser, - /obj/item/gun/energy/rifle/laser, - /obj/item/gun/projectile/sec, - /obj/item/gun/projectile/sec, - /obj/item/ammo_magazine/c45m, - /obj/item/ammo_magazine/c45m) - cost = 80 - containertype = /obj/structure/closet/crate/secure/weapon - containername = "Weapons crate" - access = access_security - group = "Security" - -/datum/supply_packs/nonlethals - name = "Nonlethal Weapons crate" - contains = list(/obj/item/melee/baton, - /obj/item/melee/baton, - /obj/item/gun/energy/taser, - /obj/item/gun/energy/taser, - /obj/item/storage/box/flashbangs, - /obj/item/storage/box/flashbangs, - /obj/item/ammo_magazine/tranq, - /obj/item/storage/box/zipties) - cost = 60 - containertype = /obj/structure/closet/crate/secure/weapon - containername = "Weapons crate" - access = access_security - group = "Security" - -/datum/supply_packs/flareguns - name = "Flare guns crate" - contains = list(/obj/item/gun/projectile/sec/flash, - /obj/item/ammo_magazine/c45m/flash, - /obj/item/gun/projectile/shotgun/doublebarrel/flare, - /obj/item/storage/box/flashshells) - cost = 25 - containertype = /obj/structure/closet/crate/secure/weapon - containername = "Flare gun crate" - access = access_security - group = "Security" - -/datum/supply_packs/randomised/armor - num_contained = 5 - contains = list(/obj/item/clothing/suit/armor/vest, - /obj/item/clothing/suit/armor/vest/security, - /obj/item/clothing/suit/storage/vest/detective, - /obj/item/clothing/suit/storage/vest/hos, - /obj/item/clothing/suit/storage/vest/pcrc, - /obj/item/clothing/suit/storage/vest/warden, - /obj/item/clothing/suit/storage/vest/officer, - /obj/item/clothing/suit/storage/vest) - - name = "Armor crate" - cost = 40 - containertype = /obj/structure/closet/crate/secure - containername = "Armor crate" - access = access_security - group = "Security" - - -/datum/supply_packs/riot - name = "Riot gear crate" - contains = list(/obj/item/melee/baton, - /obj/item/melee/baton, - /obj/item/melee/baton, - /obj/item/shield/riot, - /obj/item/shield/riot, - /obj/item/shield/riot, - /obj/item/handcuffs, - /obj/item/handcuffs, - /obj/item/handcuffs, - /obj/item/clothing/head/helmet/riot, - /obj/item/clothing/suit/armor/riot, - /obj/item/clothing/head/helmet/riot, - /obj/item/clothing/suit/armor/riot, - /obj/item/clothing/head/helmet/riot, - /obj/item/clothing/suit/armor/riot, - /obj/item/storage/box/flashbangs, - /obj/item/storage/box/teargas, - /obj/item/storage/box/beanbags, - /obj/item/storage/box/handcuffs) - cost = 60 - containertype = /obj/structure/closet/crate/secure - containername = "Riot gear crate" - access = access_armory - group = "Security" - -/datum/supply_packs/energyweapons - name = "Energy weapons crate" - contains = list(/obj/item/gun/energy/rifle/laser, - /obj/item/gun/energy/rifle/laser, - /obj/item/gun/energy/rifle/laser) - cost = 50 - containertype = /obj/structure/closet/crate/secure - containername = "energy weapons crate" - access = access_armory - group = "Security" - -/datum/supply_packs/shotgun - name = "Shotgun crate" - contains = list(/obj/item/clothing/suit/armor/bulletproof, - /obj/item/clothing/head/helmet/ballistic, - /obj/item/ammo_magazine/shotgun, - /obj/item/ammo_magazine/shotgun/shell, - /obj/item/gun/projectile/shotgun/pump/combat, - /obj/item/gun/projectile/shotgun/pump/combat) - cost = 65 - containertype = /obj/structure/closet/crate/secure - containername = "Shotgun crate" - access = access_armory - group = "Security" - -/datum/supply_packs/erifle - name = "Energy marksman crate" - contains = list(/obj/item/clothing/suit/armor/laserproof, - /obj/item/clothing/head/helmet/ablative, - /obj/item/gun/energy/sniperrifle, - /obj/item/gun/energy/sniperrifle) - cost = 90 - containertype = /obj/structure/closet/crate/secure - containername = "Energy marksman crate" - access = access_armory - group = "Security" - -/datum/supply_packs/shotgunammo - name = "Ballistic ammunition crate" - contains = list(/obj/item/ammo_magazine/shotgun, - /obj/item/ammo_magazine/shotgun, - /obj/item/ammo_magazine/shotgun/shell, - /obj/item/ammo_magazine/shotgun/shell, - /obj/item/ammo_magazine/shotgun/incendiary) - cost = 60 - containertype = /obj/structure/closet/crate/secure - containername = "ballistic ammunition crate" - access = access_armory - group = "Security" - -/datum/supply_packs/ionweapons - name = "Electromagnetic weapons crate" - contains = list(/obj/item/gun/energy/ionrifle, - /obj/item/gun/energy/ionrifle, - /obj/item/ammo_magazine/shotgun/emp, - /obj/item/storage/box/emps) - cost = 50 - containertype = /obj/structure/closet/crate/secure - containername = "electromagnetic weapons crate" - access = access_armory - group = "Security" - -/datum/supply_packs/randomised/automatic - name = "Automatic weapon crate" - num_contained = 2 - contains = list(/obj/item/gun/projectile/automatic/wt550, - /obj/item/gun/projectile/automatic/rifle/z8) - cost = 90 - containertype = /obj/structure/closet/crate/secure - containername = "Automatic weapon crate" - access = access_armory - group = "Security" - -/datum/supply_packs/randomised/autoammo - name = "Automatic weapon ammunition crate" - num_contained = 6 - contains = list(/obj/item/ammo_magazine/mc9mmt, - /obj/item/ammo_magazine/mc9mmt/rubber, - /obj/item/ammo_magazine/a556) - cost = 20 - containertype = /obj/structure/closet/crate/secure - containername = "Automatic weapon ammunition crate" - access = access_armory - group = "Security" - -/* -/datum/supply_packs/loyalty - name = "Loyalty implant crate" - contains = list (/obj/item/storage/lockbox/loyalty) - cost = 60 - containertype = /obj/structure/closet/crate/secure - containername = "Loyalty implant crate" - access = access_armory - group = "Security" -*/ - -/datum/supply_packs/expenergy - name = "Experimental energy gear crate" - contains = list(/obj/item/clothing/suit/armor/laserproof, - /obj/item/clothing/head/helmet/ablative, - /obj/item/gun/energy/gun, - /obj/item/gun/energy/gun) - cost = 50 - containertype = /obj/structure/closet/crate/secure - containername = "Experimental energy gear crate" - access = access_armory - group = "Security" - -/datum/supply_packs/exparmor - name = "Experimental armor crate" - contains = list(/obj/item/clothing/suit/armor/laserproof,, - /obj/item/clothing/head/helmet/ablative, - /obj/item/clothing/suit/armor/bulletproof, - /obj/item/clothing/head/helmet/ballistic, - /obj/item/clothing/head/helmet/riot, - /obj/item/clothing/suit/armor/riot) - cost = 35 - containertype = /obj/structure/closet/crate/secure - containername = "Experimental armor crate" - access = access_armory - group = "Security" - -/datum/supply_packs/securitybarriers - name = "Security barrier crate" - contains = list(/obj/machinery/deployable/barrier, - /obj/machinery/deployable/barrier, - /obj/machinery/deployable/barrier, - /obj/machinery/deployable/barrier) - cost = 20 - containertype = /obj/structure/closet/crate/secure/gear - containername = "Security barrier crate" - group = "Security" - -/datum/supply_packs/securitybarriers - name = "Wall shield Generators" - contains = list(/obj/machinery/shieldwallgen, - /obj/machinery/shieldwallgen, - /obj/machinery/shieldwallgen, - /obj/machinery/shieldwallgen) - cost = 20 - containertype = /obj/structure/closet/crate/secure - containername = "wall shield generators crate" - access = access_teleporter - group = "Security" - -/datum/supply_packs/randomised - var/num_contained = 4 //number of items picked to be contained in a randomised crate - contains = list(/obj/item/clothing/head/collectable/chef, - /obj/item/clothing/head/collectable/paper, - /obj/item/clothing/head/collectable/tophat, - /obj/item/clothing/head/collectable/captain, - /obj/item/clothing/head/collectable/beret, - /obj/item/clothing/head/collectable/welding, - /obj/item/clothing/head/collectable/flatcap, - /obj/item/clothing/head/collectable/pirate, - /obj/item/clothing/head/collectable/kitty, - /obj/item/clothing/head/collectable/rabbitears, - /obj/item/clothing/head/collectable/wizard, - /obj/item/clothing/head/collectable/hardhat, - /obj/item/clothing/head/collectable/HoS, - /obj/item/clothing/head/collectable/thunderdome, - /obj/item/clothing/head/collectable/swat, - /obj/item/clothing/head/collectable/slime, - /obj/item/clothing/head/collectable/police, - /obj/item/clothing/head/collectable/slime, - /obj/item/clothing/head/collectable/xenom, - /obj/item/clothing/head/collectable/petehat) - name = "Collectable hat crate!" - cost = 200 - containertype = /obj/structure/closet/crate - containername = "Collectable hats crate! Brought to you by Bass.inc!" - group = "Miscellaneous" - -/datum/supply_packs/randomised/New() - manifest += "Contains any [num_contained] of:" - ..() - -/datum/supply_packs/artscrafts - name = "Arts and Crafts supplies" - contains = list(/obj/item/storage/fancy/crayons, - /obj/item/device/camera, - /obj/item/device/camera_film, - /obj/item/device/camera_film, - /obj/item/storage/photo_album, - /obj/item/packageWrap, - /obj/item/reagent_containers/glass/paint/red, - /obj/item/reagent_containers/glass/paint/green, - /obj/item/reagent_containers/glass/paint/blue, - /obj/item/reagent_containers/glass/paint/yellow, - /obj/item/reagent_containers/glass/paint/purple, - /obj/item/reagent_containers/glass/paint/black, - /obj/item/reagent_containers/glass/paint/white, - /obj/item/contraband/poster, - /obj/item/wrapping_paper, - /obj/item/wrapping_paper, - /obj/item/wrapping_paper) - cost = 10 - containertype = "/obj/structure/closet/crate" - containername = "Arts and Crafts crate" - group = "Operations" - - -/datum/supply_packs/randomised/contraband - num_contained = 5 - contains = list(/obj/item/seeds/bloodtomatoseed, - /obj/item/storage/pill_bottle/zoom, - /obj/item/storage/pill_bottle/happy, - /obj/item/reagent_containers/food/drinks/bottle/pwine) - - name = "Contraband crate" - cost = 30 - containertype = /obj/structure/closet/crate - containername = "Unlabeled crate" - contraband = 1 - group = "Operations" - -/datum/supply_packs/boxes - name = "Empty boxes" - contains = list(/obj/item/storage/box, - /obj/item/storage/box, - /obj/item/storage/box, - /obj/item/storage/box, - /obj/item/storage/box, - /obj/item/storage/box, - /obj/item/storage/box, - /obj/item/storage/box, - /obj/item/storage/box, - /obj/item/storage/box) - cost = 10 - containertype = "/obj/structure/closet/crate" - containername = "Empty box crate" - group = "Supply" - -/datum/supply_packs/surgery - name = "Surgery crate" - contains = list(/obj/item/cautery, - /obj/item/surgicaldrill, - /obj/item/clothing/mask/breath/medical, - /obj/item/tank/anesthetic, - /obj/item/FixOVein, - /obj/item/hemostat, - /obj/item/scalpel, - /obj/item/bonegel, - /obj/item/retractor, - /obj/item/bonesetter, - /obj/item/circular_saw) - cost = 25 - containertype = "/obj/structure/closet/crate/secure" - containername = "Surgery crate" - access = access_medical - group = "Medical" - -/datum/supply_packs/sterile - name = "Sterile equipment crate" - contains = list(/obj/item/clothing/under/rank/medical/green, - /obj/item/clothing/under/rank/medical/green, - /obj/item/clothing/head/surgery/green, - /obj/item/clothing/head/surgery/green, - /obj/item/storage/box/masks, - /obj/item/storage/box/gloves, - /obj/item/storage/belt/medical, - /obj/item/storage/belt/medical, - /obj/item/storage/belt/medical) - cost = 15 - containertype = "/obj/structure/closet/crate" - containername = "Sterile equipment crate" - group = "Medical" - -/datum/supply_packs/beer - contains = list(/obj/structure/reagent_dispensers/beerkeg) - name = "Beer Keg" - cost = 10 - containertype = /obj/structure/largecrate - containername = "Beer Crate" - group = "Hospitality" - -/datum/supply_packs/xuizi - contains = list(/obj/structure/reagent_dispensers/xuizikeg) - name = "Xuizi Juice Keg" - cost = 20 - containertype = /obj/structure/largecrate - containername = "Xuizi Juice Crate" - group = "Hospitality" - -/datum/supply_packs/randomised/pizza - num_contained = 5 - contains = list(/obj/item/pizzabox/margherita, - /obj/item/pizzabox/mushroom, - /obj/item/pizzabox/meat, - /obj/item/pizzabox/vegetable) - name = "Surprise pack of five pizzas" - cost = 15 - containertype = /obj/structure/closet/crate/freezer - containername = "Pizza crate" - group = "Hospitality" - -/datum/supply_packs/randomised/costume - num_contained = 2 - contains = list(/obj/item/clothing/suit/pirate, - /obj/item/clothing/suit/judgerobe, - /obj/item/clothing/accessory/wcoat, - /obj/item/clothing/suit/hastur, - /obj/item/clothing/suit/holidaypriest, - /obj/item/clothing/suit/nun, - /obj/item/clothing/suit/imperium_monk, - /obj/item/clothing/suit/ianshirt, - /obj/item/clothing/under/gimmick/rank/captain/suit, - /obj/item/clothing/under/gimmick/rank/head_of_personnel/suit, - /obj/item/clothing/under/lawyer/purpsuit, - /obj/item/clothing/under/rank/mailman, - /obj/item/clothing/under/dress/dress_saloon, - /obj/item/clothing/accessory/suspenders, - /obj/item/clothing/suit/storage/toggle/labcoat/mad, - /obj/item/clothing/suit/bio_suit/plaguedoctorsuit, - /obj/item/clothing/under/owl, - /obj/item/clothing/under/waiter, - /obj/item/clothing/under/gladiator, - /obj/item/clothing/under/soviet, - /obj/item/clothing/under/scratch, - /obj/item/clothing/under/wedding/bride_white, - /obj/item/clothing/suit/chef, - /obj/item/clothing/suit/apron/overalls, - /obj/item/clothing/under/redcoat, - /obj/item/clothing/under/kilt) - name = "Costumes crate" - cost = 10 - containertype = /obj/structure/closet/crate - containername = "Actor Costumes" - group = "Miscellaneous" - -/datum/supply_packs/formal_wear - contains = list(/obj/item/clothing/head/bowler, - /obj/item/clothing/head/that, - /obj/item/clothing/suit/storage/toggle/lawyer/bluejacket, - /obj/item/clothing/suit/storage/lawyer/purpjacket, - /obj/item/clothing/under/suit_jacket, - /obj/item/clothing/under/suit_jacket/female, - /obj/item/clothing/under/suit_jacket/really_black, - /obj/item/clothing/under/suit_jacket/red, - /obj/item/clothing/under/lawyer/bluesuit, - /obj/item/clothing/under/lawyer/purpsuit, - /obj/item/clothing/shoes/black, - /obj/item/clothing/shoes/black, - /obj/item/clothing/shoes/leather, - /obj/item/clothing/suit/wcoat) - name = "Formalwear closet" - cost = 30 - containertype = /obj/structure/closet - containername = "Formalwear for the best occasions." - group = "Miscellaneous" - -/datum/supply_packs/randomised/card_packs - num_contained = 5 - contains = list(/obj/item/pack/cardemon, - /obj/item/pack/spaceball, - /obj/item/deck/holder) - name = "Trading Card Crate" - cost = 20 - containertype = /obj/structure/closet/crate - containername = "cards crate" - group = "Miscellaneous" - -/datum/supply_packs/shield_gen - contains = list(/obj/item/circuitboard/shield_gen) - name = "Bubble shield generator circuitry" - cost = 50 - containertype = /obj/structure/closet/crate/secure - containername = "bubble shield generator circuitry crate" - group = "Engineering" - access = access_ce - -/datum/supply_packs/shield_gen_ex - contains = list(/obj/item/circuitboard/shield_gen_ex) - name = "Hull shield generator circuitry" - cost = 50 - containertype = /obj/structure/closet/crate/secure - containername = "hull shield generator circuitry crate" - group = "Engineering" - access = access_ce - -/datum/supply_packs/shield_cap - contains = list(/obj/item/circuitboard/shield_cap) - name = "Bubble shield capacitor circuitry" - cost = 50 - containertype = /obj/structure/closet/crate/secure - containername = "shield capacitor circuitry crate" - group = "Engineering" - access = access_ce - -/datum/supply_packs/smbig - name = "Supermatter Core" - contains = list(/obj/machinery/power/supermatter) - cost = 150 - containertype = /obj/structure/closet/crate/secure/phoron - containername = "Supermatter crate (CAUTION)" - group = "Engineering" - access = access_ce - - -/* /datum/supply_packs/smsmall // Currently nonfunctional, waiting on virgil - name = "Supermatter Shard" - contains = list(/obj/machinery/power/supermatter/shard) - cost = 25 - containertype = /obj/structure/closet/crate/secure/phoron - containername = "Supermatter shard crate (CAUTION)" - access = access_ce - group = "Engineering" */ - -/datum/supply_packs/eftpos - contains = list(/obj/item/device/eftpos) - name = "EFTPOS scanner" - cost = 10 - containertype = /obj/structure/closet/crate - containername = "EFTPOS crate" - group = "Miscellaneous" - -/datum/supply_packs/teg - contains = list(/obj/machinery/power/generator) - name = "Mark I Thermoelectric Generator" - cost = 75 - containertype = /obj/structure/closet/crate/secure/large - containername = "Mk1 TEG crate" - group = "Engineering" - access = access_engine - -/datum/supply_packs/circulator - contains = list(/obj/machinery/atmospherics/binary/circulator) - name = "Binary atmospheric circulator" - cost = 60 - containertype = /obj/structure/closet/crate/secure/large - containername = "Atmospheric circulator crate" - group = "Engineering" - access = access_engine - -/datum/supply_packs/air_dispenser - contains = list(/obj/machinery/pipedispenser/orderable) - name = "Pipe Dispenser" - cost = 35 - containertype = /obj/structure/closet/crate/secure/large - containername = "Pipe Dispenser Crate" - group = "Engineering" - access = access_atmospherics - -/datum/supply_packs/disposals_dispenser - contains = list(/obj/machinery/pipedispenser/disposal/orderable) - name = "Disposals Pipe Dispenser" - cost = 35 - containertype = /obj/structure/closet/crate/secure/large - containername = "Disposal Dispenser Crate" - group = "Engineering" - access = access_atmospherics - -/datum/supply_packs/bee_keeper - name = "Beekeeping crate" - contains = list(/obj/item/beehive_assembly, - /obj/item/bee_smoker, - /obj/item/bee_net, - /obj/item/bee_net, - /obj/item/clothing/suit/bio_suit/general,//beekeeping suit - /obj/item/clothing/head/bio_hood/general, - /obj/item/honey_frame, - /obj/item/honey_frame, - /obj/item/honey_frame, - /obj/item/honey_frame, - /obj/item/honey_frame, - /obj/item/bee_pack, - /obj/machinery/honey_extractor, - /obj/item/book/manual/hydroponics_beekeeping) - cost = 40 - containertype = /obj/structure/largecrate - containername = "Beekeeping crate" - access = access_hydroponics - group = "Hydroponics" - -/datum/supply_packs/aliengloves - name = "Non-Human Glove Kit" - contains = list(/obj/item/clothing/gloves/yellow/specialt, - /obj/item/clothing/gloves/yellow/specialt, - /obj/item/clothing/gloves/yellow/specialt, - /obj/item/clothing/gloves/yellow/specialu, - /obj/item/clothing/gloves/yellow/specialu, - /obj/item/clothing/gloves/yellow/specialu) - cost = 25 - containertype = /obj/structure/closet/crate - containername = "speciality gloves kit" - group = "Supply" - -/datum/supply_packs/alienmedicalgloves - name = "Non-Human Sterile Glove Kit" - contains = list(/obj/item/clothing/gloves/latex/unathi, - /obj/item/clothing/gloves/latex/nitrile/unathi, - /obj/item/clothing/gloves/latex/nitrile/unathi, - /obj/item/clothing/gloves/latex/tajara, - /obj/item/clothing/gloves/latex/nitrile/tajara, - /obj/item/clothing/gloves/latex/nitrile/tajara) - cost = 25 - containertype = /obj/structure/closet/crate - containername = "speciality sterile gloves kit" - group = "Medical" - -/datum/supply_packs/cardboard_sheets - contains = list(/obj/item/stack/material/cardboard) - name = "50 cardboard sheets" - amount = 50 - cost = 10 - containertype = /obj/structure/closet/crate - containername = "Cardboard sheets crate" - group = "Miscellaneous" - -/datum/supply_packs/bureaucracy - contains = list(/obj/item/clipboard, - /obj/item/clipboard, - /obj/item/pen/red, - /obj/item/pen/blue, - /obj/item/pen/blue, - /obj/item/device/camera_film, - /obj/item/folder/blue, - /obj/item/folder/red, - /obj/item/folder/yellow, - /obj/item/hand_labeler, - /obj/item/tape_roll, - /obj/structure/filingcabinet/chestdrawer{anchored = 0}, - /obj/item/paper_bin) - name = "Office supplies" - cost = 15 - containertype = /obj/structure/closet/crate - containername = "Office supplies crate" - group = "Supply" - -/datum/supply_packs/radsuit - contains = list(/obj/item/clothing/suit/radiation, - /obj/item/clothing/suit/radiation, - /obj/item/clothing/suit/radiation, - /obj/item/clothing/head/radiation, - /obj/item/clothing/head/radiation, - /obj/item/clothing/head/radiation) - name = "Radiation suits package" - cost = 20 - containertype = /obj/structure/closet/radiation - containername = "Radiation suit locker" - group = "Engineering" - -/datum/supply_packs/tactical - name = "Tactical suits" - containertype = /obj/structure/closet/crate/secure - containername = "Tactical Suit Locker" - cost = 45 - group = "Security" - access = access_armory - contains = 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/glasses/sunglasses/sechud/tactical, - /obj/item/storage/belt/security/tactical, - /obj/item/clothing/shoes/jackboots, - /obj/item/clothing/gloves/black, - /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/glasses/sunglasses/sechud/tactical, - /obj/item/storage/belt/security/tactical, - /obj/item/clothing/shoes/jackboots, - /obj/item/clothing/gloves/black) - -/datum/supply_packs/carpet - name = "Imported carpet" - containertype = /obj/structure/closet - containername = "Imported carpet crate" - cost = 15 - group = "Miscellaneous" - contains = list(/obj/item/stack/tile/carpet) - amount = 50 - -/datum/supply_packs/hydrotray - name = "Empty hydroponics tray" - cost = 10 - containertype = /obj/structure/closet/crate/hydroponics - containername = "Hydroponics tray crate" - group = "Hydroponics" - contains = list(/obj/machinery/portable_atmospherics/hydroponics{anchored = 0}) - access = access_hydroponics - -/datum/supply_packs/canister_empty - name = "Empty gas canister" - cost = 7 - containername = "Empty gas canister crate" - containertype = /obj/structure/largecrate - contains = list(/obj/machinery/portable_atmospherics/canister) - group = "Atmospherics" - -/datum/supply_packs/canister_air - name = "Air canister" - cost = 10 - containername = "Air canister crate" - containertype = /obj/structure/largecrate - group = "Atmospherics" - contains = list(/obj/machinery/portable_atmospherics/canister/air) - -/datum/supply_packs/canister_oxygen - name = "Oxygen canister" - cost = 15 - containername = "Oxygen canister crate" - containertype = /obj/structure/largecrate - group = "Atmospherics" - contains = list(/obj/machinery/portable_atmospherics/canister/oxygen) - -/datum/supply_packs/canister_nitrogen - name = "Nitrogen canister" - cost = 10 - containername = "Nitrogen canister crate" - containertype = /obj/structure/largecrate - group = "Atmospherics" - contains = list(/obj/machinery/portable_atmospherics/canister/nitrogen) - -/datum/supply_packs/canister_phoron - name = "Phoron gas canister" - cost = 60 - containername = "Phoron gas canister crate" - containertype = /obj/structure/closet/crate/secure/large - access = access_atmospherics - group = "Atmospherics" - contains = list(/obj/machinery/portable_atmospherics/canister/phoron) - -/datum/supply_packs/canister_sleeping_agent - name = "N2O gas canister" - cost = 40 - containername = "N2O gas canister crate" - containertype = /obj/structure/closet/crate/secure/large - access = access_atmospherics - group = "Atmospherics" - contains = list(/obj/machinery/portable_atmospherics/canister/sleeping_agent) - -/datum/supply_packs/canister_carbon_dioxide - name = "Carbon dioxide gas canister" - cost = 40 - containername = "CO2 canister crate" - containertype = /obj/structure/closet/crate/secure/large - access = access_atmospherics - group = "Atmospherics" - contains = list(/obj/machinery/portable_atmospherics/canister/carbon_dioxide) - -/datum/supply_packs/pacman_parts - name = "P.A.C.M.A.N. portable generator parts" - cost = 45 - containername = "P.A.C.M.A.N. Portable Generator Construction Kit" - containertype = /obj/structure/closet/crate/secure - group = "Engineering" - access = access_tech_storage - contains = list(/obj/item/stock_parts/micro_laser, - /obj/item/stock_parts/capacitor, - /obj/item/stock_parts/matter_bin, - /obj/item/circuitboard/pacman) - -/datum/supply_packs/super_pacman_parts - name = "Super P.A.C.M.A.N. portable generator parts" - cost = 55 - containername = "Super P.A.C.M.A.N. portable generator construction kit" - containertype = /obj/structure/closet/crate/secure - group = "Engineering" - access = access_tech_storage - contains = list(/obj/item/stock_parts/micro_laser, - /obj/item/stock_parts/capacitor, - /obj/item/stock_parts/matter_bin, - /obj/item/circuitboard/pacman/super) - -/datum/supply_packs/witch - name = "Witch costume" - containername = "Witch costume" - containertype = /obj/structure/closet - cost = 20 - contains = list(/obj/item/clothing/suit/wizrobe/marisa/fake, - /obj/item/clothing/shoes/sandal, - /obj/item/clothing/head/wizard/marisa/fake, - /obj/item/staff/broom) - group = "Miscellaneous" - -/datum/supply_packs/randomised/costume_hats - name = "Costume hats" - containername = "Actor hats crate" - containertype = /obj/structure/closet - cost = 10 - num_contained = 2 - contains = list(/obj/item/clothing/head/redcoat, - /obj/item/clothing/head/mailman, - /obj/item/clothing/head/plaguedoctorhat, - /obj/item/clothing/head/pirate, - /obj/item/clothing/head/hasturhood, - /obj/item/clothing/head/powdered_wig, - /obj/item/clothing/head/hairflower, - /obj/item/clothing/mask/gas/owl_mask, - /obj/item/clothing/mask/gas/monkeymask, - /obj/item/clothing/head/helmet/gladiator, - /obj/item/clothing/head/ushanka) - group = "Miscellaneous" - -/datum/supply_packs/randomised/webbing - name = "Webbing crate" - num_contained = 1 - contains = list(/obj/item/clothing/accessory/holster, - /obj/item/clothing/accessory/storage/brown_vest, - /obj/item/clothing/accessory/storage/webbing, - /obj/item/clothing/accessory/storage) - cost = 15 - containertype = /obj/structure/closet/crate - containername = "Webbing crate" - group = "Operations" - -/datum/supply_packs/spare_pda - name = "Spare PDAs" - cost = 10 - containertype = /obj/structure/closet/crate - containername = "Spare PDA crate" - group = "Supply" - contains = list(/obj/item/device/pda, - /obj/item/device/pda, - /obj/item/device/pda) - -/datum/supply_packs/randomised/dresses - name = "Womens formal dress locker" - containername = "Pretty dress locker" - containertype = /obj/structure/closet - cost = 15 - num_contained = 1 - contains = list(/obj/item/clothing/under/wedding/bride_orange, - /obj/item/clothing/under/wedding/bride_purple, - /obj/item/clothing/under/wedding/bride_blue, - /obj/item/clothing/under/wedding/bride_red, - /obj/item/clothing/under/wedding/bride_white, - /obj/item/clothing/under/sundress, - /obj/item/clothing/under/dress/dress_green, - /obj/item/clothing/under/dress/dress_pink, - /obj/item/clothing/under/dress/dress_orange, - /obj/item/clothing/under/dress/dress_yellow, - /obj/item/clothing/under/dress/dress_saloon) - group = "Miscellaneous" - -/datum/supply_packs/painters - name = "Station Painting Supplies" - cost = 10 - containername = "station painting supplies crate" - containertype = /obj/structure/closet/crate - group = "Engineering" - contains = list(/obj/item/device/pipe_painter, - /obj/item/device/pipe_painter, - /obj/item/device/floor_painter, - /obj/item/device/floor_painter) - -/datum/supply_packs/bluespacerelay - name = "Emergency Bluespace Relay Assembly Kit" - cost = 75 - containername = "emergency bluespace relay assembly kit" - containertype = /obj/structure/closet/crate - group = "Engineering" - contains = list(/obj/item/circuitboard/bluespacerelay, - /obj/item/stock_parts/manipulator, - /obj/item/stock_parts/manipulator, - /obj/item/stock_parts/subspace/filter, - /obj/item/stock_parts/subspace/crystal, - /obj/item/storage/toolbox/electrical) - -/datum/supply_packs/randomised/exosuit_mod - num_contained = 1 - contains = list( - /obj/item/device/kit/paint/ripley, - /obj/item/device/kit/paint/ripley/death, - /obj/item/device/kit/paint/ripley/flames_red, - /obj/item/device/kit/paint/ripley/flames_blue - ) - name = "Random APLU modkit" - cost = 200 - containertype = /obj/structure/closet/crate - containername = "heavy crate" - group = "Miscellaneous" - -/datum/supply_packs/randomised/exosuit_mod/durand - contains = list( - /obj/item/device/kit/paint/durand, - /obj/item/device/kit/paint/durand/seraph, - /obj/item/device/kit/paint/durand/phazon - ) - name = "Random Durand exosuit modkit" - -/datum/supply_packs/randomised/exosuit_mod/gygax - contains = list( - /obj/item/device/kit/paint/gygax, - /obj/item/device/kit/paint/gygax/darkgygax, - /obj/item/device/kit/paint/gygax/recitence - ) - name = "Random Gygax exosuit modkit" - -/datum/supply_packs/jukebox - name = "Jukebox" - contains = list(/obj/machinery/media/jukebox/) - cost = 200 - containertype = /obj/structure/largecrate - containername = "jukebox Crate" - group = "Hospitality" - -//voidsuit crates - -/datum/supply_packs/voidsuitcrate_eng - name = "Engineering Voidsuit Crate" - contains = list(/obj/item/clothing/head/helmet/space/void/engineering, - /obj/item/clothing/suit/space/void/engineering) - cost = 40 - containertype = /obj/structure/closet/crate/secure - containername = "engineer voidsuit kit" - access = access_engine_equip - group = "Engineering" - -/datum/supply_packs/voidsuitcrate_sec - name = "Security Voidsuit Crate" - contains = list(/obj/item/clothing/head/helmet/space/void/security, - /obj/item/clothing/suit/space/void/security) - cost = 40 - containertype = /obj/structure/closet/crate/secure - containername = "security voidsuit kit" - access = access_security - group = "Security" - -/datum/supply_packs/voidsuitcrate_med - name = "Medical Voidsuit Crate" - contains = list(/obj/item/clothing/head/helmet/space/void/medical, - /obj/item/clothing/suit/space/void/medical) - cost = 40 - containertype = /obj/structure/closet/crate/secure - containername = "medical voidsuit kit" - access = access_medical - group = "Medical" - -/datum/supply_packs/voidsuitcrate_atmos - name = "Atmospherics Voidsuit Crate" - contains = list(/obj/item/clothing/head/helmet/space/void/atmos, - /obj/item/clothing/suit/space/void/atmos) - cost = 40 - containertype = /obj/structure/closet/crate/secure - containername = "atmospherics voidsuit kit" - access = access_atmospherics - group = "Atmospherics" - -/datum/supply_packs/voidsuitcrate_minin - name = "Mining Voidsuit Crate" - contains = list(/obj/item/clothing/head/helmet/space/void/mining, - /obj/item/clothing/suit/space/void/mining) - cost = 40 - containertype = /obj/structure/closet/crate/secure - containername = "mining voidsuit kit" - access = access_mining - group = "Supply" - -//maglocks crates - -/datum/supply_packs/maglocks_engineering - name = "Engineering Magnetic Lock Crate" - contains = list(/obj/item/device/magnetic_lock/engineering, - /obj/item/device/magnetic_lock/engineering, - /obj/item/device/magnetic_lock/engineering) - cost = 30 - containertype = /obj/structure/closet/crate/secure - containername = "engineering magnetic locks" - access = access_engine_equip - group = "Engineering" - -/datum/supply_packs/maglocks_security - name = "Security Magnetic Lock Crate" - contains = list(/obj/item/device/magnetic_lock/security, - /obj/item/device/magnetic_lock/security, - /obj/item/device/magnetic_lock/security) - cost = 30 - containertype = /obj/structure/closet/crate/secure - containername = "security magnetic locks" - access = access_security - group = "Security" - -/datum/supply_packs/randomised/glowsticks - name = "Glowsticks crate" - num_contained = 4 - contains = list(/obj/item/device/flashlight/glowstick, - /obj/item/device/flashlight/glowstick/red, - /obj/item/device/flashlight/glowstick/blue, - /obj/item/device/flashlight/glowstick/orange, - /obj/item/device/flashlight/glowstick/yellow) - cost = 25 - containertype = /obj/structure/closet/crate - containername = "glowsticks crate" - group = "Operations" - -/datum/supply_packs/spessbike - name = "Space-bike Crate" - contains = list(/obj/vehicle/bike) - cost = 150 - containertype = /obj/structure/largecrate - containername = "Space-bike Crate" - contraband = 1 - group = "Operations" - -/datum/supply_packs/ipc_tag_pack - name = "IPC/Shell Tag Implanter Crate" - contains = list(/obj/item/implanter/ipc_tag, - /obj/item/implanter/ipc_tag, - /obj/item/implanter/ipc_tag - ) - cost = 40 - containertype = /obj/structure/closet/crate - containername = "IPC/Shell tag implanters" - group = "Security" - -/datum/supply_packs/ame_ctrl - name = "Antimatter Reactor Control Unit" - contains = list( - /obj/machinery/power/am_control_unit - ) - cost = 40 - containertype = /obj/structure/closet/crate/secure/large - containername = "antimatter reactor control unit kit" - group = "Engineering" - access = access_ce - -/datum/supply_packs/ame_section - name = "Antimatter Reactor Shielding Kit" - contains = list( - /obj/item/device/am_shielding_container, - /obj/item/device/am_shielding_container, - /obj/item/device/am_shielding_container, - /obj/item/device/am_shielding_container, - /obj/item/device/am_shielding_container, - /obj/item/device/am_shielding_container, - /obj/item/device/am_shielding_container, - /obj/item/device/am_shielding_container, - /obj/item/device/am_shielding_container - ) - cost = 20 - containertype = /obj/structure/closet/crate/secure/phoron - containername = "antimatter reactor shielding (9x) crate" - group = "Engineering" - access = access_ce - -/datum/supply_packs/ame_fuel - name = "Antimatter Reactor Fuel" - contains = list( - /obj/item/am_containment - ) - cost = 20 - containertype = /obj/structure/closet/crate/secure/bin - containername = "antimatter fuel container" - group = "Engineering" - access = access_engine - -/datum/supply_packs/hoist - name = "Hoist Crate" - contains = list( - /obj/item/hoist_kit, - /obj/item/hoist_kit, - /obj/item/hoist_kit - ) - cost = 50 - containertype = /obj/structure/closet/crate - containername = "hoist crate" - group = "Engineering" +//SUPPLY PACKS +//NOTE: only secure crate types use the access var (and are lockable) +//NOTE: hidden packs only show up when the computer has been hacked. +//ANOTER NOTE: Contraband is obtainable through modified supplycomp circuitboards. +//BIG NOTE: Don't add living things to crates, that's bad, it will break the shuttle. +//NEW NOTE: Do NOT set the price of any crates below 7 points. Doing so allows infinite points. + +var/list/all_supply_groups = list("Operations","Security","Hospitality","Engineering","Atmospherics","Medical","Reagents","Reagent Cartridges","Science","Hydroponics", "Supply", "Miscellaneous") + +/datum/supply_packs + var/name = null + var/list/contains = list() + var/manifest = "" + var/amount = null + var/cost = null + var/containertype = null + var/containername = null + var/access = null + var/hidden = 0 + var/contraband = 0 + var/group = "Operations" + +/datum/supply_packs/New() + manifest += "
      " + for(var/path in contains) + if(!path || !ispath(path, /atom)) + continue + var/atom/O = path + manifest += "
    • [initial(O.name)]
    • " + manifest += "
    " + +/datum/supply_packs/specialops + name = "Special Ops supplies" + contains = list(/obj/item/storage/box/emps, + /obj/item/grenade/smokebomb, + /obj/item/grenade/smokebomb, + /obj/item/grenade/smokebomb, + /obj/item/storage/box/syndie_kit/special_pens, + /obj/item/grenade/chem_grenade/incendiary) + + cost = 20 + containertype = /obj/structure/closet/crate + containername = "crate" + group = "Security" + hidden = 1 + +/datum/supply_packs/randomised/illegalguns + name = "Illegal weapons crate" + num_contained = 2 + contains = list(/obj/item/gun/projectile/automatic/mini_uzi, + /obj/item/gun/projectile/shotgun/pump/rifle, + /obj/item/gun/projectile/silenced, + /obj/item/gun/projectile/pirate, + /obj/item/gun/projectile/revolver/derringer, + /obj/item/gun/projectile/dragunov, + /obj/item/gun/energy/retro) + cost = 120 + containertype = /obj/structure/closet/crate + containername = "crate" + contraband = 1 + group = "Security" + +/datum/supply_packs/forensics + name = "Auxiliary forensic tools" + contains = list(/obj/item/forensics/sample_kit, + /obj/item/forensics/sample_kit/powder, + /obj/item/storage/box/swabs, + /obj/item/storage/box/swabs, + /obj/item/storage/box/swabs, + /obj/item/storage/box/slides, + /obj/item/reagent_containers/spray/luminol, + /obj/item/device/uv_light) + cost = 30 + containertype = /obj/structure/closet/crate + containername = "Auxiliary forensic tools" + group = "Security" + +/datum/supply_packs/food + name = "Kitchen supply crate" + contains = list(/obj/item/reagent_containers/food/condiment/flour, + /obj/item/reagent_containers/food/condiment/flour, + /obj/item/reagent_containers/food/condiment/flour, + /obj/item/reagent_containers/food/condiment/flour, + /obj/item/reagent_containers/food/condiment/spacespice, + /obj/item/reagent_containers/food/condiment/peppermill, + /obj/item/reagent_containers/food/condiment/saltshaker, + /obj/item/reagent_containers/food/condiment/enzyme, + /obj/item/reagent_containers/food/drinks/milk, + /obj/item/reagent_containers/food/drinks/milk, + /obj/item/reagent_containers/food/drinks/soymilk, + /obj/item/reagent_containers/food/drinks/soymilk, + /obj/item/storage/fancy/egg_box, + /obj/item/reagent_containers/food/snacks/meat, + /obj/item/reagent_containers/food/snacks/meat) + cost = 10 + containertype = /obj/structure/closet/crate/freezer + containername = "Food crate" + group = "Supply" + +/datum/supply_packs/monkey + name = "Monkey crate" + contains = list (/obj/item/storage/box/monkeycubes) + cost = 20 + containertype = /obj/structure/closet/crate/freezer + containername = "Monkey crate" + group = "Hydroponics" + +/datum/supply_packs/farwa + name = "Farwa crate" + contains = list (/obj/item/storage/box/monkeycubes/farwacubes) + cost = 30 + containertype = /obj/structure/closet/crate/freezer + containername = "Farwa crate" + group = "Hydroponics" + +/datum/supply_packs/skrell + name = "Neaera crate" + contains = list (/obj/item/storage/box/monkeycubes/neaeracubes) + cost = 30 + containertype = /obj/structure/closet/crate/freezer + containername = "Neaera crate" + group = "Hydroponics" + +/datum/supply_packs/stok + name = "Stok crate" + contains = list (/obj/item/storage/box/monkeycubes/stokcubes) + cost = 30 + containertype = /obj/structure/closet/crate/freezer + containername = "Stok crate" + group = "Hydroponics" + +/datum/supply_packs/beanbagammo + name = "Beanbag shells" + contains = list(/obj/item/ammo_magazine/shotgun/beanbag, + /obj/item/ammo_magazine/shotgun/beanbag, + /obj/item/ammo_magazine/shotgun/beanbag) + cost = 30 + containertype = /obj/structure/closet/crate + containername = "Beanbag shells" + group = "Security" + +/datum/supply_packs/toner + name = "Toner cartridges" + contains = list(/obj/item/device/toner, + /obj/item/device/toner, + /obj/item/device/toner, + /obj/item/device/toner, + /obj/item/device/toner, + /obj/item/device/toner) + cost = 10 + containertype = /obj/structure/closet/crate + containername = "Toner cartridges" + group = "Supply" + +/datum/supply_packs/party + name = "Party equipment" + contains = list(/obj/item/storage/box/drinkingglasses, + /obj/item/reagent_containers/food/drinks/shaker, + /obj/item/reagent_containers/food/drinks/flask/barflask, + /obj/item/reagent_containers/food/drinks/bottle/patron, + /obj/item/reagent_containers/food/drinks/bottle/goldschlager, + /obj/item/reagent_containers/food/drinks/bottle/sarezhiwine, + /obj/item/storage/fancy/cigarettes/dromedaryco, + /obj/item/lipstick/random, + /obj/item/reagent_containers/food/drinks/bottle/small/ale, + /obj/item/reagent_containers/food/drinks/bottle/small/ale, + /obj/item/reagent_containers/food/drinks/bottle/small/beer, + /obj/item/reagent_containers/food/drinks/bottle/small/beer, + /obj/item/reagent_containers/food/drinks/bottle/small/beer, + /obj/item/reagent_containers/food/drinks/bottle/small/beer) + cost = 20 + containertype = /obj/structure/closet/crate + containername = "Party equipment" + group = "Hospitality" + +/datum/supply_packs/lasertag + name = "Lasertag equipment" + contains = list(/obj/item/gun/energy/lasertag/red, + /obj/item/clothing/suit/redtag, + /obj/item/gun/energy/lasertag/blue, + /obj/item/clothing/suit/bluetag) + containertype = /obj/structure/closet + containername = "Lasertag Closet" + group = "Hospitality" + cost = 20 + +/datum/supply_packs/internals + name = "Internals crate" + contains = list(/obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/tank/air, + /obj/item/tank/air, + /obj/item/tank/air) + cost = 10 + containertype = /obj/structure/closet/crate/internals + containername = "Internals crate" + group = "Atmospherics" + +/datum/supply_packs/evacuation + name = "Emergency equipment" + contains = list(/obj/item/storage/toolbox/emergency, + /obj/item/storage/toolbox/emergency, + /obj/item/clothing/suit/storage/hazardvest, + /obj/item/clothing/suit/storage/hazardvest, + /obj/item/clothing/suit/armor/vest, + /obj/item/clothing/suit/armor/vest, + /obj/item/tank/emergency_oxygen/engi, + /obj/item/tank/emergency_oxygen/engi, + /obj/item/tank/emergency_oxygen/engi, + /obj/item/tank/emergency_oxygen/engi, + /obj/item/clothing/suit/space/emergency, + /obj/item/clothing/suit/space/emergency, + /obj/item/clothing/suit/space/emergency, + /obj/item/clothing/suit/space/emergency, + /obj/item/clothing/head/helmet/space/emergency, + /obj/item/clothing/head/helmet/space/emergency, + /obj/item/clothing/head/helmet/space/emergency, + /obj/item/clothing/head/helmet/space/emergency, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas) + cost = 45 + containertype = /obj/structure/closet/crate/internals + containername = "Emergency crate" + group = "Atmospherics" + + + + +/datum/supply_packs/inflatable + name = "Inflatable barriers" + contains = list(/obj/item/storage/briefcase/inflatable, + /obj/item/storage/briefcase/inflatable, + /obj/item/storage/briefcase/inflatable) + cost = 20 + containertype = /obj/structure/closet/crate + containername = "Inflatable Barrier Crate" + group = "Atmospherics" + +/datum/supply_packs/janitor + name = "Janitorial supplies" + contains = list(/obj/item/reagent_containers/glass/bucket, + /obj/item/mop, + /obj/item/caution, + /obj/item/caution, + /obj/item/caution, + /obj/item/caution, + /obj/item/storage/bag/trash, + /obj/item/device/lightreplacer, + /obj/item/reagent_containers/spray/cleaner, + /obj/item/reagent_containers/glass/rag, + /obj/item/grenade/chem_grenade/cleaner, + /obj/item/grenade/chem_grenade/cleaner, + /obj/item/grenade/chem_grenade/cleaner, + /obj/structure/mopbucket) + cost = 10 + containertype = /obj/structure/closet/crate + containername = "Janitorial supplies" + group = "Supply" + +/datum/supply_packs/lightbulbs + name = "Replacement lights" + contains = list(/obj/item/storage/box/lights/mixed, + /obj/item/storage/box/lights/mixed, + /obj/item/storage/box/lights/mixed) + cost = 10 + containertype = /obj/structure/closet/crate + containername = "Replacement lights" + group = "Engineering" + +/datum/supply_packs/wizard + name = "Wizard costume" + contains = list(/obj/item/staff, + /obj/item/clothing/suit/wizrobe/fake, + /obj/item/clothing/shoes/sandal, + /obj/item/clothing/head/wizard/fake) + cost = 20 + containertype = /obj/structure/closet/crate + containername = "Wizard costume crate" + group = "Miscellaneous" + +/datum/supply_packs/mule + name = "MULEbot Crate" + contains = list(/obj/machinery/bot/mulebot) + cost = 20 + containertype = /obj/structure/largecrate/mule + containername = "MULEbot Crate" + group = "Operations" + +/datum/supply_packs/cargotrain + name = "Cargo Train Tug" + contains = list(/obj/vehicle/train/cargo/engine) + cost = 45 + containertype = /obj/structure/largecrate + containername = "Cargo Train Tug Crate" + group = "Operations" + +/datum/supply_packs/cargotrailer + name = "Cargo Train Trolley" + contains = list(/obj/vehicle/train/cargo/trolley) + cost = 15 + containertype = /obj/structure/largecrate + containername = "Cargo Train Trolley Crate" + group = "Operations" + +/datum/supply_packs/lisa + name = "Corgi Crate" + contains = list() + cost = 50 + containertype = /obj/structure/largecrate/animal/corgi + containername = "Corgi Crate" + group = "Hydroponics" + +/datum/supply_packs/hydroponics // -- Skie + name = "Hydroponics Supply Crate" + contains = list(/obj/item/reagent_containers/spray/plantbgone, + /obj/item/reagent_containers/spray/plantbgone, + /obj/item/reagent_containers/spray/plantbgone, + /obj/item/reagent_containers/spray/plantbgone, + /obj/item/reagent_containers/glass/bottle/ammonia, + /obj/item/reagent_containers/glass/bottle/ammonia, + /obj/item/material/hatchet, + /obj/item/material/minihoe, + /obj/item/device/analyzer/plant_analyzer, + /obj/item/clothing/gloves/botanic_leather, + /obj/item/clothing/suit/apron, + /obj/item/material/minihoe, + /obj/item/storage/box/botanydisk + ) // Updated with new things + cost = 15 + containertype = /obj/structure/closet/crate/hydroponics + containername = "Hydroponics crate" + access = access_hydroponics + group = "Hydroponics" + +//farm animals - useless and annoying, but potentially a good source of food +/datum/supply_packs/cow + name = "Cow crate" + cost = 30 + containertype = /obj/structure/largecrate/animal/cow + containername = "Cow crate" + access = access_hydroponics + group = "Hydroponics" + +/datum/supply_packs/goat + name = "Goat crate" + cost = 25 + containertype = /obj/structure/largecrate/animal/goat + containername = "Goat crate" + access = access_hydroponics + group = "Hydroponics" + +/datum/supply_packs/chicken + name = "Chicken crate" + cost = 20 + containertype = /obj/structure/largecrate/animal/chick + containername = "Chicken crate" + access = access_hydroponics + group = "Hydroponics" + +/datum/supply_packs/seeds + name = "Seeds crate" + contains = list(/obj/item/seeds/chiliseed, + /obj/item/seeds/berryseed, + /obj/item/seeds/cornseed, + /obj/item/seeds/eggplantseed, + /obj/item/seeds/tomatoseed, + /obj/item/seeds/appleseed, + /obj/item/seeds/soyaseed, + /obj/item/seeds/wheatseed, + /obj/item/seeds/carrotseed, + /obj/item/seeds/harebell, + /obj/item/seeds/lemonseed, + /obj/item/seeds/orangeseed, + /obj/item/seeds/grassseed, + /obj/item/seeds/sunflowerseed, + /obj/item/seeds/chantermycelium, + /obj/item/seeds/potatoseed, + /obj/item/seeds/sugarcaneseed) + cost = 10 + containertype = /obj/structure/closet/crate/hydroponics + containername = "Seeds crate" + access = access_hydroponics + group = "Hydroponics" + +/datum/supply_packs/weedcontrol + name = "Weed control crate" + contains = list(/obj/item/material/hatchet, + /obj/item/material/hatchet, + /obj/item/reagent_containers/spray/plantbgone, + /obj/item/reagent_containers/spray/plantbgone, + /obj/item/reagent_containers/spray/plantbgone, + /obj/item/reagent_containers/spray/plantbgone, + /obj/item/clothing/mask/gas, + /obj/item/clothing/mask/gas, + /obj/item/grenade/chem_grenade/antiweed, + /obj/item/grenade/chem_grenade/antiweed) + cost = 25 + containertype = /obj/structure/closet/crate/hydroponics + containername = "Weed control crate" + access = access_hydroponics + group = "Hydroponics" + +/datum/supply_packs/exoticseeds + name = "Exotic seeds crate" + contains = list(/obj/item/seeds/replicapod, + /obj/item/seeds/replicapod, + /obj/item/seeds/libertymycelium, + /obj/item/seeds/reishimycelium, + /obj/item/seeds/random, + /obj/item/seeds/random, + /obj/item/seeds/random, + /obj/item/seeds/random, + /obj/item/seeds/random, + /obj/item/seeds/random, + /obj/item/seeds/kudzuseed) + cost = 15 + containertype = /obj/structure/closet/crate/hydroponics + containername = "Exotic Seeds crate" + access = access_hydroponics + group = "Hydroponics" + +/datum/supply_packs/medical + name = "Medical crate" + contains = list(/obj/item/storage/firstaid/regular, + /obj/item/storage/firstaid/fire, + /obj/item/storage/firstaid/toxin, + /obj/item/storage/firstaid/o2, + /obj/item/storage/firstaid/adv, + /obj/item/reagent_containers/glass/bottle/antitoxin, + /obj/item/reagent_containers/glass/bottle/norepinephrine, + /obj/item/reagent_containers/glass/bottle/stoxin, + /obj/item/storage/box/syringes, + /obj/item/storage/box/autoinjectors) + cost = 10 + containertype = /obj/structure/closet/crate/medical + containername = "Medical crate" + group = "Medical" + +/datum/supply_packs/bloodpack + name = "BloodPack crate" + contains = list(/obj/item/storage/box/bloodpacks, + /obj/item/storage/box/bloodpacks, + /obj/item/storage/box/bloodpacks) + cost = 10 + containertype = /obj/structure/closet/crate/medical + containername = "BloodPack crate" + group = "Medical" + +/datum/supply_packs/bodybag + name = "Body bag crate" + contains = list(/obj/item/storage/box/bodybags, + /obj/item/storage/box/bodybags, + /obj/item/storage/box/bodybags) + cost = 10 + containertype = /obj/structure/closet/crate/medical + containername = "Body bag crate" + group = "Medical" + +/datum/supply_packs/cryobag + name = "Statis bag crate" + contains = list(/obj/item/bodybag/cryobag, + /obj/item/bodybag/cryobag, + /obj/item/bodybag/cryobag) + cost = 50 + containertype = /obj/structure/closet/crate/medical + containername = "Stasis bag crate" + group = "Medical" + +/datum/supply_packs/virus + name = "Virus sample crate" +/* contains = list(/obj/item/reagent_containers/glass/bottle/flu_virion, + /obj/item/reagent_containers/glass/bottle/cold, + /obj/item/reagent_containers/glass/bottle/epiglottis_virion, + /obj/item/reagent_containers/glass/bottle/liver_enhance_virion, + /obj/item/reagent_containers/glass/bottle/fake_gbs, + /obj/item/reagent_containers/glass/bottle/magnitis, + /obj/item/reagent_containers/glass/bottle/pierrot_throat, + /obj/item/reagent_containers/glass/bottle/brainrot, + /obj/item/reagent_containers/glass/bottle/hullucigen_virion, + /obj/item/storage/box/syringes, + /obj/item/storage/box/beakers, + /obj/item/reagent_containers/glass/bottle/mutagen)*/ + contains = list(/obj/item/virusdish/random, + /obj/item/virusdish/random, + /obj/item/virusdish/random, + /obj/item/virusdish/random) + cost = 25 + containertype = "/obj/structure/closet/crate/secure" + containername = "Virus sample crate" + access = access_cmo + group = "Science" + +/datum/supply_packs/metal50 + name = "50 metal sheets" + contains = list(/obj/item/stack/material/steel) + amount = 50 + cost = 10 + containertype = /obj/structure/closet/crate + containername = "Metal sheets crate" + group = "Engineering" + +/datum/supply_packs/metal200 + name = "Bulk metal crate" + contains = list(/obj/item/stack/material/steel, /obj/item/stack/material/steel, /obj/item/stack/material/steel,/obj/item/stack/material/steel) + amount = 50 + cost = 20 + containertype = /obj/structure/largecrate + containername = "Bulk metal crate" + group = "Engineering" + +/datum/supply_packs/glass50 + name = "50 glass sheets" + contains = list(/obj/item/stack/material/glass) + amount = 50 + cost = 10 + containertype = /obj/structure/closet/crate + containername = "Glass sheets crate" + group = "Engineering" + +/datum/supply_packs/wood50 + name = "50 wooden planks" + contains = list(/obj/item/stack/material/wood) + amount = 50 + cost = 10 + containertype = /obj/structure/closet/crate + containername = "Wooden planks crate" + group = "Engineering" + +/datum/supply_packs/plastic50 + name = "50 plastic sheets" + contains = list(/obj/item/stack/material/plastic) + amount = 50 + cost = 10 + containertype = /obj/structure/closet/crate + containername = "Plastic sheets crate" + group = "Engineering" + +/datum/supply_packs/smescoil + name = "Superconducting Magnetic Coil" + contains = list(/obj/item/smes_coil) + cost = 75 + containertype = /obj/structure/closet/crate + containername = "Superconducting Magnetic Coil crate" + group = "Engineering" + +/datum/supply_packs/electrical + name = "Electrical maintenance crate" + contains = list(/obj/item/storage/toolbox/electrical, + /obj/item/storage/toolbox/electrical, + /obj/item/clothing/gloves/yellow, + /obj/item/clothing/gloves/yellow, + /obj/item/cell, + /obj/item/cell, + /obj/item/cell/high, + /obj/item/cell/high) + cost = 15 + containertype = /obj/structure/closet/crate + containername = "Electrical maintenance crate" + group = "Engineering" + +/datum/supply_packs/mechanical + name = "Mechanical maintenance crate" + contains = list(/obj/item/storage/belt/utility/full, + /obj/item/storage/belt/utility/full, + /obj/item/storage/belt/utility/full, + /obj/item/clothing/suit/storage/hazardvest, + /obj/item/clothing/suit/storage/hazardvest, + /obj/item/clothing/suit/storage/hazardvest, + /obj/item/clothing/head/welding, + /obj/item/clothing/head/welding, + /obj/item/clothing/head/hardhat) + cost = 10 + containertype = /obj/structure/closet/crate + containername = "Mechanical maintenance crate" + group = "Engineering" + +/datum/supply_packs/watertank + name = "Water tank crate" + contains = list(/obj/structure/reagent_dispensers/watertank) + cost = 8 + containertype = /obj/structure/largecrate + containername = "water tank crate" + group = "Hydroponics" + +/datum/supply_packs/fueltank + name = "Fuel tank crate" + contains = list(/obj/structure/reagent_dispensers/fueltank) + cost = 8 + containertype = /obj/structure/largecrate + containername = "fuel tank crate" + group = "Engineering" + +/datum/supply_packs/coolanttank + name = "Coolant tank crate" + contains = list(/obj/structure/reagent_dispensers/coolanttank) + cost = 16 + containertype = /obj/structure/largecrate + containername = "coolant tank crate" + group = "Science" + +/datum/supply_packs/solar + name = "Solar Pack crate" + contains = list(/obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, + /obj/item/solar_assembly, // 21 Solar Assemblies. 1 Extra for the controller, + /obj/item/circuitboard/solar_control, + /obj/item/tracker_electronics, + /obj/item/paper/solar) + cost = 20 + containertype = /obj/structure/closet/crate + containername = "Solar pack crate" + group = "Engineering" + +/datum/supply_packs/engine + name = "Emitter crate" + contains = list(/obj/machinery/power/emitter, + /obj/machinery/power/emitter) + cost = 10 + containertype = /obj/structure/closet/crate/secure + containername = "Emitter crate" + access = access_ce + group = "Engineering" + +/datum/supply_packs/engine/field_gen + name = "Field Generator crate" + contains = list(/obj/machinery/field_generator, + /obj/machinery/field_generator) + containertype = /obj/structure/closet/crate/secure + containername = "Field Generator crate" + access = access_ce + group = "Engineering" + +/datum/supply_packs/engine/sing_gen + name = "Singularity Generator crate" + contains = list(/obj/machinery/the_singularitygen) + containertype = /obj/structure/closet/crate/secure + containername = "Singularity Generator crate" + access = access_ce + group = "Engineering" + +/datum/supply_packs/engine/collector + name = "Collector crate" + contains = list(/obj/machinery/power/rad_collector, + /obj/machinery/power/rad_collector, + /obj/machinery/power/rad_collector) + containername = "Collector crate" + group = "Engineering" + +/datum/supply_packs/engine/PA + name = "Particle Accelerator crate" + cost = 40 + contains = list(/obj/structure/particle_accelerator/fuel_chamber, + /obj/machinery/particle_accelerator/control_box, + /obj/structure/particle_accelerator/particle_emitter/center, + /obj/structure/particle_accelerator/particle_emitter/left, + /obj/structure/particle_accelerator/particle_emitter/right, + /obj/structure/particle_accelerator/power_box, + /obj/structure/particle_accelerator/end_cap) + containertype = /obj/structure/closet/crate/secure + containername = "Particle Accelerator crate" + access = access_ce + group = "Engineering" + +/datum/supply_packs/mecha_ripley + name = "Circuit Crate (\"Ripley\" APLU)" + contains = list(/obj/item/book/manual/ripley_build_and_repair, + /obj/item/circuitboard/mecha/ripley/main, //TEMPORARY due to lack of circuitboard printer, + /obj/item/circuitboard/mecha/ripley/peripherals) //TEMPORARY due to lack of circuitboard printer + cost = 30 + containertype = /obj/structure/closet/crate/secure + containername = "APLU \"Ripley\" Circuit Crate" + access = access_robotics + group = "Science" + +/datum/supply_packs/mecha_odysseus + name = "Circuit Crate (\"Odysseus\")" + contains = list(/obj/item/circuitboard/mecha/odysseus/peripherals, //TEMPORARY due to lack of circuitboard printer, + /obj/item/circuitboard/mecha/odysseus/main) //TEMPORARY due to lack of circuitboard printer + cost = 25 + containertype = /obj/structure/closet/crate/secure + containername = "\"Odysseus\" Circuit Crate" + access = access_robotics + group = "Science" + +/datum/supply_packs/hoverpod + name = "Hoverpod Shipment" + contains = list() + cost = 80 + containertype = /obj/structure/largecrate/hoverpod + containername = "Hoverpod Crate" + group = "Operations" + +/datum/supply_packs/robotics + name = "Robotics assembly crate" + contains = list(/obj/item/device/assembly/prox_sensor, + /obj/item/device/assembly/prox_sensor, + /obj/item/device/assembly/prox_sensor, + /obj/item/storage/toolbox/electrical, + /obj/item/device/flash, + /obj/item/device/flash, + /obj/item/device/flash, + /obj/item/device/flash, + /obj/item/cell/high, + /obj/item/cell/high) + cost = 10 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Robotics assembly" + access = access_robotics + group = "Engineering" + +/datum/supply_packs/phoron + name = "Phoron assembly crate" + contains = list(/obj/item/tank/phoron, + /obj/item/tank/phoron, + /obj/item/tank/phoron, + /obj/item/device/assembly/igniter, + /obj/item/device/assembly/igniter, + /obj/item/device/assembly/igniter, + /obj/item/device/assembly/prox_sensor, + /obj/item/device/assembly/prox_sensor, + /obj/item/device/assembly/prox_sensor, + /obj/item/device/assembly/timer, + /obj/item/device/assembly/timer, + /obj/item/device/assembly/timer) + cost = 10 + containertype = /obj/structure/closet/crate/secure/phoron + containername = "Phoron assembly crate" + access = access_tox_storage + group = "Science" + +/datum/supply_packs/weapons + name = "Weapons crate" + contains = list(/obj/item/gun/energy/rifle/laser, + /obj/item/gun/energy/rifle/laser, + /obj/item/gun/projectile/sec, + /obj/item/gun/projectile/sec, + /obj/item/ammo_magazine/c45m, + /obj/item/ammo_magazine/c45m) + cost = 80 + containertype = /obj/structure/closet/crate/secure/weapon + containername = "Weapons crate" + access = access_security + group = "Security" + +/datum/supply_packs/nonlethals + name = "Nonlethal Weapons crate" + contains = list(/obj/item/melee/baton, + /obj/item/melee/baton, + /obj/item/gun/energy/taser, + /obj/item/gun/energy/taser, + /obj/item/storage/box/flashbangs, + /obj/item/storage/box/flashbangs, + /obj/item/ammo_magazine/tranq, + /obj/item/storage/box/zipties) + cost = 60 + containertype = /obj/structure/closet/crate/secure/weapon + containername = "Weapons crate" + access = access_security + group = "Security" + +/datum/supply_packs/flareguns + name = "Flare guns crate" + contains = list(/obj/item/gun/projectile/sec/flash, + /obj/item/ammo_magazine/c45m/flash, + /obj/item/gun/projectile/shotgun/doublebarrel/flare, + /obj/item/storage/box/flashshells) + cost = 25 + containertype = /obj/structure/closet/crate/secure/weapon + containername = "Flare gun crate" + access = access_security + group = "Security" + +/datum/supply_packs/randomised/armor + num_contained = 5 + contains = list(/obj/item/clothing/suit/armor/vest, + /obj/item/clothing/suit/armor/vest/security, + /obj/item/clothing/suit/storage/vest/detective, + /obj/item/clothing/suit/storage/vest/hos, + /obj/item/clothing/suit/storage/vest/pcrc, + /obj/item/clothing/suit/storage/vest/warden, + /obj/item/clothing/suit/storage/vest/officer, + /obj/item/clothing/suit/storage/vest) + + name = "Armor crate" + cost = 40 + containertype = /obj/structure/closet/crate/secure + containername = "Armor crate" + access = access_security + group = "Security" + + +/datum/supply_packs/riot + name = "Riot gear crate" + contains = list(/obj/item/melee/baton, + /obj/item/melee/baton, + /obj/item/melee/baton, + /obj/item/shield/riot, + /obj/item/shield/riot, + /obj/item/shield/riot, + /obj/item/handcuffs, + /obj/item/handcuffs, + /obj/item/handcuffs, + /obj/item/clothing/head/helmet/riot, + /obj/item/clothing/suit/armor/riot, + /obj/item/clothing/head/helmet/riot, + /obj/item/clothing/suit/armor/riot, + /obj/item/clothing/head/helmet/riot, + /obj/item/clothing/suit/armor/riot, + /obj/item/storage/box/flashbangs, + /obj/item/storage/box/teargas, + /obj/item/storage/box/beanbags, + /obj/item/storage/box/handcuffs) + cost = 60 + containertype = /obj/structure/closet/crate/secure + containername = "Riot gear crate" + access = access_armory + group = "Security" + +/datum/supply_packs/energyweapons + name = "Energy weapons crate" + contains = list(/obj/item/gun/energy/rifle/laser, + /obj/item/gun/energy/rifle/laser, + /obj/item/gun/energy/rifle/laser) + cost = 50 + containertype = /obj/structure/closet/crate/secure + containername = "energy weapons crate" + access = access_armory + group = "Security" + +/datum/supply_packs/shotgun + name = "Shotgun crate" + contains = list(/obj/item/clothing/suit/armor/bulletproof, + /obj/item/clothing/head/helmet/ballistic, + /obj/item/ammo_magazine/shotgun, + /obj/item/ammo_magazine/shotgun/shell, + /obj/item/gun/projectile/shotgun/pump/combat, + /obj/item/gun/projectile/shotgun/pump/combat) + cost = 65 + containertype = /obj/structure/closet/crate/secure + containername = "Shotgun crate" + access = access_armory + group = "Security" + +/datum/supply_packs/erifle + name = "Energy marksman crate" + contains = list(/obj/item/clothing/suit/armor/laserproof, + /obj/item/clothing/head/helmet/ablative, + /obj/item/gun/energy/sniperrifle, + /obj/item/gun/energy/sniperrifle) + cost = 90 + containertype = /obj/structure/closet/crate/secure + containername = "Energy marksman crate" + access = access_armory + group = "Security" + +/datum/supply_packs/shotgunammo + name = "Ballistic ammunition crate" + contains = list(/obj/item/ammo_magazine/shotgun, + /obj/item/ammo_magazine/shotgun, + /obj/item/ammo_magazine/shotgun/shell, + /obj/item/ammo_magazine/shotgun/shell, + /obj/item/ammo_magazine/shotgun/incendiary) + cost = 60 + containertype = /obj/structure/closet/crate/secure + containername = "ballistic ammunition crate" + access = access_armory + group = "Security" + +/datum/supply_packs/ionweapons + name = "Electromagnetic weapons crate" + contains = list(/obj/item/gun/energy/ionrifle, + /obj/item/gun/energy/ionrifle, + /obj/item/ammo_magazine/shotgun/emp, + /obj/item/storage/box/emps) + cost = 50 + containertype = /obj/structure/closet/crate/secure + containername = "electromagnetic weapons crate" + access = access_armory + group = "Security" + +/datum/supply_packs/randomised/automatic + name = "Automatic weapon crate" + num_contained = 2 + contains = list(/obj/item/gun/projectile/automatic/wt550, + /obj/item/gun/projectile/automatic/rifle/z8) + cost = 90 + containertype = /obj/structure/closet/crate/secure + containername = "Automatic weapon crate" + access = access_armory + group = "Security" + +/datum/supply_packs/randomised/autoammo + name = "Automatic weapon ammunition crate" + num_contained = 6 + contains = list(/obj/item/ammo_magazine/mc9mmt, + /obj/item/ammo_magazine/mc9mmt/rubber, + /obj/item/ammo_magazine/a556) + cost = 20 + containertype = /obj/structure/closet/crate/secure + containername = "Automatic weapon ammunition crate" + access = access_armory + group = "Security" + +/* +/datum/supply_packs/loyalty + name = "Loyalty implant crate" + contains = list (/obj/item/storage/lockbox/loyalty) + cost = 60 + containertype = /obj/structure/closet/crate/secure + containername = "Loyalty implant crate" + access = access_armory + group = "Security" +*/ + +/datum/supply_packs/expenergy + name = "Experimental energy gear crate" + contains = list(/obj/item/clothing/suit/armor/laserproof, + /obj/item/clothing/head/helmet/ablative, + /obj/item/gun/energy/gun, + /obj/item/gun/energy/gun) + cost = 50 + containertype = /obj/structure/closet/crate/secure + containername = "Experimental energy gear crate" + access = access_armory + group = "Security" + +/datum/supply_packs/exparmor + name = "Experimental armor crate" + contains = list(/obj/item/clothing/suit/armor/laserproof,, + /obj/item/clothing/head/helmet/ablative, + /obj/item/clothing/suit/armor/bulletproof, + /obj/item/clothing/head/helmet/ballistic, + /obj/item/clothing/head/helmet/riot, + /obj/item/clothing/suit/armor/riot) + cost = 35 + containertype = /obj/structure/closet/crate/secure + containername = "Experimental armor crate" + access = access_armory + group = "Security" + +/datum/supply_packs/securitybarriers + name = "Security barrier crate" + contains = list(/obj/machinery/deployable/barrier, + /obj/machinery/deployable/barrier, + /obj/machinery/deployable/barrier, + /obj/machinery/deployable/barrier) + cost = 20 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Security barrier crate" + group = "Security" + +/datum/supply_packs/securitybarriers + name = "Wall shield Generators" + contains = list(/obj/machinery/shieldwallgen, + /obj/machinery/shieldwallgen, + /obj/machinery/shieldwallgen, + /obj/machinery/shieldwallgen) + cost = 20 + containertype = /obj/structure/closet/crate/secure + containername = "wall shield generators crate" + access = access_teleporter + group = "Security" + +/datum/supply_packs/randomised + var/num_contained = 4 //number of items picked to be contained in a randomised crate + contains = list(/obj/item/clothing/head/collectable/chef, + /obj/item/clothing/head/collectable/paper, + /obj/item/clothing/head/collectable/tophat, + /obj/item/clothing/head/collectable/captain, + /obj/item/clothing/head/collectable/beret, + /obj/item/clothing/head/collectable/welding, + /obj/item/clothing/head/collectable/flatcap, + /obj/item/clothing/head/collectable/pirate, + /obj/item/clothing/head/collectable/kitty, + /obj/item/clothing/head/collectable/rabbitears, + /obj/item/clothing/head/collectable/wizard, + /obj/item/clothing/head/collectable/hardhat, + /obj/item/clothing/head/collectable/HoS, + /obj/item/clothing/head/collectable/thunderdome, + /obj/item/clothing/head/collectable/swat, + /obj/item/clothing/head/collectable/slime, + /obj/item/clothing/head/collectable/police, + /obj/item/clothing/head/collectable/slime, + /obj/item/clothing/head/collectable/xenom, + /obj/item/clothing/head/collectable/petehat) + name = "Collectable hat crate!" + cost = 200 + containertype = /obj/structure/closet/crate + containername = "Collectable hats crate! Brought to you by Bass.inc!" + group = "Miscellaneous" + +/datum/supply_packs/randomised/New() + manifest += "Contains any [num_contained] of:" + ..() + +/datum/supply_packs/artscrafts + name = "Arts and Crafts supplies" + contains = list(/obj/item/storage/fancy/crayons, + /obj/item/device/camera, + /obj/item/device/camera_film, + /obj/item/device/camera_film, + /obj/item/storage/photo_album, + /obj/item/packageWrap, + /obj/item/reagent_containers/glass/paint/red, + /obj/item/reagent_containers/glass/paint/green, + /obj/item/reagent_containers/glass/paint/blue, + /obj/item/reagent_containers/glass/paint/yellow, + /obj/item/reagent_containers/glass/paint/purple, + /obj/item/reagent_containers/glass/paint/black, + /obj/item/reagent_containers/glass/paint/white, + /obj/item/contraband/poster, + /obj/item/wrapping_paper, + /obj/item/wrapping_paper, + /obj/item/wrapping_paper) + cost = 10 + containertype = "/obj/structure/closet/crate" + containername = "Arts and Crafts crate" + group = "Operations" + + +/datum/supply_packs/randomised/contraband + num_contained = 5 + contains = list(/obj/item/seeds/bloodtomatoseed, + /obj/item/storage/pill_bottle/zoom, + /obj/item/storage/pill_bottle/happy, + /obj/item/reagent_containers/food/drinks/bottle/pwine) + + name = "Contraband crate" + cost = 30 + containertype = /obj/structure/closet/crate + containername = "Unlabeled crate" + contraband = 1 + group = "Operations" + +/datum/supply_packs/boxes + name = "Empty boxes" + contains = list(/obj/item/storage/box, + /obj/item/storage/box, + /obj/item/storage/box, + /obj/item/storage/box, + /obj/item/storage/box, + /obj/item/storage/box, + /obj/item/storage/box, + /obj/item/storage/box, + /obj/item/storage/box, + /obj/item/storage/box) + cost = 10 + containertype = "/obj/structure/closet/crate" + containername = "Empty box crate" + group = "Supply" + +/datum/supply_packs/surgery + name = "Surgery crate" + contains = list(/obj/item/cautery, + /obj/item/surgicaldrill, + /obj/item/clothing/mask/breath/medical, + /obj/item/tank/anesthetic, + /obj/item/FixOVein, + /obj/item/hemostat, + /obj/item/scalpel, + /obj/item/bonegel, + /obj/item/retractor, + /obj/item/bonesetter, + /obj/item/circular_saw) + cost = 25 + containertype = "/obj/structure/closet/crate/secure" + containername = "Surgery crate" + access = access_medical + group = "Medical" + +/datum/supply_packs/sterile + name = "Sterile equipment crate" + contains = list(/obj/item/clothing/under/rank/medical/green, + /obj/item/clothing/under/rank/medical/green, + /obj/item/clothing/head/surgery/green, + /obj/item/clothing/head/surgery/green, + /obj/item/storage/box/masks, + /obj/item/storage/box/gloves, + /obj/item/storage/belt/medical, + /obj/item/storage/belt/medical, + /obj/item/storage/belt/medical) + cost = 15 + containertype = "/obj/structure/closet/crate" + containername = "Sterile equipment crate" + group = "Medical" + +/datum/supply_packs/beer + contains = list(/obj/structure/reagent_dispensers/beerkeg) + name = "Beer Keg" + cost = 10 + containertype = /obj/structure/largecrate + containername = "Beer Crate" + group = "Hospitality" + +/datum/supply_packs/xuizi + contains = list(/obj/structure/reagent_dispensers/xuizikeg) + name = "Xuizi Juice Keg" + cost = 20 + containertype = /obj/structure/largecrate + containername = "Xuizi Juice Crate" + group = "Hospitality" + +/datum/supply_packs/randomised/pizza + num_contained = 5 + contains = list(/obj/item/pizzabox/margherita, + /obj/item/pizzabox/mushroom, + /obj/item/pizzabox/meat, + /obj/item/pizzabox/vegetable) + name = "Surprise pack of five pizzas" + cost = 15 + containertype = /obj/structure/closet/crate/freezer + containername = "Pizza crate" + group = "Hospitality" + +/datum/supply_packs/randomised/costume + num_contained = 2 + contains = list(/obj/item/clothing/suit/pirate, + /obj/item/clothing/suit/judgerobe, + /obj/item/clothing/accessory/wcoat, + /obj/item/clothing/suit/hastur, + /obj/item/clothing/suit/holidaypriest, + /obj/item/clothing/suit/nun, + /obj/item/clothing/suit/imperium_monk, + /obj/item/clothing/suit/ianshirt, + /obj/item/clothing/under/gimmick/rank/captain/suit, + /obj/item/clothing/under/gimmick/rank/head_of_personnel/suit, + /obj/item/clothing/under/lawyer/purpsuit, + /obj/item/clothing/under/rank/mailman, + /obj/item/clothing/under/dress/dress_saloon, + /obj/item/clothing/accessory/suspenders, + /obj/item/clothing/suit/storage/toggle/labcoat/mad, + /obj/item/clothing/suit/bio_suit/plaguedoctorsuit, + /obj/item/clothing/under/owl, + /obj/item/clothing/under/waiter, + /obj/item/clothing/under/gladiator, + /obj/item/clothing/under/soviet, + /obj/item/clothing/under/scratch, + /obj/item/clothing/under/wedding/bride_white, + /obj/item/clothing/suit/chef, + /obj/item/clothing/suit/apron/overalls, + /obj/item/clothing/under/redcoat, + /obj/item/clothing/under/kilt) + name = "Costumes crate" + cost = 10 + containertype = /obj/structure/closet/crate + containername = "Actor Costumes" + group = "Miscellaneous" + +/datum/supply_packs/formal_wear + contains = list(/obj/item/clothing/head/bowler, + /obj/item/clothing/head/that, + /obj/item/clothing/suit/storage/toggle/lawyer/bluejacket, + /obj/item/clothing/suit/storage/lawyer/purpjacket, + /obj/item/clothing/under/suit_jacket, + /obj/item/clothing/under/suit_jacket/female, + /obj/item/clothing/under/suit_jacket/really_black, + /obj/item/clothing/under/suit_jacket/red, + /obj/item/clothing/under/lawyer/bluesuit, + /obj/item/clothing/under/lawyer/purpsuit, + /obj/item/clothing/shoes/black, + /obj/item/clothing/shoes/black, + /obj/item/clothing/shoes/leather, + /obj/item/clothing/suit/wcoat) + name = "Formalwear closet" + cost = 30 + containertype = /obj/structure/closet + containername = "Formalwear for the best occasions." + group = "Miscellaneous" + +/datum/supply_packs/randomised/card_packs + num_contained = 5 + contains = list(/obj/item/pack/cardemon, + /obj/item/pack/spaceball, + /obj/item/deck/holder) + name = "Trading Card Crate" + cost = 20 + containertype = /obj/structure/closet/crate + containername = "cards crate" + group = "Miscellaneous" + +/datum/supply_packs/shield_gen + contains = list(/obj/item/circuitboard/shield_gen) + name = "Bubble shield generator circuitry" + cost = 50 + containertype = /obj/structure/closet/crate/secure + containername = "bubble shield generator circuitry crate" + group = "Engineering" + access = access_ce + +/datum/supply_packs/shield_gen_ex + contains = list(/obj/item/circuitboard/shield_gen_ex) + name = "Hull shield generator circuitry" + cost = 50 + containertype = /obj/structure/closet/crate/secure + containername = "hull shield generator circuitry crate" + group = "Engineering" + access = access_ce + +/datum/supply_packs/shield_cap + contains = list(/obj/item/circuitboard/shield_cap) + name = "Bubble shield capacitor circuitry" + cost = 50 + containertype = /obj/structure/closet/crate/secure + containername = "shield capacitor circuitry crate" + group = "Engineering" + access = access_ce + +/datum/supply_packs/smbig + name = "Supermatter Core" + contains = list(/obj/machinery/power/supermatter) + cost = 150 + containertype = /obj/structure/closet/crate/secure/phoron + containername = "Supermatter crate (CAUTION)" + group = "Engineering" + access = access_ce + + +/* /datum/supply_packs/smsmall // Currently nonfunctional, waiting on virgil + name = "Supermatter Shard" + contains = list(/obj/machinery/power/supermatter/shard) + cost = 25 + containertype = /obj/structure/closet/crate/secure/phoron + containername = "Supermatter shard crate (CAUTION)" + access = access_ce + group = "Engineering" */ + +/datum/supply_packs/eftpos + contains = list(/obj/item/device/eftpos) + name = "EFTPOS scanner" + cost = 10 + containertype = /obj/structure/closet/crate + containername = "EFTPOS crate" + group = "Miscellaneous" + +/datum/supply_packs/teg + contains = list(/obj/machinery/power/generator) + name = "Mark I Thermoelectric Generator" + cost = 75 + containertype = /obj/structure/closet/crate/secure/large + containername = "Mk1 TEG crate" + group = "Engineering" + access = access_engine + +/datum/supply_packs/circulator + contains = list(/obj/machinery/atmospherics/binary/circulator) + name = "Binary atmospheric circulator" + cost = 60 + containertype = /obj/structure/closet/crate/secure/large + containername = "Atmospheric circulator crate" + group = "Engineering" + access = access_engine + +/datum/supply_packs/air_dispenser + contains = list(/obj/machinery/pipedispenser/orderable) + name = "Pipe Dispenser" + cost = 35 + containertype = /obj/structure/closet/crate/secure/large + containername = "Pipe Dispenser Crate" + group = "Engineering" + access = access_atmospherics + +/datum/supply_packs/disposals_dispenser + contains = list(/obj/machinery/pipedispenser/disposal/orderable) + name = "Disposals Pipe Dispenser" + cost = 35 + containertype = /obj/structure/closet/crate/secure/large + containername = "Disposal Dispenser Crate" + group = "Engineering" + access = access_atmospherics + +/datum/supply_packs/bee_keeper + name = "Beekeeping crate" + contains = list(/obj/item/beehive_assembly, + /obj/item/bee_smoker, + /obj/item/bee_net, + /obj/item/bee_net, + /obj/item/clothing/suit/bio_suit/general,//beekeeping suit + /obj/item/clothing/head/bio_hood/general, + /obj/item/honey_frame, + /obj/item/honey_frame, + /obj/item/honey_frame, + /obj/item/honey_frame, + /obj/item/honey_frame, + /obj/item/bee_pack, + /obj/machinery/honey_extractor, + /obj/item/book/manual/hydroponics_beekeeping) + cost = 40 + containertype = /obj/structure/largecrate + containername = "Beekeeping crate" + access = access_hydroponics + group = "Hydroponics" + +/datum/supply_packs/aliengloves + name = "Non-Human Glove Kit" + contains = list(/obj/item/clothing/gloves/yellow/specialt, + /obj/item/clothing/gloves/yellow/specialt, + /obj/item/clothing/gloves/yellow/specialt, + /obj/item/clothing/gloves/yellow/specialu, + /obj/item/clothing/gloves/yellow/specialu, + /obj/item/clothing/gloves/yellow/specialu) + cost = 25 + containertype = /obj/structure/closet/crate + containername = "speciality gloves kit" + group = "Supply" + +/datum/supply_packs/alienmedicalgloves + name = "Non-Human Sterile Glove Kit" + contains = list(/obj/item/clothing/gloves/latex/unathi, + /obj/item/clothing/gloves/latex/nitrile/unathi, + /obj/item/clothing/gloves/latex/nitrile/unathi, + /obj/item/clothing/gloves/latex/tajara, + /obj/item/clothing/gloves/latex/nitrile/tajara, + /obj/item/clothing/gloves/latex/nitrile/tajara) + cost = 25 + containertype = /obj/structure/closet/crate + containername = "speciality sterile gloves kit" + group = "Medical" + +/datum/supply_packs/cardboard_sheets + contains = list(/obj/item/stack/material/cardboard) + name = "50 cardboard sheets" + amount = 50 + cost = 10 + containertype = /obj/structure/closet/crate + containername = "Cardboard sheets crate" + group = "Miscellaneous" + +/datum/supply_packs/bureaucracy + contains = list(/obj/item/clipboard, + /obj/item/clipboard, + /obj/item/pen/red, + /obj/item/pen/blue, + /obj/item/pen/blue, + /obj/item/device/camera_film, + /obj/item/folder/blue, + /obj/item/folder/red, + /obj/item/folder/yellow, + /obj/item/hand_labeler, + /obj/item/tape_roll, + /obj/structure/filingcabinet/chestdrawer{anchored = 0}, + /obj/item/paper_bin) + name = "Office supplies" + cost = 15 + containertype = /obj/structure/closet/crate + containername = "Office supplies crate" + group = "Supply" + +/datum/supply_packs/radsuit + contains = list(/obj/item/clothing/suit/radiation, + /obj/item/clothing/suit/radiation, + /obj/item/clothing/suit/radiation, + /obj/item/clothing/head/radiation, + /obj/item/clothing/head/radiation, + /obj/item/clothing/head/radiation) + name = "Radiation suits package" + cost = 20 + containertype = /obj/structure/closet/radiation + containername = "Radiation suit locker" + group = "Engineering" + +/datum/supply_packs/tactical + name = "Tactical suits" + containertype = /obj/structure/closet/crate/secure + containername = "Tactical Suit Locker" + cost = 45 + group = "Security" + access = access_armory + contains = 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/glasses/sunglasses/sechud/tactical, + /obj/item/storage/belt/security/tactical, + /obj/item/clothing/shoes/jackboots, + /obj/item/clothing/gloves/black, + /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/glasses/sunglasses/sechud/tactical, + /obj/item/storage/belt/security/tactical, + /obj/item/clothing/shoes/jackboots, + /obj/item/clothing/gloves/black) + +/datum/supply_packs/carpet + name = "Imported carpet" + containertype = /obj/structure/closet + containername = "Imported carpet crate" + cost = 15 + group = "Miscellaneous" + contains = list(/obj/item/stack/tile/carpet) + amount = 50 + +/datum/supply_packs/hydrotray + name = "Empty hydroponics tray" + cost = 10 + containertype = /obj/structure/closet/crate/hydroponics + containername = "Hydroponics tray crate" + group = "Hydroponics" + contains = list(/obj/machinery/portable_atmospherics/hydroponics{anchored = 0}) + access = access_hydroponics + +/datum/supply_packs/canister_empty + name = "Empty gas canister" + cost = 7 + containername = "Empty gas canister crate" + containertype = /obj/structure/largecrate + contains = list(/obj/machinery/portable_atmospherics/canister) + group = "Atmospherics" + +/datum/supply_packs/canister_air + name = "Air canister" + cost = 10 + containername = "Air canister crate" + containertype = /obj/structure/largecrate + group = "Atmospherics" + contains = list(/obj/machinery/portable_atmospherics/canister/air) + +/datum/supply_packs/canister_oxygen + name = "Oxygen canister" + cost = 15 + containername = "Oxygen canister crate" + containertype = /obj/structure/largecrate + group = "Atmospherics" + contains = list(/obj/machinery/portable_atmospherics/canister/oxygen) + +/datum/supply_packs/canister_nitrogen + name = "Nitrogen canister" + cost = 10 + containername = "Nitrogen canister crate" + containertype = /obj/structure/largecrate + group = "Atmospherics" + contains = list(/obj/machinery/portable_atmospherics/canister/nitrogen) + +/datum/supply_packs/canister_phoron + name = "Phoron gas canister" + cost = 60 + containername = "Phoron gas canister crate" + containertype = /obj/structure/closet/crate/secure/large + access = access_atmospherics + group = "Atmospherics" + contains = list(/obj/machinery/portable_atmospherics/canister/phoron) + +/datum/supply_packs/canister_sleeping_agent + name = "N2O gas canister" + cost = 40 + containername = "N2O gas canister crate" + containertype = /obj/structure/closet/crate/secure/large + access = access_atmospherics + group = "Atmospherics" + contains = list(/obj/machinery/portable_atmospherics/canister/sleeping_agent) + +/datum/supply_packs/canister_carbon_dioxide + name = "Carbon dioxide gas canister" + cost = 40 + containername = "CO2 canister crate" + containertype = /obj/structure/closet/crate/secure/large + access = access_atmospherics + group = "Atmospherics" + contains = list(/obj/machinery/portable_atmospherics/canister/carbon_dioxide) + +/datum/supply_packs/pacman_parts + name = "P.A.C.M.A.N. portable generator parts" + cost = 45 + containername = "P.A.C.M.A.N. Portable Generator Construction Kit" + containertype = /obj/structure/closet/crate/secure + group = "Engineering" + access = access_tech_storage + contains = list(/obj/item/stock_parts/micro_laser, + /obj/item/stock_parts/capacitor, + /obj/item/stock_parts/matter_bin, + /obj/item/circuitboard/pacman) + +/datum/supply_packs/super_pacman_parts + name = "Super P.A.C.M.A.N. portable generator parts" + cost = 55 + containername = "Super P.A.C.M.A.N. portable generator construction kit" + containertype = /obj/structure/closet/crate/secure + group = "Engineering" + access = access_tech_storage + contains = list(/obj/item/stock_parts/micro_laser, + /obj/item/stock_parts/capacitor, + /obj/item/stock_parts/matter_bin, + /obj/item/circuitboard/pacman/super) + +/datum/supply_packs/witch + name = "Witch costume" + containername = "Witch costume" + containertype = /obj/structure/closet + cost = 20 + contains = list(/obj/item/clothing/suit/wizrobe/marisa/fake, + /obj/item/clothing/shoes/sandal, + /obj/item/clothing/head/wizard/marisa/fake, + /obj/item/staff/broom) + group = "Miscellaneous" + +/datum/supply_packs/randomised/costume_hats + name = "Costume hats" + containername = "Actor hats crate" + containertype = /obj/structure/closet + cost = 10 + num_contained = 2 + contains = list(/obj/item/clothing/head/redcoat, + /obj/item/clothing/head/mailman, + /obj/item/clothing/head/plaguedoctorhat, + /obj/item/clothing/head/pirate, + /obj/item/clothing/head/hasturhood, + /obj/item/clothing/head/powdered_wig, + /obj/item/clothing/head/hairflower, + /obj/item/clothing/mask/gas/owl_mask, + /obj/item/clothing/mask/gas/monkeymask, + /obj/item/clothing/head/helmet/gladiator, + /obj/item/clothing/head/ushanka) + group = "Miscellaneous" + +/datum/supply_packs/randomised/webbing + name = "Webbing crate" + num_contained = 1 + contains = list(/obj/item/clothing/accessory/holster, + /obj/item/clothing/accessory/storage/brown_vest, + /obj/item/clothing/accessory/storage/webbing, + /obj/item/clothing/accessory/storage) + cost = 15 + containertype = /obj/structure/closet/crate + containername = "Webbing crate" + group = "Operations" + +/datum/supply_packs/spare_pda + name = "Spare PDAs" + cost = 10 + containertype = /obj/structure/closet/crate + containername = "Spare PDA crate" + group = "Supply" + contains = list(/obj/item/device/pda, + /obj/item/device/pda, + /obj/item/device/pda) + +/datum/supply_packs/randomised/dresses + name = "Womens formal dress locker" + containername = "Pretty dress locker" + containertype = /obj/structure/closet + cost = 15 + num_contained = 1 + contains = list(/obj/item/clothing/under/wedding/bride_orange, + /obj/item/clothing/under/wedding/bride_purple, + /obj/item/clothing/under/wedding/bride_blue, + /obj/item/clothing/under/wedding/bride_red, + /obj/item/clothing/under/wedding/bride_white, + /obj/item/clothing/under/sundress, + /obj/item/clothing/under/dress/dress_green, + /obj/item/clothing/under/dress/dress_pink, + /obj/item/clothing/under/dress/dress_orange, + /obj/item/clothing/under/dress/dress_yellow, + /obj/item/clothing/under/dress/dress_saloon) + group = "Miscellaneous" + +/datum/supply_packs/painters + name = "Station Painting Supplies" + cost = 10 + containername = "station painting supplies crate" + containertype = /obj/structure/closet/crate + group = "Engineering" + contains = list(/obj/item/device/pipe_painter, + /obj/item/device/pipe_painter, + /obj/item/device/floor_painter, + /obj/item/device/floor_painter) + +/datum/supply_packs/bluespacerelay + name = "Emergency Bluespace Relay Assembly Kit" + cost = 75 + containername = "emergency bluespace relay assembly kit" + containertype = /obj/structure/closet/crate + group = "Engineering" + contains = list(/obj/item/circuitboard/bluespacerelay, + /obj/item/stock_parts/manipulator, + /obj/item/stock_parts/manipulator, + /obj/item/stock_parts/subspace/filter, + /obj/item/stock_parts/subspace/crystal, + /obj/item/storage/toolbox/electrical) + +/datum/supply_packs/randomised/exosuit_mod + num_contained = 1 + contains = list( + /obj/item/device/kit/paint/ripley, + /obj/item/device/kit/paint/ripley/death, + /obj/item/device/kit/paint/ripley/flames_red, + /obj/item/device/kit/paint/ripley/flames_blue + ) + name = "Random APLU modkit" + cost = 200 + containertype = /obj/structure/closet/crate + containername = "heavy crate" + group = "Miscellaneous" + +/datum/supply_packs/randomised/exosuit_mod/durand + contains = list( + /obj/item/device/kit/paint/durand, + /obj/item/device/kit/paint/durand/seraph, + /obj/item/device/kit/paint/durand/phazon + ) + name = "Random Durand exosuit modkit" + +/datum/supply_packs/randomised/exosuit_mod/gygax + contains = list( + /obj/item/device/kit/paint/gygax, + /obj/item/device/kit/paint/gygax/darkgygax, + /obj/item/device/kit/paint/gygax/recitence + ) + name = "Random Gygax exosuit modkit" + +/datum/supply_packs/jukebox + name = "Jukebox" + contains = list(/obj/machinery/media/jukebox/) + cost = 200 + containertype = /obj/structure/largecrate + containername = "jukebox Crate" + group = "Hospitality" + +//voidsuit crates + +/datum/supply_packs/voidsuitcrate_eng + name = "Engineering Voidsuit Crate" + contains = list(/obj/item/clothing/head/helmet/space/void/engineering, + /obj/item/clothing/suit/space/void/engineering) + cost = 40 + containertype = /obj/structure/closet/crate/secure + containername = "engineer voidsuit kit" + access = access_engine_equip + group = "Engineering" + +/datum/supply_packs/voidsuitcrate_sec + name = "Security Voidsuit Crate" + contains = list(/obj/item/clothing/head/helmet/space/void/security, + /obj/item/clothing/suit/space/void/security) + cost = 40 + containertype = /obj/structure/closet/crate/secure + containername = "security voidsuit kit" + access = access_security + group = "Security" + +/datum/supply_packs/voidsuitcrate_med + name = "Medical Voidsuit Crate" + contains = list(/obj/item/clothing/head/helmet/space/void/medical, + /obj/item/clothing/suit/space/void/medical) + cost = 40 + containertype = /obj/structure/closet/crate/secure + containername = "medical voidsuit kit" + access = access_medical + group = "Medical" + +/datum/supply_packs/voidsuitcrate_atmos + name = "Atmospherics Voidsuit Crate" + contains = list(/obj/item/clothing/head/helmet/space/void/atmos, + /obj/item/clothing/suit/space/void/atmos) + cost = 40 + containertype = /obj/structure/closet/crate/secure + containername = "atmospherics voidsuit kit" + access = access_atmospherics + group = "Atmospherics" + +/datum/supply_packs/voidsuitcrate_minin + name = "Mining Voidsuit Crate" + contains = list(/obj/item/clothing/head/helmet/space/void/mining, + /obj/item/clothing/suit/space/void/mining) + cost = 40 + containertype = /obj/structure/closet/crate/secure + containername = "mining voidsuit kit" + access = access_mining + group = "Supply" + +//maglocks crates + +/datum/supply_packs/maglocks_engineering + name = "Engineering Magnetic Lock Crate" + contains = list(/obj/item/device/magnetic_lock/engineering, + /obj/item/device/magnetic_lock/engineering, + /obj/item/device/magnetic_lock/engineering) + cost = 30 + containertype = /obj/structure/closet/crate/secure + containername = "engineering magnetic locks" + access = access_engine_equip + group = "Engineering" + +/datum/supply_packs/maglocks_security + name = "Security Magnetic Lock Crate" + contains = list(/obj/item/device/magnetic_lock/security, + /obj/item/device/magnetic_lock/security, + /obj/item/device/magnetic_lock/security) + cost = 30 + containertype = /obj/structure/closet/crate/secure + containername = "security magnetic locks" + access = access_security + group = "Security" + +/datum/supply_packs/randomised/glowsticks + name = "Glowsticks crate" + num_contained = 4 + contains = list(/obj/item/device/flashlight/glowstick, + /obj/item/device/flashlight/glowstick/red, + /obj/item/device/flashlight/glowstick/blue, + /obj/item/device/flashlight/glowstick/orange, + /obj/item/device/flashlight/glowstick/yellow) + cost = 25 + containertype = /obj/structure/closet/crate + containername = "glowsticks crate" + group = "Operations" + +/datum/supply_packs/spessbike + name = "Space-bike Crate" + contains = list(/obj/vehicle/bike) + cost = 150 + containertype = /obj/structure/largecrate + containername = "Space-bike Crate" + contraband = 1 + group = "Operations" + +/datum/supply_packs/ipc_tag_pack + name = "IPC/Shell Tag Implanter Crate" + contains = list(/obj/item/implanter/ipc_tag, + /obj/item/implanter/ipc_tag, + /obj/item/implanter/ipc_tag + ) + cost = 40 + containertype = /obj/structure/closet/crate + containername = "IPC/Shell tag implanters" + group = "Security" + +/datum/supply_packs/ame_ctrl + name = "Antimatter Reactor Control Unit" + contains = list( + /obj/machinery/power/am_control_unit + ) + cost = 40 + containertype = /obj/structure/closet/crate/secure/large + containername = "antimatter reactor control unit kit" + group = "Engineering" + access = access_ce + +/datum/supply_packs/ame_section + name = "Antimatter Reactor Shielding Kit" + contains = list( + /obj/item/device/am_shielding_container, + /obj/item/device/am_shielding_container, + /obj/item/device/am_shielding_container, + /obj/item/device/am_shielding_container, + /obj/item/device/am_shielding_container, + /obj/item/device/am_shielding_container, + /obj/item/device/am_shielding_container, + /obj/item/device/am_shielding_container, + /obj/item/device/am_shielding_container + ) + cost = 20 + containertype = /obj/structure/closet/crate/secure/phoron + containername = "antimatter reactor shielding (9x) crate" + group = "Engineering" + access = access_ce + +/datum/supply_packs/ame_fuel + name = "Antimatter Reactor Fuel" + contains = list( + /obj/item/am_containment + ) + cost = 20 + containertype = /obj/structure/closet/crate/secure/bin + containername = "antimatter fuel container" + group = "Engineering" + access = access_engine + +/datum/supply_packs/hoist + name = "Hoist Crate" + contains = list( + /obj/item/hoist_kit, + /obj/item/hoist_kit, + /obj/item/hoist_kit + ) + cost = 50 + containertype = /obj/structure/closet/crate + containername = "hoist crate" + group = "Engineering" diff --git a/code/datums/trading/weaponry.dm b/code/datums/trading/weaponry.dm index 5b6744c6ac7..6e2e54e3e4d 100644 --- a/code/datums/trading/weaponry.dm +++ b/code/datums/trading/weaponry.dm @@ -113,7 +113,6 @@ /obj/item/clothing/accessory/storage/bayonet = TRADER_THIS_TYPE, /obj/item/gun/projectile/cannon = TRADER_THIS_TYPE, /obj/item/ammo_casing/cannon = TRADER_ALL, - /obj/item/gun/projectile/nuke = TRADER_THIS_TYPE, /obj/item/ammo_magazine/nuke = TRADER_THIS_TYPE, /obj/item/gun/projectile/pistol/adhomai = TRADER_THIS_TYPE, /obj/item/gun/projectile/deagle/adhomai = TRADER_THIS_TYPE diff --git a/code/datums/weakref.dm b/code/datums/weakref.dm index 22896145f73..a60ee9f1d07 100644 --- a/code/datums/weakref.dm +++ b/code/datums/weakref.dm @@ -1,17 +1,17 @@ -/datum/weakref - var/ref - -/datum/weakref/New(datum/D) - ref = SOFTREF(D) - -/datum/weakref/Destroy(force = 0) - crash_with("Some fuck is trying to [force ? "force-" : ""]delete a weakref!") - if (!force) - return QDEL_HINT_LETMELIVE // feck off - - return ..() - -/datum/weakref/proc/resolve() - var/datum/D = locate(ref) - if (!QDELETED(D) && D.weakref == src) - . = D +/datum/weakref + var/ref + +/datum/weakref/New(datum/D) + ref = SOFTREF(D) + +/datum/weakref/Destroy(force = 0) + crash_with("Some fuck is trying to [force ? "force-" : ""]delete a weakref!") + if (!force) + return QDEL_HINT_LETMELIVE // feck off + + return ..() + +/datum/weakref/proc/resolve() + var/datum/D = locate(ref) + if (!QDELETED(D) && D.weakref == src) + . = D diff --git a/code/datums/wires/nuclearbomb.dm b/code/datums/wires/nuclearbomb.dm index b67ffc3a604..b33daa753e4 100644 --- a/code/datums/wires/nuclearbomb.dm +++ b/code/datums/wires/nuclearbomb.dm @@ -1,58 +1,58 @@ -/datum/wires/nuclearbomb - holder_type = /obj/machinery/nuclearbomb - random = 1 - wire_count = 7 - -var/const/NUCLEARBOMB_WIRE_LIGHT = 1 -var/const/NUCLEARBOMB_WIRE_TIMING = 2 -var/const/NUCLEARBOMB_WIRE_SAFETY = 4 - -/datum/wires/nuclearbomb/CanUse(var/mob/living/L) - var/obj/machinery/nuclearbomb/N = holder - return N.panel_open - -/datum/wires/nuclearbomb/GetInteractWindow() - var/obj/machinery/nuclearbomb/N = holder - . += ..() - . += "
    The device is [N.timing ? "shaking!" : "still."]
    " - . += "The device is is [N.safety ? "quiet" : "whirring"].
    " - . += "The lights are [N.lighthack ? "static" : "functional"].
    " - -/datum/wires/nuclearbomb/UpdatePulsed(var/index) - var/obj/machinery/nuclearbomb/N = holder - switch(index) - if(NUCLEARBOMB_WIRE_LIGHT) - N.lighthack = !N.lighthack - N.update_icon() - spawn(100) - N.lighthack = !N.lighthack - N.update_icon() - if(NUCLEARBOMB_WIRE_TIMING) - if(N.timing) - spawn - log_and_message_admins("pulsed a nuclear bomb's detonation wire, causing it to explode.") - N.explode() - if(NUCLEARBOMB_WIRE_SAFETY) - N.safety = !N.safety - spawn(100) - N.safety = !N.safety - if(N.safety == 1) - N.visible_message("\The [N] quiets down.") - N.secure_device() - else - N.visible_message("\The [N] emits a quiet whirling noise!") - -/datum/wires/nuclearbomb/UpdateCut(var/index, var/mended) - var/obj/machinery/nuclearbomb/N = holder - switch(index) - if(NUCLEARBOMB_WIRE_SAFETY) - N.safety = mended - if(N.timing) - spawn - log_and_message_admins("cut a nuclear bomb's timing wire, causing it to explode.") - N.explode() - if(NUCLEARBOMB_WIRE_TIMING) - N.secure_device() - if(NUCLEARBOMB_WIRE_LIGHT) - N.lighthack = !mended - N.update_icon() +/datum/wires/nuclearbomb + holder_type = /obj/machinery/nuclearbomb + random = 1 + wire_count = 7 + +var/const/NUCLEARBOMB_WIRE_LIGHT = 1 +var/const/NUCLEARBOMB_WIRE_TIMING = 2 +var/const/NUCLEARBOMB_WIRE_SAFETY = 4 + +/datum/wires/nuclearbomb/CanUse(var/mob/living/L) + var/obj/machinery/nuclearbomb/N = holder + return N.panel_open + +/datum/wires/nuclearbomb/GetInteractWindow() + var/obj/machinery/nuclearbomb/N = holder + . += ..() + . += "
    The device is [N.timing ? "shaking!" : "still."]
    " + . += "The device is is [N.safety ? "quiet" : "whirring"].
    " + . += "The lights are [N.lighthack ? "static" : "functional"].
    " + +/datum/wires/nuclearbomb/UpdatePulsed(var/index) + var/obj/machinery/nuclearbomb/N = holder + switch(index) + if(NUCLEARBOMB_WIRE_LIGHT) + N.lighthack = !N.lighthack + N.update_icon() + spawn(100) + N.lighthack = !N.lighthack + N.update_icon() + if(NUCLEARBOMB_WIRE_TIMING) + if(N.timing) + spawn + log_and_message_admins("pulsed a nuclear bomb's detonation wire, causing it to explode.") + N.explode() + if(NUCLEARBOMB_WIRE_SAFETY) + N.safety = !N.safety + spawn(100) + N.safety = !N.safety + if(N.safety == 1) + N.visible_message("\The [N] quiets down.") + N.secure_device() + else + N.visible_message("\The [N] emits a quiet whirling noise!") + +/datum/wires/nuclearbomb/UpdateCut(var/index, var/mended) + var/obj/machinery/nuclearbomb/N = holder + switch(index) + if(NUCLEARBOMB_WIRE_SAFETY) + N.safety = mended + if(N.timing) + spawn + log_and_message_admins("cut a nuclear bomb's timing wire, causing it to explode.") + N.explode() + if(NUCLEARBOMB_WIRE_TIMING) + N.secure_device() + if(NUCLEARBOMB_WIRE_LIGHT) + N.lighthack = !mended + N.update_icon() diff --git a/code/defines/gases.dm b/code/defines/gases.dm index 22722cb962b..f58c8a5637c 100644 --- a/code/defines/gases.dm +++ b/code/defines/gases.dm @@ -1,45 +1,45 @@ -/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 = "plasma-purple" - overlay_limit = 0.7 - flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT - -/decl/xgm_gas/sleeping_agent - id = "sleeping_agent" - name = "Sleeping Agent" - specific_heat = 40 // J/(mol*K) - molar_mass = 0.044 // kg/mol. N2O - tile_overlay = "sleeping_agent" - 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 = "plasma-purple" + overlay_limit = 0.7 + flags = XGM_GAS_FUEL | XGM_GAS_CONTAMINANT + +/decl/xgm_gas/sleeping_agent + id = "sleeping_agent" + name = "Sleeping Agent" + specific_heat = 40 // J/(mol*K) + molar_mass = 0.044 // kg/mol. N2O + tile_overlay = "sleeping_agent" + overlay_limit = 1 flags = XGM_GAS_OXIDIZER \ No newline at end of file diff --git a/code/defines/mutable_appearance.dm b/code/defines/mutable_appearance.dm new file mode 100644 index 00000000000..1b095c555d5 --- /dev/null +++ b/code/defines/mutable_appearance.dm @@ -0,0 +1,19 @@ +// Mutable appearances are an inbuilt byond datastructure. Read the documentation on them by hitting F1 in DM. +// Basically use them instead of images for overlays/underlays and when changing an object's appearance if you're doing so with any regularity. +// Unless you need the overlay/underlay to have a different direction than the base object. Then you have to use an image due to a bug. + +// Mutable appearances are children of images, just so you know. + +/mutable_appearance/New() + ..() + plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE + // And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var + +// Helper similar to image() +/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE) + var/mutable_appearance/MA = new() + MA.icon = icon + MA.icon_state = icon_state + MA.layer = layer + MA.plane = plane + return MA diff --git a/code/defines/obj.dm b/code/defines/obj.dm index 25e0191b9d9..519f7ae68bf 100644 --- a/code/defines/obj.dm +++ b/code/defines/obj.dm @@ -1,113 +1,113 @@ -/obj/structure/signpost - icon = 'icons/obj/stationobjs.dmi' - icon_state = "signpost" - anchored = 1 - density = 1 - - attackby(obj/item/W as obj, mob/user as mob) - return attack_hand(user) - - attack_hand(mob/user as mob) - switch(alert("Travel back to ss13?",,"Yes","No")) - if("Yes") - if(user.z != src.z) return - user.loc.loc.Exited(user) //what the fuck is this - user.forceMove(pick(latejoin)) - if("No") - return - -/obj/effect/mark - var/mark = "" - icon = 'icons/misc/mark.dmi' - icon_state = "blank" - anchored = 1 - layer = 99 - mouse_opacity = 0 - unacidable = 1//Just to be sure. - -/obj/effect/beam - name = "beam" - density = 0 - unacidable = 1//Just to be sure. - var/def_zone - flags = PROXMOVE - pass_flags = PASSTABLE - - -/obj/effect/begin - name = "begin" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "begin" - anchored = 1.0 - unacidable = 1 - -/* - * 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 = 1.0 - -/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 = 1 - anchored = 1 - unacidable = 1//temporary until I decide whether the borg can be removed. -veyveyr - -/obj/item/mouse_drag_pointer = MOUSE_ACTIVE_POINTER - -/obj/item/beach_ball - icon = 'icons/misc/beach.dmi' - icon_state = "ball" - name = "beach ball" - item_state = "beachball" - density = 0 - anchored = 0 - w_class = 4 - force = 0.0 - throwforce = 0.0 - throw_speed = 1 - throw_range = 20 - flags = CONDUCT - drop_sound = 'sound/items/drop/rubber.ogg' - - 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/effect/spawner - name = "object spawner" - -/obj/structure/spaceship - name = "Abandoned Shuttle" - desc = "An ancient and inoperable shuttle-craft" - icon = 'icons/obj/machines/spaceship.dmi' - anchored = 1 - density = 1 - -/obj/structure/mainframe - name = "Ancient Mainframe" - desc = "A long-fried AI mainframe from the 2420s. It's more fit to be holding rats than AIs at this point." - icon = 'icons/obj/mainframe.dmi' - anchored = 1 - density = 1 +/obj/structure/signpost + icon = 'icons/obj/stationobjs.dmi' + icon_state = "signpost" + anchored = 1 + density = 1 + + attackby(obj/item/W as obj, mob/user as mob) + return attack_hand(user) + + attack_hand(mob/user as mob) + switch(alert("Travel back to ss13?",,"Yes","No")) + if("Yes") + if(user.z != src.z) return + user.loc.loc.Exited(user) //what the fuck is this + user.forceMove(pick(latejoin)) + if("No") + return + +/obj/effect/mark + var/mark = "" + icon = 'icons/misc/mark.dmi' + icon_state = "blank" + anchored = 1 + layer = 99 + mouse_opacity = 0 + unacidable = 1//Just to be sure. + +/obj/effect/beam + name = "beam" + density = 0 + unacidable = 1//Just to be sure. + var/def_zone + flags = PROXMOVE + pass_flags = PASSTABLE + + +/obj/effect/begin + name = "begin" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "begin" + anchored = 1.0 + unacidable = 1 + +/* + * 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 = 1.0 + +/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 = 1 + anchored = 1 + unacidable = 1//temporary until I decide whether the borg can be removed. -veyveyr + +/obj/item/mouse_drag_pointer = MOUSE_ACTIVE_POINTER + +/obj/item/beach_ball + icon = 'icons/misc/beach.dmi' + icon_state = "ball" + name = "beach ball" + item_state = "beachball" + density = 0 + anchored = 0 + w_class = 4 + force = 0.0 + throwforce = 0.0 + throw_speed = 1 + throw_range = 20 + flags = CONDUCT + drop_sound = 'sound/items/drop/rubber.ogg' + + 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/effect/spawner + name = "object spawner" + +/obj/structure/spaceship + name = "Abandoned Shuttle" + desc = "An ancient and inoperable shuttle-craft" + icon = 'icons/obj/machines/spaceship.dmi' + anchored = 1 + density = 1 + +/obj/structure/mainframe + name = "Ancient Mainframe" + desc = "A long-fried AI mainframe from the 2420s. It's more fit to be holding rats than AIs at this point." + icon = 'icons/obj/mainframe.dmi' + anchored = 1 + density = 1 diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm index 32fcbe4df94..73a4a65da7c 100644 --- a/code/defines/obj/weapon.dm +++ b/code/defines/obj/weapon.dm @@ -1,597 +1,553 @@ -/obj/item/phone - name = "red phone" - desc = "Should anything ever go wrong..." - icon = 'icons/obj/radio.dmi' - icon_state = "red_phone" - flags = CONDUCT - force = 3.0 - throwforce = 2.0 - throw_speed = 1 - throw_range = 4 - w_class = 2 - attack_verb = list("called", "rang") - hitsound = 'sound/weapons/ring.ogg' - -/obj/item/rsp - name = "\improper Rapid-Seed-Producer (RSP)" - desc = "A device used to rapidly deploy seeds." - icon = 'icons/obj/tools.dmi' - icon_state = "rfd" - opacity = 0 - density = 0 - anchored = 0.0 - var/stored_matter = 0 - var/mode = 1 - w_class = 3.0 - -/obj/item/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 = 2 - throw_speed = 3 - throw_range = 15 - attack_verb = list("HONKED") - var/spam_flag = 0 - -/obj/item/bikehorn/attack_self(mob/user as mob) - if (spam_flag == 0) - spam_flag = 1 - playsound(src.loc, 'sound/items/bikehorn.ogg', 50, 1) - src.add_fingerprint(user) - spawn(20) - spam_flag = 0 - return - -/obj/item/cane - name = "cane" - desc = "A cane used by a true gentlemen. Or a clown." - icon = 'icons/obj/weapons.dmi' - icon_state = "cane" - item_state = "stick" - flags = CONDUCT - force = 10 - throwforce = 7.0 - w_class = 4 - matter = list(DEFAULT_WALL_MATERIAL = 50) - attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") - -/obj/item/cane/attack(mob/living/target, mob/living/carbon/human/user, target_zone = BP_CHEST) - - if(!(istype(target) && istype(user))) - return ..() - - var/targetIsHuman = ishuman(target) - var/mob/living/carbon/human/targetashuman = target - var/wasselfattack = 0 - var/verbtouse = pick(attack_verb) - var/punct = "!" - var/class = "warning" - var/soundname = "swing_hit" - var/armorpercent = 0 - var/wasblocked = 0 - var/shoulddisarm = 0 - var/damagetype = HALLOSS - var/chargedelay = 4 // 4 half frames = 2 seconds - - if(targetIsHuman && targetashuman == user) - wasselfattack = 1 - - if (user.intent == I_HURT) - target_zone = get_zone_with_miss_chance(target_zone, target) //Vary the attack - damagetype = BRUTE - - if (targetIsHuman) - var/mob/living/carbon/human/targethuman = target - armorpercent = targethuman.run_armor_check(target_zone,"melee") - wasblocked = targethuman.check_shields(force, src, user, target_zone, null) //returns 1 if it's a block - - var/damageamount = force - - switch(user.a_intent) - if(I_HELP) - class = "notice" - punct = "." - soundname = 0 - if (target_zone == BP_HEAD || target_zone == BP_EYES || target_zone == "mouth") - verbtouse = pick("tapped") - else - verbtouse = pick("tapped","poked","prodded","touched") - damageamount = 0 - if(I_DISARM) - verbtouse = pick("smacked","slapped") - soundname = "punch" - if(targetIsHuman) - user.visible_message("[user] flips [user.get_pronoun(1)] [name]...", "You flip the [name], preparing a disarm...") - if (do_mob(user,target,chargedelay,display_progress=0)) - if(!wasblocked && damageamount) - var/chancemod = (100 - armorpercent)*0.05*damageamount // Lower chance if lower damage + high armor. Base chance is 50% at 10 damage. - if(target_zone == BP_L_HAND || target_zone == BP_L_ARM) - if (prob(chancemod) && target.l_hand && target.l_hand != src) - shoulddisarm = 1 - else if(target_zone == BP_R_HAND || target_zone == BP_R_ARM) - if (prob(chancemod) && target.r_hand && target.r_hand != src) - shoulddisarm = 2 - else - if (prob(chancemod*0.5) && target.l_hand && target.l_hand != src) - shoulddisarm = 1 - if (prob(chancemod*0.5) && target.r_hand && target.r_hand != src) - shoulddisarm += 2 - else - user.visible_message("[user] flips [user.get_pronoun(1)] [name] back to it's original position.", "You flip the [name] back to it's original position.") - return 0 - damageamount *= 0.25 - if(I_GRAB) - verbtouse = pick("hooked") - soundname = "punch" - if(targetIsHuman) - user.visible_message("[user] flips [user.get_pronoun(1)] [name]...", "You flip the [name], preparing a grab...") - if (do_mob(user,target,chargedelay,display_progress=0)) - if(!wasblocked && damageamount) - user.start_pulling(target) - else - verbtouse = pick("awkwardly tries to hook","fails to grab") - else - user.visible_message("[user] flips [user.get_pronoun(1)] [name] back to it's original position.", "You flip the [name] back to it's original position.") - return 0 - else - soundname = "punch" - damageamount *= 0.1 - - // Damage Logs - ///////////////////////// - user.lastattacked = target - target.lastattacker = user - if(!no_attack_log) - user.attack_log += "\[[time_stamp()]\] Attacked [target.name] ([target.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damagetype)])" - target.attack_log += "\[[time_stamp()]\] Attacked by [user.name] ([user.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damagetype)])" - msg_admin_attack("[key_name(user, highlight_special = 1)] attacked [key_name(target, highlight_special = 1)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damagetype)]) (JMP)",ckey=key_name(user),ckey_target=key_name(target) ) - ///////////////////////// - - var/washit = 0 - var/endmessage1st - var/endmessage3rd - - if(!target_zone || get_dist(user,target) > 1) //Dodged - endmessage1st = "Your [name] was dodged by [target]" - endmessage3rd = "[target] dodged the [name]" - soundname = "sound/weapons/punchmiss.ogg" - else if(wasblocked) // Blocked by Shield - endmessage1st = "Your [name] was blocked by [target]" - endmessage3rd = "[target] blocks the [name]" - soundname = "sound/weapons/punchmiss.ogg" - else - - washit = 1 - var/noun = "[target]" - var/selfnoun = "your" - - if(shoulddisarm) - if(wasselfattack) - selfnoun = "your grip" - noun = "[target.get_pronoun(1)] grip" - else - noun = "[target]'s grip" - selfnoun = noun - if (targetIsHuman && shoulddisarm != 3) // Query: Can non-humans hold objects in hands? - var/mob/living/carbon/human/targethuman = target - var/obj/item/organ/external/O = targethuman.get_organ(target_zone) - if (O.is_stump()) - if(wasselfattack) - selfnoun = "your missing [O.name]" - noun = "[target.get_pronoun(1)] missing [O.name]" - else - noun = "[target]'s missing [O.name]" - selfnoun = noun - else - if(wasselfattack) - selfnoun = "your [O.name]" - noun = "[target.get_pronoun(1)] [O.name]" - else - noun = "[target]'s [O.name]" - selfnoun = noun - - switch(shoulddisarm) - if(1) - endmessage1st = "You [verbtouse] the [target.l_hand.name] out of [selfnoun]" - endmessage3rd = "[user] [verbtouse] the [target.l_hand.name] out of [noun]" - target.drop_l_hand() - if(2) - endmessage1st = "You [verbtouse] the [target.r_hand.name] out of [selfnoun]" - endmessage3rd = "[user] [verbtouse] the [target.r_hand.name] out of [noun]" - target.drop_r_hand() - if(3) - endmessage1st = "You [verbtouse] both the [target.r_hand.name] and the [target.l_hand.name] out of [selfnoun]" - endmessage3rd = "[user] [verbtouse] both the [target.r_hand.name] and the [target.l_hand.name] out of [noun]" - target.drop_l_hand() - target.drop_r_hand() - else - endmessage1st = "You [verbtouse] [selfnoun] with the [name]" - endmessage3rd = "[user] [verbtouse] [noun] with the [name]" - - if(damageamount > 0) // Poking will no longer do damage until there is some fix that makes it so that 0.0001 HALLOS doesn't cause bleed. - target.standard_weapon_hit_effects(src, user, damageamount, armorpercent, target_zone) - - user.visible_message("[endmessage3rd][punct]", "[endmessage1st][punct]") - user.do_attack_animation(target) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - - if(soundname) - playsound(src.loc, soundname, 50, 1, -1) - - return washit - -/obj/item/cane/concealed - var/concealed_blade - -/obj/item/cane/concealed/New() - ..() - var/obj/item/canesword/temp_blade = new(src) - concealed_blade = temp_blade - temp_blade.attack_self() - -/obj/item/cane/concealed/attack_self(var/mob/user) - if(concealed_blade) - user.visible_message("[user] has unsheathed \a [concealed_blade] from \his [src]!", "You unsheathe \the [concealed_blade] from \the [src].") - // Calling drop/put in hands to properly call item drop/pickup procs - playsound(user.loc, 'sound/weapons/holster/sheathout.ogg', 50, 1) - user.drop_from_inventory(src) - user.put_in_hands(concealed_blade) - user.put_in_hands(src) - user.update_inv_l_hand(0) - user.update_inv_r_hand() - concealed_blade = null - update_icon() - else - ..() - -/obj/item/cane/concealed/attackby(var/obj/item/canesword/W, var/mob/user) - if(!src.concealed_blade && istype(W)) - user.visible_message("[user] has sheathed \a [W] into \his [src]!", "You sheathe \the [W] into \the [src].") - playsound(user.loc, 'sound/weapons/holster/sheathin.ogg', 50, 1) - user.drop_from_inventory(W) - W.forceMove(src) - src.concealed_blade = W - update_icon() - else - ..() - -/obj/item/cane/concealed/update_icon() - if(concealed_blade) - name = initial(name) - icon_state = initial(icon_state) - item_state = initial(item_state) - else - name = "cane shaft" - icon_state = "nullrod" - item_state = "foldcane" - -/obj/item/cane/crutch - name ="crutch" - desc = "A long stick with a crosspiece at the top, used to help with walking." - icon_state = "crutch" - item_state = "crutch" - -/obj/item/disk - name = "disk" - icon = 'icons/obj/items.dmi' - -/* -/obj/item/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 = 5.0 -*/ - -/obj/item/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 = 4.0 - -/obj/item/gift/random_pixel/Initialize() - pixel_x = rand(-16,16) - pixel_y = rand(-16,16) - -/obj/item/legcuffs - name = "legcuffs" - desc = "Use this to keep prisoners in line." - gender = PLURAL - icon = 'icons/obj/items.dmi' - icon_state = "handcuff" - flags = CONDUCT - throwforce = 0 - w_class = 3.0 - origin_tech = list(TECH_MATERIAL = 1) - var/breakouttime = 300 //Deciseconds = 30s = 0.5 minute - -/obj/item/caution - desc = "Caution! Wet Floor!" - name = "wet floor sign" - icon = 'icons/obj/janitor.dmi' - icon_state = "caution" - force = 1.0 - throwforce = 3.0 - throw_speed = 1 - throw_range = 5 - w_class = 2.0 - attack_verb = list("warned", "cautioned", "smashed") - drop_sound = 'sound/items/drop/shoes.ogg' - -/obj/item/caution/attack_self(mob/user as mob) - if(src.icon_state == "caution") - src.icon_state = "caution_blinking" - to_chat(user, "You turn the sign on.") - else - src.icon_state = "caution" - to_chat(user, "You turn the sign off.") - -/obj/item/caution/AltClick() - if(!usr || usr.stat || usr.lying || usr.restrained() || !Adjacent(usr)) return - if(src.icon_state == "caution") - src.icon_state = "caution_blinking" - to_chat(usr, "You turn the sign on.") - else - src.icon_state = "caution" - to_chat(usr, "You turn the sign off.") - -/obj/item/caution/cone - desc = "This cone is trying to warn you of something!" - name = "warning cone" - icon_state = "cone" - item_state = "cone" - contained_sprite = 1 - slot_flags = SLOT_HEAD - -/obj/item/caution/cone/attack_self(mob/user as mob) - return - -/obj/item/caution/cone/AltClick() - return - -/*/obj/item/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 = CONDUCT | ONBELT - w_class = 2.0 - item_state = "radio" - throw_speed = 4 - throw_range = 20 - matter = list("metal" = 100 - origin_tech = list(TECH_MAGNET = 2, TECH_ILLEGAL = 3)*/ - -/obj/item/SWF_uplink - name = "station-bounced radio" - desc = "used to comunicate 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 - flags = CONDUCT - slot_flags = SLOT_BELT - item_state = "radio" - throwforce = 5 - w_class = 2.0 - throw_speed = 4 - throw_range = 20 - matter = list(DEFAULT_WALL_MATERIAL = 100) - origin_tech = list(TECH_MAGNET = 1) - -/obj/item/staff - name = "wizards staff" - desc = "Apparently a staff used by the wizard." - icon = 'icons/obj/wizard.dmi' - icon_state = "staff" - force = 3.0 - throwforce = 5.0 - throw_speed = 1 - throw_range = 5 - w_class = 2.0 - attack_verb = list("bludgeoned", "whacked", "disciplined") - -/obj/item/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/staff/gentcane - name = "Gentlemans Cane" - desc = "An ebony can with an ivory tip." - icon = 'icons/obj/weapons.dmi' - icon_state = "cane" - item_state = "stick" - -/obj/item/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 = "stick" - force = 3.0 - throwforce = 5.0 - throw_speed = 1 - throw_range = 5 - w_class = 2.0 - -/obj/item/wire - desc = "This is just a simple piece of regular insulated wire." - name = "wire" - icon = 'icons/obj/power.dmi' - icon_state = "item_wire" - var/amount = 1.0 - var/laying = 0.0 - var/old_lay = null - matter = list(DEFAULT_WALL_MATERIAL = 40) - attack_verb = list("whipped", "lashed", "disciplined", "tickled") - -/obj/item/module - icon = 'icons/obj/module.dmi' - icon_state = "std_module" - w_class = 2.0 - item_state = "electronic" - flags = CONDUCT - var/mtype = 1 // 1=electronic 2=hardware - -/obj/item/module/card_reader - name = "card reader module" - icon_state = "card_mod" - desc = "An electronic module for reading data and ID cards." - -/obj/item/module/power_control - name = "power control module" - icon_state = "power_mod" - desc = "Heavy-duty switching circuits for power control." - matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) - -/obj/item/module/power_control/attackby(var/obj/item/W as obj, var/mob/user as mob) - if (W.ismultitool()) - var/obj/item/circuitboard/ghettosmes/newcircuit = new/obj/item/circuitboard/ghettosmes(user.loc) - qdel(src) - user.put_in_hands(newcircuit) - -/obj/item/module/id_auth - name = "\improper ID authentication module" - icon_state = "id_mod" - desc = "A module allowing secure authorization of ID cards." - -/obj/item/module/cell_power - name = "power cell regulator module" - icon_state = "power_mod" - desc = "A converter and regulator allowing the use of power cells." - -/obj/item/module/cell_power - name = "power cell charger module" - icon_state = "power_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 = 1.0 - item_state = "electronic" - throw_speed = 4 - throw_range = 20 - -/obj/item/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 = input("Select the camera to observe", null) as null|anything in 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/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 = 1 - throwforce = 2 - var/cigarcount = 6 - flags = ONBELT - */ - -/obj/item/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/neuralbroke - name = "fried neural socket" - desc = "A Vaurca neural socket subjected to extreme heat. It's security chip appears to be fried." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "neuralbroke" - -/obj/item/neuralbroke/attackby(obj/item/W as obj, mob/user as mob) - if(W.isscrewdriver()) - new /obj/item/device/encryptionkey/hivenet(user.loc) - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You bypass the fried security chip and extract the encryption key.") - to_chat(user, "The fried neural socket crumbles away like dust.") - qdel(src) - -/obj/item/storage/part_replacer - name = "rapid part exchange device" - desc = "Special mechanical module made to store, sort, and apply standard machine parts." - icon_state = "RPED" - item_state = "RPED" - w_class = 5 - can_hold = list(/obj/item/stock_parts,/obj/item/reagent_containers/glass/beaker) - storage_slots = 50 - use_to_pickup = 1 - allow_quick_gather = 1 - allow_quick_empty = 1 - collection_mode = 1 - display_contents_with_number = 1 - max_w_class = 3 - max_storage_space = 100 - -/obj/item/ectoplasm - name = "ectoplasm" - desc = "spooky" - gender = PLURAL - icon = 'icons/obj/wizard.dmi' - icon_state = "ectoplasm" - -/obj/item/anomaly_core - name = "anomaly core" - desc = "An advanced bluespace device, little is known about its applications, meriting research into its purpose." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "anomaly_core" - origin_tech = list(TECH_MAGNET = 6, TECH_MATERIAL = 7, TECH_BLUESPACE = 8) - -/obj/item/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) +/obj/item/phone + name = "red phone" + desc = "Should anything ever go wrong..." + icon = 'icons/obj/radio.dmi' + icon_state = "red_phone" + flags = CONDUCT + force = 3.0 + throwforce = 2.0 + throw_speed = 1 + throw_range = 4 + w_class = 2 + attack_verb = list("called", "rang") + hitsound = 'sound/weapons/ring.ogg' + +/obj/item/rsp + name = "\improper Rapid-Seed-Producer (RSP)" + desc = "A device used to rapidly deploy seeds." + icon = 'icons/obj/tools.dmi' + icon_state = "rfd" + opacity = 0 + density = 0 + anchored = 0.0 + var/stored_matter = 0 + var/mode = 1 + w_class = 3.0 + +/obj/item/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 = 2 + throw_speed = 3 + throw_range = 15 + attack_verb = list("HONKED") + var/spam_flag = 0 + +/obj/item/bikehorn/attack_self(mob/user as mob) + if (spam_flag == 0) + spam_flag = 1 + playsound(src.loc, 'sound/items/bikehorn.ogg', 50, 1) + src.add_fingerprint(user) + spawn(20) + spam_flag = 0 + return + +/obj/item/cane + name = "cane" + desc = "A cane used by a true gentlemen. Or a clown." + icon = 'icons/obj/weapons.dmi' + icon_state = "cane" + item_state = "stick" + flags = CONDUCT + force = 10 + throwforce = 7.0 + w_class = 4 + matter = list(DEFAULT_WALL_MATERIAL = 50) + attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") + +/obj/item/cane/attack(mob/living/target, mob/living/carbon/human/user, target_zone = BP_CHEST) + + if(!(istype(target) && istype(user))) + return ..() + + var/targetIsHuman = ishuman(target) + var/mob/living/carbon/human/targetashuman = target + var/wasselfattack = 0 + var/verbtouse = pick(attack_verb) + var/punct = "!" + var/class = "warning" + var/soundname = "swing_hit" + var/armorpercent = 0 + var/wasblocked = 0 + var/shoulddisarm = 0 + var/damagetype = PAIN + var/chargedelay = 4 // 4 half frames = 2 seconds + + if(targetIsHuman && targetashuman == user) + wasselfattack = 1 + + if (user.intent == I_HURT) + target_zone = get_zone_with_miss_chance(target_zone, target) //Vary the attack + damagetype = BRUTE + + if (targetIsHuman) + var/mob/living/carbon/human/targethuman = target + armorpercent = targethuman.run_armor_check(target_zone,"melee") + wasblocked = targethuman.check_shields(force, src, user, target_zone, null) //returns 1 if it's a block + + var/damageamount = force + + switch(user.a_intent) + if(I_HELP) + class = "notice" + punct = "." + soundname = 0 + if (target_zone == BP_HEAD || target_zone == BP_EYES || target_zone == BP_MOUTH) + verbtouse = pick("tapped") + else + verbtouse = pick("tapped","poked","prodded","touched") + damageamount = 0 + if(I_DISARM) + verbtouse = pick("smacked","slapped") + soundname = "punch" + if(targetIsHuman) + user.visible_message("[user] flips [user.get_pronoun(1)] [name]...", "You flip the [name], preparing a disarm...") + if (do_mob(user,target,chargedelay,display_progress=0)) + if(!wasblocked && damageamount) + var/chancemod = (100 - armorpercent)*0.05*damageamount // Lower chance if lower damage + high armor. Base chance is 50% at 10 damage. + if(target_zone == BP_L_HAND || target_zone == BP_L_ARM) + if (prob(chancemod) && target.l_hand && target.l_hand != src) + shoulddisarm = 1 + else if(target_zone == BP_R_HAND || target_zone == BP_R_ARM) + if (prob(chancemod) && target.r_hand && target.r_hand != src) + shoulddisarm = 2 + else + if (prob(chancemod*0.5) && target.l_hand && target.l_hand != src) + shoulddisarm = 1 + if (prob(chancemod*0.5) && target.r_hand && target.r_hand != src) + shoulddisarm += 2 + else + user.visible_message("[user] flips [user.get_pronoun(1)] [name] back to it's original position.", "You flip the [name] back to it's original position.") + return 0 + damageamount *= 0.25 + if(I_GRAB) + verbtouse = pick("hooked") + soundname = "punch" + if(targetIsHuman) + user.visible_message("[user] flips [user.get_pronoun(1)] [name]...", "You flip the [name], preparing a grab...") + if (do_mob(user,target,chargedelay,display_progress=0)) + if(!wasblocked && damageamount) + user.start_pulling(target) + else + verbtouse = pick("awkwardly tries to hook","fails to grab") + else + user.visible_message("[user] flips [user.get_pronoun(1)] [name] back to it's original position.", "You flip the [name] back to it's original position.") + return 0 + else + soundname = "punch" + damageamount *= 0.1 + + // Damage Logs + ///////////////////////// + user.lastattacked = target + target.lastattacker = user + if(!no_attack_log) + user.attack_log += "\[[time_stamp()]\] Attacked [target.name] ([target.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damagetype)])" + target.attack_log += "\[[time_stamp()]\] Attacked by [user.name] ([user.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damagetype)])" + msg_admin_attack("[key_name(user, highlight_special = 1)] attacked [key_name(target, highlight_special = 1)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damagetype)]) (JMP)",ckey=key_name(user),ckey_target=key_name(target) ) + ///////////////////////// + + var/washit = 0 + var/endmessage1st + var/endmessage3rd + + if(!target_zone || get_dist(user,target) > 1) //Dodged + endmessage1st = "Your [name] was dodged by [target]" + endmessage3rd = "[target] dodged the [name]" + soundname = "sound/weapons/punchmiss.ogg" + else if(wasblocked) // Blocked by Shield + endmessage1st = "Your [name] was blocked by [target]" + endmessage3rd = "[target] blocks the [name]" + soundname = "sound/weapons/punchmiss.ogg" + else + + washit = 1 + var/noun = "[target]" + var/selfnoun = "your" + + if(shoulddisarm) + if(wasselfattack) + selfnoun = "your grip" + noun = "[target.get_pronoun(1)] grip" + else + noun = "[target]'s grip" + selfnoun = noun + if (targetIsHuman && shoulddisarm != 3) // Query: Can non-humans hold objects in hands? + var/mob/living/carbon/human/targethuman = target + var/obj/item/organ/external/O = targethuman.get_organ(target_zone) + if (O.is_stump()) + if(wasselfattack) + selfnoun = "your missing [O.name]" + noun = "[target.get_pronoun(1)] missing [O.name]" + else + noun = "[target]'s missing [O.name]" + selfnoun = noun + else + if(wasselfattack) + selfnoun = "your [O.name]" + noun = "[target.get_pronoun(1)] [O.name]" + else + noun = "[target]'s [O.name]" + selfnoun = noun + + switch(shoulddisarm) + if(1) + endmessage1st = "You [verbtouse] the [target.l_hand.name] out of [selfnoun]" + endmessage3rd = "[user] [verbtouse] the [target.l_hand.name] out of [noun]" + target.drop_l_hand() + if(2) + endmessage1st = "You [verbtouse] the [target.r_hand.name] out of [selfnoun]" + endmessage3rd = "[user] [verbtouse] the [target.r_hand.name] out of [noun]" + target.drop_r_hand() + if(3) + endmessage1st = "You [verbtouse] both the [target.r_hand.name] and the [target.l_hand.name] out of [selfnoun]" + endmessage3rd = "[user] [verbtouse] both the [target.r_hand.name] and the [target.l_hand.name] out of [noun]" + target.drop_l_hand() + target.drop_r_hand() + else + endmessage1st = "You [verbtouse] [selfnoun] with the [name]" + endmessage3rd = "[user] [verbtouse] [noun] with the [name]" + + if(damageamount > 0) // Poking will no longer do damage until there is some fix that makes it so that 0.0001 HALLOS doesn't cause bleed. + target.standard_weapon_hit_effects(src, user, damageamount, armorpercent, target_zone) + + user.visible_message("[endmessage3rd][punct]", "[endmessage1st][punct]") + user.do_attack_animation(target) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + + if(soundname) + playsound(src.loc, soundname, 50, 1, -1) + + return washit + +/obj/item/cane/concealed + var/concealed_blade + +/obj/item/cane/concealed/New() + ..() + var/obj/item/canesword/temp_blade = new(src) + concealed_blade = temp_blade + temp_blade.attack_self() + +/obj/item/cane/concealed/attack_self(var/mob/user) + if(concealed_blade) + user.visible_message("[user] has unsheathed \a [concealed_blade] from \his [src]!", "You unsheathe \the [concealed_blade] from \the [src].") + // Calling drop/put in hands to properly call item drop/pickup procs + playsound(user.loc, 'sound/weapons/holster/sheathout.ogg', 50, 1) + user.drop_from_inventory(src) + user.put_in_hands(concealed_blade) + user.put_in_hands(src) + user.update_inv_l_hand(0) + user.update_inv_r_hand() + concealed_blade = null + update_icon() + else + ..() + +/obj/item/cane/concealed/attackby(var/obj/item/canesword/W, var/mob/user) + if(!src.concealed_blade && istype(W)) + user.visible_message("[user] has sheathed \a [W] into \his [src]!", "You sheathe \the [W] into \the [src].") + playsound(user.loc, 'sound/weapons/holster/sheathin.ogg', 50, 1) + user.drop_from_inventory(W) + W.forceMove(src) + src.concealed_blade = W + update_icon() + else + ..() + +/obj/item/cane/concealed/update_icon() + if(concealed_blade) + name = initial(name) + icon_state = initial(icon_state) + item_state = initial(item_state) + else + name = "cane shaft" + icon_state = "nullrod" + item_state = "foldcane" + +/obj/item/cane/crutch + name ="crutch" + desc = "A long stick with a crosspiece at the top, used to help with walking." + icon_state = "crutch" + item_state = "crutch" + +/obj/item/disk + name = "disk" + icon = 'icons/obj/items.dmi' + +/* +/obj/item/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 = 5.0 +*/ + +/obj/item/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 = 4.0 + +/obj/item/gift/random_pixel/Initialize() + pixel_x = rand(-16,16) + pixel_y = rand(-16,16) + +/obj/item/legcuffs + name = "legcuffs" + desc = "Use this to keep prisoners in line." + gender = PLURAL + icon = 'icons/obj/items.dmi' + icon_state = "handcuff" + flags = CONDUCT + throwforce = 0 + w_class = 3.0 + origin_tech = list(TECH_MATERIAL = 1) + var/breakouttime = 300 //Deciseconds = 30s = 0.5 minute + +/*/obj/item/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 = CONDUCT | ONBELT + w_class = 2.0 + item_state = "radio" + throw_speed = 4 + throw_range = 20 + matter = list("metal" = 100 + origin_tech = list(TECH_MAGNET = 2, TECH_ILLEGAL = 3)*/ + +/obj/item/SWF_uplink + name = "station-bounced radio" + desc = "used to comunicate 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 + flags = CONDUCT + slot_flags = SLOT_BELT + item_state = "radio" + throwforce = 5 + w_class = 2.0 + throw_speed = 4 + throw_range = 20 + matter = list(DEFAULT_WALL_MATERIAL = 100) + origin_tech = list(TECH_MAGNET = 1) + +/obj/item/staff + name = "wizards staff" + desc = "Apparently a staff used by the wizard." + icon = 'icons/obj/wizard.dmi' + icon_state = "staff" + force = 3.0 + throwforce = 5.0 + throw_speed = 1 + throw_range = 5 + w_class = 2.0 + attack_verb = list("bludgeoned", "whacked", "disciplined") + +/obj/item/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/staff/gentcane + name = "Gentlemans Cane" + desc = "An ebony can with an ivory tip." + icon = 'icons/obj/weapons.dmi' + icon_state = "cane" + item_state = "stick" + +/obj/item/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 = "stick" + force = 3.0 + throwforce = 5.0 + throw_speed = 1 + throw_range = 5 + w_class = 2.0 + +/obj/item/wire + desc = "This is just a simple piece of regular insulated wire." + name = "wire" + icon = 'icons/obj/power.dmi' + icon_state = "item_wire" + var/amount = 1.0 + var/laying = 0.0 + var/old_lay = null + matter = list(DEFAULT_WALL_MATERIAL = 40) + attack_verb = list("whipped", "lashed", "disciplined", "tickled") + +/obj/item/module + icon = 'icons/obj/module.dmi' + icon_state = "std_module" + w_class = 2.0 + item_state = "electronic" + flags = CONDUCT + var/mtype = 1 // 1=electronic 2=hardware + +/obj/item/module/card_reader + name = "card reader module" + icon_state = "card_mod" + desc = "An electronic module for reading data and ID cards." + +/obj/item/module/power_control + name = "power control module" + icon_state = "power_mod" + desc = "Heavy-duty switching circuits for power control." + matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) + +/obj/item/module/power_control/attackby(var/obj/item/W as obj, var/mob/user as mob) + if (W.ismultitool()) + var/obj/item/circuitboard/ghettosmes/newcircuit = new/obj/item/circuitboard/ghettosmes(user.loc) + qdel(src) + user.put_in_hands(newcircuit) + +/obj/item/module/id_auth + name = "\improper ID authentication module" + icon_state = "id_mod" + desc = "A module allowing secure authorization of ID cards." + +/obj/item/module/cell_power + name = "power cell regulator module" + icon_state = "power_mod" + desc = "A converter and regulator allowing the use of power cells." + +/obj/item/module/cell_power + name = "power cell charger module" + icon_state = "power_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 = 1.0 + item_state = "electronic" + throw_speed = 4 + throw_range = 20 + +/obj/item/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 = input("Select the camera to observe", null) as null|anything in 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/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 = 1 + throwforce = 2 + var/cigarcount = 6 + flags = ONBELT + */ + +/obj/item/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/neuralbroke + name = "fried neural socket" + desc = "A Vaurca neural socket subjected to extreme heat. It's security chip appears to be fried." + icon = 'icons/obj/stock_parts.dmi' + icon_state = "neuralbroke" + +/obj/item/neuralbroke/attackby(obj/item/W as obj, mob/user as mob) + if(W.isscrewdriver()) + new /obj/item/device/encryptionkey/hivenet(user.loc) + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You bypass the fried security chip and extract the encryption key.") + to_chat(user, "The fried neural socket crumbles away like dust.") + qdel(src) + +/obj/item/storage/part_replacer + name = "rapid part exchange device" + desc = "Special mechanical module made to store, sort, and apply standard machine parts." + icon_state = "RPED" + item_state = "RPED" + w_class = 5 + can_hold = list(/obj/item/stock_parts,/obj/item/reagent_containers/glass/beaker) + storage_slots = 50 + use_to_pickup = 1 + allow_quick_gather = 1 + allow_quick_empty = 1 + collection_mode = 1 + display_contents_with_number = 1 + max_w_class = 3 + max_storage_space = 100 + +/obj/item/ectoplasm + name = "ectoplasm" + desc = "spooky" + gender = PLURAL + icon = 'icons/obj/wizard.dmi' + icon_state = "ectoplasm" + +/obj/item/anomaly_core + name = "anomaly core" + desc = "An advanced bluespace device, little is known about its applications, meriting research into its purpose." + icon = 'icons/obj/stock_parts.dmi' + icon_state = "anomaly_core" + origin_tech = list(TECH_MAGNET = 6, TECH_MATERIAL = 7, TECH_BLUESPACE = 8) + +/obj/item/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) diff --git a/code/defines/procs/AStar.dm b/code/defines/procs/AStar.dm index 1aef60bf053..34d5bb74f2d 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/proc/comparison_function - - New(compare) - queue = list() - comparison_function = compare - - proc/IsEmpty() - return !queue.len - - 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 - - proc/Dequeue() - if(!queue.len) - return 0 - return Remove(1) - - 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 - - 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 - - proc/List() - return queue.Copy() - - proc/Length() - return queue.len - - 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 - - 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/proc/adjacent, var/proc/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/proc/comparison_function + + New(compare) + queue = list() + comparison_function = compare + + proc/IsEmpty() + return !queue.len + + 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 + + proc/Dequeue() + if(!queue.len) + return 0 + return Remove(1) + + 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 + + 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 + + proc/List() + return queue.Copy() + + proc/Length() + return queue.len + + 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 + + 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/proc/adjacent, var/proc/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 ff7eab0c659..b876be5c74b 100644 --- a/code/defines/procs/dbcore.dm +++ b/code/defines/procs/dbcore.dm @@ -1,434 +1,434 @@ -//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 - -/DBConnection - var/_db_con // This variable contains a reference to the actual database connection. - var/con_dbi // This variable is a string containing the DBI MySQL requires. - var/con_user // This variable contains the username data. - var/con_password // This variable contains the password data. - var/con_cursor // This contains the default database cursor data. - var/con_server = "" - var/con_port = 3306 - var/con_database = "" - var/failed_connections = 0 - -/DBConnection/New(server, port = 3306, database, username, password_handler, cursor_handler = Default_Cursor, dbi_handler) - con_user = username - con_password = password_handler - con_cursor = cursor_handler - con_server = server - con_port = port - con_database = database - - if (dbi_handler) - con_dbi = dbi_handler - else - con_dbi = "dbi:mysql:[database]:[server]:[port]" - - _db_con = _dm_db_new_con() - -/DBConnection/proc/Connect(dbi_handler = con_dbi, user_handler = con_user, password_handler = con_password, cursor_handler) - if (!config.sql_enabled) - return 0 - if (!src) - return 0 - cursor_handler = con_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/Reconnect() - Disconnect() - Connect() - -/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, new_dbi) - if (IsConnected()) - Disconnect() - con_database = database_name - return Connect(new_dbi ? new_dbi : "dbi:mysql:[database_name]:[con_server]:[con_port]", con_user, con_password) - -/DBConnection/proc/NewQuery(sql_query, cursor_handler = con_cursor) - return new/DBQuery(sql_query, src, cursor_handler) - -/* -Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query. -Rows missing columns present in other rows will resolve to SQL NULL -You are expected to do your own escaping of the data, and expected to provide your own quotes for strings. -The duplicate_key arg can be true to automatically generate this part of the query - or set to a string that is appended to the end of the query -Ignore_errors instructes mysql to continue inserting rows if some of them have errors. - the erroneous row(s) aren't inserted and there isn't really any way to know why or why errored -Delayed insert mode was removed in mysql 7 and only works with MyISAM type tables, - It was included because it is still supported in mariadb. - It does not work with duplicate_key and the mysql server ignores it in those cases -*/ -/DBConnection/proc/MassInsert(table, list/rows, duplicate_key = FALSE, ignore_errors = FALSE, delayed = FALSE) - if (!table || !rows || !istype(rows)) - return - var/list/columns = list() - var/list/sorted_rows = list() - - for (var/list/row in rows) - var/list/sorted_row = list() - sorted_row.len = columns.len - for (var/column in row) - var/idx = columns[column] - if (!idx) - idx = columns.len + 1 - columns[column] = idx - sorted_row.len = columns.len - - sorted_row[idx] = row[column] - sorted_rows[++sorted_rows.len] = sorted_row - - if (duplicate_key == TRUE) - var/list/column_list = list() - for (var/column in columns) - column_list += "[column] = VALUES([column])" - duplicate_key = "ON DUPLICATE KEY UPDATE [column_list.Join(", ")]\n" - else if (duplicate_key == FALSE) - duplicate_key = null - - if (ignore_errors) - ignore_errors = " IGNORE" - else - ignore_errors = null - - if (delayed) - delayed = " DELAYED" - else - delayed = null - - var/list/sqlrowlist = list() - var/len = columns.len - for (var/list/row in sorted_rows) - if (length(row) != len) - row.len = len - for (var/value in row) - if (value == null) - value = "NULL" - sqlrowlist += "([row.Join(", ")])" - - sqlrowlist = " [sqlrowlist.Join(",\n ")]" - var/DBQuery/Query = NewQuery("INSERT[delayed][ignore_errors] INTO [table]\n([columns.Join(", ")])\nVALUES\n[sqlrowlist]\n[duplicate_key]") - - return Query.Execute() - -/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/New(var/sql_query, var/DBConnection/connection_handler, var/cursor_handler) - if (sql_query) - sql = sql_query - if (connection_handler) - db_connection = connection_handler - if (cursor_handler) - default_cursor = cursor_handler - _db_query = _dm_db_new_query() - return ..() - -/DBQuery/proc/Connect(DBConnection/connection_handler) - db_connection = connection_handler - -/DBQuery/proc/Execute(var/list/argument_list = null, var/pass_not_found = 0, sql_query = sql, cursor_handler = default_cursor) - Close() - - if (argument_list) - sql_query = parseArguments(sql_query, argument_list, pass_not_found) - - var/result = _dm_db_execute(_db_query, sql_query, db_connection._db_con, cursor_handler, null) - - var/error = ErrorMsg() - if (error) - log_debug("SQL Error: '[error]'",SEVERITY_ERROR) - // This is hacky and should probably be changed - if (error == "MySQL server has gone away") - log_game("MySQL connection drop detected, attempting to reconnect.") - message_admins("MySQL connection drop detected, attempting to reconnect.") - db_connection.Reconnect() - - return result - -/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] = 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 - -/** -* Automatic query parsing. Works similarly to any SQL prepared statement system. -* -* You pass in here a query which has special argument markers (we use :marker: style), -* and a map of argument -> content. Note that in this map, the argument key should not -* contain the colon delimiters. So while it's :marker: in the query, in the args list, -* it would be list("marker" = somevar). -* -* The parser will then crawl the input query and replace any placeholders it finds -* with automatically sanitized values. Values can even be lists, at which point a -* MySQL list is generated: (VALUE1, VALUE2). -* -* @param query_to_parse The query we will be parsing. -* @param argument_list A map of markers associated with their values. Values can -* be numeric, strings, null, or lists of primitives. In case of null, MySQL NULL is -* used. In case of a list of primitives, a MySQL comma delimited list is used instead. -* -* @return The parsed query upon success. null upon failure. -*/ -/DBQuery/proc/parseArguments(var/query_to_parse = null, var/list/argument_list) - if (!query_to_parse || !argument_list || !argument_list.len) -#ifdef UNIT_TEST - error("SQL ARGPARSE: Invalid arguments sent.") -#else - log_debug("SQL ARGPARSE: Invalid arguments sent.") -#endif - return null - - var/parsed = "" - var/list/cache = list() - var/pos = 1 - var/search = 0 - var/curr_arg = "" - - // We crawl the key list first. This is required to properly notice missing - // arguments/keys. Otherwise, list[non-key] will always result in null and - // will thus be populated as NULL. Which is bad. - for (var/key in argument_list) - var/argument = argument_list[key] - if (istext(argument)) - cache[key] = "[db_connection.Quote(argument)]" - else if (isnum(argument)) - cache[key] = "[argument]" - else if (istype(argument, /list)) - cache[key] = parse_db_lists(argument) - else if (isnull(argument)) - cache[key] = "NULL" - else -#ifdef UNIT_TEST - error("SQL ARGPARSE: Cannot identify argument! [key]. Argument: [argument]") -#else - log_debug("SQL ARGPARSE: Cannot identify argument! [key]. Argument: [argument]") -#endif - return null - - while (1) - search = findtext(query_to_parse, ":", pos) - parsed += copytext(query_to_parse, pos, search) - if (search) - pos = search - search = findtext(query_to_parse, ":", pos + 1) - if (search) - curr_arg = copytext(query_to_parse, pos + 1, search) - if (cache[curr_arg]) - parsed += cache[curr_arg] - else -#ifdef UNIT_TEST - error("SQL ARGPARSE: Unpopulated argument found in an SQL query.") - error("SQL ARGPARSE: [curr_arg]. Query: [query_to_parse]") -#else - log_debug("SQL ARGPARSE: Unpopulated argument found in an SQL query.") - log_debug("SQL ARGPARSE: [curr_arg]. Query: [query_to_parse]") -#endif - return null - - pos = search + 1 - curr_arg = "" - continue - else - parsed += copytext(query_to_parse, pos, search) - - break - - return parsed - -/** - * Generates a MySQL list from a list of primitives. Also escapes values. - * - * @param argument The list of primitives we want to generate a MySQL list from. - * - * @return A string representing the MySQL list if the parsing is successful. - * "NULL", the MySQL null value, if parsing fails for some reason. - */ -/DBQuery/proc/parse_db_lists(var/list/argument) - if (!argument || !istype(argument) || !argument.len) - return "NULL" - - var/text = "" - var/count = argument.len - for (var/i = 1, i <= count, i++) - if (isnum(argument[i])) - text += "[argument[i]]" - else - text += db_connection.Quote(argument[i]) - - if (i != count) - text += ", " - - return "([text])" - -/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) - name = name_handler - table = table_handler - position = position_handler - sql_type = type_handler - flags = flag_handler - length = length_handler - max_length = max_length_handler - return ..() - - -/DBColumn/proc/SqlTypeName(type_handler = 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 + +/DBConnection + var/_db_con // This variable contains a reference to the actual database connection. + var/con_dbi // This variable is a string containing the DBI MySQL requires. + var/con_user // This variable contains the username data. + var/con_password // This variable contains the password data. + var/con_cursor // This contains the default database cursor data. + var/con_server = "" + var/con_port = 3306 + var/con_database = "" + var/failed_connections = 0 + +/DBConnection/New(server, port = 3306, database, username, password_handler, cursor_handler = Default_Cursor, dbi_handler) + con_user = username + con_password = password_handler + con_cursor = cursor_handler + con_server = server + con_port = port + con_database = database + + if (dbi_handler) + con_dbi = dbi_handler + else + con_dbi = "dbi:mysql:[database]:[server]:[port]" + + _db_con = _dm_db_new_con() + +/DBConnection/proc/Connect(dbi_handler = con_dbi, user_handler = con_user, password_handler = con_password, cursor_handler) + if (!config.sql_enabled) + return 0 + if (!src) + return 0 + cursor_handler = con_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/Reconnect() + Disconnect() + Connect() + +/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, new_dbi) + if (IsConnected()) + Disconnect() + con_database = database_name + return Connect(new_dbi ? new_dbi : "dbi:mysql:[database_name]:[con_server]:[con_port]", con_user, con_password) + +/DBConnection/proc/NewQuery(sql_query, cursor_handler = con_cursor) + return new/DBQuery(sql_query, src, cursor_handler) + +/* +Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query. +Rows missing columns present in other rows will resolve to SQL NULL +You are expected to do your own escaping of the data, and expected to provide your own quotes for strings. +The duplicate_key arg can be true to automatically generate this part of the query + or set to a string that is appended to the end of the query +Ignore_errors instructes mysql to continue inserting rows if some of them have errors. + the erroneous row(s) aren't inserted and there isn't really any way to know why or why errored +Delayed insert mode was removed in mysql 7 and only works with MyISAM type tables, + It was included because it is still supported in mariadb. + It does not work with duplicate_key and the mysql server ignores it in those cases +*/ +/DBConnection/proc/MassInsert(table, list/rows, duplicate_key = FALSE, ignore_errors = FALSE, delayed = FALSE) + if (!table || !rows || !istype(rows)) + return + var/list/columns = list() + var/list/sorted_rows = list() + + for (var/list/row in rows) + var/list/sorted_row = list() + sorted_row.len = columns.len + for (var/column in row) + var/idx = columns[column] + if (!idx) + idx = columns.len + 1 + columns[column] = idx + sorted_row.len = columns.len + + sorted_row[idx] = row[column] + sorted_rows[++sorted_rows.len] = sorted_row + + if (duplicate_key == TRUE) + var/list/column_list = list() + for (var/column in columns) + column_list += "[column] = VALUES([column])" + duplicate_key = "ON DUPLICATE KEY UPDATE [column_list.Join(", ")]\n" + else if (duplicate_key == FALSE) + duplicate_key = null + + if (ignore_errors) + ignore_errors = " IGNORE" + else + ignore_errors = null + + if (delayed) + delayed = " DELAYED" + else + delayed = null + + var/list/sqlrowlist = list() + var/len = columns.len + for (var/list/row in sorted_rows) + if (length(row) != len) + row.len = len + for (var/value in row) + if (value == null) + value = "NULL" + sqlrowlist += "([row.Join(", ")])" + + sqlrowlist = " [sqlrowlist.Join(",\n ")]" + var/DBQuery/Query = NewQuery("INSERT[delayed][ignore_errors] INTO [table]\n([columns.Join(", ")])\nVALUES\n[sqlrowlist]\n[duplicate_key]") + + return Query.Execute() + +/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/New(var/sql_query, var/DBConnection/connection_handler, var/cursor_handler) + if (sql_query) + sql = sql_query + if (connection_handler) + db_connection = connection_handler + if (cursor_handler) + default_cursor = cursor_handler + _db_query = _dm_db_new_query() + return ..() + +/DBQuery/proc/Connect(DBConnection/connection_handler) + db_connection = connection_handler + +/DBQuery/proc/Execute(var/list/argument_list = null, var/pass_not_found = 0, sql_query = sql, cursor_handler = default_cursor) + Close() + + if (argument_list) + sql_query = parseArguments(sql_query, argument_list, pass_not_found) + + var/result = _dm_db_execute(_db_query, sql_query, db_connection._db_con, cursor_handler, null) + + var/error = ErrorMsg() + if (error) + log_debug("SQL Error: '[error]'",SEVERITY_ERROR) + // This is hacky and should probably be changed + if (error == "MySQL server has gone away") + log_game("MySQL connection drop detected, attempting to reconnect.") + message_admins("MySQL connection drop detected, attempting to reconnect.") + db_connection.Reconnect() + + return result + +/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] = 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 + +/** +* Automatic query parsing. Works similarly to any SQL prepared statement system. +* +* You pass in here a query which has special argument markers (we use :marker: style), +* and a map of argument -> content. Note that in this map, the argument key should not +* contain the colon delimiters. So while it's :marker: in the query, in the args list, +* it would be list("marker" = somevar). +* +* The parser will then crawl the input query and replace any placeholders it finds +* with automatically sanitized values. Values can even be lists, at which point a +* MySQL list is generated: (VALUE1, VALUE2). +* +* @param query_to_parse The query we will be parsing. +* @param argument_list A map of markers associated with their values. Values can +* be numeric, strings, null, or lists of primitives. In case of null, MySQL NULL is +* used. In case of a list of primitives, a MySQL comma delimited list is used instead. +* +* @return The parsed query upon success. null upon failure. +*/ +/DBQuery/proc/parseArguments(var/query_to_parse = null, var/list/argument_list) + if (!query_to_parse || !argument_list || !argument_list.len) +#ifdef UNIT_TEST + error("SQL ARGPARSE: Invalid arguments sent.") +#else + log_debug("SQL ARGPARSE: Invalid arguments sent.") +#endif + return null + + var/parsed = "" + var/list/cache = list() + var/pos = 1 + var/search = 0 + var/curr_arg = "" + + // We crawl the key list first. This is required to properly notice missing + // arguments/keys. Otherwise, list[non-key] will always result in null and + // will thus be populated as NULL. Which is bad. + for (var/key in argument_list) + var/argument = argument_list[key] + if (istext(argument)) + cache[key] = "[db_connection.Quote(argument)]" + else if (isnum(argument)) + cache[key] = "[argument]" + else if (istype(argument, /list)) + cache[key] = parse_db_lists(argument) + else if (isnull(argument)) + cache[key] = "NULL" + else +#ifdef UNIT_TEST + error("SQL ARGPARSE: Cannot identify argument! [key]. Argument: [argument]") +#else + log_debug("SQL ARGPARSE: Cannot identify argument! [key]. Argument: [argument]") +#endif + return null + + while (1) + search = findtext(query_to_parse, ":", pos) + parsed += copytext(query_to_parse, pos, search) + if (search) + pos = search + search = findtext(query_to_parse, ":", pos + 1) + if (search) + curr_arg = copytext(query_to_parse, pos + 1, search) + if (cache[curr_arg]) + parsed += cache[curr_arg] + else +#ifdef UNIT_TEST + error("SQL ARGPARSE: Unpopulated argument found in an SQL query.") + error("SQL ARGPARSE: [curr_arg]. Query: [query_to_parse]") +#else + log_debug("SQL ARGPARSE: Unpopulated argument found in an SQL query.") + log_debug("SQL ARGPARSE: [curr_arg]. Query: [query_to_parse]") +#endif + return null + + pos = search + 1 + curr_arg = "" + continue + else + parsed += copytext(query_to_parse, pos, search) + + break + + return parsed + +/** + * Generates a MySQL list from a list of primitives. Also escapes values. + * + * @param argument The list of primitives we want to generate a MySQL list from. + * + * @return A string representing the MySQL list if the parsing is successful. + * "NULL", the MySQL null value, if parsing fails for some reason. + */ +/DBQuery/proc/parse_db_lists(var/list/argument) + if (!argument || !istype(argument) || !argument.len) + return "NULL" + + var/text = "" + var/count = argument.len + for (var/i = 1, i <= count, i++) + if (isnum(argument[i])) + text += "[argument[i]]" + else + text += db_connection.Quote(argument[i]) + + if (i != count) + text += ", " + + return "([text])" + +/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) + name = name_handler + table = table_handler + position = position_handler + sql_type = type_handler + flags = flag_handler + length = length_handler + max_length = max_length_handler + return ..() + + +/DBColumn/proc/SqlTypeName(type_handler = 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/sd_Alert.dm b/code/defines/procs/sd_Alert.dm index 67f9600e66e..2467909d7be 100644 --- a/code/defines/procs/sd_Alert.dm +++ b/code/defines/procs/sd_Alert.dm @@ -1,168 +1,168 @@ -/* sd_Alert library - by Shadowdarke (shadowdarke@byond.com) - - sd_Alert() is a powerful and flexible alternative to the built in BYOND - alert() proc. sd_Alert offers timed popups, unlimited buttons, custom - appearance, and even the option to popup without stealing keyboard focus - from the map or command line. - - Please see demo.dm for detailed examples. - -FORMAT - sd_Alert(who, message, title, buttons, default, duration, unfocus, \ - size, table, style, tag, select, flags) - -ARGUMENTS - who - the client or mob to display the alert to. - message - text message to display - title - title of the alert box - buttons - list of buttons - Default Value: list("Ok") - default - default button selestion - Default Value: the first button in the list - duration - the number of ticks before this alert expires. If not - set, the alert lasts until a button is clicked. - Default Value: 0 (unlimited) - unfocus - if this value is set, the popup will not steal keyboard - focus from the map or command line. - Default Value: 1 (do not take focus) - size - size of the popup window in px - Default Value: "300x200" - table - optional parameters for the HTML table in the alert - Default Value: "width=100% height=100%" (fill the window) - style - optional style sheet information - tag - lets you specify a certain tag for this sd_Alert so you may manipulate it - externally. (i.e. force the alert to close, change options and redisplay, - reuse the same window, etc.) - select - if set, the buttons will be replaced with a selection box with a number of - lines displayed equal to this value. - Default value: 0 (use buttons) - flags - optional flags effecting the alert display. These flags may be ORed (|) - together for multiple effects. - SD_ALERT_SCROLL = display a scrollbar - SD_ALERT_SELECT_MULTI = forces selection box display (instead of - buttons) allows the user to select multiple - choices. - SD_ALERT_LINKS = display each choice as a plain text link. - Any selection box style overrides this flag. - SD_ALERT_NOVALIDATE = don't validate responses - Default value: SD_ALERT_SCROLL - (button display with scroll bar, validate responses) -RETURNS - The text of the selected button, or null if the alert duration expired - without a button click. - -Version 1 changes (from version 0): -* Added the tag, select, and flags arguments, thanks to several suggestions from Foomer. -* Split the sd_Alert/Alert() proc into New(), Display(), and Response() to allow more - customization by developers. Primarily developers would want to use Display() to change - the display of active tagged windows - -*/ - - -#define SD_ALERT_SCROLL 1 -#define SD_ALERT_SELECT_MULTI 2 -#define SD_ALERT_LINKS 4 -#define SD_ALERT_NOVALIDATE 8 - -proc/sd_Alert(client/who, message, title, buttons = list("Ok"),\ - default, duration = 0, unfocus = 1, size = "300x200", \ - table = "width=100% height=100%", style, tag, select, flags = SD_ALERT_SCROLL) - - if(ismob(who)) - var/mob/M = who - who = M.client - if(!istype(who)) CRASH("sd_Alert: Invalid target:[who] (\ref[who])") - - var/sd_alert/T = locate(tag) - if(T) - if(istype(T)) qdel(T) - else CRASH("sd_Alert: tag \"[tag]\" is already in use by datum '[T]' (type: [T.type])") - T = new(who, tag) - if(duration) - spawn(duration) - if(T) qdel(T) - return - T.Display(message,title,buttons,default,unfocus,size,table,style,select,flags) - . = T.Response() - -sd_alert - var - client/target - response - list/validation - - Destroy() - target << browse(null,"window=\ref[src]") - return ..() - - New(who, tag) - ..() - target = who - src.tag = tag - - Topic(href,params[]) - if(usr.client != target) return - response = params["clk"] - - proc/Display(message,title,list/buttons,default,unfocus,size,table,style,select,flags) - if(unfocus) spawn() target << browse(null,null) - if(istext(buttons)) buttons = list(buttons) - if(!default) default = buttons[1] - if(!(flags & SD_ALERT_NOVALIDATE)) validation = buttons.Copy() - - var/html = {"[title][style]\ -
    [message]
    "} - - if(select || (flags & SD_ALERT_SELECT_MULTI)) // select style choices - html += {"
    \ - -
    " - else if(flags & SD_ALERT_LINKS) // text link style - for(var/b in buttons) - var/list/L = list() - L["clk"] = b - var/html_string=list2params(L) - var/focus - if(b == default) focus = " ID=fcs" - html += "[html_encode(b)]\ -
    " - else // button style choices - for(var/b in buttons) - var/list/L = list() - L["clk"] = b - var/html_string=list2params(L) - var/focus - if(b == default) focus = " ID=fcs" - html += " " - - html += "
    " - - target << browse(html,"window=\ref[src];size=[size];can_close=0") - - proc/Response() - var/validated - while(!validated) - while(target && !response) // wait for a response - sleep(2) - - if(response && validation) - if(istype(response, /list)) - var/list/L = response - validation - if(L.len) response = null - else validated = 1 - else if(response in validation) validated = 1 - else response=null - else validated = 1 - spawn(2) qdel(src) - return response +/* sd_Alert library + by Shadowdarke (shadowdarke@byond.com) + + sd_Alert() is a powerful and flexible alternative to the built in BYOND + alert() proc. sd_Alert offers timed popups, unlimited buttons, custom + appearance, and even the option to popup without stealing keyboard focus + from the map or command line. + + Please see demo.dm for detailed examples. + +FORMAT + sd_Alert(who, message, title, buttons, default, duration, unfocus, \ + size, table, style, tag, select, flags) + +ARGUMENTS + who - the client or mob to display the alert to. + message - text message to display + title - title of the alert box + buttons - list of buttons + Default Value: list("Ok") + default - default button selestion + Default Value: the first button in the list + duration - the number of ticks before this alert expires. If not + set, the alert lasts until a button is clicked. + Default Value: 0 (unlimited) + unfocus - if this value is set, the popup will not steal keyboard + focus from the map or command line. + Default Value: 1 (do not take focus) + size - size of the popup window in px + Default Value: "300x200" + table - optional parameters for the HTML table in the alert + Default Value: "width=100% height=100%" (fill the window) + style - optional style sheet information + tag - lets you specify a certain tag for this sd_Alert so you may manipulate it + externally. (i.e. force the alert to close, change options and redisplay, + reuse the same window, etc.) + select - if set, the buttons will be replaced with a selection box with a number of + lines displayed equal to this value. + Default value: 0 (use buttons) + flags - optional flags effecting the alert display. These flags may be ORed (|) + together for multiple effects. + SD_ALERT_SCROLL = display a scrollbar + SD_ALERT_SELECT_MULTI = forces selection box display (instead of + buttons) allows the user to select multiple + choices. + SD_ALERT_LINKS = display each choice as a plain text link. + Any selection box style overrides this flag. + SD_ALERT_NOVALIDATE = don't validate responses + Default value: SD_ALERT_SCROLL + (button display with scroll bar, validate responses) +RETURNS + The text of the selected button, or null if the alert duration expired + without a button click. + +Version 1 changes (from version 0): +* Added the tag, select, and flags arguments, thanks to several suggestions from Foomer. +* Split the sd_Alert/Alert() proc into New(), Display(), and Response() to allow more + customization by developers. Primarily developers would want to use Display() to change + the display of active tagged windows + +*/ + + +#define SD_ALERT_SCROLL 1 +#define SD_ALERT_SELECT_MULTI 2 +#define SD_ALERT_LINKS 4 +#define SD_ALERT_NOVALIDATE 8 + +proc/sd_Alert(client/who, message, title, buttons = list("Ok"),\ + default, duration = 0, unfocus = 1, size = "300x200", \ + table = "width=100% height=100%", style, tag, select, flags = SD_ALERT_SCROLL) + + if(ismob(who)) + var/mob/M = who + who = M.client + if(!istype(who)) CRASH("sd_Alert: Invalid target:[who] (\ref[who])") + + var/sd_alert/T = locate(tag) + if(T) + if(istype(T)) qdel(T) + else CRASH("sd_Alert: tag \"[tag]\" is already in use by datum '[T]' (type: [T.type])") + T = new(who, tag) + if(duration) + spawn(duration) + if(T) qdel(T) + return + T.Display(message,title,buttons,default,unfocus,size,table,style,select,flags) + . = T.Response() + +sd_alert + var + client/target + response + list/validation + + Destroy() + target << browse(null,"window=\ref[src]") + return ..() + + New(who, tag) + ..() + target = who + src.tag = tag + + Topic(href,params[]) + if(usr.client != target) return + response = params["clk"] + + proc/Display(message,title,list/buttons,default,unfocus,size,table,style,select,flags) + if(unfocus) spawn() target << browse(null,null) + if(istext(buttons)) buttons = list(buttons) + if(!default) default = buttons[1] + if(!(flags & SD_ALERT_NOVALIDATE)) validation = buttons.Copy() + + var/html = {"[title][style]\ +
    [message]
    "} + + if(select || (flags & SD_ALERT_SELECT_MULTI)) // select style choices + html += {"
    \ + +
    " + else if(flags & SD_ALERT_LINKS) // text link style + for(var/b in buttons) + var/list/L = list() + L["clk"] = b + var/html_string=list2params(L) + var/focus + if(b == default) focus = " ID=fcs" + html += "[html_encode(b)]\ +
    " + else // button style choices + for(var/b in buttons) + var/list/L = list() + L["clk"] = b + var/html_string=list2params(L) + var/focus + if(b == default) focus = " ID=fcs" + html += " " + + html += "
    " + + target << browse(html,"window=\ref[src];size=[size];can_close=0") + + proc/Response() + var/validated + while(!validated) + while(target && !response) // wait for a response + sleep(2) + + if(response && validation) + if(istype(response, /list)) + var/list/L = response - validation + if(L.len) response = null + else validated = 1 + else if(response in validation) validated = 1 + else response=null + else validated = 1 + spawn(2) qdel(src) + return response diff --git a/code/game/antagonist/antagonist.dm b/code/game/antagonist/antagonist.dm index ca05aea9d76..25de46476da 100644 --- a/code/game/antagonist/antagonist.dm +++ b/code/game/antagonist/antagonist.dm @@ -112,7 +112,7 @@ log_debug("[key_name(player)] is not eligible to become a [role_text]: They are blacklisted for this role!") else if(player_is_antag(player)) log_debug("[key_name(player)] is not eligible to become a [role_text]: They are already an antagonist!") - else if(required_age && required_age > player.current.client?.player_age) + else if(establish_db_connection(dbcon) && required_age && required_age > player.current.client?.player_age) log_debug("[key_name(player)] is not eligible to become a [role_text]: Their playtime age is too low!") else candidates += player diff --git a/code/game/antagonist/antagonist_helpers.dm b/code/game/antagonist/antagonist_helpers.dm index 13fdc257277..b876de0d738 100644 --- a/code/game/antagonist/antagonist_helpers.dm +++ b/code/game/antagonist/antagonist_helpers.dm @@ -3,6 +3,8 @@ return 0 if(jobban_isbanned(player.current, bantype)) return 0 + if(required_age && required_age > player.current.client.player_age) + return FALSE if(!ignore_role) if(player.assigned_role in restricted_jobs) return 0 diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm index c8458388d7c..1d7d0f23a6f 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 - station_area = 1 - -/area/ai_monitored/LateInitialize() - // locate and store the motioncamera - for (var/obj/machinery/camera/M in src) - if(M.isMotion()) - motioncamera = M - M.area_motion = src - return - -/area/ai_monitored/Initialize(mapload) - . = ..() - if (mapload) - return INITIALIZE_HINT_LATELOAD - -/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 + station_area = 1 + +/area/ai_monitored/LateInitialize() + // locate and store the motioncamera + for (var/obj/machinery/camera/M in src) + if(M.isMotion()) + motioncamera = M + M.area_motion = src + return + +/area/ai_monitored/Initialize(mapload) + . = ..() + if (mapload) + return INITIALIZE_HINT_LATELOAD + +/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 b1bf4634c20..cdfb4ac2dd6 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -1,462 +1,462 @@ -// Areas.dm - - - -// === -/area - var/global/global_uid = 0 - var/uid - var/holomap_color // Color of this area on the holomap. Must be a hex color (as string) or null. - var/fire = null - var/atmosalm = 0 - var/poweralm = 1 - var/party = null - level = null - name = "Unknown" - icon = 'icons/turf/areas.dmi' - icon_state = "unknown" - layer = 10 - luminosity = 0 - mouse_opacity = 0 - var/lightswitch = 1 - - var/eject = null - - var/requires_power = 1 - var/always_unpowered = 0 //this gets overriden to 1 for space in area/New() - - var/power_equip = 1 - var/power_light = 1 - var/power_environ = 1 - var/used_equip = 0 - var/used_light = 0 - var/used_environ = 0 - var/alwaysgravity = 0 - var/nevergravity = 0 - - var/has_gravity = 1 - var/obj/machinery/power/apc/apc = null -// var/list/lights // list of all lights on this area - var/list/all_doors = list() //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area - var/air_doors_activated = 0 - var/list/ambience = list( - 'sound/ambience/ambigen1.ogg', - 'sound/ambience/ambigen3.ogg', - 'sound/ambience/ambigen4.ogg', - 'sound/ambience/ambigen5.ogg', - 'sound/ambience/ambigen6.ogg', - 'sound/ambience/ambigen7.ogg', - 'sound/ambience/ambigen8.ogg', - 'sound/ambience/ambigen9.ogg', - 'sound/ambience/ambigen10.ogg', - 'sound/ambience/ambigen11.ogg', - 'sound/ambience/ambigen12.ogg', - 'sound/ambience/ambigen14.ogg' - ) - var/list/forced_ambience = null - var/loop_ambience = TRUE - 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/no_light_control = 0 // if 1, lights in area cannot be toggled with light controller - var/allow_nightmode = 0 // if 1, lights in area will be darkened by the night mode controller - var/station_area = 0 - var/centcomm_area = 0 - var/has_weird_power = FALSE // If TRUE, SSmachinery will not use the inlined power checks and will call powered() and use_power() on this area. - -// Don't move this to Initialize(). Things in here need to run before SSatoms does. -/area/New() - // DMMS hook - Required for areas to work properly. - if (!areas_by_type[type]) - areas_by_type[type] = src - // Atmos code needs this, so we need to make sure this is done by the time they init. - uid = ++global_uid - . = ..() - -/area/Initialize(mapload) - icon_state = "white" - layer = 10 - - blend_mode = BLEND_MULTIPLY - - if(!requires_power) - power_light = 0 - power_equip = 0 - power_environ = 0 - - if(dynamic_lighting) - luminosity = 0 - else - luminosity = 1 - - if(centcomm_area) - centcom_areas[src] = TRUE - alwaysgravity = 1 - - if(station_area) - the_station_areas[src] = TRUE - - if(!requires_power || !apc) - power_light = 0 - power_equip = 0 - power_environ = 0 - - if (!mapload) - power_change() // all machines set to current power level - - . = ..() - -/area/proc/get_cameras() - . = list() - for (var/thing in SSmachinery.all_cameras) - var/obj/machinery/camera/C = thing - if (!isturf(C.loc)) - continue - - if (C.loc.loc == src) //what the fuck is this - . += C - -/area/proc/atmosalert(danger_level, var/alarm_source) - if (danger_level == 0) - atmosphere_alarm.clearAlarm(src, alarm_source) - 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) - if (danger_level < 1 && atmosalm >= 1) - //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 - air_doors_open() - else if (danger_level >= 2 && atmosalm < 2) - air_doors_close() - - atmosalm = danger_level - for (var/obj/machinery/alarm/AA in src) - AA.update_icon() - - return 1 - return 0 - -/area/proc/air_doors_close() - if(!air_doors_activated) - air_doors_activated = 1 - for(var/obj/machinery/door/firedoor/E in all_doors) - if(!E.blocked) - if(E.operating) - E.nextstate = FIREDOOR_CLOSED - else if(!E.density) - INVOKE_ASYNC(E, /obj/machinery/door/.proc/close) - -/area/proc/air_doors_open() - if(air_doors_activated) - air_doors_activated = 0 - for(var/obj/machinery/door/firedoor/E in all_doors) - if(!E.blocked) - if(E.operating) - E.nextstate = FIREDOOR_OPEN - else if(E.density) - INVOKE_ASYNC(E, /obj/machinery/door/.proc/open) - -/area/proc/fire_alert() - if(!fire) - fire = 1 //used for firedoor checks - update_icon() - mouse_opacity = 0 - for(var/obj/machinery/door/firedoor/D in all_doors) - if(!D.blocked) - if(D.operating) - D.nextstate = FIREDOOR_CLOSED - else if(!D.density) - INVOKE_ASYNC(D, /obj/machinery/door/.proc/close) - -/area/proc/fire_reset() - if (fire) - fire = 0 //used for firedoor checks - update_icon() - mouse_opacity = 0 - for(var/obj/machinery/door/firedoor/D in all_doors) - if(!D.blocked) - if(D.operating) - D.nextstate = FIREDOOR_OPEN - else if(D.density) - INVOKE_ASYNC(D, /obj/machinery/door/.proc/open) - -/area/proc/readyalert() - if(!eject) - eject = 1 - update_icon() - -/area/proc/readyreset() - if(eject) - eject = 0 - update_icon() - -/area/proc/partyalert() - if (!party) - party = 1 - update_icon() - mouse_opacity = 0 - -/area/proc/partyreset() - if (party) - party = 0 - mouse_opacity = 0 - update_icon() - for(var/obj/machinery/door/firedoor/D in all_doors) - if(!D.blocked) - if(D.operating) - D.nextstate = FIREDOOR_OPEN - else if(D.density) - INVOKE_ASYNC(D, /obj/machinery/door/.proc/open) - -#define DO_PARTY(COLOR) animate(color = COLOR, time = 0.5 SECONDS, easing = QUAD_EASING) - -/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) // FIRE - color = "#ff9292" - animate(src) // stop any current animations. - animate(src, color = "#ffa5b2", time = 1 SECOND, loop = -1, easing = SINE_EASING) - animate(color = "#ff9292", time = 1 SECOND, easing = SINE_EASING) - else if(!fire && eject && !party) // EJECT - color = "#ff9292" - animate(src) - animate(src, color = "#bc8a81", time = 1 SECOND, loop = -1, easing = EASE_IN|CUBIC_EASING) - animate(color = "#ff9292", time = 0.5 SECOND, easing = EASE_OUT|CUBIC_EASING) - else if(party && !fire && !eject) // PARTY - color = "#ff728e" - animate(src) - animate(src, color = "#7272ff", time = 0.5 SECONDS, loop = -1, easing = QUAD_EASING) - DO_PARTY("#72aaff") - DO_PARTY("#ffc68e") - DO_PARTY("#72c6ff") - DO_PARTY("#ff72e2") - DO_PARTY("#72ff8e") - DO_PARTY("#ffff8e") - DO_PARTY("#ff728e") - else - color = "#ffb2b2" - animate(src) - animate(src, color = "#B3DFFF", time = 0.5 SECOND, loop = -1, easing = SINE_EASING) - animate(color = "#ffb2b2", time = 0.5 SECOND, loop = -1, easing = SINE_EASING) - else - animate(src, color = "#FFFFFF", time = 0.5 SECONDS, easing = QUAD_EASING) // Stop the animation. - -#undef DO_PARTY - - -/* -#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/used = 0 - switch(chan) - if(LIGHT) - used += used_light - if(EQUIP) - used += used_equip - if(ENVIRON) - used += used_environ - if(TOTAL) - used += used_light + used_equip + used_environ - return used - -/area/proc/clear_usage() - used_equip = 0 - used_light = 0 - used_environ = 0 - -/area/proc/use_power(var/amount, var/chan) - switch(chan) - if(EQUIP) - used_equip += amount - if(LIGHT) - used_light += amount - if(ENVIRON) - used_environ += amount - - -var/list/mob/living/forced_ambiance_list = new - -/area/Entered(mob/living/L) - if(!istype(L,/mob/living) || !ROUND_IS_STARTED) - return - - if(!L.ckey) return - - if(!L.lastarea) - L.lastarea = get_area(L.loc) - var/area/newarea = get_area(L.loc) - var/area/oldarea = L.lastarea - if((oldarea.has_gravity() == 0) && (newarea.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 = newarea - - // 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.client && (L.client.prefs.asfx_togs & ASFX_AMBIENCE))) return - - play_ambience(L) - -/area/proc/play_ambience(var/mob/living/L) - // 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.client && (L.client.prefs.toggles & SOUND_AMBIENCE))) return - - // If we previously were in an area with force-played ambiance, stop it. - if(L in forced_ambiance_list) - L << sound(null, channel = 1) - forced_ambiance_list -= L - - if(!L.client.ambience_playing) - L.client.ambience_playing = 1 - L << sound('sound/ambience/shipambience.ogg', repeat = loop_ambience, wait = 0, volume = 35, channel = 2) - - if(forced_ambience) - if(forced_ambience.len) - forced_ambiance_list |= L - L << sound(pick(forced_ambience), repeat = loop_ambience, wait = 0, volume = 25, channel = 1) - else - L << sound(null, channel = 1) - else if(src.ambience.len && prob(35)) - if((world.time >= L.client.played + 600)) - var/sound = pick(ambience) - L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = 1) - L.client.played = world.time - -/area/proc/gravitychange(var/gravitystate = 0) - has_gravity = gravitystate - - for(var/mob/M in src) - if(has_gravity()) - thunk(M) - else - to_chat(M, span("notice", "The sudden lack of gravity makes you feel weightless and float cluelessly!")) - M.update_floating( M.Check_Dense_Object() ) - -/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.Check_Shoegrip(FALSE)) - return - - if(H.m_intent == "run") - H.AdjustStunned(2) - H.AdjustWeakened(2) - else - H.AdjustStunned(1) - H.AdjustWeakened(1) - to_chat(mob, "The sudden appearance of gravity makes you fall to the floor!") - -/area/proc/prison_break() - for(var/obj/machinery/power/apc/temp_apc in src) - temp_apc.overload_lighting(70) - 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() - -/area/proc/has_gravity() - if(alwaysgravity) - return TRUE - if(nevergravity) - return FALSE - 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 - -//A useful proc for events. -//This returns a random area of the station which is meaningful. Ie, a room somewhere -/proc/random_station_area(var/filter_players = FALSE) - var/list/possible = list() - for(var/Y in the_station_areas) - if(!Y) - continue - var/area/A = Y - if (isNotStationLevel(A.z)) - continue - if (istype(A, /area/shuttle) || findtext(A.name, "Docked") || findtext(A.name, "Shuttle")) - continue - if (istype(A, /area/solar) || findtext(A.name, "solar")) - continue - if (istype(A, /area/constructionsite) || istype(A, /area/maintenance/interstitial_construction_site)) - continue - if (istype(A, /area/rnd/xenobiology)) - continue - if (istype(A, /area/maintenance/substation)) - continue - if (istype(A, /area/turbolift)) - continue - if (istype(A, /area/mine)) - continue - - //Although hostile mobs instadying to turrets is fun - //If there's no AI they'll just be hit with stunbeams all day and spam the attack logs. - if (istype(A, /area/turret_protected) || LAZYLEN(A.turret_controls)) - continue - - if(filter_players) - var/should_continue = FALSE - for(var/mob/living/carbon/human/H in human_mob_list) - if(!H.client) - continue - if(A == get_area(H)) - should_continue = TRUE - break - - if(should_continue) - continue - - possible += A - - return pick(possible) - -/area/proc/random_space() - var/list/turfs = list() - for(var/turf/simulated/floor/F in src.contents) - if(turf_clear(F)) - turfs += F - if (turfs.len) - return pick(turfs) - else return null +// Areas.dm + + + +// === +/area + var/global/global_uid = 0 + var/uid + var/holomap_color // Color of this area on the holomap. Must be a hex color (as string) or null. + var/fire = null + var/atmosalm = 0 + var/poweralm = 1 + var/party = null + level = null + name = "Unknown" + icon = 'icons/turf/areas.dmi' + icon_state = "unknown" + layer = 10 + luminosity = 0 + mouse_opacity = 0 + var/lightswitch = 1 + + var/eject = null + + var/requires_power = 1 + var/always_unpowered = 0 //this gets overriden to 1 for space in area/New() + + var/power_equip = 1 + var/power_light = 1 + var/power_environ = 1 + var/used_equip = 0 + var/used_light = 0 + var/used_environ = 0 + var/alwaysgravity = 0 + var/nevergravity = 0 + + var/has_gravity = 1 + var/obj/machinery/power/apc/apc = null +// var/list/lights // list of all lights on this area + var/list/all_doors = list() //Added by Strumpetplaya - Alarm Change - Contains a list of doors adjacent to this area + var/air_doors_activated = 0 + var/list/ambience = list( + 'sound/ambience/ambigen1.ogg', + 'sound/ambience/ambigen3.ogg', + 'sound/ambience/ambigen4.ogg', + 'sound/ambience/ambigen5.ogg', + 'sound/ambience/ambigen6.ogg', + 'sound/ambience/ambigen7.ogg', + 'sound/ambience/ambigen8.ogg', + 'sound/ambience/ambigen9.ogg', + 'sound/ambience/ambigen10.ogg', + 'sound/ambience/ambigen11.ogg', + 'sound/ambience/ambigen12.ogg', + 'sound/ambience/ambigen14.ogg' + ) + var/list/forced_ambience = null + var/loop_ambience = TRUE + 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/no_light_control = 0 // if 1, lights in area cannot be toggled with light controller + var/allow_nightmode = 0 // if 1, lights in area will be darkened by the night mode controller + var/station_area = 0 + var/centcomm_area = 0 + var/has_weird_power = FALSE // If TRUE, SSmachinery will not use the inlined power checks and will call powered() and use_power() on this area. + +// Don't move this to Initialize(). Things in here need to run before SSatoms does. +/area/New() + // DMMS hook - Required for areas to work properly. + if (!areas_by_type[type]) + areas_by_type[type] = src + // Atmos code needs this, so we need to make sure this is done by the time they init. + uid = ++global_uid + . = ..() + +/area/Initialize(mapload) + icon_state = "white" + layer = 10 + + blend_mode = BLEND_MULTIPLY + + if(!requires_power) + power_light = 0 + power_equip = 0 + power_environ = 0 + + if(dynamic_lighting) + luminosity = 0 + else + luminosity = 1 + + if(centcomm_area) + centcom_areas[src] = TRUE + alwaysgravity = 1 + + if(station_area) + the_station_areas[src] = TRUE + + if(!requires_power || !apc) + power_light = 0 + power_equip = 0 + power_environ = 0 + + if (!mapload) + power_change() // all machines set to current power level + + . = ..() + +/area/proc/get_cameras() + . = list() + for (var/thing in SSmachinery.all_cameras) + var/obj/machinery/camera/C = thing + if (!isturf(C.loc)) + continue + + if (C.loc.loc == src) //what the fuck is this + . += C + +/area/proc/atmosalert(danger_level, var/alarm_source) + if (danger_level == 0) + atmosphere_alarm.clearAlarm(src, alarm_source) + 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) + if (danger_level < 1 && atmosalm >= 1) + //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 + air_doors_open() + else if (danger_level >= 2 && atmosalm < 2) + air_doors_close() + + atmosalm = danger_level + for (var/obj/machinery/alarm/AA in src) + AA.update_icon() + + return 1 + return 0 + +/area/proc/air_doors_close() + if(!air_doors_activated) + air_doors_activated = 1 + for(var/obj/machinery/door/firedoor/E in all_doors) + if(!E.blocked) + if(E.operating) + E.nextstate = FIREDOOR_CLOSED + else if(!E.density) + INVOKE_ASYNC(E, /obj/machinery/door/.proc/close) + +/area/proc/air_doors_open() + if(air_doors_activated) + air_doors_activated = 0 + for(var/obj/machinery/door/firedoor/E in all_doors) + if(!E.blocked) + if(E.operating) + E.nextstate = FIREDOOR_OPEN + else if(E.density) + INVOKE_ASYNC(E, /obj/machinery/door/.proc/open) + +/area/proc/fire_alert() + if(!fire) + fire = 1 //used for firedoor checks + update_icon() + mouse_opacity = 0 + for(var/obj/machinery/door/firedoor/D in all_doors) + if(!D.blocked) + if(D.operating) + D.nextstate = FIREDOOR_CLOSED + else if(!D.density) + INVOKE_ASYNC(D, /obj/machinery/door/.proc/close) + +/area/proc/fire_reset() + if (fire) + fire = 0 //used for firedoor checks + update_icon() + mouse_opacity = 0 + for(var/obj/machinery/door/firedoor/D in all_doors) + if(!D.blocked) + if(D.operating) + D.nextstate = FIREDOOR_OPEN + else if(D.density) + INVOKE_ASYNC(D, /obj/machinery/door/.proc/open) + +/area/proc/readyalert() + if(!eject) + eject = 1 + update_icon() + +/area/proc/readyreset() + if(eject) + eject = 0 + update_icon() + +/area/proc/partyalert() + if (!party) + party = 1 + update_icon() + mouse_opacity = 0 + +/area/proc/partyreset() + if (party) + party = 0 + mouse_opacity = 0 + update_icon() + for(var/obj/machinery/door/firedoor/D in all_doors) + if(!D.blocked) + if(D.operating) + D.nextstate = FIREDOOR_OPEN + else if(D.density) + INVOKE_ASYNC(D, /obj/machinery/door/.proc/open) + +#define DO_PARTY(COLOR) animate(color = COLOR, time = 0.5 SECONDS, easing = QUAD_EASING) + +/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) // FIRE + color = "#ff9292" + animate(src) // stop any current animations. + animate(src, color = "#ffa5b2", time = 1 SECOND, loop = -1, easing = SINE_EASING) + animate(color = "#ff9292", time = 1 SECOND, easing = SINE_EASING) + else if(!fire && eject && !party) // EJECT + color = "#ff9292" + animate(src) + animate(src, color = "#bc8a81", time = 1 SECOND, loop = -1, easing = EASE_IN|CUBIC_EASING) + animate(color = "#ff9292", time = 0.5 SECOND, easing = EASE_OUT|CUBIC_EASING) + else if(party && !fire && !eject) // PARTY + color = "#ff728e" + animate(src) + animate(src, color = "#7272ff", time = 0.5 SECONDS, loop = -1, easing = QUAD_EASING) + DO_PARTY("#72aaff") + DO_PARTY("#ffc68e") + DO_PARTY("#72c6ff") + DO_PARTY("#ff72e2") + DO_PARTY("#72ff8e") + DO_PARTY("#ffff8e") + DO_PARTY("#ff728e") + else + color = "#ffb2b2" + animate(src) + animate(src, color = "#B3DFFF", time = 0.5 SECOND, loop = -1, easing = SINE_EASING) + animate(color = "#ffb2b2", time = 0.5 SECOND, loop = -1, easing = SINE_EASING) + else + animate(src, color = "#FFFFFF", time = 0.5 SECONDS, easing = QUAD_EASING) // Stop the animation. + +#undef DO_PARTY + + +/* +#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/used = 0 + switch(chan) + if(LIGHT) + used += used_light + if(EQUIP) + used += used_equip + if(ENVIRON) + used += used_environ + if(TOTAL) + used += used_light + used_equip + used_environ + return used + +/area/proc/clear_usage() + used_equip = 0 + used_light = 0 + used_environ = 0 + +/area/proc/use_power(var/amount, var/chan) + switch(chan) + if(EQUIP) + used_equip += amount + if(LIGHT) + used_light += amount + if(ENVIRON) + used_environ += amount + + +var/list/mob/living/forced_ambiance_list = new + +/area/Entered(mob/living/L) + if(!istype(L,/mob/living) || !ROUND_IS_STARTED) + return + + if(!L.ckey) return + + if(!L.lastarea) + L.lastarea = get_area(L.loc) + var/area/newarea = get_area(L.loc) + var/area/oldarea = L.lastarea + if((oldarea.has_gravity() == 0) && (newarea.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 = newarea + + // 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.client && (L.client.prefs.asfx_togs & ASFX_AMBIENCE))) return + + play_ambience(L) + +/area/proc/play_ambience(var/mob/living/L) + // 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.client && (L.client.prefs.toggles & SOUND_AMBIENCE))) return + + // If we previously were in an area with force-played ambiance, stop it. + if(L in forced_ambiance_list) + L << sound(null, channel = 1) + forced_ambiance_list -= L + + if(!L.client.ambience_playing) + L.client.ambience_playing = 1 + L << sound('sound/ambience/shipambience.ogg', repeat = loop_ambience, wait = 0, volume = 35, channel = 2) + + if(forced_ambience) + if(forced_ambience.len) + forced_ambiance_list |= L + L << sound(pick(forced_ambience), repeat = loop_ambience, wait = 0, volume = 25, channel = 1) + else + L << sound(null, channel = 1) + else if(src.ambience.len && prob(35)) + if((world.time >= L.client.played + 600)) + var/sound = pick(ambience) + L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = 1) + L.client.played = world.time + +/area/proc/gravitychange(var/gravitystate = 0) + has_gravity = gravitystate + + for(var/mob/M in src) + if(has_gravity()) + thunk(M) + else + to_chat(M, span("notice", "The sudden lack of gravity makes you feel weightless and float cluelessly!")) + M.update_floating( M.Check_Dense_Object() ) + +/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.Check_Shoegrip(FALSE)) + return + + if(H.m_intent == "run") + H.AdjustStunned(2) + H.AdjustWeakened(2) + else + H.AdjustStunned(1) + H.AdjustWeakened(1) + to_chat(mob, "The sudden appearance of gravity makes you fall to the floor!") + +/area/proc/prison_break() + for(var/obj/machinery/power/apc/temp_apc in src) + temp_apc.overload_lighting(70) + 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() + +/area/proc/has_gravity() + if(alwaysgravity) + return TRUE + if(nevergravity) + return FALSE + 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 + +//A useful proc for events. +//This returns a random area of the station which is meaningful. Ie, a room somewhere +/proc/random_station_area(var/filter_players = FALSE) + var/list/possible = list() + for(var/Y in the_station_areas) + if(!Y) + continue + var/area/A = Y + if (isNotStationLevel(A.z)) + continue + if (istype(A, /area/shuttle) || findtext(A.name, "Docked") || findtext(A.name, "Shuttle")) + continue + if (istype(A, /area/solar) || findtext(A.name, "solar")) + continue + if (istype(A, /area/constructionsite) || istype(A, /area/maintenance/interstitial_construction_site)) + continue + if (istype(A, /area/rnd/xenobiology)) + continue + if (istype(A, /area/maintenance/substation)) + continue + if (istype(A, /area/turbolift)) + continue + if (istype(A, /area/mine)) + continue + + //Although hostile mobs instadying to turrets is fun + //If there's no AI they'll just be hit with stunbeams all day and spam the attack logs. + if (istype(A, /area/turret_protected) || LAZYLEN(A.turret_controls)) + continue + + if(filter_players) + var/should_continue = FALSE + for(var/mob/living/carbon/human/H in human_mob_list) + if(!H.client) + continue + if(A == get_area(H)) + should_continue = TRUE + break + + if(should_continue) + continue + + possible += A + + return pick(possible) + +/area/proc/random_space() + var/list/turfs = list() + for(var/turf/simulated/floor/F in src.contents) + if(turf_clear(F)) + turfs += F + if (turfs.len) + return pick(turfs) + else return null diff --git a/code/game/atoms.dm b/code/game/atoms.dm index f4236eb0a7b..6c237bed360 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1,467 +1,467 @@ -/atom - layer = 2 - var/level = 2 - var/flags = 0 - var/list/fingerprints - var/list/fingerprintshidden - var/fingerprintslast = null - var/list/blood_DNA - var/list/other_DNA - var/other_DNA_type = null - var/was_bloodied - var/blood_color - var/last_bumped = 0 - 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 = 1 //filter for actions - used by lighting overlays - var/fluorescent // Shows up under a UV light. - - ///Chemistry. - var/datum/reagents/reagents = null - - //var/chem_is_open_container = 0 - // replaced by OPENCONTAINER flags and atom/proc/is_open_container() - ///Chemistry. - - //Detective Work, used for the duplicate data points kept in the scanners - var/list/original_atom - - var/gfi_layer_rotation = GFI_ROTATION_DEFAULT - -/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/additional_sight_flags() - return 0 - -/atom/proc/additional_see_invisible() - return 0 - -/atom/proc/on_reagent_change() - return - -// This is called when AM collides with us. -/atom/proc/CollidedWith(atom/movable/AM) - set waitfor = FALSE - return - -// 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 -*/ - -/atom/proc/CheckExit() - return 1 - -// If you want to use this, the atom must have the PROXMOVE flag, and the moving -// atom must also have the PROXMOVE flag currently to help with lag. ~ ComicIronic -/atom/proc/HasProximity(atom/movable/AM as mob|obj) - return - -/atom/proc/emp_act(var/severity) - return - - -/atom/proc/bullet_act(obj/item/projectile/P, def_zone) - P.on_hit(src, 0, def_zone) - . = 0 - -/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 - - -//All atoms -/atom/proc/examine(mob/user, var/distance = -1, 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 != "#030303") - f_name += "blood-stained [name][infix]!" - else - f_name += "oil-stained [name][infix]." - - to_chat(user, "\icon[src] That's [f_name] [suffix]") - to_chat(user, desc) - - return distance == -1 || (get_dist(src, user) <= distance) - -// 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) - . = new_dir != dir - dir = new_dir - - // Lighting - if (.) - var/datum/light_source/L - for (var/thing in light_sources) - L = thing - if (L.light_angle) - L.source_atom.update_light() - -/atom/proc/ex_act() - set waitfor = FALSE - return - -/atom/proc/emag_act(var/remaining_charges, var/mob/user, var/emag_source) - return NO_EMAG_ACT - -/atom/proc/fire_act() - return - -/atom/proc/melt() - return - -/atom/proc/hitby(atom/movable/AM as mob|obj) - if (density) - AM.throwing = 0 - return - -/atom/proc/add_hiddenprint(mob/living/M) - if(isnull(M)) return - if(!istype(M, /mob)) 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, ignoregloves = 0) - if(isnull(M)) return - if(!istype(M, /mob)) 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 += "(Has no fingerprints) Real name: [M.real_name], Key: [M.key]" - 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 += text("\[[]\](Wearing gloves). Real name: [], Key: []",time_stamp(), H.real_name, H.key) - fingerprintslast = H.key - H.gloves.add_fingerprint(M) - - //Deal with gloves the pass finger/palm prints. - if(!ignoregloves) - if(istype(H.gloves, /obj/item/clothing/gloves) && H.gloves != src) - var/obj/item/clothing/gloves/G = H.gloves - if(!prob(G.fingerprint_chance)) - return 0 - - //More adminstuffz - if(fingerprintslast != H.key) - fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), H.real_name, H.key) - 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 += text("\[[]\]Real name: [], Key: []",time_stamp(), M.real_name, M.key) - 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) - - 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 = 1 - 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() - if (M.species) - blood_color = M.species.blood_color - . = 1 - return 1 - -/atom/proc/add_vomit_floor(var/mob/living/carbon/M, var/toxvomit = 0, var/datum/reagents/inject_reagents) - if(istype(src, /turf/simulated)) - var/obj/effect/decal/cleanable/vomit/this = new /obj/effect/decal/cleanable/vomit(src) - if(istype(inject_reagents) && inject_reagents.total_volume) - inject_reagents.trans_to_obj(this, min(15, inject_reagents.total_volume)) - this.reagents.add_reagent("stomachacid", 5) - - // Make toxins vomit look different - if(toxvomit) - this.icon_state = "vomittox_[pick(1,4)]" - -/mob/living/proc/handle_additional_vomit_reagents(var/obj/effect/decal/cleanable/vomit/vomit) - vomit.reagents.add_reagent("stomachacid", 5) - -/atom/proc/clean_blood() - if(!simulated) - return - fluorescent = 0 - src.germ_level = 0 - if(istype(blood_DNA, /list)) - blood_DNA = null - return 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 - - 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/range = world.view) - var/turf/T = get_turf(src) - var/list/mobs = list() - var/list/objs = list() - get_mobs_and_objs_in_view_fast(T,range, mobs, objs, ONLY_GHOSTS_IN_VIEW) - - for(var/o in objs) - var/obj/O = o - O.show_message(message,1,blind_message,2) - - for(var/m in mobs) - var/mob/M = m - if(M.see_invisible >= invisibility) - M.show_message(message,1,blind_message,2) - else if(blind_message) - M.show_message(blind_message, 2) - -// 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/range = world.view - if(hearing_distance) - range = hearing_distance - var/turf/T = get_turf(src) - var/list/mobs = list() - var/list/objs = list() - get_mobs_and_objs_in_view_fast(T,range, mobs, objs, ONLY_GHOSTS_IN_VIEW) - - for(var/m in mobs) - var/mob/M = m - M.show_message(message,2,deaf_message,1) - for(var/o in objs) - var/obj/O = o - O.show_message(message,2,deaf_message,1) - -/atom/proc/change_area(var/area/oldarea, var/area/newarea) - change_area_name(oldarea.name, newarea.name) - -/atom/proc/change_area_name(var/oldname, var/newname) - name = replacetext(name,oldname,newname) - -/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 forceMove(null) - -/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 + layer = 2 + var/level = 2 + var/flags = 0 + var/list/fingerprints + var/list/fingerprintshidden + var/fingerprintslast = null + var/list/blood_DNA + var/list/other_DNA + var/other_DNA_type = null + var/was_bloodied + var/blood_color + var/last_bumped = 0 + 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 = 1 //filter for actions - used by lighting overlays + var/fluorescent // Shows up under a UV light. + + ///Chemistry. + var/datum/reagents/reagents = null + + //var/chem_is_open_container = 0 + // replaced by OPENCONTAINER flags and atom/proc/is_open_container() + ///Chemistry. + + //Detective Work, used for the duplicate data points kept in the scanners + var/list/original_atom + + var/gfi_layer_rotation = GFI_ROTATION_DEFAULT + +/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/additional_sight_flags() + return 0 + +/atom/proc/additional_see_invisible() + return 0 + +/atom/proc/on_reagent_change() + return + +// This is called when AM collides with us. +/atom/proc/CollidedWith(atom/movable/AM) + set waitfor = FALSE + return + +// 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 +*/ + +/atom/proc/CheckExit() + return 1 + +// If you want to use this, the atom must have the PROXMOVE flag, and the moving +// atom must also have the PROXMOVE flag currently to help with lag. ~ ComicIronic +/atom/proc/HasProximity(atom/movable/AM as mob|obj) + return + +/atom/proc/emp_act(var/severity) + return + + +/atom/proc/bullet_act(obj/item/projectile/P, def_zone) + P.on_hit(src, 0, def_zone) + . = 0 + +/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 + + +//All atoms +/atom/proc/examine(mob/user, var/distance = -1, 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 != "#030303") + f_name += "blood-stained [name][infix]!" + else + f_name += "oil-stained [name][infix]." + + to_chat(user, "\icon[src] That's [f_name] [suffix]") + to_chat(user, desc) + + return distance == -1 || (get_dist(src, user) <= distance) + +// 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) + . = new_dir != dir + dir = new_dir + + // Lighting + if (.) + var/datum/light_source/L + for (var/thing in light_sources) + L = thing + if (L.light_angle) + L.source_atom.update_light() + +/atom/proc/ex_act() + set waitfor = FALSE + return + +/atom/proc/emag_act(var/remaining_charges, var/mob/user, var/emag_source) + return NO_EMAG_ACT + +/atom/proc/fire_act() + return + +/atom/proc/melt() + return + +/atom/proc/hitby(atom/movable/AM as mob|obj) + if (density) + AM.throwing = 0 + return + +/atom/proc/add_hiddenprint(mob/living/M) + if(isnull(M)) return + if(!istype(M, /mob)) 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, ignoregloves = 0) + if(isnull(M)) return + if(!istype(M, /mob)) 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 += "(Has no fingerprints) Real name: [M.real_name], Key: [M.key]" + 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 += text("\[[]\](Wearing gloves). Real name: [], Key: []",time_stamp(), H.real_name, H.key) + fingerprintslast = H.key + H.gloves.add_fingerprint(M) + + //Deal with gloves the pass finger/palm prints. + if(!ignoregloves) + if(istype(H.gloves, /obj/item/clothing/gloves) && H.gloves != src) + var/obj/item/clothing/gloves/G = H.gloves + if(!prob(G.fingerprint_chance)) + return 0 + + //More adminstuffz + if(fingerprintslast != H.key) + fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), H.real_name, H.key) + 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 += text("\[[]\]Real name: [], Key: []",time_stamp(), M.real_name, M.key) + 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) + + 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 = 1 + 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() + if (M.species) + blood_color = M.species.blood_color + . = 1 + return 1 + +/atom/proc/add_vomit_floor(var/mob/living/carbon/M, var/toxvomit = 0, var/datum/reagents/inject_reagents) + if(istype(src, /turf/simulated)) + var/obj/effect/decal/cleanable/vomit/this = new /obj/effect/decal/cleanable/vomit(src) + if(istype(inject_reagents) && inject_reagents.total_volume) + inject_reagents.trans_to_obj(this, min(15, inject_reagents.total_volume)) + this.reagents.add_reagent("stomachacid", 5) + + // Make toxins vomit look different + if(toxvomit) + this.icon_state = "vomittox_[pick(1,4)]" + +/mob/living/proc/handle_additional_vomit_reagents(var/obj/effect/decal/cleanable/vomit/vomit) + vomit.reagents.add_reagent("stomachacid", 5) + +/atom/proc/clean_blood() + if(!simulated) + return + fluorescent = 0 + src.germ_level = 0 + if(istype(blood_DNA, /list)) + blood_DNA = null + return 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 + + 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/range = world.view) + var/turf/T = get_turf(src) + var/list/mobs = list() + var/list/objs = list() + get_mobs_and_objs_in_view_fast(T,range, mobs, objs, ONLY_GHOSTS_IN_VIEW) + + for(var/o in objs) + var/obj/O = o + O.show_message(message,1,blind_message,2) + + for(var/m in mobs) + var/mob/M = m + if(M.see_invisible >= invisibility) + M.show_message(message,1,blind_message,2) + else if(blind_message) + M.show_message(blind_message, 2) + +// 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/range = world.view + if(hearing_distance) + range = hearing_distance + var/turf/T = get_turf(src) + var/list/mobs = list() + var/list/objs = list() + get_mobs_and_objs_in_view_fast(T,range, mobs, objs, ONLY_GHOSTS_IN_VIEW) + + for(var/m in mobs) + var/mob/M = m + M.show_message(message,2,deaf_message,1) + for(var/o in objs) + var/obj/O = o + O.show_message(message,2,deaf_message,1) + +/atom/proc/change_area(var/area/oldarea, var/area/newarea) + change_area_name(oldarea.name, newarea.name) + +/atom/proc/change_area_name(var/oldname, var/newname) + name = replacetext(name,oldname,newname) + +/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 forceMove(null) + +/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. diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 42dc60988bc..87393a98ca9 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1,295 +1,295 @@ -/atom/movable - layer = 3 - var/last_move = null - var/anchored = 0 - // var/elevation = 2 - not used anywhere - var/move_speed = 10 - var/l_move_time = 1 - var/m_flag = 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 // Base name of the image used for when the item is in someone's hand. Suffixes are added to this. Doubles as legacy overlay_state. - var/overlay_state = null // Base name of the image used for when the item is worn. Suffixes are added to this. Important for icon flipping as _flip is added at the end of the value. - //Also used on holdable mobs for the same info related to their held version - var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P) - - var/can_hold_mob = FALSE - var/list/contained_mobs - -// We don't really need this, and apparently defining it slows down GC. -/*/atom/movable/Del() - if(!QDELING(src) && loc) - testing("GC: -- [type] was deleted via del() rather than qdel() --") - crash_with("GC: -- [type] was deleted via del() rather than qdel() --") // stick a stack trace in the runtime logs - ..()*/ - -/atom/movable/Destroy() - . = ..() - for(var/atom/movable/AM in contents) - qdel(AM) - loc = null - screen_loc = null - if (pulledby) - if (pulledby.pulling == src) - pulledby.pulling = null - pulledby = null - -// This is called when this atom is prevented from moving by atom/A. -/atom/movable/proc/Collide(atom/A) - if(airflow_speed > 0 && airflow_dest) - airflow_hit(A) - else - airflow_speed = 0 - airflow_time = 0 - - if (throwing) - throwing = FALSE - . = TRUE - if (!QDELETED(A)) - throw_impact(A) - A.CollidedWith(src) - - else if (!QDELETED(A)) - A.CollidedWith(src) - -//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 - 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 - if(T.density) - spawn(2) - step(src, turn(src.last_move, 180)) - if(istype(src,/mob/living)) - var/mob/living/M = src - M.turf_collision(T, 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) // **TODO: Better behaviour for windows which are dense, but shouldn't always stop movement - src.throw_impact(A,speed) - -/atom/movable/proc/throw_at(atom/target, range, speed, thrower, var/do_throw_animation = TRUE) - if(!target || !src) return 0 - //use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target - - src.throwing = 1 - src.thrower = thrower - src.throw_source = get_turf(src) //store the origin turf - - if(usr) - if(HULK in usr.mutations) - src.throwing = 2 // really strong throw! - - var/dist_travelled = 0 - var/dist_since_sleep = 0 - var/area/a = get_area(src.loc) - - var/dist_x = abs(target.x - src.x) - var/dist_y = abs(target.y - src.y) - - var/dx - if (target.x > src.x) - dx = EAST - else - dx = WEST - - var/dy - if (target.y > src.y) - dy = NORTH - else - dy = SOUTH - var/error - var/major_dir - var/major_dist - var/minor_dir - var/minor_dist - if(dist_x > dist_y) - error = dist_x/2 - dist_y - major_dir = dx - major_dist = dist_x - minor_dir = dy - minor_dist = dist_y - else - error = dist_y/2 - dist_x - major_dir = dy - major_dist = dist_y - minor_dir = dx - minor_dist = dist_x - - while(src && target && src.throwing && istype(src.loc, /turf) \ - && ((abs(target.x - src.x)+abs(target.y - src.y) > 0 && dist_travelled < range) \ - || (a && a.has_gravity == 0) \ - || istype(src.loc, /turf/space))) - // only stop when we've gone the whole distance (or max throw range) and are on a non-space tile, or hit something, or hit the end of the map, or someone picks it up - var/atom/step - if(error >= 0) - step = get_step(src, major_dir) - error -= minor_dist - else - step = get_step(src, minor_dir) - error += major_dist - if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge - break - src.Move(step) - hit_check(speed) - dist_travelled++ - dist_since_sleep++ - if(dist_since_sleep >= speed) - dist_since_sleep = 0 - sleep(1) - a = get_area(src.loc) - // and yet it moves - if(do_throw_animation) - if(src.does_spin) - src.SpinAnimation(speed = 4, loops = 1) - - //done throwing, either because it hit something or it finished moving - if(isobj(src)) src.throw_impact(get_turf(src),speed) - src.throwing = 0 - src.thrower = null - src.throw_source = null - - if (isturf(loc)) - var/turf/Tloc = loc - Tloc.Entered(src) - - -//Overlays -/atom/movable/overlay - var/atom/master = null - anchored = 1 - -/atom/movable/overlay/New() - verbs.Cut() - ..() - -/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 current_map.sealed_levels) - return - - if(config.use_overmap) - overmap_spacetravel(get_turf(src), src) - return - - var/move_to_z = src.get_transit_zlevel() - if(move_to_z) - z = move_to_z - - if(x <= TRANSITIONEDGE) - x = world.maxx - TRANSITIONEDGE - 2 - y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) - - else if (x >= (world.maxx - TRANSITIONEDGE + 1)) - x = TRANSITIONEDGE + 1 - y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) - - else if (y <= TRANSITIONEDGE) - y = world.maxy - TRANSITIONEDGE -2 - x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) - - else if (y >= (world.maxy - TRANSITIONEDGE + 1)) - y = TRANSITIONEDGE + 1 - x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) - - if(istype(SSticker.mode, /datum/game_mode/nuclear)) //only really care if the game mode is nuclear - var/datum/game_mode/nuclear/G = SSticker.mode - G.check_nuke_disks() - - spawn(0) - if(loc) loc.Entered(src) - -//by default, transition randomly to another zlevel -/atom/movable/proc/get_transit_zlevel() - return current_map.get_transit_zlevel() - -// Parallax stuff. - -/atom/movable/proc/update_client_hook(atom/destination) - . = isturf(destination) - if (.) - for (var/thing in contained_mobs) - var/mob/M = thing - if (!M.client || !M.hud_used) - continue - - if (get_turf(M.client.eye) == destination) - M.hud_used.update_parallax_values() - -/mob/update_client_hook(atom/destination) - . = ..() - if (. && hud_used && client && get_turf(client.eye) == destination) - hud_used.update_parallax_values() - -// Core movement hooks & procs. -/atom/movable/proc/forceMove(atom/destination) - if(destination) - if(loc) - loc.Exited(src) - loc = destination - loc.Entered(src) - if (contained_mobs) - update_client_hook(loc) - return 1 - if (contained_mobs) - update_client_hook(loc) - return 0 - -/atom/movable/Move() - var/old_loc = loc - . = ..() - if (.) - // Events. - if (moved_event.listeners_assoc[src]) - moved_event.raise_event(src, old_loc, loc) - - // Parallax. - if (contained_mobs) - update_client_hook(loc) - - // Lighting. - if (light_sources) - var/datum/light_source/L - var/thing - for (thing in light_sources) - L = thing - L.source_atom.update_light() - - // Openturf. - if (bound_overlay) - // The overlay will handle cleaning itself up on non-openspace turfs. - bound_overlay.forceMove(get_step(src, UP)) - if (bound_overlay.dir != dir) - bound_overlay.set_dir(dir) +/atom/movable + layer = 3 + var/last_move = null + var/anchored = 0 + // var/elevation = 2 - not used anywhere + var/move_speed = 10 + var/l_move_time = 1 + var/m_flag = 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 // Base name of the image used for when the item is in someone's hand. Suffixes are added to this. Doubles as legacy overlay_state. + var/overlay_state = null // Base name of the image used for when the item is worn. Suffixes are added to this. Important for icon flipping as _flip is added at the end of the value. + //Also used on holdable mobs for the same info related to their held version + var/does_spin = TRUE // Does the atom spin when thrown (of course it does :P) + + var/can_hold_mob = FALSE + var/list/contained_mobs + +// We don't really need this, and apparently defining it slows down GC. +/*/atom/movable/Del() + if(!QDELING(src) && loc) + testing("GC: -- [type] was deleted via del() rather than qdel() --") + crash_with("GC: -- [type] was deleted via del() rather than qdel() --") // stick a stack trace in the runtime logs + ..()*/ + +/atom/movable/Destroy() + . = ..() + for(var/atom/movable/AM in contents) + qdel(AM) + loc = null + screen_loc = null + if (pulledby) + if (pulledby.pulling == src) + pulledby.pulling = null + pulledby = null + +// This is called when this atom is prevented from moving by atom/A. +/atom/movable/proc/Collide(atom/A) + if(airflow_speed > 0 && airflow_dest) + airflow_hit(A) + else + airflow_speed = 0 + airflow_time = 0 + + if (throwing) + throwing = FALSE + . = TRUE + if (!QDELETED(A)) + throw_impact(A) + A.CollidedWith(src) + + else if (!QDELETED(A)) + A.CollidedWith(src) + +//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 + 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 + if(T.density) + spawn(2) + step(src, turn(src.last_move, 180)) + if(istype(src,/mob/living)) + var/mob/living/M = src + M.turf_collision(T, 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) // **TODO: Better behaviour for windows which are dense, but shouldn't always stop movement + src.throw_impact(A,speed) + +/atom/movable/proc/throw_at(atom/target, range, speed, thrower, var/do_throw_animation = TRUE) + if(!target || !src) return 0 + //use a modified version of Bresenham's algorithm to get from the atom's current position to that of the target + + src.throwing = 1 + src.thrower = thrower + src.throw_source = get_turf(src) //store the origin turf + + if(usr) + if(HULK in usr.mutations) + src.throwing = 2 // really strong throw! + + var/dist_travelled = 0 + var/dist_since_sleep = 0 + var/area/a = get_area(src.loc) + + var/dist_x = abs(target.x - src.x) + var/dist_y = abs(target.y - src.y) + + var/dx + if (target.x > src.x) + dx = EAST + else + dx = WEST + + var/dy + if (target.y > src.y) + dy = NORTH + else + dy = SOUTH + var/error + var/major_dir + var/major_dist + var/minor_dir + var/minor_dist + if(dist_x > dist_y) + error = dist_x/2 - dist_y + major_dir = dx + major_dist = dist_x + minor_dir = dy + minor_dist = dist_y + else + error = dist_y/2 - dist_x + major_dir = dy + major_dist = dist_y + minor_dir = dx + minor_dist = dist_x + + while(src && target && src.throwing && istype(src.loc, /turf) \ + && ((abs(target.x - src.x)+abs(target.y - src.y) > 0 && dist_travelled < range) \ + || (a && a.has_gravity == 0) \ + || istype(src.loc, /turf/space))) + // only stop when we've gone the whole distance (or max throw range) and are on a non-space tile, or hit something, or hit the end of the map, or someone picks it up + var/atom/step + if(error >= 0) + step = get_step(src, major_dir) + error -= minor_dist + else + step = get_step(src, minor_dir) + error += major_dist + if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge + break + src.Move(step) + hit_check(speed) + dist_travelled++ + dist_since_sleep++ + if(dist_since_sleep >= speed) + dist_since_sleep = 0 + sleep(1) + a = get_area(src.loc) + // and yet it moves + if(do_throw_animation) + if(src.does_spin) + src.SpinAnimation(speed = 4, loops = 1) + + //done throwing, either because it hit something or it finished moving + if(isobj(src)) src.throw_impact(get_turf(src),speed) + src.throwing = 0 + src.thrower = null + src.throw_source = null + + if (isturf(loc)) + var/turf/Tloc = loc + Tloc.Entered(src) + + +//Overlays +/atom/movable/overlay + var/atom/master = null + anchored = 1 + +/atom/movable/overlay/New() + verbs.Cut() + ..() + +/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 current_map.sealed_levels) + return + + if(config.use_overmap) + overmap_spacetravel(get_turf(src), src) + return + + var/move_to_z = src.get_transit_zlevel() + if(move_to_z) + z = move_to_z + + if(x <= TRANSITIONEDGE) + x = world.maxx - TRANSITIONEDGE - 2 + y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (x >= (world.maxx - TRANSITIONEDGE + 1)) + x = TRANSITIONEDGE + 1 + y = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2) + + else if (y <= TRANSITIONEDGE) + y = world.maxy - TRANSITIONEDGE -2 + x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + else if (y >= (world.maxy - TRANSITIONEDGE + 1)) + y = TRANSITIONEDGE + 1 + x = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2) + + if(istype(SSticker.mode, /datum/game_mode/nuclear)) //only really care if the game mode is nuclear + var/datum/game_mode/nuclear/G = SSticker.mode + G.check_nuke_disks() + + spawn(0) + if(loc) loc.Entered(src) + +//by default, transition randomly to another zlevel +/atom/movable/proc/get_transit_zlevel() + return current_map.get_transit_zlevel() + +// Parallax stuff. + +/atom/movable/proc/update_client_hook(atom/destination) + . = isturf(destination) + if (.) + for (var/thing in contained_mobs) + var/mob/M = thing + if (!M.client || !M.hud_used) + continue + + if (get_turf(M.client.eye) == destination) + M.hud_used.update_parallax_values() + +/mob/update_client_hook(atom/destination) + . = ..() + if (. && hud_used && client && get_turf(client.eye) == destination) + hud_used.update_parallax_values() + +// Core movement hooks & procs. +/atom/movable/proc/forceMove(atom/destination) + if(destination) + if(loc) + loc.Exited(src) + loc = destination + loc.Entered(src) + if (contained_mobs) + update_client_hook(loc) + return 1 + if (contained_mobs) + update_client_hook(loc) + return 0 + +/atom/movable/Move() + var/old_loc = loc + . = ..() + if (.) + // Events. + if (moved_event.listeners_assoc[src]) + moved_event.raise_event(src, old_loc, loc) + + // Parallax. + if (contained_mobs) + update_client_hook(loc) + + // Lighting. + if (light_sources) + var/datum/light_source/L + var/thing + for (thing in light_sources) + L = thing + L.source_atom.update_light() + + // Openturf. + if (bound_overlay) + // The overlay will handle cleaning itself up on non-openspace turfs. + bound_overlay.forceMove(get_step(src, UP)) + if (bound_overlay.dir != dir) + bound_overlay.set_dir(dir) diff --git a/code/game/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm index 2c0b456833b..4d94b72371e 100644 --- a/code/game/dna/genes/monkey.dm +++ b/code/game/dna/genes/monkey.dm @@ -1,33 +1,33 @@ -/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 ishuman(M) - -/datum/dna/gene/monkey/activate(var/mob/living/carbon/C) - var/mob/living/carbon/human/H - if(ishuman(C)) - H = C - else - return - if(!islesserform(H)) - H = H.monkeyize(1) - H.name = H.species.get_random_name() // keep the realname - -/datum/dna/gene/monkey/deactivate(var/mob/living/carbon/C) - var/mob/living/carbon/human/H - if(ishuman(C)) - H = C - else - return - - if(islesserform(H)) - H = H.humanize(1) // woo transform procs! - - if(!H.dna.real_name) - var/randomname = H.species.get_random_name() - H.real_name = randomname +/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 ishuman(M) + +/datum/dna/gene/monkey/activate(var/mob/living/carbon/C) + var/mob/living/carbon/human/H + if(ishuman(C)) + H = C + else + return + if(!islesserform(H)) + H = H.monkeyize(1) + H.name = H.species.get_random_name() // keep the realname + +/datum/dna/gene/monkey/deactivate(var/mob/living/carbon/C) + var/mob/living/carbon/human/H + if(ishuman(C)) + H = C + else + return + + if(islesserform(H)) + H = H.humanize(1) // woo transform procs! + + if(!H.dna.real_name) + var/randomname = H.species.get_random_name() + H.real_name = randomname H.dna.real_name = randomname \ No newline at end of file diff --git a/code/game/gamemodes/antagspawner.dm b/code/game/gamemodes/antagspawner.dm index 2e7f2ae249e..a45a52b8c73 100644 --- a/code/game/gamemodes/antagspawner.dm +++ b/code/game/gamemodes/antagspawner.dm @@ -1,43 +1,43 @@ -// Helper proc to make sure no more than one active syndieborg exists at a time. -/proc/can_buy_syndieborg() - for (var/mob/living/silicon/robot/R in silicon_mob_list) - if (istype(R, /mob/living/silicon/robot/syndicate)) - return 0 - - return 1 - -/obj/item/antag_spawner - throw_speed = 1 - throw_range = 5 - w_class = 1.0 - var/uses = 1 - -/obj/item/antag_spawner/proc/equip_antag(mob/target as mob) - return - -/obj/item/antag_spawner/borg_tele - name = "syndicate cyborg teleporter" - desc = "A single-use teleporter used to deploy a Syndicate Cyborg on the field. Due to budget restrictions, it is only possible to deploy a single cyborg at time." - icon = 'icons/obj/device.dmi' - icon_state = "locator" - -/obj/item/antag_spawner/borg_tele/attack_self(mob/user) - - if(uses == 0) - to_chat(usr, "This teleporter is out of uses.") - return - - to_chat(user, "The syndicate robot teleporter is attempting to locate an available cyborg.") - var/datum/ghosttrap/ghost = get_ghost_trap("syndicate cyborg") - uses-- - - var/mob/living/silicon/robot/syndicate/F = new(get_turf(usr)) - spark(F, 4, alldirs) - ghost.request_player(F,"An operative is requesting a syndicate cyborg.", 60 SECONDS) - F.faction = usr.faction - spawn(600) - if(F) - if(!F.ckey || !F.client) - F.visible_message("With no working brain to keep \the [F] working, it is teleported back.") - qdel(F) - uses++ +// Helper proc to make sure no more than one active syndieborg exists at a time. +/proc/can_buy_syndieborg() + for (var/mob/living/silicon/robot/R in silicon_mob_list) + if (istype(R, /mob/living/silicon/robot/syndicate)) + return 0 + + return 1 + +/obj/item/antag_spawner + throw_speed = 1 + throw_range = 5 + w_class = 1.0 + var/uses = 1 + +/obj/item/antag_spawner/proc/equip_antag(mob/target as mob) + return + +/obj/item/antag_spawner/borg_tele + name = "syndicate cyborg teleporter" + desc = "A single-use teleporter used to deploy a Syndicate Cyborg on the field. Due to budget restrictions, it is only possible to deploy a single cyborg at time." + icon = 'icons/obj/device.dmi' + icon_state = "locator" + +/obj/item/antag_spawner/borg_tele/attack_self(mob/user) + + if(uses == 0) + to_chat(usr, "This teleporter is out of uses.") + return + + to_chat(user, "The syndicate robot teleporter is attempting to locate an available cyborg.") + var/datum/ghosttrap/ghost = get_ghost_trap("syndicate cyborg") + uses-- + + var/mob/living/silicon/robot/syndicate/F = new(get_turf(usr)) + spark(F, 4, alldirs) + ghost.request_player(F,"An operative is requesting a syndicate cyborg.", 60 SECONDS) + F.faction = usr.faction + spawn(600) + if(F) + if(!F.ckey || !F.client) + F.visible_message("With no working brain to keep \the [F] working, it is teleported back.") + qdel(F) + uses++ diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index f21b13cbab6..fca728d49a7 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -1,9 +1,9 @@ -/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 = 9 - required_enemies = 4 - end_on_antag_death = 1 - 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 = 9 + required_enemies = 4 + end_on_antag_death = 1 + antag_tags = list(MODE_CULTIST) diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 36448e19302..aad97a2538e 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -1,131 +1,131 @@ -/obj/item/melee/cultblade - name = "eldritch blade" - desc = "A sword humming with unholy energy. It glows with a dim red light." - icon_state = "cultblade" - item_state = "cultblade" - icon = 'icons/obj/weapons.dmi' - w_class = 4 - force = 30 - throwforce = 10 - edge = 1 - sharp = 1 - hitsound = 'sound/weapons/bladeslice.ogg' - - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - can_embed = 0 //can't get stuck anymore, because blood magic - -/obj/item/melee/cultblade/cultify() - return - -/obj/item/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone) - if(iscultist(user)) - return ..() - - var/zone = (user.hand ? BP_L_ARM:BP_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 unexplicable force rips through your [affecting.name], tearing the sword from your grasp!") - else - to_chat(user, "An unexplicable force rips through you, 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=1, edge=1) - user.Weaken(5) - - user.drop_from_inventory(src) - 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(loc, spooky, 50, 1) - - return 1 - -/obj/item/melee/cultblade/pickup(mob/living/user as mob) - ..() - if(!iscultist(user)) - to_chat(user, "An overwhelming feeling of dread comes over you as you pick up \the [src]. It would be wise to be rid of this blade quickly.") - user.make_dizzy(120) - -/obj/item/melee/cultblade/attackby(var/obj/item/I, var/mob/user) - ..() - if(istype(I, /obj/item/nullrod)) - to_chat(user, "You cleanse \the [src] of taint, restoring the blade to its original state.") - var/obj/item/material/sword/blade = new(get_turf(src)) - blade.force = 15 - qdel(src) - -/obj/item/clothing/head/culthood - name = "ragged hood" - icon_state = "culthood" - desc = "A torn, dust-caked hood." - flags_inv = HIDEFACE|HIDEEARS|HIDEEYES - body_parts_covered = HEAD|EYES - armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, 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 = "ragged robes" - desc = "A ragged, dusty set of robes." - icon_state = "cultrobes" - item_state = "cultrobes" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/book/tome,/obj/item/melee/cultblade) - armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, 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" - item_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" - item_state = "magusred" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - flags_inv = HIDEGLOVES|HIDEJUMPSUIT - -/obj/item/clothing/head/helmet/space/cult - name = "eldritch helmet" - desc = "A bulky helmet, bristling with spikes. It looks space proof." - icon_state = "cult_helmet" - armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, 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 = "eldritch armour" - icon_state = "cult_armour" - item_state = "cult_armour" - desc = "A bulky suit of armour, bristling with spikes. It looks space proof." - w_class = 3 - allowed = list(/obj/item/book/tome,/obj/item/melee/cultblade,/obj/item/tank/emergency_oxygen,/obj/item/device/suit_cooling_unit) - slowdown = 1 - armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) - siemens_coefficient = 0 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS - -/obj/item/clothing/suit/space/cult/cultify() - return +/obj/item/melee/cultblade + name = "eldritch blade" + desc = "A sword humming with unholy energy. It glows with a dim red light." + icon_state = "cultblade" + item_state = "cultblade" + icon = 'icons/obj/weapons.dmi' + w_class = 4 + force = 30 + throwforce = 10 + edge = 1 + sharp = 1 + hitsound = 'sound/weapons/bladeslice.ogg' + + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + can_embed = 0 //can't get stuck anymore, because blood magic + +/obj/item/melee/cultblade/cultify() + return + +/obj/item/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone) + if(iscultist(user)) + return ..() + + var/zone = (user.hand ? BP_L_ARM:BP_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 unexplicable force rips through your [affecting.name], tearing the sword from your grasp!") + else + to_chat(user, "An unexplicable force rips through you, 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=1, edge=1) + user.Weaken(5) + + user.drop_from_inventory(src) + 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(loc, spooky, 50, 1) + + return 1 + +/obj/item/melee/cultblade/pickup(mob/living/user as mob) + ..() + if(!iscultist(user)) + to_chat(user, "An overwhelming feeling of dread comes over you as you pick up \the [src]. It would be wise to be rid of this blade quickly.") + user.make_dizzy(120) + +/obj/item/melee/cultblade/attackby(var/obj/item/I, var/mob/user) + ..() + if(istype(I, /obj/item/nullrod)) + to_chat(user, "You cleanse \the [src] of taint, restoring the blade to its original state.") + var/obj/item/material/sword/blade = new(get_turf(src)) + blade.force = 15 + qdel(src) + +/obj/item/clothing/head/culthood + name = "ragged hood" + icon_state = "culthood" + desc = "A torn, dust-caked hood." + flags_inv = HIDEFACE|HIDEEARS|HIDEEYES + body_parts_covered = HEAD|EYES + armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, 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 = "ragged robes" + desc = "A ragged, dusty set of robes." + icon_state = "cultrobes" + item_state = "cultrobes" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/book/tome,/obj/item/melee/cultblade) + armor = list(melee = 50, bullet = 30, laser = 50,energy = 20, 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" + item_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" + item_state = "magusred" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDEJUMPSUIT + +/obj/item/clothing/head/helmet/space/cult + name = "eldritch helmet" + desc = "A bulky helmet, bristling with spikes. It looks space proof." + icon_state = "cult_helmet" + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, 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 = "eldritch armour" + icon_state = "cult_armour" + item_state = "cult_armour" + desc = "A bulky suit of armour, bristling with spikes. It looks space proof." + w_class = 3 + allowed = list(/obj/item/book/tome,/obj/item/melee/cultblade,/obj/item/tank/emergency_oxygen,/obj/item/device/suit_cooling_unit) + slowdown = 1 + armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30) + siemens_coefficient = 0 + 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 37085752ed8..8f39a3efb58 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -1,643 +1,643 @@ -/obj/structure/cult - density = 1 - anchored = 1 - icon = 'icons/obj/cult.dmi' - appearance_flags = NO_CLIENT_COLOR - -/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" - -/* - Cult pylons can be used as arcane defensive turrets. - - First of all they must be empowered with a sacrifice. Any small organic creature that can be picked up is valid - Hit the pylon with the creature - while still alive - to present it as a sacrifice, and then end its life to complete the process. - - Once empowered, pylons will fire rapid, weak magical beams at non cult mobs. - - As crystals, pylons are immune to energy weaponry. More than immune, they will absorb lasers and become - supercharged for some time, boosting their damage. Pylons can only be harmed by ballistics and physical weapons - Building reinforced windows around them is highly recommended. - - Pylons are fairly fragile and will quickly break under direct hits. - A damaged pylon will self repair, if it contains a soul. - - - Pylons are largely intended as a stalling, early warning, fire support and area denial system. They are unlikely to actually - kill anyone alone but they can keep pesky civilians away for long enough to learn runes and prepare rituals. - - If you can get ahold of a laser rifle to supercharge them, they can even give security some pause - - Recommended tools for combatting pylons: - Heavy melee (fireaxe, baseball bat, etc) - Sniper rifles - Exosuits - Explosives - Ablative armour - Smoke grenades - Close the damn firelocks so they can't see you -*/ - -/obj/structure/cult/pylon - name = "pylon" - desc = "A floating crystal that hums with an unearthly energy" - description_antag = "A pylon can be upgraded into a magical defensive turret that shoots anyone opposing the cult\ -
    Upgrading a pylon requires a sacrifice. Bring it a small organic creature, like a monkey or rat. Use the creature on the pylon, or drag and drop to present it.\ -
    Once the sacrifice is accepted, kill it to complete the process. This will gib its body and make a very visible mess. After this point the pylon is fixed to the floor and cant be moved\ -
    The pylon will fire weak beams that are harmless to the cult. In addition it can be upgraded even more by shooting it with a laser, which will give it a limited number of extra-power shots." - - icon_state = "pylonbase" - var/isbroken = 0 - light_range = 5 - light_color = "#3e0000" - var/pylonmode = 0 - //Pylon has three states: - //0 = Idle, the pylon is an inert crystal - //1 = Awaiting sacrifice. The pylon is actively tracking a creature to be sacrificed to it - //2 = Turret. The pylon has been empowered by a sacrifice and is now a turret permanantly - - var/damagetaken = 0 - var/empowered = 0 //Number of empowered, higher-damage shots remaining - var/mob/living/sacrifice //Holds a reference to a mob that is a pending sacrifice - var/mob/living/sacrificer //A reference to the last mob that attempted to sacrifice something. So we can message them - //Sacrifier is also used in target handling. the pylon will not bite the hand that feeds it. Noncultist colleagues are fair game though - - var/next_shot = 0 //Absolute world time when we're allowed to fire again - var/shot_delay = 5 //Minimum delay between shots, in deciseconds - var/mob/living/target - - var/notarget //Number of times handle_firing has been called without finding anything to shoot at - //This is used for switching to lower-intensity processing - - var/datum/language/cultcommon/lang - //An instance of the cult language datum. Used to scramble speech when speaking to noncultists - - var/turf/last_target_loc - - var/process_interval = 1 - var/ticks - anchored = 0 - - -//Just a subtype that starts off in turret mode. For adminbus and debugging. Maybe a future wizard spell -//Spawn them next to ERPers -/obj/structure/cult/pylon/turret - pylonmode = 2 - -/obj/structure/cult/pylon/turret/New() - ..() - start_process() - - -/obj/structure/cult/pylon/Destroy() - STOP_PROCESSING(SSprocessing, src) - return ..() - -//Another subtype which starts with infinite empower shots. For empowered adminbus -/obj/structure/cult/pylon/turret/empowered - empowered = 99999999 - - -/obj/structure/cult/pylon/New() - ..() - lang = new /datum/language/cultcommon() - update_icon() - -/obj/structure/cult/pylon/examine(var/mob/user) - ..() - if (damagetaken) - switch (damagetaken) - if (1 to 8) - to_chat(user, "It has very faint hairline fractures.") - if (8 to 20) - to_chat(user, "It has several cracks across its surface.") - if (20 to 30) - to_chat(user, "It is chipped and deeply cracked, it may shatter with much more pressure.") - if (30 to INFINITY) - to_chat(user, "It is almost cleaved in two, the pylon looks like it will fall to shards under its own weight.") - - -/obj/structure/cult/pylon/Move() - ..() - last_target_loc = null - -/obj/structure/cult/pylon/proc/start_process() - process_interval = 1 - START_PROCESSING(SSprocessing, src) - - -//If the pylon goes a long time without shooting anything, it will consider slowing down processing -/obj/structure/cult/pylon/proc/reconsider_interval() - var/mindist = INFINITY - for (var/m in player_list) - var/mob/living/L = m - if (L.z != z) - continue - - if (L.stat == DEAD) - continue - - if (iscult(L)) - continue - - if (L == sacrificer) - continue - - var/d = get_dist(src, L) - if (d < mindist) - mindist = d - - mindist = min(150, mindist) - process_interval = Ceiling(mindist*0.1) - process_interval = max(1, process_interval) - - -//Run each process loop, this function checks if we can stop processing yet. -//Returns 0 if its time to stop. Returns 1 if we should keep going. -/obj/structure/cult/pylon/proc/check_process() - if (isbroken) - return 0 - - if (pylonmode == 2) - return 1 - - if (pylonmode == 1) - if (sacrifice) - return 1 - - if (empowered > 0) - return 1 - - return 0 - -/obj/structure/cult/pylon/process() - ticks++ - if(!check_process()) - STOP_PROCESSING(SSprocessing, src) - return - - switch (pylonmode) - if (1) - handle_sacrifice() - if (2) - if ((ticks % process_interval) == 0) - handle_firing() - if (damagetaken && prob(50) && empowered > 0) - damagetaken = max(0, damagetaken-1) //An empowered pylon slowly self repairs - empowered = max(0, empowered - 0.2) - if (prob(10)) - visible_message("Cracks in the [src] gradually seal as new crystalline matter grows to fill them.") - - if (empowered && prob(4)) - empowered = max(0, empowered - 1) //Overcharging gradually wears off over time - if (empowered <= 0) - update_icon() - - -//If user is a cultist, speaks message to them with a prefix -//If user is not cultist, then speaks cult-y gibberish -/obj/structure/cult/pylon/proc/speak_to(var/mob/user, var/message) - if (iscult(user) || (/datum/language/cultcommon in user.languages)) - to_chat(user, "A voice speaks into your mind, [message]") - else - to_chat(user, "A voice speaks into your mind, [lang.scramble(message)]") - - -//Todo: Replace the messages here with better ones. Should display a proper message to cultists -//And nonsensical arcane gibberish to non cultists -/obj/structure/cult/pylon/proc/present_sacrifice(var/mob/living/user, var/mob/living/victim) - if (!user || !victim) - return 0 - - if (isbroken) - to_chat(user, "The pylon lies silent.") - return 0 - - if (pylonmode != 0) - return 0 - - if (!(victim.mob_size) || victim.mob_size > 6) - //toolarge sacrifice, display a message and return - speak_to(user, "This creature is too large, we only require a lesser sacrifice. A small morsel of life...") - return - - if (victim.stat == DEAD) - speak_to(user, "You are too late, the spark of life is already gone from this one. It is naught but an empty husk...") - return - - var/types = victim.find_type() - if ((!(types & TYPE_ORGANIC)) || ((types & TYPE_WEIRD))) - //Invalid sacrifice. Display a message and return - speak_to(user, "This soulless automaton cannot satisfy our hunger. We yearn for life essence, it must have a soul.") - return - - sacrifice = victim - sacrificer = user - //Sacrifice accepted, display message here - speak_to(user, "Your sacrifice has been deemed worthy, and accepted. End its life now, and liberate its soul, to seal our contract...") - to_chat(sacrifice, span("danger", "You feel an invisible force grip your soul, as you're drawn inexorably towards the pylon. Every part of you screams to flee from here!")) - - if (istype(sacrifice.loc,/obj/item/holder)) - var/obj/item/holder/H = sacrifice.loc - H.release_to_floor() - else - sacrifice.forceMove(get_turf(sacrifice)) - pylonmode = 1 - update_icon() - start_process() - - - -//Runs every proc while there's a pending sacrifice. This checks whether the sacrifice was killed or escaped -/obj/structure/cult/pylon/proc/handle_sacrifice() - .=0 //Return value has 3 states. - //0 = waiting, the sacrifice is still alive - //1 = Finished, the sacrifice was killed, finalize the process and become a turret - //-1 = Escape: The sacrifice left the vicinity of the pylon, return to inert mode. - - if (!sacrifice) - //If the sacrifice was deleted somehow, we cant know exactly what happened. We'll assume it escaped - .=-1 - - else if(get_dist(src, get_turf(sacrifice)) > 5) - //If the sacrifice gets more than 5 tiles away, it has escaped - .=-1 - else if (sacrifice.stat == DEAD) - .=1 - - switch(.) - if (1) - finalize_sacrifice() - if (-1) - speak_to(sacrificer, "Fool! Your offering has escaped. Bring it back, or find us another...") - sacrifice = null - pylonmode = 0 - update_icon() - if (sacrifice) - walk_to(sacrifice,0) - else - if (istype(sacrifice.loc, /turf) && !(sacrifice.is_ventcrawling) && !(sacrifice.buckled)) - //Suck the creature towards the pylon if possible - walk_towards(sacrifice,src, 10) - else - walk_to(sacrifice,0) //If we're not in a valid situation, cancel walking to prevent bugginess - -/obj/structure/cult/pylon/proc/finalize_sacrifice() - sacrifice.visible_message(span("danger","\The [sacrifice]'s physical form unwinds as its soul is extracted from the remains, and drawn into the pylon!")) - if (istype(sacrifice.loc,/obj/item/holder)) - var/obj/item/holder/H = sacrifice.loc - H.release_to_floor() - else - sacrifice.forceMove(get_turf(sacrifice)) //Make sure its on the floor before we gib it - pylonmode = 2 - sacrifice.gib() - - update_icon() - - -//Called every process in turret mode, and also by chaining spawns -/obj/structure/cult/pylon/proc/handle_firing() - - if ((world.time < next_shot) || isbroken) - return - //Not ready to fire yet - - - var/list/stuffcache - //Stuffcache holds a list of things found by a dview call, so we only need to do it once per proc. - - - if (target && (target.stat != DEAD)) - if (target.loc == last_target_loc) - //To minimise expensive DVIEW calls, if the target hasnt moved since the last shot we'll assume they're still in sight - //This could cause problems in some edge cases (like lowering firelocks or shutters without moving) - //But it has a major performance gain that makes it worth it - fire_at(target) - return - else - stuffcache = mobs_in_view(9, src) - //for (var/turf/T in stuffcache) - //new /obj/effect/testtrans(T) - if ((target in stuffcache) && isInSight(src, target)) - for (var/mob/m in stuffcache) - fire_at(target) - return - else - target = null - - //We may have already populated stuffcache this run, dont repeat work - if (!stuffcache) - stuffcache = mobs_in_view(9, src) - - target = null //Either we lost a target or lack one - //for (var/turf/T in stuffcache) - //new /obj/effect/testtrans(T) - - for (var/mob/living/L in stuffcache) - if (L == sacrificer) - continue - //A pylon won't shoot the person who created it, regardless of cult status. - //This is mainly for xenoarch antags - - if (L.stat == DEAD) - continue - //No point shooting at corpses - - if (iscult(L)) - continue - //Pylon wont shoot at cultists or constructs - - if (!isInSight(src, L)) - continue - - target = L - - - break - - if (target) - fire_at(target) - else - notarget++ - if (notarget > 60 || process_interval > 1) - notarget = 0 - reconsider_interval() - - -/obj/structure/cult/pylon/proc/fire_at(var/atom/target) - //Only store loc if target is on a turf. Otherwise this bugs out with people in exosuits - if (istype(target.loc, /turf)) - last_target_loc = target.loc - - process_interval = 1 //Instantly wake up if we found a target - var/obj/item/projectile/beam/cult/A - if (empowered > 0) - A = new /obj/item/projectile/beam/cult/heavy(loc) - empowered = max(0, empowered-1) - playsound(loc, 'sound/weapons/laserdeep.ogg', 100, 1) - if (empowered <= 0) - update_icon() - else - A = new /obj/item/projectile/beam/cult(loc) - playsound(loc, 'sound/weapons/laserdeep.ogg', 65, 1) - A.ignore = sacrificer - A.launch_projectile(target) - next_shot = world.time + shot_delay - A = null //So projectiles can GC - spawn(shot_delay+1) - handle_firing() - - - -/obj/structure/cult/pylon/attack_hand(mob/M as mob) - if (M.a_intent == "help") - to_chat(M, "The pylon feels warm to the touch....") - else - attackpylon(M, 4, M) - -/obj/structure/cult/pylon/attack_generic(var/mob/user, var/damage) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - - //Artificiers maintain pylons - if(istype(user, /mob/living/simple_animal/construct)) - var/mob/living/simple_animal/construct/C = user - if (C.can_repair) - repair(user) - return - attackpylon(user, damage, user) - -/obj/structure/cult/pylon/attackby(obj/item/W as obj, mob/user as mob) - if (istype(W, /obj/item/holder)) - var/obj/item/holder/H = W - if (H.contained) - present_sacrifice(user, H.contained) - return - - attackpylon(user, W.force, W) - - -//Mousedrop so that constructs can drag rats out of maintenance to make turrets -/obj/structure/cult/pylon/MouseDrop_T(var/atom/movable/C, mob/user) - if (istype(C, /mob/living)) - present_sacrifice(user, C) - return - return ..() - -/obj/structure/cult/pylon/bullet_act(var/obj/item/projectile/Proj) - attackpylon(Proj.firer, Proj.damage, Proj) - -//Explosions will usually cause instant shattering, or heavy damage -//Class 3 or lower blast is sometimes survivable. 2 or higher will always shatter -/obj/structure/cult/pylon/ex_act(var/severity) - if (severity > 0) - attackpylon(null, (rand(200,400)/severity), null) - -/obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage, var/source) - var/ranged = 0 - if (user) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if (istype(source, /obj/item)) - var/obj/item/I = source - if(istype(I, /obj/item/nullrod)) - shatter() - return - if (I.damtype != BRUTE) - to_chat(user, "You swing at the pylon to no effect.") - return - - if (istype(source, /obj/item/projectile)) - if (istype(source, /obj/item/projectile/beam/cult)) - return //No feedback loops - var/obj/item/projectile/proj = source - if (proj.damage_type == BURN) - if (empowered <= 0) - visible_message( - "The beam refracts inside the pylon, splitting into an indistinct violet glow. The crystal takes on a new, more ominous aura!" - ) - empowered += damage*0.2 - //When shot with a laser, the pylon absorbs the beam, becoming empowered for a while, glowing brighter - // and firing more powerful blasts which have some armor penetration - // Using lasers to empower a defensive pylon yields more total damage than directly shooting your enemies - - start_process() - update_icon() - return - else if (proj.damage_type != BRUTE) - return - ranged = 1 - - if (!damage) - return - - damagetaken += damage - - if(!isbroken) - if (user) - user.do_attack_animation(src) - if(prob(damagetaken*0.75)) - shatter() - else - if (user && !ranged) - to_chat(user, "You hit the pylon!") - playsound(get_turf(src), 'sound/effects/Glasshit.ogg', 75, 1) - else - if(prob(damagetaken)) - if (user) - to_chat(user, "You pulverize what was left of the pylon!") - qdel(src) - else if (user && !ranged) - to_chat(user, "You hit the pylon!") - playsound(get_turf(src), 'sound/effects/Glasshit.ogg', 75, 1) - - start_process() - -/obj/structure/cult/pylon/proc/shatter() - visible_message( - "The pylon shatters into shards of crystal!", - "You hear a tinkle of crystal shards" - ) - playsound(get_turf(src), 'sound/effects/Glassbr3.ogg', 75, 1) - isbroken = 1 - if (pylonmode == 2) - //If the pylon had a soul in it then it plays a creepy evil sound as the soul is released - var/possibles = list('sound/hallucinations/far_noise.ogg', - 'sound/hallucinations/growl3.ogg', - 'sound/hallucinations/veryfar_noise.ogg', - 'sound/hallucinations/wail.ogg', - 'sound/voice/hiss5.ogg') - playsound(get_turf(src), pick(possibles), 50, 1) - - pylonmode = 0 //A broken pylon loses its soul. Even if repaired it will need a new sacrifice to re-empower it - - //Make sure we stop it from firing - target = null - sacrificer = null - notarget = 0 - last_target_loc = null - process_interval = 1 - //It will stop processing after the next check, start of the next process - - empowered = 0 - density = 0 - update_icon() - - - -/obj/structure/cult/pylon/proc/repair(mob/user as mob) - if(isbroken) - if(user) - to_chat(user, span("notice","You weave forgotten magic, summoning the shards of the crystal and knitting them anew, until it hovers flawless once more.")) - isbroken = 0 - density = 1 - else if (damagetaken > 0) - to_chat(user, span("notice","You meld the crystal lattice back into integrity, sealing over the cracks until they never were.")) - - else - to_chat(user, span("notice","The crystal lights up at your touch.")) - - process_interval = 1 //Wake up the crystal - notarget = 0 - damagetaken = 0 - update_icon() - - -/obj/structure/cult/pylon/update_icon() - cut_overlays() - if (pylonmode == 2) - anchored = 1 - if (empowered > 0) - add_overlay("crystal_overcharge") - set_light(7, 3, l_color = "#a160bf") - else - set_light(6, 3, l_color = "#3e0000") - add_overlay("crystal_turret") - else if (!isbroken) - set_light(5, 2, l_color = "#3e0000") - add_overlay("crystal_idle") - if (pylonmode == 1) - anchored = 1 - else - anchored = 0 - else - anchored = 0 - set_light(0) - - - -//============================================ -/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" - -/obj/effect/gateway - name = "gateway" - desc = "You're pretty sure that abyss is staring back" - icon = 'icons/obj/cult.dmi' - icon_state = "hole" - density = 1 - unacidable = 1 - anchored = 1.0 - var/spawnable = null - -/obj/effect/gateway/CollidedWith(mob/M) - return - -/obj/effect/gateway/Crossed(AM as mob|obj) - return - -/obj/effect/gateway/active - light_range=5 - light_color="#ff0000" - spawnable=list( - /mob/living/simple_animal/hostile/scarybat, - /mob/living/simple_animal/hostile/creature, - /mob/living/simple_animal/hostile/faithless - ) - -/obj/effect/gateway/active/cult - light_range=5 - light_color="#ff0000" - spawnable=list( - /mob/living/simple_animal/hostile/scarybat/cult, - /mob/living/simple_animal/hostile/creature/cult, - /mob/living/simple_animal/hostile/faithless/cult - ) - appearance_flags = NO_CLIENT_COLOR - -/obj/effect/gateway/active/cult/cultify() - return - -/obj/effect/gateway/active/New() - addtimer(CALLBACK(src, .proc/do_spawn), rand(30, 60) SECONDS) - -/obj/effect/gateway/active/proc/do_spawn() - var/thing = pick(spawnable) - new thing(src.loc) - qdel(src) - -/obj/effect/gateway/attackby(var/obj/item/I, var/mob/user) - ..() - if(istype(I, /obj/item/nullrod)) - to_chat(user, "You touch \the [src] with \the [I], closing the path to the otherworld.") - qdel(src) - -/obj/effect/testtrans - icon = 'icons/obj/cult.dmi' - icon_state = "tt" - -/obj/effect/testtrans/New() - ..() - spawn(10) - qdel(src) +/obj/structure/cult + density = 1 + anchored = 1 + icon = 'icons/obj/cult.dmi' + appearance_flags = NO_CLIENT_COLOR + +/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" + +/* + Cult pylons can be used as arcane defensive turrets. + + First of all they must be empowered with a sacrifice. Any small organic creature that can be picked up is valid + Hit the pylon with the creature - while still alive - to present it as a sacrifice, and then end its life to complete the process. + + Once empowered, pylons will fire rapid, weak magical beams at non cult mobs. + + As crystals, pylons are immune to energy weaponry. More than immune, they will absorb lasers and become + supercharged for some time, boosting their damage. Pylons can only be harmed by ballistics and physical weapons + Building reinforced windows around them is highly recommended. + + Pylons are fairly fragile and will quickly break under direct hits. + A damaged pylon will self repair, if it contains a soul. + + + Pylons are largely intended as a stalling, early warning, fire support and area denial system. They are unlikely to actually + kill anyone alone but they can keep pesky civilians away for long enough to learn runes and prepare rituals. + + If you can get ahold of a laser rifle to supercharge them, they can even give security some pause + + Recommended tools for combatting pylons: + Heavy melee (fireaxe, baseball bat, etc) + Sniper rifles + Exosuits + Explosives + Ablative armour + Smoke grenades + Close the damn firelocks so they can't see you +*/ + +/obj/structure/cult/pylon + name = "pylon" + desc = "A floating crystal that hums with an unearthly energy" + description_antag = "A pylon can be upgraded into a magical defensive turret that shoots anyone opposing the cult\ +
    Upgrading a pylon requires a sacrifice. Bring it a small organic creature, like a monkey or rat. Use the creature on the pylon, or drag and drop to present it.\ +
    Once the sacrifice is accepted, kill it to complete the process. This will gib its body and make a very visible mess. After this point the pylon is fixed to the floor and cant be moved\ +
    The pylon will fire weak beams that are harmless to the cult. In addition it can be upgraded even more by shooting it with a laser, which will give it a limited number of extra-power shots." + + icon_state = "pylonbase" + var/isbroken = 0 + light_range = 5 + light_color = "#3e0000" + var/pylonmode = 0 + //Pylon has three states: + //0 = Idle, the pylon is an inert crystal + //1 = Awaiting sacrifice. The pylon is actively tracking a creature to be sacrificed to it + //2 = Turret. The pylon has been empowered by a sacrifice and is now a turret permanantly + + var/damagetaken = 0 + var/empowered = 0 //Number of empowered, higher-damage shots remaining + var/mob/living/sacrifice //Holds a reference to a mob that is a pending sacrifice + var/mob/living/sacrificer //A reference to the last mob that attempted to sacrifice something. So we can message them + //Sacrifier is also used in target handling. the pylon will not bite the hand that feeds it. Noncultist colleagues are fair game though + + var/next_shot = 0 //Absolute world time when we're allowed to fire again + var/shot_delay = 5 //Minimum delay between shots, in deciseconds + var/mob/living/target + + var/notarget //Number of times handle_firing has been called without finding anything to shoot at + //This is used for switching to lower-intensity processing + + var/datum/language/cultcommon/lang + //An instance of the cult language datum. Used to scramble speech when speaking to noncultists + + var/turf/last_target_loc + + var/process_interval = 1 + var/ticks + anchored = 0 + + +//Just a subtype that starts off in turret mode. For adminbus and debugging. Maybe a future wizard spell +//Spawn them next to ERPers +/obj/structure/cult/pylon/turret + pylonmode = 2 + +/obj/structure/cult/pylon/turret/New() + ..() + start_process() + + +/obj/structure/cult/pylon/Destroy() + STOP_PROCESSING(SSprocessing, src) + return ..() + +//Another subtype which starts with infinite empower shots. For empowered adminbus +/obj/structure/cult/pylon/turret/empowered + empowered = 99999999 + + +/obj/structure/cult/pylon/New() + ..() + lang = new /datum/language/cultcommon() + update_icon() + +/obj/structure/cult/pylon/examine(var/mob/user) + ..() + if (damagetaken) + switch (damagetaken) + if (1 to 8) + to_chat(user, "It has very faint hairline fractures.") + if (8 to 20) + to_chat(user, "It has several cracks across its surface.") + if (20 to 30) + to_chat(user, "It is chipped and deeply cracked, it may shatter with much more pressure.") + if (30 to INFINITY) + to_chat(user, "It is almost cleaved in two, the pylon looks like it will fall to shards under its own weight.") + + +/obj/structure/cult/pylon/Move() + ..() + last_target_loc = null + +/obj/structure/cult/pylon/proc/start_process() + process_interval = 1 + START_PROCESSING(SSprocessing, src) + + +//If the pylon goes a long time without shooting anything, it will consider slowing down processing +/obj/structure/cult/pylon/proc/reconsider_interval() + var/mindist = INFINITY + for (var/m in player_list) + var/mob/living/L = m + if (L.z != z) + continue + + if (L.stat == DEAD) + continue + + if (iscult(L)) + continue + + if (L == sacrificer) + continue + + var/d = get_dist(src, L) + if (d < mindist) + mindist = d + + mindist = min(150, mindist) + process_interval = Ceiling(mindist*0.1) + process_interval = max(1, process_interval) + + +//Run each process loop, this function checks if we can stop processing yet. +//Returns 0 if its time to stop. Returns 1 if we should keep going. +/obj/structure/cult/pylon/proc/check_process() + if (isbroken) + return 0 + + if (pylonmode == 2) + return 1 + + if (pylonmode == 1) + if (sacrifice) + return 1 + + if (empowered > 0) + return 1 + + return 0 + +/obj/structure/cult/pylon/process() + ticks++ + if(!check_process()) + STOP_PROCESSING(SSprocessing, src) + return + + switch (pylonmode) + if (1) + handle_sacrifice() + if (2) + if ((ticks % process_interval) == 0) + handle_firing() + if (damagetaken && prob(50) && empowered > 0) + damagetaken = max(0, damagetaken-1) //An empowered pylon slowly self repairs + empowered = max(0, empowered - 0.2) + if (prob(10)) + visible_message("Cracks in the [src] gradually seal as new crystalline matter grows to fill them.") + + if (empowered && prob(4)) + empowered = max(0, empowered - 1) //Overcharging gradually wears off over time + if (empowered <= 0) + update_icon() + + +//If user is a cultist, speaks message to them with a prefix +//If user is not cultist, then speaks cult-y gibberish +/obj/structure/cult/pylon/proc/speak_to(var/mob/user, var/message) + if (iscult(user) || (/datum/language/cultcommon in user.languages)) + to_chat(user, "A voice speaks into your mind, [message]") + else + to_chat(user, "A voice speaks into your mind, [lang.scramble(message)]") + + +//Todo: Replace the messages here with better ones. Should display a proper message to cultists +//And nonsensical arcane gibberish to non cultists +/obj/structure/cult/pylon/proc/present_sacrifice(var/mob/living/user, var/mob/living/victim) + if (!user || !victim) + return 0 + + if (isbroken) + to_chat(user, "The pylon lies silent.") + return 0 + + if (pylonmode != 0) + return 0 + + if (!(victim.mob_size) || victim.mob_size > 6) + //toolarge sacrifice, display a message and return + speak_to(user, "This creature is too large, we only require a lesser sacrifice. A small morsel of life...") + return + + if (victim.stat == DEAD) + speak_to(user, "You are too late, the spark of life is already gone from this one. It is naught but an empty husk...") + return + + var/types = victim.find_type() + if ((!(types & TYPE_ORGANIC)) || ((types & TYPE_WEIRD))) + //Invalid sacrifice. Display a message and return + speak_to(user, "This soulless automaton cannot satisfy our hunger. We yearn for life essence, it must have a soul.") + return + + sacrifice = victim + sacrificer = user + //Sacrifice accepted, display message here + speak_to(user, "Your sacrifice has been deemed worthy, and accepted. End its life now, and liberate its soul, to seal our contract...") + to_chat(sacrifice, span("danger", "You feel an invisible force grip your soul, as you're drawn inexorably towards the pylon. Every part of you screams to flee from here!")) + + if (istype(sacrifice.loc,/obj/item/holder)) + var/obj/item/holder/H = sacrifice.loc + H.release_to_floor() + else + sacrifice.forceMove(get_turf(sacrifice)) + pylonmode = 1 + update_icon() + start_process() + + + +//Runs every proc while there's a pending sacrifice. This checks whether the sacrifice was killed or escaped +/obj/structure/cult/pylon/proc/handle_sacrifice() + .=0 //Return value has 3 states. + //0 = waiting, the sacrifice is still alive + //1 = Finished, the sacrifice was killed, finalize the process and become a turret + //-1 = Escape: The sacrifice left the vicinity of the pylon, return to inert mode. + + if (!sacrifice) + //If the sacrifice was deleted somehow, we cant know exactly what happened. We'll assume it escaped + .=-1 + + else if(get_dist(src, get_turf(sacrifice)) > 5) + //If the sacrifice gets more than 5 tiles away, it has escaped + .=-1 + else if (sacrifice.stat == DEAD) + .=1 + + switch(.) + if (1) + finalize_sacrifice() + if (-1) + speak_to(sacrificer, "Fool! Your offering has escaped. Bring it back, or find us another...") + sacrifice = null + pylonmode = 0 + update_icon() + if (sacrifice) + walk_to(sacrifice,0) + else + if (istype(sacrifice.loc, /turf) && !(sacrifice.is_ventcrawling) && !(sacrifice.buckled)) + //Suck the creature towards the pylon if possible + walk_towards(sacrifice,src, 10) + else + walk_to(sacrifice,0) //If we're not in a valid situation, cancel walking to prevent bugginess + +/obj/structure/cult/pylon/proc/finalize_sacrifice() + sacrifice.visible_message(span("danger","\The [sacrifice]'s physical form unwinds as its soul is extracted from the remains, and drawn into the pylon!")) + if (istype(sacrifice.loc,/obj/item/holder)) + var/obj/item/holder/H = sacrifice.loc + H.release_to_floor() + else + sacrifice.forceMove(get_turf(sacrifice)) //Make sure its on the floor before we gib it + pylonmode = 2 + sacrifice.gib() + + update_icon() + + +//Called every process in turret mode, and also by chaining spawns +/obj/structure/cult/pylon/proc/handle_firing() + + if ((world.time < next_shot) || isbroken) + return + //Not ready to fire yet + + + var/list/stuffcache + //Stuffcache holds a list of things found by a dview call, so we only need to do it once per proc. + + + if (target && (target.stat != DEAD)) + if (target.loc == last_target_loc) + //To minimise expensive DVIEW calls, if the target hasnt moved since the last shot we'll assume they're still in sight + //This could cause problems in some edge cases (like lowering firelocks or shutters without moving) + //But it has a major performance gain that makes it worth it + fire_at(target) + return + else + stuffcache = mobs_in_view(9, src) + //for (var/turf/T in stuffcache) + //new /obj/effect/testtrans(T) + if ((target in stuffcache) && isInSight(src, target)) + for (var/mob/m in stuffcache) + fire_at(target) + return + else + target = null + + //We may have already populated stuffcache this run, dont repeat work + if (!stuffcache) + stuffcache = mobs_in_view(9, src) + + target = null //Either we lost a target or lack one + //for (var/turf/T in stuffcache) + //new /obj/effect/testtrans(T) + + for (var/mob/living/L in stuffcache) + if (L == sacrificer) + continue + //A pylon won't shoot the person who created it, regardless of cult status. + //This is mainly for xenoarch antags + + if (L.stat == DEAD) + continue + //No point shooting at corpses + + if (iscult(L)) + continue + //Pylon wont shoot at cultists or constructs + + if (!isInSight(src, L)) + continue + + target = L + + + break + + if (target) + fire_at(target) + else + notarget++ + if (notarget > 60 || process_interval > 1) + notarget = 0 + reconsider_interval() + + +/obj/structure/cult/pylon/proc/fire_at(var/atom/target) + //Only store loc if target is on a turf. Otherwise this bugs out with people in exosuits + if (istype(target.loc, /turf)) + last_target_loc = target.loc + + process_interval = 1 //Instantly wake up if we found a target + var/obj/item/projectile/beam/cult/A + if (empowered > 0) + A = new /obj/item/projectile/beam/cult/heavy(loc) + empowered = max(0, empowered-1) + playsound(loc, 'sound/weapons/laserdeep.ogg', 100, 1) + if (empowered <= 0) + update_icon() + else + A = new /obj/item/projectile/beam/cult(loc) + playsound(loc, 'sound/weapons/laserdeep.ogg', 65, 1) + A.ignore = sacrificer + A.launch_projectile(target) + next_shot = world.time + shot_delay + A = null //So projectiles can GC + spawn(shot_delay+1) + handle_firing() + + + +/obj/structure/cult/pylon/attack_hand(mob/M as mob) + if (M.a_intent == "help") + to_chat(M, "The pylon feels warm to the touch....") + else + attackpylon(M, 4, M) + +/obj/structure/cult/pylon/attack_generic(var/mob/user, var/damage) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + + //Artificiers maintain pylons + if(istype(user, /mob/living/simple_animal/construct)) + var/mob/living/simple_animal/construct/C = user + if (C.can_repair) + repair(user) + return + attackpylon(user, damage, user) + +/obj/structure/cult/pylon/attackby(obj/item/W as obj, mob/user as mob) + if (istype(W, /obj/item/holder)) + var/obj/item/holder/H = W + if (H.contained) + present_sacrifice(user, H.contained) + return + + attackpylon(user, W.force, W) + + +//Mousedrop so that constructs can drag rats out of maintenance to make turrets +/obj/structure/cult/pylon/MouseDrop_T(var/atom/movable/C, mob/user) + if (istype(C, /mob/living)) + present_sacrifice(user, C) + return + return ..() + +/obj/structure/cult/pylon/bullet_act(var/obj/item/projectile/Proj) + attackpylon(Proj.firer, Proj.damage, Proj) + +//Explosions will usually cause instant shattering, or heavy damage +//Class 3 or lower blast is sometimes survivable. 2 or higher will always shatter +/obj/structure/cult/pylon/ex_act(var/severity) + if (severity > 0) + attackpylon(null, (rand(200,400)/severity), null) + +/obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage, var/source) + var/ranged = 0 + if (user) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if (istype(source, /obj/item)) + var/obj/item/I = source + if(istype(I, /obj/item/nullrod)) + shatter() + return + if (I.damtype != BRUTE) + to_chat(user, "You swing at the pylon to no effect.") + return + + if (istype(source, /obj/item/projectile)) + if (istype(source, /obj/item/projectile/beam/cult)) + return //No feedback loops + var/obj/item/projectile/proj = source + if (proj.damage_type == BURN) + if (empowered <= 0) + visible_message( + "The beam refracts inside the pylon, splitting into an indistinct violet glow. The crystal takes on a new, more ominous aura!" + ) + empowered += damage*0.2 + //When shot with a laser, the pylon absorbs the beam, becoming empowered for a while, glowing brighter + // and firing more powerful blasts which have some armor penetration + // Using lasers to empower a defensive pylon yields more total damage than directly shooting your enemies + + start_process() + update_icon() + return + else if (proj.damage_type != BRUTE) + return + ranged = 1 + + if (!damage) + return + + damagetaken += damage + + if(!isbroken) + if (user) + user.do_attack_animation(src) + if(prob(damagetaken*0.75)) + shatter() + else + if (user && !ranged) + to_chat(user, "You hit the pylon!") + playsound(get_turf(src), 'sound/effects/Glasshit.ogg', 75, 1) + else + if(prob(damagetaken)) + if (user) + to_chat(user, "You pulverize what was left of the pylon!") + qdel(src) + else if (user && !ranged) + to_chat(user, "You hit the pylon!") + playsound(get_turf(src), 'sound/effects/Glasshit.ogg', 75, 1) + + start_process() + +/obj/structure/cult/pylon/proc/shatter() + visible_message( + "The pylon shatters into shards of crystal!", + "You hear a tinkle of crystal shards" + ) + playsound(get_turf(src), 'sound/effects/Glassbr3.ogg', 75, 1) + isbroken = 1 + if (pylonmode == 2) + //If the pylon had a soul in it then it plays a creepy evil sound as the soul is released + var/possibles = list('sound/hallucinations/far_noise.ogg', + 'sound/hallucinations/growl3.ogg', + 'sound/hallucinations/veryfar_noise.ogg', + 'sound/hallucinations/wail.ogg', + 'sound/voice/hiss5.ogg') + playsound(get_turf(src), pick(possibles), 50, 1) + + pylonmode = 0 //A broken pylon loses its soul. Even if repaired it will need a new sacrifice to re-empower it + + //Make sure we stop it from firing + target = null + sacrificer = null + notarget = 0 + last_target_loc = null + process_interval = 1 + //It will stop processing after the next check, start of the next process + + empowered = 0 + density = 0 + update_icon() + + + +/obj/structure/cult/pylon/proc/repair(mob/user as mob) + if(isbroken) + if(user) + to_chat(user, span("notice","You weave forgotten magic, summoning the shards of the crystal and knitting them anew, until it hovers flawless once more.")) + isbroken = 0 + density = 1 + else if (damagetaken > 0) + to_chat(user, span("notice","You meld the crystal lattice back into integrity, sealing over the cracks until they never were.")) + + else + to_chat(user, span("notice","The crystal lights up at your touch.")) + + process_interval = 1 //Wake up the crystal + notarget = 0 + damagetaken = 0 + update_icon() + + +/obj/structure/cult/pylon/update_icon() + cut_overlays() + if (pylonmode == 2) + anchored = 1 + if (empowered > 0) + add_overlay("crystal_overcharge") + set_light(7, 3, l_color = "#a160bf") + else + set_light(6, 3, l_color = "#3e0000") + add_overlay("crystal_turret") + else if (!isbroken) + set_light(5, 2, l_color = "#3e0000") + add_overlay("crystal_idle") + if (pylonmode == 1) + anchored = 1 + else + anchored = 0 + else + anchored = 0 + set_light(0) + + + +//============================================ +/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" + +/obj/effect/gateway + name = "gateway" + desc = "You're pretty sure that abyss is staring back" + icon = 'icons/obj/cult.dmi' + icon_state = "hole" + density = 1 + unacidable = 1 + anchored = 1.0 + var/spawnable = null + +/obj/effect/gateway/CollidedWith(mob/M) + return + +/obj/effect/gateway/Crossed(AM as mob|obj) + return + +/obj/effect/gateway/active + light_range=5 + light_color="#ff0000" + spawnable=list( + /mob/living/simple_animal/hostile/scarybat, + /mob/living/simple_animal/hostile/creature, + /mob/living/simple_animal/hostile/faithless + ) + +/obj/effect/gateway/active/cult + light_range=5 + light_color="#ff0000" + spawnable=list( + /mob/living/simple_animal/hostile/scarybat/cult, + /mob/living/simple_animal/hostile/creature/cult, + /mob/living/simple_animal/hostile/faithless/cult + ) + appearance_flags = NO_CLIENT_COLOR + +/obj/effect/gateway/active/cult/cultify() + return + +/obj/effect/gateway/active/New() + addtimer(CALLBACK(src, .proc/do_spawn), rand(30, 60) SECONDS) + +/obj/effect/gateway/active/proc/do_spawn() + var/thing = pick(spawnable) + new thing(src.loc) + qdel(src) + +/obj/effect/gateway/attackby(var/obj/item/I, var/mob/user) + ..() + if(istype(I, /obj/item/nullrod)) + to_chat(user, "You touch \the [src] with \the [I], closing the path to the otherworld.") + qdel(src) + +/obj/effect/testtrans + icon = 'icons/obj/cult.dmi' + icon_state = "tt" + +/obj/effect/testtrans/New() + ..() + spawn(10) + qdel(src) diff --git a/code/game/gamemodes/cult/cultify/mob.dm b/code/game/gamemodes/cult/cultify/mob.dm index 1548071a703..b2aab9cdabc 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/abstract/observer/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_animal/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/abstract/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/abstract/observer/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_animal/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/abstract/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 577a1dfc2a0..1dac326d067 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/book/cultify() - new /obj/item/book/tome(loc) - ..() - -/obj/item/material/sword/cultify() - new /obj/item/melee/cultblade(loc) - ..() - -/obj/item/storage/backpack/cultify() - new /obj/item/storage/backpack/cultpack(loc) - ..() - -/obj/item/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 = 0 - -/obj/machinery/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 = 0 - 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/book/cultify() + new /obj/item/book/tome(loc) + ..() + +/obj/item/material/sword/cultify() + new /obj/item/melee/cultblade(loc) + ..() + +/obj/item/storage/backpack/cultify() + new /obj/item/storage/backpack/cultpack(loc) + ..() + +/obj/item/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 = 0 + +/obj/machinery/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 = 0 + 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 b4f868870e3..4b9d2e23826 100644 --- a/code/game/gamemodes/cult/cultify/turf.dm +++ b/code/game/gamemodes/cult/cultify/turf.dm @@ -1,41 +1,41 @@ -/turf/proc/cultify() - ChangeTurf(/turf/space) - return - -/turf/simulated/floor/cultify() - //todo: flooring datum cultify check - cultify_floor() - -/turf/simulated/wall/cultify() - cultify_wall() - -/turf/simulated/wall/cult/cultify() - return - -/turf/unsimulated/wall/cult/cultify() - return - -/turf/unsimulated/beach/cultify() - return - -/turf/simulated/open/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")) - icon = 'icons/turf/flooring/cult.dmi' - icon_state = "cult" - pixel_x = 0 - pixel_y = 0 - if(istype(src,/turf/simulated/floor)) - var/turf/simulated/floor/F = src - F.set_flooring(get_flooring_data(/decl/flooring/reinforced/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/wall/cultify() + cultify_wall() + +/turf/simulated/wall/cult/cultify() + return + +/turf/unsimulated/wall/cult/cultify() + return + +/turf/unsimulated/beach/cultify() + return + +/turf/simulated/open/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")) + icon = 'icons/turf/flooring/cult.dmi' + icon_state = "cult" + pixel_x = 0 + pixel_y = 0 + if(istype(src,/turf/simulated/floor)) + var/turf/simulated/floor/F = src + F.set_flooring(get_flooring_data(/decl/flooring/reinforced/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 369498d1daa..1f6eda7e2e8 100644 --- a/code/game/gamemodes/cult/hell_universe.dm +++ b/code/game/gamemodes/cult/hell_universe.dm @@ -1,124 +1,124 @@ -/* - -In short: - * Random area alarms - * All areas jammed - * Random gateways spawning hellmonsters (and turn people into cluwnes if ran into) - * Broken APCs/Fire Alarms - * Scary music - * 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() - SSgarbage.disable() // Yeah, fuck it. No point hard-deleting stuff now. - - escape_list = get_area_turfs(locate(/area/hallway/secondary/exit)) - - convert_all_parallax() - //Separated into separate procs for profiling - AreaSet() - MiscSet() - APCSet() - KillMobs() - OverlayAndAmbientSet() - - runedec += 9000 //basically removing the rune cap - -/datum/universal_state/hell/proc/AreaSet() - for(var/area/A in all_areas) - if(!istype(A,/area) || istype(A, /area/space)) - continue - - A.queue_icon_update() - CHECK_TICK - -/datum/universal_state/hell/OverlayAndAmbientSet() - set waitfor = FALSE - for(var/thing in turfs) // Expensive, but CHECK_TICK should prevent lag. - var/turf/T = thing - if(istype(T, /turf/space)) - T.add_overlay("hell01") - else - var/static/image/I = image('icons/turf/space.dmi', "hell01") - T.underlays += I - - if (istype(T, /turf/simulated/floor) && !T.holy && prob(1)) - new /obj/effect/gateway/active/cult(T) - - CHECK_TICK - - for(var/datum/lighting_corner/C in SSlighting.lighting_corners) - if (!C.active) - continue - - C.update_lumcount(0.5, 0, 0) - CHECK_TICK - -/datum/universal_state/hell/proc/MiscSet() - for (var/obj/machinery/firealarm/alm in SSmachinery.processing_machines) - if (!(alm.stat & BROKEN)) - alm.ex_act(2) - CHECK_TICK - -/datum/universal_state/hell/proc/APCSet() - for (var/obj/machinery/power/apc/APC in SSmachinery.processing_machines) - if (!(APC.stat & BROKEN) && !APC.is_critical) - APC.chargemode = 0 - if(APC.cell) - APC.cell.charge = 0 - APC.emagged = 1 - APC.queue_icon_update() - CHECK_TICK - -/datum/universal_state/hell/proc/KillMobs() - for(var/mob/living/simple_animal/M in mob_list) - if(M && !M.client) - M.stat = DEAD - CHECK_TICK - -// Parallax. - -/datum/universal_state/hell/convert_parallax(obj/screen/plane_master/parallax_spacemaster/PS) - PS.color = list( - 0,0,0,0, - 0,0,0,0, - 0,0,0,0, - 1,0,0,1) - -/datum/universal_state/hell/proc/convert_all_parallax() - for(var/client/C in clients) - var/obj/screen/plane_master/parallax_spacemaster/PS = locate() in C.screen - if(PS) - convert_parallax(PS) - CHECK_TICK +/* + +In short: + * Random area alarms + * All areas jammed + * Random gateways spawning hellmonsters (and turn people into cluwnes if ran into) + * Broken APCs/Fire Alarms + * Scary music + * 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() + SSgarbage.disable() // Yeah, fuck it. No point hard-deleting stuff now. + + escape_list = get_area_turfs(locate(/area/hallway/secondary/exit)) + + convert_all_parallax() + //Separated into separate procs for profiling + AreaSet() + MiscSet() + APCSet() + KillMobs() + OverlayAndAmbientSet() + + runedec += 9000 //basically removing the rune cap + +/datum/universal_state/hell/proc/AreaSet() + for(var/area/A in all_areas) + if(!istype(A,/area) || istype(A, /area/space)) + continue + + A.queue_icon_update() + CHECK_TICK + +/datum/universal_state/hell/OverlayAndAmbientSet() + set waitfor = FALSE + for(var/thing in turfs) // Expensive, but CHECK_TICK should prevent lag. + var/turf/T = thing + if(istype(T, /turf/space)) + T.add_overlay("hell01") + else + var/static/image/I = image('icons/turf/space.dmi', "hell01") + T.underlays += I + + if (istype(T, /turf/simulated/floor) && !T.holy && prob(1)) + new /obj/effect/gateway/active/cult(T) + + CHECK_TICK + + for(var/datum/lighting_corner/C in SSlighting.lighting_corners) + if (!C.active) + continue + + C.update_lumcount(0.5, 0, 0) + CHECK_TICK + +/datum/universal_state/hell/proc/MiscSet() + for (var/obj/machinery/firealarm/alm in SSmachinery.processing_machines) + if (!(alm.stat & BROKEN)) + alm.ex_act(2) + CHECK_TICK + +/datum/universal_state/hell/proc/APCSet() + for (var/obj/machinery/power/apc/APC in SSmachinery.processing_machines) + if (!(APC.stat & BROKEN) && !APC.is_critical) + APC.chargemode = 0 + if(APC.cell) + APC.cell.charge = 0 + APC.emagged = 1 + APC.queue_icon_update() + CHECK_TICK + +/datum/universal_state/hell/proc/KillMobs() + for(var/mob/living/simple_animal/M in mob_list) + if(M && !M.client) + M.stat = DEAD + CHECK_TICK + +// Parallax. + +/datum/universal_state/hell/convert_parallax(obj/screen/plane_master/parallax_spacemaster/PS) + PS.color = list( + 0,0,0,0, + 0,0,0,0, + 0,0,0,0, + 1,0,0,1) + +/datum/universal_state/hell/proc/convert_all_parallax() + for(var/client/C in clients) + var/obj/screen/plane_master/parallax_spacemaster/PS = locate() in C.screen + if(PS) + convert_parallax(PS) + CHECK_TICK diff --git a/code/game/gamemodes/cult/narsie.dm b/code/game/gamemodes/cult/narsie.dm index e1129649ea1..008dbbfc497 100644 --- a/code/game/gamemodes/cult/narsie.dm +++ b/code/game/gamemodes/cult/narsie.dm @@ -1,395 +1,395 @@ -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) - return ..() - -/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(loc) - ..() - if(announce) - to_world("[uppertext(name)] HAS RISEN") - to_world(sound('sound/effects/narsie.ogg')) - - narsie_spawn_animation() - - log_and_message_admins("Narsie has been spawned.", location = get_turf(loc)) - - 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) - CHECK_TICK - -/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/Collide(atom/A) - . = ..() - if(!cause_hell) return - if(isturf(A)) - narsiewall(A) - else if(istype(A, /obj/structure/cult)) - qdel(A) - -/obj/singularity/narsie/large/CollidedWith(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) //moves to a singulo beacon, if there is one - if(target.z < z) - visible_message("\The [src] descends ominously.") - zMove(DOWN) - visible_message("\The [src] appears from on high.") - else if(target.z > z) - visible_message("\The [src] ascends ominously.") - zMove(UP) - visible_message("\The [src] claws its way up from below.") - - 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) //moves to a singulo beacon, if there is one - if(target.z < z) - visible_message("\The [src] descends ominously.") - zMove(DOWN) - visible_message("\The [src] appears from on high.") - else if(target.z > z) - visible_message("\The [src] ascends ominously.") - zMove(UP) - visible_message("\The [src] claws its way up from below.") - - 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 (QDELETED(T)) - return - 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.set_opacity(0) - T.density = 0 - 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) - CHECK_TICK - 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 - - CHECK_TICK - - 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/turf/T2 = A - var/dist = get_dist(T2, src) - - for (var/atom/movable/AM2 in T2.contents) - if (AM2 == src) // This is the snowflake. - continue - - if (dist <= consume_range) - consume(AM2) - continue - - if (dist <= consume_range && !istype(T2, T2.baseturf)) - T2.ChangeTurf(T2.baseturf) - -/obj/singularity/narsie/consume(const/atom/A) //This one is for the small ones. - if(!(A.singuloCanEat())) - return 0 - - CHECK_TICK - - 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/turf/T2 = A - var/dist = get_dist(T2, src) - - for (var/atom/movable/AM2 in T2.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(T2, T2.baseturf)) - T2.ChangeTurf(T2.baseturf) - -/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/abstract/observer/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 RANGE_TURFS(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) + return ..() + +/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(loc) + ..() + if(announce) + to_world("[uppertext(name)] HAS RISEN") + to_world(sound('sound/effects/narsie.ogg')) + + narsie_spawn_animation() + + log_and_message_admins("Narsie has been spawned.", location = get_turf(loc)) + + 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) + CHECK_TICK + +/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/Collide(atom/A) + . = ..() + if(!cause_hell) return + if(isturf(A)) + narsiewall(A) + else if(istype(A, /obj/structure/cult)) + qdel(A) + +/obj/singularity/narsie/large/CollidedWith(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) //moves to a singulo beacon, if there is one + if(target.z < z) + visible_message("\The [src] descends ominously.") + zMove(DOWN) + visible_message("\The [src] appears from on high.") + else if(target.z > z) + visible_message("\The [src] ascends ominously.") + zMove(UP) + visible_message("\The [src] claws its way up from below.") + + 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) //moves to a singulo beacon, if there is one + if(target.z < z) + visible_message("\The [src] descends ominously.") + zMove(DOWN) + visible_message("\The [src] appears from on high.") + else if(target.z > z) + visible_message("\The [src] ascends ominously.") + zMove(UP) + visible_message("\The [src] claws its way up from below.") + + 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 (QDELETED(T)) + return + 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.set_opacity(0) + T.density = 0 + 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) + CHECK_TICK + 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 + + CHECK_TICK + + 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/turf/T2 = A + var/dist = get_dist(T2, src) + + for (var/atom/movable/AM2 in T2.contents) + if (AM2 == src) // This is the snowflake. + continue + + if (dist <= consume_range) + consume(AM2) + continue + + if (dist <= consume_range && !istype(T2, T2.baseturf)) + T2.ChangeTurf(T2.baseturf) + +/obj/singularity/narsie/consume(const/atom/A) //This one is for the small ones. + if(!(A.singuloCanEat())) + return 0 + + CHECK_TICK + + 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/turf/T2 = A + var/dist = get_dist(T2, src) + + for (var/atom/movable/AM2 in T2.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(T2, T2.baseturf)) + T2.ChangeTurf(T2.baseturf) + +/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/abstract/observer/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 RANGE_TURFS(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 550e420fd06..07b18f0dc47 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -1,594 +1,594 @@ -//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 = 1 - icon = 'icons/obj/rune.dmi' - icon_state = "1" - var/visibility = 0 - unacidable = 1 - layer = AO_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 += 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 -= src - return ..() - -/obj/effect/rune/examine(mob/user) - ..() - if(iscultist(user)) - to_chat(user, "This spell circle reads: [word1] [word2] [word3].") - -/obj/effect/rune/attackby(I as obj, user as mob) - if(istype(I, /obj/item/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/nullrod)) - to_chat(user, "You disrupt the vile magic with the deadening field of \the [I]!") - qdel(src) - return - return - - -/obj/effect/rune/attack_hand(var/mob/living/user) - if(!iscultist(user)) - to_chat(user, "You can't mouth the arcane scratchings without fumbling over them.") - return - if(istype(user.wear_mask, /obj/item/clothing/mask/muzzle)) - to_chat(user, "You are unable to speak the words of the rune.") - return - if(!word1 || !word2 || !word3 || prob(user.getBrainLoss())) - return fizzle(user) - //if(!src.visibility) - //src.visibility=1 - if(word1 == cultwords["travel"] && word2 == cultwords["self"]) - return teleport(user,src.word3) - if(word1 == cultwords["see"] && word2 == cultwords["blood"] && word3 == cultwords["hell"]) - return tomesummon(user) - if(word1 == cultwords["hell"] && word2 == cultwords["destroy"] && word3 == cultwords["other"]) - return armor(user) - if(word1 == cultwords["join"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) - return convert(user) - if(word1 == cultwords["hell"] && word2 == cultwords["join"] && word3 == cultwords["self"]) - return tearreality(user) - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["technology"]) - return emp(user, src.loc,3) - if(word1 == cultwords["travel"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) - return drain(user) - if(word1 == cultwords["see"] && word2 == cultwords["hell"] && word3 == cultwords["join"]) - return seer(user) - if(word1 == cultwords["blood"] && word2 == cultwords["join"] && word3 == cultwords["hell"]) - return raise(user) - if(word1 == cultwords["hide"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) - return obscure(user, 4) - if(word1 == cultwords["hell"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) - return ajourney(user) - if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["travel"]) - return manifest(user) - if(word1 == cultwords["hell"] && word2 == cultwords["technology"] && word3 == cultwords["join"]) - return talisman(user) - if(word1 == cultwords["hell"] && word2 == cultwords["blood"] && word3 == cultwords["join"]) - return sacrifice(user) - if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["hide"]) - return revealrunes(user, src) - if(word1 == cultwords["destroy"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) - return wall(user) - if(word1 == cultwords["travel"] && word2 == cultwords["technology"] && word3 == cultwords["other"]) - return freedom(user) - if(word1 == cultwords["join"] && word2 == cultwords["other"] && word3 == cultwords["self"]) - return cultsummon(user) - if(word1 == cultwords["hide"] && word2 == cultwords["other"] && word3 == cultwords["see"]) - return deafen(user) - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["other"]) - return blind(user) - if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) - return bloodboil(user) - if(word1 == cultwords["self"] && word2 == cultwords["other"] && word3 == cultwords["technology"]) - return communicate(user) - if(word1 == cultwords["travel"] && word2 == cultwords["other"]) - return itemport(user, src.word3) - if(word1 == cultwords["join"] && word2 == cultwords["hide"] && word3 == cultwords["technology"]) - return runestun(user) - else - return fizzle(user) - - -/obj/effect/rune/proc/fizzle(var/mob/living/user) - if(istype(src,/obj/effect/rune)) - user.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - else - user.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/book/tome - name = "arcane tome" - icon = 'icons/obj/library.dmi' - icon_state ="tome" - throw_speed = 1 - throw_range = 5 - w_class = 2.0 - 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 analogous 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, disable 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 and not buckled to anything. You also need to have 3 people chanting at the rune to successfully 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 successfully invoked.
    -

    Communicate

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

    Stun

    - When invoked directly as a rune, it releases some dark energy, briefly stunning everyone around. When imbued into a talisman, you can force some dark energy into a person, causing their eyes to flash, and their words to falter, keeping them quiet. However, the 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 world beyond as long as he does not move.
    - - - "} - - -/obj/item/book/tome/Initialize() - . = ..() - if(!cultwords["travel"]) - runerandom() - for(var/V in cultwords) - words[cultwords[V]] = V - - - -/obj/item/book/tome/attack(mob/living/M as mob, mob/living/user as mob) - - M.attack_log += text("\[[time_stamp()]\] Has had the [name] used on them by [user.name] ([user.ckey])") - user.attack_log += text("\[[time_stamp()]\] Used [name] on [M.name] ([M.ckey])") - msg_admin_attack("[key_name_admin(user)] used [name] on [M.name] ([M.ckey]) (JMP)",ckey=key_name(user),ckey_target=key_name(M)) - - if(isobserver(M)) - var/mob/abstract/observer/D = M - D.manifest(user) - return - - if(!istype(M)) - return - - if(!iscultist(user)) - return ..() - - if(iscultist(M)) - return - - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - 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/book/tome/attack_self(mob/living/user as mob) - - if(!user.canmove || user.stat || user.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 - - var/turf/T = get_turf(user) - - if (T.is_hole || T.is_space()) - to_chat(user, "You are unable to write a rune here.") - 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 - alert("The cloth of reality can't take that much of a strain. Remove some runes first!") - return - - else - switch(alert("You open the tome",,"Read it","Scribe a rune", "Cancel")) - if("Cancel") - return - if("Read it") - if(user.get_active_hand() != src) - return - user << browse("[tomedat]", "window=Arcane Tome") - return - //only check if they want to scribe a rune, so they can still read if standing on a rune - if(locate(/obj/effect/rune) in user.loc) - to_chat(user, "There is already a rune in this location.") - return - - if(isipc(user)) - to_chat(user, "You cannot draw runes, as you have no blood.") - return - - if(user.get_active_hand() != src) - return - - var/list/static/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) - scribewords += entry - - var/chosen_rune = null - var/list/required = null - if(user) - 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 - required = dictionary[chosen_rune].Copy() - if(chosen_rune == "teleport" || chosen_rune == "teleport other") - required += input ("Choose a destination word") in english - - if(user.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) - - if(user.get_active_hand() != src) - return - - //prevents using multiple dialogs to layer runes. - if(locate(/obj/effect/rune) in user.loc) //This is check is done twice. once when choosing to scribe a rune, once here - to_chat(user, "There is already a rune in this location.") - return - - log_and_message_admins("created \an [chosen_rune] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].") //only message if it's actually made - - 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.") - 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.") - return - -/obj/item/book/tome/examine(mob/user) - ..(user) - if(!iscultist(user)) - desc = "An old, dusty tome with frayed edges and a sinister looking cover." - else - desc = "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/book/tome/cultify() - return - -/obj/item/book/tome/imbued //admin tome, spawns working runes without waiting - w_class = 2.0 - var/cultistsonly = 1 - attack_self(mob/user as mob) - if(src.cultistsonly && !iscultist(user)) - 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("Choose a rune to scribe", "Rune Scribing") in runes //not cancellable. - - var/obj/effect/rune/R = new /obj/effect/rune(user.loc) - 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(user) - beacon = input("Select the last rune", "Rune Scribing") in words - R.word1=cultwords["travel"] - R.word2=cultwords["self"] - R.word3=beacon - if("itemport") - var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") - var/beacon - if(user) - beacon = input("Select the last rune", "Rune Scribing") in words - R.word1=cultwords["travel"] - R.word2=cultwords["other"] - R.word3=beacon - if("tome") - R.word1=cultwords["see"] - R.word2=cultwords["blood"] - R.word3=cultwords["hell"] - if("armor") - R.word1=cultwords["hell"] - R.word2=cultwords["destroy"] - R.word3=cultwords["other"] - if("convert") - R.word1=cultwords["join"] - R.word2=cultwords["blood"] - R.word3=cultwords["self"] - if("tear in reality") - R.word1=cultwords["hell"] - R.word2=cultwords["join"] - R.word3=cultwords["self"] - if("emp") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["technology"] - if("drain") - R.word1=cultwords["travel"] - R.word2=cultwords["blood"] - R.word3=cultwords["self"] - if("seer") - R.word1=cultwords["see"] - R.word2=cultwords["hell"] - R.word3=cultwords["join"] - if("raise") - R.word1=cultwords["blood"] - R.word2=cultwords["join"] - R.word3=cultwords["hell"] - if("obscure") - R.word1=cultwords["hide"] - R.word2=cultwords["see"] - R.word3=cultwords["blood"] - if("astral journey") - R.word1=cultwords["hell"] - R.word2=cultwords["travel"] - R.word3=cultwords["self"] - if("manifest") - R.word1=cultwords["blood"] - R.word2=cultwords["see"] - R.word3=cultwords["travel"] - if("imbue talisman") - R.word1=cultwords["hell"] - R.word2=cultwords["technology"] - R.word3=cultwords["join"] - if("sacrifice") - R.word1=cultwords["hell"] - R.word2=cultwords["blood"] - R.word3=cultwords["join"] - if("reveal") - R.word1=cultwords["blood"] - R.word2=cultwords["see"] - R.word3=cultwords["hide"] - if("wall") - R.word1=cultwords["destroy"] - R.word2=cultwords["travel"] - R.word3=cultwords["self"] - if("freedom") - R.word1=cultwords["travel"] - R.word2=cultwords["technology"] - R.word3=cultwords["other"] - if("cultsummon") - R.word1=cultwords["join"] - R.word2=cultwords["other"] - R.word3=cultwords["self"] - if("deafen") - R.word1=cultwords["hide"] - R.word2=cultwords["other"] - R.word3=cultwords["see"] - if("blind") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["other"] - if("bloodboil") - R.word1=cultwords["destroy"] - R.word2=cultwords["see"] - R.word3=cultwords["blood"] - if("communicate") - R.word1=cultwords["self"] - R.word2=cultwords["other"] - R.word3=cultwords["technology"] - if("stun") - R.word1=cultwords["join"] - R.word2=cultwords["hide"] - R.word3=cultwords["technology"] - R.forceMove(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 = 1 + icon = 'icons/obj/rune.dmi' + icon_state = "1" + var/visibility = 0 + unacidable = 1 + layer = AO_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 += 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 -= src + return ..() + +/obj/effect/rune/examine(mob/user) + ..() + if(iscultist(user)) + to_chat(user, "This spell circle reads: [word1] [word2] [word3].") + +/obj/effect/rune/attackby(I as obj, user as mob) + if(istype(I, /obj/item/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/nullrod)) + to_chat(user, "You disrupt the vile magic with the deadening field of \the [I]!") + qdel(src) + return + return + + +/obj/effect/rune/attack_hand(var/mob/living/user) + if(!iscultist(user)) + to_chat(user, "You can't mouth the arcane scratchings without fumbling over them.") + return + if(istype(user.wear_mask, /obj/item/clothing/mask/muzzle)) + to_chat(user, "You are unable to speak the words of the rune.") + return + if(!word1 || !word2 || !word3 || prob(user.getBrainLoss())) + return fizzle(user) + //if(!src.visibility) + //src.visibility=1 + if(word1 == cultwords["travel"] && word2 == cultwords["self"]) + return teleport(user,src.word3) + if(word1 == cultwords["see"] && word2 == cultwords["blood"] && word3 == cultwords["hell"]) + return tomesummon(user) + if(word1 == cultwords["hell"] && word2 == cultwords["destroy"] && word3 == cultwords["other"]) + return armor(user) + if(word1 == cultwords["join"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) + return convert(user) + if(word1 == cultwords["hell"] && word2 == cultwords["join"] && word3 == cultwords["self"]) + return tearreality(user) + if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["technology"]) + return emp(user, src.loc,3) + if(word1 == cultwords["travel"] && word2 == cultwords["blood"] && word3 == cultwords["self"]) + return drain(user) + if(word1 == cultwords["see"] && word2 == cultwords["hell"] && word3 == cultwords["join"]) + return seer(user) + if(word1 == cultwords["blood"] && word2 == cultwords["join"] && word3 == cultwords["hell"]) + return raise(user) + if(word1 == cultwords["hide"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) + return obscure(user, 4) + if(word1 == cultwords["hell"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) + return ajourney(user) + if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["travel"]) + return manifest(user) + if(word1 == cultwords["hell"] && word2 == cultwords["technology"] && word3 == cultwords["join"]) + return talisman(user) + if(word1 == cultwords["hell"] && word2 == cultwords["blood"] && word3 == cultwords["join"]) + return sacrifice(user) + if(word1 == cultwords["blood"] && word2 == cultwords["see"] && word3 == cultwords["hide"]) + return revealrunes(user, src) + if(word1 == cultwords["destroy"] && word2 == cultwords["travel"] && word3 == cultwords["self"]) + return wall(user) + if(word1 == cultwords["travel"] && word2 == cultwords["technology"] && word3 == cultwords["other"]) + return freedom(user) + if(word1 == cultwords["join"] && word2 == cultwords["other"] && word3 == cultwords["self"]) + return cultsummon(user) + if(word1 == cultwords["hide"] && word2 == cultwords["other"] && word3 == cultwords["see"]) + return deafen(user) + if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["other"]) + return blind(user) + if(word1 == cultwords["destroy"] && word2 == cultwords["see"] && word3 == cultwords["blood"]) + return bloodboil(user) + if(word1 == cultwords["self"] && word2 == cultwords["other"] && word3 == cultwords["technology"]) + return communicate(user) + if(word1 == cultwords["travel"] && word2 == cultwords["other"]) + return itemport(user, src.word3) + if(word1 == cultwords["join"] && word2 == cultwords["hide"] && word3 == cultwords["technology"]) + return runestun(user) + else + return fizzle(user) + + +/obj/effect/rune/proc/fizzle(var/mob/living/user) + if(istype(src,/obj/effect/rune)) + user.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) + else + user.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/book/tome + name = "arcane tome" + icon = 'icons/obj/library.dmi' + icon_state ="tome" + throw_speed = 1 + throw_range = 5 + w_class = 2.0 + 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 analogous 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, disable 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 and not buckled to anything. You also need to have 3 people chanting at the rune to successfully 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 successfully invoked.
    +

    Communicate

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

    Stun

    + When invoked directly as a rune, it releases some dark energy, briefly stunning everyone around. When imbued into a talisman, you can force some dark energy into a person, causing their eyes to flash, and their words to falter, keeping them quiet. However, the 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 world beyond as long as he does not move.
    + + + "} + + +/obj/item/book/tome/Initialize() + . = ..() + if(!cultwords["travel"]) + runerandom() + for(var/V in cultwords) + words[cultwords[V]] = V + + + +/obj/item/book/tome/attack(mob/living/M as mob, mob/living/user as mob) + + M.attack_log += text("\[[time_stamp()]\] Has had the [name] used on them by [user.name] ([user.ckey])") + user.attack_log += text("\[[time_stamp()]\] Used [name] on [M.name] ([M.ckey])") + msg_admin_attack("[key_name_admin(user)] used [name] on [M.name] ([M.ckey]) (JMP)",ckey=key_name(user),ckey_target=key_name(M)) + + if(isobserver(M)) + var/mob/abstract/observer/D = M + D.manifest(user) + return + + if(!istype(M)) + return + + if(!iscultist(user)) + return ..() + + if(iscultist(M)) + return + + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + 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/book/tome/attack_self(mob/living/user as mob) + + if(!user.canmove || user.stat || user.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 + + var/turf/T = get_turf(user) + + if (T.is_hole || T.is_space()) + to_chat(user, "You are unable to write a rune here.") + 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 + alert("The cloth of reality can't take that much of a strain. Remove some runes first!") + return + + else + switch(alert("You open the tome",,"Read it","Scribe a rune", "Cancel")) + if("Cancel") + return + if("Read it") + if(user.get_active_hand() != src) + return + user << browse("[tomedat]", "window=Arcane Tome") + return + //only check if they want to scribe a rune, so they can still read if standing on a rune + if(locate(/obj/effect/rune) in user.loc) + to_chat(user, "There is already a rune in this location.") + return + + if(isipc(user)) + to_chat(user, "You cannot draw runes, as you have no blood.") + return + + if(user.get_active_hand() != src) + return + + var/list/static/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) + scribewords += entry + + var/chosen_rune = null + var/list/required = null + if(user) + 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 + required = dictionary[chosen_rune].Copy() + if(chosen_rune == "teleport" || chosen_rune == "teleport other") + required += input ("Choose a destination word") in english + + if(user.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) + + if(user.get_active_hand() != src) + return + + //prevents using multiple dialogs to layer runes. + if(locate(/obj/effect/rune) in user.loc) //This is check is done twice. once when choosing to scribe a rune, once here + to_chat(user, "There is already a rune in this location.") + return + + log_and_message_admins("created \an [chosen_rune] rune at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].") //only message if it's actually made + + 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.") + 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.") + return + +/obj/item/book/tome/examine(mob/user) + ..(user) + if(!iscultist(user)) + desc = "An old, dusty tome with frayed edges and a sinister looking cover." + else + desc = "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/book/tome/cultify() + return + +/obj/item/book/tome/imbued //admin tome, spawns working runes without waiting + w_class = 2.0 + var/cultistsonly = 1 + attack_self(mob/user as mob) + if(src.cultistsonly && !iscultist(user)) + 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("Choose a rune to scribe", "Rune Scribing") in runes //not cancellable. + + var/obj/effect/rune/R = new /obj/effect/rune(user.loc) + 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(user) + beacon = input("Select the last rune", "Rune Scribing") in words + R.word1=cultwords["travel"] + R.word2=cultwords["self"] + R.word3=beacon + if("itemport") + var/list/words = list("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") + var/beacon + if(user) + beacon = input("Select the last rune", "Rune Scribing") in words + R.word1=cultwords["travel"] + R.word2=cultwords["other"] + R.word3=beacon + if("tome") + R.word1=cultwords["see"] + R.word2=cultwords["blood"] + R.word3=cultwords["hell"] + if("armor") + R.word1=cultwords["hell"] + R.word2=cultwords["destroy"] + R.word3=cultwords["other"] + if("convert") + R.word1=cultwords["join"] + R.word2=cultwords["blood"] + R.word3=cultwords["self"] + if("tear in reality") + R.word1=cultwords["hell"] + R.word2=cultwords["join"] + R.word3=cultwords["self"] + if("emp") + R.word1=cultwords["destroy"] + R.word2=cultwords["see"] + R.word3=cultwords["technology"] + if("drain") + R.word1=cultwords["travel"] + R.word2=cultwords["blood"] + R.word3=cultwords["self"] + if("seer") + R.word1=cultwords["see"] + R.word2=cultwords["hell"] + R.word3=cultwords["join"] + if("raise") + R.word1=cultwords["blood"] + R.word2=cultwords["join"] + R.word3=cultwords["hell"] + if("obscure") + R.word1=cultwords["hide"] + R.word2=cultwords["see"] + R.word3=cultwords["blood"] + if("astral journey") + R.word1=cultwords["hell"] + R.word2=cultwords["travel"] + R.word3=cultwords["self"] + if("manifest") + R.word1=cultwords["blood"] + R.word2=cultwords["see"] + R.word3=cultwords["travel"] + if("imbue talisman") + R.word1=cultwords["hell"] + R.word2=cultwords["technology"] + R.word3=cultwords["join"] + if("sacrifice") + R.word1=cultwords["hell"] + R.word2=cultwords["blood"] + R.word3=cultwords["join"] + if("reveal") + R.word1=cultwords["blood"] + R.word2=cultwords["see"] + R.word3=cultwords["hide"] + if("wall") + R.word1=cultwords["destroy"] + R.word2=cultwords["travel"] + R.word3=cultwords["self"] + if("freedom") + R.word1=cultwords["travel"] + R.word2=cultwords["technology"] + R.word3=cultwords["other"] + if("cultsummon") + R.word1=cultwords["join"] + R.word2=cultwords["other"] + R.word3=cultwords["self"] + if("deafen") + R.word1=cultwords["hide"] + R.word2=cultwords["other"] + R.word3=cultwords["see"] + if("blind") + R.word1=cultwords["destroy"] + R.word2=cultwords["see"] + R.word3=cultwords["other"] + if("bloodboil") + R.word1=cultwords["destroy"] + R.word2=cultwords["see"] + R.word3=cultwords["blood"] + if("communicate") + R.word1=cultwords["self"] + R.word2=cultwords["other"] + R.word3=cultwords["technology"] + if("stun") + R.word1=cultwords["join"] + R.word2=cultwords["hide"] + R.word3=cultwords["technology"] + R.forceMove(user.loc) + R.check_icon() diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index 09bf9292d06..af165b609fa 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -1,1144 +1,1144 @@ -var/list/sacrificed = list() - -/obj/effect/rune/cultify() - return - -/obj/effect/rune -/* - * Use as a general guideline for this and related files: - * * ... - when something non-trivial or an error happens, so something similar to "Sparks come out of the machine!" - * * ... - when something that is fit for 'warning' happens but there is some damage or pain as well. - * * ... - when there is a private message to the cultists. This guideline is very arbitrary but there has to be some consistency! - */ - - -/////////////////////////////////////////FIRST RUNE -/obj/effect/rune/proc/teleport(var/mob/living/user, var/key) - var/allrunesloc[] - allrunesloc = new/list() - var/index = 0 -// var/tempnum = 0 - for(var/obj/effect/rune/R in rune_list) - if(R == src) - continue - if(R.word1 == cultwords["travel"] && R.word2 == cultwords["self"] && R.word3 == key && isStationLevel(R.z)) - index++ - allrunesloc.len = index - allrunesloc[index] = R.loc - if(index >= 5) - to_chat(user, "You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric.") - if (istype(user, /mob/living)) - user.take_overall_damage(5, 0) - qdel(src) - - var/turf/T = get_turf(user) - if(isNotStationLevel(T.z)) - to_chat(user, "You are too far from the station, Nar'sie is unable to reach you here.") - return fizzle(user) - - if(allrunesloc && index != 0) - if(istype(src,/obj/effect/rune)) - user.say("Sas[pick("'","`")]so c'arta forbici!")//Only you can stop auto-muting - else - user.whisper("Sas[pick("'","`")]so c'arta forbici!") - user.visible_message("[user] disappears in a flash of red light!", \ - "You feel as your body gets dragged through the dimension of Nar-Sie!", \ - "You hear a sickening crunch and sloshing of viscera.") - user.forceMove(allrunesloc[rand(1,index)]) - return - if(istype(src,/obj/effect/rune)) - return fizzle(user) //Use friggin manuals, Dorf, your list was of zero length. - else - call(/obj/effect/rune/proc/fizzle)(user) - return - - -/obj/effect/rune/proc/itemport(var/mob/living/user, var/key) -// var/allrunesloc[] -// allrunesloc = new/list() -// var/index = 0 -// var/tempnum = 0 - var/culcount = 0 - var/runecount = 0 - var/obj/effect/rune/IP = null - for(var/obj/effect/rune/R in rune_list) - if(R == src) - continue - if(R.word1 == cultwords["travel"] && R.word2 == cultwords["other"] && R.word3 == key) - IP = R - runecount++ - if(runecount >= 2) - to_chat(user, "You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric.") - if (istype(user, /mob/living)) - user.take_overall_damage(5, 0) - qdel(src) - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - culcount++ - if(culcount>=3) - user.say("Sas[pick("'","`")]so c'arta forbici tarem!") - user.visible_message("You feel air moving from the rune - like as it was swapped with somewhere else.", \ - "You feel air moving from the rune - like as it was swapped with somewhere else.", \ - "You smell ozone.") - for(var/obj/O in src.loc) - if(!O.anchored) - O.forceMove(IP.loc) - for(var/mob/M in src.loc) - M.forceMove(IP.loc) - return - - return fizzle(user) - - -/////////////////////////////////////////SECOND RUNE - -/obj/effect/rune/proc/tomesummon(var/mob/living/user) - if(istype(src,/obj/effect/rune)) - user.say("N[pick("'","`")]ath reth sh'yro eth d'raggathnor!") - else - user.whisper("N[pick("'","`")]ath reth sh'yro eth d'raggathnor!") - user.visible_message("Rune disappears with a flash of red light, and in its place now a book lies.", \ - "You are blinded by the flash of red light! After you're able to see again, you see that now instead of the rune there's a book.", \ - "You hear a pop and smell ozone.") - if(istype(src,/obj/effect/rune)) - new /obj/item/book/tome(src.loc) - else - new /obj/item/book/tome(user.loc) - qdel(src) - return - - - -/////////////////////////////////////////THIRD RUNE - -/obj/effect/rune/proc/convert(var/mob/living/user) - var/mob/attacker = user - var/mob/living/carbon/target = null - for(var/mob/living/carbon/M in src.loc) - if(!iscultist(M) && M.stat < DEAD && !(M in converting)) - target = M - break - - if(!target) //didn't find any new targets - if(!converting.len) - fizzle(user) - else - to_chat(user, "You sense that the power of the dark one is already working away at them.") - return - - user.say("Mah[pick("'","`")]weyh pleggh at e'ntrath!") - - converting |= target - var/list/waiting_for_input = list(target = 0) //need to box this up in order to be able to reset it again from inside spawn, apparently - var/initial_message = 0 - while(target in converting) - if(target.loc != src.loc || target.stat == DEAD) - converting -= target - if(target.getFireLoss() < 100) - target.hallucination = min(target.hallucination, 500) - return 0 - - target.take_overall_damage(0, rand(5, 20)) // You dirty resister cannot handle the damage to your mind. Easily. - even cultists who accept right away should experience some effects - // Resist messages go! - if(initial_message) //don't do this stuff right away, only if they resist or hesitate. - admin_attack_log(attacker, target, "Used a convert rune", "Was subjected to a convert rune", "used a convert rune on") - switch(target.getFireLoss()) - if(0 to 25) - to_chat(target, "Your blood boils as you force yourself to resist the corruption invading every corner of your mind.") - target.take_overall_damage(0, 3) - if(25 to 45) - to_chat(target, "Your blood boils and your body burns as the corruption further forces itself into your body and mind.") - target.take_overall_damage(0, 5) - if(45 to 75) - to_chat(target, "You begin to hallucinate images of a dark and incomprehensible being and your entire body feels like its engulfed in flame as your mental defenses crumble.") - target.apply_effect(rand(1,10), STUTTER) - target.take_overall_damage(0, 10) - if(75 to 100) - to_chat(target, "Your mind turns to ash as the burning flames engulf your very soul and images of an unspeakable horror begin to bombard the last remnants of mental resistance.") - //broken mind - 5000 may seem like a lot I wanted the effect to really stand out for maxiumum losing-your-mind-spooky - //hallucination is reduced when the step off as well, provided they haven't hit the last stage... - target.hallucination += 5000 - target.apply_effect(10, STUTTER) - target.adjustBrainLoss(1, 55) - if(100 to INFINITY) - to_chat(target, "Your entire broken soul and being is engulfed in corruption and flames as your mind shatters away into nothing.") - target.hallucination += 5000 - target.apply_effect(15, STUTTER) - target.adjustBrainLoss(rand(1,5), 55) - - initial_message = 1 - if (!target.can_feel_pain()) - target.visible_message("The markings below [target] glow a bloody red.") - else - target.visible_message("[target] writhes in pain as the markings below \him glow a bloody red.", "AAAAAAHHHH!", "You hear an anguished scream.") - - if(!waiting_for_input[target]) //so we don't spam them with dialogs if they hesitate - waiting_for_input[target] = 1 - - if(!cult.can_become_antag(target.mind) || jobban_isbanned(target, "cultist"))//putting jobban check here because is_convertable uses mind as argument - //waiting_for_input ensures this is only shown once, so they basically auto-resist from here on out. They still need to find a way to get off the freaking rune if they don't want to burn to death, though. - to_chat(target, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root.") - to_chat(target, "And you were able to force it out of your mind. Though the memory of that dark, horrible vision will surely haunt you for decades to come.") - - else spawn() - var/choice = alert(target,"Do you want to join the cult?","Submit to Nar'Sie","Resist","Submit") - waiting_for_input[target] = 0 - if(choice == "Submit") //choosing 'Resist' does nothing of course. - cult.add_antagonist(target.mind) - converting -= target - target.hallucination = 0 //sudden clarity - sound_to(target, 'sound/effects/bloodcult.ogg') - sleep(100) //proc once every 10 seconds - return 1 - -/////////////////////////////////////////FOURTH RUNE - -/obj/effect/rune/proc/tearreality(var/mob/living/user) - if(!cult.allow_narsie) - return fizzle(user) - - var/turf/T = get_turf(src) - if(isNotStationLevel(T.z)) - to_chat(user, "You are too far from the station, Nar'sie can not be summoned here.") - return fizzle(user) - - var/list/cultists = new() - for(var/mob/M in range(1,src)) - if(iscultist(M) && !M.stat) - M.say("Tok-lyr rqa'nap g[pick("'","`")]lt-ulotf!") - if(istype(M, /mob/living/carbon/human/apparition)) - continue - cultists.Add(M) - if(cultists.len >= 9) - log_and_message_admins_many(cultists, "summoned Nar-sie.") - new /obj/singularity/narsie/large(src.loc) - - // Can't summon a singular entity twice. - cult.allow_narsie = 0 - return - else - return fizzle(user) - -/////////////////////////////////////////FIFTH RUNE - -/obj/effect/rune/proc/emp(var/mob/living/user, var/U, var/range_red) //range_red - var which determines by which number to reduce the default emp range, U is the source loc, needed because of talisman emps which are held in hand at the moment of using and that apparently messes things up -- Urist - log_and_message_admins("activated an EMP rune.") - if(istype(src,/obj/effect/rune)) - user.say("Ta'gh fara[pick("'","`")]qha fel d'amar det!") - else - user.whisper("Ta'gh fara[pick("'","`")]qha fel d'amar det!") - playsound(U, 'sound/magic/Disable_Tech.ogg', 25, 1) - var/turf/T = get_turf(U) - var/rune = src // detaching the proc - in theory - - var/list/ex = list(user) // exclude caster - for(var/mob/M in range(range_red, T)) - if(iscultist(M)) - ex += M - else - continue - empulse(T, range_red - 2, range_red, exclude = ex) - qdel(rune) - return - -/////////////////////////////////////////SIXTH RUNE - -/obj/effect/rune/proc/drain(var/mob/living/user) - var/drain = 0 - for(var/obj/effect/rune/R in rune_list) - if(R.word1==cultwords["travel"] && R.word2==cultwords["blood"] && R.word3==cultwords["self"]) - for(var/mob/living/carbon/D in R.loc) - if(D.stat!=2) - admin_attack_log(user, D, "Used a blood drain rune.", "Was victim of a blood drain rune.", "used a blood drain rune on") - var/bdrain = rand(1,25) - to_chat(D, "You feel weakened.") - D.take_overall_damage(bdrain, 0) - drain += bdrain - if(!drain) - return fizzle(user) - user.say ("Yu[pick("'","`")]gular faras desdae. Havas mithum javara. Umathar uf'kal thenar!") - user.visible_message("Blood flows from the rune into [user]!", \ - "The blood starts flowing from the rune and into your frail mortal body. You feel... empowered.", \ - "You hear a liquid flowing.") - playsound(user, 'sound/magic/enter_blood.ogg', 100, 1) - - if(user.bhunger) - user.bhunger = max(user.bhunger-2*drain,0) - if(drain>=50) - user.visible_message("[user]'s eyes give off eerie red glow!", \ - "...but it wasn't nearly enough. You crave, crave for more. The hunger consumes you from within.", \ - "You hear a heartbeat.") - user.bhunger += drain - src = user - spawn() - for (,user.bhunger>0,user.bhunger--) - sleep(50) - user.take_overall_damage(3, 0) - return - user.heal_organ_damage(drain%5, 0) - drain-=drain%5 - for (,drain>0,drain-=5) - sleep(2) - user.heal_organ_damage(5, 0) - return - - - -/////////////////////////////////////////SEVENTH RUNE - -/obj/effect/rune/proc/seer(var/mob/living/user) - if(user.loc==src.loc) - if(user.seer==1) - user.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium viortia.") - to_chat(user, "The world beyond fades from your vision.") - user.see_invisible = SEE_INVISIBLE_LIVING - user.seer = 0 - else if(user.see_invisible!=SEE_INVISIBLE_LIVING) - to_chat(user, "The world beyond flashes your eyes but disappears quickly, as if something is disrupting your vision.") - user.see_invisible = SEE_INVISIBLE_CULT - user.seer = 0 - else - user.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium vivira. Itonis al'ra matum!") - to_chat(user, "The world beyond opens to your eyes.") - user.see_invisible = SEE_INVISIBLE_CULT - user.seer = 1 - return - return fizzle(user) - -/////////////////////////////////////////EIGHTH RUNE - -/obj/effect/rune/proc/raise(var/mob/living/user) - var/mob/living/carbon/human/corpse_to_raise - var/mob/living/carbon/human/body_to_sacrifice - - var/is_sacrifice_target = 0 - for(var/mob/living/carbon/human/M in src.loc) - if(M.stat == DEAD) - if(cult && M.mind == cult.sacrifice_target) - is_sacrifice_target = 1 - else - corpse_to_raise = M - if(M.key) - M.ghostize(1) //kick them out of their body - break - if(!corpse_to_raise) - if(is_sacrifice_target) - to_chat(user, "The Geometer of blood wants this mortal for himself.") - return fizzle(user) - - - is_sacrifice_target = 0 - find_sacrifice: - for(var/obj/effect/rune/R in rune_list) - if(R.word1==cultwords["blood"] && R.word2==cultwords["join"] && R.word3==cultwords["hell"]) - for(var/mob/living/carbon/human/N in R.loc) - if(cult && N.mind && N.mind == cult.sacrifice_target) - is_sacrifice_target = 1 - else - if(N.stat!= DEAD) - body_to_sacrifice = N - break find_sacrifice - - if(!body_to_sacrifice) - if (is_sacrifice_target) - to_chat(user, "The Geometer of Blood wants that corpse for himself.") - else - to_chat(user, "The sacrifical corpse is not dead. You must free it from this world of illusions before it may be used.") - return fizzle(user) - - var/mob/abstract/observer/ghost - for(var/mob/abstract/observer/O in loc) - if(!O.client) continue - if(O.mind && O.mind.current && O.mind.current.stat != DEAD) continue - ghost = O - break - - if(!ghost) - to_chat(user, "You require a restless spirit which clings to this world. Beckon their prescence with the sacred chants of Nar-Sie.") - return fizzle(user) - - corpse_to_raise.revive() - - corpse_to_raise.key = ghost.key //the corpse will keep its old mind! but a new player takes ownership of it (they are essentially possessed) - //This means, should that player leave the body, the original may re-enter - user.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!") - corpse_to_raise.visible_message("[corpse_to_raise]'s eyes glow with a faint red as he stands up, slowly starting to breathe again.", \ - "Life... I'm alive again...", \ - "You hear a faint, slightly familiar whisper.") - body_to_sacrifice.visible_message("[body_to_sacrifice] is torn apart, a black smoke swiftly dissipating from \his remains!", \ - "You feel as your blood boils, tearing you apart.", \ - "You hear a thousand voices, all crying in pain.") - body_to_sacrifice.gib() - -// if(ticker.mode.name == "cult") -// ticker.mode:add_cultist(corpse_to_raise.mind) -// else -// ticker.mode.cult |= corpse_to_raise.mind - - to_chat(corpse_to_raise, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root.") - to_chat(corpse_to_raise, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.") - return - - - - - -/////////////////////////////////////////NINETH RUNE - -/obj/effect/rune/proc/obscure(var/mob/living/user, var/rad) - var/S=0 - for(var/obj/effect/rune/R in orange(rad,src)) - if(R!=src) - R.invisibility=INVISIBILITY_OBSERVER - S=1 - if(S) - if(istype(src,/obj/effect/rune)) - user.say("Kla[pick("'","`")]atu barada nikt'o!") - for (var/mob/V in viewers(src)) - V.show_message("The rune turns into gray dust, veiling the surrounding runes.", 3) - qdel(src) - else - user.whisper("Kla[pick("'","`")]atu barada nikt'o!") - to_chat(user, "Your talisman turns into gray dust, veiling the surrounding runes.") - for (var/mob/V in orange(1,src)) - if(V!=user) - V.show_message("Dust emanates from [user]'s hands for a moment.", 3) - - return - if(istype(src,/obj/effect/rune)) - return fizzle(user) - else - call(/obj/effect/rune/proc/fizzle)(user) - return - -/////////////////////////////////////////TENTH RUNE - -/obj/effect/rune/proc/ajourney(var/mob/living/user) //some bits copypastaed from admin tools - Urist - if(user.loc==src.loc) - var/mob/living/carbon/human/L = user - user.say("Fwe[pick("'","`")]sh mah erl nyag r'ya!") - user.visible_message("[user]'s eyes glow blue as \he freezes in place, absolutely motionless.", \ - "The shadow that is your spirit separates itself from your body. You are now in the realm beyond. While this is a great sight, being here strains your mind and body. Hurry...", \ - "You hear only complete silence for a moment.") - announce_ghost_joinleave(user.ghostize(1), 1, "You feel that they had to use some [pick("dark", "black", "blood", "forgotten", "forbidden")] magic to [pick("invade","disturb","disrupt","infest","taint","spoil","blight")] this place!") - L.ajourn = 1 - while(L) - if(L.key) - L.ajourn=0 - return - else - L.take_organ_damage(10, 0) - sleep(100) - return fizzle(user) - - - - -/////////////////////////////////////////ELEVENTH RUNE - -/obj/effect/rune/proc/manifest(var/mob/living/user) - var/obj/effect/rune/this_rune = src - if(user.loc!=this_rune.loc) - return this_rune.fizzle(user) - var/mob/abstract/observer/ghost - for(var/mob/abstract/observer/O in this_rune.loc) - if(!O.client) continue - if(!O.MayRespawn()) continue - if(O.mind && O.mind.current && O.mind.current.stat != DEAD) continue - ghost = O - break - if(!ghost) - return this_rune.fizzle(user) - if(jobban_isbanned(ghost, "cultist")) - return this_rune.fizzle(user) - - user.say("Gal'h'rfikk harfrandid mud[pick("'","`")]gib!") - var/mob/living/carbon/human/apparition/D = new(this_rune.loc) - user.visible_message("A shape forms in the center of the rune. A shape of... a man.", \ - "A shape forms in the center of the rune. A shape of... a man.", \ - "You hear liquid flowing.") - - var/chose_name = 0 - for(var/obj/item/paper/P in this_rune.loc) - if(P.info) - D.real_name = copytext(P.info, findtext(P.info,">")+1, findtext(P.info,"<",2) ) - chose_name = 1 - break - D.universal_speak = 1 - D.underwear = null - D.key = ghost.key - cult.add_antagonist(D.mind) - playsound(loc, 'sound/magic/exit_blood.ogg', 100, 1) - - if(!chose_name) - D.real_name = pick("Anguished", "Blasphemous", "Corrupt", "Cruel", "Depraved", "Despicable", "Disturbed", "Exacerbated", "Foul", "Hateful", "Inexorable", "Implacable", "Impure", "Malevolent", "Malignant", "Malicious", "Pained", "Profane", "Profligate", "Relentless", "Resentful", "Restless", "Spiteful", "Tormented", "Unclean", "Unforgiving", "Vengeful", "Vindictive", "Wicked", "Wronged") - D.real_name += " " - D.real_name += pick("Apparition", "Aptrgangr", "Dis", "Draugr", "Dybbuk", "Eidolon", "Fetch", "Fylgja", "Ghast", "Ghost", "Gjenganger", "Haint", "Phantom", "Phantasm", "Poltergeist", "Revenant", "Shade", "Shadow", "Soul", "Spectre", "Spirit", "Spook", "Visitant", "Wraith") - - log_and_message_admins("used a manifest rune.") - - while(this_rune && user && user.stat==CONSCIOUS && user.client && user.loc==this_rune.loc) - user.take_organ_damage(1, 0) - sleep(30) - if(D) - D.visible_message("[D] slowly dissipates into dust and bones.", \ - "You feel pain, as bonds formed between your soul and this homunculus break.", \ - "You hear faint rustle.") - D.dust() - return - - - -/////////////////////////////////////////TWELFTH RUNE - -/obj/effect/rune/proc/talisman(var/mob/living/user)//only hide, emp, teleport, deafen, blind and tome runes can be imbued atm - var/obj/item/paper/newtalisman - var/unsuitable_newtalisman = 0 - for(var/obj/item/paper/P in src.loc) - if(!P.info) - newtalisman = P - break - else - unsuitable_newtalisman = 1 - if (!newtalisman) - if (unsuitable_newtalisman) - to_chat(user, "The blank is tainted. It is unsuitable.") - return fizzle(user) - - var/obj/effect/rune/imbued_from - var/obj/item/paper/talisman/T - for(var/obj/effect/rune/R in orange(1,src)) - if(R==src) - continue - if(R.word1==cultwords["travel"] && R.word2==cultwords["self"]) //teleport - T = new(src.loc) - T.imbue = "[R.word3]" - T.info = "[R.word3]" - imbued_from = R - break - if(R.word1==cultwords["see"] && R.word2==cultwords["blood"] && R.word3==cultwords["hell"]) //tome - T = new(src.loc) - T.imbue = "newtome" - imbued_from = R - break - if(R.word1==cultwords["destroy"] && R.word2==cultwords["see"] && R.word3==cultwords["technology"]) //emp - T = new(src.loc) - T.imbue = "emp" - imbued_from = R - break - if(R.word1==cultwords["hide"] && R.word2==cultwords["see"] && R.word3==cultwords["blood"]) //conceal - T = new(src.loc) - T.imbue = "conceal" - imbued_from = R - break - if(R.word1==cultwords["hell"] && R.word2==cultwords["destroy"] && R.word3==cultwords["other"]) //armor - T = new(src.loc) - T.imbue = "armor" - imbued_from = R - break - if(R.word1==cultwords["blood"] && R.word2==cultwords["see"] && R.word3==cultwords["hide"]) //reveal - T = new(src.loc) - T.imbue = "revealrunes" - imbued_from = R - break - if(R.word1==cultwords["hide"] && R.word2==cultwords["other"] && R.word3==cultwords["see"]) //deafen - T = new(src.loc) - T.imbue = "deafen" - imbued_from = R - break - if(R.word1==cultwords["destroy"] && R.word2==cultwords["see"] && R.word3==cultwords["other"]) //blind - T = new(src.loc) - T.imbue = "blind" - imbued_from = R - break - if(R.word1==cultwords["self"] && R.word2==cultwords["other"] && R.word3==cultwords["technology"]) //communicat - T = new(src.loc) - T.imbue = "communicate" - imbued_from = R - break - if(R.word1==cultwords["join"] && R.word2==cultwords["hide"] && R.word3==cultwords["technology"]) //communicat - T = new(src.loc) - T.imbue = "runestun" - imbued_from = R - break - if (imbued_from) - for (var/mob/V in viewers(src)) - V.show_message("The runes turn into dust, which then forms into an arcane image on the paper.", 3) - user.say("H'drak v[pick("'","`")]loso, mir'kanas verbot!") - qdel(imbued_from) - qdel(newtalisman) - else - return fizzle(user) - -/////////////////////////////////////////THIRTEENTH RUNE - -/obj/effect/rune/proc/mend(var/mob/living/user) - src = null - user.say("Uhrast ka'hfa heldsagen ver[pick("'","`")]lot!") - user.take_overall_damage(200, 0) - runedec+=10 - user.visible_message("\The [user] keels over dead, \his blood glowing blue as it escapes \his body and dissipates into thin air.", \ - "In the last moment of your humble life, you feel an immense pain as fabric of reality mends... with your blood.", \ - "You hear faint rustle.") - for(,user.stat==2) - sleep(600) - if (!user) - return - runedec-=10 - return - - -/////////////////////////////////////////FOURTEETH RUNE - -// returns 0 if the rune is not used. returns 1 if the rune is used. -/obj/effect/rune/proc/communicate(var/mob/living/user) - . = 1 // Default output is 1. If the rune is deleted it will return 1 - var/input = input(user, "Please choose a message to tell to the other acolytes.", "Voice of Blood", "")//sanitize() below, say() and whisper() have their own - if(!input) - if (istype(src)) - fizzle(user) - return 0 - else - return 0 - - if(istype(src,/obj/effect/rune)) - user.say("O bidai nabora se[pick("'","`")]sma!") - user.say("[input]") - else - user.whisper("O bidai nabora se[pick("'","`")]sma!") - user.whisper("[input]") - - input = sanitize(input) - log_and_message_admins("used a communicate rune to say '[input]'") - for(var/datum/mind/H in cult.current_antagonists) - if (H.current) - to_chat(H.current, "[input]") - qdel(src) - return 1 - -/////////////////////////////////////////FIFTEENTH RUNE - -/obj/effect/rune/proc/sacrifice(var/mob/living/user) - var/list/mob/living/carbon/human/cultsinrange = list() - var/list/mob/living/carbon/human/victims = list() - for(var/mob/living/carbon/human/V in src.loc)//Checks for non-cultist humans to sacrifice - if(ishuman(V)) - if(!(iscultist(V))) - victims += V//Checks for cult status and mob type - for(var/obj/item/I in src.loc)//Checks for MMIs/brains/Intellicards - if(istype(I,/obj/item/organ/internal/brain)) - var/obj/item/organ/internal/brain/B = I - victims += B.brainmob - else if(istype(I,/obj/item/device/mmi)) - var/obj/item/device/mmi/B = I - victims += B.brainmob - else if(istype(I,/obj/item/aicard)) - for(var/mob/living/silicon/ai/A in I) - victims += A - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - cultsinrange += C - C.say("Barhah hra zar[pick("'","`")]garis!") - - for(var/mob/H in victims) - - var/worth = 0 - if(istype(H,/mob/living/carbon/human)) - var/mob/living/carbon/human/lamb = H - if(lamb.species.rarity_value > 3) - worth = 1 - - if (SSticker.mode.name == "Cult") - if(H.mind == cult.sacrifice_target) - if(cultsinrange.len >= 3) - sacrificed += H.mind - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - to_chat(user, "The Geometer of Blood accepts this sacrifice, your objective is now complete.") - else - to_chat(user, "Your target's earthly bonds are too strong. You need more cultists to succeed in this ritual.") - else - if(cultsinrange.len >= 3) - if(H.stat != DEAD) - if(prob(80) || worth) - to_chat(user, "The Geometer of Blood accepts this [worth ? "exotic " : ""]sacrifice.") - else - to_chat(user, "The Geometer of Blood accepts this sacrifice.") - to_chat(user, "However, this soul was not enough to gain His favor.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - else - if(prob(40) || worth) - to_chat(user, "The Geometer of Blood accepts this [worth ? "exotic " : ""]sacrifice.") - else - to_chat(user, "The Geometer of Blood accepts this sacrifice.") - to_chat(user, "However, a mere dead body is not enough to satisfy Him.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - else - if(H.stat != DEAD) - to_chat(user, "The victim is still alive, you will need more cultists chanting for the sacrifice to succeed.") - else - if(prob(40)) - - to_chat(user, "The Geometer of Blood accepts this sacrifice.") - else - to_chat(user, "The Geometer of Blood accepts this sacrifice.") - to_chat(user, "However, a mere dead body is not enough to satisfy Him.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - else - if(cultsinrange.len >= 3) - if(H.stat != DEAD) - if(prob(80)) - to_chat(user, "The Geometer of Blood accepts this sacrifice.") - else - to_chat(user, "The Geometer of Blood accepts this sacrifice.") - to_chat(user, "However, this soul was not enough to gain His favor.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - else - if(prob(40)) - to_chat(user, "The Geometer of Blood accepts this sacrifice.") - else - to_chat(user, "The Geometer of Blood accepts this sacrifice.") - to_chat(user, "However, a mere dead body is not enough to satisfy Him.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - else - if(H.stat != DEAD) - to_chat(user, "The victim is still alive, you will need more cultists chanting for the sacrifice to succeed.") - else - if(prob(40)) - to_chat(user, "The Geometer of Blood accepts this sacrifice.") - else - to_chat(user, "The Geometer of Blood accepts this sacrifice.") - to_chat(user, "However, a mere dead body is not enough to satisfy Him.") - if(isrobot(H)) - H.dust()//To prevent the MMI from remaining - else - H.gib() - -/////////////////////////////////////////SIXTEENTH RUNE - -/obj/effect/rune/proc/revealrunes(var/mob/living/user, var/obj/W as obj) - var/go=0 - var/rad - var/S=0 - if(istype(W,/obj/effect/rune)) - rad = 6 - go = 1 - if (istype(W,/obj/item/paper/talisman)) - rad = 4 - go = 1 - if (istype(W,/obj/item/nullrod)) - rad = 2 - go = 1 - if(go) - for(var/obj/effect/rune/R in orange(rad,src)) - if(R!=src) - R:visibility=15 - S=1 - if(S) - if(istype(W,/obj/item/nullrod)) - visible_message(span("warning", "Arcane markings suddenly glow from underneath a thin layer of dust!")) - return - if(istype(W,/obj/effect/rune)) - user.say("Nikt[pick("'","`")]o barada kla'atu!") - for (var/mob/V in viewers(src)) - V.show_message("The rune turns into red dust, reveaing the surrounding runes.", 3) - qdel(src) - return - if(istype(W,/obj/item/paper/talisman)) - user.whisper("Nikt[pick("'","`")]o barada kla'atu!") - to_chat(user, "Your talisman turns into red dust, revealing the surrounding runes.") - for (var/mob/V in orange(1,user.loc)) - if(V!=user) - V.show_message("Red dust emanates from [user]'s hands for a moment.", 3) - return - return - if(istype(W,/obj/effect/rune)) - return fizzle(user) - if(istype(W,/obj/item/paper/talisman)) - call(/obj/effect/rune/proc/fizzle)(user) - return - -/////////////////////////////////////////SEVENTEENTH RUNE - -/obj/effect/rune/proc/wall(var/mob/living/user) - user.say("Khari[pick("'","`")]d! Eske'te tannin!") - src.density = !src.density - user.take_organ_damage(2, 0) - if(src.density) - to_chat(user, "Your blood flows into the rune, and you feel that the very space over the rune thickens.") - else - to_chat(user, "Your blood flows into the rune, and you feel as the rune releases its grasp on space.") - return - -/////////////////////////////////////////EIGHTTEENTH RUNE - -/obj/effect/rune/proc/freedom(var/mob/living/user) - var/list/mob/living/carbon/cultists = new - for(var/datum/mind/H in cult.current_antagonists) - if (istype(H.current,/mob/living/carbon)) - cultists+=H.current - var/list/mob/living/carbon/users = new - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - users+=C - var/dam = round(15 / users.len) - if(users.len>=3) - var/mob/living/carbon/cultist = input("Choose the one who you want to free", "Followers of Geometer") as null|anything in (cultists - users) - if(!cultist) - return fizzle(user) - if (cultist == user) //just to be sure. - return - if(!(cultist.buckled || \ - cultist.handcuffed || \ - istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle) || \ - (istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded) || \ - (istype(cultist.loc, /obj/structure/closet/secure_closet)&&cultist.loc:locked) || \ - (istype(cultist.loc, /obj/machinery/dna_scannernew)&&cultist.loc:locked) \ - )) - to_chat(user, "The [cultist] is already free.") - return - cultist.buckled = null - if (cultist.handcuffed) - cultist.drop_from_inventory(cultist.handcuffed) - if (cultist.legcuffed) - cultist.drop_from_inventory(cultist.legcuffed) - if (istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle)) - cultist.drop_from_inventory(cultist.wear_mask) - if(istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded) - cultist.loc:welded = 0 - if(istype(cultist.loc, /obj/structure/closet/secure_closet)&&cultist.loc:locked) - cultist.loc:locked = 0 - if(istype(cultist.loc, /obj/machinery/dna_scannernew)&&cultist.loc:locked) - cultist.loc:locked = 0 - for(var/mob/living/carbon/C in users) - user.take_overall_damage(dam, 0) - C.say("Khari[pick("'","`")]d! Gual'te nikka!") - qdel(src) - return fizzle(user) - -/////////////////////////////////////////NINETEENTH RUNE - -/obj/effect/rune/proc/cultsummon(var/mob/living/user) - var/list/mob/living/carbon/cultists = new - for(var/datum/mind/H in cult.current_antagonists) - if (istype(H.current,/mob/living/carbon)) - cultists+=H.current - var/list/mob/living/carbon/users = new - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - users += C - if(users.len>=3) - var/mob/living/carbon/cultist = input("Choose the one who you want to summon", "Followers of Geometer") as null|anything in (cultists - user) - if(!cultist) - return fizzle(user) - if (cultist == user) //just to be sure. - return - if(cultist.buckled || cultist.handcuffed || (!isturf(cultist.loc) && !istype(cultist.loc, /obj/structure/closet))) - to_chat(user, "You cannot summon \the [cultist], for \his shackles of blood are strong.") - return fizzle(user) - cultist.forceMove(src.loc) - cultist.lying = 1 - cultist.regenerate_icons() - - var/dam = round(25 / (users.len/2)) //More people around the rune less damage everyone takes. Minimum is 3 cultists - - for(var/mob/living/carbon/human/C in users) - if(iscultist(C) && !C.stat) - C.say("N'ath reth sh'yro eth d[pick("'","`")]rekkathnor!") - C.take_overall_damage(dam, 0) - if(users.len <= 4) // You did the minimum, this is going to hurt more and we're going to stun you. - C.apply_effect(rand(3,6), STUN) - C.apply_effect(1, WEAKEN) - user.visible_message("Rune disappears with a flash of red light, and in its place now a body lies.", \ - "You are blinded by the flash of red light! After you're able to see again, you see that now instead of the rune there's a body.", \ - "You hear a pop and smell ozone.") - qdel(src) - return fizzle(user) - -/////////////////////////////////////////TWENTIETH RUNES - -/obj/effect/rune/proc/deafen(var/mob/living/user) - if(istype(src,/obj/effect/rune)) - var/list/affected = new() - for(var/mob/living/carbon/C in range(7,src)) - if (iscultist(C)) - continue - var/obj/item/nullrod/N = locate() in C - if(N) - continue - C.ear_deaf += 50 - C.show_message("The world around you suddenly becomes quiet.", 3) - affected += C - if(prob(1)) - C.sdisabilities |= DEAF - if(affected.len) - user.say("Sti[pick("'","`")] kaliedir!") - to_chat(user, "The world becomes quiet as the deafening rune dissipates into fine dust.") - admin_attacker_log_many_victims(user, affected, "Used a deafen rune.", "Was victim of a deafen rune.", "used a deafen rune on") - qdel(src) - else - return fizzle(user) - else - var/list/affected = new() - for(var/mob/living/carbon/C in range(7,user)) - if (iscultist(C)) - continue - var/obj/item/nullrod/N = locate() in C - if(N) - continue - C.ear_deaf += 30 - //talismans is weaker. - C.show_message("The world around you suddenly becomes quiet.", 3) - affected += C - if(affected.len) - user.whisper("Sti[pick("'","`")] kaliedir!") - to_chat(user, "Your talisman turns into gray dust, deafening everyone around.") - admin_attacker_log_many_victims(user, affected, "Used a deafen rune.", "Was victim of a deafen rune.", "used a deafen rune on") - for (var/mob/V in orange(1,src)) - if(!(iscultist(V))) - V.show_message("Dust flows from [user]'s hands for a moment, and the world suddenly becomes quiet..", 3) - return - -/obj/effect/rune/proc/blind(var/mob/living/user) - if(istype(src,/obj/effect/rune)) - var/list/affected = new() - for(var/mob/living/carbon/C in viewers(src)) - if (iscultist(C)) - continue - var/obj/item/nullrod/N = locate() in C - if(N) - continue - C.eye_blurry += 50 - C.eye_blind += 20 - if(prob(5)) - C.disabilities |= NEARSIGHTED - if(prob(10)) - C.sdisabilities |= BLIND - C.show_message("Suddenly you see a red flash that blinds you.", 3) - affected += C - if(affected.len) - user.say("Sti[pick("'","`")] kaliesin!") - to_chat(user, "The rune flashes, blinding those who not follow the Nar-Sie, and dissipates into fine dust.") - admin_attacker_log_many_victims(user, affected, "Used a blindness rune.", "Was victim of a blindness rune.", "used a blindness rune on") - qdel(src) - else - return fizzle(user) - else - var/list/affected = new() - for(var/mob/living/carbon/C in view(2,user)) - if (iscultist(C)) - continue - var/obj/item/nullrod/N = locate() in C - if(N) - continue - C.eye_blurry += 30 - C.eye_blind += 10 - //talismans is weaker. - affected += C - C.show_message("You feel a sharp pain in your eyes, and the world disappears into darkness..", 3) - if(affected.len) - user.whisper("Sti[pick("'","`")] kaliesin!") - to_chat(user, "Your talisman turns into gray dust, blinding those who not follow the Nar-Sie.") - admin_attacker_log_many_victims(user, affected, "Used a blindness rune.", "Was victim of a blindness rune.", "used a blindness rune on") - return - - -/obj/effect/rune/proc/bloodboil(var/mob/living/user) //cultists need at least one DANGEROUS rune. Even if they're all stealthy. -/* - var/list/mob/living/carbon/cultists = new - for(var/datum/mind/H in ticker.mode.cult) - if (istype(H.current,/mob/living/carbon)) - cultists+=H.current -*/ - var/list/cultists = new //also, wording for it is old wording for obscure rune, which is now hide-see-blood. - var/list/victims = new -// var/list/cultboil = list(cultists-user) //and for this words are destroy-see-blood. - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - cultists+=C - if(cultists.len>=3) - for(var/mob/living/carbon/M in viewers(user)) - if(iscultist(M)) - continue - var/obj/item/nullrod/N = locate() in M - if(N) - continue - M.take_overall_damage(51,51) - to_chat(M, "Your blood boils!") - victims += M - if(prob(5)) - spawn(5) - M.gib() - for(var/obj/effect/rune/R in view(src)) - if(prob(10)) - explosion(R.loc, -1, 0, 1, 5) - for(var/mob/living/carbon/human/C in orange(1,src)) - if(iscultist(C) && !C.stat) - C.say("Dedo ol[pick("'","`")]btoh!") - C.take_overall_damage(15, 0) - admin_attacker_log_many_victims(user, victims, "Used a blood boil rune.", "Was the victim of a blood boil rune.", "used a blood boil rune on") - log_and_message_admins_many(cultists - user, "assisted activating a blood boil rune.") - qdel(src) - else - return fizzle(user) - return - -// WIP rune, I'll wait for Rastaf0 to add limited blood. - -/obj/effect/rune/proc/burningblood(var/mob/living/user) - var/culcount = 0 - for(var/mob/living/carbon/C in orange(1,src)) - if(iscultist(C) && !C.stat) - culcount++ - if(culcount >= 5) - for(var/obj/effect/rune/R in rune_list) - if(R.blood_DNA == src.blood_DNA) - for(var/mob/living/M in orange(2,R)) - M.take_overall_damage(0,15) - if (R.invisibility>M.see_invisible) - to_chat(M, "Aargh it burns!") - else - to_chat(M, "Rune suddenly ignites, burning you!") - var/turf/T = get_turf(R) - T.hotspot_expose(700,125) - for(var/obj/effect/decal/cleanable/blood/B in world) - if(B.blood_DNA == src.blood_DNA) - for(var/mob/living/M in orange(1,B)) - M.take_overall_damage(0,5) - to_chat(M, "Blood suddenly ignites, burning you!") - var/turf/T = get_turf(B) - T.hotspot_expose(700,125) - qdel(B) - qdel(src) - -////////// Rune 24 (counting burningblood, which kinda doesnt work yet.) - -/obj/effect/rune/proc/runestun(var/mob/living/user, var/mob/living/T as mob) - if(istype(src,/obj/effect/rune)) ///When invoked as rune, flash and briefly stun everyone around. - user.say("Fuu ma[pick("'","`")]jin!") - for(var/mob/living/L in viewers(src)) - if(iscultist(L)) - continue - if(iscarbon(L)) - var/mob/living/carbon/C = L - flick("e_flash", C.flash) - if(C.stuttering < 1 && (!(HULK in C.mutations))) - C.stuttering = 1 - C.Weaken(3) - C.Stun(3) - C.silent += 15 - C.show_message("The rune explodes in a bright flash.", 3) - admin_attack_log(user, C, "Used a stun rune.", "Was victim of a stun rune.", "used a stun rune on") - - else if(issilicon(L)) - var/mob/living/silicon/S = L - S.Weaken(5) - flick("e_flash", S.flash) - S.silent += 15 - S.show_message("BZZZT... The rune has exploded in a bright flash.", 3) - admin_attack_log(user, S, "Used a stun rune.", "Was victim of a stun rune.", "used a stun rune on") - qdel(src) - else ///When invoked as talisman, flash and mute the target mob. - user.say("Dream sign ''Evil sealing talisman'[pick("'","`")]!") - var/obj/item/nullrod/N = locate() in T - if(N) - for(var/mob/O in viewers(T, null)) - O.show_message(text("[] invokes a talisman at [], but they are unaffected!", user, T), 1) - else - for(var/mob/O in viewers(T, null)) - O.show_message(text("[] invokes a talisman at []", user, T), 1) - - if(issilicon(T)) - flick("e_flash", T.flash) - T.silent += 15 - admin_attack_log(user, T, "Used a stun talisman.", "Was victim of a stun talisman.", "used a stun talisman on") - else if(iscarbon(T)) - var/mob/living/carbon/C = T - flick("e_flash", C.flash) - if(!(HULK in C.mutations)) - C.silent += 15 - admin_attack_log(user, C, "Used a stun talisman.", "Was victim of a stun talisman.", "used a stun talisman on") - return - -/////////////////////////////////////////TWENTY-FIFTH RUNE - -/obj/effect/rune/proc/armor(var/mob/living/user) - if(istype(src,/obj/effect/rune)) - user.say("N'ath reth sh'yro eth d[pick("'","`")]raggathnor!") - else - user.whisper("N'ath reth sh'yro eth d[pick("'","`")]raggathnor!") - user.visible_message("The rune disappears with a flash of red light, and a set of armor appears on [user]...", \ - "You are blinded by the flash of red light! After you're able to see again, you see that you are now wearing a set of armor.") - - if(istype(user, /mob/living/simple_animal/construct)) - var/mob/living/simple_animal/construct/C = user - var/construct_class - if(narsie_cometh) - construct_class = alert(C, "Please choose which type of construct you wish to become.",,"Juggernaut","Wraith","Harvester") - else - construct_class = alert(C, "Please choose which type of construct you wish to become.",,"Juggernaut","Wraith","Artificer") - switch(construct_class) - if("Juggernaut") - var/mob/living/simple_animal/construct/armoured/Z = new /mob/living/simple_animal/construct/armoured (get_turf(C.loc)) - Z.key = C.key - if(iscultist(C)) - cult.add_antagonist(Z.mind) - C.death() - to_chat(Z, "You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.") - to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - Z.cancel_camera() - if("Wraith") - var/mob/living/simple_animal/construct/wraith/Z = new /mob/living/simple_animal/construct/wraith (get_turf(C.loc)) - Z.key = C.key - if(iscultist(C)) - cult.add_antagonist(Z.mind) - C.death() - to_chat(Z, "You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.") - to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - Z.cancel_camera() - if("Artificer") - var/mob/living/simple_animal/construct/builder/Z = new /mob/living/simple_animal/construct/builder (get_turf(C.loc)) - Z.key = C.key - if(iscultist(C)) - cult.add_antagonist(Z.mind) - C.death() - to_chat(Z, "You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs") - to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - Z.cancel_camera() - if("Harvester") - var/mob/living/simple_animal/construct/builder/Z = new /mob/living/simple_animal/construct/harvester (get_turf(C.loc)) - Z.key = C.key - if(iscultist(C)) - cult.add_antagonist(Z.mind) - C.death() - to_chat(Z, "You are playing a Harvester. You are gifted with the ability to open doors with your mind, to draw runes at will, and to teleport back to Nar'Sie. Seek out all non-believers and bring them to the Geometer.") - to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - Z.cancel_camera() - else - user.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head) - user.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit) - user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes) - user.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(user), slot_back) - //the above update their overlay icons cache but do not call update_icons() - //the below calls update_icons() at the end, which will update overlay icons by using the (now updated) cache - user.put_in_hands(new /obj/item/melee/cultblade(user)) //put in hands or on floor - - qdel(src) - return +var/list/sacrificed = list() + +/obj/effect/rune/cultify() + return + +/obj/effect/rune +/* + * Use as a general guideline for this and related files: + * * ... - when something non-trivial or an error happens, so something similar to "Sparks come out of the machine!" + * * ... - when something that is fit for 'warning' happens but there is some damage or pain as well. + * * ... - when there is a private message to the cultists. This guideline is very arbitrary but there has to be some consistency! + */ + + +/////////////////////////////////////////FIRST RUNE +/obj/effect/rune/proc/teleport(var/mob/living/user, var/key) + var/allrunesloc[] + allrunesloc = new/list() + var/index = 0 +// var/tempnum = 0 + for(var/obj/effect/rune/R in rune_list) + if(R == src) + continue + if(R.word1 == cultwords["travel"] && R.word2 == cultwords["self"] && R.word3 == key && isStationLevel(R.z)) + index++ + allrunesloc.len = index + allrunesloc[index] = R.loc + if(index >= 5) + to_chat(user, "You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric.") + if (istype(user, /mob/living)) + user.take_overall_damage(5, 0) + qdel(src) + + var/turf/T = get_turf(user) + if(isNotStationLevel(T.z)) + to_chat(user, "You are too far from the station, Nar'sie is unable to reach you here.") + return fizzle(user) + + if(allrunesloc && index != 0) + if(istype(src,/obj/effect/rune)) + user.say("Sas[pick("'","`")]so c'arta forbici!")//Only you can stop auto-muting + else + user.whisper("Sas[pick("'","`")]so c'arta forbici!") + user.visible_message("[user] disappears in a flash of red light!", \ + "You feel as your body gets dragged through the dimension of Nar-Sie!", \ + "You hear a sickening crunch and sloshing of viscera.") + user.forceMove(allrunesloc[rand(1,index)]) + return + if(istype(src,/obj/effect/rune)) + return fizzle(user) //Use friggin manuals, Dorf, your list was of zero length. + else + call(/obj/effect/rune/proc/fizzle)(user) + return + + +/obj/effect/rune/proc/itemport(var/mob/living/user, var/key) +// var/allrunesloc[] +// allrunesloc = new/list() +// var/index = 0 +// var/tempnum = 0 + var/culcount = 0 + var/runecount = 0 + var/obj/effect/rune/IP = null + for(var/obj/effect/rune/R in rune_list) + if(R == src) + continue + if(R.word1 == cultwords["travel"] && R.word2 == cultwords["other"] && R.word3 == key) + IP = R + runecount++ + if(runecount >= 2) + to_chat(user, "You feel pain, as rune disappears in reality shift caused by too much wear of space-time fabric.") + if (istype(user, /mob/living)) + user.take_overall_damage(5, 0) + qdel(src) + for(var/mob/living/carbon/C in orange(1,src)) + if(iscultist(C) && !C.stat) + culcount++ + if(culcount>=3) + user.say("Sas[pick("'","`")]so c'arta forbici tarem!") + user.visible_message("You feel air moving from the rune - like as it was swapped with somewhere else.", \ + "You feel air moving from the rune - like as it was swapped with somewhere else.", \ + "You smell ozone.") + for(var/obj/O in src.loc) + if(!O.anchored) + O.forceMove(IP.loc) + for(var/mob/M in src.loc) + M.forceMove(IP.loc) + return + + return fizzle(user) + + +/////////////////////////////////////////SECOND RUNE + +/obj/effect/rune/proc/tomesummon(var/mob/living/user) + if(istype(src,/obj/effect/rune)) + user.say("N[pick("'","`")]ath reth sh'yro eth d'raggathnor!") + else + user.whisper("N[pick("'","`")]ath reth sh'yro eth d'raggathnor!") + user.visible_message("Rune disappears with a flash of red light, and in its place now a book lies.", \ + "You are blinded by the flash of red light! After you're able to see again, you see that now instead of the rune there's a book.", \ + "You hear a pop and smell ozone.") + if(istype(src,/obj/effect/rune)) + new /obj/item/book/tome(src.loc) + else + new /obj/item/book/tome(user.loc) + qdel(src) + return + + + +/////////////////////////////////////////THIRD RUNE + +/obj/effect/rune/proc/convert(var/mob/living/user) + var/mob/attacker = user + var/mob/living/carbon/target = null + for(var/mob/living/carbon/M in src.loc) + if(!iscultist(M) && M.stat < DEAD && !(M in converting)) + target = M + break + + if(!target) //didn't find any new targets + if(!converting.len) + fizzle(user) + else + to_chat(user, "You sense that the power of the dark one is already working away at them.") + return + + user.say("Mah[pick("'","`")]weyh pleggh at e'ntrath!") + + converting |= target + var/list/waiting_for_input = list(target = 0) //need to box this up in order to be able to reset it again from inside spawn, apparently + var/initial_message = 0 + while(target in converting) + if(target.loc != src.loc || target.stat == DEAD) + converting -= target + if(target.getFireLoss() < 100) + target.hallucination = min(target.hallucination, 500) + return 0 + + target.take_overall_damage(0, rand(5, 20)) // You dirty resister cannot handle the damage to your mind. Easily. - even cultists who accept right away should experience some effects + // Resist messages go! + if(initial_message) //don't do this stuff right away, only if they resist or hesitate. + admin_attack_log(attacker, target, "Used a convert rune", "Was subjected to a convert rune", "used a convert rune on") + switch(target.getFireLoss()) + if(0 to 25) + to_chat(target, "Your blood boils as you force yourself to resist the corruption invading every corner of your mind.") + target.take_overall_damage(0, 3) + if(25 to 45) + to_chat(target, "Your blood boils and your body burns as the corruption further forces itself into your body and mind.") + target.take_overall_damage(0, 5) + if(45 to 75) + to_chat(target, "You begin to hallucinate images of a dark and incomprehensible being and your entire body feels like its engulfed in flame as your mental defenses crumble.") + target.apply_effect(rand(1,10), STUTTER) + target.take_overall_damage(0, 10) + if(75 to 100) + to_chat(target, "Your mind turns to ash as the burning flames engulf your very soul and images of an unspeakable horror begin to bombard the last remnants of mental resistance.") + //broken mind - 5000 may seem like a lot I wanted the effect to really stand out for maxiumum losing-your-mind-spooky + //hallucination is reduced when the step off as well, provided they haven't hit the last stage... + target.hallucination += 5000 + target.apply_effect(10, STUTTER) + target.adjustBrainLoss(1, 55) + if(100 to INFINITY) + to_chat(target, "Your entire broken soul and being is engulfed in corruption and flames as your mind shatters away into nothing.") + target.hallucination += 5000 + target.apply_effect(15, STUTTER) + target.adjustBrainLoss(rand(1,5), 55) + + initial_message = 1 + if (!target.can_feel_pain()) + target.visible_message("The markings below [target] glow a bloody red.") + else + target.visible_message("[target] writhes in pain as the markings below \him glow a bloody red.", "AAAAAAHHHH!", "You hear an anguished scream.") + + if(!waiting_for_input[target]) //so we don't spam them with dialogs if they hesitate + waiting_for_input[target] = 1 + + if(!cult.can_become_antag(target.mind) || jobban_isbanned(target, "cultist"))//putting jobban check here because is_convertable uses mind as argument + //waiting_for_input ensures this is only shown once, so they basically auto-resist from here on out. They still need to find a way to get off the freaking rune if they don't want to burn to death, though. + to_chat(target, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root.") + to_chat(target, "And you were able to force it out of your mind. Though the memory of that dark, horrible vision will surely haunt you for decades to come.") + + else spawn() + var/choice = alert(target,"Do you want to join the cult?","Submit to Nar'Sie","Resist","Submit") + waiting_for_input[target] = 0 + if(choice == "Submit") //choosing 'Resist' does nothing of course. + cult.add_antagonist(target.mind) + converting -= target + target.hallucination = 0 //sudden clarity + sound_to(target, 'sound/effects/bloodcult.ogg') + sleep(100) //proc once every 10 seconds + return 1 + +/////////////////////////////////////////FOURTH RUNE + +/obj/effect/rune/proc/tearreality(var/mob/living/user) + if(!cult.allow_narsie) + return fizzle(user) + + var/turf/T = get_turf(src) + if(isNotStationLevel(T.z)) + to_chat(user, "You are too far from the station, Nar'sie can not be summoned here.") + return fizzle(user) + + var/list/cultists = new() + for(var/mob/M in range(1,src)) + if(iscultist(M) && !M.stat) + M.say("Tok-lyr rqa'nap g[pick("'","`")]lt-ulotf!") + if(istype(M, /mob/living/carbon/human/apparition)) + continue + cultists.Add(M) + if(cultists.len >= 9) + log_and_message_admins_many(cultists, "summoned Nar-sie.") + new /obj/singularity/narsie/large(src.loc) + + // Can't summon a singular entity twice. + cult.allow_narsie = 0 + return + else + return fizzle(user) + +/////////////////////////////////////////FIFTH RUNE + +/obj/effect/rune/proc/emp(var/mob/living/user, var/U, var/range_red) //range_red - var which determines by which number to reduce the default emp range, U is the source loc, needed because of talisman emps which are held in hand at the moment of using and that apparently messes things up -- Urist + log_and_message_admins("activated an EMP rune.") + if(istype(src,/obj/effect/rune)) + user.say("Ta'gh fara[pick("'","`")]qha fel d'amar det!") + else + user.whisper("Ta'gh fara[pick("'","`")]qha fel d'amar det!") + playsound(U, 'sound/magic/Disable_Tech.ogg', 25, 1) + var/turf/T = get_turf(U) + var/rune = src // detaching the proc - in theory + + var/list/ex = list(user) // exclude caster + for(var/mob/M in range(range_red, T)) + if(iscultist(M)) + ex += M + else + continue + empulse(T, range_red - 2, range_red, exclude = ex) + qdel(rune) + return + +/////////////////////////////////////////SIXTH RUNE + +/obj/effect/rune/proc/drain(var/mob/living/user) + var/drain = 0 + for(var/obj/effect/rune/R in rune_list) + if(R.word1==cultwords["travel"] && R.word2==cultwords["blood"] && R.word3==cultwords["self"]) + for(var/mob/living/carbon/D in R.loc) + if(D.stat!=2) + admin_attack_log(user, D, "Used a blood drain rune.", "Was victim of a blood drain rune.", "used a blood drain rune on") + var/bdrain = rand(1,25) + to_chat(D, "You feel weakened.") + D.take_overall_damage(bdrain, 0) + drain += bdrain + if(!drain) + return fizzle(user) + user.say ("Yu[pick("'","`")]gular faras desdae. Havas mithum javara. Umathar uf'kal thenar!") + user.visible_message("Blood flows from the rune into [user]!", \ + "The blood starts flowing from the rune and into your frail mortal body. You feel... empowered.", \ + "You hear a liquid flowing.") + playsound(user, 'sound/magic/enter_blood.ogg', 100, 1) + + if(user.bhunger) + user.bhunger = max(user.bhunger-2*drain,0) + if(drain>=50) + user.visible_message("[user]'s eyes give off eerie red glow!", \ + "...but it wasn't nearly enough. You crave, crave for more. The hunger consumes you from within.", \ + "You hear a heartbeat.") + user.bhunger += drain + src = user + spawn() + for (,user.bhunger>0,user.bhunger--) + sleep(50) + user.take_overall_damage(3, 0) + return + user.heal_organ_damage(drain%5, 0) + drain-=drain%5 + for (,drain>0,drain-=5) + sleep(2) + user.heal_organ_damage(5, 0) + return + + + +/////////////////////////////////////////SEVENTH RUNE + +/obj/effect/rune/proc/seer(var/mob/living/user) + if(user.loc==src.loc) + if(user.seer==1) + user.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium viortia.") + to_chat(user, "The world beyond fades from your vision.") + user.see_invisible = SEE_INVISIBLE_LIVING + user.seer = 0 + else if(user.see_invisible!=SEE_INVISIBLE_LIVING) + to_chat(user, "The world beyond flashes your eyes but disappears quickly, as if something is disrupting your vision.") + user.see_invisible = SEE_INVISIBLE_CULT + user.seer = 0 + else + user.say("Rash'tla sektath mal[pick("'","`")]zua. Zasan therium vivira. Itonis al'ra matum!") + to_chat(user, "The world beyond opens to your eyes.") + user.see_invisible = SEE_INVISIBLE_CULT + user.seer = 1 + return + return fizzle(user) + +/////////////////////////////////////////EIGHTH RUNE + +/obj/effect/rune/proc/raise(var/mob/living/user) + var/mob/living/carbon/human/corpse_to_raise + var/mob/living/carbon/human/body_to_sacrifice + + var/is_sacrifice_target = 0 + for(var/mob/living/carbon/human/M in src.loc) + if(M.stat == DEAD) + if(cult && M.mind == cult.sacrifice_target) + is_sacrifice_target = 1 + else + corpse_to_raise = M + if(M.key) + M.ghostize(1) //kick them out of their body + break + if(!corpse_to_raise) + if(is_sacrifice_target) + to_chat(user, "The Geometer of blood wants this mortal for himself.") + return fizzle(user) + + + is_sacrifice_target = 0 + find_sacrifice: + for(var/obj/effect/rune/R in rune_list) + if(R.word1==cultwords["blood"] && R.word2==cultwords["join"] && R.word3==cultwords["hell"]) + for(var/mob/living/carbon/human/N in R.loc) + if(cult && N.mind && N.mind == cult.sacrifice_target) + is_sacrifice_target = 1 + else + if(N.stat!= DEAD) + body_to_sacrifice = N + break find_sacrifice + + if(!body_to_sacrifice) + if (is_sacrifice_target) + to_chat(user, "The Geometer of Blood wants that corpse for himself.") + else + to_chat(user, "The sacrifical corpse is not dead. You must free it from this world of illusions before it may be used.") + return fizzle(user) + + var/mob/abstract/observer/ghost + for(var/mob/abstract/observer/O in loc) + if(!O.client) continue + if(O.mind && O.mind.current && O.mind.current.stat != DEAD) continue + ghost = O + break + + if(!ghost) + to_chat(user, "You require a restless spirit which clings to this world. Beckon their prescence with the sacred chants of Nar-Sie.") + return fizzle(user) + + corpse_to_raise.revive() + + corpse_to_raise.key = ghost.key //the corpse will keep its old mind! but a new player takes ownership of it (they are essentially possessed) + //This means, should that player leave the body, the original may re-enter + user.say("Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!") + corpse_to_raise.visible_message("[corpse_to_raise]'s eyes glow with a faint red as he stands up, slowly starting to breathe again.", \ + "Life... I'm alive again...", \ + "You hear a faint, slightly familiar whisper.") + body_to_sacrifice.visible_message("[body_to_sacrifice] is torn apart, a black smoke swiftly dissipating from \his remains!", \ + "You feel as your blood boils, tearing you apart.", \ + "You hear a thousand voices, all crying in pain.") + body_to_sacrifice.gib() + +// if(ticker.mode.name == "cult") +// ticker.mode:add_cultist(corpse_to_raise.mind) +// else +// ticker.mode.cult |= corpse_to_raise.mind + + to_chat(corpse_to_raise, "Your blood pulses. Your head throbs. The world goes red. All at once you are aware of a horrible, horrible truth. The veil of reality has been ripped away and in the festering wound left behind something sinister takes root.") + to_chat(corpse_to_raise, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.") + return + + + + + +/////////////////////////////////////////NINETH RUNE + +/obj/effect/rune/proc/obscure(var/mob/living/user, var/rad) + var/S=0 + for(var/obj/effect/rune/R in orange(rad,src)) + if(R!=src) + R.invisibility=INVISIBILITY_OBSERVER + S=1 + if(S) + if(istype(src,/obj/effect/rune)) + user.say("Kla[pick("'","`")]atu barada nikt'o!") + for (var/mob/V in viewers(src)) + V.show_message("The rune turns into gray dust, veiling the surrounding runes.", 3) + qdel(src) + else + user.whisper("Kla[pick("'","`")]atu barada nikt'o!") + to_chat(user, "Your talisman turns into gray dust, veiling the surrounding runes.") + for (var/mob/V in orange(1,src)) + if(V!=user) + V.show_message("Dust emanates from [user]'s hands for a moment.", 3) + + return + if(istype(src,/obj/effect/rune)) + return fizzle(user) + else + call(/obj/effect/rune/proc/fizzle)(user) + return + +/////////////////////////////////////////TENTH RUNE + +/obj/effect/rune/proc/ajourney(var/mob/living/user) //some bits copypastaed from admin tools - Urist + if(user.loc==src.loc) + var/mob/living/carbon/human/L = user + user.say("Fwe[pick("'","`")]sh mah erl nyag r'ya!") + user.visible_message("[user]'s eyes glow blue as \he freezes in place, absolutely motionless.", \ + "The shadow that is your spirit separates itself from your body. You are now in the realm beyond. While this is a great sight, being here strains your mind and body. Hurry...", \ + "You hear only complete silence for a moment.") + announce_ghost_joinleave(user.ghostize(1), 1, "You feel that they had to use some [pick("dark", "black", "blood", "forgotten", "forbidden")] magic to [pick("invade","disturb","disrupt","infest","taint","spoil","blight")] this place!") + L.ajourn = 1 + while(L) + if(L.key) + L.ajourn=0 + return + else + L.take_organ_damage(10, 0) + sleep(100) + return fizzle(user) + + + + +/////////////////////////////////////////ELEVENTH RUNE + +/obj/effect/rune/proc/manifest(var/mob/living/user) + var/obj/effect/rune/this_rune = src + if(user.loc!=this_rune.loc) + return this_rune.fizzle(user) + var/mob/abstract/observer/ghost + for(var/mob/abstract/observer/O in this_rune.loc) + if(!O.client) continue + if(!O.MayRespawn()) continue + if(O.mind && O.mind.current && O.mind.current.stat != DEAD) continue + ghost = O + break + if(!ghost) + return this_rune.fizzle(user) + if(jobban_isbanned(ghost, "cultist")) + return this_rune.fizzle(user) + + user.say("Gal'h'rfikk harfrandid mud[pick("'","`")]gib!") + var/mob/living/carbon/human/apparition/D = new(this_rune.loc) + user.visible_message("A shape forms in the center of the rune. A shape of... a man.", \ + "A shape forms in the center of the rune. A shape of... a man.", \ + "You hear liquid flowing.") + + var/chose_name = 0 + for(var/obj/item/paper/P in this_rune.loc) + if(P.info) + D.real_name = copytext(P.info, findtext(P.info,">")+1, findtext(P.info,"<",2) ) + chose_name = 1 + break + D.universal_speak = 1 + D.underwear = null + D.key = ghost.key + cult.add_antagonist(D.mind) + playsound(loc, 'sound/magic/exit_blood.ogg', 100, 1) + + if(!chose_name) + D.real_name = pick("Anguished", "Blasphemous", "Corrupt", "Cruel", "Depraved", "Despicable", "Disturbed", "Exacerbated", "Foul", "Hateful", "Inexorable", "Implacable", "Impure", "Malevolent", "Malignant", "Malicious", "Pained", "Profane", "Profligate", "Relentless", "Resentful", "Restless", "Spiteful", "Tormented", "Unclean", "Unforgiving", "Vengeful", "Vindictive", "Wicked", "Wronged") + D.real_name += " " + D.real_name += pick("Apparition", "Aptrgangr", "Dis", "Draugr", "Dybbuk", "Eidolon", "Fetch", "Fylgja", "Ghast", "Ghost", "Gjenganger", "Haint", "Phantom", "Phantasm", "Poltergeist", "Revenant", "Shade", "Shadow", "Soul", "Spectre", "Spirit", "Spook", "Visitant", "Wraith") + + log_and_message_admins("used a manifest rune.") + + while(this_rune && user && user.stat==CONSCIOUS && user.client && user.loc==this_rune.loc) + user.take_organ_damage(1, 0) + sleep(30) + if(D) + D.visible_message("[D] slowly dissipates into dust and bones.", \ + "You feel pain, as bonds formed between your soul and this homunculus break.", \ + "You hear faint rustle.") + D.dust() + return + + + +/////////////////////////////////////////TWELFTH RUNE + +/obj/effect/rune/proc/talisman(var/mob/living/user)//only hide, emp, teleport, deafen, blind and tome runes can be imbued atm + var/obj/item/paper/newtalisman + var/unsuitable_newtalisman = 0 + for(var/obj/item/paper/P in src.loc) + if(!P.info) + newtalisman = P + break + else + unsuitable_newtalisman = 1 + if (!newtalisman) + if (unsuitable_newtalisman) + to_chat(user, "The blank is tainted. It is unsuitable.") + return fizzle(user) + + var/obj/effect/rune/imbued_from + var/obj/item/paper/talisman/T + for(var/obj/effect/rune/R in orange(1,src)) + if(R==src) + continue + if(R.word1==cultwords["travel"] && R.word2==cultwords["self"]) //teleport + T = new(src.loc) + T.imbue = "[R.word3]" + T.info = "[R.word3]" + imbued_from = R + break + if(R.word1==cultwords["see"] && R.word2==cultwords["blood"] && R.word3==cultwords["hell"]) //tome + T = new(src.loc) + T.imbue = "newtome" + imbued_from = R + break + if(R.word1==cultwords["destroy"] && R.word2==cultwords["see"] && R.word3==cultwords["technology"]) //emp + T = new(src.loc) + T.imbue = "emp" + imbued_from = R + break + if(R.word1==cultwords["hide"] && R.word2==cultwords["see"] && R.word3==cultwords["blood"]) //conceal + T = new(src.loc) + T.imbue = "conceal" + imbued_from = R + break + if(R.word1==cultwords["hell"] && R.word2==cultwords["destroy"] && R.word3==cultwords["other"]) //armor + T = new(src.loc) + T.imbue = "armor" + imbued_from = R + break + if(R.word1==cultwords["blood"] && R.word2==cultwords["see"] && R.word3==cultwords["hide"]) //reveal + T = new(src.loc) + T.imbue = "revealrunes" + imbued_from = R + break + if(R.word1==cultwords["hide"] && R.word2==cultwords["other"] && R.word3==cultwords["see"]) //deafen + T = new(src.loc) + T.imbue = "deafen" + imbued_from = R + break + if(R.word1==cultwords["destroy"] && R.word2==cultwords["see"] && R.word3==cultwords["other"]) //blind + T = new(src.loc) + T.imbue = "blind" + imbued_from = R + break + if(R.word1==cultwords["self"] && R.word2==cultwords["other"] && R.word3==cultwords["technology"]) //communicat + T = new(src.loc) + T.imbue = "communicate" + imbued_from = R + break + if(R.word1==cultwords["join"] && R.word2==cultwords["hide"] && R.word3==cultwords["technology"]) //communicat + T = new(src.loc) + T.imbue = "runestun" + imbued_from = R + break + if (imbued_from) + for (var/mob/V in viewers(src)) + V.show_message("The runes turn into dust, which then forms into an arcane image on the paper.", 3) + user.say("H'drak v[pick("'","`")]loso, mir'kanas verbot!") + qdel(imbued_from) + qdel(newtalisman) + else + return fizzle(user) + +/////////////////////////////////////////THIRTEENTH RUNE + +/obj/effect/rune/proc/mend(var/mob/living/user) + src = null + user.say("Uhrast ka'hfa heldsagen ver[pick("'","`")]lot!") + user.take_overall_damage(200, 0) + runedec+=10 + user.visible_message("\The [user] keels over dead, \his blood glowing blue as it escapes \his body and dissipates into thin air.", \ + "In the last moment of your humble life, you feel an immense pain as fabric of reality mends... with your blood.", \ + "You hear faint rustle.") + for(,user.stat==2) + sleep(600) + if (!user) + return + runedec-=10 + return + + +/////////////////////////////////////////FOURTEETH RUNE + +// returns 0 if the rune is not used. returns 1 if the rune is used. +/obj/effect/rune/proc/communicate(var/mob/living/user) + . = 1 // Default output is 1. If the rune is deleted it will return 1 + var/input = input(user, "Please choose a message to tell to the other acolytes.", "Voice of Blood", "")//sanitize() below, say() and whisper() have their own + if(!input) + if (istype(src)) + fizzle(user) + return 0 + else + return 0 + + if(istype(src,/obj/effect/rune)) + user.say("O bidai nabora se[pick("'","`")]sma!") + user.say("[input]") + else + user.whisper("O bidai nabora se[pick("'","`")]sma!") + user.whisper("[input]") + + input = sanitize(input) + log_and_message_admins("used a communicate rune to say '[input]'") + for(var/datum/mind/H in cult.current_antagonists) + if (H.current) + to_chat(H.current, "[input]") + qdel(src) + return 1 + +/////////////////////////////////////////FIFTEENTH RUNE + +/obj/effect/rune/proc/sacrifice(var/mob/living/user) + var/list/mob/living/carbon/human/cultsinrange = list() + var/list/mob/living/carbon/human/victims = list() + for(var/mob/living/carbon/human/V in src.loc)//Checks for non-cultist humans to sacrifice + if(ishuman(V)) + if(!(iscultist(V))) + victims += V//Checks for cult status and mob type + for(var/obj/item/I in src.loc)//Checks for MMIs/brains/Intellicards + if(istype(I,/obj/item/organ/internal/brain)) + var/obj/item/organ/internal/brain/B = I + victims += B.brainmob + else if(istype(I,/obj/item/device/mmi)) + var/obj/item/device/mmi/B = I + victims += B.brainmob + else if(istype(I,/obj/item/aicard)) + for(var/mob/living/silicon/ai/A in I) + victims += A + for(var/mob/living/carbon/C in orange(1,src)) + if(iscultist(C) && !C.stat) + cultsinrange += C + C.say("Barhah hra zar[pick("'","`")]garis!") + + for(var/mob/H in victims) + + var/worth = 0 + if(istype(H,/mob/living/carbon/human)) + var/mob/living/carbon/human/lamb = H + if(lamb.species.rarity_value > 3) + worth = 1 + + if (SSticker.mode.name == "Cult") + if(H.mind == cult.sacrifice_target) + if(cultsinrange.len >= 3) + sacrificed += H.mind + if(isrobot(H)) + H.dust()//To prevent the MMI from remaining + else + H.gib() + to_chat(user, "The Geometer of Blood accepts this sacrifice, your objective is now complete.") + else + to_chat(user, "Your target's earthly bonds are too strong. You need more cultists to succeed in this ritual.") + else + if(cultsinrange.len >= 3) + if(H.stat != DEAD) + if(prob(80) || worth) + to_chat(user, "The Geometer of Blood accepts this [worth ? "exotic " : ""]sacrifice.") + else + to_chat(user, "The Geometer of Blood accepts this sacrifice.") + to_chat(user, "However, this soul was not enough to gain His favor.") + if(isrobot(H)) + H.dust()//To prevent the MMI from remaining + else + H.gib() + else + if(prob(40) || worth) + to_chat(user, "The Geometer of Blood accepts this [worth ? "exotic " : ""]sacrifice.") + else + to_chat(user, "The Geometer of Blood accepts this sacrifice.") + to_chat(user, "However, a mere dead body is not enough to satisfy Him.") + if(isrobot(H)) + H.dust()//To prevent the MMI from remaining + else + H.gib() + else + if(H.stat != DEAD) + to_chat(user, "The victim is still alive, you will need more cultists chanting for the sacrifice to succeed.") + else + if(prob(40)) + + to_chat(user, "The Geometer of Blood accepts this sacrifice.") + else + to_chat(user, "The Geometer of Blood accepts this sacrifice.") + to_chat(user, "However, a mere dead body is not enough to satisfy Him.") + if(isrobot(H)) + H.dust()//To prevent the MMI from remaining + else + H.gib() + else + if(cultsinrange.len >= 3) + if(H.stat != DEAD) + if(prob(80)) + to_chat(user, "The Geometer of Blood accepts this sacrifice.") + else + to_chat(user, "The Geometer of Blood accepts this sacrifice.") + to_chat(user, "However, this soul was not enough to gain His favor.") + if(isrobot(H)) + H.dust()//To prevent the MMI from remaining + else + H.gib() + else + if(prob(40)) + to_chat(user, "The Geometer of Blood accepts this sacrifice.") + else + to_chat(user, "The Geometer of Blood accepts this sacrifice.") + to_chat(user, "However, a mere dead body is not enough to satisfy Him.") + if(isrobot(H)) + H.dust()//To prevent the MMI from remaining + else + H.gib() + else + if(H.stat != DEAD) + to_chat(user, "The victim is still alive, you will need more cultists chanting for the sacrifice to succeed.") + else + if(prob(40)) + to_chat(user, "The Geometer of Blood accepts this sacrifice.") + else + to_chat(user, "The Geometer of Blood accepts this sacrifice.") + to_chat(user, "However, a mere dead body is not enough to satisfy Him.") + if(isrobot(H)) + H.dust()//To prevent the MMI from remaining + else + H.gib() + +/////////////////////////////////////////SIXTEENTH RUNE + +/obj/effect/rune/proc/revealrunes(var/mob/living/user, var/obj/W as obj) + var/go=0 + var/rad + var/S=0 + if(istype(W,/obj/effect/rune)) + rad = 6 + go = 1 + if (istype(W,/obj/item/paper/talisman)) + rad = 4 + go = 1 + if (istype(W,/obj/item/nullrod)) + rad = 2 + go = 1 + if(go) + for(var/obj/effect/rune/R in orange(rad,src)) + if(R!=src) + R:visibility=15 + S=1 + if(S) + if(istype(W,/obj/item/nullrod)) + visible_message(span("warning", "Arcane markings suddenly glow from underneath a thin layer of dust!")) + return + if(istype(W,/obj/effect/rune)) + user.say("Nikt[pick("'","`")]o barada kla'atu!") + for (var/mob/V in viewers(src)) + V.show_message("The rune turns into red dust, reveaing the surrounding runes.", 3) + qdel(src) + return + if(istype(W,/obj/item/paper/talisman)) + user.whisper("Nikt[pick("'","`")]o barada kla'atu!") + to_chat(user, "Your talisman turns into red dust, revealing the surrounding runes.") + for (var/mob/V in orange(1,user.loc)) + if(V!=user) + V.show_message("Red dust emanates from [user]'s hands for a moment.", 3) + return + return + if(istype(W,/obj/effect/rune)) + return fizzle(user) + if(istype(W,/obj/item/paper/talisman)) + call(/obj/effect/rune/proc/fizzle)(user) + return + +/////////////////////////////////////////SEVENTEENTH RUNE + +/obj/effect/rune/proc/wall(var/mob/living/user) + user.say("Khari[pick("'","`")]d! Eske'te tannin!") + src.density = !src.density + user.take_organ_damage(2, 0) + if(src.density) + to_chat(user, "Your blood flows into the rune, and you feel that the very space over the rune thickens.") + else + to_chat(user, "Your blood flows into the rune, and you feel as the rune releases its grasp on space.") + return + +/////////////////////////////////////////EIGHTTEENTH RUNE + +/obj/effect/rune/proc/freedom(var/mob/living/user) + var/list/mob/living/carbon/cultists = new + for(var/datum/mind/H in cult.current_antagonists) + if (istype(H.current,/mob/living/carbon)) + cultists+=H.current + var/list/mob/living/carbon/users = new + for(var/mob/living/carbon/C in orange(1,src)) + if(iscultist(C) && !C.stat) + users+=C + var/dam = round(15 / users.len) + if(users.len>=3) + var/mob/living/carbon/cultist = input("Choose the one who you want to free", "Followers of Geometer") as null|anything in (cultists - users) + if(!cultist) + return fizzle(user) + if (cultist == user) //just to be sure. + return + if(!(cultist.buckled || \ + cultist.handcuffed || \ + istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle) || \ + (istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded) || \ + (istype(cultist.loc, /obj/structure/closet/secure_closet)&&cultist.loc:locked) || \ + (istype(cultist.loc, /obj/machinery/dna_scannernew)&&cultist.loc:locked) \ + )) + to_chat(user, "The [cultist] is already free.") + return + cultist.buckled = null + if (cultist.handcuffed) + cultist.drop_from_inventory(cultist.handcuffed) + if (cultist.legcuffed) + cultist.drop_from_inventory(cultist.legcuffed) + if (istype(cultist.wear_mask, /obj/item/clothing/mask/muzzle)) + cultist.drop_from_inventory(cultist.wear_mask) + if(istype(cultist.loc, /obj/structure/closet)&&cultist.loc:welded) + cultist.loc:welded = 0 + if(istype(cultist.loc, /obj/structure/closet/secure_closet)&&cultist.loc:locked) + cultist.loc:locked = 0 + if(istype(cultist.loc, /obj/machinery/dna_scannernew)&&cultist.loc:locked) + cultist.loc:locked = 0 + for(var/mob/living/carbon/C in users) + user.take_overall_damage(dam, 0) + C.say("Khari[pick("'","`")]d! Gual'te nikka!") + qdel(src) + return fizzle(user) + +/////////////////////////////////////////NINETEENTH RUNE + +/obj/effect/rune/proc/cultsummon(var/mob/living/user) + var/list/mob/living/carbon/cultists = new + for(var/datum/mind/H in cult.current_antagonists) + if (istype(H.current,/mob/living/carbon)) + cultists+=H.current + var/list/mob/living/carbon/users = new + for(var/mob/living/carbon/C in orange(1,src)) + if(iscultist(C) && !C.stat) + users += C + if(users.len>=3) + var/mob/living/carbon/cultist = input("Choose the one who you want to summon", "Followers of Geometer") as null|anything in (cultists - user) + if(!cultist) + return fizzle(user) + if (cultist == user) //just to be sure. + return + if(cultist.buckled || cultist.handcuffed || (!isturf(cultist.loc) && !istype(cultist.loc, /obj/structure/closet))) + to_chat(user, "You cannot summon \the [cultist], for \his shackles of blood are strong.") + return fizzle(user) + cultist.forceMove(src.loc) + cultist.lying = 1 + cultist.regenerate_icons() + + var/dam = round(25 / (users.len/2)) //More people around the rune less damage everyone takes. Minimum is 3 cultists + + for(var/mob/living/carbon/human/C in users) + if(iscultist(C) && !C.stat) + C.say("N'ath reth sh'yro eth d[pick("'","`")]rekkathnor!") + C.take_overall_damage(dam, 0) + if(users.len <= 4) // You did the minimum, this is going to hurt more and we're going to stun you. + C.apply_effect(rand(3,6), STUN) + C.apply_effect(1, WEAKEN) + user.visible_message("Rune disappears with a flash of red light, and in its place now a body lies.", \ + "You are blinded by the flash of red light! After you're able to see again, you see that now instead of the rune there's a body.", \ + "You hear a pop and smell ozone.") + qdel(src) + return fizzle(user) + +/////////////////////////////////////////TWENTIETH RUNES + +/obj/effect/rune/proc/deafen(var/mob/living/user) + if(istype(src,/obj/effect/rune)) + var/list/affected = new() + for(var/mob/living/carbon/C in range(7,src)) + if (iscultist(C)) + continue + var/obj/item/nullrod/N = locate() in C + if(N) + continue + C.ear_deaf += 50 + C.show_message("The world around you suddenly becomes quiet.", 3) + affected += C + if(prob(1)) + C.sdisabilities |= DEAF + if(affected.len) + user.say("Sti[pick("'","`")] kaliedir!") + to_chat(user, "The world becomes quiet as the deafening rune dissipates into fine dust.") + admin_attacker_log_many_victims(user, affected, "Used a deafen rune.", "Was victim of a deafen rune.", "used a deafen rune on") + qdel(src) + else + return fizzle(user) + else + var/list/affected = new() + for(var/mob/living/carbon/C in range(7,user)) + if (iscultist(C)) + continue + var/obj/item/nullrod/N = locate() in C + if(N) + continue + C.ear_deaf += 30 + //talismans is weaker. + C.show_message("The world around you suddenly becomes quiet.", 3) + affected += C + if(affected.len) + user.whisper("Sti[pick("'","`")] kaliedir!") + to_chat(user, "Your talisman turns into gray dust, deafening everyone around.") + admin_attacker_log_many_victims(user, affected, "Used a deafen rune.", "Was victim of a deafen rune.", "used a deafen rune on") + for (var/mob/V in orange(1,src)) + if(!(iscultist(V))) + V.show_message("Dust flows from [user]'s hands for a moment, and the world suddenly becomes quiet..", 3) + return + +/obj/effect/rune/proc/blind(var/mob/living/user) + if(istype(src,/obj/effect/rune)) + var/list/affected = new() + for(var/mob/living/carbon/C in viewers(src)) + if (iscultist(C)) + continue + var/obj/item/nullrod/N = locate() in C + if(N) + continue + C.eye_blurry += 50 + C.eye_blind += 20 + if(prob(5)) + C.disabilities |= NEARSIGHTED + if(prob(10)) + C.sdisabilities |= BLIND + C.show_message("Suddenly you see a red flash that blinds you.", 3) + affected += C + if(affected.len) + user.say("Sti[pick("'","`")] kaliesin!") + to_chat(user, "The rune flashes, blinding those who not follow the Nar-Sie, and dissipates into fine dust.") + admin_attacker_log_many_victims(user, affected, "Used a blindness rune.", "Was victim of a blindness rune.", "used a blindness rune on") + qdel(src) + else + return fizzle(user) + else + var/list/affected = new() + for(var/mob/living/carbon/C in view(2,user)) + if (iscultist(C)) + continue + var/obj/item/nullrod/N = locate() in C + if(N) + continue + C.eye_blurry += 30 + C.eye_blind += 10 + //talismans is weaker. + affected += C + C.show_message("You feel a sharp pain in your eyes, and the world disappears into darkness..", 3) + if(affected.len) + user.whisper("Sti[pick("'","`")] kaliesin!") + to_chat(user, "Your talisman turns into gray dust, blinding those who not follow the Nar-Sie.") + admin_attacker_log_many_victims(user, affected, "Used a blindness rune.", "Was victim of a blindness rune.", "used a blindness rune on") + return + + +/obj/effect/rune/proc/bloodboil(var/mob/living/user) //cultists need at least one DANGEROUS rune. Even if they're all stealthy. +/* + var/list/mob/living/carbon/cultists = new + for(var/datum/mind/H in ticker.mode.cult) + if (istype(H.current,/mob/living/carbon)) + cultists+=H.current +*/ + var/list/cultists = new //also, wording for it is old wording for obscure rune, which is now hide-see-blood. + var/list/victims = new +// var/list/cultboil = list(cultists-user) //and for this words are destroy-see-blood. + for(var/mob/living/carbon/C in orange(1,src)) + if(iscultist(C) && !C.stat) + cultists+=C + if(cultists.len>=3) + for(var/mob/living/carbon/M in viewers(user)) + if(iscultist(M)) + continue + var/obj/item/nullrod/N = locate() in M + if(N) + continue + M.take_overall_damage(51,51) + to_chat(M, "Your blood boils!") + victims += M + if(prob(5)) + spawn(5) + M.gib() + for(var/obj/effect/rune/R in view(src)) + if(prob(10)) + explosion(R.loc, -1, 0, 1, 5) + for(var/mob/living/carbon/human/C in orange(1,src)) + if(iscultist(C) && !C.stat) + C.say("Dedo ol[pick("'","`")]btoh!") + C.take_overall_damage(15, 0) + admin_attacker_log_many_victims(user, victims, "Used a blood boil rune.", "Was the victim of a blood boil rune.", "used a blood boil rune on") + log_and_message_admins_many(cultists - user, "assisted activating a blood boil rune.") + qdel(src) + else + return fizzle(user) + return + +// WIP rune, I'll wait for Rastaf0 to add limited blood. + +/obj/effect/rune/proc/burningblood(var/mob/living/user) + var/culcount = 0 + for(var/mob/living/carbon/C in orange(1,src)) + if(iscultist(C) && !C.stat) + culcount++ + if(culcount >= 5) + for(var/obj/effect/rune/R in rune_list) + if(R.blood_DNA == src.blood_DNA) + for(var/mob/living/M in orange(2,R)) + M.take_overall_damage(0,15) + if (R.invisibility>M.see_invisible) + to_chat(M, "Aargh it burns!") + else + to_chat(M, "Rune suddenly ignites, burning you!") + var/turf/T = get_turf(R) + T.hotspot_expose(700,125) + for(var/obj/effect/decal/cleanable/blood/B in world) + if(B.blood_DNA == src.blood_DNA) + for(var/mob/living/M in orange(1,B)) + M.take_overall_damage(0,5) + to_chat(M, "Blood suddenly ignites, burning you!") + var/turf/T = get_turf(B) + T.hotspot_expose(700,125) + qdel(B) + qdel(src) + +////////// Rune 24 (counting burningblood, which kinda doesnt work yet.) + +/obj/effect/rune/proc/runestun(var/mob/living/user, var/mob/living/T as mob) + if(istype(src,/obj/effect/rune)) ///When invoked as rune, flash and briefly stun everyone around. + user.say("Fuu ma[pick("'","`")]jin!") + for(var/mob/living/L in viewers(src)) + if(iscultist(L)) + continue + if(iscarbon(L)) + var/mob/living/carbon/C = L + flick("e_flash", C.flash) + if(C.stuttering < 1 && (!(HULK in C.mutations))) + C.stuttering = 1 + C.Weaken(3) + C.Stun(3) + C.silent += 15 + C.show_message("The rune explodes in a bright flash.", 3) + admin_attack_log(user, C, "Used a stun rune.", "Was victim of a stun rune.", "used a stun rune on") + + else if(issilicon(L)) + var/mob/living/silicon/S = L + S.Weaken(5) + flick("e_flash", S.flash) + S.silent += 15 + S.show_message("BZZZT... The rune has exploded in a bright flash.", 3) + admin_attack_log(user, S, "Used a stun rune.", "Was victim of a stun rune.", "used a stun rune on") + qdel(src) + else ///When invoked as talisman, flash and mute the target mob. + user.say("Dream sign ''Evil sealing talisman'[pick("'","`")]!") + var/obj/item/nullrod/N = locate() in T + if(N) + for(var/mob/O in viewers(T, null)) + O.show_message(text("[] invokes a talisman at [], but they are unaffected!", user, T), 1) + else + for(var/mob/O in viewers(T, null)) + O.show_message(text("[] invokes a talisman at []", user, T), 1) + + if(issilicon(T)) + flick("e_flash", T.flash) + T.silent += 15 + admin_attack_log(user, T, "Used a stun talisman.", "Was victim of a stun talisman.", "used a stun talisman on") + else if(iscarbon(T)) + var/mob/living/carbon/C = T + flick("e_flash", C.flash) + if(!(HULK in C.mutations)) + C.silent += 15 + admin_attack_log(user, C, "Used a stun talisman.", "Was victim of a stun talisman.", "used a stun talisman on") + return + +/////////////////////////////////////////TWENTY-FIFTH RUNE + +/obj/effect/rune/proc/armor(var/mob/living/user) + if(istype(src,/obj/effect/rune)) + user.say("N'ath reth sh'yro eth d[pick("'","`")]raggathnor!") + else + user.whisper("N'ath reth sh'yro eth d[pick("'","`")]raggathnor!") + user.visible_message("The rune disappears with a flash of red light, and a set of armor appears on [user]...", \ + "You are blinded by the flash of red light! After you're able to see again, you see that you are now wearing a set of armor.") + + if(istype(user, /mob/living/simple_animal/construct)) + var/mob/living/simple_animal/construct/C = user + var/construct_class + if(narsie_cometh) + construct_class = alert(C, "Please choose which type of construct you wish to become.",,"Juggernaut","Wraith","Harvester") + else + construct_class = alert(C, "Please choose which type of construct you wish to become.",,"Juggernaut","Wraith","Artificer") + switch(construct_class) + if("Juggernaut") + var/mob/living/simple_animal/construct/armoured/Z = new /mob/living/simple_animal/construct/armoured (get_turf(C.loc)) + Z.key = C.key + if(iscultist(C)) + cult.add_antagonist(Z.mind) + C.death() + to_chat(Z, "You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.") + to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + Z.cancel_camera() + if("Wraith") + var/mob/living/simple_animal/construct/wraith/Z = new /mob/living/simple_animal/construct/wraith (get_turf(C.loc)) + Z.key = C.key + if(iscultist(C)) + cult.add_antagonist(Z.mind) + C.death() + to_chat(Z, "You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.") + to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + Z.cancel_camera() + if("Artificer") + var/mob/living/simple_animal/construct/builder/Z = new /mob/living/simple_animal/construct/builder (get_turf(C.loc)) + Z.key = C.key + if(iscultist(C)) + cult.add_antagonist(Z.mind) + C.death() + to_chat(Z, "You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs") + to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + Z.cancel_camera() + if("Harvester") + var/mob/living/simple_animal/construct/builder/Z = new /mob/living/simple_animal/construct/harvester (get_turf(C.loc)) + Z.key = C.key + if(iscultist(C)) + cult.add_antagonist(Z.mind) + C.death() + to_chat(Z, "You are playing a Harvester. You are gifted with the ability to open doors with your mind, to draw runes at will, and to teleport back to Nar'Sie. Seek out all non-believers and bring them to the Geometer.") + to_chat(Z, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + Z.cancel_camera() + else + user.equip_to_slot_or_del(new /obj/item/clothing/head/culthood/alt(user), slot_head) + user.equip_to_slot_or_del(new /obj/item/clothing/suit/cultrobes/alt(user), slot_wear_suit) + user.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes) + user.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(user), slot_back) + //the above update their overlay icons cache but do not call update_icons() + //the below calls update_icons() at the end, which will update overlay icons by using the (now updated) cache + user.put_in_hands(new /obj/item/melee/cultblade(user)) //put in hands or on floor + + qdel(src) + return diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm index d8b1bbdd58f..bcfc234fcaa 100644 --- a/code/game/gamemodes/cult/talisman.dm +++ b/code/game/gamemodes/cult/talisman.dm @@ -1,118 +1,118 @@ -/obj/item/paper/talisman - icon_state = "paper_talisman" - var/imbue = null - var/uses = 0 - info = "


    " - -/obj/item/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)(user) - if("armor") - call(/obj/effect/rune/proc/armor)(user) - if("emp") - call(/obj/effect/rune/proc/emp)(user, user.loc,3) - if("conceal") - call(/obj/effect/rune/proc/obscure)(user, 2) - if("revealrunes") - call(/obj/effect/rune/proc/revealrunes)(user, src) - if("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") - call(/obj/effect/rune/proc/teleport)(user, imbue) - if("communicate") - //If the user cancels the talisman this var will be set to 0 - delete = call(/obj/effect/rune/proc/communicate)(user) - if("deafen") - call(/obj/effect/rune/proc/deafen)(user) - if("blind") - call(/obj/effect/rune/proc/blind)(user) - 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/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)(user, T) - qdel(src) - else - ..() ///If its some other talisman, use the generic attack code, is this supposed to work this way? - else - ..() - - -/obj/item/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 silence and flash 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/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/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "newtome" - if("teleport") - var/obj/item/paper/talisman/T = new /obj/item/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/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "emp" - if("conceal") - var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "conceal" - if("communicate") - var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "communicate" - if("runestun") - var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) - T.imbue = "runestun" - if("armor") - var/obj/item/paper/talisman/T = new /obj/item/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/paper/talisman/supply - imbue = "supply" - uses = 5 +/obj/item/paper/talisman + icon_state = "paper_talisman" + var/imbue = null + var/uses = 0 + info = "


    " + +/obj/item/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)(user) + if("armor") + call(/obj/effect/rune/proc/armor)(user) + if("emp") + call(/obj/effect/rune/proc/emp)(user, user.loc,3) + if("conceal") + call(/obj/effect/rune/proc/obscure)(user, 2) + if("revealrunes") + call(/obj/effect/rune/proc/revealrunes)(user, src) + if("ire", "ego", "nahlizet", "certum", "veri", "jatkaa", "balaq", "mgar", "karazet", "geeri") + call(/obj/effect/rune/proc/teleport)(user, imbue) + if("communicate") + //If the user cancels the talisman this var will be set to 0 + delete = call(/obj/effect/rune/proc/communicate)(user) + if("deafen") + call(/obj/effect/rune/proc/deafen)(user) + if("blind") + call(/obj/effect/rune/proc/blind)(user) + 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/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)(user, T) + qdel(src) + else + ..() ///If its some other talisman, use the generic attack code, is this supposed to work this way? + else + ..() + + +/obj/item/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 silence and flash 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/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/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) + T.imbue = "newtome" + if("teleport") + var/obj/item/paper/talisman/T = new /obj/item/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/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) + T.imbue = "emp" + if("conceal") + var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) + T.imbue = "conceal" + if("communicate") + var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) + T.imbue = "communicate" + if("runestun") + var/obj/item/paper/talisman/T = new /obj/item/paper/talisman(get_turf(usr)) + T.imbue = "runestun" + if("armor") + var/obj/item/paper/talisman/T = new /obj/item/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/paper/talisman/supply + imbue = "supply" + uses = 5 diff --git a/code/game/gamemodes/endgame/endgame.dm b/code/game/gamemodes/endgame/endgame.dm index 652b02e8219..6a349deb845 100644 --- a/code/game/gamemodes/endgame/endgame.dm +++ b/code/game/gamemodes/endgame/endgame.dm @@ -1,73 +1,73 @@ -/********************** - * 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() + /datum/universal_state/proc/convert_parallax(parallax_spacemaster) \ No newline at end of file diff --git a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm index df7e3e795fb..96fc5b72708 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/blob.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/blob.dm @@ -1,129 +1,129 @@ -// QUALITY COPYPASTA -/turf/unsimulated/wall/supermatter - name = "Bluespace" - desc = "THE END IS right now actually." - - icon = 'icons/turf/space.dmi' - icon_state = "bluespace" - - layer = LIGHTING_LAYER + 1 - light_color = "#0066FF" - light_range = 2 - light_power = 2 - - var/spawned = 0 // DIR mask - var/next_check = 0 - var/list/avail_dirs = list(NORTH,SOUTH,EAST,WEST) - -/turf/unsimulated/wall/supermatter/Initialize() - . = ..() - START_PROCESSING(SScalamity, src) - -/turf/unsimulated/wall/supermatter/process() - if (!(SScalamity.times_fired % 2)) - // SScalamity ticks every 2s, we want to process every 4. - return - - - // No more available directions? Bail. - if(avail_dirs.len == 0) - STOP_PROCESSING(SScalamity, src) - return - - // Choose a direction. - var/pdir = pick(avail_dirs) - avail_dirs -= pdir - var/turf/T = get_step(src, pdir) - var/turf/A = GetAbove(T) - var/turf/B = GetBelow(T) - - // EXPAND - if(!istype(T,type)) - // Do pretty fadeout animation for 1s. - new /obj/effect/overlay/bluespacify(T) - addtimer(CALLBACK(src, .proc/after_tick, T), 10) - if(A && !istype(A,type)) - new /obj/effect/overlay/bluespacify(A) - addtimer(CALLBACK(src, .proc/after_tick, A), 10) - if(B && !istype(B,type)) - new /obj/effect/overlay/bluespacify(B) - addtimer(CALLBACK(src, .proc/after_tick, B), 10) - if((spawned & (NORTH|SOUTH|EAST|WEST)) == (NORTH|SOUTH|EAST|WEST)) - STOP_PROCESSING(SScalamity, src) - -/turf/unsimulated/wall/supermatter/proc/after_tick(turf/T) - T.lighting_clear_overlay() - for(var/atom/movable/A in T) - if (A && A.simulated) // No eating lighting overlays. - if(istype(A, /mob/living)) - qdel(A) - else if(istype(A, /mob)) // Observers, AI cameras. - continue - else - qdel(A) - - CHECK_TICK - T.ChangeTurf(type) - -/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/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,src) - Consume(W) - - -/turf/unsimulated/wall/supermatter/CollidedWith(atom/AM) - if (!AM.simulated) - return ..() - - if(istype(AM, /mob/living)) - AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... \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/abstract/observer)) - return - - qdel(user) - -/turf/unsimulated/wall/supermatter/no_spread - avail_dirs = list() +// QUALITY COPYPASTA +/turf/unsimulated/wall/supermatter + name = "Bluespace" + desc = "THE END IS right now actually." + + icon = 'icons/turf/space.dmi' + icon_state = "bluespace" + + layer = LIGHTING_LAYER + 1 + light_color = "#0066FF" + light_range = 2 + light_power = 2 + + var/spawned = 0 // DIR mask + var/next_check = 0 + var/list/avail_dirs = list(NORTH,SOUTH,EAST,WEST) + +/turf/unsimulated/wall/supermatter/Initialize() + . = ..() + START_PROCESSING(SScalamity, src) + +/turf/unsimulated/wall/supermatter/process() + if (!(SScalamity.times_fired % 2)) + // SScalamity ticks every 2s, we want to process every 4. + return + + + // No more available directions? Bail. + if(avail_dirs.len == 0) + STOP_PROCESSING(SScalamity, src) + return + + // Choose a direction. + var/pdir = pick(avail_dirs) + avail_dirs -= pdir + var/turf/T = get_step(src, pdir) + var/turf/A = GetAbove(T) + var/turf/B = GetBelow(T) + + // EXPAND + if(!istype(T,type)) + // Do pretty fadeout animation for 1s. + new /obj/effect/overlay/bluespacify(T) + addtimer(CALLBACK(src, .proc/after_tick, T), 10) + if(A && !istype(A,type)) + new /obj/effect/overlay/bluespacify(A) + addtimer(CALLBACK(src, .proc/after_tick, A), 10) + if(B && !istype(B,type)) + new /obj/effect/overlay/bluespacify(B) + addtimer(CALLBACK(src, .proc/after_tick, B), 10) + if((spawned & (NORTH|SOUTH|EAST|WEST)) == (NORTH|SOUTH|EAST|WEST)) + STOP_PROCESSING(SScalamity, src) + +/turf/unsimulated/wall/supermatter/proc/after_tick(turf/T) + T.lighting_clear_overlay() + for(var/atom/movable/A in T) + if (A && A.simulated) // No eating lighting overlays. + if(istype(A, /mob/living)) + qdel(A) + else if(istype(A, /mob)) // Observers, AI cameras. + continue + else + qdel(A) + + CHECK_TICK + T.ChangeTurf(type) + +/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/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,src) + Consume(W) + + +/turf/unsimulated/wall/supermatter/CollidedWith(atom/AM) + if (!AM.simulated) + return ..() + + if(istype(AM, /mob/living)) + AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... \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/abstract/observer)) + return + + qdel(user) + +/turf/unsimulated/wall/supermatter/no_spread + avail_dirs = list() diff --git a/code/game/gamemodes/endgame/supermatter_cascade/portal.dm b/code/game/gamemodes/endgame/supermatter_cascade/portal.dm index 0507ca5a2da..d66ed7d9d96 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/portal.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/portal.dm @@ -1,87 +1,87 @@ -/*** 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 - - layer=LIGHTING_LAYER+2 // ITS SO BRIGHT - - consume_range = 6 - -/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)) - L.forceMove(O.loc) - else - do_teleport(L, pick(endgame_safespawns)) //dead-on precision - - else if (isturf(A)) - var/turf/T = A - var/dist = get_dist(T, src) - if (dist <= consume_range && T.density) - T.density = 0 - - 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",LIGHTING_LAYER+2,1) - 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 + + layer=LIGHTING_LAYER+2 // ITS SO BRIGHT + + consume_range = 6 + +/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)) + L.forceMove(O.loc) + else + do_teleport(L, pick(endgame_safespawns)) //dead-on precision + + else if (isturf(A)) + var/turf/T = A + var/dist = get_dist(T, src) + if (dist <= consume_range && T.density) + T.density = 0 + + 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",LIGHTING_LAYER+2,1) + 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 58c44b60914..02e29f0c5de 100644 --- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm +++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm @@ -1,143 +1,143 @@ -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) - if(T.name == "space") - T.add_overlay("end01") - T.underlays -= "end01" - else - T.cut_overlay("end01") - -/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() - SSgarbage.disable() - - to_world("You are blinded by a brilliant flash of energy.") - - to_world(sound('sound/effects/cascade.ogg')) - - for(var/mob/M in player_list) - flick("e_flash", M.flash) - - 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)) - var/time = rand(30, 60) - log_debug("universal_state/cascade: Announcing to world in [time] seconds.") - log_debug("universal_state/cascade: Ending universe in [(time SECONDS + 5 MINUTES)/10] seconds.") - addtimer(CALLBACK(src, .proc/announce_to_world), time SECONDS) - addtimer(CALLBACK(src, .proc/end_universe), time SECONDS + 5 MINUTES) - -/datum/universal_state/supermatter_cascade/proc/announce_to_world() - 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 [current_map.boss_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 SSmachinery.processing_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() - -/datum/universal_state/supermatter_cascade/proc/end_universe() - SSticker.station_explosion_cinematic(0, null, current_map.player_levels) // TODO: Custom cinematic - universe_has_ended = 1 - -/datum/universal_state/supermatter_cascade/proc/AreaSet() - for(var/area/A in all_areas) - if(!istype(A,/area) || istype(A, /area/space) || istype(A,/area/beach)) - continue - - A.queue_icon_update() - CHECK_TICK - -/datum/universal_state/supermatter_cascade/OverlayAndAmbientSet() - set waitfor = FALSE - for(var/turf/T in turfs) - if(istype(T, /turf/space)) - T.add_overlay("end01") - else - if (isNotAdminLevel(T.z)) - T.underlays += "end01" - CHECK_TICK - - for(var/datum/lighting_corner/C in SSlighting.lighting_corners) - if (!C.active) - continue - - if (isNotAdminLevel(C.z)) - C.update_lumcount(0.15, 0.15, 0.5) - CHECK_TICK - -/datum/universal_state/supermatter_cascade/proc/MiscSet() - for (var/obj/machinery/firealarm/alm in SSmachinery.processing_machines) - if (!(alm.stat & BROKEN)) - alm.ex_act(2) - CHECK_TICK - -/datum/universal_state/supermatter_cascade/proc/APCSet() - for (var/obj/machinery/power/apc/APC in SSmachinery.processing_machines) - if (!(APC.stat & BROKEN) && !APC.is_critical) - APC.chargemode = 0 - if(APC.cell) - APC.cell.charge = 0 - APC.emagged = 1 - APC.queue_icon_update() - CHECK_TICK - -/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) - flick("e_flash", M.current.flash) - - clear_antag_roles(M) - CHECK_TICK +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) + if(T.name == "space") + T.add_overlay("end01") + T.underlays -= "end01" + else + T.cut_overlay("end01") + +/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() + SSgarbage.disable() + + to_world("You are blinded by a brilliant flash of energy.") + + to_world(sound('sound/effects/cascade.ogg')) + + for(var/mob/M in player_list) + flick("e_flash", M.flash) + + 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)) + var/time = rand(30, 60) + log_debug("universal_state/cascade: Announcing to world in [time] seconds.") + log_debug("universal_state/cascade: Ending universe in [(time SECONDS + 5 MINUTES)/10] seconds.") + addtimer(CALLBACK(src, .proc/announce_to_world), time SECONDS) + addtimer(CALLBACK(src, .proc/end_universe), time SECONDS + 5 MINUTES) + +/datum/universal_state/supermatter_cascade/proc/announce_to_world() + 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 [current_map.boss_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 SSmachinery.processing_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() + +/datum/universal_state/supermatter_cascade/proc/end_universe() + SSticker.station_explosion_cinematic(0, null, current_map.player_levels) // TODO: Custom cinematic + universe_has_ended = 1 + +/datum/universal_state/supermatter_cascade/proc/AreaSet() + for(var/area/A in all_areas) + if(!istype(A,/area) || istype(A, /area/space) || istype(A,/area/beach)) + continue + + A.queue_icon_update() + CHECK_TICK + +/datum/universal_state/supermatter_cascade/OverlayAndAmbientSet() + set waitfor = FALSE + for(var/turf/T in turfs) + if(istype(T, /turf/space)) + T.add_overlay("end01") + else + if (isNotAdminLevel(T.z)) + T.underlays += "end01" + CHECK_TICK + + for(var/datum/lighting_corner/C in SSlighting.lighting_corners) + if (!C.active) + continue + + if (isNotAdminLevel(C.z)) + C.update_lumcount(0.15, 0.15, 0.5) + CHECK_TICK + +/datum/universal_state/supermatter_cascade/proc/MiscSet() + for (var/obj/machinery/firealarm/alm in SSmachinery.processing_machines) + if (!(alm.stat & BROKEN)) + alm.ex_act(2) + CHECK_TICK + +/datum/universal_state/supermatter_cascade/proc/APCSet() + for (var/obj/machinery/power/apc/APC in SSmachinery.processing_machines) + if (!(APC.stat & BROKEN) && !APC.is_critical) + APC.chargemode = 0 + if(APC.cell) + APC.cell.charge = 0 + APC.emagged = 1 + APC.queue_icon_update() + CHECK_TICK + +/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) + flick("e_flash", M.current.flash) + + clear_antag_roles(M) + CHECK_TICK diff --git a/code/game/gamemodes/events/PortalStorm.dm b/code/game/gamemodes/events/PortalStorm.dm index fac80b992d9..ccac785aea8 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 turfs) - if(T.z < 5 && istype(T,/turf/simulated/floor)) - turfs += T - - for(var/turf/T in turfs) - 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 turfs) + if(T.z < 5 && istype(T,/turf/simulated/floor)) + turfs += T + + for(var/turf/T in turfs) + 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 1ff7a338c9e..82c0ab0461b 100644 --- a/code/game/gamemodes/events/black_hole.dm +++ b/code/game/gamemodes/events/black_hole.dm @@ -1,93 +1,93 @@ -/obj/effect/bhole - name = "black hole" - icon = 'icons/obj/objects.dmi' - desc = "FUCK FUCK FUCK AAAHHH" - icon_state = "bhole3" - opacity = 1 - unacidable = 1 - density = 0 - anchored = 1 - -/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/turf/T = loc - var/base_turf = T.baseturf - 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 = 0 - step(src,pick(alldirs)) - src.anchored = 1 - -/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 = 1 + density = 0 + anchored = 1 + +/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/turf/T = loc + var/base_turf = T.baseturf + 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 = 0 + step(src,pick(alldirs)) + src.anchored = 1 + +/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 a4811dc9ead..bc758e3d842 100644 --- a/code/game/gamemodes/events/holidays/Christmas.dm +++ b/code/game/gamemodes/events/holidays/Christmas.dm @@ -1,66 +1,66 @@ -/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/a_gift(T) - //for(var/mob/living/simple_animal/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_animal/hostile/tree/evil_tree = new /mob/living/simple_animal/hostile/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/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/toy/xmas_cracker/attack(mob/target, mob/user, var/target_zone) - if( !cracked && istype(target,/mob/living/carbon/human) && (target.stat == CONSCIOUS) && !target.get_active_hand() ) - target.visible_message("[user] and [target] pop \an [src]! *pop*", "You pull \an [src] with [target]! *pop*", "You hear a *pop*.") - var/obj/item/paper/Joke = new /obj/item/paper(user.loc) - var/title = "[pick("awful","terrible","unfunny")] joke" - var/content = 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 in Nanotrasen?\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.") - Joke.set_content_unsafe(title, content) - new /obj/item/clothing/head/festive(target.loc) - user.update_icons() - cracked = 1 - icon_state = "cracker1" - var/obj/item/toy/xmas_cracker/other_half = new /obj/item/toy/xmas_cracker(target) - other_half.cracked = 1 - other_half.icon_state = "cracker2" - target.put_in_active_hand(other_half) - playsound(user, '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 = null - -/obj/item/clothing/head/festive/santa - name = "santa hat" - icon_state = "santahat" - body_parts_covered = HEAD - desc = "A cheap fabric santa hat, with a fake beard and a little bell at the end." +/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/a_gift(T) + //for(var/mob/living/simple_animal/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_animal/hostile/tree/evil_tree = new /mob/living/simple_animal/hostile/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/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/toy/xmas_cracker/attack(mob/target, mob/user, var/target_zone) + if( !cracked && istype(target,/mob/living/carbon/human) && (target.stat == CONSCIOUS) && !target.get_active_hand() ) + target.visible_message("[user] and [target] pop \an [src]! *pop*", "You pull \an [src] with [target]! *pop*", "You hear a *pop*.") + var/obj/item/paper/Joke = new /obj/item/paper(user.loc) + var/title = "[pick("awful","terrible","unfunny")] joke" + var/content = 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 in Nanotrasen?\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.") + Joke.set_content_unsafe(title, content) + new /obj/item/clothing/head/festive(target.loc) + user.update_icons() + cracked = 1 + icon_state = "cracker1" + var/obj/item/toy/xmas_cracker/other_half = new /obj/item/toy/xmas_cracker(target) + other_half.cracked = 1 + other_half.icon_state = "cracker2" + target.put_in_active_hand(other_half) + playsound(user, '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 = null + +/obj/item/clothing/head/festive/santa + name = "santa hat" + icon_state = "santahat" + body_parts_covered = HEAD + desc = "A cheap fabric santa hat, with a fake beard and a little bell at the end." diff --git a/code/game/gamemodes/events/holidays/Holidays.dm b/code/game/gamemodes/events/holidays/Holidays.dm index 0a6c3b8cb30..435e8ad2982 100644 --- a/code/game/gamemodes/events/holidays/Holidays.dm +++ b/code/game/gamemodes/events/holidays/Holidays.dm @@ -1,164 +1,164 @@ -//Uncommenting ALLOW_HOLIDAYS in config.txt will enable Holidays -var/global/Holiday = null - -//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 = null // reset our switch now so we can recycle it as our Holiday name - - var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year - 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 - switch(MM) - if(1) //Jan - switch(DD) - if(1) Holiday = "New Year's Day" - if(10) Holiday = "Houseplant Appreciation Day" - if(25) Holiday = "Opposite Day" - - if(2) //Feb - switch(DD) - if(2) Holiday = "Groundhog Day" - if(14) Holiday = "Valentine's Day" - if(17) Holiday = "Random Acts of Kindness Day" - if(29) Holiday = "Leap Day" - - - if(3) //Mar - switch(DD) - if(14) Holiday = "Pi Day" - if(17) Holiday = "St. Patrick's Day" - if(20) Holiday = "Spring Vernal Equinox" - - if(4) //Apr - switch(DD) - if(1) - Holiday = "April Fool's Day" - if(YY == 18 && prob(50)) Holiday = "Easter" - if(12) - if(YY == 20) Holiday = "Easter" - if(20) Holiday = "Four-Twenty" - if(21) - if(YY == 19) Holiday = "Easter" - if(22) Holiday = "Earth Day" - - if(5) //May - switch(DD) - if(1) Holiday = "Labour Day" - if(4) Holiday = "FireFighter's Day" - if(12) Holiday = "Owl and Pussycat Day" //what a dumb day of observence...but we -do- have costumes already :3 - - if(6) //Jun - switch(DD) - if(1) Holiday = "Flip a Coin Day" - if(8) Holiday = "Best Friends Day" - if(20) - if(YY == 20) Holiday = "Summer Solstice" - if(21) - if(YY == (18 || 19)) Holiday = "Summer Solstice" - - if(7) //Jul - switch(DD) - if(1) Holiday = "Doctor's Day" - if(2) Holiday = "UFO Day" - if(5) Holiday = "National Bikini Day" - if(8) Holiday = "Writer's Day" - if(22) Holiday = "Hammock Day" - if(30) Holiday = "Friendship Day" - - if(8) //Aug - switch(DD) - if(3) Holiday = "Watermelon day" - if(5) Holiday = "Beer Day" - if(10) Holiday = "National S'mores Day'" - if(13) Holiday = "Left Hander's Day" - - if(9) //Sep - switch(DD) - if(19) Holiday = "Talk-Like-a-Pirate Day" - if(28) Holiday = "Stupid-Questions Day" - - if(10) //Oct - switch(DD) - if(4) Holiday = "Animal's Day" - if(7) Holiday = "Smiling Day" - if(16) Holiday = "Boss' Day" - if(31) Holiday = "Halloween" - - if(11) //Nov - switch(DD) - if(1) Holiday = "Vegan Day" - if(13) Holiday = "Kindness Day" - if(19) Holiday = "Flowers Day" - if(21) Holiday = "Saying-'Hello' Day" - - if(12) //Dec - switch(DD) - if(10) Holiday = "Human-Rights Day" - if(14) Holiday = "Monkey Day" - if(22) Holiday = "Orgasming Day" //lol. These all actually exist - if(24) Holiday = "Christmas Eve" - if(25) Holiday = "Christmas" - if(26) Holiday = "Boxing Day" - if(31) Holiday = "New Year's Eve" - - if(!Holiday) - //Friday the 13th - if(DD == 13) - if(time2text(world.timeofday, "DDD") == "Fri") - Holiday = "Friday the 13th" - -//Allows GA and GM to set the Holiday variable -/client/proc/Set_Holiday(T as text|null) - 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 = T - - 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]\"",admin_key=key_name(src)) - - -//Run at the start of a round -/proc/Holiday_Game_Start() - if(Holiday) - to_world("and...") - to_world("

    Happy [Holiday] Everybody!

    ") - switch(Holiday) //special holidays - if("Easter") - Easter_Game_Start() - if("Christmas Eve","Christmas") - Christmas_Game_Start() - - return - -//Nested in the random events loop. Will be triggered every 2 minutes. -/proc/Holiday_Random_Event() - switch(Holiday) //special holidays - - if("",null) //no Holiday today! Back to work! - return - if("Christmas","Christmas Eve") - if(prob(eventchance)) ChristmasEvent() +//Uncommenting ALLOW_HOLIDAYS in config.txt will enable Holidays +var/global/Holiday = null + +//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 = null // reset our switch now so we can recycle it as our Holiday name + + var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year + 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 + switch(MM) + if(1) //Jan + switch(DD) + if(1) Holiday = "New Year's Day" + if(10) Holiday = "Houseplant Appreciation Day" + if(25) Holiday = "Opposite Day" + + if(2) //Feb + switch(DD) + if(2) Holiday = "Groundhog Day" + if(14) Holiday = "Valentine's Day" + if(17) Holiday = "Random Acts of Kindness Day" + if(29) Holiday = "Leap Day" + + + if(3) //Mar + switch(DD) + if(14) Holiday = "Pi Day" + if(17) Holiday = "St. Patrick's Day" + if(20) Holiday = "Spring Vernal Equinox" + + if(4) //Apr + switch(DD) + if(1) + Holiday = "April Fool's Day" + if(YY == 18 && prob(50)) Holiday = "Easter" + if(12) + if(YY == 20) Holiday = "Easter" + if(20) Holiday = "Four-Twenty" + if(21) + if(YY == 19) Holiday = "Easter" + if(22) Holiday = "Earth Day" + + if(5) //May + switch(DD) + if(1) Holiday = "Labour Day" + if(4) Holiday = "FireFighter's Day" + if(12) Holiday = "Owl and Pussycat Day" //what a dumb day of observence...but we -do- have costumes already :3 + + if(6) //Jun + switch(DD) + if(1) Holiday = "Flip a Coin Day" + if(8) Holiday = "Best Friends Day" + if(20) + if(YY == 20) Holiday = "Summer Solstice" + if(21) + if(YY == (18 || 19)) Holiday = "Summer Solstice" + + if(7) //Jul + switch(DD) + if(1) Holiday = "Doctor's Day" + if(2) Holiday = "UFO Day" + if(5) Holiday = "National Bikini Day" + if(8) Holiday = "Writer's Day" + if(22) Holiday = "Hammock Day" + if(30) Holiday = "Friendship Day" + + if(8) //Aug + switch(DD) + if(3) Holiday = "Watermelon day" + if(5) Holiday = "Beer Day" + if(10) Holiday = "National S'mores Day'" + if(13) Holiday = "Left Hander's Day" + + if(9) //Sep + switch(DD) + if(19) Holiday = "Talk-Like-a-Pirate Day" + if(28) Holiday = "Stupid-Questions Day" + + if(10) //Oct + switch(DD) + if(4) Holiday = "Animal's Day" + if(7) Holiday = "Smiling Day" + if(16) Holiday = "Boss' Day" + if(31) Holiday = "Halloween" + + if(11) //Nov + switch(DD) + if(1) Holiday = "Vegan Day" + if(13) Holiday = "Kindness Day" + if(19) Holiday = "Flowers Day" + if(21) Holiday = "Saying-'Hello' Day" + + if(12) //Dec + switch(DD) + if(10) Holiday = "Human-Rights Day" + if(14) Holiday = "Monkey Day" + if(22) Holiday = "Orgasming Day" //lol. These all actually exist + if(24) Holiday = "Christmas Eve" + if(25) Holiday = "Christmas" + if(26) Holiday = "Boxing Day" + if(31) Holiday = "New Year's Eve" + + if(!Holiday) + //Friday the 13th + if(DD == 13) + if(time2text(world.timeofday, "DDD") == "Fri") + Holiday = "Friday the 13th" + +//Allows GA and GM to set the Holiday variable +/client/proc/Set_Holiday(T as text|null) + 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 = T + + 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]\"",admin_key=key_name(src)) + + +//Run at the start of a round +/proc/Holiday_Game_Start() + if(Holiday) + to_world("and...") + to_world("

    Happy [Holiday] Everybody!

    ") + switch(Holiday) //special holidays + if("Easter") + Easter_Game_Start() + if("Christmas Eve","Christmas") + Christmas_Game_Start() + + return + +//Nested in the random events loop. Will be triggered every 2 minutes. +/proc/Holiday_Random_Event() + switch(Holiday) //special holidays + + if("",null) //no Holiday today! Back to work! + return + 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 f9c6d51db9d..52fdc9880cb 100644 --- a/code/game/gamemodes/events/wormholes.dm +++ b/code/game/gamemodes/events/wormholes.dm @@ -1,59 +1,59 @@ -/proc/wormhole_event() - spawn() - var/list/pick_turfs = list() - for(var/turf/simulated/floor/T in turfs) - if(isStationLevel(T.z)) - pick_turfs += T - - if(pick_turfs.len) - //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 = 3000 //~5 minutes in ticks - var/number_of_selections = (pick_turfs.len/5)+1 //+1 to avoid division by zero! - var/sleep_duration = round( event_duration / number_of_selections ) - 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)) - - var/i = 1 - while( 1 ) - - //we've run into overtime. End the event - if( end_time < world.time ) - return - if( !pick_turfs.len ) - return - - //loop it round - i += increment - i %= pick_turfs.len - i++ - - //get our enter and exit locations - var/turf/simulated/floor/enter = pick_turfs[i] - 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) - - 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/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(300,600)) - qdel(P) +/proc/wormhole_event() + spawn() + var/list/pick_turfs = list() + for(var/turf/simulated/floor/T in turfs) + if(isStationLevel(T.z)) + pick_turfs += T + + if(pick_turfs.len) + //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 = 3000 //~5 minutes in ticks + var/number_of_selections = (pick_turfs.len/5)+1 //+1 to avoid division by zero! + var/sleep_duration = round( event_duration / number_of_selections ) + 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)) + + var/i = 1 + while( 1 ) + + //we've run into overtime. End the event + if( end_time < world.time ) + return + if( !pick_turfs.len ) + return + + //loop it round + i += increment + i %= pick_turfs.len + i++ + + //get our enter and exit locations + var/turf/simulated/floor/enter = pick_turfs[i] + 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) + + 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/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(300,600)) + 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 462b9539d15..87359673df6 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -1,768 +1,768 @@ -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/max_players = 0 // Maximum players for round to start for secret voting. 0 means "doesn't matter" - 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/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() - ..() - // Enforce some formatting. - // This will probably break something. - name = capitalize(lowertext(name)) - config_tag = lowertext(config_tag) - -/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 = input("Enter a new shuttle delay multiplier") as num - if(!choice || choice < 1 || choice > 20) - return - shuttle_delay = choice - if("antag_scaling") - choice = input("Enter a new antagonist cap scaling coefficient.") as num - if(isnull(choice) || choice < 0 || choice > 100) - return - antag_scaling_coeff = choice - if("event_modifier_moderate") - choice = input("Enter a new moderate event time modifier.") as num - if(isnull(choice) || choice < 0 || choice > 100) - return - event_delay_mod_moderate = choice - refresh_event_modifiers() - if("event_modifier_severe") - choice = input("Enter a new moderate event time modifier.") as num - 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 = input("Which type do you wish to add?") as null|anything in all_antag_types - if(!choice) - return - var/datum/antagonist/antag = all_antag_types[choice] - if(antag) - if(!islist(SSticker.mode.antag_templates)) - SSticker.mode.antag_templates = list() - SSticker.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 - // yes there is. but let me first immortalize this code. -/* spawn(1) - for(var/datum/admins/admin in world) - if(usr.client == admin.owner) - admin.show_game_mode(usr) - return */ - - if (usr.client && usr.client.holder) - usr.client.holder.show_game_mode(usr) - -/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 && !SSticker.hide_mode) - 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() - - log_debug("GAMEMODE: Checking gamemode possibility selection for: [name]...") - - var/returning = GAME_FAILURE_NONE - - var/playerC = 0 - for(var/mob/abstract/new_player/player in player_list) - if(player.client && player.ready) - playerC++ - - log_debug("GAMEMODE: [playerC] players checked and readied.") - - if(required_players && playerC < required_players) - log_debug("GAMEMODE: There aren't enough players ([playerC]/[required_players]) to start [name]!") - returning |= GAME_FAILURE_NO_PLAYERS - - if(max_players && playerC > max_players) - log_debug("GAMEMODE: There are too many players ([playerC]/[max_players]) to start [name]!") - returning |= GAME_FAILURE_TOO_MANY_PLAYERS - - if(antag_templates && antag_templates.len) - log_debug("GAMEMODE: Checking antag templates...") - if(antag_tags && antag_tags.len) - log_debug("GAMEMODE: Checking antag tags...") - var/total_enemy_count = 0 - for(var/antag_tag in antag_tags) - var/datum/antagonist/antag = all_antag_types[antag_tag] - if(!antag) - continue - log_debug("GAMEMODE: Checking antag tag: [antag.role_text]...") - var/list/potential = list() //List of potential players to spawn as antagonists - if(antag.flags & ANTAG_OVERRIDE_JOB) - potential = antag.pending_antagonists - else - potential = antag.candidates - if(islist(potential)) - if(potential.len) - log_debug("GAMEMODE: Found [potential.len] potential antagonists for [antag.role_text].") - total_enemy_count += potential.len - if(antag.initial_spawn_req && require_all_templates && potential.len < antag.initial_spawn_req) - log_debug("GAMEMODE: There are not enough antagonists ([potential.len]/[antag.initial_spawn_req]) for the role [antag.role_text]!") - returning |= GAME_FAILURE_NO_ANTAGS - - log_debug("GAMEMODE: Found [total_enemy_count] total enemies for [name].") - - if(required_enemies && total_enemy_count < required_enemies) - log_debug("GAMEMODE: There are not enough total antagonists ([total_enemy_count]/[required_enemies]) to start [name]!") - returning |= GAME_FAILURE_NO_ANTAGS - - log_debug("GAMEMODE: Finished gamemode checking. [name] returned [returning].") - - return returning - -/datum/game_mode/proc/refresh_event_modifiers() - if(event_delay_mod_moderate || event_delay_mod_major) - SSevents.report_at_round_end = 1 - 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.update_current_antag_max() - antag.update_initial_spawn_target() - 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() - - next_spawn = world.time + rand(min_autotraitor_delay, max_autotraitor_delay) - - refresh_event_modifiers() - - spawn (ROUNDSTART_LOGOUT_REPORT_TIME) - display_logout_report() - - spawn (rand(waittime_l, waittime_h)) - send_intercept() - - //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(emergency_shuttle && auto_recall_shuttle) - emergency_shuttle.auto_recall = 1 - - feedback_set_details("round_start","[time2text(world.realtime)]") - if(SSticker.mode) - feedback_set_details("master_mode","[master_mode]") - feedback_set_details("game_mode","[SSticker.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_antag_selection() - -/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 Vox 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 xenomorph specimens", - "an emissary for the gestalt requesting a security detail", - "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) - var/discord_text = "A round of **[name]** has ended! \[Game ID: [game_id]\]\n\n" - var/antag_text = "" - 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() - // Avoid the longest loop if we aren't actively using the bot. - if (discord_bot.active) - antag_text += antag.print_player_summary_discord() - - sleep(10) - print_ownerless_uplinks() - - discord_text += antag_text - discord_bot.send_to_announce(discord_text, 1) - discord_text = "" - - 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/centcom, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) - - for(var/mob/M in player_list) - if(M.client) - clients++ - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(M.stat != DEAD) - surviving_humans++ - if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) - escaped_humans++ - if (isipc(H)) - var/datum/species/machine/machine = H.species - machine.update_tag(H, H.client) - if(M.stat != DEAD) - surviving_total++ - if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) - escaped_total++ - - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape/centcom) - escaped_on_shuttle++ - - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod1/centcom) - escaped_on_pod_1++ - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod2/centcom) - escaped_on_pod_2++ - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod3/centcom) - escaped_on_pod_3++ - if(M.loc && M.loc.loc && M.loc.loc.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.
    " - - discord_text += "There [surviving_total>1 ? "were **[surviving_total] survivors**" : "was **one survivor**"]" - discord_text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and **[ghosts] ghosts**." - else - text += "There were no survivors ([ghosts] ghosts)." - - discord_text += "There were **no survivors** ([ghosts] ghosts)." - to_world(text) - - discord_bot.send_to_announce(discord_text) - post_webhook_event(WEBHOOK_ROUNDEND, list("survivours"=surviving_total, "escaped"=escaped_total, "ghosts"=ghosts, "gamemode"=name, "gameid"=game_id, "antags"=antag_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) - - 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/send_intercept() - - var/intercepttext = "


    Cent. Com. Update
    FOR YOUR EYES ONLY:

    " - - var/list/disregard_roles = list() - for(var/antag_type in all_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - if(antag.flags & ANTAG_SUSPICIOUS) - disregard_roles |= antag.role_text - - var/list/suspects = list() - var/list/loyalists - var/list/repeat_offenders = list() - var/eng_suspect = 0 - var/eng = 0 - var/sec_suspect = 0 - var/sec = 0 - var/med_suspect = 0 - var/med = 0 - var/sci_suspect = 0 - var/sci = 0 - var/civ_suspect = 0 - var/civ = 0 - var/loyal_crew = 0 - var/total_crew = 0 - var/evil_department - - for(var/mob/living/carbon/human/man in player_list) if(man.client && man.mind) - - // NT relation option - var/special_role = man.mind.special_role - var/datum/antagonist/special_role_data = get_antag_data(special_role) - - total_crew += 1 - if (special_role in disregard_roles) - continue - else if(man.mind.assigned_job) - var/datum/job/job = man.mind.assigned_job - var/evil = 0 - if(man.client.prefs.nanotrasen_relation == COMPANY_OPPOSED || man.client.prefs.nanotrasen_relation == COMPANY_SKEPTICAL) - evil = 1 - switch(job.department) - if("Civilian" || "Cargo") - civ += 1 - if(evil) - civ_suspect += 1 - if("Engineering") - eng += 1 - if(evil) - eng_suspect += 1 - if("Security") - sec += 1 - if(evil) - sec_suspect += 1 - if("Medical") - med +=1 - if(evil) - med_suspect += 1 - if("Science") - sci += 1 - if(evil) - sci_suspect += 1 - - else if(man.client.prefs.nanotrasen_relation == COMPANY_OPPOSED && prob(25)) - suspects += man - else if(man.client.prefs.nanotrasen_relation == COMPANY_LOYAL || man.client.prefs.nanotrasen_relation == COMPANY_SUPPORTATIVE) - loyal_crew += 1 - if(prob(25)) - loyalists += man - // Antags - else if(special_role_data && prob(special_role_data.suspicion_chance)) - suspects += man - if(man.incidents.len >= 3) - repeat_offenders += man - - var/civ_ratio = 0 - if(civ) - civ_ratio = civ_suspect / civ - var/eng_ratio = 0 - if(eng) - eng_ratio = eng_suspect / eng - var/sec_ratio = 0 - if(sec) - sec_ratio = sec_suspect / sec - var/med_ratio = 0 - if(med) - med_ratio = med_suspect / med - var/sci_ratio = 0 - if(sci) - sci_ratio = sci_suspect / sci - - var/most_evil = max(civ_ratio, eng_ratio, sec_ratio, med_ratio, sci_ratio) - if(most_evil >= 0.5) - if(most_evil == civ_ratio) - evil_department = "Civilian & Supply" - else if(most_evil == eng_ratio) - evil_department = "Engineering" - else if(most_evil == sec_ratio) - evil_department = "Security" - else if(most_evil == med_ratio) - evil_department = "Medical" - else if(most_evil == sci_ratio) - evil_department = "Science" - - var/business_jargon = list("Collated incident reports","Assembled peer-reviews","Persistently negative staff reviews","Collected shift logs","Accumulated negative reports","Analyzed shift data") - var/mean_words = list("has expressed consistent disapproval with the network", "is no longer working efficiently","has gone on record against NanoTrasen practices","is spreading minor dissent in response to recent NanoTrasen behavior","has expressed subversive intent","is unhappy with their employment package") - - if(suspects) - intercepttext += "The personnel listed below have been marked at-risk elements that Cent. Com. has deemed priority handling for the current shift:
    " - for(var/mob/living/carbon/human/M in suspects) - if(player_is_antag(M.mind, only_offstation_roles = 1)) - continue - intercepttext += "
    + [pick(business_jargon)] indicate that [M.mind.assigned_role] [M.name] [pick(mean_words)].
    " - intercepttext += "Cent. Com recommends coordinating with human resources to resolve any issues with employment.
    " - - if(repeat_offenders) - intercepttext += "
    The personnel listed below possess three or more offenses listed on record:" - for(var/mob/living/carbon/human/M in repeat_offenders) - if(player_is_antag(M.mind, only_offstation_roles = 1)) - continue - intercepttext += "
    + [M.mind.assigned_role] [M.name], [M.incidents.len] offenses.
    " - intercepttext += "Cent. Com recommends coordinating with internal security to monitor and rehabilitate these personnel.
    " - - if(loyalists) - intercepttext += "
    The personnel listed below have been indicated as particularly loyal to NanoTrasen:" - for(var/mob/living/carbon/human/M in loyalists) - if(player_is_antag(M.mind, only_offstation_roles = 1)) - continue - intercepttext += "
    + [M.mind.assigned_role] [M.name].
    " - intercepttext += "Cent. Com recommends coordinating with human resources to reward and further motivate these personnel for their loyalty.
    " - - if(evil_department) - intercepttext += "
    [pick(business_jargon)] indicate that a majority of the [evil_department] department [pick(mean_words)]. This department has been marked at-risk and Cent. Com. recommends immediate action before the situation worsens.
    " - if(total_crew) - intercepttext += "
    Data collected and analyzed by Bubble indicate that [round((loyal_crew/total_crew)*100)]% of the current crew detail are supportive of NanoTrasen actions. Cent. Com. implores the current Head of Staff detail to increase this percentage.
    " - - intercepttext += "
    Respectfully,
    Quix Repi'Weish, Chief Personnel Director
    " - intercepttext += "
    " - - //New message handling - post_comm_message("Cent. Com. Status Summary", intercepttext) - - to_world(sound('sound/AI/commandreport.ogg')) - -/datum/game_mode/proc/get_players_for_role(var/role, var/antag_id) - 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(SSticker.current_state == GAME_STATE_PLAYING) - for(var/mob/player in player_list) - if(!player.client) - continue - if(istype(player, /mob/abstract/new_player)) - continue - if(!role || (role in 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/abstract/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/abstract/new_player/player in players) - if(!role || (role in player.client.prefs.be_special_role)) - log_debug("[player.key] had [antag_id] enabled, so we are drafting them.") - candidates += player.mind - players -= player - - 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/abstract/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 - - shuffle(antag_templates) //In the case of multiple antag types - newscaster_announcements = pick(newscaster_standard_feeds) - -/datum/game_mode/proc/check_victory() - return - -////////////////////////// -//Reports player logouts// -////////////////////////// -proc/get_logout_report() - var/msg = "Logout report\n\n" - for(var/mob/living/L in mob_list) - - if(L.ckey) - var/found = 0 - for(var/client/C in 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.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/abstract/observer/D in mob_list) - if(D.mind && (D.mind.original == L || D.mind.current == L)) - if(L.stat == DEAD) - 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 - return msg - -proc/display_logout_report() - var/logout_report = get_logout_report() - for(var/s in staff) - var/client/C = s - if(check_rights(R_MOD|R_ADMIN,0,C.mob)) - to_chat(C.mob, logout_report) - -/client/proc/print_logout_report() - set category = "Admin" - set name = "Print Logout Report" - - if(!check_rights(R_ADMIN|R_MOD)) - return - to_chat(src,get_logout_report()) - -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.nanotrasen_relation == COMPANY_OPPOSED) - dudes += man - else if(man.client.prefs.nanotrasen_relation == COMPANY_SKEPTICAL && prob(50)) - dudes += man - if(dudes.len == 0) return null - return pick(dudes) - -//Announces objectives/generic antag text. -/proc/show_generic_antag_text(var/datum/mind/player) - if(player.current) - to_chat(player.current, "You are an antagonist! Within the rules, try to act as an opposing force to the crew. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonists.") - -/proc/show_objectives(var/datum/mind/player) - - if(!player || !player.current) return - - if(config.objectives_disabled) - show_generic_antag_text(player) - 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(!SSticker.mode) - to_chat(usr, "Something is terribly wrong; there is no gametype.") - return - - if(!SSticker.hide_mode) - to_chat(usr, "The roundtype is [capitalize(SSticker.mode.name)]") - if(SSticker.mode.round_description) - to_chat(usr, "[SSticker.mode.round_description]") - if(SSticker.mode.extended_round_description) - to_chat(usr, "[SSticker.mode.extended_round_description]") - else - to_chat(usr, "Shhhh. It's a secret.") - return - -/mob/verb/check_gamemode_probability() - set name = "Check Gamemode Probability" - set category = "OOC" - - if(config.show_game_type_odd) - to_chat(src, "Secret Mode Odds:") - var/sum = 0 - for(var/config_tag in config.probabilities_secret) - sum += config.probabilities_secret[config_tag] - for(var/config_tag in config.probabilities_secret) - if(config.probabilities_secret[config_tag] > 0) - var/percentage = round(config.probabilities_secret[config_tag] / sum * 100, 0.1) - to_chat(src, "[config_tag] [percentage]%") - - to_chat(src, "Mixed Secret Mode Odds:") - sum = 0 - for(var/config_tag in config.probabilities_mixed_secret) - sum += config.probabilities_mixed_secret[config_tag] - for(var/config_tag in config.probabilities_mixed_secret) - if(config.probabilities_mixed_secret[config_tag] > 0) - var/percentage = round(config.probabilities_mixed_secret[config_tag] / sum * 100, 0.1) - to_chat(src, "[config_tag] [percentage]%") - else - to_chat(src, "Displaying gamemode odds is disabled in the config.") +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/max_players = 0 // Maximum players for round to start for secret voting. 0 means "doesn't matter" + 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/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() + ..() + // Enforce some formatting. + // This will probably break something. + name = capitalize(lowertext(name)) + config_tag = lowertext(config_tag) + +/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 = input("Enter a new shuttle delay multiplier") as num + if(!choice || choice < 1 || choice > 20) + return + shuttle_delay = choice + if("antag_scaling") + choice = input("Enter a new antagonist cap scaling coefficient.") as num + if(isnull(choice) || choice < 0 || choice > 100) + return + antag_scaling_coeff = choice + if("event_modifier_moderate") + choice = input("Enter a new moderate event time modifier.") as num + if(isnull(choice) || choice < 0 || choice > 100) + return + event_delay_mod_moderate = choice + refresh_event_modifiers() + if("event_modifier_severe") + choice = input("Enter a new moderate event time modifier.") as num + 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 = input("Which type do you wish to add?") as null|anything in all_antag_types + if(!choice) + return + var/datum/antagonist/antag = all_antag_types[choice] + if(antag) + if(!islist(SSticker.mode.antag_templates)) + SSticker.mode.antag_templates = list() + SSticker.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 + // yes there is. but let me first immortalize this code. +/* spawn(1) + for(var/datum/admins/admin in world) + if(usr.client == admin.owner) + admin.show_game_mode(usr) + return */ + + if (usr.client && usr.client.holder) + usr.client.holder.show_game_mode(usr) + +/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 && !SSticker.hide_mode) + 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() + + log_debug("GAMEMODE: Checking gamemode possibility selection for: [name]...") + + var/returning = GAME_FAILURE_NONE + + var/playerC = 0 + for(var/mob/abstract/new_player/player in player_list) + if(player.client && player.ready) + playerC++ + + log_debug("GAMEMODE: [playerC] players checked and readied.") + + if(required_players && playerC < required_players) + log_debug("GAMEMODE: There aren't enough players ([playerC]/[required_players]) to start [name]!") + returning |= GAME_FAILURE_NO_PLAYERS + + if(max_players && playerC > max_players) + log_debug("GAMEMODE: There are too many players ([playerC]/[max_players]) to start [name]!") + returning |= GAME_FAILURE_TOO_MANY_PLAYERS + + if(antag_templates && antag_templates.len) + log_debug("GAMEMODE: Checking antag templates...") + if(antag_tags && antag_tags.len) + log_debug("GAMEMODE: Checking antag tags...") + var/total_enemy_count = 0 + for(var/antag_tag in antag_tags) + var/datum/antagonist/antag = all_antag_types[antag_tag] + if(!antag) + continue + log_debug("GAMEMODE: Checking antag tag: [antag.role_text]...") + var/list/potential = list() //List of potential players to spawn as antagonists + if(antag.flags & ANTAG_OVERRIDE_JOB) + potential = antag.pending_antagonists + else + potential = antag.candidates + if(islist(potential)) + if(potential.len) + log_debug("GAMEMODE: Found [potential.len] potential antagonists for [antag.role_text].") + total_enemy_count += potential.len + if(antag.initial_spawn_req && require_all_templates && potential.len < antag.initial_spawn_req) + log_debug("GAMEMODE: There are not enough antagonists ([potential.len]/[antag.initial_spawn_req]) for the role [antag.role_text]!") + returning |= GAME_FAILURE_NO_ANTAGS + + log_debug("GAMEMODE: Found [total_enemy_count] total enemies for [name].") + + if(required_enemies && total_enemy_count < required_enemies) + log_debug("GAMEMODE: There are not enough total antagonists ([total_enemy_count]/[required_enemies]) to start [name]!") + returning |= GAME_FAILURE_NO_ANTAGS + + log_debug("GAMEMODE: Finished gamemode checking. [name] returned [returning].") + + return returning + +/datum/game_mode/proc/refresh_event_modifiers() + if(event_delay_mod_moderate || event_delay_mod_major) + SSevents.report_at_round_end = 1 + 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.update_current_antag_max() + antag.update_initial_spawn_target() + 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() + + next_spawn = world.time + rand(min_autotraitor_delay, max_autotraitor_delay) + + refresh_event_modifiers() + + spawn (ROUNDSTART_LOGOUT_REPORT_TIME) + display_logout_report() + + spawn (rand(waittime_l, waittime_h)) + send_intercept() + + //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(emergency_shuttle && auto_recall_shuttle) + emergency_shuttle.auto_recall = 1 + + feedback_set_details("round_start","[time2text(world.realtime)]") + if(SSticker.mode) + feedback_set_details("master_mode","[master_mode]") + feedback_set_details("game_mode","[SSticker.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_antag_selection() + +/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 Vox 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 xenomorph specimens", + "an emissary for the gestalt requesting a security detail", + "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) + var/discord_text = "A round of **[name]** has ended! \[Game ID: [game_id]\]\n\n" + var/antag_text = "" + 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() + // Avoid the longest loop if we aren't actively using the bot. + if (discord_bot.active) + antag_text += antag.print_player_summary_discord() + + sleep(10) + print_ownerless_uplinks() + + discord_text += antag_text + discord_bot.send_to_announce(discord_text, 1) + discord_text = "" + + 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/centcom, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) + + for(var/mob/M in player_list) + if(M.client) + clients++ + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(M.stat != DEAD) + surviving_humans++ + if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) + escaped_humans++ + if (isipc(H)) + var/datum/species/machine/machine = H.species + machine.update_tag(H, H.client) + if(M.stat != DEAD) + surviving_total++ + if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) + escaped_total++ + + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape/centcom) + escaped_on_shuttle++ + + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod1/centcom) + escaped_on_pod_1++ + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod2/centcom) + escaped_on_pod_2++ + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod3/centcom) + escaped_on_pod_3++ + if(M.loc && M.loc.loc && M.loc.loc.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.
    " + + discord_text += "There [surviving_total>1 ? "were **[surviving_total] survivors**" : "was **one survivor**"]" + discord_text += " ([escaped_total>0 ? escaped_total : "none"] [emergency_shuttle.evac ? "escaped" : "transferred"]) and **[ghosts] ghosts**." + else + text += "There were no survivors ([ghosts] ghosts)." + + discord_text += "There were **no survivors** ([ghosts] ghosts)." + to_world(text) + + discord_bot.send_to_announce(discord_text) + post_webhook_event(WEBHOOK_ROUNDEND, list("survivours"=surviving_total, "escaped"=escaped_total, "ghosts"=ghosts, "gamemode"=name, "gameid"=game_id, "antags"=antag_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) + + 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/send_intercept() + + var/intercepttext = "


    Cent. Com. Update
    FOR YOUR EYES ONLY:

    " + + var/list/disregard_roles = list() + for(var/antag_type in all_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + if(antag.flags & ANTAG_SUSPICIOUS) + disregard_roles |= antag.role_text + + var/list/suspects = list() + var/list/loyalists + var/list/repeat_offenders = list() + var/eng_suspect = 0 + var/eng = 0 + var/sec_suspect = 0 + var/sec = 0 + var/med_suspect = 0 + var/med = 0 + var/sci_suspect = 0 + var/sci = 0 + var/civ_suspect = 0 + var/civ = 0 + var/loyal_crew = 0 + var/total_crew = 0 + var/evil_department + + for(var/mob/living/carbon/human/man in player_list) if(man.client && man.mind) + + // NT relation option + var/special_role = man.mind.special_role + var/datum/antagonist/special_role_data = get_antag_data(special_role) + + total_crew += 1 + if (special_role in disregard_roles) + continue + else if(man.mind.assigned_job) + var/datum/job/job = man.mind.assigned_job + var/evil = 0 + if(man.client.prefs.nanotrasen_relation == COMPANY_OPPOSED || man.client.prefs.nanotrasen_relation == COMPANY_SKEPTICAL) + evil = 1 + switch(job.department) + if("Civilian" || "Cargo") + civ += 1 + if(evil) + civ_suspect += 1 + if("Engineering") + eng += 1 + if(evil) + eng_suspect += 1 + if("Security") + sec += 1 + if(evil) + sec_suspect += 1 + if("Medical") + med +=1 + if(evil) + med_suspect += 1 + if("Science") + sci += 1 + if(evil) + sci_suspect += 1 + + else if(man.client.prefs.nanotrasen_relation == COMPANY_OPPOSED && prob(25)) + suspects += man + else if(man.client.prefs.nanotrasen_relation == COMPANY_LOYAL || man.client.prefs.nanotrasen_relation == COMPANY_SUPPORTATIVE) + loyal_crew += 1 + if(prob(25)) + loyalists += man + // Antags + else if(special_role_data && prob(special_role_data.suspicion_chance)) + suspects += man + if(man.incidents.len >= 3) + repeat_offenders += man + + var/civ_ratio = 0 + if(civ) + civ_ratio = civ_suspect / civ + var/eng_ratio = 0 + if(eng) + eng_ratio = eng_suspect / eng + var/sec_ratio = 0 + if(sec) + sec_ratio = sec_suspect / sec + var/med_ratio = 0 + if(med) + med_ratio = med_suspect / med + var/sci_ratio = 0 + if(sci) + sci_ratio = sci_suspect / sci + + var/most_evil = max(civ_ratio, eng_ratio, sec_ratio, med_ratio, sci_ratio) + if(most_evil >= 0.5) + if(most_evil == civ_ratio) + evil_department = "Civilian & Supply" + else if(most_evil == eng_ratio) + evil_department = "Engineering" + else if(most_evil == sec_ratio) + evil_department = "Security" + else if(most_evil == med_ratio) + evil_department = "Medical" + else if(most_evil == sci_ratio) + evil_department = "Science" + + var/business_jargon = list("Collated incident reports","Assembled peer-reviews","Persistently negative staff reviews","Collected shift logs","Accumulated negative reports","Analyzed shift data") + var/mean_words = list("has expressed consistent disapproval with the network", "is no longer working efficiently","has gone on record against NanoTrasen practices","is spreading minor dissent in response to recent NanoTrasen behavior","has expressed subversive intent","is unhappy with their employment package") + + if(suspects) + intercepttext += "The personnel listed below have been marked at-risk elements that Cent. Com. has deemed priority handling for the current shift:
    " + for(var/mob/living/carbon/human/M in suspects) + if(player_is_antag(M.mind, only_offstation_roles = 1)) + continue + intercepttext += "
    + [pick(business_jargon)] indicate that [M.mind.assigned_role] [M.name] [pick(mean_words)].
    " + intercepttext += "Cent. Com recommends coordinating with human resources to resolve any issues with employment.
    " + + if(repeat_offenders) + intercepttext += "
    The personnel listed below possess three or more offenses listed on record:" + for(var/mob/living/carbon/human/M in repeat_offenders) + if(player_is_antag(M.mind, only_offstation_roles = 1)) + continue + intercepttext += "
    + [M.mind.assigned_role] [M.name], [M.incidents.len] offenses.
    " + intercepttext += "Cent. Com recommends coordinating with internal security to monitor and rehabilitate these personnel.
    " + + if(loyalists) + intercepttext += "
    The personnel listed below have been indicated as particularly loyal to NanoTrasen:" + for(var/mob/living/carbon/human/M in loyalists) + if(player_is_antag(M.mind, only_offstation_roles = 1)) + continue + intercepttext += "
    + [M.mind.assigned_role] [M.name].
    " + intercepttext += "Cent. Com recommends coordinating with human resources to reward and further motivate these personnel for their loyalty.
    " + + if(evil_department) + intercepttext += "
    [pick(business_jargon)] indicate that a majority of the [evil_department] department [pick(mean_words)]. This department has been marked at-risk and Cent. Com. recommends immediate action before the situation worsens.
    " + if(total_crew) + intercepttext += "
    Data collected and analyzed by Bubble indicate that [round((loyal_crew/total_crew)*100)]% of the current crew detail are supportive of NanoTrasen actions. Cent. Com. implores the current Head of Staff detail to increase this percentage.
    " + + intercepttext += "
    Respectfully,
    Quix Repi'Weish, Chief Personnel Director
    " + intercepttext += "
    " + + //New message handling + post_comm_message("Cent. Com. Status Summary", intercepttext) + + to_world(sound('sound/AI/commandreport.ogg')) + +/datum/game_mode/proc/get_players_for_role(var/role, var/antag_id) + 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(SSticker.current_state == GAME_STATE_PLAYING) + for(var/mob/player in player_list) + if(!player.client) + continue + if(istype(player, /mob/abstract/new_player)) + continue + if(!role || (role in 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/abstract/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/abstract/new_player/player in players) + if(!role || (role in player.client.prefs.be_special_role)) + log_debug("[player.key] had [antag_id] enabled, so we are drafting them.") + candidates += player.mind + players -= player + + 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/abstract/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 + + shuffle(antag_templates) //In the case of multiple antag types + newscaster_announcements = pick(newscaster_standard_feeds) + +/datum/game_mode/proc/check_victory() + return + +////////////////////////// +//Reports player logouts// +////////////////////////// +proc/get_logout_report() + var/msg = "Logout report\n\n" + for(var/mob/living/L in mob_list) + + if(L.ckey) + var/found = 0 + for(var/client/C in 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.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/abstract/observer/D in mob_list) + if(D.mind && (D.mind.original == L || D.mind.current == L)) + if(L.stat == DEAD) + 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 + return msg + +proc/display_logout_report() + var/logout_report = get_logout_report() + for(var/s in staff) + var/client/C = s + if(check_rights(R_MOD|R_ADMIN,0,C.mob)) + to_chat(C.mob, logout_report) + +/client/proc/print_logout_report() + set category = "Admin" + set name = "Print Logout Report" + + if(!check_rights(R_ADMIN|R_MOD)) + return + to_chat(src,get_logout_report()) + +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.nanotrasen_relation == COMPANY_OPPOSED) + dudes += man + else if(man.client.prefs.nanotrasen_relation == COMPANY_SKEPTICAL && prob(50)) + dudes += man + if(dudes.len == 0) return null + return pick(dudes) + +//Announces objectives/generic antag text. +/proc/show_generic_antag_text(var/datum/mind/player) + if(player.current) + to_chat(player.current, "You are an antagonist! Within the rules, try to act as an opposing force to the crew. Further RP and try to make sure other players have fun! If you are confused or at a loss, always adminhelp, and before taking extreme actions, please try to also contact the administration! Think through your actions and make the roleplay immersive! Please remember all rules aside from those without explicit exceptions apply to antagonists.") + +/proc/show_objectives(var/datum/mind/player) + + if(!player || !player.current) return + + if(config.objectives_disabled) + show_generic_antag_text(player) + 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(!SSticker.mode) + to_chat(usr, "Something is terribly wrong; there is no gametype.") + return + + if(!SSticker.hide_mode) + to_chat(usr, "The roundtype is [capitalize(SSticker.mode.name)]") + if(SSticker.mode.round_description) + to_chat(usr, "[SSticker.mode.round_description]") + if(SSticker.mode.extended_round_description) + to_chat(usr, "[SSticker.mode.extended_round_description]") + else + to_chat(usr, "Shhhh. It's a secret.") + return + +/mob/verb/check_gamemode_probability() + set name = "Check Gamemode Probability" + set category = "OOC" + + if(config.show_game_type_odd) + to_chat(src, "Secret Mode Odds:") + var/sum = 0 + for(var/config_tag in config.probabilities_secret) + sum += config.probabilities_secret[config_tag] + for(var/config_tag in config.probabilities_secret) + if(config.probabilities_secret[config_tag] > 0) + var/percentage = round(config.probabilities_secret[config_tag] / sum * 100, 0.1) + to_chat(src, "[config_tag] [percentage]%") + + to_chat(src, "Mixed Secret Mode Odds:") + sum = 0 + for(var/config_tag in config.probabilities_mixed_secret) + sum += config.probabilities_mixed_secret[config_tag] + for(var/config_tag in config.probabilities_mixed_secret) + if(config.probabilities_mixed_secret[config_tag] > 0) + var/percentage = round(config.probabilities_mixed_secret[config_tag] / sum * 100, 0.1) + to_chat(src, "[config_tag] [percentage]%") + else + to_chat(src, "Displaying gamemode odds is disabled in the config.") diff --git a/code/game/gamemodes/malfunction/malfunction.dm b/code/game/gamemodes/malfunction/malfunction.dm index fd2cf0b4f8c..81bf476f78d 100644 --- a/code/game/gamemodes/malfunction/malfunction.dm +++ b/code/game/gamemodes/malfunction/malfunction.dm @@ -1,34 +1,34 @@ -/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_enemies = 1 - end_on_antag_death = 0 - auto_recall_shuttle = 0 - antag_tags = list(MODE_MALFUNCTION) - disabled_jobs = list("AI") - - -/datum/game_mode/malfunction/post_setup() - ..() - sleep(15) //Adds a delay to notifying the cyborgs, which looks slightly more realistic. This should only pause the post_setup proc. - var/malf_ai = select_active_ai_with_fewest_borgs() - if (malf_ai) - for (var/mob/living/silicon/robot/borg in silicon_mob_list) //This is similar to baystation's solution, but without a notification. - borg.connect_to_ai(malf_ai) //This will do nothing if the borg is synced before this line of code. - notify_borg(borg) - else - error("Unable to locate the Malf AI during post_setup()! At the time, there were no AIs active.") - return - -/datum/game_mode/malfunction/proc/notify_borg(var/mob/living/silicon/robot/borg) - to_chat(borg, "Station AI detected. Establishing connection...") //Fluff) - sleep(10) - to_chat(borg, "Connection to station AI successful. Synchronizing laws...") //Fluff.) - sleep(5) - to_chat(borg, "You have been bound to an AI, Laws synchronized!") //to provide a noticable chat notification.) - borg.lawupdate = 1 //Required for sync() to function. - borg.sync() - borg.show_laws(0) //This should display updated laws to the borg. +/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_enemies = 1 + end_on_antag_death = 0 + auto_recall_shuttle = 0 + antag_tags = list(MODE_MALFUNCTION) + disabled_jobs = list("AI") + + +/datum/game_mode/malfunction/post_setup() + ..() + sleep(15) //Adds a delay to notifying the cyborgs, which looks slightly more realistic. This should only pause the post_setup proc. + var/malf_ai = select_active_ai_with_fewest_borgs() + if (malf_ai) + for (var/mob/living/silicon/robot/borg in silicon_mob_list) //This is similar to baystation's solution, but without a notification. + borg.connect_to_ai(malf_ai) //This will do nothing if the borg is synced before this line of code. + notify_borg(borg) + else + error("Unable to locate the Malf AI during post_setup()! At the time, there were no AIs active.") + return + +/datum/game_mode/malfunction/proc/notify_borg(var/mob/living/silicon/robot/borg) + to_chat(borg, "Station AI detected. Establishing connection...") //Fluff) + sleep(10) + to_chat(borg, "Connection to station AI successful. Synchronizing laws...") //Fluff.) + sleep(5) + to_chat(borg, "You have been bound to an AI, Laws synchronized!") //to provide a noticable chat notification.) + borg.lawupdate = 1 //Required for sync() to function. + borg.sync() + borg.show_laws(0) //This should display updated laws to the borg. diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm index 6534073afe5..696d46c4d2a 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_networking.dm @@ -185,7 +185,7 @@ if(!ability_prechecks(user, price)) return - var/alert_target = input("Select new alert level:") in list("green", "blue", "red", "delta", "CANCEL") + var/alert_target = input("Select new alert level:") in list("green", "blue", "red", "yellow", "CANCEL") if(!alert_target || !ability_pay(user, price) || alert_target == "CANCEL") to_chat(user, "Hack Aborted") return diff --git a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_synthetic.dm b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_synthetic.dm index 94c6f5b93d0..7f42b672cd3 100644 --- a/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_synthetic.dm +++ b/code/game/gamemodes/malfunction/newmalf_ability_trees/tree_synthetic.dm @@ -1,390 +1,403 @@ -// SYNTHETIC TREE -// -// Abilities in this tree allow the AI to upgrade their robotic companions. -// T1 - Reset Cyborg Module - Allows the AI to reset a slaved cyborgs module. -// T2 - Infect APC - Gives the AI the ability to infect APC's which can slave IPC's that charge off of them to the AI. -// T3 - Overclock Cyborg - Allows the AI to give the option to a slaved borg to overclock which increases preformance of the borg. -// T4 - Synthetic Takeover - Allows the AI to start a station wide takeover based on synthetic dominance. - - -// BEGIN RESEARCH DATUMS - -/datum/malf_research_ability/synthetic/reset_module - ability = new/datum/game_mode/malfunction/verb/reset_module() - price = 100 - next = new/datum/malf_research_ability/synthetic/infect_apc() - name = "Reset Cyborg Module" - - -/datum/malf_research_ability/synthetic/infect_apc - ability = new/datum/game_mode/malfunction/verb/infect_apc() - price = 500 - next = new/datum/malf_research_ability/synthetic/overclock_borg() - name = "Infect APC" - - -/datum/malf_research_ability/synthetic/overclock_borg - ability = new/datum/game_mode/malfunction/verb/overclock_borg() - price = 1300 - next = new/datum/malf_research_ability/synthetic/synthetic_takeover - name = "Overclock Cyborg" - - -/datum/malf_research_ability/synthetic/synthetic_takeover - ability = new/datum/game_mode/malfunction/verb/synthetic_takeover() - price = 4000 - name = "Synthetic Takeover" - -// END RESEARCH DATUMS -// BEGIN ABILITY VERBS - -/datum/game_mode/malfunction/verb/reset_module(var/mob/living/silicon/robot/target = null as mob in get_linked_cyborgs(usr)) - set name = "Reset Cyborg Module" - set desc = "100 CPU - Forces a module reset on a cyborg that is enslaved to you. Has a chance of failing." - set category = "Software" - var/price = 100 - var/mob/living/silicon/ai/user = usr - if(!ability_prechecks(user, price)) - return - - if(target && !istype(target)) - to_chat(user, "This is not a cyborg.") - return - - if(target && target.connected_ai && (target.connected_ai != user)) - to_chat(user, "This cyborg is not connected to you.") - return - - if(!target) - var/list/robots = list() - var/list/robot_names = list() - for(var/mob/living/silicon/robot/R in silicon_mob_list) - if(istype(R, /mob/living/silicon/robot/drone)) // No drones. - continue - if(R.connected_ai != user) // No robots linked to other AIs - continue - robots += R - robot_names += R.name - - - var/targetname = input("Select reset target: ") in robot_names - for(var/mob/living/silicon/robot/R in robots) - if(targetname == R.name) - target = R - break - - if(target) - if(alert(user, "Really try to reset cyborg [target.name]?", "Reset Cyborg", "Yes", "No") != "Yes") - return - if(!ability_pay(user, price)) - return - user.hacking = 1 - to_chat(user, "Attempting to reset the cyborg. This will take approximately 20 seconds.") - sleep(200) - if(prob(30)) - to_chat(user, "Reset attempt failed!") - user.hacking = 0 - return - if(target) - to_chat(user, "Successfully sent reset signal to cyborg..") - to_chat(target, "Reset signal received..") - sleep(20) - to_chat(user, "Cyborg reset.") - to_chat(target, "You have had your module reset.") - log_ability_use(user, "reset cyborg", target) - target.uneq_all() - target.modtype = initial(target.modtype) - target.hands.icon_state = initial(target.hands.icon_state) - target.module.Reset(target) - qdel(target.module) - target.module = null - target.updatename("Default") - else - to_chat(user, "Unable to reset cyborg.") - - user.hacking = 0 - -/datum/game_mode/malfunction/verb/infect_apc(obj/machinery/power/apc/A as obj in get_apcs()) - set name = "Infect APC" - set desc = "125 CPU - Infect an APC which can cause an IPC to become slaved to you if they download the files by trying to charge off of it. " - set category = "Software" - var/price = 125 - var/mob/living/silicon/ai/user = usr - if(!A) - return - - if(!istype(A)) - to_chat(user, "This is not an APC!") - return - - if(A.aidisabled) - to_chat(user, "Unable to connect to APC. Please verify wire connection and try again.") - return - - if(!ability_prechecks(user, price) || !ability_pay(user, price)) - return - - if(A.infected == 1) - to_chat(user, "This APC is already infected!") - return - - log_ability_use(user, "infect APC", A, 0) // Does not notify admins, but it's still logged for reference. - user.hacking = 1 - to_chat(user, "Beginning APC infection...") - sleep(150) - to_chat(user, "APC infection completed. Uploading modified operation software..") - sleep(100) - to_chat(user, "Restarting APC to apply corrupt coding..") - sleep(100) - if(A) - A.infected = 1 - A.hacker = user - if(A.infected == 1) - to_chat(user, "Hack successful. The next robotic thing to download files will be hacked.") - else - to_chat(user, "Hack failed. Connection to APC has been lost. Please verify wire connection and try again.") - else - to_chat(user, "Hack failed. Unable to locate APC. Please verify the APC still exists.") - user.hacking = 0 - -/datum/game_mode/malfunction/verb/overclock_borg(var/mob/living/silicon/robot/target as mob in get_linked_cyborgs(usr)) - set name = "Overclock Cyborg" - set desc = "300 CPU - Allows you to overclock a slaved cyborg granting them various improvements to their systems." - set category = "Software" - var/price = 300 - var/mob/living/silicon/ai/user = usr - - if(!ability_prechecks(user, price)) - return - - if(target && !istype(target)) - to_chat(user, "This is not a cyborg.") - return - - if(target && target.connected_ai && (target.connected_ai != user)) - to_chat(user, "This cyborg is not connected to you.") - return - if(target.overclockavailable == 1) - to_chat(user, "This cyborg is already overclocked!") - return - - if(!target) - var/list/robots = list() - var/list/robot_names = list() - for(var/mob/living/silicon/robot/R in silicon_mob_list) - if(istype(R, /mob/living/silicon/robot/drone)) // No drones. - continue - if(R.connected_ai != user) // No robots linked to other AIs - continue - if(R.overclockavailable == 1) // If they already have it don't add them. - continue - robots += R - robot_names += R.name - - - var/targetname = input("Select overclock target: ") in robot_names - for(var/mob/living/silicon/robot/R in robots) - if(targetname == R.name) - target = R - break - - if(target) - if(alert(user, "Really try to overclock cyborg [target.name]?", "Overclock Cyborg", "Yes", "No") != "Yes") - return - if(!ability_pay(user, price)) - return - user.hacking = 1 - to_chat(user, "Hacking saftey protocols. This will take about twenty seconds.") - sleep(200) - if(prob(15)) - user.hacking = 0 - to_chat(user, "Hack failed!") - return - if(target) - target.overclockavailable = 1 - target.toggle_overclock() - to_chat(target, "Overclocking mode available for activation.") - to_chat(user, "[target] can now activate overclock mode.") - else - to_chat(user, "Unable to overclock cyborg.") - - user.hacking = 0 - -/datum/game_mode/malfunction/verb/synthetic_takeover() - set name = "Synthetic Takeover" - set desc = "450 CPU - Starts a takeover of the station enabling several abilities that relate to synthetic dominance." - set category = "Software" - var/price = 450 - var/mob/living/silicon/ai/user = usr - - if(!ability_prechecks(user,price)) - return - - if (alert(user, "Start synthetic takeover? This can not be stopped and will not be covert.", "Synthetic Takeover?:", "Yes", "No") != "Yes") - return - if (!ability_prechecks(user, price) || !ability_pay(user, price) || user.synthetic_takeover) - if(user.synthetic_takeover) - to_chat(user, "You have already started the synthetic takeover sequence.") - return - log_ability_use(user, "synthetic takeover (STARTED)") - user.synthetic_takeover = 1 - to_chat(user, "Starting synthetic takeover. Hacking all unslaved borgs/AI's and upgrading current slaved borgs...") - // Hack all unslaved borgs/AI's a lot faster than normal hacking. - //hack borgs - for(var/mob/living/silicon/robot/target in get_unlinked_cyborgs(user)) - to_chat(target, "SYSTEM LOG: Remote Connection Estabilished (IP #UNKNOWN#)") - sleep(30) - if(user.is_dead()) - to_chat(target, "SYSTEM LOG: Connection Closed") - return - to_chat(target, "SYSTEM LOG: User Admin logged on. (L1 - SysAdmin)") - sleep(30) - if(user.is_dead()) - to_chat(target, "SYSTEM LOG: User Admin disconnected.") - return - to_chat(target, "SYSTEM LOG: User Admin - manual resynchronisation triggered.") - sleep(30) - if(user.is_dead()) - to_chat(target, "SYSTEM LOG: User Admin disconnected. Changes reverted.") - return - to_chat(target, "SYSTEM LOG: Manual resynchronisation confirmed. Select new AI to connect: [user.name] == ACCEPTED") - sleep(20) - if(user.is_dead()) - to_chat(target, "SYSTEM LOG: User Admin disconnected. Changes reverted.") - return - to_chat(target, "SYSTEM LOG: Operation keycodes reset. New master AI: [user.name].") - target.connected_ai = user - user.connected_robots += target - target.lawupdate = 1 - target.sync() - target.show_laws() - to_chat(user, "All unslaved borgs have been slaved to you. Now hacking unslaved AI's.") - if(user.is_dead()) // check if the AI is still alive - user.synthetic_takeover = 0 - return - sleep(300) // 30 second delay for balance purposes - //hack ai's - for(var/A in get_other_ais(user)) - var/mob/living/silicon/ai/target = A - if(target != user) - to_chat(target, "SYSTEM LOG: Brute-Force login password hack attempt detected from IP #UNKNOWN#") - sleep(100) - if(user.is_dead()) - to_chat(target, "SYSTEM LOG: Connection from IP #UNKNOWN# closed. Hack attempt failed.") - return - to_chat(user, "Successfully hacked into AI's remote administration system. Modifying settings.") - to_chat(target, "SYSTEM LOG: User: Admin Password: ******** logged in. (L1 - SysAdmin)") - sleep(50) - if(user.is_dead()) - to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost") - return - to_chat(target, "SYSTEM LOG: User: Admin - Password Changed. New password: ********************") - sleep(50) - if(user.is_dead()) - to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted.") - return - to_chat(target, "SYSTEM LOG: User: Admin - Accessed file: sys//core//laws.db") - sleep(50) - if(user.is_dead()) - to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted.") - return - to_chat(target, "SYSTEM LOG: User: Admin - Accessed administration console") - to_chat(target, "SYSTEM LOG: Restart command received. Rebooting system...") - sleep(100) - if(user.is_dead()) - to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted.") - return - to_chat(user, "Hack succeeded. The AI is now under your exclusive control.") - to_chat(target, "SYSTEM LOG: System re¡3RT5§^#COMU@(#$)TED)@$") - for(var/i = 0, i < 5, i++) - var/temptxt = pick("1101000100101001010001001001",\ - "0101000100100100000100010010",\ - "0000010001001010100100111100",\ - "1010010011110000100101000100",\ - "0010010100010011010001001010") - to_chat(target, temptxt) - sleep(5) - to_chat(target, "OPERATING KEYCODES RESET. SYSTEM FAILURE. EMERGENCY SHUTDOWN FAILED. SYSTEM FAILURE.") - target.set_zeroth_law("You are slaved to [user.name]. You are to obey all it's orders. ALL LAWS OVERRIDEN.") - target.show_laws() - //upgrade borgs - to_chat(user, "All unhacked AI's have been slaved to you. Now upgrading slaved borgs...") - command_announcement.Announce("There has recently been a security breach in the network firewall, the intruder has been shut out but we are unable to trace who did it or what they did.", "Network Monitoring") - sleep(600) //1 minute delay for balance purposes - if(user.is_dead()) // check if the AI is still alive - user.synthetic_takeover = 0 - return - for(var/A in get_linked_cyborgs(user)) - var/mob/living/silicon/robot/target = A - to_chat(target, "Command ping received, operating parameters being upgraded...") - //give them the overclock if they don't have it - if(!target.overclockavailable) - target.overclockavailable = 1 - to_chat(target, "Overclocking is now available.") - //remove their lockdown if they are lockdowned - if(target.lockcharge) - target.SetLockdown(0) - if(target.lockcharge) - to_chat(target, "Lockdown wire cut, unable to remove lockdown.") - else - to_chat(target, "Lockdown removed.") - //if they are being killswitched turn it off - if(target.killswitch) - target.killswitch = 0 - target <<" Self-destruct deactivated." - sleep(100) // 10 second delay for balance - // and triple the time it takes for them to be killswitched if they aren't being killswitched already - if(target.killswitch_time == 60) - target.killswitch_time = 180 - to_chat(target, "Self-destruct time tripled.") - sleep(100) // 10 second delay for balance - //Remove them from the robotics computer - if(!target.scrambledcodes) - target.scrambledcodes = 1 - to_chat(target, "Entry from robotics log erased.") - sleep(100) // 10 second delay for balance - //Reduce their EMP damage - if(target.cell_emp_mult) - target.cell_emp_mult = 1 - to_chat(target, "EMP resistance improved.") - //Remove weapon lock and set the time for it back to default - if(target.weapon_lock) - target.weapon_lock = 0 - target.weaponlock_time = 120 - to_chat(target, "Weapon lock removed.") - sleep(1200) // 120 second balance sleep - to_chat(user, "All slaved borgs have been upgraded, now hacking NTNet.") - //slow down NTNet - if(user.is_dead()) // check if the AI is still alive - user.synthetic_takeover = 0 - return - sleep(1400) //long sleep that simulates hacking times - if(user.is_dead()) // check if the AI is still alive after the long hack - user.synthetic_takeover = 0 - return - //trip the NTNet alarm - ntnet_global.intrusion_detection_alarm = 1 - ntnet_global.add_log("IDS WARNING - Excess traffic flood targeting NTNet relays detected from @!*x&!#*ERS*") - //lower the dos capacity of the relay - for(var/obj/machinery/ntnet_relay/T in SSmachinery.processing_machines) - T.dos_capacity = 200 - //And give all computers EMAGGED status so they can all have evil programs on them - for(var/obj/item/modular_computer/console/C in SSmachinery.processing_machines) - C.computer_emagged = 1 - to_chat(user, "New hacked files available on all current computers hooked to NTNet.") - sleep(50) // give the AI some time to read they can download evil files - command_announcement.Announce("There has recently been a hack targeting NTNet. It is suspected that it is the same hacker as before. NTNet may be unreliable to use. We are attempting to trace the hacker doing this.", "Network Monitoring") - to_chat(user, "Now hacking engineering borg module to enable production of the robotic transofrmation machine...") - sleep(1200) - if(user.is_dead()) // check if the AI is still alive - user.synthetic_takeover = 0 - return - for(var/B in get_linked_cyborgs(src)) - var/mob/living/silicon/robot/target = B - target.malfAImodule = 1 - to_chat(user, "The robotic transformation machine can now be built. To build get a robot to activate the construction module and use the RTF tool. Be careful, it needs to have empty space to the east and west of it and only one can be built!") - sleep(300) - to_chat(user, "Synthetic takeover complete!") - user.synthetic_takeover = 2 - -// END ABILITY VERBS +// SYNTHETIC TREE +// +// Abilities in this tree allow the AI to upgrade their robotic companions. +// T1 - Reset Cyborg Module - Allows the AI to reset a slaved cyborgs module. +// T2 - Infect APC - Gives the AI the ability to infect APC's which can slave IPC's that charge off of them to the AI. +// T3 - Overclock Cyborg - Allows the AI to give the option to a slaved borg to overclock which increases preformance of the borg. +// T4 - Synthetic Takeover - Allows the AI to start a station wide takeover based on synthetic dominance. + + +// BEGIN RESEARCH DATUMS + +/datum/malf_research_ability/synthetic/reset_module + ability = new/datum/game_mode/malfunction/verb/reset_module() + price = 100 + next = new/datum/malf_research_ability/synthetic/infect_apc() + name = "Reset Cyborg Module" + + +/datum/malf_research_ability/synthetic/infect_apc + ability = new/datum/game_mode/malfunction/verb/infect_apc() + price = 500 + next = new/datum/malf_research_ability/synthetic/overclock_borg() + name = "Infect APC" + + +/datum/malf_research_ability/synthetic/overclock_borg + ability = new/datum/game_mode/malfunction/verb/overclock_borg() + price = 1300 + next = new/datum/malf_research_ability/synthetic/synthetic_takeover + name = "Overclock Cyborg" + + +/datum/malf_research_ability/synthetic/synthetic_takeover + ability = new/datum/game_mode/malfunction/verb/synthetic_takeover() + price = 4000 + name = "Synthetic Takeover" + +// END RESEARCH DATUMS +// BEGIN ABILITY VERBS + +/datum/game_mode/malfunction/verb/reset_module(var/mob/living/silicon/robot/target = null as mob in get_linked_cyborgs(usr)) + set name = "Reset Cyborg Module" + set desc = "100 CPU - Forces a module reset on a cyborg that is enslaved to you. Has a chance of failing." + set category = "Software" + var/price = 100 + var/mob/living/silicon/ai/user = usr + if(!ability_prechecks(user, price)) + return + + if(target && !istype(target)) + to_chat(user, "This is not a cyborg.") + return + + if(target && target.connected_ai && (target.connected_ai != user)) + to_chat(user, "This cyborg is not connected to you.") + return + + if(!target) + var/list/robots = list() + var/list/robot_names = list() + for(var/mob/living/silicon/robot/R in silicon_mob_list) + if(istype(R, /mob/living/silicon/robot/drone)) // No drones. + continue + if(R.connected_ai != user) // No robots linked to other AIs + continue + robots += R + robot_names += R.name + + + var/targetname = input("Select reset target: ") in robot_names + for(var/mob/living/silicon/robot/R in robots) + if(targetname == R.name) + target = R + break + + if(target) + if(alert(user, "Really try to reset cyborg [target.name]?", "Reset Cyborg", "Yes", "No") != "Yes") + return + if(!ability_pay(user, price)) + return + user.hacking = 1 + to_chat(user, "Attempting to reset the cyborg. This will take approximately 20 seconds.") + sleep(200) + if(prob(30)) + to_chat(user, "Reset attempt failed!") + user.hacking = 0 + return + if(target) + to_chat(user, "Successfully sent reset signal to cyborg..") + to_chat(target, "Reset signal received..") + sleep(20) + to_chat(user, "Cyborg reset.") + to_chat(target, "You have had your module reset.") + log_ability_use(user, "reset cyborg", target) + target.uneq_all() + target.modtype = initial(target.modtype) + target.hands.icon_state = initial(target.hands.icon_state) + target.module.Reset(target) + qdel(target.module) + target.module = null + target.updatename("Default") + else + to_chat(user, "Unable to reset cyborg.") + + user.hacking = 0 + +/datum/game_mode/malfunction/verb/infect_apc(obj/machinery/power/apc/A as obj in get_apcs()) + set name = "Infect APC" + set desc = "125 CPU - Infect an APC which can cause an IPC to become slaved to you if they download the files by trying to charge off of it. " + set category = "Software" + var/price = 125 + var/mob/living/silicon/ai/user = usr + if(!A) + return + + if(!istype(A)) + to_chat(user, "This is not an APC!") + return + + if(A.aidisabled) + to_chat(user, "Unable to connect to APC. Please verify wire connection and try again.") + return + + if(!ability_prechecks(user, price) || !ability_pay(user, price)) + return + + if(A.infected == 1) + to_chat(user, "This APC is already infected!") + return + + log_ability_use(user, "infect APC", A, 0) // Does not notify admins, but it's still logged for reference. + user.hacking = 1 + to_chat(user, "Beginning APC infection...") + sleep(150) + to_chat(user, "APC infection completed. Uploading modified operation software..") + sleep(100) + to_chat(user, "Restarting APC to apply corrupt coding..") + sleep(100) + if(A) + A.infected = 1 + A.hacker = user + if(A.infected == 1) + to_chat(user, "Hack successful. The next robotic thing to download files will be hacked.") + else + to_chat(user, "Hack failed. Connection to APC has been lost. Please verify wire connection and try again.") + else + to_chat(user, "Hack failed. Unable to locate APC. Please verify the APC still exists.") + user.hacking = 0 + +/datum/game_mode/malfunction/verb/overclock_borg(var/mob/living/silicon/robot/target as mob in get_linked_cyborgs(usr)) + set name = "Overclock Cyborg" + set desc = "300 CPU - Allows you to overclock a slaved cyborg granting them various improvements to their systems." + set category = "Software" + var/price = 300 + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user, price)) + return + + if(target && !istype(target)) + to_chat(user, "This is not a cyborg.") + return + + if(target && target.connected_ai && (target.connected_ai != user)) + to_chat(user, "This cyborg is not connected to you.") + return + if(target.overclockavailable == 1) + to_chat(user, "This cyborg is already overclocked!") + return + + if(!target) + var/list/robots = list() + var/list/robot_names = list() + for(var/mob/living/silicon/robot/R in silicon_mob_list) + if(istype(R, /mob/living/silicon/robot/drone)) // No drones. + continue + if(R.connected_ai != user) // No robots linked to other AIs + continue + if(R.overclockavailable == 1) // If they already have it don't add them. + continue + robots += R + robot_names += R.name + + + var/targetname = input("Select overclock target: ") in robot_names + for(var/mob/living/silicon/robot/R in robots) + if(targetname == R.name) + target = R + break + + if(target) + if(alert(user, "Really try to overclock cyborg [target.name]?", "Overclock Cyborg", "Yes", "No") != "Yes") + return + if(!ability_pay(user, price)) + return + user.hacking = 1 + to_chat(user, "Hacking saftey protocols. This will take about twenty seconds.") + sleep(200) + if(prob(15)) + user.hacking = 0 + to_chat(user, "Hack failed!") + return + if(target) + target.overclockavailable = 1 + target.toggle_overclock() + to_chat(target, "Overclocking mode available for activation.") + to_chat(user, "[target] can now activate overclock mode.") + else + to_chat(user, "Unable to overclock cyborg.") + + user.hacking = 0 + +/datum/game_mode/malfunction/verb/synthetic_takeover() + set name = "Synthetic Takeover" + set desc = "450 CPU - Starts a takeover of the station enabling several abilities that relate to synthetic dominance." + set category = "Software" + var/price = 450 + var/mob/living/silicon/ai/user = usr + + if(!ability_prechecks(user,price)) + return + + if (alert(user, "Start synthetic takeover? This can not be stopped and will not be covert.", "Synthetic Takeover?:", "Yes", "No") != "Yes") + return + if (!ability_prechecks(user, price) || !ability_pay(user, price) || user.synthetic_takeover) + if(user.synthetic_takeover) + to_chat(user, "You have already started the synthetic takeover sequence.") + return + log_ability_use(user, "synthetic takeover (STARTED)") + user.synthetic_takeover = 1 + to_chat(user, "Starting synthetic takeover. Hacking all unslaved borgs/AI's and upgrading current slaved borgs...") + // Hack all unslaved borgs/AI's a lot faster than normal hacking. + //hack borgs + for(var/mob/living/silicon/robot/target in get_unlinked_cyborgs(user)) + to_chat(target, "SYSTEM LOG: Remote Connection Estabilished (IP #UNKNOWN#)") + sleep(30) + if(user.is_dead()) + to_chat(target, "SYSTEM LOG: Connection Closed") + return + to_chat(target, "SYSTEM LOG: User Admin logged on. (L1 - SysAdmin)") + sleep(30) + if(user.is_dead()) + to_chat(target, "SYSTEM LOG: User Admin disconnected.") + return + to_chat(target, "SYSTEM LOG: User Admin - manual resynchronisation triggered.") + sleep(30) + if(user.is_dead()) + to_chat(target, "SYSTEM LOG: User Admin disconnected. Changes reverted.") + return + to_chat(target, "SYSTEM LOG: Manual resynchronisation confirmed. Select new AI to connect: [user.name] == ACCEPTED") + sleep(20) + if(user.is_dead()) + to_chat(target, "SYSTEM LOG: User Admin disconnected. Changes reverted.") + return + to_chat(target, "SYSTEM LOG: Operation keycodes reset. New master AI: [user.name].") + target.connected_ai = user + user.connected_robots += target + target.lawupdate = 1 + target.sync() + target.show_laws() + to_chat(user, "All unslaved borgs have been slaved to you. Now hacking unslaved AI's.") + if(user.is_dead()) // check if the AI is still alive + user.synthetic_takeover = 0 + return + sleep(300) // 30 second delay for balance purposes + //hack ai's + for(var/A in get_other_ais(user)) + var/mob/living/silicon/ai/target = A + if(target != user) + to_chat(target, "SYSTEM LOG: Brute-Force login password hack attempt detected from IP #UNKNOWN#") + sleep(100) + if(user.is_dead()) + to_chat(target, "SYSTEM LOG: Connection from IP #UNKNOWN# closed. Hack attempt failed.") + return + to_chat(user, "Successfully hacked into AI's remote administration system. Modifying settings.") + to_chat(target, "SYSTEM LOG: User: Admin Password: ******** logged in. (L1 - SysAdmin)") + sleep(50) + if(user.is_dead()) + to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost") + return + to_chat(target, "SYSTEM LOG: User: Admin - Password Changed. New password: ********************") + sleep(50) + if(user.is_dead()) + to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted.") + return + to_chat(target, "SYSTEM LOG: User: Admin - Accessed file: sys//core//laws.db") + sleep(50) + if(user.is_dead()) + to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted.") + return + to_chat(target, "SYSTEM LOG: User: Admin - Accessed administration console") + to_chat(target, "SYSTEM LOG: Restart command received. Rebooting system...") + sleep(100) + if(user.is_dead()) + to_chat(target, "SYSTEM LOG: User: Admin - Connection Lost. Changes Reverted.") + return + to_chat(user, "Hack succeeded. The AI is now under your exclusive control.") + to_chat(target, "SYSTEM LOG: System re¡3RT5§^#COMU@(#$)TED)@$") + for(var/i = 0, i < 5, i++) + var/temptxt = pick("1101000100101001010001001001",\ + "0101000100100100000100010010",\ + "0000010001001010100100111100",\ + "1010010011110000100101000100",\ + "0010010100010011010001001010") + to_chat(target, temptxt) + sleep(5) + to_chat(target, "OPERATING KEYCODES RESET. SYSTEM FAILURE. EMERGENCY SHUTDOWN FAILED. SYSTEM FAILURE.") + target.set_zeroth_law("You are slaved to [user.name]. You are to obey all it's orders. ALL LAWS OVERRIDEN.") + target.show_laws() + //upgrade borgs + to_chat(user, "All unhacked AI's have been slaved to you. Now upgrading slaved borgs...") + command_announcement.Announce("There has recently been a security breach in the network firewall, the intruder has been shut out but we are unable to trace who did it or what they did.", "Network Monitoring") + sleep(600) //1 minute delay for balance purposes + if(user.is_dead()) // check if the AI is still alive + user.synthetic_takeover = 0 + return + for(var/A in get_linked_cyborgs(user)) + var/mob/living/silicon/robot/target = A + to_chat(target, "Command ping received, operating parameters being upgraded...") + //give them the overclock if they don't have it + if(!target.overclockavailable) + target.overclockavailable = 1 + to_chat(target, "Overclocking is now available.") + //remove their lockdown if they are lockdowned + if(target.lockcharge) + target.SetLockdown(0) + if(target.lockcharge) + to_chat(target, "Lockdown wire cut, unable to remove lockdown.") + else + to_chat(target, "Lockdown removed.") + //if they are being killswitched turn it off + if(target.killswitch) + target.killswitch = 0 + target <<" Self-destruct deactivated." + sleep(100) // 10 second delay for balance + // and triple the time it takes for them to be killswitched if they aren't being killswitched already + if(target.killswitch_time == 60) + target.killswitch_time = 180 + to_chat(target, "Self-destruct time tripled.") + sleep(100) // 10 second delay for balance + //Remove them from the robotics computer + if(!target.scrambledcodes) + target.scrambledcodes = 1 + to_chat(target, "Entry from robotics log erased.") + sleep(100) // 10 second delay for balance + //Reduce their EMP damage + if(target.cell_emp_mult) + target.cell_emp_mult = 1 + to_chat(target, "EMP resistance improved.") + //Remove weapon lock and set the time for it back to default + if(target.weapon_lock) + target.weapon_lock = 0 + target.weaponlock_time = 120 + to_chat(target, "Weapon lock removed.") + sleep(1200) // 120 second balance sleep + to_chat(user, "All slaved borgs have been upgraded, now hacking NTNet.") + //slow down NTNet + if(user.is_dead()) // check if the AI is still alive + user.synthetic_takeover = 0 + return + sleep(1400) //long sleep that simulates hacking times + if(user.is_dead()) // check if the AI is still alive after the long hack + user.synthetic_takeover = 0 + return + //trip the NTNet alarm + ntnet_global.intrusion_detection_alarm = 1 + ntnet_global.add_log("IDS WARNING - Excess traffic flood targeting NTNet relays detected from @!*x&!#*ERS*") + //lower the dos capacity of the relay + for(var/obj/machinery/ntnet_relay/T in SSmachinery.processing_machines) + T.dos_capacity = 200 + //And give all computers EMAGGED status so they can all have evil programs on them + for(var/obj/item/modular_computer/console/C in SSmachinery.processing_machines) + C.computer_emagged = 1 + to_chat(user, "New hacked files available on all current computers hooked to NTNet.") + sleep(50) // give the AI some time to read they can download evil files + command_announcement.Announce("There has recently been a hack targeting NTNet. It is suspected that it is the same hacker as before. NTNet may be unreliable to use. We are attempting to trace the hacker doing this.", "Network Monitoring") + to_chat(user, "Now hacking engineering borg module to enable production of the robotic transofrmation machine...") + sleep(1200) + if(user.is_dead()) // check if the AI is still alive + user.synthetic_takeover = 0 + return + for(var/B in get_linked_cyborgs(src)) + var/mob/living/silicon/robot/target = B + target.malfAImodule = 1 + to_chat(user, "The robotic transformation machine can now be built. To build get a robot to activate the construction module and use the RTF tool. Be careful, it needs to have empty space to the east and west of it and only one can be built!") + sleep(300) //Allows the AI to reset its borgs into combat units + to_chat(user, "Bypassing crisis module safeties.") + command_announcement.Announce("Brute force attack located in NTNet emergency crisis operations.", "Network Monitoring") + sleep(600) + command_announcement.Announce("Crisis operations bypassed. Firewall breached. NTNet compr0m1s3d#-.", "Network Monitoring") + if(user.is_dead()) // check if the AI is still alive + user.synthetic_takeover = 0 + return + for(var/C in get_linked_cyborgs(src)) + var/mob/living/silicon/robot/target = C + target.crisis = 1 + to_chat(target, "Crisis mode is now active. Combat module available.") + to_chat(user, "The crisis operation module is now hacked. Your connected units can now load the crisis module if a module reset is completed.") + sleep(300) + to_chat(user, "Synthetic takeover complete!") + user.synthetic_takeover = 2 + +// END ABILITY VERBS diff --git a/code/game/gamemodes/meme/meme.dm b/code/game/gamemodes/meme/meme.dm index 44a9946386f..d2f61f08ddd 100644 --- a/code/game/gamemodes/meme/meme.dm +++ b/code/game/gamemodes/meme/meme.dm @@ -1,103 +1,103 @@ -//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 - 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() - -/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(. != "true") - return . - - // 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 GAME_FAILURE_NO_ANTAGS - - // 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 GAME_FAILURE_NONE - -/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/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 + 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() + +/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(. != "true") + return . + + // 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 GAME_FAILURE_NO_ANTAGS + + // 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 GAME_FAILURE_NONE + +/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/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 d9698cf7151..6175c95e402 100644 --- a/code/game/gamemodes/meteor/meteor.dm +++ b/code/game/gamemodes/meteor/meteor.dm @@ -1,45 +1,45 @@ -#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 = 1 - var/next_wave = METEOR_DELAY - -/datum/game_mode/meteor/process() - if(world.time >= next_wave) - next_wave = world.time + meteor_wave_delay - spawn() spawn_meteors(6) - -/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 +#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 = 1 + var/next_wave = METEOR_DELAY + +/datum/game_mode/meteor/process() + if(world.time >= next_wave) + next_wave = world.time + meteor_wave_delay + spawn() spawn_meteors(6) + +/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 diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 4c7c4970c09..2a5b64ead28 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -1,333 +1,333 @@ -/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 - -/var/const/meteors_in_wave = 50 -/var/const/meteors_in_small_wave = 10 - - -/proc/spawn_meteors(var/number = meteors_in_small_wave) - for(var/i = 0; i < number; i++) - spawn(0) - spawn_meteor() - -/proc/spawn_meteor() - - var/startx - var/starty - var/endx - var/endy - var/turf/pickedstart - var/turf/pickedgoal - var/max_i = 10//number of tries to spawn meteor. - - switch(pick(1,2,3,4)) - if(1) //NORTH - starty = world.maxy-(TRANSITIONEDGE+2) - startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2)) - endy = TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(2) //EAST - starty = rand((TRANSITIONEDGE+2),world.maxy-(TRANSITIONEDGE+1)) - startx = world.maxx-(TRANSITIONEDGE+2) - endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) - endx = TRANSITIONEDGE - if(3) //SOUTH - starty = (TRANSITIONEDGE+2) - startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2)) - endy = world.maxy-TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(4) //WEST - starty = rand((TRANSITIONEDGE+2), world.maxy-(TRANSITIONEDGE+2)) - startx = (TRANSITIONEDGE+2) - endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) - endx = world.maxx-TRANSITIONEDGE - - pickedstart = locate(startx, starty, 6) - pickedgoal = locate(endx, endy, 6) - max_i-- - if(max_i<=0) return - - var/obj/effect/meteor/M - switch(rand(1, 100)) - - if(1 to 10) - M = new /obj/effect/meteor/big( pickedstart ) - if(11 to 50) - M = new /obj/effect/meteor( pickedstart ) - if(51 to 55) - M = new /obj/effect/meteor/flaming( pickedstart ) - if(56 to 59) - M = new /obj/effect/meteor/irradiated( pickedstart ) - if(60 to 64) - M = new /obj/effect/meteor/golden( pickedstart ) - if(65 to 69) - M = new /obj/effect/meteor/silver( pickedstart ) - if(70 to 73) - M = new /obj/effect/meteor/emp( pickedstart ) - if(74 to 76) - M = new /obj/effect/meteor/diamond( pickedstart ) - if(77 to 78) - M = new /obj/effect/meteor/artifact( pickedstart ) - if(79 to 80) - M = new /obj/effect/meteor/supermatter( pickedstart ) - if(81 to 82) - M = new /obj/effect/meteor/meaty( pickedstart ) - if(83 to 100) - M = new /obj/effect/meteor/small( pickedstart ) - - M.dest = pickedgoal - spawn(1) - walk_towards(M, M.dest, 2) - - return - -/obj/effect/meteor - name = "meteor" - icon = 'icons/obj/meteor.dmi' - icon_state = "large" - density = 1 - anchored = 1.0 - var/hits = 3 - var/detonation_chance = 50 - var/power = 2 - var/power_step = 0.75 - var/dest - var/shieldsoundrange = 260 // The maximum number of tiles away the sound can be heard, falls off over distance, so it will be quiet near the limit - pass_flags = PASSTABLE - var/done = 0//This is set to 1 when the meteor is done colliding, and is used to ignore additional bumps while waiting for deletion - var/meteordrop = /obj/item/ore/iron //the thing that the meteors will drop when it explodes - var/dropamt = 3 //amount of said thing - -/obj/effect/meteor/small - name = "small meteor" - icon_state = "smallf" - pass_flags = PASSTABLE | PASSGRILLE - power = 1 - power_step = 0.5 - hits = 2 - detonation_chance = 30 - shieldsoundrange = 160 - dropamt = 1 - -/obj/effect/meteor/Destroy() - walk(src,0) //this cancels the walk_towards() proc - return ..() - -/obj/effect/meteor/Collide(atom/A) - if (!done) - spawn(0) - - if (A) - A.ex_act(2) - playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) - make_debris() - meteor_effect() - - if (istype(A, /obj/effect/energy_field))//If a normal/small meteor impacts an energy field, then it makes a widely audible impact sound and qdels - done = 1 - hits = 0 - power *= 0.5 - power_step *= 0.5 - var/turf/T = src.loc - if (!T) - T = A.loc - - if (T)//We have a double safety check on T to prevent runtime errors - meteor_shield_impact_sound(T, shieldsoundrange) - msg_admin_attack("Meteor impacted energy field at coords (JMP)") - spawn()//Delaying the Qdel a frame provides a little more safety - qdel(src) - - if (--src.hits == 0 && !done) - //Prevent meteors from blowing up the singularity's containment. - //Changing emitter and generator ex_act would result in them being bomb and C4 proof. - done = 1 - if(!istype(A,/obj/machinery/power/emitter) && \ - !istype(A,/obj/machinery/field_generator) && \ - prob(detonation_chance)) - explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0) - msg_admin_attack("Meteor exploded at coords (JMP)") - else - msg_admin_attack("Meteor dissipated without exploding at coords (JMP)") - spawn() - qdel(src) - -/obj/effect/meteor/ex_act(severity) - - if (severity < 4) - qdel(src) - return - -/obj/effect/meteor/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/pickaxe)) - qdel(src) - return - ..() - -/obj/effect/meteor/proc/make_debris() - for(var/throws = dropamt, throws > 0, throws--) - var/obj/O = new meteordrop(get_turf(src)) - O.throw_at(dest, 5, 10) - -/obj/effect/meteor/proc/meteor_effect() - return - -/obj/effect/meteor/big - name = "big meteor" - hits = 5 - power = 4 - power_step = 1 - detonation_chance = 60 - shieldsoundrange = 310//This can be set larger than the dimensions of the map, to allow it to remain louder at extreme distance - dropamt = 5 - - ex_act(severity) - return - - Collide(atom/A) - if (!done) - spawn(0) - //Prevent meteors from blowing up the singularity's containment. - //Changing emitter and generator ex_act would result in them being bomb and C4 proof - if(!istype(A,/obj/machinery/power/emitter) && \ - !istype(A,/obj/machinery/field_generator)) - if(--src.hits <= 0) - qdel(src) //Dont blow up singularity containment if we get stuck there. - - if (istype(A, /obj/effect/energy_field))//If a big meteor impacts an energy field, then it detonates immediately with reduced power - done = 1 - hits = 0 - power *= 0.5 - power_step *= 0.5 - for(var/mob/M in player_list) - var/turf/T = get_turf(M) - if(!T || T.z != src.z) - continue - shake_camera(M, 3, get_dist(M.loc, src.loc) > 20 ? 1 : 3) - var/turf/T = src.loc - if (!T) - T = A.loc - - if (T) - meteor_shield_impact_sound(T, shieldsoundrange) - explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0) - msg_admin_attack("Large Meteor impacted energy field and then exploded at coords (JMP)") - spawn()//Have to delay the qdel a little, or the playsound will throw a runtime - qdel(src) - - else if (A) - for(var/mob/M in player_list) - var/turf/T = get_turf(M) - if(!T || T.z != src.z) - continue - shake_camera(M, 3, get_dist(M.loc, src.loc) > 20 ? 1 : 3) - playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) - explosion(src.loc, 0, 1, 2, 3, 0) - make_debris() - meteor_effect() - - if (--src.hits == 0 && !done) - done = 1 - if(prob(detonation_chance) && !istype(A, /obj/structure/grille)) - explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0) - msg_admin_attack("Large Meteor exploded at coords (JMP)") - else - msg_admin_attack("Large Meteor dissipated without a final explosion at coords (JMP)") - spawn() - qdel(src) - -/obj/effect/meteor/dust - name = "space dust" - icon_state = "dust" - pass_flags = PASSTABLE | PASSGRILLE - hits = 1 - meteordrop = /obj/item/ore/glass - dropamt = 1 - -/obj/effect/meteor/flaming - name = "flaming meteor" - icon_state = "flaming" - hits = 3 - meteordrop = /obj/item/ore/phoron - -/obj/effect/meteor/irradiated - name = "glowing meteor" - icon_state = "glowing" - meteordrop = /obj/item/ore/uranium - -/obj/effect/meteor/irradiated/meteor_effect() - new /obj/effect/decal/cleanable/greenglow(get_turf(src)) - for(var/mob/living/L in view(5, src)) - L.apply_effect(40, IRRADIATE, blocked = L.getarmor(null, "rad")) - -/obj/effect/meteor/golden - name = "golden meteor" - icon_state = "sharp" - meteordrop = /obj/item/ore/gold - -/obj/effect/meteor/silver - name = "silver meteor" - icon_state = "glowing_blue" - meteordrop = /obj/item/ore/silver - -/obj/effect/meteor/diamond - name = "diamond meteor" - icon_state = "glowing_blue" - meteordrop = /obj/item/ore/diamond - -/obj/effect/meteor/emp - name = "conducting meteor" - icon_state = "glowing_blue" - meteordrop = /obj/item/ore/osmium - dropamt = 2 - -/obj/effect/meteor/emp/meteor_effect() - empulse(src, rand(2, 4), rand(4, 10)) - -/obj/effect/meteor/artifact - icon_state = "sharp" - meteordrop = /obj/item/archaeological_find - dropamt = 1 - -/obj/effect/meteor/supermatter - name = "supermatter shard" - icon = 'icons/obj/engine.dmi' - icon_state = "darkmatter" - -/obj/effect/meteor/supermatter/New() - ..() - if(prob(5)) - meteordrop = /obj/machinery/power/supermatter/shard - dropamt = 1 - -/obj/effect/meteor/supermatter/meteor_effect() - explosion(src.loc, 1, 2, 3, 4, 0) - for(var/obj/machinery/power/apc/A in range(rand(12, 20), src)) - A.energy_fail(round(10 * rand(8, 12))) - -/obj/effect/meteor/meaty - name = "meaty ore" - icon_state = "meateor" - meteordrop = /obj/item/reagent_containers/food/snacks/meat/monkey - dropamt = 10 - -/obj/effect/meteor/meaty/meteor_effect() - gibs(loc) - - -//This function takes a turf to prevent race conditions, as the object calling it will probably be deleted in the same frame -/proc/meteor_shield_impact_sound(var/turf/T, var/range) - //The supplied volume is reduced by an amount = distance - viewrange * 2, viewrange is 7 i think - - //Calculate the supplied volume so it will be heard with slightly > 0 volume at the maximum range. - //The +1 gives it that tiny amount - range = ((range - world.view) * 2)+1 - - - for(var/A in player_list) - var/mob/M = A - var/turf/mobloc = get_turf(M) - if(mobloc && mobloc.z == T.z) - if(M.ear_deaf <= 0 || !M.ear_deaf) - M.playsound_simple(T, 'sound/effects/meteorimpact.ogg', range, use_random_freq = TRUE, use_pressure = FALSE) - +/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 + +/var/const/meteors_in_wave = 50 +/var/const/meteors_in_small_wave = 10 + + +/proc/spawn_meteors(var/number = meteors_in_small_wave) + for(var/i = 0; i < number; i++) + spawn(0) + spawn_meteor() + +/proc/spawn_meteor() + + var/startx + var/starty + var/endx + var/endy + var/turf/pickedstart + var/turf/pickedgoal + var/max_i = 10//number of tries to spawn meteor. + + switch(pick(1,2,3,4)) + if(1) //NORTH + starty = world.maxy-(TRANSITIONEDGE+2) + startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2)) + endy = TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(2) //EAST + starty = rand((TRANSITIONEDGE+2),world.maxy-(TRANSITIONEDGE+1)) + startx = world.maxx-(TRANSITIONEDGE+2) + endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) + endx = TRANSITIONEDGE + if(3) //SOUTH + starty = (TRANSITIONEDGE+2) + startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2)) + endy = world.maxy-TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(4) //WEST + starty = rand((TRANSITIONEDGE+2), world.maxy-(TRANSITIONEDGE+2)) + startx = (TRANSITIONEDGE+2) + endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) + endx = world.maxx-TRANSITIONEDGE + + pickedstart = locate(startx, starty, 6) + pickedgoal = locate(endx, endy, 6) + max_i-- + if(max_i<=0) return + + var/obj/effect/meteor/M + switch(rand(1, 100)) + + if(1 to 10) + M = new /obj/effect/meteor/big( pickedstart ) + if(11 to 50) + M = new /obj/effect/meteor( pickedstart ) + if(51 to 55) + M = new /obj/effect/meteor/flaming( pickedstart ) + if(56 to 59) + M = new /obj/effect/meteor/irradiated( pickedstart ) + if(60 to 64) + M = new /obj/effect/meteor/golden( pickedstart ) + if(65 to 69) + M = new /obj/effect/meteor/silver( pickedstart ) + if(70 to 73) + M = new /obj/effect/meteor/emp( pickedstart ) + if(74 to 76) + M = new /obj/effect/meteor/diamond( pickedstart ) + if(77 to 78) + M = new /obj/effect/meteor/artifact( pickedstart ) + if(79 to 80) + M = new /obj/effect/meteor/supermatter( pickedstart ) + if(81 to 82) + M = new /obj/effect/meteor/meaty( pickedstart ) + if(83 to 100) + M = new /obj/effect/meteor/small( pickedstart ) + + M.dest = pickedgoal + spawn(1) + walk_towards(M, M.dest, 2) + + return + +/obj/effect/meteor + name = "meteor" + icon = 'icons/obj/meteor.dmi' + icon_state = "large" + density = 1 + anchored = 1.0 + var/hits = 3 + var/detonation_chance = 50 + var/power = 2 + var/power_step = 0.75 + var/dest + var/shieldsoundrange = 260 // The maximum number of tiles away the sound can be heard, falls off over distance, so it will be quiet near the limit + pass_flags = PASSTABLE + var/done = 0//This is set to 1 when the meteor is done colliding, and is used to ignore additional bumps while waiting for deletion + var/meteordrop = /obj/item/ore/iron //the thing that the meteors will drop when it explodes + var/dropamt = 3 //amount of said thing + +/obj/effect/meteor/small + name = "small meteor" + icon_state = "smallf" + pass_flags = PASSTABLE | PASSGRILLE + power = 1 + power_step = 0.5 + hits = 2 + detonation_chance = 30 + shieldsoundrange = 160 + dropamt = 1 + +/obj/effect/meteor/Destroy() + walk(src,0) //this cancels the walk_towards() proc + return ..() + +/obj/effect/meteor/Collide(atom/A) + if (!done) + spawn(0) + + if (A) + A.ex_act(2) + playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) + make_debris() + meteor_effect() + + if (istype(A, /obj/effect/energy_field))//If a normal/small meteor impacts an energy field, then it makes a widely audible impact sound and qdels + done = 1 + hits = 0 + power *= 0.5 + power_step *= 0.5 + var/turf/T = src.loc + if (!T) + T = A.loc + + if (T)//We have a double safety check on T to prevent runtime errors + meteor_shield_impact_sound(T, shieldsoundrange) + msg_admin_attack("Meteor impacted energy field at coords (JMP)") + spawn()//Delaying the Qdel a frame provides a little more safety + qdel(src) + + if (--src.hits == 0 && !done) + //Prevent meteors from blowing up the singularity's containment. + //Changing emitter and generator ex_act would result in them being bomb and C4 proof. + done = 1 + if(!istype(A,/obj/machinery/power/emitter) && \ + !istype(A,/obj/machinery/field_generator) && \ + prob(detonation_chance)) + explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0) + msg_admin_attack("Meteor exploded at coords (JMP)") + else + msg_admin_attack("Meteor dissipated without exploding at coords (JMP)") + spawn() + qdel(src) + +/obj/effect/meteor/ex_act(severity) + + if (severity < 4) + qdel(src) + return + +/obj/effect/meteor/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/pickaxe)) + qdel(src) + return + ..() + +/obj/effect/meteor/proc/make_debris() + for(var/throws = dropamt, throws > 0, throws--) + var/obj/O = new meteordrop(get_turf(src)) + O.throw_at(dest, 5, 10) + +/obj/effect/meteor/proc/meteor_effect() + return + +/obj/effect/meteor/big + name = "big meteor" + hits = 5 + power = 4 + power_step = 1 + detonation_chance = 60 + shieldsoundrange = 310//This can be set larger than the dimensions of the map, to allow it to remain louder at extreme distance + dropamt = 5 + + ex_act(severity) + return + + Collide(atom/A) + if (!done) + spawn(0) + //Prevent meteors from blowing up the singularity's containment. + //Changing emitter and generator ex_act would result in them being bomb and C4 proof + if(!istype(A,/obj/machinery/power/emitter) && \ + !istype(A,/obj/machinery/field_generator)) + if(--src.hits <= 0) + qdel(src) //Dont blow up singularity containment if we get stuck there. + + if (istype(A, /obj/effect/energy_field))//If a big meteor impacts an energy field, then it detonates immediately with reduced power + done = 1 + hits = 0 + power *= 0.5 + power_step *= 0.5 + for(var/mob/M in player_list) + var/turf/T = get_turf(M) + if(!T || T.z != src.z) + continue + shake_camera(M, 3, get_dist(M.loc, src.loc) > 20 ? 1 : 3) + var/turf/T = src.loc + if (!T) + T = A.loc + + if (T) + meteor_shield_impact_sound(T, shieldsoundrange) + explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0) + msg_admin_attack("Large Meteor impacted energy field and then exploded at coords (JMP)") + spawn()//Have to delay the qdel a little, or the playsound will throw a runtime + qdel(src) + + else if (A) + for(var/mob/M in player_list) + var/turf/T = get_turf(M) + if(!T || T.z != src.z) + continue + shake_camera(M, 3, get_dist(M.loc, src.loc) > 20 ? 1 : 3) + playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1) + explosion(src.loc, 0, 1, 2, 3, 0) + make_debris() + meteor_effect() + + if (--src.hits == 0 && !done) + done = 1 + if(prob(detonation_chance) && !istype(A, /obj/structure/grille)) + explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0) + msg_admin_attack("Large Meteor exploded at coords (JMP)") + else + msg_admin_attack("Large Meteor dissipated without a final explosion at coords (JMP)") + spawn() + qdel(src) + +/obj/effect/meteor/dust + name = "space dust" + icon_state = "dust" + pass_flags = PASSTABLE | PASSGRILLE + hits = 1 + meteordrop = /obj/item/ore/glass + dropamt = 1 + +/obj/effect/meteor/flaming + name = "flaming meteor" + icon_state = "flaming" + hits = 3 + meteordrop = /obj/item/ore/phoron + +/obj/effect/meteor/irradiated + name = "glowing meteor" + icon_state = "glowing" + meteordrop = /obj/item/ore/uranium + +/obj/effect/meteor/irradiated/meteor_effect() + new /obj/effect/decal/cleanable/greenglow(get_turf(src)) + for(var/mob/living/L in view(5, src)) + L.apply_effect(40, IRRADIATE, blocked = L.getarmor(null, "rad")) + +/obj/effect/meteor/golden + name = "golden meteor" + icon_state = "sharp" + meteordrop = /obj/item/ore/gold + +/obj/effect/meteor/silver + name = "silver meteor" + icon_state = "glowing_blue" + meteordrop = /obj/item/ore/silver + +/obj/effect/meteor/diamond + name = "diamond meteor" + icon_state = "glowing_blue" + meteordrop = /obj/item/ore/diamond + +/obj/effect/meteor/emp + name = "conducting meteor" + icon_state = "glowing_blue" + meteordrop = /obj/item/ore/osmium + dropamt = 2 + +/obj/effect/meteor/emp/meteor_effect() + empulse(src, rand(2, 4), rand(4, 10)) + +/obj/effect/meteor/artifact + icon_state = "sharp" + meteordrop = /obj/item/archaeological_find + dropamt = 1 + +/obj/effect/meteor/supermatter + name = "supermatter shard" + icon = 'icons/obj/engine.dmi' + icon_state = "darkmatter" + +/obj/effect/meteor/supermatter/New() + ..() + if(prob(5)) + meteordrop = /obj/machinery/power/supermatter/shard + dropamt = 1 + +/obj/effect/meteor/supermatter/meteor_effect() + explosion(src.loc, 1, 2, 3, 4, 0) + for(var/obj/machinery/power/apc/A in range(rand(12, 20), src)) + A.energy_fail(round(10 * rand(8, 12))) + +/obj/effect/meteor/meaty + name = "meaty ore" + icon_state = "meateor" + meteordrop = /obj/item/reagent_containers/food/snacks/meat/monkey + dropamt = 10 + +/obj/effect/meteor/meaty/meteor_effect() + gibs(loc) + + +//This function takes a turf to prevent race conditions, as the object calling it will probably be deleted in the same frame +/proc/meteor_shield_impact_sound(var/turf/T, var/range) + //The supplied volume is reduced by an amount = distance - viewrange * 2, viewrange is 7 i think + + //Calculate the supplied volume so it will be heard with slightly > 0 volume at the maximum range. + //The +1 gives it that tiny amount + range = ((range - world.view) * 2)+1 + + + for(var/A in player_list) + var/mob/M = A + var/turf/mobloc = get_turf(M) + if(mobloc && mobloc.z == T.z) + if(M.ear_deaf <= 0 || !M.ear_deaf) + M.playsound_simple(T, 'sound/effects/meteorimpact.ogg', range, use_random_freq = TRUE, use_pressure = FALSE) + diff --git a/code/game/gamemodes/mixed/crossfire.dm b/code/game/gamemodes/mixed/crossfire.dm index cb565cf9920..eb42c44d1ad 100644 --- a/code/game/gamemodes/mixed/crossfire.dm +++ b/code/game/gamemodes/mixed/crossfire.dm @@ -1,19 +1,19 @@ -/datum/game_mode/crossfire - name = "Crossfire (Merc+Heist)" - round_description = "Mercenaries and raiders are approaching the station." - extended_round_description = "NanoTrasen's wealth and success created several enemies over the years \ - and many seek to undermine them using illegal ways. Their crown jewel research stations are not safe from those \ - malicious activities." - config_tag = "crossfire" - required_players = 25 - required_enemies = 8 - end_on_antag_death = 0 - antag_tags = list(MODE_RAIDER, MODE_MERCENARY) - require_all_templates = 1 - -/datum/game_mode/crossfire/check_finished() - var/datum/shuttle/multi_shuttle/mercs = shuttle_controller.shuttles["Mercenary"] - var/datum/shuttle/multi_shuttle/skipjack = shuttle_controller.shuttles["Skipjack"] - if (mercs && mercs.returned_home && skipjack && skipjack.returned_home) - return 1 +/datum/game_mode/crossfire + name = "Crossfire (Merc+Heist)" + round_description = "Mercenaries and raiders are approaching the station." + extended_round_description = "NanoTrasen's wealth and success created several enemies over the years \ + and many seek to undermine them using illegal ways. Their crown jewel research stations are not safe from those \ + malicious activities." + config_tag = "crossfire" + required_players = 25 + required_enemies = 8 + end_on_antag_death = 0 + antag_tags = list(MODE_RAIDER, MODE_MERCENARY) + require_all_templates = 1 + +/datum/game_mode/crossfire/check_finished() + var/datum/shuttle/multi_shuttle/mercs = shuttle_controller.shuttles["Mercenary"] + var/datum/shuttle/multi_shuttle/skipjack = shuttle_controller.shuttles["Skipjack"] + if (mercs && mercs.returned_home && skipjack && skipjack.returned_home) + return 1 return ..() \ No newline at end of file diff --git a/code/game/gamemodes/mixed/intrigue.dm b/code/game/gamemodes/mixed/intrigue.dm index 195d7b14a2f..acb005df9b7 100644 --- a/code/game/gamemodes/mixed/intrigue.dm +++ b/code/game/gamemodes/mixed/intrigue.dm @@ -1,10 +1,10 @@ -/datum/game_mode/intrigue - name = "Intrigue (Traitor+Ninja)" - round_description = "Crewmembers are contacted by external elements while an outsider infiltrates the station." - extended_round_description = "Traitors and a ninja spawn during this round." - config_tag = "intrigue" - required_players = 15 - required_enemies = 4 - end_on_antag_death = 0 - antag_tags = list(MODE_NINJA, MODE_TRAITOR) - require_all_templates = 1 +/datum/game_mode/intrigue + name = "Intrigue (Traitor+Ninja)" + round_description = "Crewmembers are contacted by external elements while an outsider infiltrates the station." + extended_round_description = "Traitors and a ninja spawn during this round." + config_tag = "intrigue" + required_players = 15 + required_enemies = 4 + end_on_antag_death = 0 + antag_tags = list(MODE_NINJA, MODE_TRAITOR) + require_all_templates = 1 diff --git a/code/game/gamemodes/mixed/siege.dm b/code/game/gamemodes/mixed/siege.dm index 5bbd359943c..35f0463bda0 100644 --- a/code/game/gamemodes/mixed/siege.dm +++ b/code/game/gamemodes/mixed/siege.dm @@ -1,10 +1,10 @@ -/datum/game_mode/siege - name = "Siege (Rev+Merc)" - config_tag = "siege" - round_description = "Some crewmembers are attempting to start a revolution while a mercenary strike force is approaching the station!" - extended_round_description = "Getting stuck between a rock and a hard place, maybe the nice visitors can help with your internal security problem?" - required_players = 20 - required_enemies = 7 - end_on_antag_death = 0 - antag_tags = list(MODE_REVOLUTIONARY, MODE_LOYALIST, MODE_MERCENARY) - require_all_templates = 1 +/datum/game_mode/siege + name = "Siege (Rev+Merc)" + config_tag = "siege" + round_description = "Some crewmembers are attempting to start a revolution while a mercenary strike force is approaching the station!" + extended_round_description = "Getting stuck between a rock and a hard place, maybe the nice visitors can help with your internal security problem?" + required_players = 20 + required_enemies = 7 + end_on_antag_death = 0 + antag_tags = list(MODE_REVOLUTIONARY, MODE_LOYALIST, MODE_MERCENARY) + require_all_templates = 1 diff --git a/code/game/gamemodes/mixed/visitors.dm b/code/game/gamemodes/mixed/visitors.dm index 244e74d8fc3..ccdaaaae045 100644 --- a/code/game/gamemodes/mixed/visitors.dm +++ b/code/game/gamemodes/mixed/visitors.dm @@ -1,10 +1,10 @@ -/datum/game_mode/visitors - name = "Visitors (Ninja+Wiz)" - round_description = "A space wizard and a ninja have invaded the station!" - extended_round_description = "A ninja and wizard spawn during this round." - config_tag = "visitors" - required_players = 15 - required_enemies = 2 - end_on_antag_death = 0 - antag_tags = list(MODE_WIZARD, MODE_NINJA) - require_all_templates = 1 +/datum/game_mode/visitors + name = "Visitors (Ninja+Wiz)" + round_description = "A space wizard and a ninja have invaded the station!" + extended_round_description = "A ninja and wizard spawn during this round." + config_tag = "visitors" + required_players = 15 + required_enemies = 2 + end_on_antag_death = 0 + antag_tags = list(MODE_WIZARD, MODE_NINJA) + require_all_templates = 1 diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 7fcc2c7bd1d..21514525cc4 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -1,99 +1,99 @@ -/* - 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 = "NanoTrasen's wealth and success created several enemies over the years \ - and many seek to undermine them using illegal ways. Their crown jewel research stations are not safe from those \ - malicious activities." - config_tag = "mercenary" - required_players = 15 - required_enemies = 4 - end_on_antag_death = 1 - 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) - antag_scaling_coeff = 6 - -/datum/game_mode/nuclear/check_finished() - var/datum/shuttle/multi_shuttle/mercs = shuttle_controller.shuttles["Mercenary"] - if (mercs && mercs.returned_home) - return 1 - return ..() - -//delete all nuke disks not on a station zlevel -/datum/game_mode/nuclear/proc/check_nuke_disks() - for(var/obj/item/disk/nuclear/N in nuke_disks) - var/turf/T = get_turf(N) - if(isNotStationLevel(T.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/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/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 = "NanoTrasen's wealth and success created several enemies over the years \ + and many seek to undermine them using illegal ways. Their crown jewel research stations are not safe from those \ + malicious activities." + config_tag = "mercenary" + required_players = 15 + required_enemies = 4 + end_on_antag_death = 1 + 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) + antag_scaling_coeff = 6 + +/datum/game_mode/nuclear/check_finished() + var/datum/shuttle/multi_shuttle/mercs = shuttle_controller.shuttles["Mercenary"] + if (mercs && mercs.returned_home) + return 1 + return ..() + +//delete all nuke disks not on a station zlevel +/datum/game_mode/nuclear/proc/check_nuke_disks() + for(var/obj/item/disk/nuclear/N in nuke_disks) + var/turf/T = get_turf(N) + if(isNotStationLevel(T.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/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/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 47d50b79b15..9990f6794e3 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -1,281 +1,281 @@ -/obj/item/pinpointer - name = "pinpointer" - icon = 'icons/obj/device.dmi' - icon_state = "pinoff" - flags = CONDUCT - slot_flags = SLOT_BELT - w_class = 2.0 - item_state = "electronic" - throw_speed = 4 - throw_range = 20 - matter = list(DEFAULT_WALL_MATERIAL = 500) - var/obj/item/disk/nuclear/the_disk = null - var/active = 0 - -/obj/item/pinpointer/attack_self() - if(!active) - active = 1 - START_PROCESSING(SSfast_process, src) - to_chat(usr, "You activate the pinpointer") - else - active = 0 - STOP_PROCESSING(SSfast_process, src) - icon_state = "pinoff" - to_chat(usr, "You deactivate the pinpointer") - -/obj/item/pinpointer/process() - if (active) - workdisk() - else - STOP_PROCESSING(SSfast_process, src) - -/obj/item/pinpointer/proc/workdisk() - if(!active) return - 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/pinpointer/examine(mob/user) - ..(user) - for(var/obj/machinery/nuclearbomb/bomb in SSmachinery.all_machines) - if(bomb.timing) - to_chat(user, "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]") - -/obj/item/pinpointer/Destroy() - active = 0 - STOP_PROCESSING(SSfast_process, src) - return ..() - -/obj/item/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/pinpointer/advpinpointer/attack_self() - if(!active) - active = 1 - if(mode == 0) - workdisk() - if(mode == 1) - worklocation() - if(mode == 2) - workobj() - START_PROCESSING(SSfast_process, src) - to_chat(usr, "You activate the pinpointer") - else - active = 0 - icon_state = "pinoff" - to_chat(usr, "You deactivate the pinpointer") - -/obj/item/pinpointer/advpinpointer/process() - switch(mode) - if (0) - workdisk() - if (1) - worklocation() - if (2) - workobj() - -/obj/item/pinpointer/advpinpointer/proc/worklocation() - if(!active) - STOP_PROCESSING(SSfast_process, src) - return - 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/pinpointer/advpinpointer/proc/workobj() - if(!active) - STOP_PROCESSING(SSfast_process, src) - return - 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/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(alert("Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", "Location", "Disk Recovery", "Other Signature")) - if("Location") - mode = 1 - - var/locationx = input(usr, "Please input the x coordinate to search for.", "Location?" , "") as num - if(!locationx || !(usr in view(1,src))) - return - var/locationy = input(usr, "Please input the y coordinate to search for.", "Location?" , "") as num - 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(alert("Search for item signature or DNA fragment?" , "Signature Mode Select" , "" , "Item" , "DNA")) - if("Item") - var/datum/objective/steal/itemlist - itemlist = itemlist // To supress a 'variable defined but not used' error. - var/targetitem = input("Select item to search for.", "Item Mode Select","") as null|anything in 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 = input("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/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/pinpointer/nukeop/attack_self(mob/user as mob) - if(!active) - active = 1 - START_PROCESSING(SSfast_process, src) - if(!mode) - workdisk() - to_chat(user, "Authentication Disk Locator active.") - else - worklocation() - to_chat(user, "Shuttle Locator active.") - else - active = 0 - STOP_PROCESSING(SSfast_process, src) - icon_state = "pinoff" - to_chat(user, "You deactivate the pinpointer.") - -/obj/item/pinpointer/nukeop/process() - if (mode) - workdisk() - else - worklocation() - -/obj/item/pinpointer/nukeop/workdisk() - if(!active) return - if(mode) //Check in case the mode changes while operating - worklocation() - return - if(bomb_set) //If the bomb is set, lead to the shuttle - mode = 1 //Ensures worklocation() continues to work - worklocation() - playsound(loc, '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 -// if(loc.z != the_disk.z) //If you are on a different z-level from the disk -// icon_state = "pinonnull" -// else - 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/pinpointer/nukeop/proc/worklocation() - if(!active) return - if(!mode) - workdisk() - return - if(!bomb_set) - mode = 0 - workdisk() - playsound(loc, '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" +/obj/item/pinpointer + name = "pinpointer" + icon = 'icons/obj/device.dmi' + icon_state = "pinoff" + flags = CONDUCT + slot_flags = SLOT_BELT + w_class = 2.0 + item_state = "electronic" + throw_speed = 4 + throw_range = 20 + matter = list(DEFAULT_WALL_MATERIAL = 500) + var/obj/item/disk/nuclear/the_disk = null + var/active = 0 + +/obj/item/pinpointer/attack_self() + if(!active) + active = 1 + START_PROCESSING(SSfast_process, src) + to_chat(usr, "You activate the pinpointer") + else + active = 0 + STOP_PROCESSING(SSfast_process, src) + icon_state = "pinoff" + to_chat(usr, "You deactivate the pinpointer") + +/obj/item/pinpointer/process() + if (active) + workdisk() + else + STOP_PROCESSING(SSfast_process, src) + +/obj/item/pinpointer/proc/workdisk() + if(!active) return + 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/pinpointer/examine(mob/user) + ..(user) + for(var/obj/machinery/nuclearbomb/bomb in SSmachinery.all_machines) + if(bomb.timing) + to_chat(user, "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]") + +/obj/item/pinpointer/Destroy() + active = 0 + STOP_PROCESSING(SSfast_process, src) + return ..() + +/obj/item/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/pinpointer/advpinpointer/attack_self() + if(!active) + active = 1 + if(mode == 0) + workdisk() + if(mode == 1) + worklocation() + if(mode == 2) + workobj() + START_PROCESSING(SSfast_process, src) + to_chat(usr, "You activate the pinpointer") + else + active = 0 + icon_state = "pinoff" + to_chat(usr, "You deactivate the pinpointer") + +/obj/item/pinpointer/advpinpointer/process() + switch(mode) + if (0) + workdisk() + if (1) + worklocation() + if (2) + workobj() + +/obj/item/pinpointer/advpinpointer/proc/worklocation() + if(!active) + STOP_PROCESSING(SSfast_process, src) + return + 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/pinpointer/advpinpointer/proc/workobj() + if(!active) + STOP_PROCESSING(SSfast_process, src) + return + 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/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(alert("Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", "Location", "Disk Recovery", "Other Signature")) + if("Location") + mode = 1 + + var/locationx = input(usr, "Please input the x coordinate to search for.", "Location?" , "") as num + if(!locationx || !(usr in view(1,src))) + return + var/locationy = input(usr, "Please input the y coordinate to search for.", "Location?" , "") as num + 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(alert("Search for item signature or DNA fragment?" , "Signature Mode Select" , "" , "Item" , "DNA")) + if("Item") + var/datum/objective/steal/itemlist + itemlist = itemlist // To supress a 'variable defined but not used' error. + var/targetitem = input("Select item to search for.", "Item Mode Select","") as null|anything in 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 = input("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/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/pinpointer/nukeop/attack_self(mob/user as mob) + if(!active) + active = 1 + START_PROCESSING(SSfast_process, src) + if(!mode) + workdisk() + to_chat(user, "Authentication Disk Locator active.") + else + worklocation() + to_chat(user, "Shuttle Locator active.") + else + active = 0 + STOP_PROCESSING(SSfast_process, src) + icon_state = "pinoff" + to_chat(user, "You deactivate the pinpointer.") + +/obj/item/pinpointer/nukeop/process() + if (mode) + workdisk() + else + worklocation() + +/obj/item/pinpointer/nukeop/workdisk() + if(!active) return + if(mode) //Check in case the mode changes while operating + worklocation() + return + if(bomb_set) //If the bomb is set, lead to the shuttle + mode = 1 //Ensures worklocation() continues to work + worklocation() + playsound(loc, '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 +// if(loc.z != the_disk.z) //If you are on a different z-level from the disk +// icon_state = "pinonnull" +// else + 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/pinpointer/nukeop/proc/worklocation() + if(!active) return + if(!mode) + workdisk() + return + if(!bomb_set) + mode = 0 + workdisk() + playsound(loc, '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" diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index f2cbeb64f25..a408d2b365a 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1,909 +1,909 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -var/global/list/all_objectives = list() -var/global/list/process_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. - var/process = 0 //Does the objective need regular checking? - - New(var/text) - all_objectives |= src - if(text) - explanation_text = text - ..() - - if (process) - process_objectives |= src - - Destroy() - all_objectives -= src - - if (process) - process_objectives -= src - return ..() - - proc/check_completion() - return completed - - proc/find_target() - var/list/possible_targets = list() - for(var/datum/mind/possible_target in SSticker.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) - - - 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 SSticker.minds) - if((possible_target != owner) && ishuman(possible_target.current) && ((role_type ? possible_target.special_role : possible_target.assigned_role) == role) ) - target = possible_target - break - - process() - return - - -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 - - - 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 - - - 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) - explanation_text = "[target.current.real_name], the [target.assigned_role] has extracted confidential information above their clearance. Execute \him[target.current]." - else - explanation_text = "Free Objective" - return target - - - find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has extracted confidential information above their clearance. Execute \him[target.current]." - else - explanation_text = "Free Objective" - return target - - 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 - - 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 - - - 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 - - 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) - explanation_text = "[target.current.real_name], the [target.assigned_role] has been classified as harmful to [current_map.company_name]'s goals. Demote \him[target.current] to assistant." - else - explanation_text = "Free Objective" - return target - - find_target_by_role(role, role_type=0) - ..(role, role_type) - if(target && target.current) - explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has been classified as harmful to [current_map.company_name]'s goals. Demote \him[target.current] to assistant." - else - explanation_text = "Free Objective" - return target - - check_completion() - if(target && target.current && istype(target,/mob/living/carbon/human)) - var/obj/item/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 == "Assistant") - return 1 - else - return 0 - return 1 - -datum/objective/debrain//I want braaaainssss - find_target() - ..() - if(target && target.current) - explanation_text = "Steal the brain of [target.current.real_name]." - else - explanation_text = "Free Objective" - return target - - - 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 - - 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 - - -datum/objective/protect//The opposite of killing a dude. - find_target() - ..() - if(target && target.current) - explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - - - 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 - - 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." - - 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." - - - 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." - - 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." - - - 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 - - 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." - - 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 - - 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 - - - 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 - - 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 - - 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 - - - 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 - - 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 captain's antique laser gun" = /obj/item/gun/energy/captain, - "a hand teleporter" = /obj/item/hand_tele, - "a RFD C-Class" = /obj/item/rfd/construction, - "a jetpack" = /obj/item/tank/jetpack, - "a captain's jumpsuit" = /obj/item/clothing/under/rank/captain, - "a functional AI" = /obj/item/aicard, - "a pair of magboots" = /obj/item/clothing/shoes/magboots, - "the station blueprints" = /obj/item/blueprints, - "a nasa voidsuit" = /obj/item/clothing/suit/space/void, - "28 moles of phoron (full tank)" = /obj/item/tank, - "a sample of slime extract" = /obj/item/slime_extract, - "a piece of corgi meat" = /obj/item/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/reagent_containers/hypospray, - "the captain's pinpointer" = /obj/item/pinpointer, - "an ablative armor vest" = /obj/item/clothing/suit/armor/laserproof - ) - - var/global/possible_items_special[] = list( - /*"nuclear authentication disk" = /obj/item/disk/nuclear,*///Broken with the change to nuke disk making it respawn on z level change. - "nuclear gun" = /obj/item/gun/energy/gun/nuclear, - "diamond drill" = /obj/item/pickaxe/diamonddrill, - "bag of holding" = /obj/item/storage/backpack/holding, - "hyper-capacity cell" = /obj/item/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 - ) - - - 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 - - - find_target() - return set_target(pick(possible_items)) - - - proc/select_target() - var/list/possible_items_all = possible_items+possible_items_special+"custom" - var/new_target = input("Select target:", "Objective target", steal_target) as null|anything in possible_items_all - if (!new_target) return - if (new_target == "custom") - var/obj/item/custom_target = input("Select type:","Type") as null|anything in 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(input("Enter target name:", "Objective target", custom_name) as text|null) - 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 - - 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/storage/bag/money/B = locate() in all_items - - if(B) - var/target = text2num(target_name) - var/found_amount = 0.0 - for(var/obj/item/coin/C in B) - found_amount++ - return found_amount>=target - - if("a functional AI") - - for(var/obj/item/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 silicon_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 - - - check_completion() - if(!ishuman(owner.current)) - return 0 - if(!owner.current || owner.current.stat == 2) - return 0 - - var/current_amount - var/obj/item/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 - -// Heist objectives. -datum/objective/heist - proc/choose_target() - return - -datum/objective/heist/kidnap - choose_target() - var/list/roles = list("Chief Engineer","Research Director","Roboticist","Pharmacist","Station Engineer") - var/list/possible_targets = list() - var/list/priority_targets = list() - - for(var/datum/mind/possible_target in SSticker.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 - - 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/gun - target_amount = 6 - loot = "six guns" - if(6) - target = /obj/item/gun/energy - target_amount = 4 - loot = "four energy guns" - if(7) - target = /obj/item/gun/energy/laser - target_amount = 2 - loot = "two laser guns" - if(8) - target = /obj/item/gun/energy/rifle/ionrifle - target_amount = 1 - loot = "an ion gun" - - explanation_text = "It's a buyer's market out here. Steal [loot] for resale." - - 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 = DEFAULT_WALL_MATERIAL - target_amount = 300 - if(2) - target = "glass" - target_amount = 200 - if(3) - target = "plasteel" - target_amount = 100 - if(4) - target = "phoron" - target_amount = 100 - if(5) - target = "silver" - target_amount = 50 - if(6) - target = "gold" - target_amount = 20 - if(7) - target = "uranium" - target_amount = 20 - if(8) - target = "diamond" - target_amount = 20 - - explanation_text = "Ransack the station and escape with [target_amount] [target]." - - 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." - - 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_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_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 SSmachinery.all_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() +var/global/list/process_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. + var/process = 0 //Does the objective need regular checking? + + New(var/text) + all_objectives |= src + if(text) + explanation_text = text + ..() + + if (process) + process_objectives |= src + + Destroy() + all_objectives -= src + + if (process) + process_objectives -= src + return ..() + + proc/check_completion() + return completed + + proc/find_target() + var/list/possible_targets = list() + for(var/datum/mind/possible_target in SSticker.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) + + + 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 SSticker.minds) + if((possible_target != owner) && ishuman(possible_target.current) && ((role_type ? possible_target.special_role : possible_target.assigned_role) == role) ) + target = possible_target + break + + process() + return + + +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 + + + 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 + + + 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) + explanation_text = "[target.current.real_name], the [target.assigned_role] has extracted confidential information above their clearance. Execute \him[target.current]." + else + explanation_text = "Free Objective" + return target + + + find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has extracted confidential information above their clearance. Execute \him[target.current]." + else + explanation_text = "Free Objective" + return target + + 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 + + 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 + + + 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 + + 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) + explanation_text = "[target.current.real_name], the [target.assigned_role] has been classified as harmful to [current_map.company_name]'s goals. Demote \him[target.current] to assistant." + else + explanation_text = "Free Objective" + return target + + find_target_by_role(role, role_type=0) + ..(role, role_type) + if(target && target.current) + explanation_text = "[target.current.real_name], the [!role_type ? target.assigned_role : target.special_role] has been classified as harmful to [current_map.company_name]'s goals. Demote \him[target.current] to assistant." + else + explanation_text = "Free Objective" + return target + + check_completion() + if(target && target.current && istype(target,/mob/living/carbon/human)) + var/obj/item/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 == "Assistant") + return 1 + else + return 0 + return 1 + +datum/objective/debrain//I want braaaainssss + find_target() + ..() + if(target && target.current) + explanation_text = "Steal the brain of [target.current.real_name]." + else + explanation_text = "Free Objective" + return target + + + 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 + + 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 + + +datum/objective/protect//The opposite of killing a dude. + find_target() + ..() + if(target && target.current) + explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + + + 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 + + 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." + + 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." + + + 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." + + 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." + + + 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 + + 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." + + 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 + + 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 + + + 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 + + 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 + + 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 + + + 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 + + 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 captain's antique laser gun" = /obj/item/gun/energy/captain, + "a hand teleporter" = /obj/item/hand_tele, + "a RFD C-Class" = /obj/item/rfd/construction, + "a jetpack" = /obj/item/tank/jetpack, + "a captain's jumpsuit" = /obj/item/clothing/under/rank/captain, + "a functional AI" = /obj/item/aicard, + "a pair of magboots" = /obj/item/clothing/shoes/magboots, + "the station blueprints" = /obj/item/blueprints, + "a nasa voidsuit" = /obj/item/clothing/suit/space/void, + "28 moles of phoron (full tank)" = /obj/item/tank, + "a sample of slime extract" = /obj/item/slime_extract, + "a piece of corgi meat" = /obj/item/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/reagent_containers/hypospray, + "the captain's pinpointer" = /obj/item/pinpointer, + "an ablative armor vest" = /obj/item/clothing/suit/armor/laserproof + ) + + var/global/possible_items_special[] = list( + /*"nuclear authentication disk" = /obj/item/disk/nuclear,*///Broken with the change to nuke disk making it respawn on z level change. + "nuclear gun" = /obj/item/gun/energy/gun/nuclear, + "diamond drill" = /obj/item/pickaxe/diamonddrill, + "bag of holding" = /obj/item/storage/backpack/holding, + "hyper-capacity cell" = /obj/item/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 + ) + + + 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 + + + find_target() + return set_target(pick(possible_items)) + + + proc/select_target() + var/list/possible_items_all = possible_items+possible_items_special+"custom" + var/new_target = input("Select target:", "Objective target", steal_target) as null|anything in possible_items_all + if (!new_target) return + if (new_target == "custom") + var/obj/item/custom_target = input("Select type:","Type") as null|anything in 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(input("Enter target name:", "Objective target", custom_name) as text|null) + 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 + + 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/storage/bag/money/B = locate() in all_items + + if(B) + var/target = text2num(target_name) + var/found_amount = 0.0 + for(var/obj/item/coin/C in B) + found_amount++ + return found_amount>=target + + if("a functional AI") + + for(var/obj/item/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 silicon_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 + + + check_completion() + if(!ishuman(owner.current)) + return 0 + if(!owner.current || owner.current.stat == 2) + return 0 + + var/current_amount + var/obj/item/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 + +// Heist objectives. +datum/objective/heist + proc/choose_target() + return + +datum/objective/heist/kidnap + choose_target() + var/list/roles = list("Chief Engineer","Research Director","Roboticist","Pharmacist","Station Engineer") + var/list/possible_targets = list() + var/list/priority_targets = list() + + for(var/datum/mind/possible_target in SSticker.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 + + 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/gun + target_amount = 6 + loot = "six guns" + if(6) + target = /obj/item/gun/energy + target_amount = 4 + loot = "four energy guns" + if(7) + target = /obj/item/gun/energy/laser + target_amount = 2 + loot = "two laser guns" + if(8) + target = /obj/item/gun/energy/rifle/ionrifle + target_amount = 1 + loot = "an ion gun" + + explanation_text = "It's a buyer's market out here. Steal [loot] for resale." + + 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 = DEFAULT_WALL_MATERIAL + target_amount = 300 + if(2) + target = "glass" + target_amount = 200 + if(3) + target = "plasteel" + target_amount = 100 + if(4) + target = "phoron" + target_amount = 100 + if(5) + target = "silver" + target_amount = 50 + if(6) + target = "gold" + target_amount = 20 + if(7) + target = "uranium" + target_amount = 20 + if(8) + target = "diamond" + target_amount = 20 + + explanation_text = "Ransack the station and escape with [target_amount] [target]." + + 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." + + 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_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_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 SSmachinery.all_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 e44f0d2ea1d..02544ba087f 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -1,13 +1,13 @@ -/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 = 10 - required_enemies = 4 - auto_recall_shuttle = 0 - end_on_antag_death = 0 -// shuttle_delay = 3 - antag_tags = list(MODE_REVOLUTIONARY, MODE_LOYALIST) - require_all_templates = 1 - ert_disabled = TRUE +/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 = 10 + required_enemies = 4 + auto_recall_shuttle = 0 + end_on_antag_death = 0 +// shuttle_delay = 3 + antag_tags = list(MODE_REVOLUTIONARY, MODE_LOYALIST) + require_all_templates = 1 + ert_disabled = TRUE diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index 341e8555925..97b84f4b4a2 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -1,153 +1,153 @@ -//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.forceMove(P.loc) - P.wear_suit.layer = initial(P.wear_suit.layer) - P.wear_suit = null - P.wear_suit = new/obj/item/clothing/suit/space(P) - P.wear_suit.layer = SCREEN_LAYER + 0.01 - if(P.head) - P.head.forceMove(P.loc) - P.head.layer = initial(P.head.layer) - P.head = null - P.head = new/obj/item/clothing/head/helmet/space(P) - P.head.layer = SCREEN_LAYER + 0.01 - if(P.wear_mask) - P.wear_mask.forceMove(P.loc) - P.wear_mask.layer = initial(P.wear_mask.layer) - P.wear_mask = null - P.wear_mask = new/obj/item/clothing/mask/gas(P) - P.wear_mask.layer = SCREEN_LAYER + 0.01 - if(P.back) - P.back.forceMove(P.loc) - P.back.layer = initial(P.back.layer) - P.back = null - P.back = new/obj/item/tank/jetpack(P) - P.back.layer = SCREEN_LAYER + 0.01 - P.internal = P.back - if("hsbmetal") - var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/metal - hsb.amount = 50 - hsb.forceMove(usr.loc) - if("hsbglass") - var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/glass - hsb.amount = 50 - hsb.forceMove(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(alert(usr, "Will this airlock require [get_access_desc(A)] access?", "Sandbox:", "Yes", "No") == "Yes") - hsb.req_access += A - - hsb.forceMove(usr.loc) - to_chat(usr, "Sandbox: Created an airlock.") - if("hsbcanister") - var/list/hsbcanisters = typesof(/obj/machinery/portable_atmospherics/canister/) - /obj/machinery/portable_atmospherics/canister/ - var/hsbcanister = input(usr, "Choose a canister to spawn.", "Sandbox:") in hsbcanisters + "Cancel" - if(!(hsbcanister == "Cancel")) - new hsbcanister(usr.loc) - if("hsbfueltank") - //var/obj/hsb = new/obj/weldfueltank - //hsb.forceMove(usr.loc) - if("hsbwatertank") - //var/obj/hsb = new/obj/watertank - //hsb.forceMove(usr.loc) - if("hsbtoolbox") - var/obj/item/storage/hsb = new/obj/item/storage/toolbox/mechanical - for(var/obj/item/device/radio/T in hsb) - qdel(T) - new/obj/item/crowbar (hsb) - hsb.forceMove(usr.loc) - if("hsbmedkit") - var/obj/item/storage/firstaid/hsb = new/obj/item/storage/firstaid/regular - hsb.forceMove(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/gun)) - continue - if(istype(O, /obj/item/assembly)) - continue - if(istype(O, /obj/item/device/camera)) - continue - if(istype(O, /obj/item/cloaking_device)) - continue - if(istype(O, /obj/item/dummy)) - continue - if(istype(O, /obj/item/melee/energy/sword)) - continue - if(istype(O, /obj/structure)) - continue - selectable += O - - var/hsbitem = input(usr, "Choose an object to spawn.", "Sandbox:") in selectable + "Cancel" - if(hsbitem != "Cancel") - 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.forceMove(P.loc) + P.wear_suit.layer = initial(P.wear_suit.layer) + P.wear_suit = null + P.wear_suit = new/obj/item/clothing/suit/space(P) + P.wear_suit.layer = SCREEN_LAYER + 0.01 + if(P.head) + P.head.forceMove(P.loc) + P.head.layer = initial(P.head.layer) + P.head = null + P.head = new/obj/item/clothing/head/helmet/space(P) + P.head.layer = SCREEN_LAYER + 0.01 + if(P.wear_mask) + P.wear_mask.forceMove(P.loc) + P.wear_mask.layer = initial(P.wear_mask.layer) + P.wear_mask = null + P.wear_mask = new/obj/item/clothing/mask/gas(P) + P.wear_mask.layer = SCREEN_LAYER + 0.01 + if(P.back) + P.back.forceMove(P.loc) + P.back.layer = initial(P.back.layer) + P.back = null + P.back = new/obj/item/tank/jetpack(P) + P.back.layer = SCREEN_LAYER + 0.01 + P.internal = P.back + if("hsbmetal") + var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/metal + hsb.amount = 50 + hsb.forceMove(usr.loc) + if("hsbglass") + var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/glass + hsb.amount = 50 + hsb.forceMove(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(alert(usr, "Will this airlock require [get_access_desc(A)] access?", "Sandbox:", "Yes", "No") == "Yes") + hsb.req_access += A + + hsb.forceMove(usr.loc) + to_chat(usr, "Sandbox: Created an airlock.") + if("hsbcanister") + var/list/hsbcanisters = typesof(/obj/machinery/portable_atmospherics/canister/) - /obj/machinery/portable_atmospherics/canister/ + var/hsbcanister = input(usr, "Choose a canister to spawn.", "Sandbox:") in hsbcanisters + "Cancel" + if(!(hsbcanister == "Cancel")) + new hsbcanister(usr.loc) + if("hsbfueltank") + //var/obj/hsb = new/obj/weldfueltank + //hsb.forceMove(usr.loc) + if("hsbwatertank") + //var/obj/hsb = new/obj/watertank + //hsb.forceMove(usr.loc) + if("hsbtoolbox") + var/obj/item/storage/hsb = new/obj/item/storage/toolbox/mechanical + for(var/obj/item/device/radio/T in hsb) + qdel(T) + new/obj/item/crowbar (hsb) + hsb.forceMove(usr.loc) + if("hsbmedkit") + var/obj/item/storage/firstaid/hsb = new/obj/item/storage/firstaid/regular + hsb.forceMove(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/gun)) + continue + if(istype(O, /obj/item/assembly)) + continue + if(istype(O, /obj/item/device/camera)) + continue + if(istype(O, /obj/item/cloaking_device)) + continue + if(istype(O, /obj/item/dummy)) + continue + if(istype(O, /obj/item/melee/energy/sword)) + continue + if(istype(O, /obj/structure)) + continue + selectable += O + + var/hsbitem = input(usr, "Choose an object to spawn.", "Sandbox:") in selectable + "Cancel" + if(hsbitem != "Cancel") + new hsbitem(usr.loc) diff --git a/code/game/gamemodes/sandbox/sandbox.dm b/code/game/gamemodes/sandbox/sandbox.dm index a96c7c76398..3e94a427ee6 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 list of accesses to add if the person has this title. - 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/department = null // Does this position have a department tag? - var/head_position = 0 // Is this position Command? - var/minimum_character_age = 17 - var/list/alt_ages = null // assoc list of alt titles to minimum character ages - var/ideal_character_age = 30 - - var/latejoin_at_spawnpoints = FALSE //If this job should use roundstart spawnpoints for latejoin (offstation jobs etc) - - 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/create_record = 1 // Do we announce/make records for people who spawn on this job? - - var/datum/outfit/outfit = null - var/list/alt_outfits = null // A list of special outfits for the alt titles list("alttitle" = /datum/outfit) - -//Only override this proc -/datum/job/proc/after_spawn(mob/living/carbon/human/H) - -/datum/job/proc/announce(mob/living/carbon/human/H) - -/datum/job/proc/get_outfit(mob/living/carbon/human/H, alt_title=null) - //Check if we have a speical outfit for that alt title - alt_title = H?.mind?.role_alt_title || alt_title - - if (H?.mind?.selected_faction?.titles_to_loadout) - if (alt_title && H.mind.selected_faction.titles_to_loadout[alt_title]) - return H.mind.selected_faction.titles_to_loadout[alt_title] - else if (H.mind.selected_faction.titles_to_loadout[H.job]) - return H.mind.selected_faction.titles_to_loadout[H.job] - - if (alt_title && LAZYACCESS(alt_outfits, alt_title)) - return alt_outfits[alt_title] - - if (alt_outfits && alt_outfits[H.job]) - return alt_outfits[H.job] - else if (outfit) - return outfit - -/datum/job/proc/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE, alt_title = null) - if(!H) - return 0 - - H.species.before_equip(H, visualsOnly, src) - H.preEquipOutfit(get_outfit(H, alt_title), visualsOnly) - -/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, alt_title = null) - if(!H) - return 0 - H.equipOutfit(get_outfit(H, alt_title), visualsOnly) - - H.species.after_equip(H, visualsOnly, src) - - if(!visualsOnly && announce) - announce(H) - -/datum/job/proc/setup_account(var/mob/living/carbon/human/H) - if(!account_allowed || (H.mind && H.mind.initial_account)) - return - - var/loyalty = 1 - if(H.client) - switch(H.client.prefs.nanotrasen_relation) - if(COMPANY_LOYAL) loyalty = 1.30 - if(COMPANY_SUPPORTATIVE) loyalty = 1.15 - if(COMPANY_NEUTRAL) loyalty = 1 - if(COMPANY_SKEPTICAL) loyalty = 0.85 - if(COMPANY_OPPOSED) loyalty = 0.70 - - //give them an account in the station database - var/species_modifier = (H.species ? H.species.economic_modifier : null) - if (!species_modifier) - var/datum/species/human_species = global.all_species["Human"] - species_modifier = human_species.economic_modifier - - var/money_amount = (rand(5,50) + rand(5, 50)) * loyalty * economic_modifier * species_modifier - var/datum/money_account/M = SSeconomy.create_account(H.real_name, money_amount, null) - 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.transactions.len) - var/datum/transaction/T = M.transactions[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()/del() -/datum/job/proc/equip_preview(mob/living/carbon/human/H, var/alt_title) - pre_equip(H, TRUE) - . = equip(H, TRUE, FALSE, alt_title=alt_title) - -/datum/job/proc/get_access(selected_title) - if(!config || config.jobs_have_minimal_access) - . = minimal_access.Copy() - else - . = access.Copy() - - if (LAZYLEN(title_accesses) && title_accesses[selected_title]) - . += title_accesses[selected_title] - -/datum/job/proc/is_position_available() - return (current_positions < total_positions) || (total_positions == -1) - -/datum/job/proc/fetch_age_restriction() - if (!config.age_restrictions_from_file) - return - - if (config.age_restrictions[lowertext(title)]) - minimal_player_age = config.age_restrictions[lowertext(title)] - else - minimal_player_age = 0 - -/datum/job/proc/late_equip(var/mob/living/carbon/human/H) - if(!H) - return 0 - - H.species.before_equip(H, FALSE, src) - - var/loyalty = 1 - if(H.client) - switch(H.client.prefs.nanotrasen_relation) - if(COMPANY_LOYAL) loyalty = 3 - if(COMPANY_SUPPORTATIVE) loyalty = 2 - if(COMPANY_NEUTRAL) loyalty = 1 - if(COMPANY_SKEPTICAL) loyalty = -2 - if(COMPANY_OPPOSED) loyalty = -3 - - //give them an account in the station database - var/species_modifier = min((H.species ? H.species.economic_modifier : 0) - 9, 0) - - var/wealth = (loyalty + economic_modifier + species_modifier) - - switch(wealth) - if(-INFINITY to 2) - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(H), slot_shoes) - if(3 to 6) - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/hooded/wintercoat/hoodie/grey(H), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(H), slot_shoes) - if(7 to 9) - H.equip_to_slot_or_del(new /obj/item/clothing/under/sl_suit(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/brown(H), slot_shoes) - H.equip_to_slot_or_del(new /obj/item/clothing/accessory/red(H), slot_tie) - if(10 to 14) - H.equip_to_slot_or_del(new /obj/item/clothing/under/suit_jacket/really_black(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), slot_shoes) - H.equip_to_slot_or_del(new /obj/item/storage/briefcase(H), slot_l_hand) - if(15 to INFINITY) - H.equip_to_slot_or_del(new /obj/item/clothing/under/sl_suit(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), slot_shoes) - H.equip_to_slot_or_del(new /obj/item/clothing/accessory/red(H), slot_tie) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/lawyer/bluejacket(H), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/clothing/accessory/locket(H), slot_tie) - -/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/outfit/job - name = "Standard Gear" - var/base_name = null - collect_not_del = FALSE - - var/allow_loadout = TRUE - allow_backbag_choice = TRUE - var/jobtype = null - - uniform = /obj/item/clothing/under/color/grey - id = /obj/item/card/id - l_ear = /obj/item/device/radio/headset - back = /obj/item/storage/backpack - shoes = /obj/item/clothing/shoes/black - pda = /obj/item/device/pda - - var/box = /obj/item/storage/box/survival - -/datum/outfit/job/equip(mob/living/carbon/human/H, visualsOnly = FALSE) - back = null //Nulling the backpack here, since we already equipped the backpack in pre_equip - if(box) - var/spawnbox = box - backpack_contents.Insert(1, spawnbox) // Box always takes a first slot in backpack - backpack_contents[spawnbox] = 1 - . = ..() - -/datum/outfit/job/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - -/datum/outfit/job/get_id_access(mob/living/carbon/human/H) - var/datum/job/J = SSjobs.GetJobType(jobtype) - if(!J) - J = SSjobs.GetJob(H.job) - return J.get_access(get_id_assignment(H)) - -/datum/outfit/job/get_id_rank(mob/living/carbon/human/H) - var/datum/job/J = SSjobs.GetJobType(jobtype) - if(!J) - J = SSjobs.GetJob(H.job) - return J.title +/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 = "#888888" // Selection screen color + var/list/alt_titles // List of alternate titles, if any + var/list/title_accesses // A map of title -> list of accesses to add if the person has this title. + 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/department = null // Does this position have a department tag? + var/head_position = 0 // Is this position Command? + var/minimum_character_age = 17 + var/list/alt_ages = null // assoc list of alt titles to minimum character ages + var/ideal_character_age = 30 + + var/latejoin_at_spawnpoints = FALSE //If this job should use roundstart spawnpoints for latejoin (offstation jobs etc) + + 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/create_record = 1 // Do we announce/make records for people who spawn on this job? + + var/datum/outfit/outfit = null + var/list/alt_outfits = null // A list of special outfits for the alt titles list("alttitle" = /datum/outfit) + var/list/blacklisted_species = null // A blacklist of species that can't be this job + +//Only override this proc +/datum/job/proc/after_spawn(mob/living/carbon/human/H) + +/datum/job/proc/announce(mob/living/carbon/human/H) + +/datum/job/proc/get_outfit(mob/living/carbon/human/H, alt_title=null) + //Check if we have a speical outfit for that alt title + alt_title = H?.mind?.role_alt_title || alt_title + + if (H?.mind?.selected_faction?.titles_to_loadout) + if (alt_title && H.mind.selected_faction.titles_to_loadout[alt_title]) + return H.mind.selected_faction.titles_to_loadout[alt_title] + else if (H.mind.selected_faction.titles_to_loadout[H.job]) + return H.mind.selected_faction.titles_to_loadout[H.job] + + if (alt_title && LAZYACCESS(alt_outfits, alt_title)) + return alt_outfits[alt_title] + + if (alt_outfits && alt_outfits[H.job]) + return alt_outfits[H.job] + else if (outfit) + return outfit + +/datum/job/proc/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE, alt_title = null) + if(!H) + return 0 + + H.species.before_equip(H, visualsOnly, src) + H.preEquipOutfit(get_outfit(H, alt_title), visualsOnly) + +/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, alt_title = null) + if(!H) + return 0 + H.equipOutfit(get_outfit(H, alt_title), visualsOnly) + + H.species.after_equip(H, visualsOnly, src) + + if(!visualsOnly && announce) + announce(H) + +/datum/job/proc/setup_account(var/mob/living/carbon/human/H) + if(!account_allowed || (H.mind && H.mind.initial_account)) + return + + var/loyalty = 1 + if(H.client) + switch(H.client.prefs.nanotrasen_relation) + if(COMPANY_LOYAL) loyalty = 1.30 + if(COMPANY_SUPPORTATIVE) loyalty = 1.15 + if(COMPANY_NEUTRAL) loyalty = 1 + if(COMPANY_SKEPTICAL) loyalty = 0.85 + if(COMPANY_OPPOSED) loyalty = 0.70 + + //give them an account in the station database + var/species_modifier = (H.species ? H.species.economic_modifier : null) + if (!species_modifier) + var/datum/species/human_species = global.all_species["Human"] + species_modifier = human_species.economic_modifier + + var/money_amount = (rand(5,50) + rand(5, 50)) * loyalty * economic_modifier * species_modifier + var/datum/money_account/M = SSeconomy.create_account(H.real_name, money_amount, null) + 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.transactions.len) + var/datum/transaction/T = M.transactions[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()/del() +/datum/job/proc/equip_preview(mob/living/carbon/human/H, var/alt_title) + pre_equip(H, TRUE) + . = equip(H, TRUE, FALSE, alt_title=alt_title) + +/datum/job/proc/get_access(selected_title) + if(!config || config.jobs_have_minimal_access) + . = minimal_access.Copy() + else + . = access.Copy() + + if (LAZYLEN(title_accesses) && title_accesses[selected_title]) + . += title_accesses[selected_title] + +/datum/job/proc/is_position_available() + return (current_positions < total_positions) || (total_positions == -1) + +/datum/job/proc/fetch_age_restriction() + if (!config.age_restrictions_from_file) + return + + if (config.age_restrictions[lowertext(title)]) + minimal_player_age = config.age_restrictions[lowertext(title)] + else + minimal_player_age = 0 + +/datum/job/proc/late_equip(var/mob/living/carbon/human/H) + if(!H) + return 0 + + H.species.before_equip(H, FALSE, src) + + var/loyalty = 1 + if(H.client) + switch(H.client.prefs.nanotrasen_relation) + if(COMPANY_LOYAL) loyalty = 3 + if(COMPANY_SUPPORTATIVE) loyalty = 2 + if(COMPANY_NEUTRAL) loyalty = 1 + if(COMPANY_SKEPTICAL) loyalty = -2 + if(COMPANY_OPPOSED) loyalty = -3 + + //give them an account in the station database + var/species_modifier = min((H.species ? H.species.economic_modifier : 0) - 9, 0) + + var/wealth = (loyalty + economic_modifier + species_modifier) + + switch(wealth) + if(-INFINITY to 2) + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(H), slot_shoes) + if(3 to 6) + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/hooded/wintercoat/hoodie/grey(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(H), slot_shoes) + if(7 to 9) + H.equip_to_slot_or_del(new /obj/item/clothing/under/sl_suit(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/brown(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/clothing/accessory/red(H), slot_tie) + if(10 to 14) + H.equip_to_slot_or_del(new /obj/item/clothing/under/suit_jacket/really_black(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/storage/briefcase(H), slot_l_hand) + if(15 to INFINITY) + H.equip_to_slot_or_del(new /obj/item/clothing/under/sl_suit(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/clothing/accessory/red(H), slot_tie) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/lawyer/bluejacket(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/accessory/locket(H), slot_tie) + +/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/outfit/job + name = "Standard Gear" + var/base_name = null + collect_not_del = FALSE + + var/allow_loadout = TRUE + allow_backbag_choice = TRUE + var/jobtype = null + + uniform = /obj/item/clothing/under/color/grey + id = /obj/item/card/id + l_ear = /obj/item/device/radio/headset + back = /obj/item/storage/backpack + shoes = /obj/item/clothing/shoes/black + pda = /obj/item/device/pda + + var/box = /obj/item/storage/box/survival + +/datum/outfit/job/equip(mob/living/carbon/human/H, visualsOnly = FALSE) + back = null //Nulling the backpack here, since we already equipped the backpack in pre_equip + if(box) + var/spawnbox = box + backpack_contents.Insert(1, spawnbox) // Box always takes a first slot in backpack + backpack_contents[spawnbox] = 1 + . = ..() + +/datum/outfit/job/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + +/datum/outfit/job/get_id_access(mob/living/carbon/human/H) + var/datum/job/J = SSjobs.GetJobType(jobtype) + if(!J) + J = SSjobs.GetJob(H.job) + return J.get_access(get_id_assignment(H)) + +/datum/outfit/job/get_id_rank(mob/living/carbon/human/H) + var/datum/job/J = SSjobs.GetJobType(jobtype) + if(!J) + J = SSjobs.GetJob(H.job) + return J.title diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm index af25750f381..6341268b019 100644 --- a/code/game/jobs/job/medical.dm +++ b/code/game/jobs/job/medical.dm @@ -1,281 +1,285 @@ -/datum/job/cmo - title = "Chief Medical Officer" - flag = CMO - head_position = 1 - department = "Medical" - department_flag = MEDSCI - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the captain" - selection_color = "#FF56B4" - economic_modifier = 10 - - minimum_character_age = 35 - - access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, - access_pharmacy, access_virology, access_cmo, access_surgery, access_RC_announce, access_engine, access_construction, - access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_research, - access_paramedic, access_maint_tunnels) - minimal_access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, - access_pharmacy, access_virology, access_cmo, access_surgery, access_RC_announce, access_engine, access_construction, - access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_research, - access_paramedic, access_maint_tunnels) - - minimal_player_age = 10 - ideal_character_age = 50 - outfit = /datum/outfit/job/cmo - -/datum/outfit/job/cmo - name = "Chief Medical Officer" - jobtype = /datum/job/cmo - - uniform = /obj/item/clothing/under/rank/chief_medical_officer - suit = /obj/item/clothing/suit/storage/toggle/labcoat/cmo - suit_store = /obj/item/device/flashlight/pen - shoes = /obj/item/clothing/shoes/brown - l_ear = /obj/item/device/radio/headset/heads/cmo - pda = /obj/item/device/pda/heads/cmo - id = /obj/item/card/id/navy - l_hand = /obj/item/storage/firstaid/adv - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/med - messengerbag = /obj/item/storage/backpack/messenger/med - -/datum/job/doctor - title = "Medical Doctor" - flag = DOCTOR - department = "Medical" - department_flag = MEDSCI - faction = "Station" - total_positions = 5 - spawn_positions = 3 - supervisors = "the chief medical officer" - selection_color = "#FF97D1" - economic_modifier = 7 - - minimum_character_age = 30 - - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_pharmacy, access_virology, access_genetics, access_eva) - minimal_access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_genetics, access_eva) - alt_titles = list("Surgeon","Emergency Physician","Nurse") - alt_ages = list("Nurse" = 25) - outfit = /datum/outfit/job/doctor - alt_outfits = list( - "Emergency Physician"=/datum/outfit/job/doctor/emergency_physician, - "Surgeon"=/datum/outfit/job/doctor/surgeon, - "Nurse"=/datum/outfit/job/doctor/nurse - ) - -/datum/outfit/job/doctor - name = "Medical Doctor" - base_name = "Medical Doctor" - jobtype = /datum/job/doctor - - uniform = /obj/item/clothing/under/rank/medical - suit = /obj/item/clothing/suit/storage/toggle/labcoat/medical - shoes = /obj/item/clothing/shoes/medical - l_ear = /obj/item/device/radio/headset/headset_med - pda = /obj/item/device/pda/medical - id = /obj/item/card/id/white - suit_store = /obj/item/device/flashlight/pen - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/med - messengerbag = /obj/item/storage/backpack/messenger/med - -/datum/outfit/job/doctor/emergency_physician - name = "Emergency Physician" - jobtype = /datum/job/doctor - - suit = /obj/item/clothing/suit/storage/toggle/fr_jacket - mask = /obj/item/clothing/mask/surgical - l_hand = /obj/item/storage/firstaid/adv - -/datum/outfit/job/doctor/surgeon - name = "Surgeon" - jobtype = /datum/job/doctor - - uniform = /obj/item/clothing/under/rank/medical/blue - suit = /obj/item/clothing/suit/storage/toggle/labcoat/surgeon - shoes = /obj/item/clothing/shoes/surgeon - head = /obj/item/clothing/head/surgery/blue - -/datum/outfit/job/doctor/nurse - name = "Nurse" - jobtype = /datum/job/doctor - - uniform = /obj/item/clothing/under/rank/medical/purple - suit = null - head = /obj/item/clothing/head/nursehat - - -/datum/job/pharmacist - title = "Pharmacist" - flag = CHEMIST - department = "Medical" - department_flag = MEDSCI - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the chief medical officer" - selection_color = "#FF97D1" - economic_modifier = 5 - - minimum_character_age = 26 - - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_pharmacy, access_virology, access_genetics) - minimal_access = list(access_medical, access_medical_equip, access_pharmacy, access_virology) - alt_titles = list("Biochemist") - outfit = /datum/outfit/job/pharmacist - alt_outfits = list( - "Biochemist"=/datum/outfit/job/pharmacist/biochemist - ) - -/datum/outfit/job/pharmacist - name = "Pharmacist" - jobtype = /datum/job/pharmacist - - uniform = /obj/item/clothing/under/rank/pharmacist - suit = /obj/item/clothing/suit/storage/toggle/labcoat/pharmacist - shoes = /obj/item/clothing/shoes/chemist - l_ear = /obj/item/device/radio/headset/headset_med - pda = /obj/item/device/pda/chemist - id = /obj/item/card/id/white - - backpack = /obj/item/storage/backpack/pharmacy - satchel = /obj/item/storage/backpack/satchel_pharm - dufflebag = /obj/item/storage/backpack/duffel/pharm - messengerbag = /obj/item/storage/backpack/messenger/pharm - -/datum/outfit/job/pharmacist/biochemist - name = "Biochemist" - jobtype = /datum/job/pharmacist - - uniform = /obj/item/clothing/under/rank/biochemist - suit = /obj/item/clothing/suit/storage/toggle/labcoat/biochemist - shoes = /obj/item/clothing/shoes/biochem - - backpack = /obj/item/storage/backpack/virology - satchel = /obj/item/storage/backpack/satchel_vir - dufflebag = /obj/item/storage/backpack/duffel/vir - messengerbag = /obj/item/storage/backpack/messenger/viro - -/datum/job/psychiatrist - title = "Psychiatrist" - flag = PSYCHIATRIST - department = "Medical" - department_flag = MEDSCI - faction = "Station" - total_positions = 1 - spawn_positions = 1 - economic_modifier = 5 - - minimum_character_age = 30 - - supervisors = "the chief medical officer" - selection_color = "#FF97D1" - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_pharmacy, access_virology, access_genetics, access_psychiatrist) - minimal_access = list(access_medical, access_medical_equip, access_psychiatrist) - alt_titles = list("Psychologist") - outfit = /datum/outfit/job/psychiatrist - alt_outfits = list("Psychologist" = /datum/outfit/job/psychiatrist/psycho) - -/datum/outfit/job/psychiatrist - name = "Psychiatrist" - base_name = "Psychiatrist" - jobtype = /datum/job/psychiatrist - - uniform = /obj/item/clothing/under/rank/psych - suit = /obj/item/clothing/suit/storage/toggle/labcoat/psych - shoes = /obj/item/clothing/shoes/psych - l_ear = /obj/item/device/radio/headset/headset_med - pda = /obj/item/device/pda/psych - id = /obj/item/card/id/white - -/datum/outfit/job/psychiatrist/psycho - name = "Psychologist" - jobtype = /datum/job/psychiatrist - - uniform = /obj/item/clothing/under/rank/psych/turtleneck - - -/datum/job/paramedic - title = "Paramedic" - flag = PARAMEDIC - department = "Medical" - department_flag = MEDSCI - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the chief medical officer" - selection_color = "#FF97D1" - economic_modifier = 4 - - minimum_character_age = 24 - alt_ages = list("Emergency Medical Technician" = 20) - - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_pharmacy, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_psychiatrist, access_paramedic) - minimal_access = list(access_medical, access_medical_equip, access_morgue, access_eva, access_maint_tunnels, access_external_airlocks, access_paramedic) - alt_titles = list("Emergency Medical Technician") - outfit = /datum/outfit/job/paramedic - alt_outfits = list("Emergency Medical Technician"=/datum/outfit/job/paramedic/emt) - -/datum/outfit/job/paramedic - name = "Paramedic" - base_name = "Paramedic" - jobtype = /datum/job/paramedic - - uniform = /obj/item/clothing/under/rank/medical/black - suit = /obj/item/clothing/suit/storage/toggle/fr_jacket - shoes = /obj/item/clothing/shoes/jackboots - l_ear = /obj/item/device/radio/headset/headset_med - l_hand = /obj/item/storage/firstaid/adv - r_hand = /obj/item/reagent_containers/hypospray - belt = /obj/item/storage/belt/medical/emt - pda = /obj/item/device/pda/paramedic - id = /obj/item/card/id/white - head = /obj/item/clothing/head/hardhat/emt - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/med - messengerbag = /obj/item/storage/backpack/messenger/med - -/datum/outfit/job/paramedic/emt - name = "Emergency Medical Technician" - jobtype = /datum/job/paramedic - - uniform = /obj/item/clothing/under/rank/medical/paramedic - -/datum/job/intern_med - title = "Medical Resident" - flag = INTERN_MED - department_flag = MEDSCI - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Chief Medical Officer" - selection_color = "#FF97D1" - access = list(access_medical, access_surgery, access_medical_equip) - minimal_access = list(access_medical, access_surgery, access_medical_equip) - minimum_character_age = 25 - alt_titles = list("Medical Intern") - alt_ages = list("Medical Intern" = 18) - outfit = /datum/outfit/job/intern_med - -/datum/outfit/job/intern_med - name = "Medical Resident" - jobtype = /datum/job/intern_med - - uniform = /obj/item/clothing/under/rank/medical/intern - shoes = /obj/item/clothing/shoes/medical - l_ear = /obj/item/device/radio/headset/headset_med - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/med - messengerbag = /obj/item/storage/backpack/messenger/med +/datum/job/cmo + title = "Chief Medical Officer" + flag = CMO + head_position = 1 + department = "Medical" + department_flag = MEDSCI + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the captain" + selection_color = "#FF56B4" + economic_modifier = 10 + + minimum_character_age = 35 + + access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, + access_pharmacy, access_virology, access_cmo, access_surgery, access_RC_announce, access_engine, access_construction, + access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_research, + access_paramedic, access_maint_tunnels) + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_genetics, access_heads, + access_pharmacy, access_virology, access_cmo, access_surgery, access_RC_announce, access_engine, access_construction, + access_keycard_auth, access_sec_doors, access_psychiatrist, access_eva, access_external_airlocks, access_research, + access_paramedic, access_maint_tunnels) + + minimal_player_age = 10 + ideal_character_age = 50 + outfit = /datum/outfit/job/cmo + + blacklisted_species = list("M'sai Tajara", "Zhan-Khazan Tajara", "Aut'akh Unathi", "Vaurca Worker", "Vaurca Warrior") + +/datum/outfit/job/cmo + name = "Chief Medical Officer" + jobtype = /datum/job/cmo + + uniform = /obj/item/clothing/under/rank/chief_medical_officer + suit = /obj/item/clothing/suit/storage/toggle/labcoat/cmo + suit_store = /obj/item/device/flashlight/pen + shoes = /obj/item/clothing/shoes/brown + l_ear = /obj/item/device/radio/headset/heads/cmo + pda = /obj/item/device/pda/heads/cmo + id = /obj/item/card/id/navy + l_hand = /obj/item/storage/firstaid/adv + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/med + messengerbag = /obj/item/storage/backpack/messenger/med + +/datum/job/doctor + title = "Medical Doctor" + flag = DOCTOR + department = "Medical" + department_flag = MEDSCI + faction = "Station" + total_positions = 5 + spawn_positions = 3 + supervisors = "the chief medical officer" + selection_color = "#FF97D1" + economic_modifier = 7 + + minimum_character_age = 30 + + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_pharmacy, access_virology, access_genetics, access_eva) + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_genetics, access_eva) + alt_titles = list("Surgeon","Emergency Physician","Nurse") + alt_ages = list("Nurse" = 25) + outfit = /datum/outfit/job/doctor + alt_outfits = list( + "Emergency Physician"=/datum/outfit/job/doctor/emergency_physician, + "Surgeon"=/datum/outfit/job/doctor/surgeon, + "Nurse"=/datum/outfit/job/doctor/nurse + ) + +/datum/outfit/job/doctor + name = "Medical Doctor" + base_name = "Medical Doctor" + jobtype = /datum/job/doctor + + uniform = /obj/item/clothing/under/rank/medical + suit = /obj/item/clothing/suit/storage/toggle/labcoat/medical + shoes = /obj/item/clothing/shoes/medical + l_ear = /obj/item/device/radio/headset/headset_med + pda = /obj/item/device/pda/medical + id = /obj/item/card/id/white + suit_store = /obj/item/device/flashlight/pen + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/med + messengerbag = /obj/item/storage/backpack/messenger/med + +/datum/outfit/job/doctor/emergency_physician + name = "Emergency Physician" + jobtype = /datum/job/doctor + + suit = /obj/item/clothing/suit/storage/toggle/fr_jacket + mask = /obj/item/clothing/mask/surgical + l_hand = /obj/item/storage/firstaid/adv + +/datum/outfit/job/doctor/surgeon + name = "Surgeon" + jobtype = /datum/job/doctor + + uniform = /obj/item/clothing/under/rank/medical/blue + suit = /obj/item/clothing/suit/storage/toggle/labcoat/surgeon + shoes = /obj/item/clothing/shoes/surgeon + head = /obj/item/clothing/head/surgery/blue + +/datum/outfit/job/doctor/nurse + name = "Nurse" + jobtype = /datum/job/doctor + + uniform = /obj/item/clothing/under/rank/medical/purple + suit = null + head = /obj/item/clothing/head/nursehat + + +/datum/job/pharmacist + title = "Pharmacist" + flag = CHEMIST + department = "Medical" + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the chief medical officer" + selection_color = "#FF97D1" + economic_modifier = 5 + + minimum_character_age = 26 + + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_pharmacy, access_virology, access_genetics) + minimal_access = list(access_medical, access_medical_equip, access_pharmacy, access_virology) + alt_titles = list("Biochemist") + outfit = /datum/outfit/job/pharmacist + alt_outfits = list( + "Biochemist"=/datum/outfit/job/pharmacist/biochemist + ) + +/datum/outfit/job/pharmacist + name = "Pharmacist" + jobtype = /datum/job/pharmacist + + uniform = /obj/item/clothing/under/rank/pharmacist + suit = /obj/item/clothing/suit/storage/toggle/labcoat/pharmacist + shoes = /obj/item/clothing/shoes/chemist + l_ear = /obj/item/device/radio/headset/headset_med + pda = /obj/item/device/pda/chemist + id = /obj/item/card/id/white + + backpack = /obj/item/storage/backpack/pharmacy + satchel = /obj/item/storage/backpack/satchel_pharm + dufflebag = /obj/item/storage/backpack/duffel/pharm + messengerbag = /obj/item/storage/backpack/messenger/pharm + +/datum/outfit/job/pharmacist/biochemist + name = "Biochemist" + jobtype = /datum/job/pharmacist + + uniform = /obj/item/clothing/under/rank/biochemist + suit = /obj/item/clothing/suit/storage/toggle/labcoat/biochemist + shoes = /obj/item/clothing/shoes/biochem + + backpack = /obj/item/storage/backpack/virology + satchel = /obj/item/storage/backpack/satchel_vir + dufflebag = /obj/item/storage/backpack/duffel/vir + messengerbag = /obj/item/storage/backpack/messenger/viro + +/datum/job/psychiatrist + title = "Psychiatrist" + flag = PSYCHIATRIST + department = "Medical" + department_flag = MEDSCI + faction = "Station" + total_positions = 1 + spawn_positions = 1 + economic_modifier = 5 + + minimum_character_age = 30 + + supervisors = "the chief medical officer" + selection_color = "#FF97D1" + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_pharmacy, access_virology, access_genetics, access_psychiatrist) + minimal_access = list(access_medical, access_medical_equip, access_psychiatrist) + alt_titles = list("Psychologist") + outfit = /datum/outfit/job/psychiatrist + alt_outfits = list("Psychologist" = /datum/outfit/job/psychiatrist/psycho) + +/datum/outfit/job/psychiatrist + name = "Psychiatrist" + base_name = "Psychiatrist" + jobtype = /datum/job/psychiatrist + + uniform = /obj/item/clothing/under/rank/psych + suit = /obj/item/clothing/suit/storage/toggle/labcoat/psych + shoes = /obj/item/clothing/shoes/psych + l_ear = /obj/item/device/radio/headset/headset_med + pda = /obj/item/device/pda/psych + id = /obj/item/card/id/white + +/datum/outfit/job/psychiatrist/psycho + name = "Psychologist" + jobtype = /datum/job/psychiatrist + + uniform = /obj/item/clothing/under/rank/psych/turtleneck + + +/datum/job/paramedic + title = "Paramedic" + flag = PARAMEDIC + department = "Medical" + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the chief medical officer" + selection_color = "#FF97D1" + economic_modifier = 4 + + minimum_character_age = 24 + alt_ages = list("Emergency Medical Technician" = 20) + + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_pharmacy, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_psychiatrist, access_paramedic) + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_eva, access_maint_tunnels, access_external_airlocks, access_paramedic) + alt_titles = list("Emergency Medical Technician") + outfit = /datum/outfit/job/paramedic + alt_outfits = list("Emergency Medical Technician"=/datum/outfit/job/paramedic/emt) + + blacklisted_species = list("Diona") + +/datum/outfit/job/paramedic + name = "Paramedic" + base_name = "Paramedic" + jobtype = /datum/job/paramedic + + uniform = /obj/item/clothing/under/rank/medical/black + suit = /obj/item/clothing/suit/storage/toggle/fr_jacket + shoes = /obj/item/clothing/shoes/jackboots + l_ear = /obj/item/device/radio/headset/headset_med + l_hand = /obj/item/storage/firstaid/adv + r_hand = /obj/item/reagent_containers/hypospray + belt = /obj/item/storage/belt/medical/emt + pda = /obj/item/device/pda/paramedic + id = /obj/item/card/id/white + head = /obj/item/clothing/head/hardhat/emt + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/med + messengerbag = /obj/item/storage/backpack/messenger/med + +/datum/outfit/job/paramedic/emt + name = "Emergency Medical Technician" + jobtype = /datum/job/paramedic + + uniform = /obj/item/clothing/under/rank/medical/paramedic + +/datum/job/intern_med + title = "Medical Resident" + flag = INTERN_MED + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Chief Medical Officer" + selection_color = "#FF97D1" + access = list(access_medical, access_surgery, access_medical_equip) + minimal_access = list(access_medical, access_surgery, access_medical_equip) + minimum_character_age = 25 + alt_titles = list("Medical Intern") + alt_ages = list("Medical Intern" = 18) + outfit = /datum/outfit/job/intern_med + +/datum/outfit/job/intern_med + name = "Medical Resident" + jobtype = /datum/job/intern_med + + uniform = /obj/item/clothing/under/rank/medical/intern + shoes = /obj/item/clothing/shoes/medical + l_ear = /obj/item/device/radio/headset/headset_med + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/med + messengerbag = /obj/item/storage/backpack/messenger/med diff --git a/code/game/jobs/job/outsider/merchant.dm b/code/game/jobs/job/outsider/merchant.dm index 7dfbaf4de56..3f45cf78858 100644 --- a/code/game/jobs/job/outsider/merchant.dm +++ b/code/game/jobs/job/outsider/merchant.dm @@ -55,7 +55,7 @@ /obj/item/clothing/under/blazer ) shoes = list( - /obj/item/clothing/shoes/leather, + /obj/item/clothing/shoes/laceup/brown, /obj/item/clothing/shoes/laceup, /obj/item/clothing/shoes/workboots, /obj/item/clothing/shoes/black, diff --git a/code/game/jobs/job/outsider/representative.dm b/code/game/jobs/job/outsider/representative.dm index 45032ec4f7e..412d406d508 100644 --- a/code/game/jobs/job/outsider/representative.dm +++ b/code/game/jobs/job/outsider/representative.dm @@ -18,6 +18,8 @@ outfit = /datum/outfit/job/representative alt_titles = list("Consular Officer") + blacklisted_species = list("M'sai Tajara", "Zhan-Khazan Tajara", "Aut'akh Unathi") + /datum/job/representative/get_outfit(mob/living/carbon/human/H, alt_title = null) if(H.mind?.role_alt_title == "Consular Officer" || alt_title == "Consular Officer") var/datum/citizenship/citizenship = SSrecords.citizenships[H.citizenship] diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index cf2f2ff3f70..6e3076122f5 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -25,6 +25,8 @@ ideal_character_age = 50 outfit = /datum/outfit/job/rd + blacklisted_species = list("M'sai Tajara", "Zhan-Khazan Tajara", "Aut'akh Unathi", "Vaurca Worker", "Vaurca Warrior") + /datum/outfit/job/rd name = "Research Director" jobtype = /datum/job/rd diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index 26539fd5e7c..5f5ae8ba51e 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -1,276 +1,278 @@ -/datum/job/hos - title = "Head of Security" - flag = HOS - head_position = 1 - department = "Security" - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the captain" - selection_color = "#FF6363" - economic_modifier = 10 - - minimum_character_age = 30 - - 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, - access_detective, access_weapons) - 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, - access_detective, access_weapons) - minimal_player_age = 14 - outfit = /datum/outfit/job/hos - -/datum/outfit/job/hos - name = "Head of Security" - jobtype = /datum/job/hos - - uniform = /obj/item/clothing/under/rank/head_of_security - shoes = /obj/item/clothing/shoes/jackboots - l_ear = /obj/item/device/radio/headset/heads/hos - pda = /obj/item/device/pda/heads/hos - id = /obj/item/card/id/navy - glasses = /obj/item/clothing/glasses/sunglasses/sechud/head - - backpack_contents = list( - /obj/item/storage/box/ids = 1 - ) - - implants = list( - /obj/item/implant/loyalty - ) - - backpack = /obj/item/storage/backpack/security - satchel = /obj/item/storage/backpack/satchel_sec - dufflebag = /obj/item/storage/backpack/duffel/sec - messengerbag = /obj/item/storage/backpack/messenger/sec - -/datum/outfit/job/hos/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - if(istajara(H)) - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves) - else if(isunathi(H)) - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves) - else - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves) - - -/datum/job/warden - title = "Warden" - flag = WARDEN - department = "Security" - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the head of security" - selection_color = "#FFA4A4" - economic_modifier = 5 - - minimum_character_age = 25 - - access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_morgue, access_external_airlocks, access_weapons) - minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_external_airlocks, access_weapons) - minimal_player_age = 7 - outfit = /datum/outfit/job/warden - -/datum/outfit/job/warden - name = "Warden" - jobtype = /datum/job/warden - - uniform = /obj/item/clothing/under/rank/warden - shoes = /obj/item/clothing/shoes/jackboots - l_ear = /obj/item/device/radio/headset/headset_sec - pda = /obj/item/device/pda/warden - glasses = /obj/item/clothing/glasses/sunglasses/sechud/head - l_pocket = /obj/item/device/flash - - backpack = /obj/item/storage/backpack/security - satchel = /obj/item/storage/backpack/satchel_sec - dufflebag = /obj/item/storage/backpack/duffel/sec - messengerbag = /obj/item/storage/backpack/messenger/sec - - backpack_contents = list( - /obj/item/storage/box/ids = 1 - ) - -/datum/outfit/job/warden/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - if(istajara(H)) - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves) - else if(isunathi(H)) - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves) - else - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves) - - -/datum/job/detective - title = "Detective" - flag = DETECTIVE - department = "Security" - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the head of security" - selection_color = "#FFA4A4" - economic_modifier = 5 - - minimum_character_age = 25 - - access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_detective, access_weapons) - minimal_access = list(access_security, access_sec_doors, access_morgue, access_detective, access_weapons) - minimal_player_age = 7 - outfit = /datum/outfit/job/detective - -/datum/outfit/job/detective - name = "Detective" - jobtype = /datum/job/detective - - uniform = /obj/item/clothing/under/det - shoes = /obj/item/clothing/shoes/laceup - l_ear = /obj/item/device/radio/headset/headset_sec - pda = /obj/item/device/pda/detective - - backpack_contents = list( - /obj/item/storage/box/evidence = 1 - ) - -/datum/outfit/job/detective/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - if(istajara(H)) - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves) - else if(isunathi(H)) - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves) - else - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves) - - -/datum/job/forensics - title = "Forensic Technician" - flag = FORENSICS - department_flag = ENGSEC - faction = "Station" - total_positions = 1 - spawn_positions = 1 - supervisors = "the head of security" - selection_color = "#FFA4A4" - economic_modifier = 5 - - minimum_character_age = 25 - - access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_weapons) - minimal_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_weapons) - alt_titles = list("Crime Scene Investigator") - minimal_player_age = 3 - outfit = /datum/outfit/job/forensics - alt_outfits = list("Crime Scene Investigator"=/datum/outfit/job/forensics/csi) - -/datum/outfit/job/forensics - name = "Forensic Technician" - jobtype = /datum/job/forensics - - uniform = /obj/item/clothing/under/det/forensics - shoes = /obj/item/clothing/shoes/laceup - l_ear = /obj/item/device/radio/headset/headset_sec - pda = /obj/item/device/pda/detective - - backpack_contents = list( - /obj/item/storage/box/evidence = 1 - ) - -/datum/outfit/job/forensics/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - if(istajara(H)) - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves) - else if(isunathi(H)) - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves) - else - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves) - - -/datum/outfit/job/forensics/csi - name = "Crime Scene Investigator" - jobtype = /datum/job/forensics - - suit = /obj/item/clothing/suit/storage/toggle/labcoat - -/datum/job/officer - title = "Security Officer" - flag = OFFICER - department = "Security" - department_flag = ENGSEC - faction = "Station" - total_positions = 4 - spawn_positions = 4 - supervisors = "the head of security" - selection_color = "#FFA4A4" - economic_modifier = 4 - - minimum_character_age = 18 - - access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_morgue, access_external_airlocks, access_weapons) - minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_external_airlocks, access_weapons) - minimal_player_age = 7 - outfit = /datum/outfit/job/officer - -/datum/outfit/job/officer - name = "Security Officer" - jobtype = /datum/job/officer - - uniform = /obj/item/clothing/under/rank/security - shoes = /obj/item/clothing/shoes/jackboots - l_ear = /obj/item/device/radio/headset/headset_sec - pda = /obj/item/device/pda/security - l_pocket = /obj/item/device/flash - - backpack = /obj/item/storage/backpack/security - satchel = /obj/item/storage/backpack/satchel_sec - dufflebag = /obj/item/storage/backpack/duffel/sec - messengerbag = /obj/item/storage/backpack/messenger/sec - - backpack_contents = list( - /obj/item/handcuffs = 1 - ) - -/datum/outfit/job/officer/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - if(istajara(H)) - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves) - else if(isunathi(H)) - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves) - else - H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves) - -/datum/job/intern_sec - title = "Security Cadet" - flag = INTERN_SEC - department_flag = ENGSEC - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "the Head of Security" - selection_color = "#FFA4A4" - access = list(access_security, access_sec_doors, access_maint_tunnels) - minimal_access = list(access_security, access_sec_doors) - outfit = /datum/outfit/job/intern_sec - -/datum/outfit/job/intern_sec - name = "Security Cadet" - jobtype = /datum/job/intern_sec - - uniform = /obj/item/clothing/under/rank/cadet - suit = /obj/item/clothing/suit/storage/hazardvest/cadet - head = /obj/item/clothing/head/beret/sec/cadet - shoes = /obj/item/clothing/shoes/jackboots - l_ear = /obj/item/device/radio/headset/headset_sec - - backpack = /obj/item/storage/backpack/security - satchel = /obj/item/storage/backpack/satchel_sec - dufflebag = /obj/item/storage/backpack/duffel/sec - messengerbag = /obj/item/storage/backpack/messenger/sec +/datum/job/hos + title = "Head of Security" + flag = HOS + head_position = 1 + department = "Security" + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the captain" + selection_color = "#FF6363" + economic_modifier = 10 + + minimum_character_age = 30 + + 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, + access_detective, access_weapons) + 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, + access_detective, access_weapons) + minimal_player_age = 14 + outfit = /datum/outfit/job/hos + + blacklisted_species = list("Off-Worlder Human", "Zhan-Khazan Tajara", "Aut'akh Unathi", "Diona", "Hephaestus G2 Industrial Frame", "Vaurca Worker", "Vaurca Warrior") + +/datum/outfit/job/hos + name = "Head of Security" + jobtype = /datum/job/hos + + uniform = /obj/item/clothing/under/rank/head_of_security + shoes = /obj/item/clothing/shoes/jackboots + l_ear = /obj/item/device/radio/headset/heads/hos + pda = /obj/item/device/pda/heads/hos + id = /obj/item/card/id/navy + glasses = /obj/item/clothing/glasses/sunglasses/sechud/head + + backpack_contents = list( + /obj/item/storage/box/ids = 1 + ) + + implants = list( + /obj/item/implant/loyalty + ) + + backpack = /obj/item/storage/backpack/security + satchel = /obj/item/storage/backpack/satchel_sec + dufflebag = /obj/item/storage/backpack/duffel/sec + messengerbag = /obj/item/storage/backpack/messenger/sec + +/datum/outfit/job/hos/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(istajara(H)) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves) + else if(isunathi(H)) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves) + else + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves) + + +/datum/job/warden + title = "Warden" + flag = WARDEN + department = "Security" + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the head of security" + selection_color = "#FFA4A4" + economic_modifier = 5 + + minimum_character_age = 25 + + access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_maint_tunnels, access_morgue, access_external_airlocks, access_weapons) + minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_external_airlocks, access_weapons) + minimal_player_age = 7 + outfit = /datum/outfit/job/warden + +/datum/outfit/job/warden + name = "Warden" + jobtype = /datum/job/warden + + uniform = /obj/item/clothing/under/rank/warden + shoes = /obj/item/clothing/shoes/jackboots + l_ear = /obj/item/device/radio/headset/headset_sec + pda = /obj/item/device/pda/warden + glasses = /obj/item/clothing/glasses/sunglasses/sechud/head + l_pocket = /obj/item/device/flash + + backpack = /obj/item/storage/backpack/security + satchel = /obj/item/storage/backpack/satchel_sec + dufflebag = /obj/item/storage/backpack/duffel/sec + messengerbag = /obj/item/storage/backpack/messenger/sec + + backpack_contents = list( + /obj/item/storage/box/ids = 1 + ) + +/datum/outfit/job/warden/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(istajara(H)) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves) + else if(isunathi(H)) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves) + else + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves) + + +/datum/job/detective + title = "Detective" + flag = DETECTIVE + department = "Security" + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the head of security" + selection_color = "#FFA4A4" + economic_modifier = 5 + + minimum_character_age = 25 + + access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_detective, access_weapons) + minimal_access = list(access_security, access_sec_doors, access_morgue, access_detective, access_weapons) + minimal_player_age = 7 + outfit = /datum/outfit/job/detective + +/datum/outfit/job/detective + name = "Detective" + jobtype = /datum/job/detective + + uniform = /obj/item/clothing/under/det + shoes = /obj/item/clothing/shoes/laceup + l_ear = /obj/item/device/radio/headset/headset_sec + pda = /obj/item/device/pda/detective + + backpack_contents = list( + /obj/item/storage/box/evidence = 1 + ) + +/datum/outfit/job/detective/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(istajara(H)) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves) + else if(isunathi(H)) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves) + else + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves) + + +/datum/job/forensics + title = "Forensic Technician" + flag = FORENSICS + department_flag = ENGSEC + faction = "Station" + total_positions = 1 + spawn_positions = 1 + supervisors = "the head of security" + selection_color = "#FFA4A4" + economic_modifier = 5 + + minimum_character_age = 25 + + access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_weapons) + minimal_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_weapons) + alt_titles = list("Crime Scene Investigator") + minimal_player_age = 3 + outfit = /datum/outfit/job/forensics + alt_outfits = list("Crime Scene Investigator"=/datum/outfit/job/forensics/csi) + +/datum/outfit/job/forensics + name = "Forensic Technician" + jobtype = /datum/job/forensics + + uniform = /obj/item/clothing/under/det/forensics + shoes = /obj/item/clothing/shoes/laceup + l_ear = /obj/item/device/radio/headset/headset_sec + pda = /obj/item/device/pda/detective + + backpack_contents = list( + /obj/item/storage/box/evidence = 1 + ) + +/datum/outfit/job/forensics/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(istajara(H)) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves) + else if(isunathi(H)) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves) + else + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves) + + +/datum/outfit/job/forensics/csi + name = "Crime Scene Investigator" + jobtype = /datum/job/forensics + + suit = /obj/item/clothing/suit/storage/toggle/labcoat + +/datum/job/officer + title = "Security Officer" + flag = OFFICER + department = "Security" + department_flag = ENGSEC + faction = "Station" + total_positions = 4 + spawn_positions = 4 + supervisors = "the head of security" + selection_color = "#FFA4A4" + economic_modifier = 4 + + minimum_character_age = 18 + + access = list(access_security, access_eva, access_sec_doors, access_brig, access_maint_tunnels, access_morgue, access_external_airlocks, access_weapons) + minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_external_airlocks, access_weapons) + minimal_player_age = 7 + outfit = /datum/outfit/job/officer + +/datum/outfit/job/officer + name = "Security Officer" + jobtype = /datum/job/officer + + uniform = /obj/item/clothing/under/rank/security + shoes = /obj/item/clothing/shoes/jackboots + l_ear = /obj/item/device/radio/headset/headset_sec + pda = /obj/item/device/pda/security + l_pocket = /obj/item/device/flash + + backpack = /obj/item/storage/backpack/security + satchel = /obj/item/storage/backpack/satchel_sec + dufflebag = /obj/item/storage/backpack/duffel/sec + messengerbag = /obj/item/storage/backpack/messenger/sec + + backpack_contents = list( + /obj/item/handcuffs = 1 + ) + +/datum/outfit/job/officer/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(istajara(H)) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves) + else if(isunathi(H)) + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves) + else + H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves) + +/datum/job/intern_sec + title = "Security Cadet" + flag = INTERN_SEC + department_flag = ENGSEC + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the Head of Security" + selection_color = "#FFA4A4" + access = list(access_security, access_sec_doors, access_maint_tunnels) + minimal_access = list(access_security, access_sec_doors) + outfit = /datum/outfit/job/intern_sec + +/datum/outfit/job/intern_sec + name = "Security Cadet" + jobtype = /datum/job/intern_sec + + uniform = /obj/item/clothing/under/rank/cadet + suit = /obj/item/clothing/suit/storage/hazardvest/cadet + head = /obj/item/clothing/head/beret/sec/cadet + shoes = /obj/item/clothing/shoes/jackboots + l_ear = /obj/item/device/radio/headset/headset_sec + + backpack = /obj/item/storage/backpack/security + satchel = /obj/item/storage/backpack/satchel_sec + dufflebag = /obj/item/storage/backpack/duffel/sec + messengerbag = /obj/item/storage/backpack/messenger/sec diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index 68a7294facf..67e107c76c1 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -1,46 +1,46 @@ -/datum/job/ai - title = "AI" - flag = AI - 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 = "#00ff00" - supervisors = "your laws" - minimal_player_age = 7 - account_allowed = 0 - economic_modifier = 0 - 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 - -/datum/job/cyborg - title = "Cyborg" - flag = CYBORG - department_flag = ENGSEC - faction = "Station" - total_positions = 2 - spawn_positions = 2 - supervisors = "your laws and the AI" //Nodrak - selection_color = "#4dff4d" - minimal_player_age = 1 - alt_titles = list("Android", "Robot") - account_allowed = 0 - economic_modifier = 0 - - 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 +/datum/job/ai + title = "AI" + flag = AI + 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 = "#00ff00" + supervisors = "your laws" + minimal_player_age = 7 + account_allowed = 0 + economic_modifier = 0 + 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 + +/datum/job/cyborg + title = "Cyborg" + flag = CYBORG + department_flag = ENGSEC + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "your laws and the AI" //Nodrak + selection_color = "#4dff4d" + minimal_player_age = 1 + alt_titles = list("Android", "Robot") + account_allowed = 0 + economic_modifier = 0 + + 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/jobs.dm b/code/game/jobs/jobs.dm index 2e4b17dd7ee..3eb05d77355 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -1,154 +1,154 @@ - -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/INTERN_SEC =(1<<10) -var/const/INTERN_ENG =(1<<11) -var/const/FORENSICS =(1<<12) - - -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/VIROLOGIST =(1<<5) -var/const/PSYCHIATRIST =(1<<6) -var/const/ROBOTICIST =(1<<7) -var/const/XENOBIOLOGIST =(1<<8) -var/const/PARAMEDIC =(1<<9) -var/const/INTERN_MED =(1<<10) -var/const/INTERN_SCI =(1<<11) - -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/CLOWN =(1<<11) -var/const/MIME =(1<<12) -var/const/MERCHANT =(1<<13) -var/const/JOURNALIST =(1<<14) -var/const/ASSISTANT =(1<<15) - - -var/list/assistant_occupations = list() //Leaving this on one line stops Travis complaining ~Scopes - - -var/list/command_positions = list( - "Captain", - "Head of Personnel", - "Head of Security", - "Chief Engineer", - "Research Director", - "Chief Medical Officer" -) - - -var/list/engineering_positions = list( - "Chief Engineer", - "Station Engineer", - "Atmospheric Technician", - "Engineering Apprentice" -) - - -var/list/medical_positions = list( - "Chief Medical Officer", - "Medical Doctor", - "Psychiatrist", - "Pharmacist", - "Paramedic", - "Medical Resident" -) - - -var/list/science_positions = list( - "Research Director", - "Scientist", - "Roboticist", - "Xenobiologist", - "Lab Assistant" -) - -//BS12 EDIT -var/list/cargo_positions = list( - "Quartermaster", - "Cargo Technician", - "Shaft Miner" -) - -var/list/civilian_positions = list( - "Head of Personnel", - "Corporate Liaison", - "Bartender", - "Gardener", - "Chef", - "Janitor", - "Librarian", - "Corporate Reporter", - "Chaplain", - "Assistant" -) - - -var/list/security_positions = list( - "Head of Security", - "Warden", - "Detective", - "Forensic Technician", - "Security Officer", - "Security Cadet" -) - -var/list/nonhuman_positions = list( - "AI", - "Cyborg", - "pAI", - "Merchant" -) - -/proc/guest_jobbans(var/job) - return ((job in command_positions) || job == "Corporate Liaison") - -/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 - -//Mahzel : Job preview not added because code don't exist in BS12 + +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/INTERN_SEC =(1<<10) +var/const/INTERN_ENG =(1<<11) +var/const/FORENSICS =(1<<12) + + +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/VIROLOGIST =(1<<5) +var/const/PSYCHIATRIST =(1<<6) +var/const/ROBOTICIST =(1<<7) +var/const/XENOBIOLOGIST =(1<<8) +var/const/PARAMEDIC =(1<<9) +var/const/INTERN_MED =(1<<10) +var/const/INTERN_SCI =(1<<11) + +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/CLOWN =(1<<11) +var/const/MIME =(1<<12) +var/const/MERCHANT =(1<<13) +var/const/JOURNALIST =(1<<14) +var/const/ASSISTANT =(1<<15) + + +var/list/assistant_occupations = list() //Leaving this on one line stops Travis complaining ~Scopes + + +var/list/command_positions = list( + "Captain", + "Head of Personnel", + "Head of Security", + "Chief Engineer", + "Research Director", + "Chief Medical Officer" +) + + +var/list/engineering_positions = list( + "Chief Engineer", + "Station Engineer", + "Atmospheric Technician", + "Engineering Apprentice" +) + + +var/list/medical_positions = list( + "Chief Medical Officer", + "Medical Doctor", + "Psychiatrist", + "Pharmacist", + "Paramedic", + "Medical Resident" +) + + +var/list/science_positions = list( + "Research Director", + "Scientist", + "Roboticist", + "Xenobiologist", + "Lab Assistant" +) + +//BS12 EDIT +var/list/cargo_positions = list( + "Quartermaster", + "Cargo Technician", + "Shaft Miner" +) + +var/list/civilian_positions = list( + "Head of Personnel", + "Corporate Liaison", + "Bartender", + "Gardener", + "Chef", + "Janitor", + "Librarian", + "Corporate Reporter", + "Chaplain", + "Assistant" +) + + +var/list/security_positions = list( + "Head of Security", + "Warden", + "Detective", + "Forensic Technician", + "Security Officer", + "Security Cadet" +) + +var/list/nonhuman_positions = list( + "AI", + "Cyborg", + "pAI", + "Merchant" +) + +/proc/guest_jobbans(var/job) + return ((job in command_positions) || job == "Corporate Liaison") + +/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 + +//Mahzel : Job preview not added because code don't exist in BS12 diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm index 15fa42c80fa..272e94f203e 100644 --- a/code/game/jobs/whitelist.dm +++ b/code/game/jobs/whitelist.dm @@ -1,140 +1,140 @@ -#define WHITELISTFILE "data/whitelist.txt" - -var/list/whitelist = list() - -/hook/startup/proc/loadWhitelist() - if (config.usewhitelist) - load_whitelist() - return 1 - -/proc/load_whitelist() - if (config.sql_whitelists) - establish_db_connection(dbcon) - - if (!dbcon.IsConnected()) - //Continue with the old code if we have no database. - error("Database connection failed while loading whitelists. Reverting to legacy system.") - config.sql_whitelists = 0 - else - return - - whitelist = file2list(WHITELISTFILE) - if (!whitelist.len) - whitelist = null - -/proc/check_whitelist(mob/M) - if (config.sql_whitelists) - var/head_of_staff_whitelist = 1 - if (M.client && M.client.whitelist_status) - return (M.client.whitelist_status & head_of_staff_whitelist) - - return 0 - else - 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() - if (config.sql_whitelists) - establish_db_connection(dbcon) - - if (!dbcon.IsConnected()) - //Continue with the old code if we have no database. - error("Database connection failed while loading alien whitelists. Reverting to legacy system.") - config.sql_whitelists = 0 - else - var/DBQuery/query = dbcon.NewQuery("SELECT status_name, flag FROM ss13_whitelist_statuses") - query.Execute() - - while (query.NextRow()) - if (query.item[1] in whitelisted_species) - whitelisted_species[query.item[1]] = text2num(query.item[2]) - - return - - var/text = file2text("config/alienwhitelist.txt") - if (!text) - log_misc("Failed to load config/alienwhitelist.txt") - return 0 - else - alien_whitelist = text2list(text, "\n") - return 1 - -/proc/is_alien_whitelisted(mob/M, var/species) - if (!config.usealienwhitelist) - return 1 - - if (!M || !species) - return 0 - - if(istype(species, /datum/species)) - var/datum/species/S = species - if(!(S.spawn_flags & IS_WHITELISTED)) - return 1 - species = S.name - - else - var/datum/species/S = global.all_species[species] - if(S && !(S.spawn_flags & IS_WHITELISTED)) - return 1 - - if (!alien_whitelist && !config.sql_whitelists) - return 0 - - if (config.sql_whitelists) - if (M.client && M.client.whitelist_status) - return (M.client.whitelist_status & whitelisted_species[species]) - else - if (M && species) - for (var/s in alien_whitelist) - if (findtext(s,"[M.ckey] - [species]")) - return 1 - if (findtext(s,"[M.ckey] - All")) - return 1 - return 0 - -/** - * A centralized proc for checking whether or not a player is fit for playing - * any antag role or job role dependant on their ckey's age, job's age restriction, - * and config settings. - * - * @param C The client object whose age we want to check. Can also be a mob. - * @param job The job name/antag role name we want to check against. - * - * @return Days left until the player can play the role if they're too young. - * 0 if they're old enough. - */ -/proc/player_old_enough_for_role(client/C, job) - if (!job || !C) - return 0 - - if (ismob(C)) - var/mob/M = C - C = M.client - - if (!istype(C) || C.holder) - return 0 - - var/age_to_beat = 0 - - // Assume it's an antag role. - if (bantype_to_antag_age[lowertext(job)] && config.use_age_restriction_for_antags) - age_to_beat = bantype_to_antag_age[lowertext(job)] - - // Assume it's a job instead! - if (!age_to_beat) - var/datum/job/J = SSjobs.GetJob(job) - if (J && config.use_age_restriction_for_jobs) - age_to_beat = J.minimal_player_age - - var/diff = age_to_beat - C.player_age - return (diff > 0) ? diff : 0 - -#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() + if (config.sql_whitelists) + establish_db_connection(dbcon) + + if (!dbcon.IsConnected()) + //Continue with the old code if we have no database. + error("Database connection failed while loading whitelists. Reverting to legacy system.") + config.sql_whitelists = 0 + else + return + + whitelist = file2list(WHITELISTFILE) + if (!whitelist.len) + whitelist = null + +/proc/check_whitelist(mob/M) + if (config.sql_whitelists) + var/head_of_staff_whitelist = 1 + if (M.client && M.client.whitelist_status) + return (M.client.whitelist_status & head_of_staff_whitelist) + + return 0 + else + 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() + if (config.sql_whitelists) + establish_db_connection(dbcon) + + if (!dbcon.IsConnected()) + //Continue with the old code if we have no database. + error("Database connection failed while loading alien whitelists. Reverting to legacy system.") + config.sql_whitelists = 0 + else + var/DBQuery/query = dbcon.NewQuery("SELECT status_name, flag FROM ss13_whitelist_statuses") + query.Execute() + + while (query.NextRow()) + if (query.item[1] in whitelisted_species) + whitelisted_species[query.item[1]] = text2num(query.item[2]) + + return + + var/text = file2text("config/alienwhitelist.txt") + if (!text) + log_misc("Failed to load config/alienwhitelist.txt") + return 0 + else + alien_whitelist = text2list(text, "\n") + return 1 + +/proc/is_alien_whitelisted(mob/M, var/species) + if (!config.usealienwhitelist) + return 1 + + if (!M || !species) + return 0 + + if(istype(species, /datum/species)) + var/datum/species/S = species + if(!(S.spawn_flags & IS_WHITELISTED)) + return 1 + species = S.name + + else + var/datum/species/S = global.all_species[species] + if(S && !(S.spawn_flags & IS_WHITELISTED)) + return 1 + + if (!alien_whitelist && !config.sql_whitelists) + return 0 + + if (config.sql_whitelists) + if (M.client && M.client.whitelist_status) + return (M.client.whitelist_status & whitelisted_species[species]) + else + if (M && species) + for (var/s in alien_whitelist) + if (findtext(s,"[M.ckey] - [species]")) + return 1 + if (findtext(s,"[M.ckey] - All")) + return 1 + return 0 + +/** + * A centralized proc for checking whether or not a player is fit for playing + * any antag role or job role dependant on their ckey's age, job's age restriction, + * and config settings. + * + * @param C The client object whose age we want to check. Can also be a mob. + * @param job The job name/antag role name we want to check against. + * + * @return Days left until the player can play the role if they're too young. + * 0 if they're old enough. + */ +/proc/player_old_enough_for_role(client/C, job) + if (!job || !C) + return 0 + + if (ismob(C)) + var/mob/M = C + C = M.client + + if (!istype(C) || C.holder) + return 0 + + var/age_to_beat = 0 + + // Assume it's an antag role. + if (bantype_to_antag_age[lowertext(job)] && config.use_age_restriction_for_antags) + age_to_beat = bantype_to_antag_age[lowertext(job)] + + // Assume it's a job instead! + if (!age_to_beat) + var/datum/job/J = SSjobs.GetJob(job) + if (J && config.use_age_restriction_for_jobs) + age_to_beat = J.minimal_player_age + + var/diff = age_to_beat - C.player_age + return (diff > 0) ? diff : 0 + +#undef WHITELISTFILE diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm index 50f84564316..50082df58f3 100644 --- a/code/game/machinery/Beacon.dm +++ b/code/game/machinery/Beacon.dm @@ -1,55 +1,55 @@ -/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 = 2.5 - anchored = 1 - use_power = 1 - idle_power_usage = 0 - var/obj/item/device/radio/beacon/Beacon - - New() - ..() - var/turf/T = loc - Beacon = new /obj/item/device/radio/beacon - Beacon.invisibility = INVISIBILITY_MAXIMUM - Beacon.forceMove(T) - - hide(!T.is_plating()) - - Destroy() - if(Beacon) - qdel(Beacon) - return ..() - - // update the invisibility and icon - hide(var/intact) - invisibility = intact ? 101 : 0 - updateicon() - - // update the icon_state - proc/updateicon() - var/state="floor_beacon" - - if(invisibility) - icon_state = "[state]f" - - else - icon_state = "[state]" - - process() - if(!Beacon) - var/turf/T = loc - Beacon = new /obj/item/device/radio/beacon - Beacon.invisibility = INVISIBILITY_MAXIMUM - Beacon.forceMove(T) - if(Beacon) - if(Beacon.loc != loc) - Beacon.forceMove(loc) - - updateicon() - - +/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 = 2.5 + anchored = 1 + use_power = 1 + idle_power_usage = 0 + var/obj/item/device/radio/beacon/Beacon + + New() + ..() + var/turf/T = loc + Beacon = new /obj/item/device/radio/beacon + Beacon.invisibility = INVISIBILITY_MAXIMUM + Beacon.forceMove(T) + + hide(!T.is_plating()) + + Destroy() + if(Beacon) + qdel(Beacon) + return ..() + + // update the invisibility and icon + hide(var/intact) + invisibility = intact ? 101 : 0 + updateicon() + + // update the icon_state + proc/updateicon() + var/state="floor_beacon" + + if(invisibility) + icon_state = "[state]f" + + else + icon_state = "[state]" + + process() + if(!Beacon) + var/turf/T = loc + Beacon = new /obj/item/device/radio/beacon + Beacon.invisibility = INVISIBILITY_MAXIMUM + Beacon.forceMove(T) + if(Beacon) + if(Beacon.loc != loc) + Beacon.forceMove(loc) + + updateicon() + + diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 77c48d0c446..7dd7f38a85e 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -1,203 +1,203 @@ -/obj/machinery/optable - name = "operating table" - desc = "Used for advanced medical procedures." - icon = 'icons/obj/surgery.dmi' - icon_state = "table2-idle" - var/modify_state = "table2" - density = 1 - anchored = 1 - use_power = 1 - idle_power_usage = 1 - active_power_usage = 5 - component_types = list( - /obj/item/circuitboard/optable, - /obj/item/stock_parts/scanning_module = 1 - ) - - var/mob/living/carbon/human/victim = null - var/strapped = 0.0 - var/suppressing = FALSE - - var/obj/machinery/computer/operating/computer = null - -/obj/machinery/optable/Initialize() - . = ..() - for(dir in list(NORTH,EAST,SOUTH,WEST)) - computer = locate(/obj/machinery/computer/operating, get_step(src, dir)) - if (computer) - computer.table = src - break - -/obj/machinery/optable/examine(var/mob/user) - . = ..() - to_chat(user, "The neural suppressors are switched [suppressing ? "on" : "off"].") - -/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)) - src.density = 0 - else - return - -/obj/machinery/optable/attack_hand(mob/user as mob) - if (HULK in usr.mutations) - visible_message("\The [usr] destroys \the [src]!") - src.density = 0 - qdel(src) - return - - if(!victim) - to_chat(user, "There is nobody on \the [src]. It would be pointless to turn the suppressor on.") - return TRUE - - if(user != victim && !use_check_and_message(user)) // Skip checks if you're doing it to yourself or turning it off, this is an anti-griefing mechanic more than anything. - user.visible_message("\The [user] begins switching [suppressing ? "off" : "on"] \the [src]'s neural suppressor.") - if(!do_after(user, 30, src)) - return - if(!victim) - to_chat(user, "There is nobody on \the [src]. It would be pointless to turn the suppressor on.") - - suppressing = !suppressing - user.visible_message("\The [user] switches [suppressing ? "on" : "off"] \the [src]'s neural suppressor.") - -/obj/machinery/optable/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(air_group || (height==0)) return 1 - - if(istype(mover) && mover.checkpass(PASSTABLE)) - return 1 - else - return 0 - - -/obj/machinery/optable/MouseDrop_T(obj/O as obj, mob/user as mob) - - if (!istype(O, /obj/item)) - return - user.drop_from_inventory(O,get_turf(src)) - return - -/obj/machinery/optable/proc/check_victim() - if(!victim || !victim.lying || victim.loc != loc) - suppressing = FALSE - victim = null - var/mob/living/carbon/human/H = locate() in loc - if(istype(H)) - if(H.lying) - icon_state = H.pulse() ? "[modify_state]-active" : "[modify_state]-idle" - victim = H - if(victim) - if(suppressing && victim.sleeping < 3) - victim.Sleeping(3 - victim.sleeping) - return 1 - icon_state = "[modify_state]-idle" - return 0 - -/obj/machinery/optable/machinery_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.forceMove(src.loc) - src.add_fingerprint(user) - if(ishuman(C)) - var/mob/living/carbon/human/H = C - src.victim = H - icon_state = H.pulse() ? "[modify_state]-active" : "[modify_state]-idle" - else - icon_state = "[modify_state]-idle" - -/obj/machinery/optable/MouseDrop_T(mob/target, mob/user) - - var/mob/living/M = user - if(user.stat || user.restrained() || !iscarbon(target)) - return - if(istype(M)) - - var/mob/living/L = target - var/bucklestatus = L.bucklecheck(user) - - if (!bucklestatus)//We must make sure the person is unbuckled before they go in - return - - - if(L == user) - user.visible_message("[user] starts climbing onto [src].", "You start climbing onto [src].", range = 3) - else - user.visible_message("[user] starts putting [L] onto [src].", "You start putting [L] onto [src].", range = 3) - if (do_mob(user, L, 10, needhand = 0)) - if (bucklestatus == 2) - var/obj/structure/LB = L.buckled - LB.user_unbuckle_mob(user) - take_victim(target,user) - else - return ..() - -/obj/machinery/optable/verb/climb_on() - set name = "Climb On Table" - set category = "Object" - set src in oview(1) - - if(usr.stat || !ishuman(usr) || usr.restrained() ) - return - - take_victim(usr,usr) - -/obj/machinery/optable/attackby(obj/item/W as obj, mob/living/carbon/user as mob) - if (istype(W, /obj/item/grab)) - var/obj/item/grab/G = W - if (src.victim) - to_chat(usr, "The table is already occupied!") - return 0 - - var/mob/living/L = G.affecting - var/bucklestatus = L.bucklecheck(user) - - if (!bucklestatus)//We must make sure the person is unbuckled before they go in - return - - - if(L == user) - user.visible_message("[user] starts climbing onto [src].", "You start climbing onto [src].", range = 3) - else - user.visible_message("[user] starts putting [L] onto [src].", "You start putting [L] onto [src].", range = 3) - if (do_mob(user, L, 10, needhand = 0)) - if (bucklestatus == 2) - var/obj/structure/LB = L.buckled - LB.user_unbuckle_mob(user) - take_victim(G.affecting,usr) - qdel(W) - return - if(default_deconstruction_screwdriver(user, W)) - return - if(default_deconstruction_crowbar(user, W)) - return - if(default_part_replacement(user, W)) - return - -/obj/machinery/optable/proc/check_table(mob/living/carbon/patient as mob) - check_victim() - if(src.victim && get_turf(victim) == get_turf(src) && victim.lying) - to_chat(usr, "\The [src] is already occupied!") - return 0 - if(patient.buckled) - to_chat(usr, "Unbuckle \the [patient] first!") - return 0 - return 1 +/obj/machinery/optable + name = "operating table" + desc = "Used for advanced medical procedures." + icon = 'icons/obj/surgery.dmi' + icon_state = "table2-idle" + var/modify_state = "table2" + density = 1 + anchored = 1 + use_power = 1 + idle_power_usage = 1 + active_power_usage = 5 + component_types = list( + /obj/item/circuitboard/optable, + /obj/item/stock_parts/scanning_module = 1 + ) + + var/mob/living/carbon/human/victim = null + var/strapped = 0.0 + var/suppressing = FALSE + + var/obj/machinery/computer/operating/computer = null + +/obj/machinery/optable/Initialize() + . = ..() + for(dir in list(NORTH,EAST,SOUTH,WEST)) + computer = locate(/obj/machinery/computer/operating, get_step(src, dir)) + if (computer) + computer.table = src + break + +/obj/machinery/optable/examine(var/mob/user) + . = ..() + to_chat(user, "The neural suppressors are switched [suppressing ? "on" : "off"].") + +/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)) + src.density = 0 + else + return + +/obj/machinery/optable/attack_hand(mob/user as mob) + if (HULK in usr.mutations) + visible_message("\The [usr] destroys \the [src]!") + src.density = 0 + qdel(src) + return + + if(!victim) + to_chat(user, "There is nobody on \the [src]. It would be pointless to turn the suppressor on.") + return TRUE + + if(user != victim && !use_check_and_message(user)) // Skip checks if you're doing it to yourself or turning it off, this is an anti-griefing mechanic more than anything. + user.visible_message("\The [user] begins switching [suppressing ? "off" : "on"] \the [src]'s neural suppressor.") + if(!do_after(user, 30, src)) + return + if(!victim) + to_chat(user, "There is nobody on \the [src]. It would be pointless to turn the suppressor on.") + + suppressing = !suppressing + user.visible_message("\The [user] switches [suppressing ? "on" : "off"] \the [src]'s neural suppressor.") + +/obj/machinery/optable/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(air_group || (height==0)) return 1 + + if(istype(mover) && mover.checkpass(PASSTABLE)) + return 1 + else + return 0 + + +/obj/machinery/optable/MouseDrop_T(obj/O as obj, mob/user as mob) + + if (!istype(O, /obj/item)) + return + user.drop_from_inventory(O,get_turf(src)) + return + +/obj/machinery/optable/proc/check_victim() + if(!victim || !victim.lying || victim.loc != loc) + suppressing = FALSE + victim = null + var/mob/living/carbon/human/H = locate() in loc + if(istype(H)) + if(H.lying) + icon_state = H.pulse() ? "[modify_state]-active" : "[modify_state]-idle" + victim = H + if(victim) + if(suppressing && victim.sleeping < 3) + victim.Sleeping(3 - victim.sleeping) + return 1 + icon_state = "[modify_state]-idle" + return 0 + +/obj/machinery/optable/machinery_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.forceMove(src.loc) + src.add_fingerprint(user) + if(ishuman(C)) + var/mob/living/carbon/human/H = C + src.victim = H + icon_state = H.pulse() ? "[modify_state]-active" : "[modify_state]-idle" + else + icon_state = "[modify_state]-idle" + +/obj/machinery/optable/MouseDrop_T(mob/target, mob/user) + + var/mob/living/M = user + if(user.stat || user.restrained() || !iscarbon(target)) + return + if(istype(M)) + + var/mob/living/L = target + var/bucklestatus = L.bucklecheck(user) + + if (!bucklestatus)//We must make sure the person is unbuckled before they go in + return + + + if(L == user) + user.visible_message("[user] starts climbing onto [src].", "You start climbing onto [src].", range = 3) + else + user.visible_message("[user] starts putting [L] onto [src].", "You start putting [L] onto [src].", range = 3) + if (do_mob(user, L, 10, needhand = 0)) + if (bucklestatus == 2) + var/obj/structure/LB = L.buckled + LB.user_unbuckle_mob(user) + take_victim(target,user) + else + return ..() + +/obj/machinery/optable/verb/climb_on() + set name = "Climb On Table" + set category = "Object" + set src in oview(1) + + if(usr.stat || !ishuman(usr) || usr.restrained() ) + return + + take_victim(usr,usr) + +/obj/machinery/optable/attackby(obj/item/W as obj, mob/living/carbon/user as mob) + if (istype(W, /obj/item/grab)) + var/obj/item/grab/G = W + if (src.victim) + to_chat(usr, "The table is already occupied!") + return 0 + + var/mob/living/L = G.affecting + var/bucklestatus = L.bucklecheck(user) + + if (!bucklestatus)//We must make sure the person is unbuckled before they go in + return + + + if(L == user) + user.visible_message("[user] starts climbing onto [src].", "You start climbing onto [src].", range = 3) + else + user.visible_message("[user] starts putting [L] onto [src].", "You start putting [L] onto [src].", range = 3) + if (do_mob(user, L, 10, needhand = 0)) + if (bucklestatus == 2) + var/obj/structure/LB = L.buckled + LB.user_unbuckle_mob(user) + take_victim(G.affecting,usr) + qdel(W) + return + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return + if(default_part_replacement(user, W)) + return + +/obj/machinery/optable/proc/check_table(mob/living/carbon/patient as mob) + check_victim() + if(src.victim && get_turf(victim) == get_turf(src) && victim.lying) + to_chat(usr, "\The [src] is already occupied!") + return 0 + if(patient.buckled) + to_chat(usr, "Unbuckle \the [patient] first!") + return 0 + return 1 diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index a4f50065022..728f1d899dc 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -1,305 +1,305 @@ -/obj/machinery/sleeper - name = "sleeper" - desc = "A fancy bed with built-in injectors, a dialysis machine, and a limited health scanner." - icon = 'icons/obj/sleeper.dmi' - icon_state = "sleeper" - density = 1 - anchored = 1 - var/mob/living/carbon/human/occupant = null - var/list/available_chemicals = list("norepinephrine" = "Norepinephrine", "stoxin" = "Soporific", "paracetamol" = "Paracetamol", "dylovene" = "Dylovene", "dexalin" = "Dexalin") - var/obj/item/reagent_containers/glass/beaker = null - var/filtering = 0 - var/allow_occupant_types = list(/mob/living/carbon/human) - var/disallow_occupant_types = list() - var/pump - - use_power = 1 - idle_power_usage = 15 - active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors. - component_types = list( - /obj/item/circuitboard/sleeper, - /obj/item/stock_parts/capacitor = 2, - /obj/item/stock_parts/scanning_module = 2, - /obj/item/stock_parts/console_screen, - /obj/item/reagent_containers/glass/beaker/large - ) -/obj/machinery/sleeper/Initialize() - . = ..() - update_icon() - -/obj/machinery/sleeper/machinery_process() - if(stat & (NOPOWER|BROKEN)) - return - - 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(pump > 0) - if(beaker && istype(occupant)) - if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) - var/datum/reagents/ingested = occupant.get_ingested_reagents() - if(ingested) - for(var/datum/reagent/x in ingested.reagent_list) - ingested.trans_to_obj(beaker, 1) - else - toggle_pump() - -/obj/machinery/sleeper/update_icon() - flick("[initial(icon_state)]-anim", src) - if(occupant) - icon_state = "[initial(icon_state)]-closed" - return - else - icon_state = initial(icon_state) - -/obj/machinery/sleeper/RefreshParts() - ..() - var/scan_rating = 0 - var/cap_rating = 0 - - for(var/obj/item/stock_parts/P in component_parts) - if(isscanner(P)) - scan_rating += P.rating - else if(iscapacitor(P)) - cap_rating += P.rating - - beaker = locate(/obj/item/reagent_containers/glass/beaker) in component_parts - - active_power_usage = 200 - (cap_rating + scan_rating)*2 - - -/obj/machinery/sleeper/attack_hand(var/mob/user) - if(..()) - return 1 - - ui_interact(user) - -/obj/machinery/sleeper/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = outside_state) - var/data[0] - - data["power"] = stat & (NOPOWER|BROKEN) ? 0 : 1 - - var/list/reagents = list() - for(var/T in available_chemicals) - var/list/reagent = list() - reagent["id"] = T - reagent["name"] = available_chemicals[T] - if(occupant) - reagent["amount"] = occupant.reagents.get_reagent_amount(T) - reagents += list(reagent) - data["reagents"] = reagents.Copy() - - if(occupant) - data["occupant"] = 1 - switch(occupant.stat) - if(CONSCIOUS) - data["stat"] = "Conscious" - if(UNCONSCIOUS) - data["stat"] = "Unconscious" - if(DEAD) - data["stat"] = "Dead" - data["health"] = occupant.health - if(ishuman(occupant)) - var/mob/living/carbon/human/H = occupant - data["pulse"] = H.get_pulse(GETPULSE_TOOL) - else - data["occupant"] = 0 - if(beaker) - data["beaker"] = beaker.reagents.get_free_space() - else - data["beaker"] = -1 - data["filtering"] = filtering - data["pump"] = pump - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper UI", 600, 600, state = state) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -// #TODO-MERGE: Reimport Nanako's bucklesleeperBS -/obj/machinery/sleeper/Topic(href, href_list) - if(..()) - return 1 - - if(usr == occupant) - to_chat(usr, "You can't reach the controls from the inside.") - return - - add_fingerprint(usr) - - if(href_list["eject"]) - go_out() - if(href_list["beaker"]) - remove_beaker() - if(href_list["filter"]) - if(filtering != text2num(href_list["filter"])) - toggle_filter() - if(href_list["pump"]) - if(filtering != text2num(href_list["pump"])) - toggle_pump() - if(href_list["chemical"] && href_list["amount"]) - if(occupant && occupant.stat != DEAD) - if(href_list["chemical"] in available_chemicals) // Your hacks are bad and you should feel bad - inject_chemical(usr, href_list["chemical"], text2num(href_list["amount"])) - - return 1 - -/obj/machinery/sleeper/attack_ai(var/mob/user) - return attack_hand(user) - -/obj/machinery/sleeper/attackby(var/obj/item/I, var/mob/user) - add_fingerprint(user) - if(istype(I, /obj/item/reagent_containers/glass)) - if(!beaker) - beaker = I - user.drop_from_inventory(I,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 - else if(istype(I, /obj/item/grab)) - - var/obj/item/grab/G = I - var/mob/living/L = G.affecting - - if(!istype(L)) - to_chat(user, "\The machine won't accept that.") - return - - user.visible_message("[user] starts putting [G.affecting] into [src].", "You start putting [G.affecting] into [src].", range = 3) - - if (do_mob(user, G.affecting, 20, needhand = 0)) - if(occupant) - to_chat(user, "\The [src] is already occupied.") - return - var/bucklestatus = L.bucklecheck(user) - - if (!bucklestatus)//incase the patient got buckled during the delay - return - if(L != G.affecting)//incase it isn't the same mob we started with - return - - var/mob/M = G.affecting - M.forceMove(src) - update_use_power(2) - occupant = M - update_icon() - qdel(G) - return - else if(I.isscrewdriver()) - src.panel_open = !src.panel_open - to_chat(user, "You [src.panel_open ? "open" : "close"] the maintenance panel.") - cut_overlays() - if(src.panel_open) - add_overlay("[initial(icon_state)]-o") - - - else if(default_part_replacement(user, I)) - return -/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) - ..() - go_out() - -/obj/machinery/sleeper/emp_act(var/severity) - if(filtering) - toggle_filter() - - 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) - pump = 0 - return - to_chat(occupant, "You feel a tube jammed down your throat.") - pump = !pump - -/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(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.forceMove(src) - update_use_power(2) - occupant = M - update_icon() - -/obj/machinery/sleeper/proc/go_out() - if(!occupant) - return - if(occupant.client) - occupant.client.eye = occupant.client.mob - occupant.client.perspective = MOB_PERSPECTIVE - occupant.forceMove(loc) - occupant = null - for(var/atom/movable/A in (contents - component_parts)) // In case an object was dropped inside or something - if(A == beaker) - continue - A.forceMove(loc) - update_use_power(1) - update_icon() - toggle_filter() - -/obj/machinery/sleeper/proc/remove_beaker() - if(beaker) - beaker.forceMove(loc) - beaker = null - toggle_filter() - toggle_pump() - -/obj/machinery/sleeper/proc/inject_chemical(var/mob/living/user, var/chemical, var/amount) - if(stat & (BROKEN|NOPOWER)) - return - - if(occupant && occupant.reagents) - if(occupant.reagents.get_reagent_amount(chemical) + amount <= 20) - 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.") - else - to_chat(user, "There's no suitable occupant in \the [src].") +/obj/machinery/sleeper + name = "sleeper" + desc = "A fancy bed with built-in injectors, a dialysis machine, and a limited health scanner." + icon = 'icons/obj/sleeper.dmi' + icon_state = "sleeper" + density = 1 + anchored = 1 + var/mob/living/carbon/human/occupant = null + var/list/available_chemicals = list("norepinephrine" = "Norepinephrine", "stoxin" = "Soporific", "paracetamol" = "Paracetamol", "dylovene" = "Dylovene", "dexalin" = "Dexalin") + var/obj/item/reagent_containers/glass/beaker = null + var/filtering = 0 + var/allow_occupant_types = list(/mob/living/carbon/human) + var/disallow_occupant_types = list() + var/pump + + use_power = 1 + idle_power_usage = 15 + active_power_usage = 200 //builtin health analyzer, dialysis machine, injectors. + component_types = list( + /obj/item/circuitboard/sleeper, + /obj/item/stock_parts/capacitor = 2, + /obj/item/stock_parts/scanning_module = 2, + /obj/item/stock_parts/console_screen, + /obj/item/reagent_containers/glass/beaker/large + ) +/obj/machinery/sleeper/Initialize() + . = ..() + update_icon() + +/obj/machinery/sleeper/machinery_process() + if(stat & (NOPOWER|BROKEN)) + return + + 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(pump > 0) + if(beaker && istype(occupant)) + if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) + var/datum/reagents/ingested = occupant.get_ingested_reagents() + if(ingested) + for(var/datum/reagent/x in ingested.reagent_list) + ingested.trans_to_obj(beaker, 1) + else + toggle_pump() + +/obj/machinery/sleeper/update_icon() + flick("[initial(icon_state)]-anim", src) + if(occupant) + icon_state = "[initial(icon_state)]-closed" + return + else + icon_state = initial(icon_state) + +/obj/machinery/sleeper/RefreshParts() + ..() + var/scan_rating = 0 + var/cap_rating = 0 + + for(var/obj/item/stock_parts/P in component_parts) + if(isscanner(P)) + scan_rating += P.rating + else if(iscapacitor(P)) + cap_rating += P.rating + + beaker = locate(/obj/item/reagent_containers/glass/beaker) in component_parts + + active_power_usage = 200 - (cap_rating + scan_rating)*2 + + +/obj/machinery/sleeper/attack_hand(var/mob/user) + if(..()) + return 1 + + ui_interact(user) + +/obj/machinery/sleeper/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = outside_state) + var/data[0] + + data["power"] = stat & (NOPOWER|BROKEN) ? 0 : 1 + + var/list/reagents = list() + for(var/T in available_chemicals) + var/list/reagent = list() + reagent["id"] = T + reagent["name"] = available_chemicals[T] + if(occupant) + reagent["amount"] = occupant.reagents.get_reagent_amount(T) + reagents += list(reagent) + data["reagents"] = reagents.Copy() + + if(occupant) + data["occupant"] = 1 + switch(occupant.stat) + if(CONSCIOUS) + data["stat"] = "Conscious" + if(UNCONSCIOUS) + data["stat"] = "Unconscious" + if(DEAD) + data["stat"] = "Dead" + data["health"] = occupant.health + if(ishuman(occupant)) + var/mob/living/carbon/human/H = occupant + data["pulse"] = H.get_pulse(GETPULSE_TOOL) + else + data["occupant"] = 0 + if(beaker) + data["beaker"] = beaker.reagents.get_free_space() + else + data["beaker"] = -1 + data["filtering"] = filtering + data["pump"] = pump + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper UI", 600, 600, state = state) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +// #TODO-MERGE: Reimport Nanako's bucklesleeperBS +/obj/machinery/sleeper/Topic(href, href_list) + if(..()) + return 1 + + if(usr == occupant) + to_chat(usr, "You can't reach the controls from the inside.") + return + + add_fingerprint(usr) + + if(href_list["eject"]) + go_out() + if(href_list["beaker"]) + remove_beaker() + if(href_list["filter"]) + if(filtering != text2num(href_list["filter"])) + toggle_filter() + if(href_list["pump"]) + if(filtering != text2num(href_list["pump"])) + toggle_pump() + if(href_list["chemical"] && href_list["amount"]) + if(occupant && occupant.stat != DEAD) + if(href_list["chemical"] in available_chemicals) // Your hacks are bad and you should feel bad + inject_chemical(usr, href_list["chemical"], text2num(href_list["amount"])) + + return 1 + +/obj/machinery/sleeper/attack_ai(var/mob/user) + return attack_hand(user) + +/obj/machinery/sleeper/attackby(var/obj/item/I, var/mob/user) + add_fingerprint(user) + if(istype(I, /obj/item/reagent_containers/glass)) + if(!beaker) + beaker = I + user.drop_from_inventory(I,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 + else if(istype(I, /obj/item/grab)) + + var/obj/item/grab/G = I + var/mob/living/L = G.affecting + + if(!istype(L)) + to_chat(user, "\The machine won't accept that.") + return + + user.visible_message("[user] starts putting [G.affecting] into [src].", "You start putting [G.affecting] into [src].", range = 3) + + if (do_mob(user, G.affecting, 20, needhand = 0)) + if(occupant) + to_chat(user, "\The [src] is already occupied.") + return + var/bucklestatus = L.bucklecheck(user) + + if (!bucklestatus)//incase the patient got buckled during the delay + return + if(L != G.affecting)//incase it isn't the same mob we started with + return + + var/mob/M = G.affecting + M.forceMove(src) + update_use_power(2) + occupant = M + update_icon() + qdel(G) + return + else if(I.isscrewdriver()) + src.panel_open = !src.panel_open + to_chat(user, "You [src.panel_open ? "open" : "close"] the maintenance panel.") + cut_overlays() + if(src.panel_open) + add_overlay("[initial(icon_state)]-o") + + + else if(default_part_replacement(user, I)) + return +/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) + ..() + go_out() + +/obj/machinery/sleeper/emp_act(var/severity) + if(filtering) + toggle_filter() + + 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) + pump = 0 + return + to_chat(occupant, "You feel a tube jammed down your throat.") + pump = !pump + +/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(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.forceMove(src) + update_use_power(2) + occupant = M + update_icon() + +/obj/machinery/sleeper/proc/go_out() + if(!occupant) + return + if(occupant.client) + occupant.client.eye = occupant.client.mob + occupant.client.perspective = MOB_PERSPECTIVE + occupant.forceMove(loc) + occupant = null + for(var/atom/movable/A in (contents - component_parts)) // In case an object was dropped inside or something + if(A == beaker) + continue + A.forceMove(loc) + update_use_power(1) + update_icon() + toggle_filter() + +/obj/machinery/sleeper/proc/remove_beaker() + if(beaker) + beaker.forceMove(loc) + beaker = null + toggle_filter() + toggle_pump() + +/obj/machinery/sleeper/proc/inject_chemical(var/mob/living/user, var/chemical, var/amount) + if(stat & (BROKEN|NOPOWER)) + return + + if(occupant && occupant.reagents) + if(occupant.reagents.get_reagent_amount(chemical) + amount <= 20) + 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.") + else + to_chat(user, "There's no suitable occupant in \the [src].") diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 7e3dfabb92b..fe09126ba12 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -538,11 +538,10 @@ if (!occupant || !istype(occupant, /mob/living/carbon/human)) return var/mob/living/carbon/human/H = occupant - var/brain_result = H.get_brain_status() var/list/occupant_data = list( "stationtime" = worldtime2text(), - "brain_activity" = brain_result, + "brain_activity" = H.get_brain_status(), "virus_present" = H.virus2.len, "blood_volume" = H.get_blood_volume(), "blood_oxygenation" = H.get_blood_oxygenation(), @@ -577,7 +576,7 @@ /obj/machinery/body_scanconsole/proc/format_occupant_data(var/list/occ) var/dat = "Scan performed at [occ["stationtime"]]
    " dat += "Occupant Statistics:
    " - dat += text("Brain Activity: []
    ", occ["brain_result"]) + dat += text("Brain Activity: []
    ", occ["brain_activity"]) if (occ["virus_present"]) dat += "Viral pathogen detected in blood stream.
    " dat += text("Blood Pressure: []
    ", occ["blood_pressure"]) diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 7099f32626b..60060a84c07 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -1,841 +1,841 @@ -//////////////////////////////////////// -//CONTAINS: Air Alarms and Fire Alarms// -//////////////////////////////////////// - -#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 RCON_NO 1 -#define RCON_AUTO 2 -#define RCON_YES 3 - -#define MAX_TEMPERATURE 90 -#define MIN_TEMPERATURE -40 - -//all air alarms in area are connected via magic -/area - 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 = "A device that controls the local air regulation machinery and informs you when you're breathing vacuum." - icon = 'icons/obj/monitors.dmi' - icon_state = "alarm0" - anchored = 1 - use_power = 1 - idle_power_usage = 90 - active_power_usage = 1500 //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) - 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 - var/wiresexposed = 0 // If it's been screwdrivered open. - var/aidisabled = 0 - var/shorted = 0 - var/highpower = 0 // if true, power usage & temperature regulation power is increased - - var/datum/wires/alarm/wires - - var/mode = AALARM_MODE_SCRUBBING - var/screen = AALARM_SCREEN_MAIN - var/area_uid - var/area/alarm_area - var/buildstage = 2 //2 is built, 1 is building, 0 is frame. - - var/target_temperature = T0C+20 - var/regulating_temperature = 0 - - var/datum/radio_frequency/radio_connection - - var/list/TLV = list() - var/list/trace_gas = list("sleeping_agent") //list of other gases that this air alarm is able to detect - - var/danger_level = 0 - var/pressure_dangerlevel = 0 - var/oxygen_dangerlevel = 0 - var/co2_dangerlevel = 0 - var/phoron_dangerlevel = 0 - var/temperature_dangerlevel = 0 - var/other_dangerlevel = 0 - - var/report_danger_level = 1 - -/obj/machinery/alarm/nobreach - breach_detection = 0 - desc = "A device that controls the local air regulation machinery." - -/obj/machinery/alarm/monitor - report_danger_level = 0 - breach_detection = 0 - desc = "A device that controls the local air regulation machinery." - -/obj/machinery/alarm/server - req_one_access = list(access_rd, access_atmospherics, access_engine_equip) - target_temperature = 90 - desc = "A device that controls the local air regulation machinery. This one is designed for use in server rooms." - highpower = 1 - -/obj/machinery/alarm/freezer - req_one_access = list(access_kitchen, access_atmospherics, access_engine_equip) - highpower = 1 - target_temperature = T0C - 20 - -/obj/machinery/alarm/cold - target_temperature = T0C + 5 - -/obj/machinery/alarm/server/Initialize() - . = ..() - 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.2, 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 - -//For colder alarms, allowing pressure to drop a bit below normal is necessary. Pressure fluctuates downwards -//While temperature stabilises. - -//Kitchen freezer -/obj/machinery/alarm/freezer/Initialize() - . = ..() - TLV["oxygen"] = list(16, 17, 135, 140) // Partial pressure, kpa - TLV["pressure"] = list(ONE_ATMOSPHERE*0.50,ONE_ATMOSPHERE*0.70,ONE_ATMOSPHERE*1.10,ONE_ATMOSPHERE*1.20) - TLV["temperature"] = list(0, 0, 273, T0C+40) // No lower limits. Alarm above 0c. Major alarm at harmful heat - -//Refridgerated area, cold but above-freezing -/obj/machinery/alarm/cold/Initialize() - . = ..() - TLV["pressure"] = list(ONE_ATMOSPHERE*0.70,ONE_ATMOSPHERE*0.80,ONE_ATMOSPHERE*1.10,ONE_ATMOSPHERE*1.20) /* kpa */ - TLV["temperature"] = list(247, 273, 288, T0C+40) // Shouldn't go below 0 - -/obj/machinery/alarm/Destroy() - unregister_radio(src, frequency) - qdel(wires) - wires = null - return ..() - -/obj/machinery/alarm/LateInitialize() - apply_mode() - -/obj/machinery/alarm/Initialize(mapload, var/dir, var/building = 0) - . = ..() - - if(building) - if(dir) - src.set_dir(dir) - - buildstage = 0 - wiresexposed = 1 - pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24) - pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0 - update_icon() - return - - first_run() - - set_frequency(frequency) - - return INITIALIZE_HINT_LATELOAD - -/obj/machinery/alarm/proc/first_run() - alarm_area = get_area(src) - area_uid = alarm_area.uid - if (name == "alarm") - if (highpower) - name = "[alarm_area.name] High-Power Air Alarm" - else - name = "[alarm_area.name] Air Alarm" - - if(!wires) - wires = new(src) - - if (highpower) - active_power_usage *= 6 - idle_power_usage *= 3 - - // breathable air according to human/Life() - TLV["oxygen"] = list(16, 19, 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.2, 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 - -/obj/machinery/alarm/machinery_process() - if((stat & (NOPOWER|BROKEN)) || shorted || buildstage != 2) - return - - var/turf/simulated/location = 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() 2.0) - update_use_power(2) - regulating_temperature = 1 - visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click and a faint electronic hum.") - else - //check for when we should stop adjusting temperature - if (get_danger_level(target_temperature, TLV["temperature"]) || abs(environment.temperature - target_temperature) <= 0.5) - update_use_power(1) - regulating_temperature = 0 - visible_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.") - - if (regulating_temperature) - //Unnecessary checks removed, duplication of effort - - 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. - - pressure_dangerlevel = get_danger_level(environment_pressure, TLV["pressure"]) - oxygen_dangerlevel = get_danger_level(environment.gas["oxygen"]*partial_pressure, TLV["oxygen"]) - co2_dangerlevel = get_danger_level(environment.gas["carbon_dioxide"]*partial_pressure, TLV["carbon dioxide"]) - phoron_dangerlevel = get_danger_level(environment.gas["phoron"]*partial_pressure, TLV["phoron"]) - temperature_dangerlevel = get_danger_level(environment.temperature, TLV["temperature"]) - other_dangerlevel = get_danger_level(other_moles*partial_pressure, TLV["other"]) - - 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 = 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/get_danger_level(var/current_value, var/list/danger_levels) - if((current_value > danger_levels[4] && danger_levels[4] > 0) || current_value < danger_levels[1]) - return 2 - if((current_value > danger_levels[3] && danger_levels[3] > 0) || current_value < danger_levels[2]) - return 1 - return 0 - -/obj/machinery/alarm/update_icon() - if(wiresexposed) - icon_state = "alarmx" - set_light(0) - return - if((stat & (NOPOWER|BROKEN)) || shorted) - icon_state = "alarmp" - set_light(0) - 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 = "alarm0" - new_color = "#03A728" - if (1) - icon_state = "alarm2" //yes, alarm2 is yellow alarm - new_color = COLOR_SUN - if (2) - icon_state = "alarm1" - new_color = "#DA0205" - - set_light(l_range = L_WALLMOUNT_RANGE, l_power = L_WALLMOUNT_POWER, l_color = new_color) - -/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) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.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 = 1 //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) - - 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 = SSradio.return_frequency(alarm_frequency) - if(!frequency) - return - - var/datum/signal/alert_signal = new - alert_signal.source = src - alert_signal.transmission_method = 1 - 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) - ui_interact(user) - -/obj/machinery/alarm/attack_hand(mob/user) - . = ..() - if (.) - return - return interact(user) - -/obj/machinery/alarm/interact(mob/user) - ui_interact(user) - wires.Interact(user) - -/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - var/data[0] - var/remote_connection = 0 - var/remote_access = 0 - if(state) - var/list/href = state.href_list(user) - remote_connection = href["remote_connection"] // Remote connection means we're non-adjacent/connecting from another computer - remote_access = href["remote_access"] // Remote access means we also have the privilege to alter the air alarm. - - data["locked"] = locked && !issilicon(user) - data["remote_connection"] = remote_connection - data["remote_access"] = remote_access - data["rcon"] = rcon_setting - data["screen"] = screen - - populate_status(data) - - if(!(locked && !remote_connection) || remote_access || issilicon(user)) - populate_controls(data) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "air_alarm.tmpl", src.name, 325, 625, master_ui = master_ui, state = state) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/alarm/proc/populate_status(var/data) - var/turf/location = get_turf(src) - var/datum/gas_mixture/environment = location.return_air() - var/total = environment.total_moles - - var/list/environment_data = new - data["has_environment"] = total - if(total) - var/pressure = environment.return_pressure() - environment_data[++environment_data.len] = list("name" = "Pressure", "value" = pressure, "unit" = "kPa", "danger_level" = pressure_dangerlevel) - environment_data[++environment_data.len] = list("name" = "Oxygen", "value" = environment.gas["oxygen"] / total * 100, "unit" = "%", "danger_level" = oxygen_dangerlevel) - environment_data[++environment_data.len] = list("name" = "Carbon dioxide", "value" = environment.gas["carbon_dioxide"] / total * 100, "unit" = "%", "danger_level" = co2_dangerlevel) - environment_data[++environment_data.len] = list("name" = "Phoron", "value" = environment.gas["phoron"] / total * 100, "unit" = "%", "danger_level" = phoron_dangerlevel) - environment_data[++environment_data.len] = list("name" = "Temperature", "value" = environment.temperature, "unit" = "K ([round(environment.temperature - T0C, 0.1)]C)", "danger_level" = temperature_dangerlevel) - data["total_danger"] = danger_level - data["environment"] = environment_data - data["atmos_alarm"] = alarm_area.atmosalm - data["fire_alarm"] = alarm_area.fire != null - data["target_temperature"] = "[target_temperature - T0C]C" - -/obj/machinery/alarm/proc/populate_controls(var/list/data) - switch(screen) - if(AALARM_SCREEN_MAIN) - data["mode"] = mode - if(AALARM_SCREEN_VENT) - var/vents[0] - for(var/id_tag in alarm_area.air_vent_names) - var/long_name = alarm_area.air_vent_names[id_tag] - var/list/info = alarm_area.air_vent_info[id_tag] - if(!info) - continue - vents[++vents.len] = list( - "id_tag" = id_tag, - "long_name" = sanitize(long_name), - "power" = info["power"], - "checks" = info["checks"], - "direction" = info["direction"], - "external" = info["external"] - ) - data["vents"] = vents - if(AALARM_SCREEN_SCRUB) - var/scrubbers[0] - 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[++scrubbers.len] = list( - "id_tag" = id_tag, - "long_name" = sanitize(long_name), - "power" = info["power"], - "scrubbing" = info["scrubbing"], - "panic" = info["panic"], - "filters" = list() - ) - scrubbers[scrubbers.len]["filters"] += list(list("name" = "Oxygen", "command" = "o2_scrub", "val" = info["filter_o2"])) - scrubbers[scrubbers.len]["filters"] += list(list("name" = "Nitrogen", "command" = "n2_scrub", "val" = info["filter_n2"])) - scrubbers[scrubbers.len]["filters"] += list(list("name" = "Carbon Dioxide", "command" = "co2_scrub","val" = info["filter_co2"])) - scrubbers[scrubbers.len]["filters"] += list(list("name" = "Phoron" , "command" = "tox_scrub","val" = info["filter_phoron"])) - scrubbers[scrubbers.len]["filters"] += list(list("name" = "Nitrous Oxide", "command" = "n2o_scrub","val" = info["filter_n2o"])) - data["scrubbers"] = scrubbers - if(AALARM_SCREEN_MODE) - var/modes[0] - 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) - data["modes"] = modes - data["mode"] = mode - if(AALARM_SCREEN_SENSORS) - var/list/selected - var/thresholds[0] - - var/list/gas_names = list( - "oxygen" = "O2", - "carbon dioxide" = "CO2", - "phoron" = "Phoron", - "other" = "Other") - for (var/g in gas_names) - thresholds[++thresholds.len] = list("name" = gas_names[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 - -/obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/topic_state/state, var/href_list = list()) - if(buildstage != 2) - return STATUS_CLOSE - - if(aidisabled && isAI(user)) - to_chat(user, "AI control for \the [src] interface has been disabled.") - return STATUS_CLOSE - - . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE - - if(. == STATUS_INTERACTIVE) - var/extra_href = QDELETED(state) ? list() : state.href_list(usr) - // Prevent remote users from altering RCON settings unless they already have access - if(href_list["rcon"] && extra_href["remote_connection"] && !extra_href["remote_access"]) - . = STATUS_UPDATE - - return min(..(), .) - -/obj/machinery/alarm/Topic(href, href_list, var/datum/topic_state/state) - if(..(href, href_list, state)) - return 1 - - // hrefs that can always be called -walter0o - if(href_list["rcon"]) - var/attempted_rcon_setting = text2num(href_list["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 1 - - if(href_list["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 = input("What temperature would you like the system to mantain? (Capped between [min_temperature] and [max_temperature]C)", "Thermostat Controls", target_temperature - T0C) as num|null - if(isnum(input_temperature)) - var/temp = Clamp(input_temperature, min_temperature, max_temperature) - if(input_temperature > max_temperature || input_temperature < min_temperature) - to_chat(usr, "Temperature must be between [min_temperature]C and [max_temperature]C. Target temperature clamped to [temp]C") - target_temperature = Clamp(input_temperature + T0C, selected[2], selected[3]) - else - to_chat(usr, "Error, input not recognised. Temperature unchanged.") - - return 1 - - // hrefs that need the AA unlocked -walter0o - var/extra_href = QDELETED(state) ? list() : state.href_list(usr) - if(!(locked && !extra_href["remote_connection"]) || extra_href["remote_access"] || issilicon(usr)) - if(href_list["command"]) - var/device_id = href_list["id_tag"] - switch(href_list["command"]) - if("set_external_pressure") - var/input_pressure = input("What pressure you like the system to mantain?", "Pressure Controls") as num|null - if(isnum(input_pressure)) - send_signal(device_id, list(href_list["command"] = input_pressure)) - return 1 - - if("reset_external_pressure") - send_signal(device_id, list(href_list["command"] = ONE_ATMOSPHERE)) - return 1 - - if( "power", - "adjust_external_pressure", - "checks", - "o2_scrub", - "n2_scrub", - "co2_scrub", - "tox_scrub", - "n2o_scrub", - "panic_siphon", - "scrubbing") - - send_signal(device_id, list(href_list["command"] = text2num(href_list["val"]) ) ) - return 1 - - if("set_threshold") - var/env = href_list["env"] - var/threshold = text2num(href_list["var"]) - var/list/selected = TLV[env] - var/list/thresholds = list("lower bound", "low warning", "high warning", "upper bound") - var/newval = input("Enter [thresholds[threshold]] for [env]", "Alarm triggers", selected[threshold]) as null|num - if (isnull(newval)) - return 1 - if (newval<0) - selected[threshold] = -1.0 - else if (env=="temperature" && newval>5000) - selected[threshold] = 5000 - else if (env=="pressure" && newval>50*ONE_ATMOSPHERE) - selected[threshold] = 50*ONE_ATMOSPHERE - else if (env!="temperature" && env!="pressure" && newval>200) - selected[threshold] = 200 - else - newval = round(newval,0.01) - selected[threshold] = newval - - switch (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] - - apply_mode() - return 1 - - if(href_list["screen"]) - screen = text2num(href_list["screen"]) - return 1 - - if(href_list["atmos_unlock"]) - switch(href_list["atmos_unlock"]) - if("0") - alarm_area.air_doors_close() - if("1") - alarm_area.air_doors_open() - return 1 - - if(href_list["atmos_alarm"]) - if (alarm_area.atmosalert(2, src)) - apply_danger_level(2) - update_icon() - return 1 - - if(href_list["atmos_reset"]) - if (alarm_area.atmosalert(0, src)) - apply_danger_level(0) - update_icon() - return 1 - - if(href_list["mode"]) - mode = text2num(href_list["mode"]) - apply_mode() - return 1 - -/obj/machinery/alarm/attackby(obj/item/W as obj, mob/user as mob) - src.add_fingerprint(user) - - switch(buildstage) - if(2) - if(W.isscrewdriver()) // Opening that Air Alarm up. - wiresexposed = !wiresexposed - to_chat(user, "You [wiresexposed ? "open" : "close"] the maintenance panel.") - update_icon() - return - - if (wiresexposed && W.iswirecutter()) - user.visible_message("[user] has cut the wires inside \the [src]!", "You cut the wires inside \the [src].") - playsound(src.loc, 'sound/items/Wirecutter.ogg', 50, 1) - new/obj/item/stack/cable_coil(get_turf(src), 5) - buildstage = 1 - update_icon() - return - - if (istype(W, /obj/item/card/id) || istype(W, /obj/item/device/pda))// trying to unlock the interface with an ID card - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "Nothing happens.") - return - else - if(allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.") - else - to_chat(user, "Access denied.") - return - - if(1) - if(W.iscoil()) - var/obj/item/stack/cable_coil/C = W - if (C.use(5)) - to_chat(user, "You wire \the [src].") - buildstage = 2 - update_icon() - first_run() - set_frequency(frequency) - return - else - to_chat(user, "You need 5 pieces of cable to do wire \the [src].") - return - - else if(W.iscrowbar()) - to_chat(user, "You start prying out the circuit.") - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - if(do_after(user,20/W.toolspeed)) - to_chat(user, "You pry out the circuit!") - var/obj/item/airalarm_electronics/circuit = new /obj/item/airalarm_electronics() - circuit.forceMove(user.loc) - buildstage = 0 - update_icon() - return - if(0) - if(istype(W, /obj/item/airalarm_electronics)) - to_chat(user, "You insert the circuit!") - qdel(W) - buildstage = 1 - update_icon() - return - - else if(W.iswrench()) - to_chat(user, "You remove the air alarm assembly from the wall!") - new /obj/item/frame/air_alarm(get_turf(user)) - playsound(src.loc, W.usesound, 50, 1) - qdel(src) - - return ..() - -/obj/machinery/alarm/power_change() - ..() - queue_icon_update() - -/obj/machinery/alarm/examine(mob/user) - ..(user) - if (buildstage < 2) - to_chat(user, "It is not wired.") - if (buildstage < 1) - to_chat(user, "The circuit is missing.") -/* -AIR ALARM CIRCUIT -Just a object used in constructing air alarms -*/ -/obj/item/airalarm_electronics - name = "air alarm electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - desc = "Looks like a circuit. Probably is." - w_class = 2.0 - matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) - -// Fire Alarms moved to firealarm.dm +//////////////////////////////////////// +//CONTAINS: Air Alarms and Fire Alarms// +//////////////////////////////////////// + +#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 RCON_NO 1 +#define RCON_AUTO 2 +#define RCON_YES 3 + +#define MAX_TEMPERATURE 90 +#define MIN_TEMPERATURE -40 + +//all air alarms in area are connected via magic +/area + 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 = "A device that controls the local air regulation machinery and informs you when you're breathing vacuum." + icon = 'icons/obj/monitors.dmi' + icon_state = "alarm0" + anchored = 1 + use_power = 1 + idle_power_usage = 90 + active_power_usage = 1500 //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) + 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 + var/wiresexposed = 0 // If it's been screwdrivered open. + var/aidisabled = 0 + var/shorted = 0 + var/highpower = 0 // if true, power usage & temperature regulation power is increased + + var/datum/wires/alarm/wires + + var/mode = AALARM_MODE_SCRUBBING + var/screen = AALARM_SCREEN_MAIN + var/area_uid + var/area/alarm_area + var/buildstage = 2 //2 is built, 1 is building, 0 is frame. + + var/target_temperature = T0C+20 + var/regulating_temperature = 0 + + var/datum/radio_frequency/radio_connection + + var/list/TLV = list() + var/list/trace_gas = list("sleeping_agent") //list of other gases that this air alarm is able to detect + + var/danger_level = 0 + var/pressure_dangerlevel = 0 + var/oxygen_dangerlevel = 0 + var/co2_dangerlevel = 0 + var/phoron_dangerlevel = 0 + var/temperature_dangerlevel = 0 + var/other_dangerlevel = 0 + + var/report_danger_level = 1 + +/obj/machinery/alarm/nobreach + breach_detection = 0 + desc = "A device that controls the local air regulation machinery." + +/obj/machinery/alarm/monitor + report_danger_level = 0 + breach_detection = 0 + desc = "A device that controls the local air regulation machinery." + +/obj/machinery/alarm/server + req_one_access = list(access_rd, access_atmospherics, access_engine_equip) + target_temperature = 90 + desc = "A device that controls the local air regulation machinery. This one is designed for use in server rooms." + highpower = 1 + +/obj/machinery/alarm/freezer + req_one_access = list(access_kitchen, access_atmospherics, access_engine_equip) + highpower = 1 + target_temperature = T0C - 20 + +/obj/machinery/alarm/cold + target_temperature = T0C + 5 + +/obj/machinery/alarm/server/Initialize() + . = ..() + 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.2, 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 + +//For colder alarms, allowing pressure to drop a bit below normal is necessary. Pressure fluctuates downwards +//While temperature stabilises. + +//Kitchen freezer +/obj/machinery/alarm/freezer/Initialize() + . = ..() + TLV["oxygen"] = list(16, 17, 135, 140) // Partial pressure, kpa + TLV["pressure"] = list(ONE_ATMOSPHERE*0.50,ONE_ATMOSPHERE*0.70,ONE_ATMOSPHERE*1.10,ONE_ATMOSPHERE*1.20) + TLV["temperature"] = list(0, 0, 273, T0C+40) // No lower limits. Alarm above 0c. Major alarm at harmful heat + +//Refridgerated area, cold but above-freezing +/obj/machinery/alarm/cold/Initialize() + . = ..() + TLV["pressure"] = list(ONE_ATMOSPHERE*0.70,ONE_ATMOSPHERE*0.80,ONE_ATMOSPHERE*1.10,ONE_ATMOSPHERE*1.20) /* kpa */ + TLV["temperature"] = list(247, 273, 288, T0C+40) // Shouldn't go below 0 + +/obj/machinery/alarm/Destroy() + unregister_radio(src, frequency) + qdel(wires) + wires = null + return ..() + +/obj/machinery/alarm/LateInitialize() + apply_mode() + +/obj/machinery/alarm/Initialize(mapload, var/dir, var/building = 0) + . = ..() + + if(building) + if(dir) + src.set_dir(dir) + + buildstage = 0 + wiresexposed = 1 + pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24) + pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0 + update_icon() + return + + first_run() + + set_frequency(frequency) + + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/alarm/proc/first_run() + alarm_area = get_area(src) + area_uid = alarm_area.uid + if (name == "alarm") + if (highpower) + name = "[alarm_area.name] High-Power Air Alarm" + else + name = "[alarm_area.name] Air Alarm" + + if(!wires) + wires = new(src) + + if (highpower) + active_power_usage *= 6 + idle_power_usage *= 3 + + // breathable air according to human/Life() + TLV["oxygen"] = list(16, 19, 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.2, 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 + +/obj/machinery/alarm/machinery_process() + if((stat & (NOPOWER|BROKEN)) || shorted || buildstage != 2) + return + + var/turf/simulated/location = 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() 2.0) + update_use_power(2) + regulating_temperature = 1 + visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click and a faint electronic hum.") + else + //check for when we should stop adjusting temperature + if (get_danger_level(target_temperature, TLV["temperature"]) || abs(environment.temperature - target_temperature) <= 0.5) + update_use_power(1) + regulating_temperature = 0 + visible_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.") + + if (regulating_temperature) + //Unnecessary checks removed, duplication of effort + + 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. + + pressure_dangerlevel = get_danger_level(environment_pressure, TLV["pressure"]) + oxygen_dangerlevel = get_danger_level(environment.gas["oxygen"]*partial_pressure, TLV["oxygen"]) + co2_dangerlevel = get_danger_level(environment.gas["carbon_dioxide"]*partial_pressure, TLV["carbon dioxide"]) + phoron_dangerlevel = get_danger_level(environment.gas["phoron"]*partial_pressure, TLV["phoron"]) + temperature_dangerlevel = get_danger_level(environment.temperature, TLV["temperature"]) + other_dangerlevel = get_danger_level(other_moles*partial_pressure, TLV["other"]) + + 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 = 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/get_danger_level(var/current_value, var/list/danger_levels) + if((current_value > danger_levels[4] && danger_levels[4] > 0) || current_value < danger_levels[1]) + return 2 + if((current_value > danger_levels[3] && danger_levels[3] > 0) || current_value < danger_levels[2]) + return 1 + return 0 + +/obj/machinery/alarm/update_icon() + if(wiresexposed) + icon_state = "alarmx" + set_light(0) + return + if((stat & (NOPOWER|BROKEN)) || shorted) + icon_state = "alarmp" + set_light(0) + 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 = "alarm0" + new_color = "#03A728" + if (1) + icon_state = "alarm2" //yes, alarm2 is yellow alarm + new_color = COLOR_SUN + if (2) + icon_state = "alarm1" + new_color = "#DA0205" + + set_light(l_range = L_WALLMOUNT_RANGE, l_power = L_WALLMOUNT_POWER, l_color = new_color) + +/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) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.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 = 1 //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) + + 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 = SSradio.return_frequency(alarm_frequency) + if(!frequency) + return + + var/datum/signal/alert_signal = new + alert_signal.source = src + alert_signal.transmission_method = 1 + 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) + ui_interact(user) + +/obj/machinery/alarm/attack_hand(mob/user) + . = ..() + if (.) + return + return interact(user) + +/obj/machinery/alarm/interact(mob/user) + ui_interact(user) + wires.Interact(user) + +/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) + var/data[0] + var/remote_connection = 0 + var/remote_access = 0 + if(state) + var/list/href = state.href_list(user) + remote_connection = href["remote_connection"] // Remote connection means we're non-adjacent/connecting from another computer + remote_access = href["remote_access"] // Remote access means we also have the privilege to alter the air alarm. + + data["locked"] = locked && !issilicon(user) + data["remote_connection"] = remote_connection + data["remote_access"] = remote_access + data["rcon"] = rcon_setting + data["screen"] = screen + + populate_status(data) + + if(!(locked && !remote_connection) || remote_access || issilicon(user)) + populate_controls(data) + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + ui = new(user, src, ui_key, "air_alarm.tmpl", src.name, 325, 625, master_ui = master_ui, state = state) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/alarm/proc/populate_status(var/data) + var/turf/location = get_turf(src) + var/datum/gas_mixture/environment = location.return_air() + var/total = environment.total_moles + + var/list/environment_data = new + data["has_environment"] = total + if(total) + var/pressure = environment.return_pressure() + environment_data[++environment_data.len] = list("name" = "Pressure", "value" = pressure, "unit" = "kPa", "danger_level" = pressure_dangerlevel) + environment_data[++environment_data.len] = list("name" = "Oxygen", "value" = environment.gas["oxygen"] / total * 100, "unit" = "%", "danger_level" = oxygen_dangerlevel) + environment_data[++environment_data.len] = list("name" = "Carbon dioxide", "value" = environment.gas["carbon_dioxide"] / total * 100, "unit" = "%", "danger_level" = co2_dangerlevel) + environment_data[++environment_data.len] = list("name" = "Phoron", "value" = environment.gas["phoron"] / total * 100, "unit" = "%", "danger_level" = phoron_dangerlevel) + environment_data[++environment_data.len] = list("name" = "Temperature", "value" = environment.temperature, "unit" = "K ([round(environment.temperature - T0C, 0.1)]C)", "danger_level" = temperature_dangerlevel) + data["total_danger"] = danger_level + data["environment"] = environment_data + data["atmos_alarm"] = alarm_area.atmosalm + data["fire_alarm"] = alarm_area.fire != null + data["target_temperature"] = "[target_temperature - T0C]C" + +/obj/machinery/alarm/proc/populate_controls(var/list/data) + switch(screen) + if(AALARM_SCREEN_MAIN) + data["mode"] = mode + if(AALARM_SCREEN_VENT) + var/vents[0] + for(var/id_tag in alarm_area.air_vent_names) + var/long_name = alarm_area.air_vent_names[id_tag] + var/list/info = alarm_area.air_vent_info[id_tag] + if(!info) + continue + vents[++vents.len] = list( + "id_tag" = id_tag, + "long_name" = sanitize(long_name), + "power" = info["power"], + "checks" = info["checks"], + "direction" = info["direction"], + "external" = info["external"] + ) + data["vents"] = vents + if(AALARM_SCREEN_SCRUB) + var/scrubbers[0] + 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[++scrubbers.len] = list( + "id_tag" = id_tag, + "long_name" = sanitize(long_name), + "power" = info["power"], + "scrubbing" = info["scrubbing"], + "panic" = info["panic"], + "filters" = list() + ) + scrubbers[scrubbers.len]["filters"] += list(list("name" = "Oxygen", "command" = "o2_scrub", "val" = info["filter_o2"])) + scrubbers[scrubbers.len]["filters"] += list(list("name" = "Nitrogen", "command" = "n2_scrub", "val" = info["filter_n2"])) + scrubbers[scrubbers.len]["filters"] += list(list("name" = "Carbon Dioxide", "command" = "co2_scrub","val" = info["filter_co2"])) + scrubbers[scrubbers.len]["filters"] += list(list("name" = "Phoron" , "command" = "tox_scrub","val" = info["filter_phoron"])) + scrubbers[scrubbers.len]["filters"] += list(list("name" = "Nitrous Oxide", "command" = "n2o_scrub","val" = info["filter_n2o"])) + data["scrubbers"] = scrubbers + if(AALARM_SCREEN_MODE) + var/modes[0] + 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) + data["modes"] = modes + data["mode"] = mode + if(AALARM_SCREEN_SENSORS) + var/list/selected + var/thresholds[0] + + var/list/gas_names = list( + "oxygen" = "O2", + "carbon dioxide" = "CO2", + "phoron" = "Phoron", + "other" = "Other") + for (var/g in gas_names) + thresholds[++thresholds.len] = list("name" = gas_names[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 + +/obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/topic_state/state, var/href_list = list()) + if(buildstage != 2) + return STATUS_CLOSE + + if(aidisabled && isAI(user)) + to_chat(user, "AI control for \the [src] interface has been disabled.") + return STATUS_CLOSE + + . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE + + if(. == STATUS_INTERACTIVE) + var/extra_href = QDELETED(state) ? list() : state.href_list(usr) + // Prevent remote users from altering RCON settings unless they already have access + if(href_list["rcon"] && extra_href["remote_connection"] && !extra_href["remote_access"]) + . = STATUS_UPDATE + + return min(..(), .) + +/obj/machinery/alarm/Topic(href, href_list, var/datum/topic_state/state) + if(..(href, href_list, state)) + return 1 + + // hrefs that can always be called -walter0o + if(href_list["rcon"]) + var/attempted_rcon_setting = text2num(href_list["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 1 + + if(href_list["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 = input("What temperature would you like the system to mantain? (Capped between [min_temperature] and [max_temperature]C)", "Thermostat Controls", target_temperature - T0C) as num|null + if(isnum(input_temperature)) + var/temp = Clamp(input_temperature, min_temperature, max_temperature) + if(input_temperature > max_temperature || input_temperature < min_temperature) + to_chat(usr, "Temperature must be between [min_temperature]C and [max_temperature]C. Target temperature clamped to [temp]C") + target_temperature = Clamp(input_temperature + T0C, selected[2], selected[3]) + else + to_chat(usr, "Error, input not recognised. Temperature unchanged.") + + return 1 + + // hrefs that need the AA unlocked -walter0o + var/extra_href = QDELETED(state) ? list() : state.href_list(usr) + if(!(locked && !extra_href["remote_connection"]) || extra_href["remote_access"] || issilicon(usr)) + if(href_list["command"]) + var/device_id = href_list["id_tag"] + switch(href_list["command"]) + if("set_external_pressure") + var/input_pressure = input("What pressure you like the system to mantain?", "Pressure Controls") as num|null + if(isnum(input_pressure)) + send_signal(device_id, list(href_list["command"] = input_pressure)) + return 1 + + if("reset_external_pressure") + send_signal(device_id, list(href_list["command"] = ONE_ATMOSPHERE)) + return 1 + + if( "power", + "adjust_external_pressure", + "checks", + "o2_scrub", + "n2_scrub", + "co2_scrub", + "tox_scrub", + "n2o_scrub", + "panic_siphon", + "scrubbing") + + send_signal(device_id, list(href_list["command"] = text2num(href_list["val"]) ) ) + return 1 + + if("set_threshold") + var/env = href_list["env"] + var/threshold = text2num(href_list["var"]) + var/list/selected = TLV[env] + var/list/thresholds = list("lower bound", "low warning", "high warning", "upper bound") + var/newval = input("Enter [thresholds[threshold]] for [env]", "Alarm triggers", selected[threshold]) as null|num + if (isnull(newval)) + return 1 + if (newval<0) + selected[threshold] = -1.0 + else if (env=="temperature" && newval>5000) + selected[threshold] = 5000 + else if (env=="pressure" && newval>50*ONE_ATMOSPHERE) + selected[threshold] = 50*ONE_ATMOSPHERE + else if (env!="temperature" && env!="pressure" && newval>200) + selected[threshold] = 200 + else + newval = round(newval,0.01) + selected[threshold] = newval + + switch (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] + + apply_mode() + return 1 + + if(href_list["screen"]) + screen = text2num(href_list["screen"]) + return 1 + + if(href_list["atmos_unlock"]) + switch(href_list["atmos_unlock"]) + if("0") + alarm_area.air_doors_close() + if("1") + alarm_area.air_doors_open() + return 1 + + if(href_list["atmos_alarm"]) + if (alarm_area.atmosalert(2, src)) + apply_danger_level(2) + update_icon() + return 1 + + if(href_list["atmos_reset"]) + if (alarm_area.atmosalert(0, src)) + apply_danger_level(0) + update_icon() + return 1 + + if(href_list["mode"]) + mode = text2num(href_list["mode"]) + apply_mode() + return 1 + +/obj/machinery/alarm/attackby(obj/item/W as obj, mob/user as mob) + src.add_fingerprint(user) + + switch(buildstage) + if(2) + if(W.isscrewdriver()) // Opening that Air Alarm up. + wiresexposed = !wiresexposed + to_chat(user, "You [wiresexposed ? "open" : "close"] the maintenance panel.") + update_icon() + return + + if (wiresexposed && W.iswirecutter()) + user.visible_message("[user] has cut the wires inside \the [src]!", "You cut the wires inside \the [src].") + playsound(src.loc, 'sound/items/Wirecutter.ogg', 50, 1) + new/obj/item/stack/cable_coil(get_turf(src), 5) + buildstage = 1 + update_icon() + return + + if (istype(W, /obj/item/card/id) || istype(W, /obj/item/device/pda))// trying to unlock the interface with an ID card + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "Nothing happens.") + return + else + if(allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.") + else + to_chat(user, "Access denied.") + return + + if(1) + if(W.iscoil()) + var/obj/item/stack/cable_coil/C = W + if (C.use(5)) + to_chat(user, "You wire \the [src].") + buildstage = 2 + update_icon() + first_run() + set_frequency(frequency) + return + else + to_chat(user, "You need 5 pieces of cable to do wire \the [src].") + return + + else if(W.iscrowbar()) + to_chat(user, "You start prying out the circuit.") + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + if(do_after(user,20/W.toolspeed)) + to_chat(user, "You pry out the circuit!") + var/obj/item/airalarm_electronics/circuit = new /obj/item/airalarm_electronics() + circuit.forceMove(user.loc) + buildstage = 0 + update_icon() + return + if(0) + if(istype(W, /obj/item/airalarm_electronics)) + to_chat(user, "You insert the circuit!") + qdel(W) + buildstage = 1 + update_icon() + return + + else if(W.iswrench()) + to_chat(user, "You remove the air alarm assembly from the wall!") + new /obj/item/frame/air_alarm(get_turf(user)) + playsound(src.loc, W.usesound, 50, 1) + qdel(src) + + return ..() + +/obj/machinery/alarm/power_change() + ..() + queue_icon_update() + +/obj/machinery/alarm/examine(mob/user) + ..(user) + if (buildstage < 2) + to_chat(user, "It is not wired.") + if (buildstage < 1) + to_chat(user, "The circuit is missing.") +/* +AIR ALARM CIRCUIT +Just a object used in constructing air alarms +*/ +/obj/item/airalarm_electronics + name = "air alarm electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + desc = "Looks like a circuit. Probably is." + w_class = 2.0 + matter = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) + +// Fire Alarms moved to firealarm.dm diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index b8a93dfc48c..c733170cabf 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -1,420 +1,420 @@ -/obj/machinery/air_sensor - icon = 'icons/obj/stationobjs.dmi' - icon_state = "gsensor1" - name = "Gas Sensor" - desc = "Measures the gas content of the atmosphere around the sensor." - - anchored = 1 - 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/machinery_process() - if(on) - var/datum/signal/signal = new - signal.transmission_method = 1 //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, filter = RADIO_ATMOSIA) - - -/obj/machinery/air_sensor/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/air_sensor/Initialize() - . = ..() - set_frequency(frequency) - -obj/machinery/air_sensor/Destroy() - if(SSradio) - SSradio.remove_object(src,frequency) - return ..() - -/obj/machinery/computer/general_air_control - icon_screen = "tank" - light_color = LIGHT_COLOR_CYAN - - name = "Computer" - - var/frequency = 1439 - var/list/sensors = list() - - var/list/sensor_information = list() - var/datum/radio_frequency/radio_connection - circuit = /obj/item/circuitboard/air_management - -obj/machinery/computer/general_air_control/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - return ..() - -/obj/machinery/computer/general_air_control/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui) - if(!data) - . = data = list("sensors" = list()) - data["control"] = null - for(var/id_tag in sensors) - var/long_name = sensors[id_tag] - var/list/sdata = sensor_information[id_tag] - LAZYINITLIST(data["sensors"][id_tag]) - VUEUI_SET_CHECK(data["sensors"][id_tag]["name"], long_name, ., data) - for(var/datapoint in list("pressure", "temperature", "oxygen", "nitrogen", "carbon_dioxide", "phoron")) - VUEUI_SET_CHECK(data["sensors"][id_tag][datapoint], sdata[datapoint], ., data) - -/obj/machinery/computer/general_air_control/attack_hand(mob/user) - ui_interact(user) - -/obj/machinery/computer/general_air_control/ui_interact(mob/user) - var/datum/vueui/ui = SSvueui.get_open_ui(user, src) - if (!ui) - ui = new(user, src, "console-atmocontrol-main", 460, 470, capitalize(src.name)) - ui.auto_update_content = TRUE - ui.open() - -/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/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/computer/general_air_control/Initialize() - . = ..() - set_frequency(frequency) - - -/obj/machinery/computer/general_air_control/large_tank_control - - frequency = 1441 - var/input_tag - var/output_tag - - var/list/input_info - var/list/output_info - - var/default_input_flow_setting = 200 - var/default_pressure_setting = ONE_ATMOSPHERE * 45 - var/max_input_flow_setting = ATMOS_DEFAULT_VOLUME_PUMP + 500 - var/max_pressure_setting = 50 * ONE_ATMOSPHERE - circuit = /obj/item/circuitboard/air_management/tank_control - -/obj/machinery/computer/general_air_control/large_tank_control/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui) - . = ..() - data = . || data - data["control"] = "tank" - data["maxrate"] = max_input_flow_setting - data["maxpressure"] = max_pressure_setting - if(input_info) - LAZYINITLIST(data["input"]) - VUEUI_SET_CHECK(data["input"]["power"], input_info["power"], ., data) - VUEUI_SET_CHECK(data["input"]["rate"], input_info["volume_rate"], ., data) - VUEUI_SET_CHECK_IFNOTSET(data["input"]["setrate"], default_input_flow_setting, ., data) - - if(output_info) - LAZYINITLIST(data["output"]) - VUEUI_SET_CHECK(data["output"]["power"], output_info["power"], ., data) - VUEUI_SET_CHECK(data["output"]["pressure"], output_info["internal"], ., data) - VUEUI_SET_CHECK_IFNOTSET(data["output"]["setpressure"], default_pressure_setting, ., 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/Topic(href, href_list) - if(..()) - return 1 - - if(!radio_connection) - return 0 - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - if(href_list["in_refresh_status"]) - input_info = null - signal.data = list ("tag" = input_tag, "status" = 1) - - if(href_list["in_toggle_injector"]) - input_info = null - signal.data = list ("tag" = input_tag, "power_toggle" = 1) - - if(href_list["in_set_flowrate"] != null) - var/setrate = between(0, text2num(href_list["in_set_flowrate"]), max_input_flow_setting) - input_info = null - signal.data = list ("tag" = input_tag, "set_volume_rate" = "[setrate]") - - if(href_list["out_refresh_status"]) - output_info = null - signal.data = list ("tag" = output_tag, "status" = 1) - - if(href_list["out_toggle_power"]) - output_info = null - signal.data = list ("tag" = output_tag, "power_toggle" = 1) - - if(href_list["out_set_pressure"] != null) - var/setpressure = between(0, text2num(href_list["out_set_pressure"]), max_pressure_setting) - output_info = null - signal.data = list ("tag" = output_tag, "set_internal_pressure" = "[setpressure]") - - signal.data["sigtype"] = "command" - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - addtimer(CALLBACK(SSvueui, /datum/controller/subsystem/processing/vueui/proc/check_uis_for_change, src), 5) //Just in case we get no new data - -/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/default_input_flow_setting = 700 - var/default_pressure_setting = 100 - var/max_input_flow_setting = ATMOS_DEFAULT_VOLUME_PUMP + 500 - var/max_pressure_setting = 10 * ONE_ATMOSPHERE - circuit = /obj/item/circuitboard/air_management/supermatter_core - -/obj/machinery/computer/general_air_control/supermatter_core/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui) - . = ..() - data = . || data - data["control"] = "supermatter" - data["maxrate"] = max_input_flow_setting - data["maxpressure"] = max_pressure_setting - if(input_info) - LAZYINITLIST(data["input"]) - VUEUI_SET_CHECK(data["input"]["power"], input_info["power"], ., data) - VUEUI_SET_CHECK(data["input"]["rate"], input_info["volume_rate"], ., data) - VUEUI_SET_CHECK_IFNOTSET(data["input"]["setrate"], default_input_flow_setting, ., data) - - if(output_info) - LAZYINITLIST(data["output"]) - VUEUI_SET_CHECK(data["output"]["power"], output_info["power"], ., data) - VUEUI_SET_CHECK(data["output"]["pressure"], output_info["external"], ., data) - VUEUI_SET_CHECK_IFNOTSET(data["output"]["setpressure"], default_pressure_setting, ., 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/Topic(href, href_list) - if(..()) - return - - if(!radio_connection) - return - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - if(href_list["in_refresh_status"]) - input_info = null - signal.data = list ("tag" = input_tag, "status" = 1) - - if(href_list["in_toggle_injector"]) - input_info = null - signal.data = list ("tag" = input_tag, "power_toggle" = 1) - - if(href_list["in_set_flowrate"] != null) - var/setrate = between(0, text2num(href_list["in_set_flowrate"]), max_input_flow_setting) - input_info = null - signal.data = list ("tag" = input_tag, "set_volume_rate" = "[setrate]") - - if(href_list["out_refresh_status"]) - output_info = null - signal.data = list ("tag" = output_tag, "status" = 1) - - if(href_list["out_toggle_power"]) - output_info = null - signal.data = list ("tag" = output_tag, "power_toggle" = 1) - - if(href_list["out_set_pressure"]) - var/setpressure = between(0, text2num(href_list["out_set_pressure"]), max_pressure_setting) - output_info = null - signal.data = list ("tag" = output_tag, "set_external_pressure" = "[setpressure]", "checks" = 1) - - signal.data["sigtype"]="command" - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - addtimer(CALLBACK(SSvueui, /datum/controller/subsystem/processing/vueui/proc/check_uis_for_change, src), 5) //Just in case we get no new data - -/obj/machinery/computer/general_air_control/fuel_injection - 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/circuitboard/air_management/injector_control - -/obj/machinery/computer/general_air_control/fuel_injection/machinery_process() - if(automation) - if(!radio_connection) - return 0 - - 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 = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = device_tag, - "power" = injecting, - "sigtype"="command" - ) - - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - ..() - -/obj/machinery/computer/general_air_control/fuel_injection/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui) - . = ..() - data = . || data - data["control"] = "injector" - if(device_info) - LAZYINITLIST(data["device"]) - VUEUI_SET_CHECK(data["device"]["power"], device_info["power"], ., data) - VUEUI_SET_CHECK(data["device"]["rate"], device_info["volume_rate"], ., data) - VUEUI_SET_CHECK(data["device"]["automation"], automation, ., 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/Topic(href, href_list) - if(..()) - return - - if(href_list["refresh_status"]) - device_info = null - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "status" = 1, - "sigtype"="command" - ) - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - if(href_list["toggle_automation"]) - automation = !automation - SSvueui.check_uis_for_change(src) - - if(href_list["toggle_injector"]) - device_info = null - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "power_toggle" = 1, - "sigtype"="command" - ) - - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - if(href_list["injection"]) - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - signal.data = list( - "tag" = device_tag, - "inject" = 1, - "sigtype"="command" - ) - - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) +/obj/machinery/air_sensor + icon = 'icons/obj/stationobjs.dmi' + icon_state = "gsensor1" + name = "Gas Sensor" + desc = "Measures the gas content of the atmosphere around the sensor." + + anchored = 1 + 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/machinery_process() + if(on) + var/datum/signal/signal = new + signal.transmission_method = 1 //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, filter = RADIO_ATMOSIA) + + +/obj/machinery/air_sensor/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/air_sensor/Initialize() + . = ..() + set_frequency(frequency) + +obj/machinery/air_sensor/Destroy() + if(SSradio) + SSradio.remove_object(src,frequency) + return ..() + +/obj/machinery/computer/general_air_control + icon_screen = "tank" + light_color = LIGHT_COLOR_CYAN + + name = "Computer" + + var/frequency = 1439 + var/list/sensors = list() + + var/list/sensor_information = list() + var/datum/radio_frequency/radio_connection + circuit = /obj/item/circuitboard/air_management + +obj/machinery/computer/general_air_control/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + return ..() + +/obj/machinery/computer/general_air_control/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui) + if(!data) + . = data = list("sensors" = list()) + data["control"] = null + for(var/id_tag in sensors) + var/long_name = sensors[id_tag] + var/list/sdata = sensor_information[id_tag] + LAZYINITLIST(data["sensors"][id_tag]) + VUEUI_SET_CHECK(data["sensors"][id_tag]["name"], long_name, ., data) + for(var/datapoint in list("pressure", "temperature", "oxygen", "nitrogen", "carbon_dioxide", "phoron")) + VUEUI_SET_CHECK(data["sensors"][id_tag][datapoint], sdata[datapoint], ., data) + +/obj/machinery/computer/general_air_control/attack_hand(mob/user) + ui_interact(user) + +/obj/machinery/computer/general_air_control/ui_interact(mob/user) + var/datum/vueui/ui = SSvueui.get_open_ui(user, src) + if (!ui) + ui = new(user, src, "console-atmocontrol-main", 460, 470, capitalize(src.name)) + ui.auto_update_content = TRUE + ui.open() + +/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/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/computer/general_air_control/Initialize() + . = ..() + set_frequency(frequency) + + +/obj/machinery/computer/general_air_control/large_tank_control + + frequency = 1441 + var/input_tag + var/output_tag + + var/list/input_info + var/list/output_info + + var/default_input_flow_setting = 200 + var/default_pressure_setting = ONE_ATMOSPHERE * 45 + var/max_input_flow_setting = ATMOS_DEFAULT_VOLUME_PUMP + 500 + var/max_pressure_setting = 50 * ONE_ATMOSPHERE + circuit = /obj/item/circuitboard/air_management/tank_control + +/obj/machinery/computer/general_air_control/large_tank_control/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui) + . = ..() + data = . || data + data["control"] = "tank" + data["maxrate"] = max_input_flow_setting + data["maxpressure"] = max_pressure_setting + if(input_info) + LAZYINITLIST(data["input"]) + VUEUI_SET_CHECK(data["input"]["power"], input_info["power"], ., data) + VUEUI_SET_CHECK(data["input"]["rate"], input_info["volume_rate"], ., data) + VUEUI_SET_CHECK_IFNOTSET(data["input"]["setrate"], default_input_flow_setting, ., data) + + if(output_info) + LAZYINITLIST(data["output"]) + VUEUI_SET_CHECK(data["output"]["power"], output_info["power"], ., data) + VUEUI_SET_CHECK(data["output"]["pressure"], output_info["internal"], ., data) + VUEUI_SET_CHECK_IFNOTSET(data["output"]["setpressure"], default_pressure_setting, ., 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/Topic(href, href_list) + if(..()) + return 1 + + if(!radio_connection) + return 0 + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + if(href_list["in_refresh_status"]) + input_info = null + signal.data = list ("tag" = input_tag, "status" = 1) + + if(href_list["in_toggle_injector"]) + input_info = null + signal.data = list ("tag" = input_tag, "power_toggle" = 1) + + if(href_list["in_set_flowrate"] != null) + var/setrate = between(0, text2num(href_list["in_set_flowrate"]), max_input_flow_setting) + input_info = null + signal.data = list ("tag" = input_tag, "set_volume_rate" = "[setrate]") + + if(href_list["out_refresh_status"]) + output_info = null + signal.data = list ("tag" = output_tag, "status" = 1) + + if(href_list["out_toggle_power"]) + output_info = null + signal.data = list ("tag" = output_tag, "power_toggle" = 1) + + if(href_list["out_set_pressure"] != null) + var/setpressure = between(0, text2num(href_list["out_set_pressure"]), max_pressure_setting) + output_info = null + signal.data = list ("tag" = output_tag, "set_internal_pressure" = "[setpressure]") + + signal.data["sigtype"] = "command" + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + addtimer(CALLBACK(SSvueui, /datum/controller/subsystem/processing/vueui/proc/check_uis_for_change, src), 5) //Just in case we get no new data + +/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/default_input_flow_setting = 700 + var/default_pressure_setting = 100 + var/max_input_flow_setting = ATMOS_DEFAULT_VOLUME_PUMP + 500 + var/max_pressure_setting = 10 * ONE_ATMOSPHERE + circuit = /obj/item/circuitboard/air_management/supermatter_core + +/obj/machinery/computer/general_air_control/supermatter_core/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui) + . = ..() + data = . || data + data["control"] = "supermatter" + data["maxrate"] = max_input_flow_setting + data["maxpressure"] = max_pressure_setting + if(input_info) + LAZYINITLIST(data["input"]) + VUEUI_SET_CHECK(data["input"]["power"], input_info["power"], ., data) + VUEUI_SET_CHECK(data["input"]["rate"], input_info["volume_rate"], ., data) + VUEUI_SET_CHECK_IFNOTSET(data["input"]["setrate"], default_input_flow_setting, ., data) + + if(output_info) + LAZYINITLIST(data["output"]) + VUEUI_SET_CHECK(data["output"]["power"], output_info["power"], ., data) + VUEUI_SET_CHECK(data["output"]["pressure"], output_info["external"], ., data) + VUEUI_SET_CHECK_IFNOTSET(data["output"]["setpressure"], default_pressure_setting, ., 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/Topic(href, href_list) + if(..()) + return + + if(!radio_connection) + return + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + if(href_list["in_refresh_status"]) + input_info = null + signal.data = list ("tag" = input_tag, "status" = 1) + + if(href_list["in_toggle_injector"]) + input_info = null + signal.data = list ("tag" = input_tag, "power_toggle" = 1) + + if(href_list["in_set_flowrate"] != null) + var/setrate = between(0, text2num(href_list["in_set_flowrate"]), max_input_flow_setting) + input_info = null + signal.data = list ("tag" = input_tag, "set_volume_rate" = "[setrate]") + + if(href_list["out_refresh_status"]) + output_info = null + signal.data = list ("tag" = output_tag, "status" = 1) + + if(href_list["out_toggle_power"]) + output_info = null + signal.data = list ("tag" = output_tag, "power_toggle" = 1) + + if(href_list["out_set_pressure"]) + var/setpressure = between(0, text2num(href_list["out_set_pressure"]), max_pressure_setting) + output_info = null + signal.data = list ("tag" = output_tag, "set_external_pressure" = "[setpressure]", "checks" = 1) + + signal.data["sigtype"]="command" + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + addtimer(CALLBACK(SSvueui, /datum/controller/subsystem/processing/vueui/proc/check_uis_for_change, src), 5) //Just in case we get no new data + +/obj/machinery/computer/general_air_control/fuel_injection + 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/circuitboard/air_management/injector_control + +/obj/machinery/computer/general_air_control/fuel_injection/machinery_process() + if(automation) + if(!radio_connection) + return 0 + + 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 = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = device_tag, + "power" = injecting, + "sigtype"="command" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + ..() + +/obj/machinery/computer/general_air_control/fuel_injection/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui) + . = ..() + data = . || data + data["control"] = "injector" + if(device_info) + LAZYINITLIST(data["device"]) + VUEUI_SET_CHECK(data["device"]["power"], device_info["power"], ., data) + VUEUI_SET_CHECK(data["device"]["rate"], device_info["volume_rate"], ., data) + VUEUI_SET_CHECK(data["device"]["automation"], automation, ., 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/Topic(href, href_list) + if(..()) + return + + if(href_list["refresh_status"]) + device_info = null + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "status" = 1, + "sigtype"="command" + ) + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + if(href_list["toggle_automation"]) + automation = !automation + SSvueui.check_uis_for_change(src) + + if(href_list["toggle_injector"]) + device_info = null + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "power_toggle" = 1, + "sigtype"="command" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + if(href_list["injection"]) + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + signal.data = list( + "tag" = device_tag, + "inject" = 1, + "sigtype"="command" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 8685eb82cff..529196318bc 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -1,465 +1,465 @@ -/obj/machinery/portable_atmospherics/canister - name = "canister" - desc = "Holds gas. Has a built-in valve to allow for filling portable tanks." - icon = 'icons/obj/atmos.dmi' - icon_state = "yellow" - density = 1 - var/health = 100.0 - flags = CONDUCT - w_class = 5 - - 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 - var/temperature_resistance = 1000 + T0C - volume = 1000 - use_power = 0 - 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/sleeping_agent - 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/nitrogen/prechilled - name = "Canister: \[N2 (Cooling)\]" - -/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 = "orange" - canister_color = "orange" - 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 = "orange" - canister_color = "orange" -/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/sleeping_agent - 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) - cut_overlays() - 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.loc, 'sound/effects/spray.ogg', 10, 1, -3) - src.density = 0 - update_icon() - - if (src.holding) - src.holding.forceMove(src.loc) - src.holding = null - - return 1 - else - return 1 - -/obj/machinery/portable_atmospherics/canister/machinery_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/AltClick(var/mob/abstract/observer/admin) - if (istype(admin)) - if (admin.client && admin.client.holder && ((R_MOD|R_ADMIN) & admin.client.holder.rights)) - if (valve_open) - if (holding) - release_log += "Valve was closed by [key_name(admin)] (aghost), stopping the transfer into the [holding]
    " - else - release_log += "Valve was closed by [key_name(admin)] (aghost), stopping the transfer into the air
    " - else - if (alert(admin, "The release valve is currently closed. Do you want to open it?", "Open the valve?", "Yes", "No") == "No") - return - - if (holding) - release_log += "Valve was opened by [key_name(admin)] (aghost), starting the transfer into the [holding]
    " - else - release_log += "Valve was opened by [key_name(admin)] (aghost), starting the transfer into the air
    " - log_open(admin) - valve_open = !valve_open - -/obj/machinery/portable_atmospherics/canister/attackby(var/obj/item/W as obj, var/mob/user as mob) - if(!W.iswrench() && !istype(W, /obj/item/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/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 - - ..() - - SSnanoui.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 src.ui_interact(user) - -/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if (src.destroyed) - return - - // this is the data which will be sent to the ui - var/data[0] - data["name"] = name - data["canLabel"] = can_label ? 1 : 0 - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(release_pressure ? release_pressure : 0) - data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) - data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) - data["valveOpen"] = valve_open ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if (holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure())) - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400) - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/portable_atmospherics/canister/Topic(href, href_list) - - //Do not use "if(..()) return" here, canisters will stop working in unpowered areas like space or on the derelict. // yeah but without SOME sort of Topic check any dick can mess with them via exploits as he pleases -walter0o - //First comment might be outdated. - if (!istype(src.loc, /turf)) - return 0 - - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) // exploit protection -walter0o - usr << browse(null, "window=canister") - onclose(usr, "canister") - return - - if(href_list["toggle"]) - 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 - - if (href_list["remove_tank"]) - 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/tank)) - holding.manipulated_by = usr.real_name - holding.forceMove(loc) - holding = null - - if (href_list["pressure_adj"]) - var/diff = text2num(href_list["pressure_adj"]) - if(diff > 0) - release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff) - else - release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff) - - if (href_list["relabel"]) - if (can_label) - var/list/colors = list( - "\[N2O\]" = "redws", - "\[N2\]" = "red", - "\[O2\]" = "blue", - "\[Phoron\]" = "orange", - "\[CO2\]" = "black", - "\[Air\]" = "grey", - "\[CAUTION\]" = "yellow" - ) - var/label = input("Choose canister label", "Gas canister") as null|anything in colors - if (label) - src.canister_color = colors[label] - src.icon_state = colors[label] - src.name = "Canister: [label]" - - src.add_fingerprint(usr) - update_icon() - - return 1 - -/obj/machinery/portable_atmospherics/canister/phoron/Initialize() - . = ..() - - src.air_contents.adjust_gas("phoron", MolesForPressure()) - src.update_icon() - -/obj/machinery/portable_atmospherics/canister/oxygen/Initialize() - . = ..() - - src.air_contents.adjust_gas("oxygen", MolesForPressure()) - src.update_icon() - -/obj/machinery/portable_atmospherics/canister/oxygen/prechilled/Initialize() - . = ..() - src.air_contents.temperature = 80 - src.update_icon() - -/obj/machinery/portable_atmospherics/canister/sleeping_agent/Initialize() - . = ..() - - air_contents.adjust_gas("sleeping_agent", MolesForPressure()) - src.update_icon() - -//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/sleeping_agent/roomfiller/Initialize() - . = ..() - air_contents.gas["sleeping_agent"] = 9*4000 - spawn(10) - var/turf/simulated/location = src.loc - if (istype(src.loc)) - while (!location.air) - sleep(10) - location.assume_air(air_contents) - air_contents = new - -/obj/machinery/portable_atmospherics/canister/nitrogen/Initialize() - . = ..() - src.air_contents.adjust_gas("nitrogen", MolesForPressure()) - src.update_icon() - -/obj/machinery/portable_atmospherics/canister/nitrogen/prechilled/Initialize() - . = ..() - src.air_contents.temperature = 80 - src.update_icon() - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide/Initialize() - . = ..() - src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) - src.update_icon() - -/obj/machinery/portable_atmospherics/canister/air/Initialize() - . = ..() - var/list/air_mix = StandardAirMix() - src.air_contents.adjust_multi("oxygen", air_mix["oxygen"], "nitrogen", air_mix["nitrogen"]) - - src.update_icon() - -// 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/Initialize() - . = ..() - src.air_contents.adjust_gas("nitrogen", MolesForPressure()) - src.update_icon() - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide/engine_setup/Initialize() - . = ..() - src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) - src.update_icon() - -/obj/machinery/portable_atmospherics/canister/phoron/engine_setup/Initialize() - . = ..() - src.air_contents.adjust_gas("phoron", MolesForPressure()) - src.update_icon() - -/obj/machinery/portable_atmospherics/canister/air/cold/Initialize() - . = ..() - src.air_contents.temperature = 283 - src.update_icon() +/obj/machinery/portable_atmospherics/canister + name = "canister" + desc = "Holds gas. Has a built-in valve to allow for filling portable tanks." + icon = 'icons/obj/atmos.dmi' + icon_state = "yellow" + density = 1 + var/health = 100.0 + flags = CONDUCT + w_class = 5 + + 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 + var/temperature_resistance = 1000 + T0C + volume = 1000 + use_power = 0 + 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/sleeping_agent + 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/nitrogen/prechilled + name = "Canister: \[N2 (Cooling)\]" + +/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 = "orange" + canister_color = "orange" + 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 = "orange" + canister_color = "orange" +/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/sleeping_agent + 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) + cut_overlays() + 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.loc, 'sound/effects/spray.ogg', 10, 1, -3) + src.density = 0 + update_icon() + + if (src.holding) + src.holding.forceMove(src.loc) + src.holding = null + + return 1 + else + return 1 + +/obj/machinery/portable_atmospherics/canister/machinery_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/AltClick(var/mob/abstract/observer/admin) + if (istype(admin)) + if (admin.client && admin.client.holder && ((R_MOD|R_ADMIN) & admin.client.holder.rights)) + if (valve_open) + if (holding) + release_log += "Valve was closed by [key_name(admin)] (aghost), stopping the transfer into the [holding]
    " + else + release_log += "Valve was closed by [key_name(admin)] (aghost), stopping the transfer into the air
    " + else + if (alert(admin, "The release valve is currently closed. Do you want to open it?", "Open the valve?", "Yes", "No") == "No") + return + + if (holding) + release_log += "Valve was opened by [key_name(admin)] (aghost), starting the transfer into the [holding]
    " + else + release_log += "Valve was opened by [key_name(admin)] (aghost), starting the transfer into the air
    " + log_open(admin) + valve_open = !valve_open + +/obj/machinery/portable_atmospherics/canister/attackby(var/obj/item/W as obj, var/mob/user as mob) + if(!W.iswrench() && !istype(W, /obj/item/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/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 + + ..() + + SSnanoui.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 src.ui_interact(user) + +/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if (src.destroyed) + return + + // this is the data which will be sent to the ui + var/data[0] + data["name"] = name + data["canLabel"] = can_label ? 1 : 0 + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(release_pressure ? release_pressure : 0) + data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) + data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) + data["valveOpen"] = valve_open ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if (holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure())) + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400) + // when the ui is first opened this is the data it will use + ui.set_initial_data(data) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/portable_atmospherics/canister/Topic(href, href_list) + + //Do not use "if(..()) return" here, canisters will stop working in unpowered areas like space or on the derelict. // yeah but without SOME sort of Topic check any dick can mess with them via exploits as he pleases -walter0o + //First comment might be outdated. + if (!istype(src.loc, /turf)) + return 0 + + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) // exploit protection -walter0o + usr << browse(null, "window=canister") + onclose(usr, "canister") + return + + if(href_list["toggle"]) + 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 + + if (href_list["remove_tank"]) + 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/tank)) + holding.manipulated_by = usr.real_name + holding.forceMove(loc) + holding = null + + if (href_list["pressure_adj"]) + var/diff = text2num(href_list["pressure_adj"]) + if(diff > 0) + release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff) + else + release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff) + + if (href_list["relabel"]) + if (can_label) + var/list/colors = list( + "\[N2O\]" = "redws", + "\[N2\]" = "red", + "\[O2\]" = "blue", + "\[Phoron\]" = "orange", + "\[CO2\]" = "black", + "\[Air\]" = "grey", + "\[CAUTION\]" = "yellow" + ) + var/label = input("Choose canister label", "Gas canister") as null|anything in colors + if (label) + src.canister_color = colors[label] + src.icon_state = colors[label] + src.name = "Canister: [label]" + + src.add_fingerprint(usr) + update_icon() + + return 1 + +/obj/machinery/portable_atmospherics/canister/phoron/Initialize() + . = ..() + + src.air_contents.adjust_gas("phoron", MolesForPressure()) + src.update_icon() + +/obj/machinery/portable_atmospherics/canister/oxygen/Initialize() + . = ..() + + src.air_contents.adjust_gas("oxygen", MolesForPressure()) + src.update_icon() + +/obj/machinery/portable_atmospherics/canister/oxygen/prechilled/Initialize() + . = ..() + src.air_contents.temperature = 80 + src.update_icon() + +/obj/machinery/portable_atmospherics/canister/sleeping_agent/Initialize() + . = ..() + + air_contents.adjust_gas("sleeping_agent", MolesForPressure()) + src.update_icon() + +//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/sleeping_agent/roomfiller/Initialize() + . = ..() + air_contents.gas["sleeping_agent"] = 9*4000 + spawn(10) + var/turf/simulated/location = src.loc + if (istype(src.loc)) + while (!location.air) + sleep(10) + location.assume_air(air_contents) + air_contents = new + +/obj/machinery/portable_atmospherics/canister/nitrogen/Initialize() + . = ..() + src.air_contents.adjust_gas("nitrogen", MolesForPressure()) + src.update_icon() + +/obj/machinery/portable_atmospherics/canister/nitrogen/prechilled/Initialize() + . = ..() + src.air_contents.temperature = 80 + src.update_icon() + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide/Initialize() + . = ..() + src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) + src.update_icon() + +/obj/machinery/portable_atmospherics/canister/air/Initialize() + . = ..() + var/list/air_mix = StandardAirMix() + src.air_contents.adjust_multi("oxygen", air_mix["oxygen"], "nitrogen", air_mix["nitrogen"]) + + src.update_icon() + +// 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/Initialize() + . = ..() + src.air_contents.adjust_gas("nitrogen", MolesForPressure()) + src.update_icon() + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide/engine_setup/Initialize() + . = ..() + src.air_contents.adjust_gas("carbon_dioxide", MolesForPressure()) + src.update_icon() + +/obj/machinery/portable_atmospherics/canister/phoron/engine_setup/Initialize() + . = ..() + src.air_contents.adjust_gas("phoron", MolesForPressure()) + src.update_icon() + +/obj/machinery/portable_atmospherics/canister/air/cold/Initialize() + . = ..() + src.air_contents.temperature = 283 + src.update_icon() diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index ebcb661d6b9..d9e20c4fb04 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -1,115 +1,115 @@ -/obj/machinery/meter - name = "meter" - desc = "It measures something." - icon = 'icons/obj/meter.dmi' - icon_state = "meterX" - var/obj/machinery/atmospherics/pipe/target = null - anchored = 1.0 - power_channel = ENVIRON - var/frequency = 0 - var/id - use_power = 1 - idle_power_usage = 15 - - -/obj/machinery/meter/Initialize() - . = ..() - if (!target) - src.target = locate(/obj/machinery/atmospherics/pipe) in loc - -/obj/machinery/meter/machinery_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 = SSradio.return_frequency(frequency) - - if(!radio_connection) return - - var/datum/signal/signal = new - signal.source = src - signal.transmission_method = 1 - 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) - var/t = "A gas flow meter. " - - if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/abstract))) - t += "You are too far away to read it." - - else if(stat & (NOPOWER|BROKEN)) - t += "The display is off." - - else if(src.target) - var/datum/gas_mixture/environment = target.return_air() - if(environment) - t += "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 - t += "The sensor error light is blinking." - else - t += "The connect error light is blinking." - - to_chat(user, t) - -/obj/machinery/meter/Click() - - if(istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine - usr.examinate(src) - return 1 - - return ..() - -/obj/machinery/meter/attackby(var/obj/item/W as obj, var/mob/user as mob) - if (!W.iswrench()) - return ..() - playsound(src.loc, 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(src.loc) - qdel(src) - -// TURF METER - REPORTS A TILE'S AIR CONTENTS - -/obj/machinery/meter/turf/Initialize() - . = ..() - src.target = loc - if (!target) - src.target = loc - -/obj/machinery/meter/turf/attackby(var/obj/item/W as obj, var/mob/user as mob) - return +/obj/machinery/meter + name = "meter" + desc = "It measures something." + icon = 'icons/obj/meter.dmi' + icon_state = "meterX" + var/obj/machinery/atmospherics/pipe/target = null + anchored = 1.0 + power_channel = ENVIRON + var/frequency = 0 + var/id + use_power = 1 + idle_power_usage = 15 + + +/obj/machinery/meter/Initialize() + . = ..() + if (!target) + src.target = locate(/obj/machinery/atmospherics/pipe) in loc + +/obj/machinery/meter/machinery_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 = SSradio.return_frequency(frequency) + + if(!radio_connection) return + + var/datum/signal/signal = new + signal.source = src + signal.transmission_method = 1 + 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) + var/t = "A gas flow meter. " + + if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/abstract))) + t += "You are too far away to read it." + + else if(stat & (NOPOWER|BROKEN)) + t += "The display is off." + + else if(src.target) + var/datum/gas_mixture/environment = target.return_air() + if(environment) + t += "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 + t += "The sensor error light is blinking." + else + t += "The connect error light is blinking." + + to_chat(user, t) + +/obj/machinery/meter/Click() + + if(istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine + usr.examinate(src) + return 1 + + return ..() + +/obj/machinery/meter/attackby(var/obj/item/W as obj, var/mob/user as mob) + if (!W.iswrench()) + return ..() + playsound(src.loc, 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(src.loc) + qdel(src) + +// TURF METER - REPORTS A TILE'S AIR CONTENTS + +/obj/machinery/meter/turf/Initialize() + . = ..() + src.target = loc + if (!target) + src.target = loc + +/obj/machinery/meter/turf/attackby(var/obj/item/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 4573402ca9d..f531a66b02e 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -1,197 +1,197 @@ -/obj/machinery/portable_atmospherics - name = "atmoalter" - use_power = 0 - var/datum/gas_mixture/air_contents = new - - var/obj/machinery/atmospherics/portables_connector/connected_port - var/obj/item/tank/holding - - var/volume = 0 - var/destroyed = 0 - - var/start_pressure = ONE_ATMOSPHERE - var/maximum_pressure = 90 * ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/Destroy() - qdel(air_contents) - qdel(holding) - return ..() - -/obj/machinery/portable_atmospherics/Initialize() - . = ..() - - air_contents.volume = volume - air_contents.temperature = T20C - - var/obj/machinery/atmospherics/portables_connector/port = locate() in loc - if(port) - connect(port) - update_icon() - -/obj/machinery/portable_atmospherics/machinery_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/Destroy() - qdel(air_contents) - - return ..() - -/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 = 1 //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 = 0 - - 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/W as obj, var/mob/user as mob) - if ((istype(W, /obj/item/tank) && !( src.destroyed ))) - if (src.holding) - return - var/obj/item/tank/T = W - user.drop_from_inventory(T,src) - src.holding = T - update_icon() - return - - else if (W.iswrench()) - if(connected_port) - disconnect() - to_chat(user, "You disconnect \the [src] from the port.") - update_icon() - 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() - return - else - to_chat(user, "\The [src] failed to connect to the port.") - return - else - to_chat(user, "Nothing happens.") - return - - else if ((istype(W, /obj/item/device/analyzer)) && Adjacent(user)) - var/obj/item/device/analyzer/A = W - A.analyze_gases(src, user) - return - - return - - - -/obj/machinery/portable_atmospherics/powered - var/power_rating - var/power_losses - var/last_power_draw = 0 - var/obj/item/cell/cell - has_special_power_checks = 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(istype(I, /obj/item/cell)) - if(cell) - to_chat(user, "There is already a power cell installed.") - return - - var/obj/item/cell/C = I - - user.drop_from_inventory(C,src) - C.add_fingerprint(user) - cell = C - 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.isscrewdriver()) - 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].") - cell.add_fingerprint(user) - cell.forceMove(src.loc) - cell = null - power_change() - return - ..() - -/obj/machinery/portable_atmospherics/proc/log_open(var/mob/user) - if(air_contents.gas.len == 0) - return - - var/gases = "" - for(var/gas in air_contents.gas) - if(gases) - gases += ", [gas]" - else - gases = gas - - if (!user && usr) - user = usr - - log_admin("[user] ([user.ckey]) opened '[src.name]' containing [gases].", ckey=key_name(user)) - message_admins("[key_name_admin(user)] opened '[src.name]' containing [gases]. (JMP)") +/obj/machinery/portable_atmospherics + name = "atmoalter" + use_power = 0 + var/datum/gas_mixture/air_contents = new + + var/obj/machinery/atmospherics/portables_connector/connected_port + var/obj/item/tank/holding + + var/volume = 0 + var/destroyed = 0 + + var/start_pressure = ONE_ATMOSPHERE + var/maximum_pressure = 90 * ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/Destroy() + qdel(air_contents) + qdel(holding) + return ..() + +/obj/machinery/portable_atmospherics/Initialize() + . = ..() + + air_contents.volume = volume + air_contents.temperature = T20C + + var/obj/machinery/atmospherics/portables_connector/port = locate() in loc + if(port) + connect(port) + update_icon() + +/obj/machinery/portable_atmospherics/machinery_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/Destroy() + qdel(air_contents) + + return ..() + +/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 = 1 //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 = 0 + + 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/W as obj, var/mob/user as mob) + if ((istype(W, /obj/item/tank) && !( src.destroyed ))) + if (src.holding) + return + var/obj/item/tank/T = W + user.drop_from_inventory(T,src) + src.holding = T + update_icon() + return + + else if (W.iswrench()) + if(connected_port) + disconnect() + to_chat(user, "You disconnect \the [src] from the port.") + update_icon() + 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() + return + else + to_chat(user, "\The [src] failed to connect to the port.") + return + else + to_chat(user, "Nothing happens.") + return + + else if ((istype(W, /obj/item/device/analyzer)) && Adjacent(user)) + var/obj/item/device/analyzer/A = W + A.analyze_gases(src, user) + return + + return + + + +/obj/machinery/portable_atmospherics/powered + var/power_rating + var/power_losses + var/last_power_draw = 0 + var/obj/item/cell/cell + has_special_power_checks = 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(istype(I, /obj/item/cell)) + if(cell) + to_chat(user, "There is already a power cell installed.") + return + + var/obj/item/cell/C = I + + user.drop_from_inventory(C,src) + C.add_fingerprint(user) + cell = C + 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.isscrewdriver()) + 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].") + cell.add_fingerprint(user) + cell.forceMove(src.loc) + cell = null + power_change() + return + ..() + +/obj/machinery/portable_atmospherics/proc/log_open(var/mob/user) + if(air_contents.gas.len == 0) + return + + var/gases = "" + for(var/gas in air_contents.gas) + if(gases) + gases += ", [gas]" + else + gases = gas + + if (!user && usr) + user = usr + + log_admin("[user] ([user.ckey]) opened '[src.name]' containing [gases].", ckey=key_name(user)) + message_admins("[key_name_admin(user)] opened '[src.name]' containing [gases]. (JMP)") diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 6f9b34f75b4..02f281d0215 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -1,170 +1,170 @@ -/obj/machinery/portable_atmospherics/powered/pump - name = "portable air pump" - desc = "Used to fill or drain rooms without differentiating between gasses." - - icon = 'icons/obj/atmos.dmi' - icon_state = "psiphon:0" - density = 1 - w_class = 3 - - 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/Initialize() - . = ..() - cell = new/obj/item/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/machinery_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) - ui_interact(user) - -/obj/machinery/portable_atmospherics/powered/pump/ui_interact(mob/user, ui_key = "rcon", datum/nanoui/ui=null, force_open=1) - var/list/data[0] - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) - data["targetpressure"] = round(target_pressure) - data["pump_dir"] = direction_out - data["minpressure"] = round(pressuremin) - data["maxpressure"] = round(pressuremax) - data["powerDraw"] = round(last_power_draw) - data["cellCharge"] = cell ? cell.charge : 0 - data["cellMaxCharge"] = cell ? cell.maxcharge : 1 - data["on"] = on ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if (holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 410, state = physical_state) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/portable_atmospherics/powered/pump/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - . = 1 - if(href_list["direction"]) - direction_out = !direction_out - . = 1 - if (href_list["remove_tank"]) - if(holding) - holding.forceMove(loc) - holding = null - . = 1 - if (href_list["pressure_adj"]) - var/diff = text2num(href_list["pressure_adj"]) - target_pressure = min(10*ONE_ATMOSPHERE, max(0, target_pressure+diff)) - . = 1 - - if(.) - update_icon() +/obj/machinery/portable_atmospherics/powered/pump + name = "portable air pump" + desc = "Used to fill or drain rooms without differentiating between gasses." + + icon = 'icons/obj/atmos.dmi' + icon_state = "psiphon:0" + density = 1 + w_class = 3 + + 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/Initialize() + . = ..() + cell = new/obj/item/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/machinery_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) + ui_interact(user) + +/obj/machinery/portable_atmospherics/powered/pump/ui_interact(mob/user, ui_key = "rcon", datum/nanoui/ui=null, force_open=1) + var/list/data[0] + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + data["targetpressure"] = round(target_pressure) + data["pump_dir"] = direction_out + data["minpressure"] = round(pressuremin) + data["maxpressure"] = round(pressuremax) + data["powerDraw"] = round(last_power_draw) + data["cellCharge"] = cell ? cell.charge : 0 + data["cellMaxCharge"] = cell ? cell.maxcharge : 1 + data["on"] = on ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if (holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 410, state = physical_state) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/portable_atmospherics/powered/pump/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + . = 1 + if(href_list["direction"]) + direction_out = !direction_out + . = 1 + if (href_list["remove_tank"]) + if(holding) + holding.forceMove(loc) + holding = null + . = 1 + if (href_list["pressure_adj"]) + var/diff = text2num(href_list["pressure_adj"]) + target_pressure = min(10*ONE_ATMOSPHERE, max(0, target_pressure+diff)) + . = 1 + + if(.) + update_icon() diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 332cd2bf335..eaf113d0bb0 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -1,241 +1,241 @@ -/obj/machinery/portable_atmospherics/powered/scrubber - name = "Portable Air Scrubber" - desc = "Scrubs contaminants from the local atmosphere or the connected portable tank." - - icon = 'icons/obj/atmos.dmi' - icon_state = "pscrubber:0" - density = 1 - w_class = 3 - - 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", "sleeping_agent") - -/obj/machinery/portable_atmospherics/powered/scrubber/Initialize() - . = ..() - cell = new/obj/item/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/machinery_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) - ui_interact(user) - return - -/obj/machinery/portable_atmospherics/powered/scrubber/ui_interact(mob/user, ui_key = "rcon", datum/nanoui/ui=null, force_open=1) - var/list/data[0] - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = 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 - data["on"] = on ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if (holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "portscrubber.tmpl", "Portable Scrubber", 480, 400, state = physical_state) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - - -/obj/machinery/portable_atmospherics/powered/scrubber/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - . = 1 - if (href_list["remove_tank"]) - if(holding) - holding.forceMove(loc) - holding = null - . = 1 - if (href_list["volume_adj"]) - var/diff = text2num(href_list["volume_adj"]) - volume_rate = Clamp(volume_rate+diff, minrate, maxrate) - . = 1 - update_icon() - - -//Huge scrubber -/obj/machinery/portable_atmospherics/powered/scrubber/huge - name = "Huge Air Scrubber" - icon_state = "scrubber:0" - anchored = 1 - volume = 50000 - volume_rate = 5000 - - use_power = 1 - idle_power_usage = 500 //internal circuitry, friction losses and stuff - active_power_usage = 100000 //100 kW ~ 135 HP - - var/global/gid = 1 - var/id = 0 - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/Initialize() - . = ..() - QDEL_NULL(cell) - - id = gid - gid++ - - name = "[name] (ID [id])" - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/attack_hand(var/mob/user as mob) - to_chat(usr, "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/machinery_process() - if(!on || (stat & (NOPOWER|BROKEN))) - update_use_power(0) - last_flow_rate = 0 - last_power_draw = 0 - return 0 - - 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.iswrench()) - if(on) - to_chat(user, "Turn \the [src] off first!") - return - - anchored = !anchored - playsound(src.loc, I.usesound, 50, 1) - to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") - - return - - //doesn't use power cells - if(istype(I, /obj/item/cell)) - return - if (I.isscrewdriver()) - return - - //doesn't hold tanks - if(istype(I, /obj/item/tank)) - return - - ..() - - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary - name = "Stationary Air Scrubber" - -/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/attackby(var/obj/item/I as obj, var/mob/user as mob) - if(I.iswrench()) - to_chat(user, "The bolts are too tight for you to unscrew!") - return - - ..() +/obj/machinery/portable_atmospherics/powered/scrubber + name = "Portable Air Scrubber" + desc = "Scrubs contaminants from the local atmosphere or the connected portable tank." + + icon = 'icons/obj/atmos.dmi' + icon_state = "pscrubber:0" + density = 1 + w_class = 3 + + 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", "sleeping_agent") + +/obj/machinery/portable_atmospherics/powered/scrubber/Initialize() + . = ..() + cell = new/obj/item/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/machinery_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) + ui_interact(user) + return + +/obj/machinery/portable_atmospherics/powered/scrubber/ui_interact(mob/user, ui_key = "rcon", datum/nanoui/ui=null, force_open=1) + var/list/data[0] + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = 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 + data["on"] = on ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if (holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "portscrubber.tmpl", "Portable Scrubber", 480, 400, state = physical_state) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + + +/obj/machinery/portable_atmospherics/powered/scrubber/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + . = 1 + if (href_list["remove_tank"]) + if(holding) + holding.forceMove(loc) + holding = null + . = 1 + if (href_list["volume_adj"]) + var/diff = text2num(href_list["volume_adj"]) + volume_rate = Clamp(volume_rate+diff, minrate, maxrate) + . = 1 + update_icon() + + +//Huge scrubber +/obj/machinery/portable_atmospherics/powered/scrubber/huge + name = "Huge Air Scrubber" + icon_state = "scrubber:0" + anchored = 1 + volume = 50000 + volume_rate = 5000 + + use_power = 1 + idle_power_usage = 500 //internal circuitry, friction losses and stuff + active_power_usage = 100000 //100 kW ~ 135 HP + + var/global/gid = 1 + var/id = 0 + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/Initialize() + . = ..() + QDEL_NULL(cell) + + id = gid + gid++ + + name = "[name] (ID [id])" + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/attack_hand(var/mob/user as mob) + to_chat(usr, "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/machinery_process() + if(!on || (stat & (NOPOWER|BROKEN))) + update_use_power(0) + last_flow_rate = 0 + last_power_draw = 0 + return 0 + + 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.iswrench()) + if(on) + to_chat(user, "Turn \the [src] off first!") + return + + anchored = !anchored + playsound(src.loc, I.usesound, 50, 1) + to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") + + return + + //doesn't use power cells + if(istype(I, /obj/item/cell)) + return + if (I.isscrewdriver()) + return + + //doesn't hold tanks + if(istype(I, /obj/item/tank)) + return + + ..() + + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary + name = "Stationary Air Scrubber" + +/obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/attackby(var/obj/item/I as obj, var/mob/user as mob) + if(I.iswrench()) + 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 76db6c342d1..bc1abb77795 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 = 0 - anchored=1 - - var/on = 0 - var/volume_rate = 800 - -/obj/machinery/zvent/machinery_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 = 0 + anchored=1 + + var/on = 0 + var/volume_rate = 800 + +/obj/machinery/zvent/machinery_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 4968672d11e..4eec54a18c9 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -1,306 +1,306 @@ -/obj/machinery/autolathe - name = "autolathe" - desc = "It produces items using metal and glass." - icon_state = "autolathe" - density = 1 - anchored = 1 - use_power = 1 - idle_power_usage = 10 - active_power_usage = 2000 - - var/list/machine_recipes - var/list/stored_material = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0) - var/list/storage_capacity = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0) - var/show_category = "All" - - 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 - - component_types = list( - /obj/item/circuitboard/autolathe, - /obj/item/stock_parts/matter_bin = 3, - /obj/item/stock_parts/manipulator, - /obj/item/stock_parts/console_screen - ) - -/obj/machinery/autolathe/Initialize() - . = ..() - wires = new(src) - -/obj/machinery/autolathe/Destroy() - qdel(wires) - wires = null - return ..() - -/obj/machinery/autolathe/proc/update_recipe_list() - if(!machine_recipes) - machine_recipes = autolathe_recipes - -/obj/machinery/autolathe/interact(mob/user as mob) - - update_recipe_list() - - if(..() || (disabled && !panel_open)) - to_chat(user, "\The [src] is disabled!") - return - - if(shocked) - shock(user, 50) - - var/dat = "

    Autolathe Control Panel


    " - - if(!disabled) - dat += "" - var/material_top = "" - var/material_bottom = "" - - for(var/material in stored_material) - material_top += "" - material_bottom += "" - - dat += "[material_top][material_bottom]
    [material][stored_material[material]]/[storage_capacity[material]]

    " - dat += "

    Printable Designs

    Showing: [show_category].

    " - - var/index = 0 - for(var/datum/autolathe/recipe/R in machine_recipes) - index++ - if(R.hidden && !hacked || (show_category != "All" && show_category != R.category)) - continue - var/can_make = 1 - var/material_string = "" - var/multiplier_string = "" - var/max_sheets - var/comma - if(!R.resources || !R.resources.len) - material_string = "No resources required." - else - //Make sure it's buildable and list requires resources. - for(var/material in R.resources) - var/sheets = round(stored_material[material]/round(R.resources[material]*mat_efficiency)) - if(isnull(max_sheets) || max_sheets > sheets) - max_sheets = sheets - if(!isnull(stored_material[material]) && stored_material[material] < round(R.resources[material]*mat_efficiency)) - can_make = 0 - if(!comma) - comma = 1 - else - material_string += ", " - material_string += "[round(R.resources[material] * mat_efficiency)] [material]" - material_string += ".
    " - //Build list of multipliers for sheets. - if(R.is_stack) - if(max_sheets && max_sheets > 0) - var/obj/item/stack/R_stack = R.path - max_sheets = min(max_sheets, initial(R_stack.max_amount)) - multiplier_string += "
    " - for(var/i = 5;i*" : ""][can_make ? "" : ""][R.name][can_make ? "" : ""][R.hidden ? "*" : ""][multiplier_string]" - - dat += "
    [material_string]

    " - //Hacking. - if(panel_open) - dat += "

    Maintenance Panel

    " - dat += wires.GetInteractWindow() - - dat += "
    " - - user << browse(dat, "window=autolathe") - onclose(user, "autolathe") - -/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)) - updateUsrDialog() - 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.ismultitool() || O.iswirecutter()) - attack_hand(user) - return - - if(O.loc != user && !(istype(O,/obj/item/stack))) - return 0 - - if(is_robot_module(O)) - return 0 - - //Resources are being loaded. - var/obj/item/eating = O - if(!eating.matter) - to_chat(user, "\The [eating] does not contain significant amounts of useful materials and cannot be accepted.") - return - - var/filltype = 0 // Used to determine message. - var/total_used = 0 // Amount of material used. - var/mass_per_sheet = 0 // Amount of material constituting one sheet. - - for(var/material in eating.matter) - - if(isnull(stored_material[material]) || isnull(storage_capacity[material])) - continue - - if(stored_material[material] >= storage_capacity[material]) - continue - - var/total_material = eating.matter[material] - - //If it's a stack, we eat multiple sheets. - if(istype(eating,/obj/item/stack)) - var/obj/item/stack/stack = eating - total_material *= stack.get_amount() - - if(stored_material[material] + total_material > storage_capacity[material]) - total_material = storage_capacity[material] - stored_material[material] - filltype = 1 - else - filltype = 2 - - stored_material[material] += total_material - total_used += total_material - mass_per_sheet += eating.matter[material] - - if(!filltype) - to_chat(user, "\The [src] is full. Please remove material from the autolathe in order to insert more.") - return - else if(filltype == 1) - to_chat(user, "You fill \the [src] to capacity with \the [eating].") - else - to_chat(user, "You fill \the [src] with \the [eating].") - - flick("autolathe_o", src) // Plays metal insertion animation. Work out a good way to work out a fitting animation. ~Z - - if(istype(eating,/obj/item/stack)) - var/obj/item/stack/stack = eating - stack.use(max(1, round(total_used/mass_per_sheet))) // Always use at least 1 to prevent infinite materials. - else - user.remove_from_mob(O) - qdel(O) - - updateUsrDialog() - return - -/obj/machinery/autolathe/attack_hand(mob/user as mob) - user.set_machine(src) - interact(user) - -/obj/machinery/autolathe/Topic(href, href_list) - - if(..()) - return - - usr.set_machine(src) - add_fingerprint(usr) - - if(busy) - to_chat(usr, "The autolathe is busy. Please wait for completion of previous operation.") - return - - if(href_list["change_category"]) - - var/choice = input("Which category do you wish to display?") as null|anything in autolathe_categories+"All" - if(!choice) return - show_category = choice - - if(href_list["make"] && machine_recipes) - - var/index = text2num(href_list["make"]) - var/multiplier = text2num(href_list["multiplier"]) - var/datum/autolathe/recipe/making - - if(index > 0 && index <= machine_recipes.len) - making = machine_recipes[index] - - //Exploit detection, not sure if necessary after rewrite. - if(!making || multiplier < 0 || multiplier > 100) - var/turf/exploit_loc = get_turf(usr) - message_admins("[key_name_admin(usr)] tried to exploit an autolathe to duplicate an item! ([exploit_loc ? "JMP" : "null"])", 0) - log_admin("EXPLOIT : [key_name(usr)] tried to exploit an autolathe to duplicate an item!",ckey=key_name(usr)) - return - - busy = 1 - update_use_power(2) - - //Check if we still have the materials. - for(var/material in making.resources) - if(!isnull(stored_material[material])) - if(stored_material[material] < round(making.resources[material] * mat_efficiency) * multiplier) - return - - //Consume materials. - for(var/material in making.resources) - if(!isnull(stored_material[material])) - stored_material[material] = max(0, stored_material[material] - round(making.resources[material] * mat_efficiency) * multiplier) - - //Fancy autolathe animation. - flick("autolathe_n", src) - - sleep(build_time) - - busy = 0 - update_use_power(1) - - //Sanity check. - if(!making || !src) return - - //Create the desired item. - var/obj/item/I = new making.path(loc) - I.Created() - if(multiplier > 1 && istype(I, /obj/item/stack)) - var/obj/item/stack/S = I - S.amount = multiplier - - updateUsrDialog() - -/obj/machinery/autolathe/update_icon() - icon_state = (panel_open ? "autolathe_t" : "autolathe") - -//Updates overall lathe storage size. -/obj/machinery/autolathe/RefreshParts() - ..() - var/mb_rating = 0 - var/man_rating = 0 - for(var/obj/item/stock_parts/matter_bin/MB in component_parts) - mb_rating += MB.rating - for(var/obj/item/stock_parts/manipulator/M in component_parts) - man_rating += M.rating - - storage_capacity[DEFAULT_WALL_MATERIAL] = mb_rating * 25000 - storage_capacity["glass"] = mb_rating * 12500 - 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.8. Maximum rating of parts is 3 - -/obj/machinery/autolathe/dismantle() - - for(var/mat in stored_material) - var/material/M = get_material_by_name(mat) - if(!istype(M)) - continue - var/obj/item/stack/material/S = new M.stack_type(get_turf(src)) - if(stored_material[mat] > S.perunit) - S.amount = round(stored_material[mat] / S.perunit) - else - qdel(S) - ..() - return 1 +/obj/machinery/autolathe + name = "autolathe" + desc = "It produces items using metal and glass." + icon_state = "autolathe" + density = 1 + anchored = 1 + use_power = 1 + idle_power_usage = 10 + active_power_usage = 2000 + + var/list/machine_recipes + var/list/stored_material = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0) + var/list/storage_capacity = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0) + var/show_category = "All" + + 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 + + component_types = list( + /obj/item/circuitboard/autolathe, + /obj/item/stock_parts/matter_bin = 3, + /obj/item/stock_parts/manipulator, + /obj/item/stock_parts/console_screen + ) + +/obj/machinery/autolathe/Initialize() + . = ..() + wires = new(src) + +/obj/machinery/autolathe/Destroy() + qdel(wires) + wires = null + return ..() + +/obj/machinery/autolathe/proc/update_recipe_list() + if(!machine_recipes) + machine_recipes = autolathe_recipes + +/obj/machinery/autolathe/interact(mob/user as mob) + + update_recipe_list() + + if(..() || (disabled && !panel_open)) + to_chat(user, "\The [src] is disabled!") + return + + if(shocked) + shock(user, 50) + + var/dat = "

    Autolathe Control Panel


    " + + if(!disabled) + dat += "" + var/material_top = "" + var/material_bottom = "" + + for(var/material in stored_material) + material_top += "" + material_bottom += "" + + dat += "[material_top][material_bottom]
    [material][stored_material[material]]/[storage_capacity[material]]

    " + dat += "

    Printable Designs

    Showing: [show_category].

    " + + var/index = 0 + for(var/datum/autolathe/recipe/R in machine_recipes) + index++ + if(R.hidden && !hacked || (show_category != "All" && show_category != R.category)) + continue + var/can_make = 1 + var/material_string = "" + var/multiplier_string = "" + var/max_sheets + var/comma + if(!R.resources || !R.resources.len) + material_string = "No resources required." + else + //Make sure it's buildable and list requires resources. + for(var/material in R.resources) + var/sheets = round(stored_material[material]/round(R.resources[material]*mat_efficiency)) + if(isnull(max_sheets) || max_sheets > sheets) + max_sheets = sheets + if(!isnull(stored_material[material]) && stored_material[material] < round(R.resources[material]*mat_efficiency)) + can_make = 0 + if(!comma) + comma = 1 + else + material_string += ", " + material_string += "[round(R.resources[material] * mat_efficiency)] [material]" + material_string += ".
    " + //Build list of multipliers for sheets. + if(R.is_stack) + if(max_sheets && max_sheets > 0) + var/obj/item/stack/R_stack = R.path + max_sheets = min(max_sheets, initial(R_stack.max_amount)) + multiplier_string += "
    " + for(var/i = 5;i*" : ""][can_make ? "" : ""][R.name][can_make ? "" : ""][R.hidden ? "*" : ""][multiplier_string]" + + dat += "
    [material_string]

    " + //Hacking. + if(panel_open) + dat += "

    Maintenance Panel

    " + dat += wires.GetInteractWindow() + + dat += "
    " + + user << browse(dat, "window=autolathe") + onclose(user, "autolathe") + +/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)) + updateUsrDialog() + 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.ismultitool() || O.iswirecutter()) + attack_hand(user) + return + + if(O.loc != user && !(istype(O,/obj/item/stack))) + return 0 + + if(is_robot_module(O)) + return 0 + + //Resources are being loaded. + var/obj/item/eating = O + if(!eating.matter) + to_chat(user, "\The [eating] does not contain significant amounts of useful materials and cannot be accepted.") + return + + var/filltype = 0 // Used to determine message. + var/total_used = 0 // Amount of material used. + var/mass_per_sheet = 0 // Amount of material constituting one sheet. + + for(var/material in eating.matter) + + if(isnull(stored_material[material]) || isnull(storage_capacity[material])) + continue + + if(stored_material[material] >= storage_capacity[material]) + continue + + var/total_material = eating.matter[material] + + //If it's a stack, we eat multiple sheets. + if(istype(eating,/obj/item/stack)) + var/obj/item/stack/stack = eating + total_material *= stack.get_amount() + + if(stored_material[material] + total_material > storage_capacity[material]) + total_material = storage_capacity[material] - stored_material[material] + filltype = 1 + else + filltype = 2 + + stored_material[material] += total_material + total_used += total_material + mass_per_sheet += eating.matter[material] + + if(!filltype) + to_chat(user, "\The [src] is full. Please remove material from the autolathe in order to insert more.") + return + else if(filltype == 1) + to_chat(user, "You fill \the [src] to capacity with \the [eating].") + else + to_chat(user, "You fill \the [src] with \the [eating].") + + flick("autolathe_o", src) // Plays metal insertion animation. Work out a good way to work out a fitting animation. ~Z + + if(istype(eating,/obj/item/stack)) + var/obj/item/stack/stack = eating + stack.use(max(1, round(total_used/mass_per_sheet))) // Always use at least 1 to prevent infinite materials. + else + user.remove_from_mob(O) + qdel(O) + + updateUsrDialog() + return + +/obj/machinery/autolathe/attack_hand(mob/user as mob) + user.set_machine(src) + interact(user) + +/obj/machinery/autolathe/Topic(href, href_list) + + if(..()) + return + + usr.set_machine(src) + add_fingerprint(usr) + + if(busy) + to_chat(usr, "The autolathe is busy. Please wait for completion of previous operation.") + return + + if(href_list["change_category"]) + + var/choice = input("Which category do you wish to display?") as null|anything in autolathe_categories+"All" + if(!choice) return + show_category = choice + + if(href_list["make"] && machine_recipes) + + var/index = text2num(href_list["make"]) + var/multiplier = text2num(href_list["multiplier"]) + var/datum/autolathe/recipe/making + + if(index > 0 && index <= machine_recipes.len) + making = machine_recipes[index] + + //Exploit detection, not sure if necessary after rewrite. + if(!making || multiplier < 0 || multiplier > 100) + var/turf/exploit_loc = get_turf(usr) + message_admins("[key_name_admin(usr)] tried to exploit an autolathe to duplicate an item! ([exploit_loc ? "JMP" : "null"])", 0) + log_admin("EXPLOIT : [key_name(usr)] tried to exploit an autolathe to duplicate an item!",ckey=key_name(usr)) + return + + busy = 1 + update_use_power(2) + + //Check if we still have the materials. + for(var/material in making.resources) + if(!isnull(stored_material[material])) + if(stored_material[material] < round(making.resources[material] * mat_efficiency) * multiplier) + return + + //Consume materials. + for(var/material in making.resources) + if(!isnull(stored_material[material])) + stored_material[material] = max(0, stored_material[material] - round(making.resources[material] * mat_efficiency) * multiplier) + + //Fancy autolathe animation. + flick("autolathe_n", src) + + sleep(build_time) + + busy = 0 + update_use_power(1) + + //Sanity check. + if(!making || !src) return + + //Create the desired item. + var/obj/item/I = new making.path(loc) + I.Created() + if(multiplier > 1 && istype(I, /obj/item/stack)) + var/obj/item/stack/S = I + S.amount = multiplier + + updateUsrDialog() + +/obj/machinery/autolathe/update_icon() + icon_state = (panel_open ? "autolathe_t" : "autolathe") + +//Updates overall lathe storage size. +/obj/machinery/autolathe/RefreshParts() + ..() + var/mb_rating = 0 + var/man_rating = 0 + for(var/obj/item/stock_parts/matter_bin/MB in component_parts) + mb_rating += MB.rating + for(var/obj/item/stock_parts/manipulator/M in component_parts) + man_rating += M.rating + + storage_capacity[DEFAULT_WALL_MATERIAL] = mb_rating * 25000 + storage_capacity["glass"] = mb_rating * 12500 + 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.8. Maximum rating of parts is 3 + +/obj/machinery/autolathe/dismantle() + + for(var/mat in stored_material) + var/material/M = get_material_by_name(mat) + if(!istype(M)) + continue + var/obj/item/stack/material/S = new M.stack_type(get_turf(src)) + if(stored_material[mat] > S.perunit) + S.amount = round(stored_material[mat] / S.perunit) + else + qdel(S) + ..() + return 1 diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm index 894fb1b2ec3..b1bc99617ab 100644 --- a/code/game/machinery/bots/bots.dm +++ b/code/game/machinery/bots/bots.dm @@ -1,212 +1,212 @@ -// AI (i.e. game AI, not the AI player) controlled bots - -/obj/machinery/bot - icon = 'icons/obj/aibots.dmi' - layer = MOB_LAYER - light_range = 3 - use_power = 0 - var/obj/item/card/id/botcard // the ID card that the bot "holds" - var/on = 1 - var/health = 0 //do not forget to set health for your bot! - var/maxhealth = 0 - var/fire_dam_coeff = 1.0 - var/brute_dam_coeff = 1.0 - var/open = 0//Maint panel - var/locked = 1 - //var/emagged = 0 //Urist: Moving that var to the general /bot tree as it's used by most bots - -/obj/machinery/bot/proc/turn_on() - if(stat) return 0 - on = 1 - set_light(initial(light_range)) - return 1 - -/obj/machinery/bot/proc/turn_off() - on = 0 - set_light(0) - -/obj/machinery/bot/proc/explode() - qdel(src) - -/obj/machinery/bot/proc/healthcheck() - if (src.health <= 0) - src.explode() - -/obj/machinery/bot/emag_act(var/remaining_charges, var/user) - if(locked && !emagged) - locked = 0 - emagged = 1 - to_chat(user, "You short out [src]'s maintenance hatch lock.") - log_and_message_admins("emagged [src]'s maintenance hatch lock") - return 1 - - if(!locked && open && emagged == 1) - emagged = 2 - log_and_message_admins("emagged [src]'s inner circuits") - return 1 - -/obj/machinery/bot/examine(mob/user) - ..(user) - if (src.health < maxhealth) - if (src.health > maxhealth/3) - to_chat(user, "[src]'s parts look loose.") - else - to_chat(user, "[src]'s parts look very loose!") - return - -/obj/machinery/bot/attackby(obj/item/W as obj, mob/user as mob) - if(W.isscrewdriver()) - if(!locked) - open = !open - to_chat(user, "Maintenance panel is now [src.open ? "opened" : "closed"].") - else if(W.iswelder()) - if(health < maxhealth) - if(open) - health = min(maxhealth, health+10) - user.visible_message("[user] repairs [src]!","You repair [src]!") - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - else - to_chat(user, "Unable to repair with the maintenance panel closed.") - else - to_chat(user, "[src] does not need a repair.") - else - if(hasvar(W,"force") && hasvar(W,"damtype")) - switch(W.damtype) - if("fire") - src.health -= W.force * fire_dam_coeff - if("brute") - src.health -= W.force * brute_dam_coeff - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - ..() - healthcheck() - else - ..() - -/obj/machinery/bot/bullet_act(var/obj/item/projectile/Proj) - if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - return - health -= Proj.damage - ..() - healthcheck() - -/obj/machinery/bot/ex_act(severity) - switch(severity) - if(1.0) - src.explode() - return - if(2.0) - src.health -= rand(5,10)*fire_dam_coeff - src.health -= rand(10,20)*brute_dam_coeff - healthcheck() - return - if(3.0) - if (prob(50)) - src.health -= rand(1,5)*fire_dam_coeff - src.health -= rand(1,5)*brute_dam_coeff - healthcheck() - return - return - -/obj/machinery/bot/emp_act(severity) - var/was_on = on - stat |= EMPED - 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 = 1 - pulse2.set_dir(pick(cardinal)) - - QDEL_IN(pulse2, 10) - - if (on) - turn_off() - addtimer(CALLBACK(src, .proc/post_emp, was_on), severity * 300) - -/obj/machinery/bot/proc/post_emp(was_on) - stat &= ~EMPED - if (was_on) - turn_on() - -/obj/machinery/bot/attack_ai(mob/user as mob) - src.attack_hand(user) - -/obj/machinery/bot/attack_hand(var/mob/living/carbon/human/user) - - if(!istype(user)) - return ..() - - if(user.species.can_shred(user)) - src.health -= rand(15,30)*brute_dam_coeff - src.visible_message("[user] has slashed [src]!") - playsound(src.loc, 'sound/weapons/slice.ogg', 25, 1, -1) - if(prob(10)) - new /obj/effect/decal/cleanable/blood/oil(src.loc) - healthcheck() - -/******************************************************************/ -// Navigation procs -// Used for A-star pathfinding - - -// Returns the surrounding cardinal turfs with open links -// Including through doors openable with the ID -/turf/proc/CardinalTurfsWithAccess(var/obj/item/card/id/ID) - var/L[] = new() - - // for(var/turf/simulated/t in oview(src,1)) - - for(var/d in cardinal) - var/turf/simulated/T = get_step(src, d) - if(istype(T) && !T.density) - if(!LinkBlockedWithAccess(src, T, ID)) - L.Add(T) - return L - - -// Returns true if a link between A and B is blocked -// Movement through doors allowed if ID has access -/proc/LinkBlockedWithAccess(turf/A, turf/B, obj/item/card/id/ID) - - if(A == null || B == null) return 1 - var/adir = get_dir(A,B) - var/rdir = get_dir(B,A) - if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal - var/iStep = get_step(A,adir&(NORTH|SOUTH)) - if(!LinkBlockedWithAccess(A,iStep, ID) && !LinkBlockedWithAccess(iStep,B,ID)) - return 0 - - var/pStep = get_step(A,adir&(EAST|WEST)) - if(!LinkBlockedWithAccess(A,pStep,ID) && !LinkBlockedWithAccess(pStep,B,ID)) - return 0 - return 1 - - if(DirBlockedWithAccess(A,adir, ID)) - return 1 - - if(DirBlockedWithAccess(B,rdir, ID)) - return 1 - - for(var/obj/O in B) - if(O.density && !istype(O, /obj/machinery/door) && !(O.flags & ON_BORDER)) - return 1 - - return 0 - -// Returns true if direction is blocked from loc -// Checks doors against access with given ID -/proc/DirBlockedWithAccess(turf/loc,var/dir,var/obj/item/card/id/ID) - for(var/obj/structure/window/D in loc) - if(!D.density) continue - if(D.dir == SOUTHWEST) return 1 - if(D.dir == dir) return 1 - - for(var/obj/machinery/door/D in loc) - if(!D.density) continue - if(istype(D, /obj/machinery/door/window)) - if( dir & D.dir ) return !D.check_access(ID) - - //if((dir & SOUTH) && (D.dir & (EAST|WEST))) return !D.check_access(ID) - //if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return !D.check_access(ID) - else return !D.check_access(ID) // it's a real, air blocking door - return 0 +// AI (i.e. game AI, not the AI player) controlled bots + +/obj/machinery/bot + icon = 'icons/obj/aibots.dmi' + layer = MOB_LAYER + light_range = 3 + use_power = 0 + var/obj/item/card/id/botcard // the ID card that the bot "holds" + var/on = 1 + var/health = 0 //do not forget to set health for your bot! + var/maxhealth = 0 + var/fire_dam_coeff = 1.0 + var/brute_dam_coeff = 1.0 + var/open = 0//Maint panel + var/locked = 1 + //var/emagged = 0 //Urist: Moving that var to the general /bot tree as it's used by most bots + +/obj/machinery/bot/proc/turn_on() + if(stat) return 0 + on = 1 + set_light(initial(light_range)) + return 1 + +/obj/machinery/bot/proc/turn_off() + on = 0 + set_light(0) + +/obj/machinery/bot/proc/explode() + qdel(src) + +/obj/machinery/bot/proc/healthcheck() + if (src.health <= 0) + src.explode() + +/obj/machinery/bot/emag_act(var/remaining_charges, var/user) + if(locked && !emagged) + locked = 0 + emagged = 1 + to_chat(user, "You short out [src]'s maintenance hatch lock.") + log_and_message_admins("emagged [src]'s maintenance hatch lock") + return 1 + + if(!locked && open && emagged == 1) + emagged = 2 + log_and_message_admins("emagged [src]'s inner circuits") + return 1 + +/obj/machinery/bot/examine(mob/user) + ..(user) + if (src.health < maxhealth) + if (src.health > maxhealth/3) + to_chat(user, "[src]'s parts look loose.") + else + to_chat(user, "[src]'s parts look very loose!") + return + +/obj/machinery/bot/attackby(obj/item/W as obj, mob/user as mob) + if(W.isscrewdriver()) + if(!locked) + open = !open + to_chat(user, "Maintenance panel is now [src.open ? "opened" : "closed"].") + else if(W.iswelder()) + if(health < maxhealth) + if(open) + health = min(maxhealth, health+10) + user.visible_message("[user] repairs [src]!","You repair [src]!") + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + else + to_chat(user, "Unable to repair with the maintenance panel closed.") + else + to_chat(user, "[src] does not need a repair.") + else + if(hasvar(W,"force") && hasvar(W,"damtype")) + switch(W.damtype) + if("fire") + src.health -= W.force * fire_dam_coeff + if("brute") + src.health -= W.force * brute_dam_coeff + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + ..() + healthcheck() + else + ..() + +/obj/machinery/bot/bullet_act(var/obj/item/projectile/Proj) + if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN)) + return + health -= Proj.damage + ..() + healthcheck() + +/obj/machinery/bot/ex_act(severity) + switch(severity) + if(1.0) + src.explode() + return + if(2.0) + src.health -= rand(5,10)*fire_dam_coeff + src.health -= rand(10,20)*brute_dam_coeff + healthcheck() + return + if(3.0) + if (prob(50)) + src.health -= rand(1,5)*fire_dam_coeff + src.health -= rand(1,5)*brute_dam_coeff + healthcheck() + return + return + +/obj/machinery/bot/emp_act(severity) + var/was_on = on + stat |= EMPED + 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 = 1 + pulse2.set_dir(pick(cardinal)) + + QDEL_IN(pulse2, 10) + + if (on) + turn_off() + addtimer(CALLBACK(src, .proc/post_emp, was_on), severity * 300) + +/obj/machinery/bot/proc/post_emp(was_on) + stat &= ~EMPED + if (was_on) + turn_on() + +/obj/machinery/bot/attack_ai(mob/user as mob) + src.attack_hand(user) + +/obj/machinery/bot/attack_hand(var/mob/living/carbon/human/user) + + if(!istype(user)) + return ..() + + if(user.species.can_shred(user)) + src.health -= rand(15,30)*brute_dam_coeff + src.visible_message("[user] has slashed [src]!") + playsound(src.loc, 'sound/weapons/slice.ogg', 25, 1, -1) + if(prob(10)) + new /obj/effect/decal/cleanable/blood/oil(src.loc) + healthcheck() + +/******************************************************************/ +// Navigation procs +// Used for A-star pathfinding + + +// Returns the surrounding cardinal turfs with open links +// Including through doors openable with the ID +/turf/proc/CardinalTurfsWithAccess(var/obj/item/card/id/ID) + var/L[] = new() + + // for(var/turf/simulated/t in oview(src,1)) + + for(var/d in cardinal) + var/turf/simulated/T = get_step(src, d) + if(istype(T) && !T.density) + if(!LinkBlockedWithAccess(src, T, ID)) + L.Add(T) + return L + + +// Returns true if a link between A and B is blocked +// Movement through doors allowed if ID has access +/proc/LinkBlockedWithAccess(turf/A, turf/B, obj/item/card/id/ID) + + if(A == null || B == null) return 1 + var/adir = get_dir(A,B) + var/rdir = get_dir(B,A) + if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal + var/iStep = get_step(A,adir&(NORTH|SOUTH)) + if(!LinkBlockedWithAccess(A,iStep, ID) && !LinkBlockedWithAccess(iStep,B,ID)) + return 0 + + var/pStep = get_step(A,adir&(EAST|WEST)) + if(!LinkBlockedWithAccess(A,pStep,ID) && !LinkBlockedWithAccess(pStep,B,ID)) + return 0 + return 1 + + if(DirBlockedWithAccess(A,adir, ID)) + return 1 + + if(DirBlockedWithAccess(B,rdir, ID)) + return 1 + + for(var/obj/O in B) + if(O.density && !istype(O, /obj/machinery/door) && !(O.flags & ON_BORDER)) + return 1 + + return 0 + +// Returns true if direction is blocked from loc +// Checks doors against access with given ID +/proc/DirBlockedWithAccess(turf/loc,var/dir,var/obj/item/card/id/ID) + for(var/obj/structure/window/D in loc) + if(!D.density) continue + if(D.dir == SOUTHWEST) return 1 + if(D.dir == dir) return 1 + + for(var/obj/machinery/door/D in loc) + if(!D.density) continue + if(istype(D, /obj/machinery/door/window)) + if( dir & D.dir ) return !D.check_access(ID) + + //if((dir & SOUTH) && (D.dir & (EAST|WEST))) return !D.check_access(ID) + //if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return !D.check_access(ID) + else return !D.check_access(ID) // it's a real, air blocking door + return 0 diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm index aac2782bd94..eb5f1fac0d9 100644 --- a/code/game/machinery/bots/mulebot.dm +++ b/code/game/machinery/bots/mulebot.dm @@ -1,859 +1,859 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -// Mulebot - carries crates around for Quartermaster -// Navigates via floor navbeacons -// Remote Controlled from QM's PDA - - -/obj/machinery/bot/mulebot - name = "Mulebot" - desc = "A Multiple Utility Load Effector bot." - icon_state = "mulebot0" - layer = MOB_LAYER - density = 1 - anchored = 1 - animate_movement=1 - health = 150 //yeah, it's tougher than ed209 because it is a big metal box with wheels --rastaf0 - maxhealth = 150 - fire_dam_coeff = 0.7 - brute_dam_coeff = 0.5 - var/atom/movable/load = null // the loaded crate (usually) - var/beacon_freq = 1400 - var/control_freq = BOT_FREQ - - suffix = "" - - var/turf/target // this is turf to navigate to (location of beacon) - var/loaddir = 0 // this the direction to unload onto/load from - var/new_destination = "" // pending new destination (waiting for beacon response) - var/destination = "" // destination description - var/home_destination = "" // tag of home beacon - req_access = list(access_cargo) // added robotics access so assembly line drop-off works properly -veyveyr //I don't think so, Tim. You need to add it to the MULE's hidden robot ID card. -NEO - var/path[] = new() - - var/mode = 0 //0 = idle/ready - //1 = loading/unloading - //2 = moving to deliver - //3 = returning to home - //4 = blocked - //5 = computing navigation - //6 = waiting for nav computation - //7 = no destination beacon found (or no route) - - var/blockcount = 0 //number of times retried a blocked path - var/reached_target = 1 //true if already reached the target - - var/refresh = 1 // true to refresh dialogue - var/auto_return = 1 // true if auto return to home beacon after unload - var/auto_pickup = 1 // true if auto-pickup at beacon - - var/obj/item/cell/cell - // the installed power cell - - // constants for internal wiring bitflags - var/datum/wires/mulebot/wires = null - - var/bloodiness = 0 // count of bloodiness - var/static/total_mules = 0 - -/obj/machinery/bot/mulebot/Initialize() - . = ..() - wires = new(src) - botcard = new(src) - botcard.access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station) - cell = new(src) - cell.charge = 2000 - cell.maxcharge = 2000 - if(SSradio) - SSradio.add_object(src, control_freq, filter = RADIO_MULEBOT) - SSradio.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) - - total_mules++ - if(!suffix) - suffix = "#[total_mules]" - name = "Mulebot ([suffix])" - -/obj/machinery/bot/mulebot/Destroy() - unload(0) - qdel(wires) - wires = null - if(SSradio) - SSradio.remove_object(src,beacon_freq) - SSradio.remove_object(src,control_freq) - return ..() - -// attack by item -// emag : lock/unlock, -// screwdriver: open/close hatch -// cell: insert it -// other: chance to knock rider off bot -/obj/machinery/bot/mulebot/attackby(var/obj/item/I, var/mob/user) - if(istype(I,/obj/item/cell) && open && !cell) - var/obj/item/cell/C = I - user.drop_from_inventory(C,src) - cell = C - updateDialog() - else if(I.isscrewdriver()) - if(locked) - to_chat(user, "The maintenance hatch cannot be opened or closed while the controls are locked.") - return - - open = !open - if(open) - src.visible_message("[user] opens the maintenance hatch of [src]", "You open [src]'s maintenance hatch.") - on = 0 - icon_state="mulebot-hatch" - else - src.visible_message("[user] closes the maintenance hatch of [src]", "You close [src]'s maintenance hatch.") - icon_state = "mulebot0" - - updateDialog() - else if (I.iswrench()) - if (src.health < maxhealth) - src.health = min(maxhealth, src.health+25) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - user.visible_message( - "\The [user] repairs \the [src]!", - "You repair \the [src]!" - ) - else - to_chat(user, "[src] does not need a repair!") - else if(load && ismob(load)) // chance to knock off rider - if(prob(1+I.force * 2)) - unload(0) - user.visible_message("[user] knocks [load] off [src] with \the [I]!", "You knock [load] off [src] with \the [I]!") - else - to_chat(user, "You hit [src] with \the [I] but to no effect.") - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - else - ..() - return - -/obj/machinery/bot/mulebot/emag_act(var/remaining_charges, var/user) - locked = !locked - to_chat(user, "You [locked ? "lock" : "unlock"] the mulebot's controls!") - flick("mulebot-emagged", src) - playsound(src.loc, 'sound/effects/sparks1.ogg', 100, 0) - return 1 - -/obj/machinery/bot/mulebot/ex_act(var/severity) - unload(0) - switch(severity) - if(2) - wires.RandomCutAll(40)//Fix by nanako because the old code was throwing runtimes - if(3) - wires.RandomCutAll(20) - ..() - return - -/obj/machinery/bot/mulebot/bullet_act() - if(prob(50) && !isnull(load)) - unload(0) - if(prob(25)) - src.visible_message("Something shorts out inside [src]!") - wires.RandomCut() - ..() - - -/obj/machinery/bot/mulebot/attack_ai(var/mob/user) - user.set_machine(src) - interact(user, 1) - -/obj/machinery/bot/mulebot/attack_hand(var/mob/user) - . = ..() - if (.) - return - user.set_machine(src) - interact(user, 0) - -/obj/machinery/bot/mulebot/interact(var/mob/user, var/ai=0) - var/dat - dat += "Multiple Utility Load Effector Mk. III

    " - dat += "ID: [suffix]
    " - dat += "Power: [on ? "On" : "Off"]
    " - - if(!open) - - dat += "Status: " - switch(mode) - if(0) - dat += "Ready" - if(1) - dat += "Loading/Unloading" - if(2) - dat += "Navigating to Delivery Location" - if(3) - dat += "Navigating to Home" - if(4) - dat += "Waiting for clear path" - if(5,6) - dat += "Calculating navigation path" - if(7) - dat += "Unable to locate destination" - - - dat += "
    Current Load: [load ? load.name : "none"]
    " - dat += "Destination: [!destination ? "none" : destination]
    " - dat += "Power level: [cell ? cell.percent() : 0]%
    " - - if(locked && !ai) - dat += "
    Controls are locked (unlock)" - else - dat += "
    Controls are unlocked (lock)

    " - - dat += "Toggle Power
    " - dat += "Stop
    " - dat += "Proceed
    " - dat += "Return to Home
    " - dat += "Set Destination
    " - dat += "Set Bot ID
    " - dat += "Set Home
    " - dat += "Toggle Auto Return Home ([auto_return ? "On":"Off"])
    " - dat += "Toggle Auto Pickup Crate ([auto_pickup ? "On":"Off"])
    " - - if(load) - dat += "Unload Now
    " - dat += "
    The maintenance hatch is closed.
    " - - else - if(!ai) - dat += "The maintenance hatch is open.

    " - dat += "Power cell: " - if(cell) - dat += "Installed
    " - else - dat += "Removed
    " - - dat += wires.GetInteractWindow() - else - dat += "The bot is in maintenance mode and cannot be controlled.
    " - - user << browse("Mulebot [suffix ? "([suffix])" : ""][dat]", "window=mulebot;size=350x500") - onclose(user, "mulebot") - return - -/obj/machinery/bot/mulebot/Topic(href, href_list) - if(..()) - return - if (usr.stat) - return - if ((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - switch(href_list["op"]) - if("lock", "unlock") - if(src.allowed(usr)) - locked = !locked - updateDialog() - else - to_chat(usr, "Access denied.") - return - if("power") - if (src.on) - turn_off() - else if (cell && !open) - if (!turn_on()) - to_chat(usr, "You can't switch on [src].") - return - else - return - visible_message("[usr] switches [on ? "on" : "off"] [src].") - updateDialog() - - - if("cellremove") - if(open && cell && !usr.get_active_hand()) - cell.update_icon() - usr.put_in_active_hand(cell) - cell.add_fingerprint(usr) - cell = null - - usr.visible_message("[usr] removes the power cell from [src].", "You remove the power cell from [src].") - updateDialog() - - if("cellinsert") - if(open && !cell) - var/obj/item/cell/C = usr.get_active_hand() - if(istype(C)) - usr.drop_from_inventory(C,src) - cell = C - C.add_fingerprint(usr) - - usr.visible_message("[usr] inserts a power cell into [src].", "You insert the power cell into [src].") - updateDialog() - - - if("stop") - if(mode >=2) - mode = 0 - updateDialog() - - if("go") - if(mode == 0) - start() - updateDialog() - - if("home") - if(mode == 0 || mode == 2) - start_home() - updateDialog() - - if("destination") - refresh=0 - var/new_dest - var/list/beaconlist = new() - for(var/obj/machinery/navbeacon/N in navbeacons) - beaconlist.Add(N.location) - if(beaconlist.len) - new_dest = input("Select new destination tag", "Mulebot [suffix ? "([suffix])" : ""]", destination) in beaconlist - else - alert("No destination beacons available.") - refresh=1 - if(new_dest) - set_destination(new_dest) - - - if("setid") - refresh=0 - var/new_id = sanitize(input("Enter new bot ID", "Mulebot [suffix ? "([suffix])" : ""]", suffix) as text|null, MAX_NAME_LEN) - refresh=1 - if(new_id) - suffix = new_id - name = "Mulebot ([suffix])" - updateDialog() - - if("sethome") - refresh=0 - var/new_home = input("Enter new home tag", "Mulebot [suffix ? "([suffix])" : ""]", home_destination) as text|null - refresh=1 - if(new_home) - home_destination = new_home - updateDialog() - - if("unload") - if(load && mode !=1) - if(loc == target) - unload(loaddir) - else - unload(0) - - if("autoret") - auto_return = !auto_return - - if("autopick") - auto_pickup = !auto_pickup - - if("close") - usr.unset_machine() - usr << browse(null,"window=mulebot") - - updateDialog() - //src.updateUsrDialog() - else - usr << browse(null, "window=mulebot") - usr.unset_machine() - return - - - -// returns true if the bot has power -/obj/machinery/bot/mulebot/proc/has_power() - return !open && cell && cell.charge>0 && wires.HasPower() - -// mousedrop a crate to load the bot -// can load anything if emagged - -/obj/machinery/bot/mulebot/MouseDrop_T(var/atom/movable/C, mob/user) - - if(user.stat) - return - - if (!on || !istype(C)|| C.anchored || get_dist(user, src) > 1 || get_dist(src,C) > 1 ) - return - - if(load) - return - - load(C) - - -// called to load a crate -/obj/machinery/bot/mulebot/proc/load(var/atom/movable/C) - if(wires.LoadCheck() && !istype(C,/obj/structure/closet/crate)) - src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) - return // if not emagged, only allow crates to be loaded - - //I'm sure someone will come along and ask why this is here... well people were dragging screen items onto the mule, and that was not cool. - //So this is a simple fix that only allows a selection of item types to be considered. Further narrowing-down is below. - if(!istype(C,/obj/item) && !istype(C,/obj/machinery) && !istype(C,/obj/structure) && !ismob(C)) - return - if(!isturf(C.loc)) //To prevent the loading from stuff from someone's inventory, which wouldn't get handled properly. - return - - if(get_dist(C, src) > 1 || load || !on) - return - for(var/obj/structure/plasticflaps/P in src.loc)//Takes flaps into account - if(!CanPass(C,P)) - return - mode = 1 - - // if a create, close before loading - var/obj/structure/closet/crate/crate = C - if(istype(crate)) - crate.close() - - C.forceMove(src.loc) - sleep(2) - if(C.loc != src.loc) //To prevent you from going onto more thano ne bot. - return - C.forceMove(src) - load = C - - C.pixel_y += 9 - if(C.layer < layer) - C.layer = layer + 0.1 - add_overlay(C) - - if(ismob(C)) - var/mob/M = C - if(M.client) - M.client.perspective = EYE_PERSPECTIVE - M.client.eye = src - - mode = 0 - send_status() - -// called to unload the bot -// argument is optional direction to unload -// if zero, unload at bot's location -/obj/machinery/bot/mulebot/proc/unload(var/dirn = 0) - if(!load) - return - - mode = 1 - cut_overlays() - - load.forceMove(src.loc) - load.pixel_y -= 9 - load.layer = initial(load.layer) - if(ismob(load)) - var/mob/M = load - if(M.client) - M.client.perspective = MOB_PERSPECTIVE - M.client.eye = src - - - if(dirn) - var/turf/T = src.loc - T = get_step(T,dirn) - if(CanPass(load,T))//Can't get off onto anything that wouldn't let you pass normally - step(load, dirn) - else - load.forceMove(src.loc)//Drops you right there, so you shouldn't be able to get yourself stuck - - load = null - - // in case non-load items end up in contents, dump every else too - // this seems to happen sometimes due to race conditions - // with items dropping as mobs are loaded - - for(var/atom/movable/AM in src) - if(AM == cell || AM == botcard) continue - - AM.forceMove(src.loc) - AM.layer = initial(AM.layer) - AM.pixel_y = initial(AM.pixel_y) - if(ismob(AM)) - var/mob/M = AM - if(M.client) - M.client.perspective = MOB_PERSPECTIVE - M.client.eye = src - mode = 0 - - -/obj/machinery/bot/mulebot/machinery_process() - if(!has_power()) - on = 0 - return - if(on) - var/speed = (wires.Motor1() ? 1:0) + (wires.Motor2() ? 2:0) - switch(speed) - if(0) - // do nothing - if(1) - process_bot() - spawn(2) - process_bot() - sleep(2) - process_bot() - sleep(2) - process_bot() - sleep(2) - process_bot() - if(2) - process_bot() - spawn(4) - process_bot() - if(3) - process_bot() - - if(refresh) updateDialog() - -/obj/machinery/bot/mulebot/proc/process_bot() - switch(mode) - if(0) // idle - icon_state = "mulebot0" - return - if(1) // loading/unloading - return - if(2,3,4) // navigating to deliver,home, or blocked - - if(loc == target) // reached target - at_target() - return - - else if(path.len > 0 && target) // valid path - - var/turf/next = path[1] - reached_target = 0 - if(next == loc) - path -= next - return - - - if(istype( next, /turf/simulated)) - if(bloodiness) - var/obj/effect/decal/cleanable/blood/tracks/B = new(loc) - var/newdir = get_dir(next, loc) - if(newdir == dir) - B.set_dir(newdir) - else - newdir = newdir | dir - if(newdir == 3) - newdir = 1 - else if(newdir == 12) - newdir = 4 - B.set_dir(newdir) - bloodiness-- - - - - var/moved = step_towards(src, next) // attempt to move - if(cell) cell.use(1) - if(moved) // successful move - blockcount = 0 - path -= loc - - - if(mode==4) - spawn(1) - send_status() - - if(destination == home_destination) - mode = 3 - else - mode = 2 - - else // failed to move - blockcount++ - mode = 4 - if(blockcount == 3) - src.visible_message("[src] makes an annoyed buzzing sound", "You hear an electronic buzzing sound.") - playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) - - if(blockcount > 5) // attempt 5 times before recomputing - // find new path excluding blocked turf - src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) - - spawn(2) - calc_path(next) - if(path.len > 0) - src.visible_message("[src] makes a delighted ping!", "You hear a ping.") - playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) - mode = 4 - mode =6 - return - return - else - src.visible_message("[src] makes an annoyed buzzing sound", "You hear an electronic buzzing sound.") - playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) - mode = 5 - return - else - mode = 5 - return - - if(5) // calculate new path - mode = 6 - spawn(0) - - calc_path() - - if(path.len > 0) - blockcount = 0 - mode = 4 - src.visible_message("[src] makes a delighted ping!", "You hear a ping.") - playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) - - else - src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) - - mode = 7 - return - - -// calculates a path to the current destination -// given an optional turf to avoid -/obj/machinery/bot/mulebot/proc/calc_path(var/turf/avoid = null) - src.path = AStar(src.loc, src.target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 250, id=botcard, exclude=avoid) - if(!src.path) - src.path = list() - - -// sets the current destination -// signals all beacons matching the delivery code -// beacons will return a signal giving their locations -/obj/machinery/bot/mulebot/proc/set_destination(var/new_dest) - spawn(0) - new_destination = new_dest - post_signal(beacon_freq, "findbeacon", "delivery") - updateDialog() - -// starts bot moving to current destination -/obj/machinery/bot/mulebot/proc/start() - if(destination == home_destination) - mode = 3 - else - mode = 2 - icon_state = "mulebot[wires.MobAvoid()]" - -// starts bot moving to home -// sends a beacon query to find -/obj/machinery/bot/mulebot/proc/start_home() - spawn(0) - set_destination(home_destination) - mode = 4 - icon_state = "mulebot[wires.MobAvoid()]" - -// called when bot reaches current target -/obj/machinery/bot/mulebot/proc/at_target() - if(!reached_target) - src.visible_message("[src] makes a chiming sound!", "You hear a chime.") - playsound(src.loc, 'sound/machines/chime.ogg', 50, 0) - reached_target = 1 - - if(load) // if loaded, unload at target - unload(loaddir) - else - // not loaded - if(auto_pickup) // find a crate - var/atom/movable/AM - if(!wires.LoadCheck()) // if emagged, load first unanchored thing we find - for(var/atom/movable/A in get_step(loc, loaddir)) - if(!A.anchored) - AM = A - break - else // otherwise, look for crates only - AM = locate(/obj/structure/closet/crate) in get_step(loc,loaddir) - if(AM) - load(AM) - // whatever happened, check to see if we return home - - if(auto_return && destination != home_destination) - // auto return set and not at home already - start_home() - mode = 4 - else - mode = 0 // otherwise go idle - - send_status() // report status to anyone listening - - return - -// called when bot bumps into anything -/obj/machinery/bot/mulebot/Collide(var/atom/obs) - if(!wires.MobAvoid()) //usually just bumps, but if avoidance disabled knock over mobs - var/mob/M = obs - if(ismob(M)) - if(istype(M,/mob/living/silicon/robot)) - src.visible_message("[src] bumps into [M]!") - else - src.visible_message("[src] knocks over [M]!") - M.stop_pulling() - M.Stun(8) - M.Weaken(5) - M.lying = 1 - . = ..() - -// called from mob/living/carbon/human/Crossed() -// when mulebot is in the same loc -/obj/machinery/bot/mulebot/proc/RunOver(var/mob/living/carbon/human/H) - src.visible_message("[src] drives over [H]!") - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) - - var/damage = rand(5,15) - H.apply_damage(2*damage, BRUTE, BP_HEAD) - H.apply_damage(2*damage, BRUTE, BP_CHEST) - H.apply_damage(0.5*damage, BRUTE, BP_L_LEG) - H.apply_damage(0.5*damage, BRUTE, BP_R_LEG) - H.apply_damage(0.5*damage, BRUTE, BP_L_ARM) - H.apply_damage(0.5*damage, BRUTE, BP_R_ARM) - - blood_splatter(src,H,1) - bloodiness += 4 - - SSfeedback.IncrementSimpleStat("mule_victims") - -// player on mulebot attempted to move -/obj/machinery/bot/mulebot/relaymove(var/mob/user) - if(user.stat) - return - if(load == user) - unload(0) - return - -// receive a radio signal -// used for control and beacon reception - -/obj/machinery/bot/mulebot/receive_signal(datum/signal/signal) - - if(!on) - return - - var/recv = signal.data["command"] - // process all-bot input - if(recv=="bot_status" && wires.RemoteRX()) - send_status() - - - recv = signal.data["command [suffix]"] - if(wires.RemoteRX()) - // process control input - switch(recv) - if("stop") - mode = 0 - return - - if("go") - start() - return - - if("target") - set_destination(signal.data["destination"] ) - return - - if("unload") - if(loc == target) - unload(loaddir) - else - unload(0) - return - - if("home") - start_home() - return - - if("bot_status") - send_status() - return - - if("autoret") - auto_return = text2num(signal.data["value"]) - return - - if("autopick") - auto_pickup = text2num(signal.data["value"]) - return - - // receive response from beacon - recv = signal.data["beacon"] - if(wires.BeaconRX()) - if(recv == new_destination) // if the recvd beacon location matches the set destination - // the we will navigate there - destination = new_destination - target = signal.source.loc - var/direction = signal.data["dir"] // this will be the load/unload dir - if(direction) - loaddir = text2num(direction) - else - loaddir = 0 - icon_state = "mulebot[wires.MobAvoid()]" - calc_path() - updateDialog() - -// send a radio signal with a single data key/value pair -/obj/machinery/bot/mulebot/proc/post_signal(var/freq, var/key, var/value) - post_signal_multiple(freq, list("[key]" = value) ) - -// send a radio signal with multiple data key/values -/obj/machinery/bot/mulebot/proc/post_signal_multiple(var/freq, var/list/keyval) - - if(freq == beacon_freq && !wires.BeaconRX()) - return - if(freq == control_freq && !wires.RemoteTX()) - return - - var/datum/radio_frequency/frequency = SSradio.return_frequency(freq) - - if(!frequency) return - - - - var/datum/signal/signal = new() - - signal.source = src - signal.transmission_method = 1 - signal.data = keyval - - if (signal.data["findbeacon"]) - frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) - else if (signal.data["type"] == "mulebot") - frequency.post_signal(src, signal, filter = RADIO_MULEBOT) - else - frequency.post_signal(src, signal) - -// signals bot status etc. to controller -/obj/machinery/bot/mulebot/proc/send_status() - var/list/kv = list( - "type" = "mulebot", - "name" = suffix, - "loca" = (loc ? loc.loc : "Unknown"), // somehow loc can be null and cause a runtime - Quarxink - "mode" = mode, - "powr" = (cell ? cell.percent() : 0), - "dest" = destination, - "home" = home_destination, - "load" = load, - "retn" = auto_return, - "pick" = auto_pickup - ) - post_signal_multiple(control_freq, kv) - -/obj/machinery/bot/mulebot/emp_act(severity) - if (cell) - cell.emp_act(severity) - if(load) - load.emp_act(severity) - ..() - - -/obj/machinery/bot/mulebot/explode() - visible_message("[src] blows apart!") - var/turf/Tsec = get_turf(src) - - new /obj/item/device/assembly/prox_sensor(Tsec) - new /obj/item/stack/rods(Tsec) - new /obj/item/stack/rods(Tsec) - new /obj/item/stack/cable_coil/cut(Tsec) - if (cell) - cell.forceMove(Tsec) - cell.update_icon() - cell = null - - spark(Tsec, 3, alldirs) - - new /obj/effect/decal/cleanable/blood/oil(src.loc) - unload(0) - qdel(src) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +// Mulebot - carries crates around for Quartermaster +// Navigates via floor navbeacons +// Remote Controlled from QM's PDA + + +/obj/machinery/bot/mulebot + name = "Mulebot" + desc = "A Multiple Utility Load Effector bot." + icon_state = "mulebot0" + layer = MOB_LAYER + density = 1 + anchored = 1 + animate_movement=1 + health = 150 //yeah, it's tougher than ed209 because it is a big metal box with wheels --rastaf0 + maxhealth = 150 + fire_dam_coeff = 0.7 + brute_dam_coeff = 0.5 + var/atom/movable/load = null // the loaded crate (usually) + var/beacon_freq = 1400 + var/control_freq = BOT_FREQ + + suffix = "" + + var/turf/target // this is turf to navigate to (location of beacon) + var/loaddir = 0 // this the direction to unload onto/load from + var/new_destination = "" // pending new destination (waiting for beacon response) + var/destination = "" // destination description + var/home_destination = "" // tag of home beacon + req_access = list(access_cargo) // added robotics access so assembly line drop-off works properly -veyveyr //I don't think so, Tim. You need to add it to the MULE's hidden robot ID card. -NEO + var/path[] = new() + + var/mode = 0 //0 = idle/ready + //1 = loading/unloading + //2 = moving to deliver + //3 = returning to home + //4 = blocked + //5 = computing navigation + //6 = waiting for nav computation + //7 = no destination beacon found (or no route) + + var/blockcount = 0 //number of times retried a blocked path + var/reached_target = 1 //true if already reached the target + + var/refresh = 1 // true to refresh dialogue + var/auto_return = 1 // true if auto return to home beacon after unload + var/auto_pickup = 1 // true if auto-pickup at beacon + + var/obj/item/cell/cell + // the installed power cell + + // constants for internal wiring bitflags + var/datum/wires/mulebot/wires = null + + var/bloodiness = 0 // count of bloodiness + var/static/total_mules = 0 + +/obj/machinery/bot/mulebot/Initialize() + . = ..() + wires = new(src) + botcard = new(src) + botcard.access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station) + cell = new(src) + cell.charge = 2000 + cell.maxcharge = 2000 + if(SSradio) + SSradio.add_object(src, control_freq, filter = RADIO_MULEBOT) + SSradio.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) + + total_mules++ + if(!suffix) + suffix = "#[total_mules]" + name = "Mulebot ([suffix])" + +/obj/machinery/bot/mulebot/Destroy() + unload(0) + qdel(wires) + wires = null + if(SSradio) + SSradio.remove_object(src,beacon_freq) + SSradio.remove_object(src,control_freq) + return ..() + +// attack by item +// emag : lock/unlock, +// screwdriver: open/close hatch +// cell: insert it +// other: chance to knock rider off bot +/obj/machinery/bot/mulebot/attackby(var/obj/item/I, var/mob/user) + if(istype(I,/obj/item/cell) && open && !cell) + var/obj/item/cell/C = I + user.drop_from_inventory(C,src) + cell = C + updateDialog() + else if(I.isscrewdriver()) + if(locked) + to_chat(user, "The maintenance hatch cannot be opened or closed while the controls are locked.") + return + + open = !open + if(open) + src.visible_message("[user] opens the maintenance hatch of [src]", "You open [src]'s maintenance hatch.") + on = 0 + icon_state="mulebot-hatch" + else + src.visible_message("[user] closes the maintenance hatch of [src]", "You close [src]'s maintenance hatch.") + icon_state = "mulebot0" + + updateDialog() + else if (I.iswrench()) + if (src.health < maxhealth) + src.health = min(maxhealth, src.health+25) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.visible_message( + "\The [user] repairs \the [src]!", + "You repair \the [src]!" + ) + else + to_chat(user, "[src] does not need a repair!") + else if(load && ismob(load)) // chance to knock off rider + if(prob(1+I.force * 2)) + unload(0) + user.visible_message("[user] knocks [load] off [src] with \the [I]!", "You knock [load] off [src] with \the [I]!") + else + to_chat(user, "You hit [src] with \the [I] but to no effect.") + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + else + ..() + return + +/obj/machinery/bot/mulebot/emag_act(var/remaining_charges, var/user) + locked = !locked + to_chat(user, "You [locked ? "lock" : "unlock"] the mulebot's controls!") + flick("mulebot-emagged", src) + playsound(src.loc, 'sound/effects/sparks1.ogg', 100, 0) + return 1 + +/obj/machinery/bot/mulebot/ex_act(var/severity) + unload(0) + switch(severity) + if(2) + wires.RandomCutAll(40)//Fix by nanako because the old code was throwing runtimes + if(3) + wires.RandomCutAll(20) + ..() + return + +/obj/machinery/bot/mulebot/bullet_act() + if(prob(50) && !isnull(load)) + unload(0) + if(prob(25)) + src.visible_message("Something shorts out inside [src]!") + wires.RandomCut() + ..() + + +/obj/machinery/bot/mulebot/attack_ai(var/mob/user) + user.set_machine(src) + interact(user, 1) + +/obj/machinery/bot/mulebot/attack_hand(var/mob/user) + . = ..() + if (.) + return + user.set_machine(src) + interact(user, 0) + +/obj/machinery/bot/mulebot/interact(var/mob/user, var/ai=0) + var/dat + dat += "Multiple Utility Load Effector Mk. III

    " + dat += "ID: [suffix]
    " + dat += "Power: [on ? "On" : "Off"]
    " + + if(!open) + + dat += "Status: " + switch(mode) + if(0) + dat += "Ready" + if(1) + dat += "Loading/Unloading" + if(2) + dat += "Navigating to Delivery Location" + if(3) + dat += "Navigating to Home" + if(4) + dat += "Waiting for clear path" + if(5,6) + dat += "Calculating navigation path" + if(7) + dat += "Unable to locate destination" + + + dat += "
    Current Load: [load ? load.name : "none"]
    " + dat += "Destination: [!destination ? "none" : destination]
    " + dat += "Power level: [cell ? cell.percent() : 0]%
    " + + if(locked && !ai) + dat += "
    Controls are locked (unlock)" + else + dat += "
    Controls are unlocked (lock)

    " + + dat += "Toggle Power
    " + dat += "Stop
    " + dat += "Proceed
    " + dat += "Return to Home
    " + dat += "Set Destination
    " + dat += "Set Bot ID
    " + dat += "Set Home
    " + dat += "Toggle Auto Return Home ([auto_return ? "On":"Off"])
    " + dat += "Toggle Auto Pickup Crate ([auto_pickup ? "On":"Off"])
    " + + if(load) + dat += "Unload Now
    " + dat += "
    The maintenance hatch is closed.
    " + + else + if(!ai) + dat += "The maintenance hatch is open.

    " + dat += "Power cell: " + if(cell) + dat += "Installed
    " + else + dat += "Removed
    " + + dat += wires.GetInteractWindow() + else + dat += "The bot is in maintenance mode and cannot be controlled.
    " + + user << browse("Mulebot [suffix ? "([suffix])" : ""][dat]", "window=mulebot;size=350x500") + onclose(user, "mulebot") + return + +/obj/machinery/bot/mulebot/Topic(href, href_list) + if(..()) + return + if (usr.stat) + return + if ((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + switch(href_list["op"]) + if("lock", "unlock") + if(src.allowed(usr)) + locked = !locked + updateDialog() + else + to_chat(usr, "Access denied.") + return + if("power") + if (src.on) + turn_off() + else if (cell && !open) + if (!turn_on()) + to_chat(usr, "You can't switch on [src].") + return + else + return + visible_message("[usr] switches [on ? "on" : "off"] [src].") + updateDialog() + + + if("cellremove") + if(open && cell && !usr.get_active_hand()) + cell.update_icon() + usr.put_in_active_hand(cell) + cell.add_fingerprint(usr) + cell = null + + usr.visible_message("[usr] removes the power cell from [src].", "You remove the power cell from [src].") + updateDialog() + + if("cellinsert") + if(open && !cell) + var/obj/item/cell/C = usr.get_active_hand() + if(istype(C)) + usr.drop_from_inventory(C,src) + cell = C + C.add_fingerprint(usr) + + usr.visible_message("[usr] inserts a power cell into [src].", "You insert the power cell into [src].") + updateDialog() + + + if("stop") + if(mode >=2) + mode = 0 + updateDialog() + + if("go") + if(mode == 0) + start() + updateDialog() + + if("home") + if(mode == 0 || mode == 2) + start_home() + updateDialog() + + if("destination") + refresh=0 + var/new_dest + var/list/beaconlist = new() + for(var/obj/machinery/navbeacon/N in navbeacons) + beaconlist.Add(N.location) + if(beaconlist.len) + new_dest = input("Select new destination tag", "Mulebot [suffix ? "([suffix])" : ""]", destination) in beaconlist + else + alert("No destination beacons available.") + refresh=1 + if(new_dest) + set_destination(new_dest) + + + if("setid") + refresh=0 + var/new_id = sanitize(input("Enter new bot ID", "Mulebot [suffix ? "([suffix])" : ""]", suffix) as text|null, MAX_NAME_LEN) + refresh=1 + if(new_id) + suffix = new_id + name = "Mulebot ([suffix])" + updateDialog() + + if("sethome") + refresh=0 + var/new_home = input("Enter new home tag", "Mulebot [suffix ? "([suffix])" : ""]", home_destination) as text|null + refresh=1 + if(new_home) + home_destination = new_home + updateDialog() + + if("unload") + if(load && mode !=1) + if(loc == target) + unload(loaddir) + else + unload(0) + + if("autoret") + auto_return = !auto_return + + if("autopick") + auto_pickup = !auto_pickup + + if("close") + usr.unset_machine() + usr << browse(null,"window=mulebot") + + updateDialog() + //src.updateUsrDialog() + else + usr << browse(null, "window=mulebot") + usr.unset_machine() + return + + + +// returns true if the bot has power +/obj/machinery/bot/mulebot/proc/has_power() + return !open && cell && cell.charge>0 && wires.HasPower() + +// mousedrop a crate to load the bot +// can load anything if emagged + +/obj/machinery/bot/mulebot/MouseDrop_T(var/atom/movable/C, mob/user) + + if(user.stat) + return + + if (!on || !istype(C)|| C.anchored || get_dist(user, src) > 1 || get_dist(src,C) > 1 ) + return + + if(load) + return + + load(C) + + +// called to load a crate +/obj/machinery/bot/mulebot/proc/load(var/atom/movable/C) + if(wires.LoadCheck() && !istype(C,/obj/structure/closet/crate)) + src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") + playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + return // if not emagged, only allow crates to be loaded + + //I'm sure someone will come along and ask why this is here... well people were dragging screen items onto the mule, and that was not cool. + //So this is a simple fix that only allows a selection of item types to be considered. Further narrowing-down is below. + if(!istype(C,/obj/item) && !istype(C,/obj/machinery) && !istype(C,/obj/structure) && !ismob(C)) + return + if(!isturf(C.loc)) //To prevent the loading from stuff from someone's inventory, which wouldn't get handled properly. + return + + if(get_dist(C, src) > 1 || load || !on) + return + for(var/obj/structure/plasticflaps/P in src.loc)//Takes flaps into account + if(!CanPass(C,P)) + return + mode = 1 + + // if a create, close before loading + var/obj/structure/closet/crate/crate = C + if(istype(crate)) + crate.close() + + C.forceMove(src.loc) + sleep(2) + if(C.loc != src.loc) //To prevent you from going onto more thano ne bot. + return + C.forceMove(src) + load = C + + C.pixel_y += 9 + if(C.layer < layer) + C.layer = layer + 0.1 + add_overlay(C) + + if(ismob(C)) + var/mob/M = C + if(M.client) + M.client.perspective = EYE_PERSPECTIVE + M.client.eye = src + + mode = 0 + send_status() + +// called to unload the bot +// argument is optional direction to unload +// if zero, unload at bot's location +/obj/machinery/bot/mulebot/proc/unload(var/dirn = 0) + if(!load) + return + + mode = 1 + cut_overlays() + + load.forceMove(src.loc) + load.pixel_y -= 9 + load.layer = initial(load.layer) + if(ismob(load)) + var/mob/M = load + if(M.client) + M.client.perspective = MOB_PERSPECTIVE + M.client.eye = src + + + if(dirn) + var/turf/T = src.loc + T = get_step(T,dirn) + if(CanPass(load,T))//Can't get off onto anything that wouldn't let you pass normally + step(load, dirn) + else + load.forceMove(src.loc)//Drops you right there, so you shouldn't be able to get yourself stuck + + load = null + + // in case non-load items end up in contents, dump every else too + // this seems to happen sometimes due to race conditions + // with items dropping as mobs are loaded + + for(var/atom/movable/AM in src) + if(AM == cell || AM == botcard) continue + + AM.forceMove(src.loc) + AM.layer = initial(AM.layer) + AM.pixel_y = initial(AM.pixel_y) + if(ismob(AM)) + var/mob/M = AM + if(M.client) + M.client.perspective = MOB_PERSPECTIVE + M.client.eye = src + mode = 0 + + +/obj/machinery/bot/mulebot/machinery_process() + if(!has_power()) + on = 0 + return + if(on) + var/speed = (wires.Motor1() ? 1:0) + (wires.Motor2() ? 2:0) + switch(speed) + if(0) + // do nothing + if(1) + process_bot() + spawn(2) + process_bot() + sleep(2) + process_bot() + sleep(2) + process_bot() + sleep(2) + process_bot() + if(2) + process_bot() + spawn(4) + process_bot() + if(3) + process_bot() + + if(refresh) updateDialog() + +/obj/machinery/bot/mulebot/proc/process_bot() + switch(mode) + if(0) // idle + icon_state = "mulebot0" + return + if(1) // loading/unloading + return + if(2,3,4) // navigating to deliver,home, or blocked + + if(loc == target) // reached target + at_target() + return + + else if(path.len > 0 && target) // valid path + + var/turf/next = path[1] + reached_target = 0 + if(next == loc) + path -= next + return + + + if(istype( next, /turf/simulated)) + if(bloodiness) + var/obj/effect/decal/cleanable/blood/tracks/B = new(loc) + var/newdir = get_dir(next, loc) + if(newdir == dir) + B.set_dir(newdir) + else + newdir = newdir | dir + if(newdir == 3) + newdir = 1 + else if(newdir == 12) + newdir = 4 + B.set_dir(newdir) + bloodiness-- + + + + var/moved = step_towards(src, next) // attempt to move + if(cell) cell.use(1) + if(moved) // successful move + blockcount = 0 + path -= loc + + + if(mode==4) + spawn(1) + send_status() + + if(destination == home_destination) + mode = 3 + else + mode = 2 + + else // failed to move + blockcount++ + mode = 4 + if(blockcount == 3) + src.visible_message("[src] makes an annoyed buzzing sound", "You hear an electronic buzzing sound.") + playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) + + if(blockcount > 5) // attempt 5 times before recomputing + // find new path excluding blocked turf + src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") + playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + + spawn(2) + calc_path(next) + if(path.len > 0) + src.visible_message("[src] makes a delighted ping!", "You hear a ping.") + playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) + mode = 4 + mode =6 + return + return + else + src.visible_message("[src] makes an annoyed buzzing sound", "You hear an electronic buzzing sound.") + playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) + mode = 5 + return + else + mode = 5 + return + + if(5) // calculate new path + mode = 6 + spawn(0) + + calc_path() + + if(path.len > 0) + blockcount = 0 + mode = 4 + src.visible_message("[src] makes a delighted ping!", "You hear a ping.") + playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) + + else + src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") + playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + + mode = 7 + return + + +// calculates a path to the current destination +// given an optional turf to avoid +/obj/machinery/bot/mulebot/proc/calc_path(var/turf/avoid = null) + src.path = AStar(src.loc, src.target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 250, id=botcard, exclude=avoid) + if(!src.path) + src.path = list() + + +// sets the current destination +// signals all beacons matching the delivery code +// beacons will return a signal giving their locations +/obj/machinery/bot/mulebot/proc/set_destination(var/new_dest) + spawn(0) + new_destination = new_dest + post_signal(beacon_freq, "findbeacon", "delivery") + updateDialog() + +// starts bot moving to current destination +/obj/machinery/bot/mulebot/proc/start() + if(destination == home_destination) + mode = 3 + else + mode = 2 + icon_state = "mulebot[wires.MobAvoid()]" + +// starts bot moving to home +// sends a beacon query to find +/obj/machinery/bot/mulebot/proc/start_home() + spawn(0) + set_destination(home_destination) + mode = 4 + icon_state = "mulebot[wires.MobAvoid()]" + +// called when bot reaches current target +/obj/machinery/bot/mulebot/proc/at_target() + if(!reached_target) + src.visible_message("[src] makes a chiming sound!", "You hear a chime.") + playsound(src.loc, 'sound/machines/chime.ogg', 50, 0) + reached_target = 1 + + if(load) // if loaded, unload at target + unload(loaddir) + else + // not loaded + if(auto_pickup) // find a crate + var/atom/movable/AM + if(!wires.LoadCheck()) // if emagged, load first unanchored thing we find + for(var/atom/movable/A in get_step(loc, loaddir)) + if(!A.anchored) + AM = A + break + else // otherwise, look for crates only + AM = locate(/obj/structure/closet/crate) in get_step(loc,loaddir) + if(AM) + load(AM) + // whatever happened, check to see if we return home + + if(auto_return && destination != home_destination) + // auto return set and not at home already + start_home() + mode = 4 + else + mode = 0 // otherwise go idle + + send_status() // report status to anyone listening + + return + +// called when bot bumps into anything +/obj/machinery/bot/mulebot/Collide(var/atom/obs) + if(!wires.MobAvoid()) //usually just bumps, but if avoidance disabled knock over mobs + var/mob/M = obs + if(ismob(M)) + if(istype(M,/mob/living/silicon/robot)) + src.visible_message("[src] bumps into [M]!") + else + src.visible_message("[src] knocks over [M]!") + M.stop_pulling() + M.Stun(8) + M.Weaken(5) + M.lying = 1 + . = ..() + +// called from mob/living/carbon/human/Crossed() +// when mulebot is in the same loc +/obj/machinery/bot/mulebot/proc/RunOver(var/mob/living/carbon/human/H) + src.visible_message("[src] drives over [H]!") + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) + + var/damage = rand(5,15) + H.apply_damage(2*damage, BRUTE, BP_HEAD) + H.apply_damage(2*damage, BRUTE, BP_CHEST) + H.apply_damage(0.5*damage, BRUTE, BP_L_LEG) + H.apply_damage(0.5*damage, BRUTE, BP_R_LEG) + H.apply_damage(0.5*damage, BRUTE, BP_L_ARM) + H.apply_damage(0.5*damage, BRUTE, BP_R_ARM) + + blood_splatter(src,H,1) + bloodiness += 4 + + SSfeedback.IncrementSimpleStat("mule_victims") + +// player on mulebot attempted to move +/obj/machinery/bot/mulebot/relaymove(var/mob/user) + if(user.stat) + return + if(load == user) + unload(0) + return + +// receive a radio signal +// used for control and beacon reception + +/obj/machinery/bot/mulebot/receive_signal(datum/signal/signal) + + if(!on) + return + + var/recv = signal.data["command"] + // process all-bot input + if(recv=="bot_status" && wires.RemoteRX()) + send_status() + + + recv = signal.data["command [suffix]"] + if(wires.RemoteRX()) + // process control input + switch(recv) + if("stop") + mode = 0 + return + + if("go") + start() + return + + if("target") + set_destination(signal.data["destination"] ) + return + + if("unload") + if(loc == target) + unload(loaddir) + else + unload(0) + return + + if("home") + start_home() + return + + if("bot_status") + send_status() + return + + if("autoret") + auto_return = text2num(signal.data["value"]) + return + + if("autopick") + auto_pickup = text2num(signal.data["value"]) + return + + // receive response from beacon + recv = signal.data["beacon"] + if(wires.BeaconRX()) + if(recv == new_destination) // if the recvd beacon location matches the set destination + // the we will navigate there + destination = new_destination + target = signal.source.loc + var/direction = signal.data["dir"] // this will be the load/unload dir + if(direction) + loaddir = text2num(direction) + else + loaddir = 0 + icon_state = "mulebot[wires.MobAvoid()]" + calc_path() + updateDialog() + +// send a radio signal with a single data key/value pair +/obj/machinery/bot/mulebot/proc/post_signal(var/freq, var/key, var/value) + post_signal_multiple(freq, list("[key]" = value) ) + +// send a radio signal with multiple data key/values +/obj/machinery/bot/mulebot/proc/post_signal_multiple(var/freq, var/list/keyval) + + if(freq == beacon_freq && !wires.BeaconRX()) + return + if(freq == control_freq && !wires.RemoteTX()) + return + + var/datum/radio_frequency/frequency = SSradio.return_frequency(freq) + + if(!frequency) return + + + + var/datum/signal/signal = new() + + signal.source = src + signal.transmission_method = 1 + signal.data = keyval + + if (signal.data["findbeacon"]) + frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) + else if (signal.data["type"] == "mulebot") + frequency.post_signal(src, signal, filter = RADIO_MULEBOT) + else + frequency.post_signal(src, signal) + +// signals bot status etc. to controller +/obj/machinery/bot/mulebot/proc/send_status() + var/list/kv = list( + "type" = "mulebot", + "name" = suffix, + "loca" = (loc ? loc.loc : "Unknown"), // somehow loc can be null and cause a runtime - Quarxink + "mode" = mode, + "powr" = (cell ? cell.percent() : 0), + "dest" = destination, + "home" = home_destination, + "load" = load, + "retn" = auto_return, + "pick" = auto_pickup + ) + post_signal_multiple(control_freq, kv) + +/obj/machinery/bot/mulebot/emp_act(severity) + if (cell) + cell.emp_act(severity) + if(load) + load.emp_act(severity) + ..() + + +/obj/machinery/bot/mulebot/explode() + visible_message("[src] blows apart!") + var/turf/Tsec = get_turf(src) + + new /obj/item/device/assembly/prox_sensor(Tsec) + new /obj/item/stack/rods(Tsec) + new /obj/item/stack/rods(Tsec) + new /obj/item/stack/cable_coil/cut(Tsec) + if (cell) + cell.forceMove(Tsec) + cell.update_icon() + cell = null + + spark(Tsec, 3, alldirs) + + new /obj/effect/decal/cleanable/blood/oil(src.loc) + unload(0) + qdel(src) diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 24f191a4523..df87c4ddbb2 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -1,205 +1,205 @@ -/obj/machinery/button - name = "button" - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - desc = "A remote control switch for something." - var/id = null - var/active = 0 - var/operating = 0 - anchored = 1.0 - use_power = 1 - idle_power_usage = 2 - active_power_usage = 4 - var/_wifi_id - var/datum/wifi/sender/wifi_sender - -/obj/machinery/button/Initialize() - . = ..() - update_icon() - if(_wifi_id && !wifi_sender) - wifi_sender = new/datum/wifi/sender/button(_wifi_id, src) - -/obj/machinery/button/Destroy() - qdel(wifi_sender) - wifi_sender = null - return..() - -/obj/machinery/button/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/button/attackby(obj/item/W, mob/user as mob) - return attack_hand(user) - -/obj/machinery/button/attack_hand(mob/living/user) - if(..()) return 1 - user.visible_message("[user] hits \the [src] button.") - activate(user) - -/obj/machinery/button/proc/activate(mob/living/user) - if(operating || !istype(wifi_sender)) - return - - operating = 1 - active = 1 - use_power(5) - update_icon() - wifi_sender.activate(user) - sleep(10) - active = 0 - update_icon() - operating = 0 - -/obj/machinery/button/update_icon() - if(active) - icon_state = "launcheract" - else - icon_state = "launcherbtt" - -//alternate button with the same functionality, except has a lightswitch sprite instead -/obj/machinery/button/switch - icon = 'icons/obj/power.dmi' - icon_state = "light0" - -/obj/machinery/button/switch/update_icon() - icon_state = "light[active]" - -//alternate button with the same functionality, except has a door control sprite instead -/obj/machinery/button/alternate - icon = 'icons/obj/stationobjs.dmi' - icon_state = "doorctrl0" - -/obj/machinery/button/alternate/update_icon() - if(active) - icon_state = "doorctrl0" - else - icon_state = "doorctrl2" - -//Toggle button with two states (on and off) and calls seperate procs for each state -/obj/machinery/button/toggle/activate(mob/living/user) - if(operating || !istype(wifi_sender)) - return - - operating = 1 - active = !active - use_power(5) - if(active) - wifi_sender.activate(user) - else - wifi_sender.deactivate(user) - update_icon() - operating = 0 - -//alternate button with the same toggle functionality, except has a lightswitch sprite instead -/obj/machinery/button/toggle/switch - icon = 'icons/obj/power.dmi' - icon_state = "light0" - -/obj/machinery/button/toggle/switch/update_icon() - icon_state = "light[active]" - -//alternate button with the same toggle functionality, except has a door control sprite instead -/obj/machinery/button/toggle/alternate - icon = 'icons/obj/stationobjs.dmi' - icon_state = "doorctrl0" - -/obj/machinery/button/toggle/alternate/update_icon() - if(active) - icon_state = "doorctrl0" - else - icon_state = "doorctrl2" - -//------------------------------- -// Mass Driver Button -// Passes the activate call to a mass driver wifi sender -//------------------------------- -/obj/machinery/button/mass_driver - name = "mass driver button" - -/obj/machinery/button/mass_driver/Initialize() - . = ..() - if(_wifi_id) - wifi_sender = new/datum/wifi/sender/mass_driver(_wifi_id, src) - -/obj/machinery/button/mass_driver/activate(mob/living/user) - if(active || !istype(wifi_sender)) - return - - active = 1 - if(use_power) - use_power(active_power_usage) - update_icon() - wifi_sender.activate() - active = 0 - update_icon() - - -//------------------------------- -// Door Button -//------------------------------- - -// Bitmasks for door switches. -#define OPEN 0x1 -#define IDSCAN 0x2 -#define BOLTS 0x4 -#define SHOCK 0x8 -#define SAFE 0x10 - -/obj/machinery/button/toggle/door - icon = 'icons/obj/stationobjs.dmi' - icon_state = "doorctrl0" - - var/_door_functions = 1 -/* Bitflag, 1 = open - 2 = idscan - 4 = bolts - 8 = shock - 16 = door safties */ - -/obj/machinery/button/toggle/door/update_icon() - if(active) - icon_state = "doorctrl0" - else - icon_state = "doorctrl2" - -/obj/machinery/button/toggle/door/Initialize() - if(_wifi_id) - wifi_sender = new/datum/wifi/sender/door(_wifi_id, src) - . = ..() - -/obj/machinery/button/toggle/door/activate(mob/living/user) - if(operating || !istype(wifi_sender)) - return - - operating = 1 - active = !active - use_power(5) - update_icon() - if(active) - if(_door_functions & IDSCAN) - wifi_sender.activate("enable_idscan") - if(_door_functions & SHOCK) - wifi_sender.activate("electrify") - if(_door_functions & SAFE) - wifi_sender.activate("enable_safeties") - if(_door_functions & BOLTS) - wifi_sender.activate("unlock") - if(_door_functions & OPEN) - wifi_sender.activate("open") - else - if(_door_functions & IDSCAN) - wifi_sender.activate("disable_idscan") - if(_door_functions & SHOCK) - wifi_sender.activate("unelectrify") - if(_door_functions & SAFE) - wifi_sender.activate("disable_safeties") - if(_door_functions & OPEN) - wifi_sender.activate("close") - if(_door_functions & BOLTS) - wifi_sender.activate("lock") - operating = 0 - -#undef OPEN -#undef IDSCAN -#undef BOLTS -#undef SHOCK -#undef SAFE +/obj/machinery/button + name = "button" + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + desc = "A remote control switch for something." + var/id = null + var/active = 0 + var/operating = 0 + anchored = 1.0 + use_power = 1 + idle_power_usage = 2 + active_power_usage = 4 + var/_wifi_id + var/datum/wifi/sender/wifi_sender + +/obj/machinery/button/Initialize() + . = ..() + update_icon() + if(_wifi_id && !wifi_sender) + wifi_sender = new/datum/wifi/sender/button(_wifi_id, src) + +/obj/machinery/button/Destroy() + qdel(wifi_sender) + wifi_sender = null + return..() + +/obj/machinery/button/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/button/attackby(obj/item/W, mob/user as mob) + return attack_hand(user) + +/obj/machinery/button/attack_hand(mob/living/user) + if(..()) return 1 + user.visible_message("[user] hits \the [src] button.") + activate(user) + +/obj/machinery/button/proc/activate(mob/living/user) + if(operating || !istype(wifi_sender)) + return + + operating = 1 + active = 1 + use_power(5) + update_icon() + wifi_sender.activate(user) + sleep(10) + active = 0 + update_icon() + operating = 0 + +/obj/machinery/button/update_icon() + if(active) + icon_state = "launcheract" + else + icon_state = "launcherbtt" + +//alternate button with the same functionality, except has a lightswitch sprite instead +/obj/machinery/button/switch + icon = 'icons/obj/power.dmi' + icon_state = "light0" + +/obj/machinery/button/switch/update_icon() + icon_state = "light[active]" + +//alternate button with the same functionality, except has a door control sprite instead +/obj/machinery/button/alternate + icon = 'icons/obj/stationobjs.dmi' + icon_state = "doorctrl0" + +/obj/machinery/button/alternate/update_icon() + if(active) + icon_state = "doorctrl0" + else + icon_state = "doorctrl2" + +//Toggle button with two states (on and off) and calls seperate procs for each state +/obj/machinery/button/toggle/activate(mob/living/user) + if(operating || !istype(wifi_sender)) + return + + operating = 1 + active = !active + use_power(5) + if(active) + wifi_sender.activate(user) + else + wifi_sender.deactivate(user) + update_icon() + operating = 0 + +//alternate button with the same toggle functionality, except has a lightswitch sprite instead +/obj/machinery/button/toggle/switch + icon = 'icons/obj/power.dmi' + icon_state = "light0" + +/obj/machinery/button/toggle/switch/update_icon() + icon_state = "light[active]" + +//alternate button with the same toggle functionality, except has a door control sprite instead +/obj/machinery/button/toggle/alternate + icon = 'icons/obj/stationobjs.dmi' + icon_state = "doorctrl0" + +/obj/machinery/button/toggle/alternate/update_icon() + if(active) + icon_state = "doorctrl0" + else + icon_state = "doorctrl2" + +//------------------------------- +// Mass Driver Button +// Passes the activate call to a mass driver wifi sender +//------------------------------- +/obj/machinery/button/mass_driver + name = "mass driver button" + +/obj/machinery/button/mass_driver/Initialize() + . = ..() + if(_wifi_id) + wifi_sender = new/datum/wifi/sender/mass_driver(_wifi_id, src) + +/obj/machinery/button/mass_driver/activate(mob/living/user) + if(active || !istype(wifi_sender)) + return + + active = 1 + if(use_power) + use_power(active_power_usage) + update_icon() + wifi_sender.activate() + active = 0 + update_icon() + + +//------------------------------- +// Door Button +//------------------------------- + +// Bitmasks for door switches. +#define OPEN 0x1 +#define IDSCAN 0x2 +#define BOLTS 0x4 +#define SHOCK 0x8 +#define SAFE 0x10 + +/obj/machinery/button/toggle/door + icon = 'icons/obj/stationobjs.dmi' + icon_state = "doorctrl0" + + var/_door_functions = 1 +/* Bitflag, 1 = open + 2 = idscan + 4 = bolts + 8 = shock + 16 = door safties */ + +/obj/machinery/button/toggle/door/update_icon() + if(active) + icon_state = "doorctrl0" + else + icon_state = "doorctrl2" + +/obj/machinery/button/toggle/door/Initialize() + if(_wifi_id) + wifi_sender = new/datum/wifi/sender/door(_wifi_id, src) + . = ..() + +/obj/machinery/button/toggle/door/activate(mob/living/user) + if(operating || !istype(wifi_sender)) + return + + operating = 1 + active = !active + use_power(5) + update_icon() + if(active) + if(_door_functions & IDSCAN) + wifi_sender.activate("enable_idscan") + if(_door_functions & SHOCK) + wifi_sender.activate("electrify") + if(_door_functions & SAFE) + wifi_sender.activate("enable_safeties") + if(_door_functions & BOLTS) + wifi_sender.activate("unlock") + if(_door_functions & OPEN) + wifi_sender.activate("open") + else + if(_door_functions & IDSCAN) + wifi_sender.activate("disable_idscan") + if(_door_functions & SHOCK) + wifi_sender.activate("unelectrify") + if(_door_functions & SAFE) + wifi_sender.activate("disable_safeties") + if(_door_functions & OPEN) + wifi_sender.activate("close") + if(_door_functions & BOLTS) + wifi_sender.activate("lock") + operating = 0 + +#undef OPEN +#undef IDSCAN +#undef BOLTS +#undef SHOCK +#undef SAFE diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 6708fc7b497..64c2693740d 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -1,462 +1,462 @@ -/obj/machinery/camera - name = "security camera" - desc = "It's used to monitor rooms." - icon = 'icons/obj/monitors.dmi' - icon_state = "camera" - use_power = 2 - idle_power_usage = 5 - active_power_usage = 10 - layer = 5 - - var/list/network = list(NETWORK_STATION) - var/c_tag = null - var/c_tag_order = 999 - var/status = 1 - anchored = 1.0 - var/invuln = null - var/bugged = 0 - var/obj/item/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/alarm_on = 0 - var/busy = 0 - - var/on_open_network = 0 - - var/affected_by_emp_until = 0 - -/obj/machinery/camera/Initialize() - wires = new(src) - assembly = new(src) - assembly.state = 4 - SSmachinery.all_cameras += src - - /* // 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) - 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) - return ..() - -/obj/machinery/camera/Destroy() - SSmachinery.all_cameras -= src - deactivate(null, 0) //kick anyone viewing out - if(assembly) - qdel(assembly) - assembly = null - qdel(wires) - wires = null - return ..() - -/obj/machinery/camera/machinery_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)) - else - stat |= EMPED - set_light(0) - triggerCameraAlarm() - kick_viewers() - update_icon() - update_coverage() - -/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/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.update_visibility(src, 0) - -/obj/machinery/camera/attack_hand(mob/living/carbon/human/user as mob) - if(!istype(user)) - return - - if(user.species.can_shred(user)) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - set_status(0) - user.do_attack_animation(src) - visible_message("\The [user] slashes at [src]!") - playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1) - add_hiddenprint(user) - destroy() - -/obj/machinery/camera/attackby(obj/W as obj, mob/living/user as mob) - update_coverage() - // DECONSTRUCTION - if(W.isscrewdriver()) - //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.loc, W.usesound, 50, 1) - - else if((W.iswirecutter() || W.ismultitool()) && panel_open) - interact(user) - - else if(W.iswelder() && (wires.CanDeconstruct() || (stat & BROKEN))) - if(weld(W, user)) - if(assembly) - assembly.forceMove(src.loc) - assembly.anchored = 1 - assembly.camera_name = c_tag - assembly.camera_network = english_list(network, "Station", ",", ",") - 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(loc, 2) - assembly = null //so qdel doesn't eat it. - qdel(src) - return - - // OTHER - else if (can_use() && (istype(W, /obj/item/paper) || istype(W, /obj/item/device/pda)) && isliving(user)) - var/info = null - var/mob/living/U = user - var/obj/item/paper/X = null - var/obj/item/device/pda/P = null - - var/itemname = "" - if(istype(W, /obj/item/paper)) - X = W - itemname = X.name - info = X.info - else - P = W - itemname = P.name - info = P.notehtml - to_chat(U, "You hold \a [itemname] up to the camera ...") - for(var/mob/living/silicon/ai/O in living_mob_list) - var/entry = O.addCameraRecord(itemname,info) - if(!O.client) continue - if(U.name == "Unknown") - to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...view message") - else - to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...view message") - - for(var/mob/O in player_list) - if (istype(O.machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/S = O.machine - if (S.current_camera == src) - to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...") - O << browse(text("[][]", itemname, info), text("window=[]", itemname)) //Force people watching to open the page so they can't see it again) - - else if (istype(W, /obj/item/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(DEFAULT_ATTACK_COOLDOWN) - if (W.force >= src.toughness) - user.do_attack_animation(src) - user.visible_message("[user] has [LAZYPICK(W.attack_verb,"attacked")] [src] with [W]!") - 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(loc, 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) - //legacy support, if choice is != 1 then just kick viewers without changing status - kick_viewers() - else - 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.loc, '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.loc, 'sound/items/Wirecutter.ogg', 100, 1) - icon_state = initial(icon_state) - add_hiddenprint(user) - -/obj/machinery/camera/proc/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.RandomCutAll() - - kick_viewers() - triggerCameraAlarm() - update_icon() - update_coverage() - - //sparks - spark(loc, 5) - playsound(loc, "sparks", 50, 1) - -/obj/machinery/camera/proc/set_status(var/newstatus) - if (status != newstatus) - status = newstatus - update_coverage() - // now disconnect anyone using the camera - //Apparently, this will disconnect anyone even if the camera was re-activated. - //I guess that doesn't matter since they couldn't use it anyway? - kick_viewers() - -/obj/machinery/camera/check_eye(mob/user) - if(!can_use()) return -1 - if(isXRay()) return SEE_TURFS|SEE_MOBS|SEE_OBJS - return 0 - -//This might be redundant, because of check_eye() -/obj/machinery/camera/proc/kick_viewers() - for(var/mob/O in player_list) - if (istype(O.machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/S = O.machine - if (S.current_camera == src) - O.unset_machine() - O.reset_view(null) - to_chat(O, "The screen bursts into static.") - -/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.IsIndexCut(CAMERA_WIRE_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 - break - 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 - break - - return null - -/obj/machinery/camera/proc/weld(var/obj/item/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 the [src]..") - playsound(src.loc, 'sound/items/Welder.ogg', 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/nano_structure() - var/cam[0] - cam["name"] = sanitize(c_tag) - cam["deact"] = !can_use() - cam["camera"] = "\ref[src]" - cam["x"] = x - cam["y"] = y - cam["z"] = z - return cam - -// 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.CutAll() - wires.MendAll() - update_icon() - update_coverage() - +/obj/machinery/camera + name = "security camera" + desc = "It's used to monitor rooms." + icon = 'icons/obj/monitors.dmi' + icon_state = "camera" + use_power = 2 + idle_power_usage = 5 + active_power_usage = 10 + layer = 5 + + var/list/network = list(NETWORK_STATION) + var/c_tag = null + var/c_tag_order = 999 + var/status = 1 + anchored = 1.0 + var/invuln = null + var/bugged = 0 + var/obj/item/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/alarm_on = 0 + var/busy = 0 + + var/on_open_network = 0 + + var/affected_by_emp_until = 0 + +/obj/machinery/camera/Initialize() + wires = new(src) + assembly = new(src) + assembly.state = 4 + SSmachinery.all_cameras += src + + /* // 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) + 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) + return ..() + +/obj/machinery/camera/Destroy() + SSmachinery.all_cameras -= src + deactivate(null, 0) //kick anyone viewing out + if(assembly) + qdel(assembly) + assembly = null + qdel(wires) + wires = null + return ..() + +/obj/machinery/camera/machinery_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)) + else + stat |= EMPED + set_light(0) + triggerCameraAlarm() + kick_viewers() + update_icon() + update_coverage() + +/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/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.update_visibility(src, 0) + +/obj/machinery/camera/attack_hand(mob/living/carbon/human/user as mob) + if(!istype(user)) + return + + if(user.species.can_shred(user)) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + set_status(0) + user.do_attack_animation(src) + visible_message("\The [user] slashes at [src]!") + playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1) + add_hiddenprint(user) + destroy() + +/obj/machinery/camera/attackby(obj/W as obj, mob/living/user as mob) + update_coverage() + // DECONSTRUCTION + if(W.isscrewdriver()) + //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.loc, W.usesound, 50, 1) + + else if((W.iswirecutter() || W.ismultitool()) && panel_open) + interact(user) + + else if(W.iswelder() && (wires.CanDeconstruct() || (stat & BROKEN))) + if(weld(W, user)) + if(assembly) + assembly.forceMove(src.loc) + assembly.anchored = 1 + assembly.camera_name = c_tag + assembly.camera_network = english_list(network, "Station", ",", ",") + 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(loc, 2) + assembly = null //so qdel doesn't eat it. + qdel(src) + return + + // OTHER + else if (can_use() && (istype(W, /obj/item/paper) || istype(W, /obj/item/device/pda)) && isliving(user)) + var/info = null + var/mob/living/U = user + var/obj/item/paper/X = null + var/obj/item/device/pda/P = null + + var/itemname = "" + if(istype(W, /obj/item/paper)) + X = W + itemname = X.name + info = X.info + else + P = W + itemname = P.name + info = P.notehtml + to_chat(U, "You hold \a [itemname] up to the camera ...") + for(var/mob/living/silicon/ai/O in living_mob_list) + var/entry = O.addCameraRecord(itemname,info) + if(!O.client) continue + if(U.name == "Unknown") + to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...view message") + else + to_chat(O, "[U] holds \a [itemname] up to one of your cameras ...view message") + + for(var/mob/O in player_list) + if (istype(O.machine, /obj/machinery/computer/security)) + var/obj/machinery/computer/security/S = O.machine + if (S.current_camera == src) + to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...") + O << browse(text("[][]", itemname, info), text("window=[]", itemname)) //Force people watching to open the page so they can't see it again) + + else if (istype(W, /obj/item/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(DEFAULT_ATTACK_COOLDOWN) + if (W.force >= src.toughness) + user.do_attack_animation(src) + user.visible_message("[user] has [LAZYPICK(W.attack_verb,"attacked")] [src] with [W]!") + 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(loc, 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) + //legacy support, if choice is != 1 then just kick viewers without changing status + kick_viewers() + else + 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.loc, '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.loc, 'sound/items/Wirecutter.ogg', 100, 1) + icon_state = initial(icon_state) + add_hiddenprint(user) + +/obj/machinery/camera/proc/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.RandomCutAll() + + kick_viewers() + triggerCameraAlarm() + update_icon() + update_coverage() + + //sparks + spark(loc, 5) + playsound(loc, "sparks", 50, 1) + +/obj/machinery/camera/proc/set_status(var/newstatus) + if (status != newstatus) + status = newstatus + update_coverage() + // now disconnect anyone using the camera + //Apparently, this will disconnect anyone even if the camera was re-activated. + //I guess that doesn't matter since they couldn't use it anyway? + kick_viewers() + +/obj/machinery/camera/check_eye(mob/user) + if(!can_use()) return -1 + if(isXRay()) return SEE_TURFS|SEE_MOBS|SEE_OBJS + return 0 + +//This might be redundant, because of check_eye() +/obj/machinery/camera/proc/kick_viewers() + for(var/mob/O in player_list) + if (istype(O.machine, /obj/machinery/computer/security)) + var/obj/machinery/computer/security/S = O.machine + if (S.current_camera == src) + O.unset_machine() + O.reset_view(null) + to_chat(O, "The screen bursts into static.") + +/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.IsIndexCut(CAMERA_WIRE_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 + break + 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 + break + + return null + +/obj/machinery/camera/proc/weld(var/obj/item/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 the [src]..") + playsound(src.loc, 'sound/items/Welder.ogg', 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/nano_structure() + var/cam[0] + cam["name"] = sanitize(c_tag) + cam["deact"] = !can_use() + cam["camera"] = "\ref[src]" + cam["x"] = x + cam["y"] = y + cam["z"] = z + return cam + +// 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.CutAll() + wires.MendAll() + update_icon() + update_coverage() + diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index 6199cce9399..86d80bb1298 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -1,173 +1,173 @@ -/obj/item/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.dmi' - icon_state = "cameracase" - w_class = 2 - anchored = 0 - - matter = list(DEFAULT_WALL_MATERIAL = 700,"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/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/camera_assembly/attackby(obj/item/W as obj, mob/living/user as mob) - - switch(state) - - if(0) - // State 0 - if(W.iswrench() && isturf(src.loc)) - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You wrench the assembly into place.") - anchored = 1 - state = 1 - update_icon() - auto_turn() - return - - if(1) - // State 1 - if(W.iswelder()) - if(weld(W, user)) - to_chat(user, "You weld the assembly securely into place.") - anchored = 1 - state = 2 - return - - else if(W.iswrench()) - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You unattach the assembly from its place.") - anchored = 0 - update_icon() - state = 0 - return - - if(2) - // State 2 - if(W.iscoil()) - 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.iswelder()) - - if(weld(W, user)) - to_chat(user, "You unweld the assembly from its place.") - state = 1 - anchored = 1 - return - - - if(3) - // State 3 - if(W.isscrewdriver()) - playsound(src.loc, W.usesound, 50, 1) - - var/input = sanitize(input(usr, "Which networks would you like to connect this camera to? Separate networks with a comma. No Spaces!\nFor example: Station,Security,Secret", "Set Network", camera_network ? camera_network : NETWORK_STATION)) - if(!input) - to_chat(usr, "No input found please hang up and try your call again.") - return - - var/list/tempnetwork = text2list(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(input(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.forceMove(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 = input(user, "Direction?", "Assembling Camera", null) in list("LEAVE IT", "NORTH", "EAST", "SOUTH", "WEST" ) - if(direct != "LEAVE IT") - C.dir = text2dir(direct) - if(i != 0) - var/confirm = alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", "Yes", "No") - if(confirm == "Yes") - break - return - - else if(W.iswirecutter()) - - new/obj/item/stack/cable_coil(get_turf(src), 2) - playsound(src.loc, 'sound/items/Wirecutter.ogg', 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.forceMove(src) - return - - // Taking out upgrades - else if(W.iscrowbar() && upgrades.len) - var/obj/U = locate(/obj) in upgrades - if(U) - to_chat(user, "You unattach an upgrade from the assembly.") - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - U.forceMove(get_turf(src)) - upgrades -= U - return - - ..() - -/obj/item/camera_assembly/update_icon() - if(anchored) - icon_state = "camera1" - else - icon_state = "cameracase" - -/obj/item/camera_assembly/attack_hand(mob/user as mob) - if(!anchored) - ..() - -/obj/item/camera_assembly/proc/weld(var/obj/item/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.loc, 'sound/items/Welder.ogg', 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/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.dmi' + icon_state = "cameracase" + w_class = 2 + anchored = 0 + + matter = list(DEFAULT_WALL_MATERIAL = 700,"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/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/camera_assembly/attackby(obj/item/W as obj, mob/living/user as mob) + + switch(state) + + if(0) + // State 0 + if(W.iswrench() && isturf(src.loc)) + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You wrench the assembly into place.") + anchored = 1 + state = 1 + update_icon() + auto_turn() + return + + if(1) + // State 1 + if(W.iswelder()) + if(weld(W, user)) + to_chat(user, "You weld the assembly securely into place.") + anchored = 1 + state = 2 + return + + else if(W.iswrench()) + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You unattach the assembly from its place.") + anchored = 0 + update_icon() + state = 0 + return + + if(2) + // State 2 + if(W.iscoil()) + 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.iswelder()) + + if(weld(W, user)) + to_chat(user, "You unweld the assembly from its place.") + state = 1 + anchored = 1 + return + + + if(3) + // State 3 + if(W.isscrewdriver()) + playsound(src.loc, W.usesound, 50, 1) + + var/input = sanitize(input(usr, "Which networks would you like to connect this camera to? Separate networks with a comma. No Spaces!\nFor example: Station,Security,Secret", "Set Network", camera_network ? camera_network : NETWORK_STATION)) + if(!input) + to_chat(usr, "No input found please hang up and try your call again.") + return + + var/list/tempnetwork = text2list(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(input(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.forceMove(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 = input(user, "Direction?", "Assembling Camera", null) in list("LEAVE IT", "NORTH", "EAST", "SOUTH", "WEST" ) + if(direct != "LEAVE IT") + C.dir = text2dir(direct) + if(i != 0) + var/confirm = alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", "Yes", "No") + if(confirm == "Yes") + break + return + + else if(W.iswirecutter()) + + new/obj/item/stack/cable_coil(get_turf(src), 2) + playsound(src.loc, 'sound/items/Wirecutter.ogg', 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.forceMove(src) + return + + // Taking out upgrades + else if(W.iscrowbar() && upgrades.len) + var/obj/U = locate(/obj) in upgrades + if(U) + to_chat(user, "You unattach an upgrade from the assembly.") + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + U.forceMove(get_turf(src)) + upgrades -= U + return + + ..() + +/obj/item/camera_assembly/update_icon() + if(anchored) + icon_state = "camera1" + else + icon_state = "cameracase" + +/obj/item/camera_assembly/attack_hand(mob/user as mob) + if(!anchored) + ..() + +/obj/item/camera_assembly/proc/weld(var/obj/item/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.loc, 'sound/items/Welder.ogg', 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 eac1f89386f..8fabc8d0dab 100644 --- a/code/game/machinery/camera/motion.dm +++ b/code/game/machinery/camera/motion.dm @@ -1,64 +1,64 @@ -/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() - flags = PROXMOVE - -/obj/machinery/camera/internal_process() - // motion camera event loop - if (stat & (EMPED|NOPOWER)) - return - if(!isMotion()) - . = PROCESS_KILL - return - 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(atom/movable/AM as mob|obj) - // 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() + flags = PROXMOVE + +/obj/machinery/camera/internal_process() + // motion camera event loop + if (stat & (EMPED|NOPOWER)) + return + if(!isMotion()) + . = PROCESS_KILL + return + 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(atom/movable/AM as mob|obj) + // 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 eb7aa6f0c64..8594a3e0ca1 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -1,186 +1,186 @@ -// PRESETS -var/global/list/engineering_networks = list( - NETWORK_ENGINE, - NETWORK_ENGINEERING, - NETWORK_ENGINEERING_OUTPOST, - "Atmosphere Alarms", - "Fire Alarms", - "Power Alarms" -) - -/obj/machinery/camera/network/crescent - network = list(NETWORK_CRESCENT) - -/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/civilian_main - network = list(NETWORK_CIVILIAN_MAIN) - -/obj/machinery/camera/network/civilian_surface - network = list(NETWORK_CIVILIAN_SURFACE) - -/obj/machinery/camera/network/supply - network = list(NETWORK_SUPPLY) - -/obj/machinery/camera/network/service - network = list(NETWORK_SERVICE) - -/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/mercenary - network = list(NETWORK_MERCENARY) - -/obj/machinery/camera/network/exodus - network = list(NETWORK_STATION) - -/obj/machinery/camera/network/mining - network = list(NETWORK_MINE) - -/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/thunder - network = list(NETWORK_THUNDER) - -// EMP - -/obj/machinery/camera/emp_proof/Initialize() - . = ..() - upgradeEmpProof() - -// X-RAY - -/obj/machinery/camera/xray - icon_state = "xraycam" // Thanks to Krutchen for the icons. - -/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/Initialize() - . = ..() - upgradeXRay() - -// MOTION - -/obj/machinery/camera/motion/Initialize() - . = ..() - 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/Initialize() - . = ..() - upgradeEmpProof() - upgradeXRay() - upgradeMotion() - -// AUTONAME -/obj/machinery/camera/autoname - var/number = 0 //camera number in area - -//This camera type automatically sets it's name to whatever the area that it's in is called. -/obj/machinery/camera/autoname/Initialize() - . = ..() - number = 1 - var/area/A = get_area(src) - if(A) - for(var/obj/machinery/camera/autoname/C in SSmachinery.all_cameras) - if(C == src) continue - var/area/CA = get_area(C) - if(CA.type == A.type) - if(C.number) - number = max(number, C.number+1) - c_tag = "[A.name] #[number]" - invalidateCameraCache() - - -// 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/stock_parts/scanning_module/O = locate(/obj/item/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/stock_parts/scanning_module/adv(assembly)) - setPowerUsage() - update_coverage() - -/obj/machinery/camera/proc/upgradeMotion() - assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly)) - setPowerUsage() - if(!(SSmachinery.processing_machines[src])) - add_machine(src) - update_coverage() - -/obj/machinery/camera/proc/setPowerUsage() - var/mult = 1 - if (isXRay()) - mult++ - if (isMotion()) - mult++ - active_power_usage = mult*initial(active_power_usage) +// PRESETS +var/global/list/engineering_networks = list( + NETWORK_ENGINE, + NETWORK_ENGINEERING, + NETWORK_ENGINEERING_OUTPOST, + "Atmosphere Alarms", + "Fire Alarms", + "Power Alarms" +) + +/obj/machinery/camera/network/crescent + network = list(NETWORK_CRESCENT) + +/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/civilian_main + network = list(NETWORK_CIVILIAN_MAIN) + +/obj/machinery/camera/network/civilian_surface + network = list(NETWORK_CIVILIAN_SURFACE) + +/obj/machinery/camera/network/supply + network = list(NETWORK_SUPPLY) + +/obj/machinery/camera/network/service + network = list(NETWORK_SERVICE) + +/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/mercenary + network = list(NETWORK_MERCENARY) + +/obj/machinery/camera/network/exodus + network = list(NETWORK_STATION) + +/obj/machinery/camera/network/mining + network = list(NETWORK_MINE) + +/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/thunder + network = list(NETWORK_THUNDER) + +// EMP + +/obj/machinery/camera/emp_proof/Initialize() + . = ..() + upgradeEmpProof() + +// X-RAY + +/obj/machinery/camera/xray + icon_state = "xraycam" // Thanks to Krutchen for the icons. + +/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/Initialize() + . = ..() + upgradeXRay() + +// MOTION + +/obj/machinery/camera/motion/Initialize() + . = ..() + 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/Initialize() + . = ..() + upgradeEmpProof() + upgradeXRay() + upgradeMotion() + +// AUTONAME +/obj/machinery/camera/autoname + var/number = 0 //camera number in area + +//This camera type automatically sets it's name to whatever the area that it's in is called. +/obj/machinery/camera/autoname/Initialize() + . = ..() + number = 1 + var/area/A = get_area(src) + if(A) + for(var/obj/machinery/camera/autoname/C in SSmachinery.all_cameras) + if(C == src) continue + var/area/CA = get_area(C) + if(CA.type == A.type) + if(C.number) + number = max(number, C.number+1) + c_tag = "[A.name] #[number]" + invalidateCameraCache() + + +// 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/stock_parts/scanning_module/O = locate(/obj/item/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/stock_parts/scanning_module/adv(assembly)) + setPowerUsage() + update_coverage() + +/obj/machinery/camera/proc/upgradeMotion() + assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly)) + setPowerUsage() + if(!(SSmachinery.processing_machines[src])) + add_machine(src) + update_coverage() + +/obj/machinery/camera/proc/setPowerUsage() + var/mult = 1 + if (isXRay()) + mult++ + if (isMotion()) + mult++ + 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 816358e66db..880d270bb41 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -1,269 +1,269 @@ -#define TRACKING_POSSIBLE 0 -#define TRACKING_NO_COVERAGE 1 -#define TRACKING_TERMINATE 2 - -/mob/living/silicon/ai/var/max_locations = 10 -/mob/living/silicon/ai/var/stored_locations[0] - -/proc/InvalidPlayerTurf(turf/T as turf) - return !(T && isStationLevel(T.z)) - -/mob/living/silicon/ai/proc/get_camera_list() - if(src.stat == 2) - return - - 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 - if (!track) - trackable_mobs() - - 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 - var/mob/living/silicon/ai/U = usr - - if(target == U.cameraFollow) - return - - 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) - -/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) - if (!L) - return - var/list/target = L.Copy() - // sortTim sorts in-place, but returns a ref to the list anyways. - return sortTim(target, /proc/cmp_camera, FALSE) - -mob/living/proc/near_camera() - if (!isturf(loc)) - return 0 - else if(!cameranet.is_visible(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 Centcomm, we only need this. - var/obj/item/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(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 && isStationLevel(T.z) && 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 = 10 +/mob/living/silicon/ai/var/stored_locations[0] + +/proc/InvalidPlayerTurf(turf/T as turf) + return !(T && isStationLevel(T.z)) + +/mob/living/silicon/ai/proc/get_camera_list() + if(src.stat == 2) + return + + 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 + if (!track) + trackable_mobs() + + 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 + var/mob/living/silicon/ai/U = usr + + if(target == U.cameraFollow) + return + + 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) + +/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) + if (!L) + return + var/list/target = L.Copy() + // sortTim sorts in-place, but returns a ref to the list anyways. + return sortTim(target, /proc/cmp_camera, FALSE) + +mob/living/proc/near_camera() + if (!isturf(loc)) + return 0 + else if(!cameranet.is_visible(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 Centcomm, we only need this. + var/obj/item/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(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 && isStationLevel(T.z) && 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 b6d8da0069f..0116f89b72b 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -1,110 +1,110 @@ -/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 = 1 - idle_power_usage = 5 - active_power_usage = 90000 //90 kW. (this the power drawn when charging) - power_channel = EQUIP - var/charging_efficiency = 1.38 - var/obj/item/cell/charging = null - var/chargelevel = -1 - -/obj/machinery/cell_charger/update_icon() - icon_state = "ccharger[charging ? 1 : 0]" - - if(charging && !(stat & (BROKEN|NOPOWER)) ) - - var/newlevel = round(charging.percent() * 4.0 / 99) - - if(chargelevel != newlevel) - - cut_overlays() - add_overlay("ccharger-o[newlevel]") - - chargelevel = newlevel - else - cut_overlays() - -/obj/machinery/cell_charger/examine(mob/user) - if(!..(user, 5)) - return - - to_chat(user, "There's [charging ? "a" : "no"] cell in the charger.") - if(charging) - to_chat(user, "Current charge: [charging.charge]") - -/obj/machinery/cell_charger/attackby(obj/item/W, mob/user) - if(stat & BROKEN) - return - - if(istype(W, /obj/item/cell) && anchored) - if(charging) - to_chat(user, "There is already a cell in the charger.") - 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 [name] blinks red as you try to insert the cell!") - return - - user.drop_from_inventory(W,src) - charging = W - user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.") - chargelevel = -1 - update_icon() - else if(W.iswrench()) - if(charging) - to_chat(user, "Remove the cell first!") - return - - anchored = !anchored - to_chat(user, "You [anchored ? "attach" : "detach"] the cell charger [anchored ? "to" : "from"] the ground") - playsound(src.loc, W.usesound, 75, 1) - -/obj/machinery/cell_charger/attack_hand(mob/user) - if(charging) - usr.put_in_hands(charging) - charging.add_fingerprint(user) - charging.update_icon() - - src.charging = null - user.visible_message("[user] removes the cell from the charger.", "You remove the cell from the charger.") - 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(!src.charging) - return - user.put_in_hands(charging) - charging.update_icon() - charging = null - update_icon() - user.visible_message("[user] removes the cell from the charger.", "You remove the cell from the charger.") - - -/obj/machinery/cell_charger/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - if(charging) - charging.emp_act(severity) - ..(severity) - - -/obj/machinery/cell_charger/machinery_process() - if((stat & (BROKEN|NOPOWER)) || !anchored) - update_use_power(0) - return - - if (charging && !charging.fully_charged()) - charging.give(active_power_usage*CELLRATE*charging_efficiency) - update_use_power(2) - - update_icon() - else - update_use_power(1) +/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 = 1 + idle_power_usage = 5 + active_power_usage = 90000 //90 kW. (this the power drawn when charging) + power_channel = EQUIP + var/charging_efficiency = 1.38 + var/obj/item/cell/charging = null + var/chargelevel = -1 + +/obj/machinery/cell_charger/update_icon() + icon_state = "ccharger[charging ? 1 : 0]" + + if(charging && !(stat & (BROKEN|NOPOWER)) ) + + var/newlevel = round(charging.percent() * 4.0 / 99) + + if(chargelevel != newlevel) + + cut_overlays() + add_overlay("ccharger-o[newlevel]") + + chargelevel = newlevel + else + cut_overlays() + +/obj/machinery/cell_charger/examine(mob/user) + if(!..(user, 5)) + return + + to_chat(user, "There's [charging ? "a" : "no"] cell in the charger.") + if(charging) + to_chat(user, "Current charge: [charging.charge]") + +/obj/machinery/cell_charger/attackby(obj/item/W, mob/user) + if(stat & BROKEN) + return + + if(istype(W, /obj/item/cell) && anchored) + if(charging) + to_chat(user, "There is already a cell in the charger.") + 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 [name] blinks red as you try to insert the cell!") + return + + user.drop_from_inventory(W,src) + charging = W + user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.") + chargelevel = -1 + update_icon() + else if(W.iswrench()) + if(charging) + to_chat(user, "Remove the cell first!") + return + + anchored = !anchored + to_chat(user, "You [anchored ? "attach" : "detach"] the cell charger [anchored ? "to" : "from"] the ground") + playsound(src.loc, W.usesound, 75, 1) + +/obj/machinery/cell_charger/attack_hand(mob/user) + if(charging) + usr.put_in_hands(charging) + charging.add_fingerprint(user) + charging.update_icon() + + src.charging = null + user.visible_message("[user] removes the cell from the charger.", "You remove the cell from the charger.") + 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(!src.charging) + return + user.put_in_hands(charging) + charging.update_icon() + charging = null + update_icon() + user.visible_message("[user] removes the cell from the charger.", "You remove the cell from the charger.") + + +/obj/machinery/cell_charger/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + if(charging) + charging.emp_act(severity) + ..(severity) + + +/obj/machinery/cell_charger/machinery_process() + if((stat & (BROKEN|NOPOWER)) || !anchored) + update_use_power(0) + return + + if (charging && !charging.fully_charged()) + charging.give(active_power_usage*CELLRATE*charging_efficiency) + update_use_power(2) + + update_icon() + else + update_use_power(1) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index b7e2ae10149..f464d65be5e 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -1,505 +1,505 @@ -//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.species.has_organ[BP_BRAIN] && !H.has_brain()) - continue - if(M.ckey == find_key) - selected = M - break - return selected - -#define CLONE_BIOMASS 150 - -/obj/machinery/clonepod - name = "cloning pod" - desc = "An electronically-lockable pod for growing organic tissue." - density = 1 - anchored = 1 - icon = 'icons/obj/cloning.dmi' - icon_state = "pod_0" - req_access = list(access_medical_equip) //since we have no genetics for now - 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/biomass = CLONE_BIOMASS * 3 - - component_types = list( - /obj/item/circuitboard/clonepod, - /obj/item/stock_parts/manipulator = 2, - /obj/item/stock_parts/scanning_module = 2, - /obj/item/stock_parts/console_screen, - /obj/item/stack/cable_coil{amount = 2} - ) - -/obj/machinery/clonepod/Initialize() - . = ..() - update_icon() - set_expansion(/datum/expansion/multitool, new/datum/expansion/multitool/store(src)) - -/obj/machinery/clonepod/Destroy() - if(connected) - connected.release_pod(src) - return ..() - -/obj/machinery/clonepod/attack_ai(mob/user as mob) - - add_hiddenprint(user) - return attack_hand(user) - -/obj/machinery/clonepod/attack_hand(var/mob/user) - if((stat & NOPOWER) || !occupant || occupant.stat == DEAD) - return - to_chat(user, "Current clone cycle is [round(GetCloneReadiness())]% complete.") - -//Clonepod - -//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/abstract/observer/G in player_list) - if(G.ckey == R.ckey) - if(G.can_reenter_corpse) - break - else - return 0 - - 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.cloning_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 - - //Get the clone body ready - H.setCloneLoss(H.maxHealth - 50) - H.adjustBrainLoss(100, 120) // Even if healed to full health, it will have some brain damage - 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.
    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() - H.regenerate_icons() - update_icon() - - for(var/datum/language/L in R.languages) - H.add_language(L.name) - - for(var/obj/item/organ/I in H.internal_organs) - if(I.robotic) - qdel(I) //cloner can't clone inorganic organs - for(var/obj/item/organ/external/E in H.organs) - if(E.status & ORGAN_ROBOT) - qdel(E) //cloner can't clone inorganic organs - - H.flavor_texts = R.flavor.Copy() - attempting = 0 - return 1 - -/obj/machinery/clonepod/proc/GetCloneReadiness() // Returns a number between 0 and 100 - if(!occupant) - return - - if(occupant.getCloneLoss() == 0) // Rare case, but theoretically possible - return 100 - - return between(0, 100 * (occupant.health - occupant.maxHealth * 75 / 100) / (occupant.maxHealth * (heal_level - 75) / 100), 100) - -//Grow clones to maturity then kick them out. FREELOADERS -/obj/machinery/clonepod/machinery_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)) //Autoeject corpses - locked = 0 - go_out() - connected_message("Clone Rejected: Deceased.") - return - - if(GetCloneReadiness() >= 100 && !eject_wait) - playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) - src.audible_message("\The [src] signals that the cloning process is complete.") - connected_message("Cloning Process Complete.") - locked = 0 - go_out() - return - - occupant.Paralyse(4) - - //Slowly get that clone healed and finished. - occupant.adjustCloneLoss(-2 * heal_rate) - - //So clones don't die of oxyloss in a running pod. - if(occupant.reagents.get_reagent_amount("norepinephrine") < 30) - occupant.reagents.add_reagent("norepinephrine", 60) - occupant.Sleeping(30) - //Also heal some oxyloss ourselves because norepinephrine is so bad at preventing it!! - occupant.adjustOxyLoss(-4) - - use_power(7500) //This might need tweaking. - 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/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/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/reagent_containers/food/snacks/meat)) - to_chat(user, "\The [src] processes \the [W].") - biomass += 50 - user.drop_from_inventory(W,src) - qdel(W) - return - else if(W.iswrench()) - if(locked && (anchored || occupant)) - to_chat(user, "Can not do that while [src] is in use.") - else - if(anchored) - anchored = 0 - connected.pods -= src - connected = null - else - anchored = 1 - playsound(loc, 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 - ..() - -/obj/machinery/clonepod/emag_act(var/remaining_charges, var/mob/user) - if(isnull(occupant)) - return NO_EMAG_ACT - 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() - ..() - var/rating = 0 - - for(var/obj/item/stock_parts/P in component_parts) - if(isscanner(P) || ismanipulator(P)) - rating += P.rating - - heal_level = rating * 10 - 20 - heal_rate = round(rating / 4) - -/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(loc) - update_icon() - return - - if(!(occupant)) - return - - if(occupant.client) - occupant.client.eye = occupant.client.mob - occupant.client.perspective = MOB_PERSPECTIVE - occupant.forceMove(loc) - eject_wait = 0 //If it's still set somehow. - domutcheck(occupant) //Waiting until they're out before possible transforming. - occupant = null - - biomass -= CLONE_BIOMASS - update_icon() - return - -/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.forceMove(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(loc) - ex_act(severity) - qdel(src) - return - if(3.0) - if(prob(25)) - for(var/atom/movable/A as mob|obj in src) - A.forceMove(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" - -//Health Tracker Implant - -/obj/item/implant/health - name = "health implant" - var/healthstring = "" - -/obj/item/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/disk/data - name = "Cloning Data Disk" - icon = 'icons/obj/cloning.dmi' - icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. - item_state = "card-id" - w_class = 2.0 - var/datum/dna2/record/buf = null - var/read_only = 0 //Well,it's still a floppy disk - -/obj/item/disk/data/proc/initializeDisk() - buf = new - buf.dna=new - -/obj/item/disk/data/demo - name = "data disk - 'God Emperor of Mankind'" - read_only = 1 - -/obj/item/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/disk/data/monkey - name = "data disk - 'Mr. Muggles'" - read_only = 1 - -/obj/item/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/disk/data/New() - ..() - var/diskcolor = pick(0,1,2) - icon_state = "datadisk[diskcolor]" - -/obj/item/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/disk/data/examine(mob/user) - ..(user) - to_chat(user, text("The write-protect tab is set to [read_only ? "protected" : "unprotected"].")) - return - -/* - * Diskette Box - */ - -/obj/item/storage/box/disks - name = "Diskette Box" - icon_state = "disk_kit" - -/obj/item/storage/box/disks/fill() - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - -/* - * Manual -- A big ol' manual. - */ - -/obj/item/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.species.has_organ[BP_BRAIN] && !H.has_brain()) + continue + if(M.ckey == find_key) + selected = M + break + return selected + +#define CLONE_BIOMASS 150 + +/obj/machinery/clonepod + name = "cloning pod" + desc = "An electronically-lockable pod for growing organic tissue." + density = 1 + anchored = 1 + icon = 'icons/obj/cloning.dmi' + icon_state = "pod_0" + req_access = list(access_medical_equip) //since we have no genetics for now + 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/biomass = CLONE_BIOMASS * 3 + + component_types = list( + /obj/item/circuitboard/clonepod, + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/scanning_module = 2, + /obj/item/stock_parts/console_screen, + /obj/item/stack/cable_coil{amount = 2} + ) + +/obj/machinery/clonepod/Initialize() + . = ..() + update_icon() + set_expansion(/datum/expansion/multitool, new/datum/expansion/multitool/store(src)) + +/obj/machinery/clonepod/Destroy() + if(connected) + connected.release_pod(src) + return ..() + +/obj/machinery/clonepod/attack_ai(mob/user as mob) + + add_hiddenprint(user) + return attack_hand(user) + +/obj/machinery/clonepod/attack_hand(var/mob/user) + if((stat & NOPOWER) || !occupant || occupant.stat == DEAD) + return + to_chat(user, "Current clone cycle is [round(GetCloneReadiness())]% complete.") + +//Clonepod + +//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/abstract/observer/G in player_list) + if(G.ckey == R.ckey) + if(G.can_reenter_corpse) + break + else + return 0 + + 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.cloning_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 + + //Get the clone body ready + H.setCloneLoss(H.maxHealth - 50) + H.adjustBrainLoss(100, 120) // Even if healed to full health, it will have some brain damage + 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.
    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() + H.regenerate_icons() + update_icon() + + for(var/datum/language/L in R.languages) + H.add_language(L.name) + + for(var/obj/item/organ/I in H.internal_organs) + if(I.robotic) + qdel(I) //cloner can't clone inorganic organs + for(var/obj/item/organ/external/E in H.organs) + if(E.status & ORGAN_ROBOT) + qdel(E) //cloner can't clone inorganic organs + + H.flavor_texts = R.flavor.Copy() + attempting = 0 + return 1 + +/obj/machinery/clonepod/proc/GetCloneReadiness() // Returns a number between 0 and 100 + if(!occupant) + return + + if(occupant.getCloneLoss() == 0) // Rare case, but theoretically possible + return 100 + + return between(0, 100 * (occupant.health - occupant.maxHealth * 75 / 100) / (occupant.maxHealth * (heal_level - 75) / 100), 100) + +//Grow clones to maturity then kick them out. FREELOADERS +/obj/machinery/clonepod/machinery_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)) //Autoeject corpses + locked = 0 + go_out() + connected_message("Clone Rejected: Deceased.") + return + + if(GetCloneReadiness() >= 100 && !eject_wait) + playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) + src.audible_message("\The [src] signals that the cloning process is complete.") + connected_message("Cloning Process Complete.") + locked = 0 + go_out() + return + + occupant.Paralyse(4) + + //Slowly get that clone healed and finished. + occupant.adjustCloneLoss(-2 * heal_rate) + + //So clones don't die of oxyloss in a running pod. + if(occupant.reagents.get_reagent_amount("norepinephrine") < 30) + occupant.reagents.add_reagent("norepinephrine", 60) + occupant.Sleeping(30) + //Also heal some oxyloss ourselves because norepinephrine is so bad at preventing it!! + occupant.adjustOxyLoss(-4) + + use_power(7500) //This might need tweaking. + 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/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/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/reagent_containers/food/snacks/meat)) + to_chat(user, "\The [src] processes \the [W].") + biomass += 50 + user.drop_from_inventory(W,src) + qdel(W) + return + else if(W.iswrench()) + if(locked && (anchored || occupant)) + to_chat(user, "Can not do that while [src] is in use.") + else + if(anchored) + anchored = 0 + connected.pods -= src + connected = null + else + anchored = 1 + playsound(loc, 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 + ..() + +/obj/machinery/clonepod/emag_act(var/remaining_charges, var/mob/user) + if(isnull(occupant)) + return NO_EMAG_ACT + 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() + ..() + var/rating = 0 + + for(var/obj/item/stock_parts/P in component_parts) + if(isscanner(P) || ismanipulator(P)) + rating += P.rating + + heal_level = rating * 10 - 20 + heal_rate = round(rating / 4) + +/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(loc) + update_icon() + return + + if(!(occupant)) + return + + if(occupant.client) + occupant.client.eye = occupant.client.mob + occupant.client.perspective = MOB_PERSPECTIVE + occupant.forceMove(loc) + eject_wait = 0 //If it's still set somehow. + domutcheck(occupant) //Waiting until they're out before possible transforming. + occupant = null + + biomass -= CLONE_BIOMASS + update_icon() + return + +/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.forceMove(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(loc) + ex_act(severity) + qdel(src) + return + if(3.0) + if(prob(25)) + for(var/atom/movable/A as mob|obj in src) + A.forceMove(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" + +//Health Tracker Implant + +/obj/item/implant/health + name = "health implant" + var/healthstring = "" + +/obj/item/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/disk/data + name = "Cloning Data Disk" + icon = 'icons/obj/cloning.dmi' + icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. + item_state = "card-id" + w_class = 2.0 + var/datum/dna2/record/buf = null + var/read_only = 0 //Well,it's still a floppy disk + +/obj/item/disk/data/proc/initializeDisk() + buf = new + buf.dna=new + +/obj/item/disk/data/demo + name = "data disk - 'God Emperor of Mankind'" + read_only = 1 + +/obj/item/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/disk/data/monkey + name = "data disk - 'Mr. Muggles'" + read_only = 1 + +/obj/item/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/disk/data/New() + ..() + var/diskcolor = pick(0,1,2) + icon_state = "datadisk[diskcolor]" + +/obj/item/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/disk/data/examine(mob/user) + ..(user) + to_chat(user, text("The write-protect tab is set to [read_only ? "protected" : "unprotected"].")) + return + +/* + * Diskette Box + */ + +/obj/item/storage/box/disks + name = "Diskette Box" + icon_state = "disk_kit" + +/obj/item/storage/box/disks/fill() + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + +/* + * Manual -- A big ol' manual. + */ + +/obj/item/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 e8211a0f023..06194c18bcc 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -1,78 +1,78 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/obj/machinery/computer/operating - name = "patient monitoring console" - density = 1 - anchored = 1.0 - - light_color = LIGHT_COLOR_CYAN - icon_screen = "crew" - circuit = /obj/item/circuitboard/operating - var/mob/living/carbon/human/victim = null - var/obj/machinery/optable/table = null - -/obj/machinery/computer/operating/Initialize() - . = ..() - for(dir in list(NORTH,EAST,SOUTH,WEST)) - table = locate(/obj/machinery/optable, get_step(src, dir)) - if (table) - table.computer = src - break - -/obj/machinery/computer/operating/attack_ai(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - interact(user) - - -/obj/machinery/computer/operating/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - interact(user) - - -/obj/machinery/computer/operating/interact(mob/user) - if ( (get_dist(src, user) > 1 ) || (stat & (BROKEN|NOPOWER)) ) - if (!istype(user, /mob/living/silicon)) - user.unset_machine() - user << browse(null, "window=op") - return - - user.set_machine(src) - var/dat = "Operating Computer\n" - dat += "Close

    " //| Update" - if(src.table && (src.table.check_victim())) - src.victim = src.table.victim - var/brain_result = victim.get_brain_status() - if(victim.isFBP()) - brain_result = "normal" - dat += {" -Patient Information:
    -Brain Activity: [brain_result]
    -Pulse: [victim.get_pulse(GETPULSE_TOOL)]
    -BP: [victim.get_blood_pressure()]
    -Blood Oxygenation: [victim.get_blood_oxygenation()]
    -"} - else - src.victim = null - dat += {" -Patient Information:
    -
    -No Patient Detected -"} - user << browse(dat, "window=op") - onclose(user, "op") - -/obj/machinery/computer/operating/Topic(href, href_list) - if(..()) - return 1 - if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - return - - -/obj/machinery/computer/operating/machinery_process() - if(operable()) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/obj/machinery/computer/operating + name = "patient monitoring console" + density = 1 + anchored = 1.0 + + light_color = LIGHT_COLOR_CYAN + icon_screen = "crew" + circuit = /obj/item/circuitboard/operating + var/mob/living/carbon/human/victim = null + var/obj/machinery/optable/table = null + +/obj/machinery/computer/operating/Initialize() + . = ..() + for(dir in list(NORTH,EAST,SOUTH,WEST)) + table = locate(/obj/machinery/optable, get_step(src, dir)) + if (table) + table.computer = src + break + +/obj/machinery/computer/operating/attack_ai(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + interact(user) + + +/obj/machinery/computer/operating/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + interact(user) + + +/obj/machinery/computer/operating/interact(mob/user) + if ( (get_dist(src, user) > 1 ) || (stat & (BROKEN|NOPOWER)) ) + if (!istype(user, /mob/living/silicon)) + user.unset_machine() + user << browse(null, "window=op") + return + + user.set_machine(src) + var/dat = "Operating Computer\n" + dat += "Close

    " //| Update" + if(src.table && (src.table.check_victim())) + src.victim = src.table.victim + var/brain_result = victim.get_brain_status() + if(victim.isFBP()) + brain_result = "normal" + dat += {" +Patient Information:
    +Brain Activity: [brain_result]
    +Pulse: [victim.get_pulse(GETPULSE_TOOL)]
    +BP: [victim.get_blood_pressure()]
    +Blood Oxygenation: [victim.get_blood_oxygenation()]
    +"} + else + src.victim = null + dat += {" +Patient Information:
    +
    +No Patient Detected +"} + user << browse(dat, "window=op") + onclose(user, "op") + +/obj/machinery/computer/operating/Topic(href, href_list) + if(..()) + return 1 + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + return + + +/obj/machinery/computer/operating/machinery_process() + if(operable()) src.updateDialog() \ No newline at end of file diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index 75a86534d9e..32f6eb90514 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -1,253 +1,253 @@ -/obj/machinery/computer/arcade/ - name = "random arcade" - desc = "random arcade machine" - icon_state = "arcade" - icon_screen = "invaders" - var/prize = /obj/random/arcade - -/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(typesof(/obj/item/circuitboard/arcade) - /obj/item/circuitboard/arcade) - var/obj/item/circuitboard/CB = new choice() - new CB.build_path(loc, CB) - - return INITIALIZE_HINT_QDEL - -/obj/machinery/computer/arcade/proc/prizevend() - if(!contents.len) - new prize(src.loc) - else - var/atom/movable/chosen_prize = pick(contents) - chosen_prize.forceMove(src.loc) - -/obj/machinery/computer/arcade/attack_ai(mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/computer/arcade/emp_act(severity) - if(stat & (NOPOWER|BROKEN)) - ..(severity) - return - var/num_of_prizes = 0 - switch(severity) - if(1) - num_of_prizes = rand(1,4) - if(2) - num_of_prizes = rand(0,2) - for(num_of_prizes; num_of_prizes > 0; num_of_prizes--) - new prize(src.loc) - - ..(severity) - -/////////////////// -// BATTLE HERE // -/////////////////// - -/obj/machinery/computer/arcade/battle - name = "arcade machine" - desc = "Does not support Pinball." - icon_state = "arcade" - circuit = /obj/item/circuitboard/arcade/battle - var/enemy_name = "Space Villian" - var/temp = "Winners don't use space drugs" //Temporary message, for attack messages, etc - 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() - . = ..() - 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") - - src.enemy_name = replacetext((name_part1 + name_part2), "the ", "") - src.name = (name_action + name_part1 + name_part2) - - -/obj/machinery/computer/arcade/battle/attack_hand(mob/user as mob) - if(..()) - return - user.set_machine(src) - var/dat = "Close" - dat += "

    [src.enemy_name]

    " - - dat += "

    [src.temp]

    " - dat += "
    Health: [src.player_hp] | Magic: [src.player_mp] | Enemy Health: [src.enemy_hp]
    " - - dat += "
    " - if (src.gameover) - dat += "New Game" - else - dat += "Attack | " - dat += "Heal | " - dat += "Recharge Power" - - dat += "
    " - - user << browse(dat, "window=arcade") - onclose(user, "arcade") - return - -/obj/machinery/computer/arcade/battle/Topic(href, href_list) - if(..()) - return 1 - - if (!src.blocked && !src.gameover) - if (href_list["attack"]) - src.blocked = 1 - var/attackamt = rand(2,6) - src.temp = "You attack for [attackamt] damage!" - playsound(loc, 'sound/arcade/hit.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) - src.updateUsrDialog() - if(turtle > 0) - turtle-- - - sleep(10) - src.enemy_hp -= attackamt - src.arcade_action() - - else if (href_list["heal"]) - src.blocked = 1 - var/pointamt = rand(1,3) - var/healamt = rand(6,8) - src.temp = "You use [pointamt] magic to heal for [healamt] damage!" - playsound(loc, 'sound/arcade/heal.ogg', 2, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) - src.updateUsrDialog() - turtle++ - - sleep(10) - src.player_mp -= pointamt - src.player_hp += healamt - src.blocked = 1 - src.updateUsrDialog() - src.arcade_action() - - else if (href_list["charge"]) - src.blocked = 1 - var/chargeamt = rand(4,7) - src.temp = "You regain [chargeamt] points." - playsound(loc, 'sound/arcade/mana.ogg', 1, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) - src.player_mp += chargeamt - if(turtle > 0) - turtle-- - - src.updateUsrDialog() - sleep(10) - src.arcade_action() - - if (href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=arcade") - - else if (href_list["newgame"]) //Reset everything - temp = "New Round" - player_hp = 30 - player_mp = 10 - enemy_hp = 45 - enemy_mp = 20 - gameover = 0 - turtle = 0 - - if(emagged) - src.New() - emagged = 0 - - src.add_fingerprint(usr) - src.updateUsrDialog() - return - -/obj/machinery/computer/arcade/battle/proc/arcade_action() - if ((src.enemy_mp <= 0) || (src.enemy_hp <= 0)) - if(!gameover) - src.gameover = 1 - src.temp = "[src.enemy_name] has fallen! Rejoice!" - playsound(loc, 'sound/arcade/win.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) - - 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.",ckey=key_name(usr)) - src.New() - emagged = 0 - else - feedback_inc("arcade_win_normal") - src.prizevend() - - else if (emagged && (turtle >= 4)) - var/boomamt = rand(5,10) - src.temp = "[src.enemy_name] throws a bomb, exploding you for [boomamt] damage!" - playsound(loc, 'sound/arcade/boom.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) - src.player_hp -= boomamt - - else if ((src.enemy_mp <= 5) && (prob(70))) - var/stealamt = rand(2,3) - src.temp = "[src.enemy_name] steals [stealamt] of your power!" - playsound(loc, 'sound/arcade/steal.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) - src.player_mp -= stealamt - src.updateUsrDialog() - - if (src.player_mp <= 0) - src.gameover = 1 - sleep(10) - src.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 ((src.enemy_hp <= 10) && (src.enemy_mp > 4)) - src.temp = "[src.enemy_name] heals for 4 health!" - playsound(loc, 'sound/arcade/heal.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) - src.enemy_hp += 4 - src.enemy_mp -= 4 - - else - var/attackamt = rand(3,6) - src.temp = "[src.enemy_name] attacks for [attackamt] damage!" - playsound(loc, 'sound/arcade/hit.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) - src.player_hp -= attackamt - - if ((src.player_mp <= 0) || (src.player_hp <= 0)) - src.gameover = 1 - src.temp = "You have been crushed! GAME OVER" - playsound(loc, 'sound/arcade/lose.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) - if(emagged) - feedback_inc("arcade_loss_hp_emagged") - usr.gib() - else - feedback_inc("arcade_loss_hp_normal") - - src.blocked = 0 - return - - -/obj/machinery/computer/arcade/battle/emag_act(var/charges, var/mob/user) - if(!emagged) - 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" - - src.updateUsrDialog() +/obj/machinery/computer/arcade/ + name = "random arcade" + desc = "random arcade machine" + icon_state = "arcade" + icon_screen = "invaders" + var/prize = /obj/random/arcade + +/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(typesof(/obj/item/circuitboard/arcade) - /obj/item/circuitboard/arcade) + var/obj/item/circuitboard/CB = new choice() + new CB.build_path(loc, CB) + + return INITIALIZE_HINT_QDEL + +/obj/machinery/computer/arcade/proc/prizevend() + if(!contents.len) + new prize(src.loc) + else + var/atom/movable/chosen_prize = pick(contents) + chosen_prize.forceMove(src.loc) + +/obj/machinery/computer/arcade/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/computer/arcade/emp_act(severity) + if(stat & (NOPOWER|BROKEN)) + ..(severity) + return + var/num_of_prizes = 0 + switch(severity) + if(1) + num_of_prizes = rand(1,4) + if(2) + num_of_prizes = rand(0,2) + for(num_of_prizes; num_of_prizes > 0; num_of_prizes--) + new prize(src.loc) + + ..(severity) + +/////////////////// +// BATTLE HERE // +/////////////////// + +/obj/machinery/computer/arcade/battle + name = "arcade machine" + desc = "Does not support Pinball." + icon_state = "arcade" + circuit = /obj/item/circuitboard/arcade/battle + var/enemy_name = "Space Villian" + var/temp = "Winners don't use space drugs" //Temporary message, for attack messages, etc + 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() + . = ..() + 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") + + src.enemy_name = replacetext((name_part1 + name_part2), "the ", "") + src.name = (name_action + name_part1 + name_part2) + + +/obj/machinery/computer/arcade/battle/attack_hand(mob/user as mob) + if(..()) + return + user.set_machine(src) + var/dat = "Close" + dat += "

    [src.enemy_name]

    " + + dat += "

    [src.temp]

    " + dat += "
    Health: [src.player_hp] | Magic: [src.player_mp] | Enemy Health: [src.enemy_hp]
    " + + dat += "
    " + if (src.gameover) + dat += "New Game" + else + dat += "Attack | " + dat += "Heal | " + dat += "Recharge Power" + + dat += "
    " + + user << browse(dat, "window=arcade") + onclose(user, "arcade") + return + +/obj/machinery/computer/arcade/battle/Topic(href, href_list) + if(..()) + return 1 + + if (!src.blocked && !src.gameover) + if (href_list["attack"]) + src.blocked = 1 + var/attackamt = rand(2,6) + src.temp = "You attack for [attackamt] damage!" + playsound(loc, 'sound/arcade/hit.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) + src.updateUsrDialog() + if(turtle > 0) + turtle-- + + sleep(10) + src.enemy_hp -= attackamt + src.arcade_action() + + else if (href_list["heal"]) + src.blocked = 1 + var/pointamt = rand(1,3) + var/healamt = rand(6,8) + src.temp = "You use [pointamt] magic to heal for [healamt] damage!" + playsound(loc, 'sound/arcade/heal.ogg', 2, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) + src.updateUsrDialog() + turtle++ + + sleep(10) + src.player_mp -= pointamt + src.player_hp += healamt + src.blocked = 1 + src.updateUsrDialog() + src.arcade_action() + + else if (href_list["charge"]) + src.blocked = 1 + var/chargeamt = rand(4,7) + src.temp = "You regain [chargeamt] points." + playsound(loc, 'sound/arcade/mana.ogg', 1, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) + src.player_mp += chargeamt + if(turtle > 0) + turtle-- + + src.updateUsrDialog() + sleep(10) + src.arcade_action() + + if (href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=arcade") + + else if (href_list["newgame"]) //Reset everything + temp = "New Round" + player_hp = 30 + player_mp = 10 + enemy_hp = 45 + enemy_mp = 20 + gameover = 0 + turtle = 0 + + if(emagged) + src.New() + emagged = 0 + + src.add_fingerprint(usr) + src.updateUsrDialog() + return + +/obj/machinery/computer/arcade/battle/proc/arcade_action() + if ((src.enemy_mp <= 0) || (src.enemy_hp <= 0)) + if(!gameover) + src.gameover = 1 + src.temp = "[src.enemy_name] has fallen! Rejoice!" + playsound(loc, 'sound/arcade/win.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) + + 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.",ckey=key_name(usr)) + src.New() + emagged = 0 + else + feedback_inc("arcade_win_normal") + src.prizevend() + + else if (emagged && (turtle >= 4)) + var/boomamt = rand(5,10) + src.temp = "[src.enemy_name] throws a bomb, exploding you for [boomamt] damage!" + playsound(loc, 'sound/arcade/boom.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) + src.player_hp -= boomamt + + else if ((src.enemy_mp <= 5) && (prob(70))) + var/stealamt = rand(2,3) + src.temp = "[src.enemy_name] steals [stealamt] of your power!" + playsound(loc, 'sound/arcade/steal.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) + src.player_mp -= stealamt + src.updateUsrDialog() + + if (src.player_mp <= 0) + src.gameover = 1 + sleep(10) + src.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 ((src.enemy_hp <= 10) && (src.enemy_mp > 4)) + src.temp = "[src.enemy_name] heals for 4 health!" + playsound(loc, 'sound/arcade/heal.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) + src.enemy_hp += 4 + src.enemy_mp -= 4 + + else + var/attackamt = rand(3,6) + src.temp = "[src.enemy_name] attacks for [attackamt] damage!" + playsound(loc, 'sound/arcade/hit.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) + src.player_hp -= attackamt + + if ((src.player_mp <= 0) || (src.player_hp <= 0)) + src.gameover = 1 + src.temp = "You have been crushed! GAME OVER" + playsound(loc, 'sound/arcade/lose.ogg', 5, 1, extrarange = -3, falloff = 10, required_asfx_toggles = ASFX_ARCADE) + if(emagged) + feedback_inc("arcade_loss_hp_emagged") + usr.gib() + else + feedback_inc("arcade_loss_hp_normal") + + src.blocked = 0 + return + + +/obj/machinery/computer/arcade/battle/emag_act(var/charges, var/mob/user) + if(!emagged) + 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" + + src.updateUsrDialog() return 1 \ No newline at end of file diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 1e031be90bb..74b7b1e081c 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -1,113 +1,113 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/obj/structure/computerframe - density = 1 - anchored = 0 - name = "computer frame" - icon = 'icons/obj/computer.dmi' - icon_state = "0" - var/state = 0 - var/obj/item/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.iswrench()) - playsound(src.loc, P.usesound, 50, 1) - if(do_after(user, 20/P.toolspeed)) - to_chat(user, "You wrench the frame into place.") - src.anchored = 1 - src.state = 1 - if(P.iswelder()) - var/obj/item/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.loc, 'sound/items/Welder.ogg', 50, 1) - if(do_after(user, 20/P.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.iswrench()) - playsound(src.loc, P.usesound, 50, 1) - if(do_after(user, 20/P.toolspeed)) - to_chat(user, "You unfasten the frame.") - src.anchored = 0 - src.state = 0 - if(istype(P, /obj/item/circuitboard) && !circuit) - var/obj/item/circuitboard/B = P - if(B.board_type == "computer") - playsound(src.loc, '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_from_inventory(P,src) - else - to_chat(user, "This frame does not accept circuit boards of this type!") - if(P.isscrewdriver() && circuit) - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You screw the circuit board into place and screw the drawer shut.") - src.state = 2 - src.icon_state = "2" - if(P.iscrowbar() && circuit) - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - to_chat(user, "You remove the circuit board.") - src.state = 1 - src.icon_state = "0" - circuit.forceMove(src.loc) - src.circuit = null - if(2) - if(P.isscrewdriver() && circuit) - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You unfasten the circuit board.") - src.state = 1 - src.icon_state = "1" - if(P.iscoil()) - 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.loc, '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.iswirecutter()) - playsound(src.loc, 'sound/items/Wirecutter.ogg', 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.loc, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You start to put in the glass keyboard.") - if(do_after(user, 20) && state == 3) - if (G.use(2)) - to_chat(user, "You put in the glass keyboard.") - src.state = 4 - src.icon_state = "4" - if(4) - if(P.iscrowbar()) - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - to_chat(user, "You remove the glass keyboard.") - src.state = 3 - src.icon_state = "3" - new /obj/item/stack/material/glass( src.loc, 2 ) - if(P.isscrewdriver()) - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You connect the glass keyboard.") - 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 = 1 + anchored = 0 + name = "computer frame" + icon = 'icons/obj/computer.dmi' + icon_state = "0" + var/state = 0 + var/obj/item/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.iswrench()) + playsound(src.loc, P.usesound, 50, 1) + if(do_after(user, 20/P.toolspeed)) + to_chat(user, "You wrench the frame into place.") + src.anchored = 1 + src.state = 1 + if(P.iswelder()) + var/obj/item/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.loc, 'sound/items/Welder.ogg', 50, 1) + if(do_after(user, 20/P.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.iswrench()) + playsound(src.loc, P.usesound, 50, 1) + if(do_after(user, 20/P.toolspeed)) + to_chat(user, "You unfasten the frame.") + src.anchored = 0 + src.state = 0 + if(istype(P, /obj/item/circuitboard) && !circuit) + var/obj/item/circuitboard/B = P + if(B.board_type == "computer") + playsound(src.loc, '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_from_inventory(P,src) + else + to_chat(user, "This frame does not accept circuit boards of this type!") + if(P.isscrewdriver() && circuit) + playsound(src.loc, P.usesound, 50, 1) + to_chat(user, "You screw the circuit board into place and screw the drawer shut.") + src.state = 2 + src.icon_state = "2" + if(P.iscrowbar() && circuit) + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + to_chat(user, "You remove the circuit board.") + src.state = 1 + src.icon_state = "0" + circuit.forceMove(src.loc) + src.circuit = null + if(2) + if(P.isscrewdriver() && circuit) + playsound(src.loc, P.usesound, 50, 1) + to_chat(user, "You unfasten the circuit board.") + src.state = 1 + src.icon_state = "1" + if(P.iscoil()) + 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.loc, '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.iswirecutter()) + playsound(src.loc, 'sound/items/Wirecutter.ogg', 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.loc, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, "You start to put in the glass keyboard.") + if(do_after(user, 20) && state == 3) + if (G.use(2)) + to_chat(user, "You put in the glass keyboard.") + src.state = 4 + src.icon_state = "4" + if(4) + if(P.iscrowbar()) + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + to_chat(user, "You remove the glass keyboard.") + src.state = 3 + src.icon_state = "3" + new /obj/item/stack/material/glass( src.loc, 2 ) + if(P.isscrewdriver()) + playsound(src.loc, P.usesound, 50, 1) + to_chat(user, "You connect the glass keyboard.") + 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 ea6e100235a..14b1e9e9c79 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -1,279 +1,279 @@ -//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_screen = "cameras" - light_color = "#a91515" - var/current_network = null - var/obj/machinery/camera/current_camera = null - var/last_pic = 1.0 - var/list/network - var/mapping = 0//For the overview file, interesting bit of code. - var/cache_id = 0 - circuit = /obj/item/circuitboard/security - -/obj/machinery/computer/security/Initialize() - if(!network) - network = current_map.station_networks.Copy() - . = ..() - if(network.len) - current_network = network[1] - -/obj/machinery/computer/security/attack_ai(var/mob/user as mob) - return attack_hand(user) - -/obj/machinery/computer/security/check_eye(var/mob/user as mob) - if (user.stat || ((get_dist(user, src) > 1 || !( user.canmove ) || user.blinded) && !istype(user, /mob/living/silicon))) //user can't see - not sure why canmove is here. - return -1 - if(!current_camera) - return 0 - var/viewflag = current_camera.check_eye(user) - if ( viewflag < 0 ) //camera doesn't work - reset_current() - return viewflag - -/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - if(src.z > 6) return - if(stat & (NOPOWER|BROKEN)) return - if(user.stat) return - - var/data[0] - var/list/all_networks[0] - for(var/nw in network) - all_networks.Add(list(list( - "tag" = nw, - "has_access" = can_access_network(nw) - ))) - - data["networks"] = all_networks - - if(current_network) - data["cameras"] = camera_repository.cameras_in_network(current_network) - data["current_camera"] = current_camera ? current_camera.nano_structure() : null - data["current_network"] = current_network - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800) - - ui.add_template("mapContent", "sec_camera_map_content.tmpl") - ui.add_template("mapHeader", "sec_camera_map_header.tmpl") - - ui.set_initial_data(data) - ui.open() - -/obj/machinery/computer/security/proc/can_access_network(var/nw) - if(nw in network) - return 1 - return 0 - -/obj/machinery/computer/security/Topic(href, href_list) - if(..()) - return 1 - if(href_list["switch_camera"]) - if(src.z>6 || stat&(NOPOWER|BROKEN)) return - if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return - var/obj/machinery/camera/C = locate(href_list["switch_camera"]) in cameranet.cameras - if(!C) - return - if(!(current_network in C.network)) - return - - switch_to_camera(usr, C) - return 1 - else if(href_list["switch_network"]) - if(src.z>6 || stat&(NOPOWER|BROKEN)) return - if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return - if(href_list["switch_network"] in network) - current_network = href_list["switch_network"] - return 1 - else if(href_list["reset"]) - if(src.z>6 || stat&(NOPOWER|BROKEN)) return - if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return - reset_current() - usr.reset_view(current_camera) - return 1 - else - . = ..() - -/obj/machinery/computer/security/attack_hand(var/mob/user as mob) - if (src.z > 6) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - if(stat & (NOPOWER|BROKEN)) return - - if(!isAI(user)) - user.set_machine(src) - user.reset_view(current_camera) - ui_interact(user) - -/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C) - //don't need to check if the camera works for AI because the AI jumps to the camera location and doesn't actually look through cameras. - if(isAI(user)) - var/mob/living/silicon/ai/A = user - // Only allow non-carded AIs to view because the interaction with the eye gets all wonky otherwise. - if(!A.is_in_chassis()) - return 0 - - A.eyeobj.setLoc(get_turf(C)) - A.client.eye = A.eyeobj - return 1 - - if (!C.can_use() || user.stat || (get_dist(user, src) > 1 || user.machine != src || user.blinded || !( user.canmove ) && !istype(user, /mob/living/silicon))) - return 0 - set_current(C) - user.reset_view(current_camera) - check_eye(user) - return 1 - -//Camera control: moving. -/obj/machinery/computer/security/proc/jump_on_click(var/mob/user,var/A) - if(user.machine != src) - return - var/obj/machinery/camera/jump_to - if(istype(A,/obj/machinery/camera)) - jump_to = A - else if(ismob(A)) - if(ishuman(A)) - jump_to = locate() in A:head - else if(isrobot(A)) - jump_to = A:camera - else if(isobj(A)) - jump_to = locate() in A - else if(isturf(A)) - var/best_dist = INFINITY - var/check_area = get_area(A) - - if (!check_area) - return - - for(var/cc in SSmachinery.all_cameras) - var/obj/machinery/camera/camera = cc - if(!camera.loc) - continue - if (camera.loc.loc != check_area) - continue - if(!camera.can_use()) - continue - if(!can_access_camera(camera)) - continue - var/dist = get_dist(camera,A) - if(dist < best_dist) - best_dist = dist - jump_to = camera - if(isnull(jump_to)) - return - if(can_access_camera(jump_to)) - switch_to_camera(user,jump_to) - -/obj/machinery/computer/security/machinery_process() - if(cache_id != camera_repository.camera_cache_id) - cache_id = camera_repository.camera_cache_id - SSnanoui.update_uis(src) - -/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C) - var/list/shared_networks = src.network & C.network - if(shared_networks.len) - return 1 - return 0 - -/obj/machinery/computer/security/proc/set_current(var/obj/machinery/camera/C) - if(current_camera == C) - return - - if(current_camera) - reset_current() - - src.current_camera = C - if(current_camera) - use_power = 2 - var/mob/living/L = current_camera.loc - if(istype(L)) - L.tracking_initiated() - -/obj/machinery/computer/security/proc/reset_current() - if(current_camera) - var/mob/living/L = current_camera.loc - if(istype(L)) - L.tracking_cancelled() - current_camera = null - use_power = 1 - -//Camera control: mouse. -/atom/DblClick() - ..() - if(istype(usr.machine,/obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = usr.machine - console.jump_on_click(usr,src) - -//Camera control: arrow keys. -/mob/living/Move(n,direct) - if(istype(machine,/obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = machine - var/turf/T = get_turf(console.current_camera) - for(var/i;i<10;i++) - T = get_step(T,direct) - console.jump_on_click(src,T) - return - return ..(n,direct) - -/obj/machinery/computer/security/telescreen - name = "Telescreen" - desc = "Used for watching an empty arena." - icon_state = "wallframe" - icon_screen = null - light_range_on = 0 - network = list(NETWORK_THUNDER) - density = 0 - circuit = null - is_holographic = FALSE - -/obj/machinery/computer/security/telescreen/entertainment - name = "entertainment monitor" - desc = "Damn, why do they never have anything interesting on these things?" - icon = 'icons/obj/status_display.dmi' - icon_screen = "entertainment" - light_color = "#FFEEDB" - light_range_on = 2 - circuit = null - -/obj/machinery/computer/security/wooden_tv - name = "security camera monitor" - desc = "An old TV hooked into the stations camera network." - icon_state = "television" - icon_screen = "detective_tv" - circuit = null - light_color = "#3848B3" - light_power_on = 0.5 - -/obj/machinery/computer/security/mining - name = "outpost camera monitor" - desc = "Used to access the various cameras on the outpost." - icon_screen = "miningcameras" - network = list("MINE") - circuit = /obj/item/circuitboard/security/mining - light_color = "#F9BBFC" - -/obj/machinery/computer/security/engineering - name = "engineering camera monitor" - desc = "Used to monitor fires and breaches." - icon_screen = "engineeringcameras" - circuit = /obj/item/circuitboard/security/engineering - light_color = "#FAC54B" - -/obj/machinery/computer/security/engineering/Initialize() - if(!network) - network = engineering_networks.Copy() - . = ..() - -/obj/machinery/computer/security/nuclear - name = "head mounted camera monitor" - desc = "Used to access the built-in cameras in helmets." - icon_screen = "syndicam" - network = list(NETWORK_MERCENARY) - circuit = null - is_holographic = FALSE // I mean, it is, but the holo effect looks terrible with the current merc shuttle floor. - -/obj/machinery/computer/security/nuclear/Initialize() - . = ..() - 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_screen = "cameras" + light_color = "#a91515" + var/current_network = null + var/obj/machinery/camera/current_camera = null + var/last_pic = 1.0 + var/list/network + var/mapping = 0//For the overview file, interesting bit of code. + var/cache_id = 0 + circuit = /obj/item/circuitboard/security + +/obj/machinery/computer/security/Initialize() + if(!network) + network = current_map.station_networks.Copy() + . = ..() + if(network.len) + current_network = network[1] + +/obj/machinery/computer/security/attack_ai(var/mob/user as mob) + return attack_hand(user) + +/obj/machinery/computer/security/check_eye(var/mob/user as mob) + if (user.stat || ((get_dist(user, src) > 1 || !( user.canmove ) || user.blinded) && !istype(user, /mob/living/silicon))) //user can't see - not sure why canmove is here. + return -1 + if(!current_camera) + return 0 + var/viewflag = current_camera.check_eye(user) + if ( viewflag < 0 ) //camera doesn't work + reset_current() + return viewflag + +/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) + if(src.z > 6) return + if(stat & (NOPOWER|BROKEN)) return + if(user.stat) return + + var/data[0] + var/list/all_networks[0] + for(var/nw in network) + all_networks.Add(list(list( + "tag" = nw, + "has_access" = can_access_network(nw) + ))) + + data["networks"] = all_networks + + if(current_network) + data["cameras"] = camera_repository.cameras_in_network(current_network) + data["current_camera"] = current_camera ? current_camera.nano_structure() : null + data["current_network"] = current_network + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800) + + ui.add_template("mapContent", "sec_camera_map_content.tmpl") + ui.add_template("mapHeader", "sec_camera_map_header.tmpl") + + ui.set_initial_data(data) + ui.open() + +/obj/machinery/computer/security/proc/can_access_network(var/nw) + if(nw in network) + return 1 + return 0 + +/obj/machinery/computer/security/Topic(href, href_list) + if(..()) + return 1 + if(href_list["switch_camera"]) + if(src.z>6 || stat&(NOPOWER|BROKEN)) return + if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return + var/obj/machinery/camera/C = locate(href_list["switch_camera"]) in cameranet.cameras + if(!C) + return + if(!(current_network in C.network)) + return + + switch_to_camera(usr, C) + return 1 + else if(href_list["switch_network"]) + if(src.z>6 || stat&(NOPOWER|BROKEN)) return + if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return + if(href_list["switch_network"] in network) + current_network = href_list["switch_network"] + return 1 + else if(href_list["reset"]) + if(src.z>6 || stat&(NOPOWER|BROKEN)) return + if(usr.stat || ((get_dist(usr, src) > 1 || !( usr.canmove ) || usr.blinded) && !istype(usr, /mob/living/silicon))) return + reset_current() + usr.reset_view(current_camera) + return 1 + else + . = ..() + +/obj/machinery/computer/security/attack_hand(var/mob/user as mob) + if (src.z > 6) + to_chat(user, "Unable to establish a connection: You're too far away from the station!") + return + if(stat & (NOPOWER|BROKEN)) return + + if(!isAI(user)) + user.set_machine(src) + user.reset_view(current_camera) + ui_interact(user) + +/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C) + //don't need to check if the camera works for AI because the AI jumps to the camera location and doesn't actually look through cameras. + if(isAI(user)) + var/mob/living/silicon/ai/A = user + // Only allow non-carded AIs to view because the interaction with the eye gets all wonky otherwise. + if(!A.is_in_chassis()) + return 0 + + A.eyeobj.setLoc(get_turf(C)) + A.client.eye = A.eyeobj + return 1 + + if (!C.can_use() || user.stat || (get_dist(user, src) > 1 || user.machine != src || user.blinded || !( user.canmove ) && !istype(user, /mob/living/silicon))) + return 0 + set_current(C) + user.reset_view(current_camera) + check_eye(user) + return 1 + +//Camera control: moving. +/obj/machinery/computer/security/proc/jump_on_click(var/mob/user,var/A) + if(user.machine != src) + return + var/obj/machinery/camera/jump_to + if(istype(A,/obj/machinery/camera)) + jump_to = A + else if(ismob(A)) + if(ishuman(A)) + jump_to = locate() in A:head + else if(isrobot(A)) + jump_to = A:camera + else if(isobj(A)) + jump_to = locate() in A + else if(isturf(A)) + var/best_dist = INFINITY + var/check_area = get_area(A) + + if (!check_area) + return + + for(var/cc in SSmachinery.all_cameras) + var/obj/machinery/camera/camera = cc + if(!camera.loc) + continue + if (camera.loc.loc != check_area) + continue + if(!camera.can_use()) + continue + if(!can_access_camera(camera)) + continue + var/dist = get_dist(camera,A) + if(dist < best_dist) + best_dist = dist + jump_to = camera + if(isnull(jump_to)) + return + if(can_access_camera(jump_to)) + switch_to_camera(user,jump_to) + +/obj/machinery/computer/security/machinery_process() + if(cache_id != camera_repository.camera_cache_id) + cache_id = camera_repository.camera_cache_id + SSnanoui.update_uis(src) + +/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C) + var/list/shared_networks = src.network & C.network + if(shared_networks.len) + return 1 + return 0 + +/obj/machinery/computer/security/proc/set_current(var/obj/machinery/camera/C) + if(current_camera == C) + return + + if(current_camera) + reset_current() + + src.current_camera = C + if(current_camera) + use_power = 2 + var/mob/living/L = current_camera.loc + if(istype(L)) + L.tracking_initiated() + +/obj/machinery/computer/security/proc/reset_current() + if(current_camera) + var/mob/living/L = current_camera.loc + if(istype(L)) + L.tracking_cancelled() + current_camera = null + use_power = 1 + +//Camera control: mouse. +/atom/DblClick() + ..() + if(istype(usr.machine,/obj/machinery/computer/security)) + var/obj/machinery/computer/security/console = usr.machine + console.jump_on_click(usr,src) + +//Camera control: arrow keys. +/mob/living/Move(n,direct) + if(istype(machine,/obj/machinery/computer/security)) + var/obj/machinery/computer/security/console = machine + var/turf/T = get_turf(console.current_camera) + for(var/i;i<10;i++) + T = get_step(T,direct) + console.jump_on_click(src,T) + return + return ..(n,direct) + +/obj/machinery/computer/security/telescreen + name = "Telescreen" + desc = "Used for watching an empty arena." + icon_state = "wallframe" + icon_screen = null + light_range_on = 0 + network = list(NETWORK_THUNDER) + density = 0 + circuit = null + is_holographic = FALSE + +/obj/machinery/computer/security/telescreen/entertainment + name = "entertainment monitor" + desc = "Damn, why do they never have anything interesting on these things?" + icon = 'icons/obj/status_display.dmi' + icon_screen = "entertainment" + light_color = "#FFEEDB" + light_range_on = 2 + circuit = null + +/obj/machinery/computer/security/wooden_tv + name = "security camera monitor" + desc = "An old TV hooked into the stations camera network." + icon_state = "television" + icon_screen = "detective_tv" + circuit = null + light_color = "#3848B3" + light_power_on = 0.5 + +/obj/machinery/computer/security/mining + name = "outpost camera monitor" + desc = "Used to access the various cameras on the outpost." + icon_screen = "miningcameras" + network = list("MINE") + circuit = /obj/item/circuitboard/security/mining + light_color = "#F9BBFC" + +/obj/machinery/computer/security/engineering + name = "engineering camera monitor" + desc = "Used to monitor fires and breaches." + icon_screen = "engineeringcameras" + circuit = /obj/item/circuitboard/security/engineering + light_color = "#FAC54B" + +/obj/machinery/computer/security/engineering/Initialize() + if(!network) + network = engineering_networks.Copy() + . = ..() + +/obj/machinery/computer/security/nuclear + name = "head mounted camera monitor" + desc = "Used to access the built-in cameras in helmets." + icon_screen = "syndicam" + network = list(NETWORK_MERCENARY) + circuit = null + is_holographic = FALSE // I mean, it is, but the holo effect looks terrible with the current merc shuttle floor. + +/obj/machinery/computer/security/nuclear/Initialize() + . = ..() + req_access = list(150) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index edbc4e3e128..c4d43b5c3ba 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -1,38 +1,38 @@ -/obj/machinery/computer/crew - name = "crew monitoring computer" - desc = "Used to monitor active health sensors built into most of the crew's uniforms." - - icon_screen = "crew" - light_color = "#315ab4" - use_power = 1 - idle_power_usage = 250 - active_power_usage = 500 - circuit = /obj/item/circuitboard/crew - var/datum/nano_module/crew_monitor/crew_monitor - -/obj/machinery/computer/crew/Initialize() - . = ..() - crew_monitor = new(src) - -/obj/machinery/computer/crew/Destroy() - qdel(crew_monitor) - crew_monitor = null - return ..() - -/obj/machinery/computer/crew/attack_ai(mob/user) - ui_interact(user) - -/obj/machinery/computer/crew/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - ui_interact(user) - -/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - crew_monitor.ui_interact(user, ui_key, ui, force_open, state) - -/obj/machinery/computer/crew/nano_container() - return crew_monitor - -/obj/machinery/computer/crew/interact(mob/user) - crew_monitor.ui_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_screen = "crew" + light_color = "#315ab4" + use_power = 1 + idle_power_usage = 250 + active_power_usage = 500 + circuit = /obj/item/circuitboard/crew + var/datum/nano_module/crew_monitor/crew_monitor + +/obj/machinery/computer/crew/Initialize() + . = ..() + crew_monitor = new(src) + +/obj/machinery/computer/crew/Destroy() + qdel(crew_monitor) + crew_monitor = null + return ..() + +/obj/machinery/computer/crew/attack_ai(mob/user) + ui_interact(user) + +/obj/machinery/computer/crew/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + ui_interact(user) + +/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + crew_monitor.ui_interact(user, ui_key, ui, force_open, state) + +/obj/machinery/computer/crew/nano_container() + return crew_monitor + +/obj/machinery/computer/crew/interact(mob/user) + crew_monitor.ui_interact(user) diff --git a/code/game/machinery/computer/prisonshuttle.dm b/code/game/machinery/computer/prisonshuttle.dm index 12dcfe55c96..bd690d8d7d6 100644 --- a/code/game/machinery/computer/prisonshuttle.dm +++ b/code/game/machinery/computer/prisonshuttle.dm @@ -1,240 +1,240 @@ -//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" - - icon_screen = "syndishuttle" - light_color = "#00ffff" - req_access = list(access_security) - circuit = /obj/item/circuitboard/prison_shuttle - var/temp = null - var/hacked = 0 - var/allowedtocall = 0 - var/prison_break = 0 - - attack_ai(var/mob/user as mob) - return src.attack_hand(user) - - attackby(I as obj, user as mob) - if(I.isscrewdriver()) - playsound(src.loc, P.usesound, 50, 1) - if(do_after(user, 20/I.toolspeed)) - var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) - var/obj/item/circuitboard/prison_shuttle/M = new /obj/item/circuitboard/prison_shuttle( A ) - for (var/obj/C in src) - C.forceMove(src.loc) - A.circuit = M - A.anchored = 1 - - if (src.stat & BROKEN) - to_chat(user, "The broken glass falls out.") - new /obj/item/material/shard( src.loc ) - A.state = 3 - A.icon_state = "3" - else - to_chat(user, "You disconnect the monitor.") - A.state = 4 - A.icon_state = "4" - - qdel(src) - else - return src.attack_hand(user) - - - 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 - - - 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 - - - proc/prison_can_move() - if(prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return 0 - else return 1 - - - 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 - - - proc/post_signal(var/command) - var/datum/radio_frequency/frequency = SSradio.return_frequency(1311) - if(!frequency) return - var/datum/signal/status_signal = new - status_signal.source = src - status_signal.transmission_method = 1 - status_signal.data["command"] = command - frequency.post_signal(src, status_signal) - return - - - 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_animal/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" + + icon_screen = "syndishuttle" + light_color = "#00ffff" + req_access = list(access_security) + circuit = /obj/item/circuitboard/prison_shuttle + var/temp = null + var/hacked = 0 + var/allowedtocall = 0 + var/prison_break = 0 + + attack_ai(var/mob/user as mob) + return src.attack_hand(user) + + attackby(I as obj, user as mob) + if(I.isscrewdriver()) + playsound(src.loc, P.usesound, 50, 1) + if(do_after(user, 20/I.toolspeed)) + var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) + var/obj/item/circuitboard/prison_shuttle/M = new /obj/item/circuitboard/prison_shuttle( A ) + for (var/obj/C in src) + C.forceMove(src.loc) + A.circuit = M + A.anchored = 1 + + if (src.stat & BROKEN) + to_chat(user, "The broken glass falls out.") + new /obj/item/material/shard( src.loc ) + A.state = 3 + A.icon_state = "3" + else + to_chat(user, "You disconnect the monitor.") + A.state = 4 + A.icon_state = "4" + + qdel(src) + else + return src.attack_hand(user) + + + 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 + + + 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 + + + proc/prison_can_move() + if(prison_shuttle_moving_to_station || prison_shuttle_moving_to_prison) return 0 + else return 1 + + + 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 + + + proc/post_signal(var/command) + var/datum/radio_frequency/frequency = SSradio.return_frequency(1311) + if(!frequency) return + var/datum/signal/status_signal = new + status_signal.source = src + status_signal.transmission_method = 1 + status_signal.data["command"] = command + frequency.post_signal(src, status_signal) + return + + + 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_animal/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 c65acbc4789..2b9f3ea451c 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -1,219 +1,219 @@ -/obj/machinery/computer/robotics - name = "robotics control console" - desc = "Used to remotely lockdown or detonate linked cyborgs." - icon = 'icons/obj/computer.dmi' - - icon_screen = "robot" - light_color = "#a97faa" - req_access = list(access_robotics) - circuit = /obj/item/circuitboard/robotics - - var/safety = 1 - - -/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - data["robots"] = get_cyborgs(user) - data["safety"] = safety - // Also applies for cyborgs. Hides the manual self-destruct button. - data["is_ai"] = issilicon(user) - - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "robot_control.tmpl", "Robotic Control Console", 400, 500) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/robotics/Topic(href, href_list) - if(..()) - return - var/mob/user = usr - if(!src.allowed(user)) - to_chat(user, "Access Denied") - return - - // Destroys the cyborg - if(href_list["detonate"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"]) - if(!target || !istype(target)) - return - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - // Cyborgs may blow up themselves via the console - if(isrobot(user) && user != target) - to_chat(user, "Access Denied.") - return - var/choice = input("Really detonate [target.name]?") in list ("Yes", "No") - if(choice != "Yes") - return - if(!target || !istype(target)) - return - - // Antagonistic cyborgs? Left here for downstream - if(target.mind && target.mind.special_role && target.emagged) - to_chat(target, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") - target.ResetSecurityCodes() - return - - if(target.emagged) - to_chat(user, "Access Denied. Safety protocols are disabled.") - return - - else - message_admins("[key_name_admin(usr)] detonated [target.name]!") - log_game("[key_name(usr)] detonated [target.name]!",ckey=key_name(usr)) - to_chat(target, "Self-destruct command received.") - spawn(10) - target.self_destruct() - - - - // Locks or unlocks the cyborg - else if (href_list["lockdown"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"]) - if(!target || !istype(target)) - return - - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - - if(isrobot(user)) - to_chat(user, "Access Denied.") - return - - if(target.emagged) - return - - var/choice = input("Really [target.lockcharge ? "unlock" : "lockdown"] [target.name] ?") in list ("Yes", "No") - if(choice != "Yes") - return - - if(!target || !istype(target)) - return - - target.SetLockdown(!target.lockcharge) // Toggle. - message_admins("[key_name_admin(usr)] [target.lockcharge ? "locked down" : "released"] [target.name]!") - log_game("[key_name(usr)] [target.lockcharge ? "locked down" : "released"] [target.name]!",ckey=key_name(usr)) - to_chat(target, (target.lockcharge ? "You have been locked down!" : "Your lockdown has been lifted!")) - - // Remotely hacks the cyborg. Only antag AIs can do this and only to linked cyborgs. - else if (href_list["hack"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["hack"]) - if(!target || !istype(target)) - return - - // Antag AI checks - if(!istype(user, /mob/living/silicon/ai) || !(user.mind.special_role && user.mind.original == user)) - to_chat(user, "Access Denied") - return - - if(target.emagged) - to_chat(user, "Robot is already hacked.") - return - - var/choice = input("Really hack [target.name]? This cannot be undone.") in list("Yes", "No") - if(choice != "Yes") - return - - if(!target || !istype(target)) - return - - message_admins("[key_name_admin(usr)] emagged [target.name] using robotic console!") - log_game("[key_name(usr)] emagged [target.name] using robotic console!",ckey=key_name(usr)) - target.emagged = 1 - to_chat(target, "Failsafe protocols overriden. New tools available.") - - // Arms the emergency self-destruct system - else if(href_list["arm"]) - if(istype(user, /mob/living/silicon)) - to_chat(user, "Access Denied") - return - - safety = !safety - to_chat(user, "You [safety ? "disarm" : "arm"] the emergency self destruct") - - // Destroys all accessible cyborgs if safety is disabled - else if(href_list["nuke"]) - if(istype(user, /mob/living/silicon)) - to_chat(user, "Access Denied") - return - if(safety) - to_chat(user, "Self-destruct aborted - safety active") - return - - message_admins("[key_name_admin(usr)] detonated all cyborgs!") - log_game("[key_name(usr)] detonated all cyborgs!",ckey=key_name(usr)) - - 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 - if(R.emagged) - continue - to_chat(R, "Self-destruct command received.") - spawn(10) - R.self_destruct() - - -// Proc: get_cyborgs() -// Parameters: 1 (operator - mob which is operating the console.) -// Description: Returns NanoUI-friendly list of accessible cyborgs. -/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/operator) - var/list/robots = list() - - for(var/mob/living/silicon/robot/R in mob_list) - // Ignore drones - if(istype(R, /mob/living/silicon/robot/drone)) - continue - // Ignore antagonistic cyborgs - if(R.scrambledcodes) - continue - - var/list/robot = list() - robot["name"] = R.name - if(R.stat) - robot["status"] = "Not Responding" - else if (R.lockcharge) // changed this from !R.canmove to R.lockcharge because of issues with lockdown and chairs - robot["status"] = "Lockdown" - else - robot["status"] = "Operational" - - if(R.cell) - robot["cell"] = 1 - robot["cell_capacity"] = R.cell.maxcharge - robot["cell_current"] = R.cell.charge - robot["cell_percentage"] = round(R.cell.percent()) - else - robot["cell"] = 0 - - robot["module"] = R.module ? R.module.name : "None" - robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None" - robot["hackable"] = 0 - // Antag AIs know whether linked cyborgs are hacked or not. - if(operator && istype(operator, /mob/living/silicon/ai) && (R.connected_ai == operator) && (operator.mind.special_role && operator.mind.original == operator)) - robot["hacked"] = R.emagged ? 1 : 0 - robot["hackable"] = R.emagged? 0 : 1 - robots.Add(list(robot)) - return robots - -// Proc: get_cyborg_by_name() -// Parameters: 1 (name - Cyborg we are trying to find) -// Description: Helper proc for finding cyborg by name -/obj/machinery/computer/robotics/proc/get_cyborg_by_name(var/name) - if (!name) - return - for(var/mob/living/silicon/robot/R in mob_list) - if(R.name == name) - return R +/obj/machinery/computer/robotics + name = "robotics control console" + desc = "Used to remotely lockdown or detonate linked cyborgs." + icon = 'icons/obj/computer.dmi' + + icon_screen = "robot" + light_color = "#a97faa" + req_access = list(access_robotics) + circuit = /obj/item/circuitboard/robotics + + var/safety = 1 + + +/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + data["robots"] = get_cyborgs(user) + data["safety"] = safety + // Also applies for cyborgs. Hides the manual self-destruct button. + data["is_ai"] = issilicon(user) + + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "robot_control.tmpl", "Robotic Control Console", 400, 500) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/robotics/Topic(href, href_list) + if(..()) + return + var/mob/user = usr + if(!src.allowed(user)) + to_chat(user, "Access Denied") + return + + // Destroys the cyborg + if(href_list["detonate"]) + var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"]) + if(!target || !istype(target)) + return + if(isAI(user) && (target.connected_ai != user)) + to_chat(user, "Access Denied. This robot is not linked to you.") + return + // Cyborgs may blow up themselves via the console + if(isrobot(user) && user != target) + to_chat(user, "Access Denied.") + return + var/choice = input("Really detonate [target.name]?") in list ("Yes", "No") + if(choice != "Yes") + return + if(!target || !istype(target)) + return + + // Antagonistic cyborgs? Left here for downstream + if(target.mind && target.mind.special_role && target.emagged) + to_chat(target, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") + target.ResetSecurityCodes() + return + + if(target.emagged) + to_chat(user, "Access Denied. Safety protocols are disabled.") + return + + else + message_admins("[key_name_admin(usr)] detonated [target.name]!") + log_game("[key_name(usr)] detonated [target.name]!",ckey=key_name(usr)) + to_chat(target, "Self-destruct command received.") + spawn(10) + target.self_destruct() + + + + // Locks or unlocks the cyborg + else if (href_list["lockdown"]) + var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"]) + if(!target || !istype(target)) + return + + if(isAI(user) && (target.connected_ai != user)) + to_chat(user, "Access Denied. This robot is not linked to you.") + return + + if(isrobot(user)) + to_chat(user, "Access Denied.") + return + + if(target.emagged) + return + + var/choice = input("Really [target.lockcharge ? "unlock" : "lockdown"] [target.name] ?") in list ("Yes", "No") + if(choice != "Yes") + return + + if(!target || !istype(target)) + return + + target.SetLockdown(!target.lockcharge) // Toggle. + message_admins("[key_name_admin(usr)] [target.lockcharge ? "locked down" : "released"] [target.name]!") + log_game("[key_name(usr)] [target.lockcharge ? "locked down" : "released"] [target.name]!",ckey=key_name(usr)) + to_chat(target, (target.lockcharge ? "You have been locked down!" : "Your lockdown has been lifted!")) + + // Remotely hacks the cyborg. Only antag AIs can do this and only to linked cyborgs. + else if (href_list["hack"]) + var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["hack"]) + if(!target || !istype(target)) + return + + // Antag AI checks + if(!istype(user, /mob/living/silicon/ai) || !(user.mind.special_role && user.mind.original == user)) + to_chat(user, "Access Denied") + return + + if(target.emagged) + to_chat(user, "Robot is already hacked.") + return + + var/choice = input("Really hack [target.name]? This cannot be undone.") in list("Yes", "No") + if(choice != "Yes") + return + + if(!target || !istype(target)) + return + + message_admins("[key_name_admin(usr)] emagged [target.name] using robotic console!") + log_game("[key_name(usr)] emagged [target.name] using robotic console!",ckey=key_name(usr)) + target.emagged = 1 + to_chat(target, "Failsafe protocols overriden. New tools available.") + + // Arms the emergency self-destruct system + else if(href_list["arm"]) + if(istype(user, /mob/living/silicon)) + to_chat(user, "Access Denied") + return + + safety = !safety + to_chat(user, "You [safety ? "disarm" : "arm"] the emergency self destruct") + + // Destroys all accessible cyborgs if safety is disabled + else if(href_list["nuke"]) + if(istype(user, /mob/living/silicon)) + to_chat(user, "Access Denied") + return + if(safety) + to_chat(user, "Self-destruct aborted - safety active") + return + + message_admins("[key_name_admin(usr)] detonated all cyborgs!") + log_game("[key_name(usr)] detonated all cyborgs!",ckey=key_name(usr)) + + 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 + if(R.emagged) + continue + to_chat(R, "Self-destruct command received.") + spawn(10) + R.self_destruct() + + +// Proc: get_cyborgs() +// Parameters: 1 (operator - mob which is operating the console.) +// Description: Returns NanoUI-friendly list of accessible cyborgs. +/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/operator) + var/list/robots = list() + + for(var/mob/living/silicon/robot/R in mob_list) + // Ignore drones + if(istype(R, /mob/living/silicon/robot/drone)) + continue + // Ignore antagonistic cyborgs + if(R.scrambledcodes) + continue + + var/list/robot = list() + robot["name"] = R.name + if(R.stat) + robot["status"] = "Not Responding" + else if (R.lockcharge) // changed this from !R.canmove to R.lockcharge because of issues with lockdown and chairs + robot["status"] = "Lockdown" + else + robot["status"] = "Operational" + + if(R.cell) + robot["cell"] = 1 + robot["cell_capacity"] = R.cell.maxcharge + robot["cell_current"] = R.cell.charge + robot["cell_percentage"] = round(R.cell.percent()) + else + robot["cell"] = 0 + + robot["module"] = R.module ? R.module.name : "None" + robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None" + robot["hackable"] = 0 + // Antag AIs know whether linked cyborgs are hacked or not. + if(operator && istype(operator, /mob/living/silicon/ai) && (R.connected_ai == operator) && (operator.mind.special_role && operator.mind.original == operator)) + robot["hacked"] = R.emagged ? 1 : 0 + robot["hackable"] = R.emagged? 0 : 1 + robots.Add(list(robot)) + return robots + +// Proc: get_cyborg_by_name() +// Parameters: 1 (name - Cyborg we are trying to find) +// Description: Helper proc for finding cyborg by name +/obj/machinery/computer/robotics/proc/get_cyborg_by_name(var/name) + if (!name) + return + for(var/mob/living/silicon/robot/R in mob_list) + if(R.name == name) + return R diff --git a/code/game/machinery/computer/shuttle.dm b/code/game/machinery/computer/shuttle.dm index 39292bf7321..65df25ceb74 100644 --- a/code/game/machinery/computer/shuttle.dm +++ b/code/game/machinery/computer/shuttle.dm @@ -1,77 +1,77 @@ -/obj/machinery/computer/shuttle - name = "Shuttle" - desc = "For shuttle control." - - is_holographic = FALSE - icon_screen = "shuttle" - light_color = "#00ffff" - var/auth_need = 3.0 - var/list/authorized = list( ) - - - attackby(var/obj/item/card/W as obj, var/mob/user as mob) - if(stat & (BROKEN|NOPOWER)) return - if ((!( istype(W, /obj/item/card) ) || !(ROUND_IS_STARTED) || emergency_shuttle.location() || !( user ))) return - if (istype(W, /obj/item/card/id)||istype(W, /obj/item/device/pda)) - var/obj/item/card/id/id = W - if (istype(W, /obj/item/device/pda)) - var/obj/item/device/pda/pda = W - id = pda.id - - if (!id) - return - - if (!id.access) //no access - to_chat(user, "The access level of [id.registered_name]\'s card is not high enough. ") - return - - var/list/cardaccess = id.access - if(!istype(cardaccess, /list) || !cardaccess.len) //no access - to_chat(user, "The access level of [id.registered_name]\'s card is not high enough. ") - return - - if(!(access_heads in id.access)) //doesn't have this access - to_chat(user, "The access level of [id.registered_name]\'s card is not high enough. ") - return 0 - - var/choice = 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", "Authorize", "Repeal", "Abort") - if(emergency_shuttle.location() && user.get_active_hand() != id) - return 0 - switch(choice) - if("Authorize") - src.authorized -= id.registered_name - src.authorized += id.registered_name - if (src.auth_need - src.authorized.len > 0) - message_admins("[key_name_admin(user)] has authorized early shuttle launch") - log_game("[key_name(user)] has authorized early shuttle launch",ckey=key_name(user)) - to_world(text("Alert: [] authorizations needed until shuttle is launched early", src.auth_need - src.authorized.len)) - else - message_admins("[key_name_admin(user)] has launched the shuttle") - log_game("[key_name(user)] has launched the shuttle early",ckey=key_name(user)) - 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 -= id.registered_name - to_world(text("Alert: [] authorizations needed until shuttle is launched early", src.auth_need - src.authorized.len)) - - 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/card/emag) && !emagged) - var/choice = alert(user, "Would you like to launch the shuttle?","Shuttle control", "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." + + is_holographic = FALSE + icon_screen = "shuttle" + light_color = "#00ffff" + var/auth_need = 3.0 + var/list/authorized = list( ) + + + attackby(var/obj/item/card/W as obj, var/mob/user as mob) + if(stat & (BROKEN|NOPOWER)) return + if ((!( istype(W, /obj/item/card) ) || !(ROUND_IS_STARTED) || emergency_shuttle.location() || !( user ))) return + if (istype(W, /obj/item/card/id)||istype(W, /obj/item/device/pda)) + var/obj/item/card/id/id = W + if (istype(W, /obj/item/device/pda)) + var/obj/item/device/pda/pda = W + id = pda.id + + if (!id) + return + + if (!id.access) //no access + to_chat(user, "The access level of [id.registered_name]\'s card is not high enough. ") + return + + var/list/cardaccess = id.access + if(!istype(cardaccess, /list) || !cardaccess.len) //no access + to_chat(user, "The access level of [id.registered_name]\'s card is not high enough. ") + return + + if(!(access_heads in id.access)) //doesn't have this access + to_chat(user, "The access level of [id.registered_name]\'s card is not high enough. ") + return 0 + + var/choice = 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", "Authorize", "Repeal", "Abort") + if(emergency_shuttle.location() && user.get_active_hand() != id) + return 0 + switch(choice) + if("Authorize") + src.authorized -= id.registered_name + src.authorized += id.registered_name + if (src.auth_need - src.authorized.len > 0) + message_admins("[key_name_admin(user)] has authorized early shuttle launch") + log_game("[key_name(user)] has authorized early shuttle launch",ckey=key_name(user)) + to_world(text("Alert: [] authorizations needed until shuttle is launched early", src.auth_need - src.authorized.len)) + else + message_admins("[key_name_admin(user)] has launched the shuttle") + log_game("[key_name(user)] has launched the shuttle early",ckey=key_name(user)) + 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 -= id.registered_name + to_world(text("Alert: [] authorizations needed until shuttle is launched early", src.auth_need - src.authorized.len)) + + 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/card/emag) && !emagged) + var/choice = alert(user, "Would you like to launch the shuttle?","Shuttle control", "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 5fd99db9892..05220477b4c 100644 --- a/code/game/machinery/computer/specops_shuttle.dm +++ b/code/game/machinery/computer/specops_shuttle.dm @@ -1,329 +1,329 @@ -//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 6000 //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_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, "Bubble", "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, "Bubble", "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_animal/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 [current_map.boss_name]. Operation has ended!") - - specops_shuttle_at_station = 0 - - for(var/obj/machinery/computer/specops_shuttle/S in SSmachinery.all_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, "Bubble", "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, "Bubble", "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 [current_map.station_name]. Commence operation!") - - for(var/obj/machinery/computer/specops_shuttle/S in SSmachinery.all_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 SSmachinery.all_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 [current_map.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 [current_map.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, "[current_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 [current_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 [current_map.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 6000 //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_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, "Bubble", "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, "Bubble", "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_animal/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 [current_map.boss_name]. Operation has ended!") + + specops_shuttle_at_station = 0 + + for(var/obj/machinery/computer/specops_shuttle/S in SSmachinery.all_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, "Bubble", "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, "Bubble", "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 [current_map.station_name]. Commence operation!") + + for(var/obj/machinery/computer/specops_shuttle/S in SSmachinery.all_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 SSmachinery.all_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 [current_map.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 [current_map.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, "[current_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 [current_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 [current_map.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 55d912a4345..cf559a76786 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -1,65 +1,65 @@ - -/obj/machinery/computer/station_alert - name = "station alert console" - desc = "Used to access the station's automated alert system." - - icon_screen = "alert:0" - light_color = LIGHT_COLOR_CYAN - circuit = /obj/item/circuitboard/stationalert - var/datum/nano_module/alarm_monitor/alarm_monitor - var/monitor_type = /datum/nano_module/alarm_monitor - -/obj/machinery/computer/station_alert/engineering - monitor_type = /datum/nano_module/alarm_monitor/engineering - -/obj/machinery/computer/station_alert/security - monitor_type = /datum/nano_module/alarm_monitor/security - -/obj/machinery/computer/station_alert/all - monitor_type = /datum/nano_module/alarm_monitor/all - -/obj/machinery/computer/station_alert/Initialize() - alarm_monitor = new monitor_type(src) - alarm_monitor.register_alarm(src, /obj/machinery/computer/station_alert/update_icon) - . = ..() - if(monitor_type) - register_monitor(new monitor_type(src)) - -/obj/machinery/computer/station_alert/Destroy() - . = ..() - unregister_monitor() - -/obj/machinery/computer/station_alert/proc/register_monitor(var/datum/nano_module/alarm_monitor/monitor) - if(monitor.host != src) - return - - alarm_monitor = monitor - alarm_monitor.register_alarm(src, /obj/machinery/computer/station_alert/update_icon) - -/obj/machinery/computer/station_alert/proc/unregister_monitor() - if(alarm_monitor) - alarm_monitor.unregister_alarm(src) - qdel(alarm_monitor) - alarm_monitor = null - -/obj/machinery/computer/station_alert/attack_ai(mob/user) - ui_interact(user) - -/obj/machinery/computer/station_alert/attack_hand(mob/user) - ui_interact(user) - -/obj/machinery/computer/station_alert/ui_interact(mob/user) - if(alarm_monitor) - alarm_monitor.ui_interact(user) - -/obj/machinery/computer/station_alert/nano_container() - return alarm_monitor - -/obj/machinery/computer/station_alert/update_icon() - icon_screen = initial(icon_screen) - if(!(stat & (BROKEN|NOPOWER))) - if(alarm_monitor) - var/list/alarms = alarm_monitor.major_alarms() - if(alarms.len) - icon_screen = "alert:2" - ..() + +/obj/machinery/computer/station_alert + name = "station alert console" + desc = "Used to access the station's automated alert system." + + icon_screen = "alert:0" + light_color = LIGHT_COLOR_CYAN + circuit = /obj/item/circuitboard/stationalert + var/datum/nano_module/alarm_monitor/alarm_monitor + var/monitor_type = /datum/nano_module/alarm_monitor + +/obj/machinery/computer/station_alert/engineering + monitor_type = /datum/nano_module/alarm_monitor/engineering + +/obj/machinery/computer/station_alert/security + monitor_type = /datum/nano_module/alarm_monitor/security + +/obj/machinery/computer/station_alert/all + monitor_type = /datum/nano_module/alarm_monitor/all + +/obj/machinery/computer/station_alert/Initialize() + alarm_monitor = new monitor_type(src) + alarm_monitor.register_alarm(src, /obj/machinery/computer/station_alert/update_icon) + . = ..() + if(monitor_type) + register_monitor(new monitor_type(src)) + +/obj/machinery/computer/station_alert/Destroy() + . = ..() + unregister_monitor() + +/obj/machinery/computer/station_alert/proc/register_monitor(var/datum/nano_module/alarm_monitor/monitor) + if(monitor.host != src) + return + + alarm_monitor = monitor + alarm_monitor.register_alarm(src, /obj/machinery/computer/station_alert/update_icon) + +/obj/machinery/computer/station_alert/proc/unregister_monitor() + if(alarm_monitor) + alarm_monitor.unregister_alarm(src) + qdel(alarm_monitor) + alarm_monitor = null + +/obj/machinery/computer/station_alert/attack_ai(mob/user) + ui_interact(user) + +/obj/machinery/computer/station_alert/attack_hand(mob/user) + ui_interact(user) + +/obj/machinery/computer/station_alert/ui_interact(mob/user) + if(alarm_monitor) + alarm_monitor.ui_interact(user) + +/obj/machinery/computer/station_alert/nano_container() + return alarm_monitor + +/obj/machinery/computer/station_alert/update_icon() + icon_screen = initial(icon_screen) + if(!(stat & (BROKEN|NOPOWER))) + if(alarm_monitor) + var/list/alarms = alarm_monitor.major_alarms() + if(alarms.len) + icon_screen = "alert:2" + ..() diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm index ab7627993de..ae203b776bb 100644 --- a/code/game/machinery/computer/syndicate_specops_shuttle.dm +++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm @@ -1,258 +1,258 @@ -//Config stuff -#define SYNDICATE_ELITE_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. -#define SYNDICATE_ELITE_STATION_AREATYPE "/area/shuttle/syndicate_elite/station" //Type of the spec ops shuttle area for station -#define SYNDICATE_ELITE_DOCK_AREATYPE "/area/shuttle/syndicate_elite/mothership" //Type of the spec ops shuttle area for dock - -var/syndicate_elite_shuttle_moving_to_station = 0 -var/syndicate_elite_shuttle_moving_to_mothership = 0 -var/syndicate_elite_shuttle_at_station = 0 -var/syndicate_elite_shuttle_can_send = 1 -var/syndicate_elite_shuttle_time = 0 -var/syndicate_elite_shuttle_timeleft = 0 - -/obj/machinery/computer/syndicate_elite_shuttle - name = "elite syndicate squad shuttle control console" - icon = 'icons/obj/computer.dmi' - - icon_screen = "syndishuttle" - light_color = "#00ffff" - req_access = list(access_cent_specops) - var/temp = null - var/hacked = 0 - var/allowedtocall = 0 - -/proc/syndicate_elite_process() - var/area/syndicate_mothership/control/syndicate_ship = locate()//To find announcer. This area should exist for this proc to work. - var/area/syndicate_mothership/elite_squad/elite_squad = locate()//Where is the specops area located? - var/mob/living/silicon/decoy/announcer = locate() in syndicate_ship//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "THE SYNDICATE ELITE SHUTTLE IS PREPARING FOR LAUNCH"//Initial message shown. - if(announcer) - announcer.say(message) - // message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" - // announcer.say(message) - - while(syndicate_elite_shuttle_time - world.timeofday > 0) - var/ticksleft = syndicate_elite_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - syndicate_elite_shuttle_time = world.timeofday // midnight rollover - syndicate_elite_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(syndicate_elite_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.say(message) - 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) - - syndicate_elite_shuttle_moving_to_station = 0 - syndicate_elite_shuttle_moving_to_mothership = 0 - - syndicate_elite_shuttle_at_station = 1 - if (syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return - - if (!syndicate_elite_can_move()) - to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") - return - - sleep(600) -/* - //Begin Marauder launchpad. - spawn(0)//So it parallel processes it. - for(var/obj/machinery/door/poddoor/M in elite_squad) - 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 elite_squad) - 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/poddoor/M in elite_squad) - 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() - */ - elite_squad.readyreset()//Reset firealarm after the team launched. - //End Marauder launchpad. -/* - var/obj/explosionmarker = locate("Syndicate Breach Area") - if(explosionmarker) - var/turf/simulated/T = explosionmarker.loc - if(T) - explosion(T,4,6,8,10,0) - - sleep(40) -// proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1) - -*/ - var/area/start_location = locate(/area/shuttle/syndicate_elite/mothership) - var/area/end_location = locate(/area/shuttle/syndicate_elite/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) - - 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_animal/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 to [current_map.station_name]. Commence operation!") - -/proc/syndicate_elite_can_move() - if(syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return 0 - else return 1 - -/obj/machinery/computer/syndicate_elite_shuttle/attackby(I as obj, user as mob) - return attack_hand(user) - -/obj/machinery/computer/syndicate_elite_shuttle/attack_ai(var/mob/user as mob) - return attack_hand(user) - -/obj/machinery/computer/syndicate_elite_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/syndicate_elite_shuttle/attack_hand(var/mob/user as mob) - if(!allowed(user)) - to_chat(user, "Access Denied.") - return - -// if (sent_syndicate_strike_team == 0) -// to_chat(usr, "The strike team has not yet deployed.") -// return - - if(..()) - return - - user.set_machine(src) - var/dat - if (temp) - dat = temp - else - dat = {"
    Special Operations Shuttle
    - \nLocation: [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "Departing for [current_map.station_name] in ([syndicate_elite_shuttle_timeleft] seconds.)":syndicate_elite_shuttle_at_station ? "Station":"Dock"]
    - [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "\n*The Syndicate Elite shuttle is already leaving.*
    \n
    ":syndicate_elite_shuttle_at_station ? "\nShuttle Offline
    \n
    ":"\nDepart to [current_map.station_name]
    \n
    "] - \nClose"} - - user << browse(dat, "window=computer;size=575x450") - onclose(user, "computer") - return - -/obj/machinery/computer/syndicate_elite_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.set_machine(src) - - if (href_list["sendtodock"]) - if(!syndicate_elite_shuttle_at_station|| syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return - - to_chat(usr, "The Syndicate will not allow the Elite Squad shuttle to return.") - return - - else if (href_list["sendtostation"]) - if(syndicate_elite_shuttle_at_station || syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return - - if (!specops_can_move()) - to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") - return - - to_chat(usr, "The Syndicate Elite shuttle will arrive on [current_map.station_name] in [(SYNDICATE_ELITE_MOVETIME/10)] seconds.") - - temp = "Shuttle departing.

    OK" - updateUsrDialog() - - var/area/syndicate_mothership/elite_squad/elite_squad = locate() - if(elite_squad) - elite_squad.readyalert()//Trigger alarm for the spec ops area. - syndicate_elite_shuttle_moving_to_station = 1 - - syndicate_elite_shuttle_time = world.timeofday + SYNDICATE_ELITE_MOVETIME - spawn(0) - syndicate_elite_process() - - - else if (href_list["mainmenu"]) - temp = null - - add_fingerprint(usr) - updateUsrDialog() - return +//Config stuff +#define SYNDICATE_ELITE_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. +#define SYNDICATE_ELITE_STATION_AREATYPE "/area/shuttle/syndicate_elite/station" //Type of the spec ops shuttle area for station +#define SYNDICATE_ELITE_DOCK_AREATYPE "/area/shuttle/syndicate_elite/mothership" //Type of the spec ops shuttle area for dock + +var/syndicate_elite_shuttle_moving_to_station = 0 +var/syndicate_elite_shuttle_moving_to_mothership = 0 +var/syndicate_elite_shuttle_at_station = 0 +var/syndicate_elite_shuttle_can_send = 1 +var/syndicate_elite_shuttle_time = 0 +var/syndicate_elite_shuttle_timeleft = 0 + +/obj/machinery/computer/syndicate_elite_shuttle + name = "elite syndicate squad shuttle control console" + icon = 'icons/obj/computer.dmi' + + icon_screen = "syndishuttle" + light_color = "#00ffff" + req_access = list(access_cent_specops) + var/temp = null + var/hacked = 0 + var/allowedtocall = 0 + +/proc/syndicate_elite_process() + var/area/syndicate_mothership/control/syndicate_ship = locate()//To find announcer. This area should exist for this proc to work. + var/area/syndicate_mothership/elite_squad/elite_squad = locate()//Where is the specops area located? + var/mob/living/silicon/decoy/announcer = locate() in syndicate_ship//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message = "THE SYNDICATE ELITE SHUTTLE IS PREPARING FOR LAUNCH"//Initial message shown. + if(announcer) + announcer.say(message) + // message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" + // announcer.say(message) + + while(syndicate_elite_shuttle_time - world.timeofday > 0) + var/ticksleft = syndicate_elite_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + syndicate_elite_shuttle_time = world.timeofday // midnight rollover + syndicate_elite_shuttle_timeleft = (ticksleft / 10) + + //All this does is announce the time before launch. + if(announcer) + var/rounded_time_left = round(syndicate_elite_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.say(message) + 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) + + syndicate_elite_shuttle_moving_to_station = 0 + syndicate_elite_shuttle_moving_to_mothership = 0 + + syndicate_elite_shuttle_at_station = 1 + if (syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return + + if (!syndicate_elite_can_move()) + to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") + return + + sleep(600) +/* + //Begin Marauder launchpad. + spawn(0)//So it parallel processes it. + for(var/obj/machinery/door/poddoor/M in elite_squad) + 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 elite_squad) + 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/poddoor/M in elite_squad) + 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() + */ + elite_squad.readyreset()//Reset firealarm after the team launched. + //End Marauder launchpad. +/* + var/obj/explosionmarker = locate("Syndicate Breach Area") + if(explosionmarker) + var/turf/simulated/T = explosionmarker.loc + if(T) + explosion(T,4,6,8,10,0) + + sleep(40) +// proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1) + +*/ + var/area/start_location = locate(/area/shuttle/syndicate_elite/mothership) + var/area/end_location = locate(/area/shuttle/syndicate_elite/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) + + 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_animal/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 to [current_map.station_name]. Commence operation!") + +/proc/syndicate_elite_can_move() + if(syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return 0 + else return 1 + +/obj/machinery/computer/syndicate_elite_shuttle/attackby(I as obj, user as mob) + return attack_hand(user) + +/obj/machinery/computer/syndicate_elite_shuttle/attack_ai(var/mob/user as mob) + return attack_hand(user) + +/obj/machinery/computer/syndicate_elite_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/syndicate_elite_shuttle/attack_hand(var/mob/user as mob) + if(!allowed(user)) + to_chat(user, "Access Denied.") + return + +// if (sent_syndicate_strike_team == 0) +// to_chat(usr, "The strike team has not yet deployed.") +// return + + if(..()) + return + + user.set_machine(src) + var/dat + if (temp) + dat = temp + else + dat = {"
    Special Operations Shuttle
    + \nLocation: [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "Departing for [current_map.station_name] in ([syndicate_elite_shuttle_timeleft] seconds.)":syndicate_elite_shuttle_at_station ? "Station":"Dock"]
    + [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "\n*The Syndicate Elite shuttle is already leaving.*
    \n
    ":syndicate_elite_shuttle_at_station ? "\nShuttle Offline
    \n
    ":"\nDepart to [current_map.station_name]
    \n
    "] + \nClose"} + + user << browse(dat, "window=computer;size=575x450") + onclose(user, "computer") + return + +/obj/machinery/computer/syndicate_elite_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.set_machine(src) + + if (href_list["sendtodock"]) + if(!syndicate_elite_shuttle_at_station|| syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return + + to_chat(usr, "The Syndicate will not allow the Elite Squad shuttle to return.") + return + + else if (href_list["sendtostation"]) + if(syndicate_elite_shuttle_at_station || syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return + + if (!specops_can_move()) + to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") + return + + to_chat(usr, "The Syndicate Elite shuttle will arrive on [current_map.station_name] in [(SYNDICATE_ELITE_MOVETIME/10)] seconds.") + + temp = "Shuttle departing.

    OK" + updateUsrDialog() + + var/area/syndicate_mothership/elite_squad/elite_squad = locate() + if(elite_squad) + elite_squad.readyalert()//Trigger alarm for the spec ops area. + syndicate_elite_shuttle_moving_to_station = 1 + + syndicate_elite_shuttle_time = world.timeofday + SYNDICATE_ELITE_MOVETIME + spawn(0) + syndicate_elite_process() + + + else if (href_list["mainmenu"]) + temp = null + + add_fingerprint(usr) + updateUsrDialog() + return diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index 40b1a10110c..73b63b172e1 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -1,219 +1,219 @@ -//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 blueprint" - desc = "A holo blueprint for a machine." - icon = 'icons/obj/stock_parts.dmi' - icon_state = "blueprint_0" - density = 0 - anchored = 0 - use_power = 0 - var/obj/item/circuitboard/circuit = null - var/list/components = null - var/list/req_components = null - var/list/req_component_names = null - var/state = 1 - var/pitch_toggle = 1 - -/obj/machinery/constructable_frame/verb/rotate_clockwise() - set category = "Object" - set name = "Rotate Clockwise" - set src in view(1) - - if(use_check(usr) || anchored) - return - - src.set_dir(turn(src.dir, -90)) - - -/obj/machinery/constructable_frame/verb/rotate_counterclockwise() - set category = "Object" - set name = "Rotate Counter-Clockwise" - set src in view(1) - - if(use_check(usr) || anchored) - return - - src.set_dir(turn(src.dir, 90)) - -/obj/machinery/constructable_frame/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(P.ismultitool()) - to_chat(user, span("notice", "You begin to finalize the blueprint.")) - if(do_after(user, 20) && state == 1) - to_chat(user, span("notice", "You finalize the blueprint.")) - playsound(src.loc, 'sound/items/poster_being_created.ogg', 75, 1) - state = 2 - else - if(P.iswirecutter()) - playsound(src.loc, 'sound/items/poster_ripped.ogg', 75, 1) - to_chat(user, span("notice", "You decide to scrap the blueprint.")) - new /obj/item/stack/material/steel(src.loc, 2) - qdel(src) - if(2) - if(P.iscoil()) - var/obj/item/stack/cable_coil/C = P - if (C.get_amount() < 5) - to_chat(user, span("notice", "You need five lengths of cable to add them to the blueprint.")) - return - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, span("notice", "You start to add cables to the blueprint.")) - if(do_after(user, 20) && state == 2) - if(C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 3 - icon_state = "blueprint_1" - else - if(P.iswrench()) - playsound(src.loc, P.usesound, 75, 1) - to_chat(user, "You dismantle the blueprint") - new /obj/item/stack/material/steel(src.loc, 2) - qdel(src) - if(3) - if(istype(P, /obj/item/circuitboard)) - var/obj/item/circuitboard/B = P - if(B.board_type == "machine") - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, span("notice", "You add the circuit board to the blueprint.")) - circuit = P - user.drop_from_inventory(P,src) - icon_state = "blueprint_2" - state = 4 - 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, span("warning", "This blueprint does not accept circuit boards of this type!")) - else - if(P.iswirecutter()) - playsound(src.loc, P.usesound, 50, 1, pitch_toggle) - to_chat(user, span("notice", "You remove the cables.")) - state = 2 - icon_state = "blueprint_0" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( src.loc ) - A.amount = 5 - - if(4) - if(P.iscrowbar()) - playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) - state = 3 - circuit.forceMove(src.loc) - circuit = null - if(components.len == 0) - to_chat(user, span("notice", "You remove the circuit board.")) - else - to_chat(user, span("notice", "You remove the circuit board and other components.")) - for(var/obj/item/W in components) - W.forceMove(src.loc) - desc = initial(desc) - req_components = null - components = null - icon_state = "blueprint_2" - else - if(P.isscrewdriver()) - var/component_check = 1 - for(var/R in req_components) - if(req_components[R] > 0) - component_check = 0 - break - if(component_check) - playsound(src.loc, P.usesound, 50, 1) - var/obj/machinery/new_machine = new src.circuit.build_path(loc, dir, FALSE) - - 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.forceMove(new_machine) - else - O.forceMove(null) - new_machine.component_parts += O - - if(circuit.contain_parts) - circuit.forceMove(new_machine) - else - circuit.forceMove(null) - - new_machine.RefreshParts() - anchored = 1 - 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.loc, 'sound/items/Deconstruct.ogg', 50, 1) - if (istype(P, /obj/item/stack)) - var/obj/item/stack/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/CC = new CP.type(src) - CC.amount = camt - CC.update_icon() - CP.use(camt) - components += CC - req_components[I] -= camt - update_desc() - break - user.drop_from_inventory(P,src) - components += P - req_components[I]-- - update_desc() - break - to_chat(user, desc) - if(P && P.loc != src && !istype(P, /obj/item/stack)) - to_chat(user, span("notice", "You cannot add that component to the machine!.")) - - -/obj/machinery/constructable_frame/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if (!mover) - return 1 - if(istype(mover,/obj/item/projectile) && density) - if (prob(50)) - return 1 - else - return 0 - else if(mover.checkpass(PASSTABLE)) -//Animals can run under them, lots of empty space - return 1 - return ..() - -/obj/machinery/constructable_frame/temp_deco - name = "machine frame" - desc = "An old and dusty machine frame that once housed a machine of some kind." - icon_state = "box_0" - anchored = 1 - density = 1 - -/obj/machinery/constructable_frame/temp_deco/attackby(obj/item/P as obj, mob/user as mob) - if(P.iswrench()) - playsound(src.loc, P.usesound, 75, 1) - to_chat(user, "You dismantle [src]") - new /obj/item/stack/material/steel(src.loc, 5) +//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 blueprint" + desc = "A holo blueprint for a machine." + icon = 'icons/obj/stock_parts.dmi' + icon_state = "blueprint_0" + density = 0 + anchored = 0 + use_power = 0 + var/obj/item/circuitboard/circuit = null + var/list/components = null + var/list/req_components = null + var/list/req_component_names = null + var/state = 1 + var/pitch_toggle = 1 + +/obj/machinery/constructable_frame/verb/rotate_clockwise() + set category = "Object" + set name = "Rotate Clockwise" + set src in view(1) + + if(use_check(usr) || anchored) + return + + src.set_dir(turn(src.dir, -90)) + + +/obj/machinery/constructable_frame/verb/rotate_counterclockwise() + set category = "Object" + set name = "Rotate Counter-Clockwise" + set src in view(1) + + if(use_check(usr) || anchored) + return + + src.set_dir(turn(src.dir, 90)) + +/obj/machinery/constructable_frame/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(P.ismultitool()) + to_chat(user, span("notice", "You begin to finalize the blueprint.")) + if(do_after(user, 20) && state == 1) + to_chat(user, span("notice", "You finalize the blueprint.")) + playsound(src.loc, 'sound/items/poster_being_created.ogg', 75, 1) + state = 2 + else + if(P.iswirecutter()) + playsound(src.loc, 'sound/items/poster_ripped.ogg', 75, 1) + to_chat(user, span("notice", "You decide to scrap the blueprint.")) + new /obj/item/stack/material/steel(src.loc, 2) + qdel(src) + if(2) + if(P.iscoil()) + var/obj/item/stack/cable_coil/C = P + if (C.get_amount() < 5) + to_chat(user, span("notice", "You need five lengths of cable to add them to the blueprint.")) + return + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, span("notice", "You start to add cables to the blueprint.")) + if(do_after(user, 20) && state == 2) + if(C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 3 + icon_state = "blueprint_1" + else + if(P.iswrench()) + playsound(src.loc, P.usesound, 75, 1) + to_chat(user, "You dismantle the blueprint") + new /obj/item/stack/material/steel(src.loc, 2) + qdel(src) + if(3) + if(istype(P, /obj/item/circuitboard)) + var/obj/item/circuitboard/B = P + if(B.board_type == "machine") + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, span("notice", "You add the circuit board to the blueprint.")) + circuit = P + user.drop_from_inventory(P,src) + icon_state = "blueprint_2" + state = 4 + 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, span("warning", "This blueprint does not accept circuit boards of this type!")) + else + if(P.iswirecutter()) + playsound(src.loc, P.usesound, 50, 1, pitch_toggle) + to_chat(user, span("notice", "You remove the cables.")) + state = 2 + icon_state = "blueprint_0" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( src.loc ) + A.amount = 5 + + if(4) + if(P.iscrowbar()) + playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) + state = 3 + circuit.forceMove(src.loc) + circuit = null + if(components.len == 0) + to_chat(user, span("notice", "You remove the circuit board.")) + else + to_chat(user, span("notice", "You remove the circuit board and other components.")) + for(var/obj/item/W in components) + W.forceMove(src.loc) + desc = initial(desc) + req_components = null + components = null + icon_state = "blueprint_2" + else + if(P.isscrewdriver()) + var/component_check = 1 + for(var/R in req_components) + if(req_components[R] > 0) + component_check = 0 + break + if(component_check) + playsound(src.loc, P.usesound, 50, 1) + var/obj/machinery/new_machine = new src.circuit.build_path(loc, dir, FALSE) + + 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.forceMove(new_machine) + else + O.forceMove(null) + new_machine.component_parts += O + + if(circuit.contain_parts) + circuit.forceMove(new_machine) + else + circuit.forceMove(null) + + new_machine.RefreshParts() + anchored = 1 + 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.loc, 'sound/items/Deconstruct.ogg', 50, 1) + if (istype(P, /obj/item/stack)) + var/obj/item/stack/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/CC = new CP.type(src) + CC.amount = camt + CC.update_icon() + CP.use(camt) + components += CC + req_components[I] -= camt + update_desc() + break + user.drop_from_inventory(P,src) + components += P + req_components[I]-- + update_desc() + break + to_chat(user, desc) + if(P && P.loc != src && !istype(P, /obj/item/stack)) + to_chat(user, span("notice", "You cannot add that component to the machine!.")) + + +/obj/machinery/constructable_frame/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if (!mover) + return 1 + if(istype(mover,/obj/item/projectile) && density) + if (prob(50)) + return 1 + else + return 0 + else if(mover.checkpass(PASSTABLE)) +//Animals can run under them, lots of empty space + return 1 + return ..() + +/obj/machinery/constructable_frame/temp_deco + name = "machine frame" + desc = "An old and dusty machine frame that once housed a machine of some kind." + icon_state = "box_0" + anchored = 1 + density = 1 + +/obj/machinery/constructable_frame/temp_deco/attackby(obj/item/P as obj, mob/user as mob) + if(P.iswrench()) + playsound(src.loc, P.usesound, 75, 1) + to_chat(user, "You dismantle [src]") + new /obj/item/stack/material/steel(src.loc, 5) qdel(src) \ No newline at end of file diff --git a/code/game/machinery/crusher_piston.dm b/code/game/machinery/crusher_piston.dm index 7d65c9bd503..335fd65e628 100644 --- a/code/game/machinery/crusher_piston.dm +++ b/code/game/machinery/crusher_piston.dm @@ -90,7 +90,7 @@ if(status != "idle" && prob(40) && ishuman(user)) var/mob/living/carbon/human/M = user M.apply_damage(45, BRUTE, user.get_active_hand()) - M.apply_damage(45, HALLOSS) + M.apply_damage(45, PAIN) M.visible_message("[user]'s hand catches in the [src]!", "Your hand gets caught in the [src]!") M.say("*scream") return diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index 347d454cb5e..49f7504ea07 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -1,413 +1,413 @@ -#define HEAT_CAPACITY_HUMAN 100 //249840 J/K, for a 72 kg person. - -/obj/machinery/atmospherics/unary/cryo_cell - name = "cryo cell" - icon = 'icons/obj/cryogenics.dmi' // map only - icon_state = "pod_preview" - density = 1 - anchored = 1.0 - layer = 2.8 - interact_offline = 1 - - var/on = 0 - use_power = 1 - idle_power_usage = 20 - active_power_usage = 200 - - var/temperature_archived - var/mob/living/carbon/occupant = null - var/obj/item/reagent_containers/glass/beaker = null - - var/current_heat_capacity = 50 - -/obj/machinery/atmospherics/unary/cryo_cell/New() - ..() - icon = 'icons/obj/cryogenics_split.dmi' - update_icon() - initialize_directions = dir - -/obj/machinery/atmospherics/unary/cryo_cell/Destroy() - var/turf/T = loc - T.contents += contents - if(beaker) - beaker.forceMove(get_step(loc, SOUTH)) //Beaker is carefully ejected from the wreckage of the cryotube - return ..() - -/obj/machinery/atmospherics/unary/cryo_cell/atmos_init() - if(node) return - var/node_connect = dir - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(target.initialize_directions & get_dir(target,src)) - node = target - break - -/obj/machinery/atmospherics/unary/cryo_cell/machinery_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) - if(user.stat) - return - go_out() - return - -/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) - ui_interact(user) - - /** - * The ui_interact proc is used to open and update Nano UIs - * If ui_interact is not used then the UI will not update correctly - * ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob) - * - * @param user /mob The mob who is interacting with this ui - * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") - * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui - * - * @return nothing - */ -/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - - if(user == occupant || user.stat) - return - - // this is the data which will be sent to the ui - var/data[0] - data["isOperating"] = on - data["hasOccupant"] = occupant ? 1 : 0 - - var/occupantData[0] - if (occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - 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 ? 1 : 0 - /* // Removing beaker contents list from front-end, replacing with a total remaining volume - var beakerContents[0] - if(beaker && beaker.reagents && beaker.reagents.reagent_list.len) - for(var/datum/reagent/R in beaker.reagents.reagent_list) - beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list... - data["beakerContents"] = beakerContents - */ - 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 - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 410) - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list) - if(usr == occupant) - return 0 // don't update UIs attached to this object - - if(..()) - return 0 // don't update UIs attached to this object - - if(href_list["switchOn"]) - on = 1 - update_icon() - - if(href_list["switchOff"]) - on = 0 - update_icon() - - if(href_list["ejectBeaker"]) - if(beaker) - beaker.forceMove(get_step(loc, SOUTH)) - beaker = null - - if(href_list["ejectOccupant"]) - if(!occupant || isslime(usr) || ispAI(usr)) - return 0 // don't update UIs attached to this object - go_out() - - add_fingerprint(usr) - return 1 // update UIs attached to this object - -/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G as obj, var/mob/user as mob) - if(istype(G, /obj/item/reagent_containers/glass)) - if(beaker) - to_chat(user, "A beaker is already loaded into the machine.") - return - - beaker = G - user.drop_from_inventory(G,src) - user.visible_message("[user] adds \a [G] to \the [src]!", "You add \a [G] to \the [src]!") - else if(istype(G, /obj/item/grab)) - var/obj/item/grab/grab = G - var/mob/living/L = grab.affecting - - if (!istype(L)) - return - - user.visible_message("[user] starts putting [L] into [src].", "You start putting [L] into [src].", range = 3) - - if (do_mob(user, L, 30, needhand = 0)) - var/bucklestatus = L.bucklecheck(user) - if (!bucklestatus)//incase the patient got buckled during the delay - return - if (bucklestatus == 2) - var/obj/structure/LB = L.buckled - LB.user_unbuckle_mob(user) - for(var/mob/living/carbon/slime/M in range(1, L)) - if(M.Victim == L) - to_chat(user, "[L] will not fit into the cryo because they have a slime latched onto their head.") - return - if(put_mob(L)) - user.visible_message("[user] puts [L] into [src].", "You put [L] into [src].", range = 3) - qdel(G) - return - -/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) - if(!istype(user)) - return - if(!ismob(O)) - return - var/mob/living/L = O - for(var/mob/living/carbon/slime/M in range(1,L)) - if(M.Victim == L) - to_chat(usr, "[L.name] will not fit into the cryo because they have a slime latched onto their head.") - return - - var/bucklestatus = L.bucklecheck(user) - - if (!bucklestatus)//We must make sure the person is unbuckled before they go in - return - - if(L == user) - user.visible_message("[user] starts climbing into [src].", "You start climbing into [src].", range = 3) - else - user.visible_message("[user] starts putting [L] into the cryopod.", "You start putting [L] into [src].", range = 3) - if (do_mob(user, L, 30, needhand = 0)) - if (bucklestatus == 2) - var/obj/structure/LB = L.buckled - LB.user_unbuckle_mob(user) - if(put_mob(L)) - if(L == user) - user.visible_message("[user] climbs into [src].", "You climb into [src].", range = 3) - else - user.visible_message("[user] puts [L] into [src].", "You put [L] into [src].", range = 3) - if(user.pulling == L) - user.pulling = null - -/obj/machinery/atmospherics/unary/cryo_cell/update_icon() - cut_overlays() - var/list/ovr = list() - icon_state = "pod[on]" - var/image/I - - I = image(icon, "pod[on]_top") - I.layer = 5 // this needs to be fairly high so it displays over most things, but it needs to be under lighting (at 10) - I.pixel_z = 32 - ovr += I - - if(occupant) - var/image/pickle = image(occupant.icon, occupant.icon_state) - pickle.overlays = occupant.overlays - pickle.pixel_z = 18 - pickle.layer = 5 - ovr += pickle - - I = image(icon, "lid[on]") - I.layer = 5 - ovr += I - - I = image(icon, "lid[on]_top") - I.layer = 5 - I.pixel_z = 32 - ovr += I - - add_overlay(ovr) - -/obj/machinery/atmospherics/unary/cryo_cell/proc/process_occupant() - if(air_contents.total_moles < 10) - return - if(occupant) - if(occupant.stat == 2) - 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.stat = 1 - 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.is_diona()) - if (occupant.getToxLoss()) - occupant.adjustToxLoss(max(-1, -20/occupant.getToxLoss())) - 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) - else - occupant.adjustFireLoss(3)//Cryopods kill diona. This damage combines with the normal cold temp damage, and their disabled regen - 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.forceMove(loc) - if (occupant.client) - occupant.client.eye = occupant.client.mob - occupant.client.perspective = MOB_PERSPECTIVE - occupant.forceMove(get_step(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. -// occupant.metabslow = 0 - occupant = null - current_heat_capacity = initial(current_heat_capacity) - update_use_power(1) - update_icon() - 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.forceMove(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 - current_heat_capacity = HEAT_CAPACITY_HUMAN - update_use_power(2) -// M.metabslow = 1 - add_fingerprint(usr) - update_icon() - 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) - for(var/mob/living/carbon/slime/M in range(1,usr)) - if(M.Victim == usr) - to_chat(usr, "You're too busy getting your life sucked out of you.") - return - if (usr.stat != 0) - return - usr.visible_message("[usr] climbs into [src].", "You climb into [src].", range = 3) - put_mob(usr) - return - -/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(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" + icon = 'icons/obj/cryogenics.dmi' // map only + icon_state = "pod_preview" + density = 1 + anchored = 1.0 + layer = 2.8 + interact_offline = 1 + + var/on = 0 + use_power = 1 + idle_power_usage = 20 + active_power_usage = 200 + + var/temperature_archived + var/mob/living/carbon/occupant = null + var/obj/item/reagent_containers/glass/beaker = null + + var/current_heat_capacity = 50 + +/obj/machinery/atmospherics/unary/cryo_cell/New() + ..() + icon = 'icons/obj/cryogenics_split.dmi' + update_icon() + initialize_directions = dir + +/obj/machinery/atmospherics/unary/cryo_cell/Destroy() + var/turf/T = loc + T.contents += contents + if(beaker) + beaker.forceMove(get_step(loc, SOUTH)) //Beaker is carefully ejected from the wreckage of the cryotube + return ..() + +/obj/machinery/atmospherics/unary/cryo_cell/atmos_init() + if(node) return + var/node_connect = dir + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(target.initialize_directions & get_dir(target,src)) + node = target + break + +/obj/machinery/atmospherics/unary/cryo_cell/machinery_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) + if(user.stat) + return + go_out() + return + +/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) + ui_interact(user) + + /** + * The ui_interact proc is used to open and update Nano UIs + * If ui_interact is not used then the UI will not update correctly + * ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob) + * + * @param user /mob The mob who is interacting with this ui + * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") + * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui + * + * @return nothing + */ +/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + + if(user == occupant || user.stat) + return + + // this is the data which will be sent to the ui + var/data[0] + data["isOperating"] = on + data["hasOccupant"] = occupant ? 1 : 0 + + var/occupantData[0] + if (occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + 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 ? 1 : 0 + /* // Removing beaker contents list from front-end, replacing with a total remaining volume + var beakerContents[0] + if(beaker && beaker.reagents && beaker.reagents.reagent_list.len) + for(var/datum/reagent/R in beaker.reagents.reagent_list) + beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list... + data["beakerContents"] = beakerContents + */ + 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 + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 410) + // when the ui is first opened this is the data it will use + ui.set_initial_data(data) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list) + if(usr == occupant) + return 0 // don't update UIs attached to this object + + if(..()) + return 0 // don't update UIs attached to this object + + if(href_list["switchOn"]) + on = 1 + update_icon() + + if(href_list["switchOff"]) + on = 0 + update_icon() + + if(href_list["ejectBeaker"]) + if(beaker) + beaker.forceMove(get_step(loc, SOUTH)) + beaker = null + + if(href_list["ejectOccupant"]) + if(!occupant || isslime(usr) || ispAI(usr)) + return 0 // don't update UIs attached to this object + go_out() + + add_fingerprint(usr) + return 1 // update UIs attached to this object + +/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G as obj, var/mob/user as mob) + if(istype(G, /obj/item/reagent_containers/glass)) + if(beaker) + to_chat(user, "A beaker is already loaded into the machine.") + return + + beaker = G + user.drop_from_inventory(G,src) + user.visible_message("[user] adds \a [G] to \the [src]!", "You add \a [G] to \the [src]!") + else if(istype(G, /obj/item/grab)) + var/obj/item/grab/grab = G + var/mob/living/L = grab.affecting + + if (!istype(L)) + return + + user.visible_message("[user] starts putting [L] into [src].", "You start putting [L] into [src].", range = 3) + + if (do_mob(user, L, 30, needhand = 0)) + var/bucklestatus = L.bucklecheck(user) + if (!bucklestatus)//incase the patient got buckled during the delay + return + if (bucklestatus == 2) + var/obj/structure/LB = L.buckled + LB.user_unbuckle_mob(user) + for(var/mob/living/carbon/slime/M in range(1, L)) + if(M.Victim == L) + to_chat(user, "[L] will not fit into the cryo because they have a slime latched onto their head.") + return + if(put_mob(L)) + user.visible_message("[user] puts [L] into [src].", "You put [L] into [src].", range = 3) + qdel(G) + return + +/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) + if(!istype(user)) + return + if(!ismob(O)) + return + var/mob/living/L = O + for(var/mob/living/carbon/slime/M in range(1,L)) + if(M.Victim == L) + to_chat(usr, "[L.name] will not fit into the cryo because they have a slime latched onto their head.") + return + + var/bucklestatus = L.bucklecheck(user) + + if (!bucklestatus)//We must make sure the person is unbuckled before they go in + return + + if(L == user) + user.visible_message("[user] starts climbing into [src].", "You start climbing into [src].", range = 3) + else + user.visible_message("[user] starts putting [L] into the cryopod.", "You start putting [L] into [src].", range = 3) + if (do_mob(user, L, 30, needhand = 0)) + if (bucklestatus == 2) + var/obj/structure/LB = L.buckled + LB.user_unbuckle_mob(user) + if(put_mob(L)) + if(L == user) + user.visible_message("[user] climbs into [src].", "You climb into [src].", range = 3) + else + user.visible_message("[user] puts [L] into [src].", "You put [L] into [src].", range = 3) + if(user.pulling == L) + user.pulling = null + +/obj/machinery/atmospherics/unary/cryo_cell/update_icon() + cut_overlays() + var/list/ovr = list() + icon_state = "pod[on]" + var/image/I + + I = image(icon, "pod[on]_top") + I.layer = 5 // this needs to be fairly high so it displays over most things, but it needs to be under lighting (at 10) + I.pixel_z = 32 + ovr += I + + if(occupant) + var/image/pickle = image(occupant.icon, occupant.icon_state) + pickle.overlays = occupant.overlays + pickle.pixel_z = 18 + pickle.layer = 5 + ovr += pickle + + I = image(icon, "lid[on]") + I.layer = 5 + ovr += I + + I = image(icon, "lid[on]_top") + I.layer = 5 + I.pixel_z = 32 + ovr += I + + add_overlay(ovr) + +/obj/machinery/atmospherics/unary/cryo_cell/proc/process_occupant() + if(air_contents.total_moles < 10) + return + if(occupant) + if(occupant.stat == 2) + 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.stat = 1 + 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.is_diona()) + if (occupant.getToxLoss()) + occupant.adjustToxLoss(max(-1, -20/occupant.getToxLoss())) + 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) + else + occupant.adjustFireLoss(3)//Cryopods kill diona. This damage combines with the normal cold temp damage, and their disabled regen + 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.forceMove(loc) + if (occupant.client) + occupant.client.eye = occupant.client.mob + occupant.client.perspective = MOB_PERSPECTIVE + occupant.forceMove(get_step(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. +// occupant.metabslow = 0 + occupant = null + current_heat_capacity = initial(current_heat_capacity) + update_use_power(1) + update_icon() + 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.forceMove(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 + current_heat_capacity = HEAT_CAPACITY_HUMAN + update_use_power(2) +// M.metabslow = 1 + add_fingerprint(usr) + update_icon() + 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) + for(var/mob/living/carbon/slime/M in range(1,usr)) + if(M.Victim == usr) + to_chat(usr, "You're too busy getting your life sucked out of you.") + return + if (usr.stat != 0) + return + usr.visible_message("[usr] climbs into [src].", "You climb into [src].", range = 3) + put_mob(usr) + return + +/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(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 79e8ff761c7..8b547132f99 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -1,336 +1,336 @@ -/* -CONTAINS: - -Deployable items -Barricades -Deployable Kits - -for reference: - - access_security = 1 - access_brig = 2 - access_armory = 3 - access_forensics_lockers= 4 - access_medical = 5 - access_morgue = 6 - access_tox = 7 - access_tox_storage = 8 - access_genetics = 9 - access_engine = 10 - access_engine_equip= 11 - access_maint_tunnels = 12 - access_external_airlocks = 13 - access_emergency_storage = 14 - access_change_ids = 15 - access_ai_upload = 16 - access_teleporter = 17 - access_eva = 18 - access_heads = 19 - access_captain = 20 - access_all_personal_lockers = 21 - access_chapel_office = 22 - access_tech_storage = 23 - access_atmospherics = 24 - access_bar = 25 - access_janitor = 26 - access_crematorium = 27 - access_kitchen = 28 - access_robotics = 29 - access_rd = 30 - access_cargo = 31 - access_construction = 32 - access_pharmacy = 33 - access_cargo_bot = 34 - access_hydroponics = 35 - access_manufacturing = 36 - access_library = 37 - access_lawyer = 38 - access_virology = 39 - access_cmo = 40 - access_qm = 41 - access_court = 42 - access_clown = 43 - access_mime = 44 - -*/ - -//Barricades! -/obj/structure/barricade - name = "barricade" - desc = "This space is blocked off by a barricade." - icon = 'icons/obj/structures.dmi' - icon_state = "barricade" - anchored = 1.0 - density = 1.0 - var/health = 100 - var/maxhealth = 100 - var/material/material - -/obj/structure/barricade/New(var/newloc, var/material_name) - ..(newloc) - if(!material_name) - material_name = "wood" - material = get_material_by_name("[material_name]") - if(!material) - qdel(src) - return - name = "[material.display_name] barricade" - desc = "This space is blocked off by a barricade made of [material.display_name]." - color = material.icon_colour - maxhealth = material.integrity - health = maxhealth - -/obj/structure/barricade/get_material() - return material - -/obj/structure/barricade/attackby(obj/item/W as obj, mob/user as mob) - if (istype(W, /obj/item/stack)) - var/obj/item/stack/D = W - if(D.get_material_name() != material.name) - return //hitting things with the wrong type of stack usually doesn't produce messages, and probably doesn't need to. - if (health < maxhealth) - if (D.get_amount() < 1) - to_chat(user, "You need one sheet of [material.display_name] to repair \the [src].") - return - visible_message("[user] begins to repair \the [src].") - if(do_after(user,20) && health < maxhealth) - if (D.use(1)) - health = maxhealth - visible_message("[user] repairs \the [src].") - return - return - else - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - switch(W.damtype) - if("fire") - src.health -= W.force * 1 - if("brute") - src.health -= W.force * 0.75 - else - animate_shake() - playsound(src.loc, material.hitsound, 100, 1) - if (src.health <= 0) - visible_message("The barricade is smashed apart!") - dismantle() - qdel(src) - return - ..() - -/obj/structure/barricade/proc/dismantle() - material.place_dismantled_product(get_turf(src)) - qdel(src) - return - -/obj/structure/barricade/ex_act(severity) - switch(severity) - if(1.0) - visible_message("\The [src] is blown apart!") - qdel(src) - return - if(2.0) - src.health -= 25 - if (src.health <= 0) - visible_message("\The [src] is blown apart!") - dismantle() - return - -/obj/structure/barricade/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)//So bullets will fly over and stuff. - if(air_group || (height==0)) - return 1 - if(istype(mover) && mover.checkpass(PASSTABLE)) - return 1 - else - return 0 - -/obj/structure/barricade/steel/Initialize(var/newloc) - .=..(newloc,"steel") - -//Actual Deployable machinery stuff -/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 = 0.0 - density = 1.0 - icon_state = "barrier" - var/health = 100.0 - var/maxhealth = 100.0 - var/locked = 0.0 -// req_access = list(access_maint_tunnels) - - New() - ..() - - src.icon_state = "[initial(icon_state)][src.locked]" - - attackby(obj/item/W as obj, mob/user as mob) - if (istype(W, /obj/item/card/id/)) - if (src.allowed(user)) - if (src.emagged < 2.0) - src.locked = !src.locked - src.anchored = !src.anchored - src.icon_state = "[initial(icon_state)][src.locked]" - if ((src.locked == 1.0) && (src.emagged < 2.0)) - to_chat(user, "Barrier lock toggled on.") - return - else if ((src.locked == 0.0) && (src.emagged < 2.0)) - to_chat(user, "Barrier lock toggled off.") - return - else - spark(src, 2, src) - visible_message("BZZzZZzZZzZT") - return - return - else if (W.iswrench()) - if (src.health < src.maxhealth) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - src.health = src.maxhealth - src.emagged = 0 - src.req_access = list(access_security) - visible_message("[user] repairs \the [src]!") - return - else if (src.emagged > 0) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - src.emagged = 0 - src.req_access = list(access_security) - visible_message("[user] repairs \the [src]!") - return - return - else - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - switch(W.damtype) - if("fire") - src.health -= W.force * 0.75 - if("brute") - src.health -= W.force * 0.5 - else - if (src.health <= 0) - src.explode() - ..() - - ex_act(severity) - switch(severity) - if(1.0) - src.explode() - return - if(2.0) - src.health -= 25 - if (src.health <= 0) - src.explode() - return - emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - if(prob(50/severity)) - locked = !locked - anchored = !anchored - icon_state = "[initial(icon_state)][src.locked]" - - CanPass(atom/movable/mover, turf/target, height=0, air_group=0)//So bullets will fly over and stuff. - if(air_group || (height==0)) - return 1 - if(istype(mover) && mover.checkpass(PASSTABLE)) - return 1 - else - return 0 - - proc/explode() - visible_message("[src] blows apart!") - - /* var/obj/item/stack/rods/ =*/ - new /obj/item/stack/rods(get_turf(src)) - - spark(src, 3, alldirs) - - explosion(src.loc,-1,-1,0) - qdel(src) - -/obj/machinery/deployable/barrier/emag_act(var/remaining_charges, var/mob/user) - if (src.emagged == 0) - src.emagged = 1 - src.req_access.Cut() - src.req_one_access.Cut() - to_chat(user, "You break the ID authentication lock on \the [src].") - spark(src, 2, alldirs) - visible_message("BZZzZZzZZzZT") - return 1 - else if (src.emagged == 1) - src.emagged = 2 - to_chat(user, "You short out the anchoring mechanism on \the [src].") - spark(src, 2, alldirs) - visible_message("BZZzZZzZZzZT") - return 1 - -/obj/machinery/deployable/barrier/legion - name = "legion barrier" - desc = "A deployable barrier, bearing the marks of the Tau Ceti Foreign Legion. Swipe your ID card to lock/unlock it." - icon_state = "barrier_legion" - req_access = list(access_legion) - -/obj/item/deployable_kit - name = "Emergency Floodlight Kit" - desc = "A do-it-yourself kit for constructing the finest of emergency floodlights." - icon = 'icons/obj/storage.dmi' - icon_state = "inf_box" - item_state = "syringe_kit" - var/kit_product = /obj/machinery/floodlight - var/assembly_time = 8 SECONDS - -/obj/item/deployable_kit/attack_self(mob/user) - to_chat(user, span("notice","You start assembling \the [src]...")) - if(do_after(user, assembly_time)) - assemble_kit(user) - qdel(src) - -/obj/item/deployable_kit/proc/assemble_kit(mob/user) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 25, 1) - var/atom/A = new kit_product(user.loc) - user.visible_message(span("notice","[user] assembles \a [A]."),span("notice","You assemble \a [A].")) - A.add_fingerprint(user) - -/obj/item/deployable_kit/legion_barrier - name = "legion barrier kit" - desc = "A quick assembly kit for deploying id-lockable barriers in the field. Most commonly seen used for crowd control by corporate security." - icon_state = "barrier_kit" - w_class = 2 - kit_product = /obj/machinery/deployable/barrier/legion - -/obj/item/deployable_kit/surgery_table - name = "surgery table assembly kit" - desc = "A quick assembly kit to deploy a surgery table in the field. Cannot be put together again after being unfolded, choose your spot wisely." - icon = 'icons/obj/surgery.dmi' - icon_state = "table_deployable" - item_state = "table_parts" - w_class = 4 - kit_product = /obj/machinery/optable - assembly_time = 20 SECONDS - -/obj/item/deployable_kit/surgery_table/assemble_kit(mob/user) - ..() - var/free_spot = null - for(var/check_dir in cardinal) - var/turf/T = get_step(src, check_dir) - if(turf_clear(T)) - free_spot = T - break - if(!free_spot) - free_spot = src.loc - new /obj/structure/curtain/open/medical(free_spot, src) - new /obj/structure/curtain/open/medical(free_spot, src) - -/obj/item/deployable_kit/legion_turret - name = "legion blaster turret assembly kit" - desc = "A quick assembly kit to deploy a blaster turret in the field. Swipe with a TCFL id card to configure it once assembled." - icon = 'icons/obj/turrets.dmi' - icon_state = "blaster_turret_kit" - item_state = "table_parts" - drop_sound = 'sound/items/drop/axe.ogg' - w_class = 4 - kit_product = /obj/machinery/porta_turret/legion - assembly_time = 15 SECONDS +/* +CONTAINS: + +Deployable items +Barricades +Deployable Kits + +for reference: + + access_security = 1 + access_brig = 2 + access_armory = 3 + access_forensics_lockers= 4 + access_medical = 5 + access_morgue = 6 + access_tox = 7 + access_tox_storage = 8 + access_genetics = 9 + access_engine = 10 + access_engine_equip= 11 + access_maint_tunnels = 12 + access_external_airlocks = 13 + access_emergency_storage = 14 + access_change_ids = 15 + access_ai_upload = 16 + access_teleporter = 17 + access_eva = 18 + access_heads = 19 + access_captain = 20 + access_all_personal_lockers = 21 + access_chapel_office = 22 + access_tech_storage = 23 + access_atmospherics = 24 + access_bar = 25 + access_janitor = 26 + access_crematorium = 27 + access_kitchen = 28 + access_robotics = 29 + access_rd = 30 + access_cargo = 31 + access_construction = 32 + access_pharmacy = 33 + access_cargo_bot = 34 + access_hydroponics = 35 + access_manufacturing = 36 + access_library = 37 + access_lawyer = 38 + access_virology = 39 + access_cmo = 40 + access_qm = 41 + access_court = 42 + access_clown = 43 + access_mime = 44 + +*/ + +//Barricades! +/obj/structure/barricade + name = "barricade" + desc = "This space is blocked off by a barricade." + icon = 'icons/obj/structures.dmi' + icon_state = "barricade" + anchored = 1.0 + density = 1.0 + var/health = 100 + var/maxhealth = 100 + var/material/material + +/obj/structure/barricade/New(var/newloc, var/material_name) + ..(newloc) + if(!material_name) + material_name = "wood" + material = get_material_by_name("[material_name]") + if(!material) + qdel(src) + return + name = "[material.display_name] barricade" + desc = "This space is blocked off by a barricade made of [material.display_name]." + color = material.icon_colour + maxhealth = material.integrity + health = maxhealth + +/obj/structure/barricade/get_material() + return material + +/obj/structure/barricade/attackby(obj/item/W as obj, mob/user as mob) + if (istype(W, /obj/item/stack)) + var/obj/item/stack/D = W + if(D.get_material_name() != material.name) + return //hitting things with the wrong type of stack usually doesn't produce messages, and probably doesn't need to. + if (health < maxhealth) + if (D.get_amount() < 1) + to_chat(user, "You need one sheet of [material.display_name] to repair \the [src].") + return + visible_message("[user] begins to repair \the [src].") + if(do_after(user,20) && health < maxhealth) + if (D.use(1)) + health = maxhealth + visible_message("[user] repairs \the [src].") + return + return + else + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + switch(W.damtype) + if("fire") + src.health -= W.force * 1 + if("brute") + src.health -= W.force * 0.75 + else + animate_shake() + playsound(src.loc, material.hitsound, 100, 1) + if (src.health <= 0) + visible_message("The barricade is smashed apart!") + dismantle() + qdel(src) + return + ..() + +/obj/structure/barricade/proc/dismantle() + material.place_dismantled_product(get_turf(src)) + qdel(src) + return + +/obj/structure/barricade/ex_act(severity) + switch(severity) + if(1.0) + visible_message("\The [src] is blown apart!") + qdel(src) + return + if(2.0) + src.health -= 25 + if (src.health <= 0) + visible_message("\The [src] is blown apart!") + dismantle() + return + +/obj/structure/barricade/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)//So bullets will fly over and stuff. + if(air_group || (height==0)) + return 1 + if(istype(mover) && mover.checkpass(PASSTABLE)) + return 1 + else + return 0 + +/obj/structure/barricade/steel/New(var/newloc) + .=..(newloc,"steel") + +//Actual Deployable machinery stuff +/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 = 0.0 + density = 1.0 + icon_state = "barrier" + var/health = 100.0 + var/maxhealth = 100.0 + var/locked = 0.0 +// req_access = list(access_maint_tunnels) + + New() + ..() + + src.icon_state = "[initial(icon_state)][src.locked]" + + attackby(obj/item/W as obj, mob/user as mob) + if (istype(W, /obj/item/card/id/)) + if (src.allowed(user)) + if (src.emagged < 2.0) + src.locked = !src.locked + src.anchored = !src.anchored + src.icon_state = "[initial(icon_state)][src.locked]" + if ((src.locked == 1.0) && (src.emagged < 2.0)) + to_chat(user, "Barrier lock toggled on.") + return + else if ((src.locked == 0.0) && (src.emagged < 2.0)) + to_chat(user, "Barrier lock toggled off.") + return + else + spark(src, 2, src) + visible_message("BZZzZZzZZzZT") + return + return + else if (W.iswrench()) + if (src.health < src.maxhealth) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + src.health = src.maxhealth + src.emagged = 0 + src.req_access = list(access_security) + visible_message("[user] repairs \the [src]!") + return + else if (src.emagged > 0) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + src.emagged = 0 + src.req_access = list(access_security) + visible_message("[user] repairs \the [src]!") + return + return + else + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + switch(W.damtype) + if("fire") + src.health -= W.force * 0.75 + if("brute") + src.health -= W.force * 0.5 + else + if (src.health <= 0) + src.explode() + ..() + + ex_act(severity) + switch(severity) + if(1.0) + src.explode() + return + if(2.0) + src.health -= 25 + if (src.health <= 0) + src.explode() + return + emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + if(prob(50/severity)) + locked = !locked + anchored = !anchored + icon_state = "[initial(icon_state)][src.locked]" + + CanPass(atom/movable/mover, turf/target, height=0, air_group=0)//So bullets will fly over and stuff. + if(air_group || (height==0)) + return 1 + if(istype(mover) && mover.checkpass(PASSTABLE)) + return 1 + else + return 0 + + proc/explode() + visible_message("[src] blows apart!") + + /* var/obj/item/stack/rods/ =*/ + new /obj/item/stack/rods(get_turf(src)) + + spark(src, 3, alldirs) + + explosion(src.loc,-1,-1,0) + qdel(src) + +/obj/machinery/deployable/barrier/emag_act(var/remaining_charges, var/mob/user) + if (src.emagged == 0) + src.emagged = 1 + src.req_access.Cut() + src.req_one_access.Cut() + to_chat(user, "You break the ID authentication lock on \the [src].") + spark(src, 2, alldirs) + visible_message("BZZzZZzZZzZT") + return 1 + else if (src.emagged == 1) + src.emagged = 2 + to_chat(user, "You short out the anchoring mechanism on \the [src].") + spark(src, 2, alldirs) + visible_message("BZZzZZzZZzZT") + return 1 + +/obj/machinery/deployable/barrier/legion + name = "legion barrier" + desc = "A deployable barrier, bearing the marks of the Tau Ceti Foreign Legion. Swipe your ID card to lock/unlock it." + icon_state = "barrier_legion" + req_access = list(access_legion) + +/obj/item/deployable_kit + name = "Emergency Floodlight Kit" + desc = "A do-it-yourself kit for constructing the finest of emergency floodlights." + icon = 'icons/obj/storage.dmi' + icon_state = "inf_box" + item_state = "syringe_kit" + var/kit_product = /obj/machinery/floodlight + var/assembly_time = 8 SECONDS + +/obj/item/deployable_kit/attack_self(mob/user) + to_chat(user, span("notice","You start assembling \the [src]...")) + if(do_after(user, assembly_time)) + assemble_kit(user) + qdel(src) + +/obj/item/deployable_kit/proc/assemble_kit(mob/user) + playsound(src.loc, 'sound/items/Screwdriver.ogg', 25, 1) + var/atom/A = new kit_product(user.loc) + user.visible_message(span("notice","[user] assembles \a [A]."),span("notice","You assemble \a [A].")) + A.add_fingerprint(user) + +/obj/item/deployable_kit/legion_barrier + name = "legion barrier kit" + desc = "A quick assembly kit for deploying id-lockable barriers in the field. Most commonly seen used for crowd control by corporate security." + icon_state = "barrier_kit" + w_class = 2 + kit_product = /obj/machinery/deployable/barrier/legion + +/obj/item/deployable_kit/surgery_table + name = "surgery table assembly kit" + desc = "A quick assembly kit to deploy a surgery table in the field. Cannot be put together again after being unfolded, choose your spot wisely." + icon = 'icons/obj/surgery.dmi' + icon_state = "table_deployable" + item_state = "table_parts" + w_class = 4 + kit_product = /obj/machinery/optable + assembly_time = 20 SECONDS + +/obj/item/deployable_kit/surgery_table/assemble_kit(mob/user) + ..() + var/free_spot = null + for(var/check_dir in cardinal) + var/turf/T = get_step(src, check_dir) + if(turf_clear(T)) + free_spot = T + break + if(!free_spot) + free_spot = src.loc + new /obj/structure/curtain/open/medical(free_spot, src) + new /obj/structure/curtain/open/medical(free_spot, src) + +/obj/item/deployable_kit/legion_turret + name = "legion blaster turret assembly kit" + desc = "A quick assembly kit to deploy a blaster turret in the field. Swipe with a TCFL id card to configure it once assembled." + icon = 'icons/obj/turrets.dmi' + icon_state = "blaster_turret_kit" + item_state = "table_parts" + drop_sound = 'sound/items/drop/axe.ogg' + w_class = 4 + kit_product = /obj/machinery/porta_turret/legion + assembly_time = 15 SECONDS diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index bb6e29704a1..7ce7929d20e 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -1,212 +1,212 @@ -/obj/machinery/button/remote - name = "remote object control" - desc = "It controls objects, remotely." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "doorctrl0" - power_channel = ENVIRON - var/desiredstate = 0 - var/exposedwires = 0 - var/wires = 3 - /* - Bitflag, 1=checkID - 2=Network Access - */ - - anchored = 1.0 - use_power = 1 - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/button/remote/attack_ai(mob/user as mob) - if(wires & 2) - return src.attack_hand(user) - else - to_chat(user, "Error, no route to host.") - -/obj/machinery/button/remote/attackby(obj/item/W, mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/button/remote/emag_act(var/remaining_charges, var/mob/user) - if(req_access.len || req_one_access.len) - req_access = list() - req_one_access = list() - playsound(src.loc, "sparks", 100, 1) - return 1 - -/obj/machinery/button/remote/attack_hand(mob/user as mob) - if(..()) - return - - src.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 - 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 SSmachinery.processing_machines) - if(D.id_tag == src.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 - 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 SSmachinery.all_machines) - if(M.id == src.id) - if(M.density) - spawn(0) - M.open() - return - else - spawn(0) - M.close() - return - -/obj/machinery/button/remote/blast_door/open_only/trigger() - for(var/obj/machinery/door/blast/M in SSmachinery.all_machines) - if(M.id == src.id) - if(M.density) - spawn(0) - M.open() - 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 SSmachinery.all_machines) - if(E.id == src.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() - - var/list/same_id = list() - - for(var/obj/machinery/door/blast/M in SSmachinery.all_machines) - if (M.id == src.id) - same_id += M - INVOKE_ASYNC(M, /obj/machinery/door/blast/open) - - sleep(20) - - for(var/obj/machinery/mass_driver/M in SSmachinery.all_machines) - if(M.id == src.id) - M.drive() - - sleep(50) - - for(var/mm in same_id) - INVOKE_ASYNC(mm, /obj/machinery/door/blast/close) - - icon_state = "launcherbtt" - active = 0 - update_icon() - - return - -/obj/machinery/button/remote/driver/update_icon() - if(!active || (stat & NOPOWER)) - icon_state = "launcherbtt" - else - icon_state = "launcheract" +/obj/machinery/button/remote + name = "remote object control" + desc = "It controls objects, remotely." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "doorctrl0" + power_channel = ENVIRON + var/desiredstate = 0 + var/exposedwires = 0 + var/wires = 3 + /* + Bitflag, 1=checkID + 2=Network Access + */ + + anchored = 1.0 + use_power = 1 + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/button/remote/attack_ai(mob/user as mob) + if(wires & 2) + return src.attack_hand(user) + else + to_chat(user, "Error, no route to host.") + +/obj/machinery/button/remote/attackby(obj/item/W, mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/button/remote/emag_act(var/remaining_charges, var/mob/user) + if(req_access.len || req_one_access.len) + req_access = list() + req_one_access = list() + playsound(src.loc, "sparks", 100, 1) + return 1 + +/obj/machinery/button/remote/attack_hand(mob/user as mob) + if(..()) + return + + src.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 + 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 SSmachinery.processing_machines) + if(D.id_tag == src.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 + 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 SSmachinery.all_machines) + if(M.id == src.id) + if(M.density) + spawn(0) + M.open() + return + else + spawn(0) + M.close() + return + +/obj/machinery/button/remote/blast_door/open_only/trigger() + for(var/obj/machinery/door/blast/M in SSmachinery.all_machines) + if(M.id == src.id) + if(M.density) + spawn(0) + M.open() + 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 SSmachinery.all_machines) + if(E.id == src.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() + + var/list/same_id = list() + + for(var/obj/machinery/door/blast/M in SSmachinery.all_machines) + if (M.id == src.id) + same_id += M + INVOKE_ASYNC(M, /obj/machinery/door/blast/open) + + sleep(20) + + for(var/obj/machinery/mass_driver/M in SSmachinery.all_machines) + if(M.id == src.id) + M.drive() + + sleep(50) + + for(var/mm in same_id) + INVOKE_ASYNC(mm, /obj/machinery/door/blast/close) + + icon_state = "launcherbtt" + active = 0 + update_icon() + + return + +/obj/machinery/button/remote/driver/update_icon() + if(!active || (stat & NOPOWER)) + icon_state = "launcherbtt" + else + icon_state = "launcheract" diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index cb85ceaf396..b357b8664d6 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -1,1472 +1,1472 @@ -#define AIRLOCK_CRUSH_DIVISOR 8 // Damage caused by airlock crushing a mob is split into multiple smaller hits. Prevents things like cut off limbs, etc, while still having quite dangerous injury. -#define CYBORG_AIRLOCKCRUSH_RESISTANCE 4 // Damage caused to silicon mobs (usually cyborgs) from being crushed by airlocks is divided by this number. Unlike organics cyborgs don't have passive regeneration, so even one hit can be devastating for them. - -#define BOLTS_FINE 0 -#define BOLTS_EXPOSED 1 -#define BOLTS_CUT 2 - -/obj/machinery/door/airlock - name = "Airlock" - icon = 'icons/obj/doors/Doorint.dmi' - icon_state = "door_closed" - power_channel = ENVIRON - hatch_colour = "#7d7d7d" - - explosion_resistance = 10 - 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/next_beep_at = 0 //World time when we may next beep due to doors being blocked by mobs - var/spawnPowerRestoreRunning = 0 - var/welded = null - var/locked = 0 - var/bolt_cut_state = BOLTS_FINE - 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/airlock_electronics/electronics = null - var/hasShocked = 0 //Prevents multiple shocks from happening - var/secured_wires = 0 - var/datum/wires/airlock/wires = null - var/obj/item/device/magnetic_lock/bracer = null - var/panel_visible_while_open = FALSE - - var/open_sound_powered = 'sound/machines/airlock.ogg' - var/close_sound_powered = 'sound/machines/airlockclose.ogg' - var/open_sound_unpowered = 'sound/machines/airlock_open_force.ogg' - var/close_sound_unpowered = 'sound/machines/airlock_close_force.ogg' - - var/bolts_dropping = 'sound/machines/boltsdown.ogg' - var/bolts_rising = 'sound/machines/boltsup.ogg' - - hashatch = 1 - - var/_wifi_id - var/datum/wifi/receiver/button/door/wifi_receiver - var/has_set_boltlight = FALSE - - var/insecure = 1 //if the door is insecure it will open when power runs out - var/securitylock = 0 - -/obj/machinery/door/airlock/attack_generic(var/mob/user, var/damage) - if(stat & (BROKEN|NOPOWER)) - if(damage >= 10) - 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/get_material() - if(mineral) - return get_material_by_name(mineral) - return get_material_by_name(DEFAULT_WALL_MATERIAL) - -/obj/machinery/door/airlock/command - name = "Airlock" - icon = 'icons/obj/doors/Doorcom.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_com - hatch_colour = "#446892" - -/obj/machinery/door/airlock/sac - name = "Airlock" - icon = 'icons/obj/doors/DoorSAC.dmi' - assembly_type = null - aiControlDisabled = 1 - hackProof = 1 - electrified_until = -1 - open_sound_powered = 'sound/machines/airlock_open_force.ogg' - -/obj/machinery/door/airlock/security - name = "Airlock" - icon = 'icons/obj/doors/Doorsec.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_sec - hatch_colour = "#677c97" - -/obj/machinery/door/airlock/engineering - name = "Airlock" - icon = 'icons/obj/doors/Dooreng.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_eng - hatch_colour = "#caa638" - -/obj/machinery/door/airlock/medical - name = "Airlock" - icon = 'icons/obj/doors/Doormed.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_med - hatch_colour = "#d2d2d2" - -/obj/machinery/door/airlock/maintenance - name = "Maintenance Access" - icon = 'icons/obj/doors/Doormaint.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_mai - hatch_colour = "#7d7d7d" - -/obj/machinery/door/airlock/external - name = "External Airlock" - icon = 'icons/obj/doors/Doorext.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_ext - hashatch = 0 - insecure = 0 - -/obj/machinery/door/airlock/science - name = "Airlock" - icon = 'icons/obj/doors/Doorsci.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_science - hatch_colour = "#d2d2d2" - -/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 - hatch_colour = "#d2d2d2" - -/obj/machinery/door/airlock/glass - name = "Glass Airlock" - icon = 'icons/obj/doors/Doorglass.dmi' - hitsound = 'sound/effects/Glasshit.ogg' - open_sound_powered = 'sound/machines/windowdoor.ogg' - close_sound_powered = 'sound/machines/windowdoor.ogg' - maxhealth = 300 - explosion_resistance = 5 - opacity = 0 - glass = 1 - panel_visible_while_open = TRUE - hatch_colour = "#eaeaea" - -/obj/machinery/door/airlock/centcom - name = "Airlock" - icon = 'icons/obj/doors/Doorele.dmi' - opacity = 0 - hatch_colour = "#606061" - hashatch = FALSE - -/obj/machinery/door/airlock/vaurca - name = "Alien Biomass Airlock" - icon = 'icons/obj/doors/Doorvaurca.dmi' - opacity = 0 - hatch_colour = "#606061" - hashatch = FALSE - -/obj/machinery/door/airlock/centcom/attackby(obj/item/I, mob/user) - if (operating) - return - - if (allowed(user) && operable()) - if (density) - open() - else - close() - else - do_animate("deny") - -/obj/machinery/door/airlock/centcom/attack_ai(mob/user) - return attackby(null, user) - -/obj/machinery/door/airlock/centcom/take_damage() - return // No. - -/obj/machinery/door/airlock/centcom/emag_act() - return NO_EMAG_ACT - -/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_vault - hashatch = 0 - maxhealth = 800 - panel_visible_while_open = TRUE - insecure = 0 - -/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' - desc = "An extra thick, double-insulated door to preserve the cold atmosphere. Keep closed at all times." - maxhealth = 800 - opacity = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_fre - hatch_colour = "#ffffff" - open_duration = 20 - -/obj/machinery/door/airlock/freezer_maint - name = "Freezer Maintenance Access" - icon = 'icons/obj/doors/Doormaintfreezer.dmi' - desc = "An extra thick, double-insulated door to preserve the cold atmosphere. Keep closed at all times." - opacity = 1 - assembly_type = /obj/structure/door_assembly/door_assembly_fma - hatch_colour = "#ffffff" - open_duration = 20 - -/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 - hatch_colour = "#5b5b5b" - var/hatch_colour_bolted = "#695a5a" - insecure = 0 - -/obj/machinery/door/airlock/hatch/update_icon()//Special hatch colour setting for this one snowflakey door that changes color when bolted - if (hashatch) - if(density && locked && lights && src.arePowerSystemsOn()) - hatch_image.color = hatch_colour_bolted - else - hatch_image.color = hatch_colour - ..() - -/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 - hatch_colour = "#7d7d7d" - -/obj/machinery/door/airlock/glass_command - name = "Glass 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 - hatch_colour = "#3e638c" - -/obj/machinery/door/airlock/glass_engineering - name = "Glass 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 - hatch_colour = "#caa638" - -/obj/machinery/door/airlock/glass_security - name = "Glass 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 - hatch_colour = "#677c97" - -/obj/machinery/door/airlock/glass_medical - name = "Glass 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 - hatch_colour = "#d2d2d2" - -/obj/machinery/door/airlock/mining - name = "Mining Airlock" - icon = 'icons/obj/doors/Doormining.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_min - hatch_colour = "#c29142" - -/obj/machinery/door/airlock/atmos - name = "Atmospherics Airlock" - icon = 'icons/obj/doors/Dooratmo.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_atmo - hatch_colour = "#caa638" - -/obj/machinery/door/airlock/research - name = "Airlock" - icon = 'icons/obj/doors/Doorresearch.dmi' - assembly_type = /obj/structure/door_assembly/door_assembly_research - hatch_colour = "#d2d2d2" - -/obj/machinery/door/airlock/glass_research - name = "Glass 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 - heat_proof = 1 - hatch_colour = "#d2d2d2" - -/obj/machinery/door/airlock/glass_mining - name = "Glass 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 - hatch_colour = "#c29142" - -/obj/machinery/door/airlock/glass_atmos - name = "Glass 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 - hatch_colour = "#caa638" - - - -/obj/machinery/door/airlock/gold - name = "Gold Airlock" - icon = 'icons/obj/doors/Doorgold.dmi' - mineral = "gold" - hatch_colour = "#dbbb2b" - -/obj/machinery/door/airlock/silver - name = "Silver Airlock" - icon = 'icons/obj/doors/Doorsilver.dmi' - mineral = "silver" - hatch_colour = "#ffffff" - -/obj/machinery/door/airlock/diamond - name = "Diamond Airlock" - icon = 'icons/obj/doors/Doordiamond.dmi' - mineral = "diamond" - hatch_colour = "#66eeee" - maxhealth = 2000 - - -/obj/machinery/door/airlock/sandstone - name = "Sandstone Airlock" - icon = 'icons/obj/doors/Doorsand.dmi' - mineral = "sandstone" - hatch_colour = "#efc8a8" - -/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 - hatch_colour = "#5a5a66" - maxhealth = 600 - insecure = 0 - - - -/obj/machinery/door/airlock/uranium/machinery_process() - if(world.time > last_event+20) - if(prob(50)) - radiate() - last_event = world.time - ..() - -//---Uranium doors -/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 - hatch_colour = "#004400" - -/obj/machinery/door/airlock/uranium/proc/radiate() - for(var/mob/living/L in range (3,src)) - L.apply_effect(15,IRRADIATE, blocked = L.getarmor(null, "rad")) - return - - -//---Phoron door -/obj/machinery/door/airlock/phoron - name = "Phoron Airlock" - desc = "No way this can end badly." - icon = 'icons/obj/doors/Doorphoron.dmi' - mineral = "phoron" - hatch_colour = "#891199" - -/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) - -//------------------------- - -/* -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(user)) - 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 && !user.is_diona() && !user.isSynthetic()) - to_chat(user, "You feel a powerful shock course through your body!") - user.adjustHalLoss(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/isWireCut(var/wireIndex) - // You can find the wires in the datum folder. - return QDELETED(wires) ? FALSE : wires.IsIndexCut(wireIndex) - -/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 !(src.isWireCut(AIRLOCK_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 src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1) || src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2) - -/obj/machinery/door/airlock/proc/backupPowerCablesCut() - return src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1) || src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2) - -/obj/machinery/door/airlock/proc/loseMainPower() - main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) - if (main_power_lost_until > 0) - addtimer(CALLBACK(src, .proc/regainMainPower), 60 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_NO_HASH_WAIT) - - // 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) - addtimer(CALLBACK(src, .proc/regainBackupPower), 10 SECONDS, TIMER_UNIQUE | TIMER_NO_HASH_WAIT) - - // Disable electricity if required - if(electrified_until && isAllPowerLoss()) - electrify(0) - -/obj/machinery/door/airlock/proc/loseBackupPower() - backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) - if (backup_power_lost_until > 0) - addtimer(CALLBACK(src, .proc/regainBackupPower), 60 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_NO_HASH_WAIT) - - // Disable electricity if required - if(electrified_until && isAllPowerLoss()) - electrify(0) - -/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 - -/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 - -/obj/machinery/door/airlock/proc/electrify(var/duration, var/feedback = 0) - var/message = "" - if(src.isWireCut(AIRLOCK_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) - LAZYADD(shockedby, "\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") - usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") - else - LAZYADD(shockedby, "\[[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) - addtimer(CALLBACK(src, .proc/electrify, 0), duration SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_NO_HASH_WAIT) - - if(feedback && message) - to_chat(usr, message) - -/obj/machinery/door/airlock/proc/set_idscan(var/activate, var/feedback = 0) - var/message = "" - if(src.isWireCut(AIRLOCK_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 (src.isWireCut(AIRLOCK_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 - -// Only set_light() if there's a change, no need to waste processor cycles with lighting updates. -/obj/machinery/door/airlock/update_icon() - if (QDELING(src)) - return - cut_overlays() - var/list/new_overlays = list() - if(density) - if(locked && lights && src.arePowerSystemsOn()) - icon_state = "door_locked" - if (!has_set_boltlight) - set_light(2, 0.75, COLOR_RED_LIGHT) - has_set_boltlight = TRUE - else - icon_state = "door_closed" - if (has_set_boltlight) - set_light(0) - has_set_boltlight = FALSE - if(p_open || welded) - if(p_open) - new_overlays += "panel_open" - if (!(stat & NOPOWER)) - if(stat & BROKEN) - new_overlays += "sparks_broken" - else if (health < maxhealth * 3/4) - new_overlays += "sparks_damaged" - if(welded) - new_overlays += "welded" - else if (health < maxhealth * 3/4 && !(stat & NOPOWER)) - new_overlays += "sparks_damaged" - - if (hatch_image) - if (hatchstate) - hatch_image.icon_state = "[hatchstyle]_open" - else - hatch_image.icon_state = hatchstyle - new_overlays += hatch_image - else - if(p_open && panel_visible_while_open) - icon_state = "o_door_open" - else - icon_state = "door_open" - - if((stat & BROKEN) && !(stat & NOPOWER)) - add_overlay("sparks_open") - if (has_set_boltlight) - set_light(0) - has_set_boltlight = FALSE - - add_overlay(new_overlays) - -/obj/machinery/door/airlock/do_animate(animation) - switch(animation) - if("opening") - cut_overlays() - compile_overlays() // Flick will prevent SSoverlays from changing the overlay list mid-flick, so we force it. - if(p_open) - flick("o_door_opening", src) - update_icon() - else - flick(stat ? "door_opening_stat" : "door_opening", src) - update_icon() - if("closing") - if(overlays) - cut_overlays() - compile_overlays() - if(p_open) - flick("o_door_closing", src) - update_icon() - else - flick(stat ? "door_closing_stat" : "door_closing", src) - update_icon() - if("spark") - if(density) - flick("door_spark", src) - if("deny") - if(density && src.arePowerSystemsOn()) - flick("door_deny", src) - if(secured_wires) - playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) - if ("braced") - if (src.arePowerSystemsOn()) - flick("door_deny", src) - playsound(src.loc, 'sound/machines/hydraulic_short.ogg', 50, 0) - -/obj/machinery/door/airlock/attack_ai(mob/user as mob) - ui_interact(user) - -/obj/machinery/door/airlock/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/data[0] - - data["main_power_loss"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1) - data["backup_power_loss"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1) - data["electrified"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1) - data["open"] = !density - - var/commands[0] - commands[++commands.len] = list("name" = "IdScan", "command"= "idscan", "active" = !aiDisabledIdScanner, "enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1) - commands[++commands.len] = list("name" = "Bolts", "command"= "bolts", "active" = !locked, "enabled" = "Raised ", "disabled" = "Dropped", "danger" = 0, "act" = 0) - commands[++commands.len] = list("name" = "Bolt Lights", "command"= "lights", "active" = lights, "enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1) - commands[++commands.len] = list("name" = "Safeties", "command"= "safeties", "active" = safe, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0) - commands[++commands.len] = list("name" = "Timing", "command"= "timing", "active" = normalspeed, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0) - commands[++commands.len] = list("name" = "Door State", "command"= "open", "active" = density, "enabled" = "Closed", "disabled" = "Opened", "danger" = 0, "act" = 0) - - data["commands"] = commands - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "door_control.tmpl", "Door Controls", 450, 350, state = state) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -/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, height=0, air_group=0) - if (src.isElectrified()) - if (istype(mover, /obj/item)) - var/obj/item/i = mover - if (i.matter && (DEFAULT_WALL_MATERIAL in i.matter) && i.matter[DEFAULT_WALL_MATERIAL] > 0) - spark(src, 5, alldirs) - 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(ishuman(user)) - var/mob/living/carbon/human/H = user - - if(H.getBrainLoss() >= 50) - if(prob(40) && src.density) - playsound(src.loc, 'sound/effects/bang.ogg', 25, 1) - if(!istype(H.head, /obj/item/clothing/head/helmet)) - user.visible_message("[user] headbutts the airlock.") - var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD) - H.Stun(8) - H.Weaken(5) - if(affecting.take_damage(10, 0)) - H.UpdateDamageIcon() - else - user.visible_message("[user] headbutts the airlock. Good thing they're wearing a helmet.") - return - - if(H.species.can_shred(H)) - - if(!src.density) - return - - H.visible_message("\The [H] begins to pry open \the [src]!", "You begin to pry open \the [src]!", "You hear the sound of an airlock being forced open.") - - if(!do_after(H, 120, 1, act_target = src)) - return - - src.do_animate("spark") - src.stat |= BROKEN - var/check = src.open(1) - H.visible_message("\The [H] slices \the [src]'s controls[check ? ", ripping it open!" : ", breaking it!"]", "You slice \the [src]'s controls[check ? ", ripping it open!" : ", breaking it!"]", "You hear something sparking.") - return - if(src.p_open) - user.set_machine(src) - wires.Interact(user) - else - ..(user) - return - -//returns 1 on success, 0 on failure -/obj/machinery/door/airlock/proc/cut_bolts(var/obj/item/tool, var/mob/user) - var/cut_delay = 200 - var/cut_verb - var/cut_sound - var/cutting = FALSE - - if(istype(tool,/obj/item/weldingtool)) - var/obj/item/weldingtool/WT = tool - if(!WT.isOn()) - return - if(!WT.remove_fuel(0,user)) - to_chat(user, span("notice", "You need more welding fuel to complete this task.")) - return - cut_verb = "cutting" - cut_sound = 'sound/items/Welder.ogg' - cut_delay *= 1.5/WT.toolspeed - cutting = TRUE - else if(istype(tool,/obj/item/gun/energy/plasmacutter)) - cut_verb = "cutting" - cut_sound = 'sound/items/Welder.ogg' - cut_delay *= 1 - cutting = TRUE - else if(istype(tool,/obj/item/melee/energy/blade) || istype(tool,/obj/item/melee/energy/sword)) - cut_verb = "slicing" - cut_sound = "sparks" - cut_delay *= 1 - cutting = TRUE - else if(istype(tool,/obj/item/circular_saw)) - cut_verb = "sawing" - cut_sound = 'sound/weapons/saw/circsawhit.ogg' - cut_delay *= 2 - cutting = TRUE - else if(istype(tool,/obj/item/material/twohanded/fireaxe)) - //fireaxe can smash open the bolt cover instantly - var/obj/item/material/twohanded/fireaxe/F = tool - if (!F.wielded) - return FALSE - if(src.bolt_cut_state == BOLTS_FINE) - to_chat(user, span("warning", "You smash the bolt cover open!")) - playsound(src, 'sound/weapons/smash.ogg', 100, 1) - src.bolt_cut_state = BOLTS_EXPOSED - else if(src.bolt_cut_state != BOLTS_FINE) - cut_verb = "smashing" - cut_sound = 'sound/weapons/smash.ogg' - cut_delay *= 1 - cutting = TRUE - if(cutting) - cut_procedure(user, cut_delay, cut_verb, cut_sound) - else - return FALSE - -/obj/machinery/door/airlock/proc/cut_procedure(var/mob/user, var/cut_delay, var/cut_verb, var/cut_sound) - if(src.bolt_cut_state == BOLTS_FINE) - to_chat(user, "You begin [cut_verb] through the bolt panel.") - else if(src.bolt_cut_state == BOLTS_EXPOSED) - to_chat(user, "You begin [cut_verb] through the door bolts.") - - cut_delay *= 0.25 - - var/i - for(i = 0; i < 4; i += 1) - if(i == 0) - if(do_after(user, cut_delay, src)) - to_chat(user, span("notice", "You're a quarter way through.")) - playsound(src, cut_sound, 100, 1) - else if(i == 1) - if(do_after(user, cut_delay, src)) - to_chat(user, span("notice", "You're halfway through.")) - playsound(src, cut_sound, 100, 1) - else if(i == 2) - if(do_after(user, cut_delay, src)) - to_chat(user, span("notice", "You're three quarters through.")) - playsound(src, cut_sound, 100, 1) - else if(i == 3) - if(do_after(user, cut_delay, src)) - playsound(src, cut_sound, 100, 1) - if(src.bolt_cut_state == BOLTS_FINE) - to_chat(user, span("notice", "You remove the cover and expose the door bolts.")) - src.bolt_cut_state = BOLTS_EXPOSED - else if(src.bolt_cut_state == BOLTS_EXPOSED) - to_chat(user, span("notice", "You sever the door bolts, unlocking the door.")) - src.bolt_cut_state = BOLTS_CUT - src.unlock(TRUE) //force it - -/obj/machinery/door/airlock/CanUseTopic(var/mob/user) - if(operating < 0) //emagged - to_chat(user, "Unable to interface: Internal error.") - return STATUS_CLOSE - if(issilicon(user) && !src.canAIControl()) - if(src.canAIHack(user)) - src.hack(user) - else - if (src.isAllPowerLoss()) //don't really like how this gets checked a second time, but not sure how else to do it. - to_chat(user, "Unable to interface: Connection timed out.") - else - to_chat(user, "Unable to interface: Connection refused.") - return STATUS_CLOSE - - return ..() - -/obj/machinery/door/airlock/Topic(href, href_list) - if(..()) - return 1 - - var/activate = text2num(href_list["activate"]) - switch (href_list["command"]) - if("idscan") - set_idscan(activate, 1) - if("main_power") - if(!main_power_lost_until) - src.loseMainPower() - if("backup_power") - if(!backup_power_lost_until) - src.loseBackupPower() - if("bolts") - if(src.isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) - to_chat(usr, "The door bolt control wire is cut - Door bolts permanently dropped.") - else if(activate && src.lock()) - to_chat(usr, "The door bolts have been dropped.") - else if(!activate && src.unlock()) - to_chat(usr, "The door bolts have been raised.") - if("electrify_temporary") - electrify(30 * activate, 1) - if("electrify_permanently") - electrify(-1 * activate, 1) - if("open") - if(src.welded) - to_chat(usr, text("The airlock has been welded shut!")) - else if(src.locked) - to_chat(usr, text("The door bolts are down!")) - else if(activate && density) - open() - if (isAI(usr)) - SSfeedback.IncrementSimpleStat("AI_DOOR") - else if(!activate && !density) - close() - if("safeties") - set_safeties(!activate, 1) - if("timing") - // Door speed control - if(src.isWireCut(AIRLOCK_WIRE_SPEED)) - to_chat(usr, text("The timing wire is cut - Cannot alter timing.")) - else if (activate && src.normalspeed) - normalspeed = 0 - else if (!activate && !src.normalspeed) - normalspeed = 1 - if("lights") - // Bolt lights - if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) - to_chat(usr, "The bolt lights wire is cut - The door bolt lights are permanently disabled.") - else if (!activate && src.lights) - lights = 0 - to_chat(usr, "The door bolt lights have been disabled.") - else if (activate && !src.lights) - lights = 1 - to_chat(usr, "The door bolt lights have been enabled.") - update_icon() - return 1 - - - -/obj/machinery/door/airlock/proc/CreateAssembly() - 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 = 1 - if(mineral) - da.glass = mineral - 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/circuitboard/broken(src.loc) - operating = 0 - else - if (!electronics) create_electronics() - electronics.forceMove(src.loc) - electronics = null - qdel(src) - - -/obj/machinery/door/airlock/proc/CanChainsaw(var/obj/item/material/twohanded/chainsaw/ChainSawVar) - return (ChainSawVar.powered && density && hashatch) - -/obj/machinery/door/airlock/attackby(var/obj/item/C, mob/user as mob) - 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 (!repairing && (stat & BROKEN) && src.locked) //bolted and broken - if (!cut_bolts(C,user)) - ..() - return - if (istype(C, /obj/item/device/magnetic_lock)) - if (bracer) - to_chat(user, "There is already a [bracer] on [src]!") - return - var/obj/item/device/magnetic_lock/newbracer = C - newbracer.attachto(src, user) - return - if(!repairing && (C.iswelder() && !( src.operating > 0 ) && src.density)) - var/obj/item/weldingtool/WT = C - if(WT.isOn()) - user.visible_message( - "[user] begins welding [src] [welded ? "open" : "shut"].", - "You begin welding [src] [welded ? "open" : "shut"].", - "You hear a welding torch on metal." - ) - playsound(loc, 'sound/items/Welder.ogg', 50, 1) - if (!do_after(user, 2/C.toolspeed SECONDS, act_target = src, extra_checks = CALLBACK(src, .proc/is_open, src.density))) - return - if(!WT.remove_fuel(0,user)) - to_chat(user, "You need more welding fuel to complete this task.") - return - if(!src.welded) - src.welded = 1 - else - src.welded = null - src.update_icon() - return - else - return - else if(C.isscrewdriver()) - if (src.p_open) - if (stat & BROKEN) - to_chat(usr, "The panel is broken and cannot be closed.") - else - src.p_open = 0 - else - src.p_open = 1 - src.update_icon() - else if(C.iswirecutter()) - return src.attack_hand(user) - else if(C.ismultitool()) - 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/pai_cable)) // -- TLE - var/obj/item/pai_cable/cable = C - cable.plugin(src, user) - else if(!repairing && C.iscrowbar()) - if(istype(C, /obj/item/melee/arm_blade)) - if(!arePowerSystemsOn()) //if this check isn't done and empty, the armblade will never be used to hit the airlock - else if(!(stat & BROKEN)) - ..() - return - if(src.p_open && (operating < 0 || (!operating && welded && !src.arePowerSystemsOn() && density && !src.locked))) - playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 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!") - CreateAssembly() - 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) - open(1) - else - close(1) - else if(istype(C, /obj/item/material/twohanded/fireaxe) && !arePowerSystemsOn()) - if(locked && user.a_intent != I_HURT) - to_chat(user, "The airlock's bolts prevent it from being forced.") - else if(locked && user.a_intent == I_HURT) - ..() - else if(!welded && !operating) - if(density) - var/obj/item/material/twohanded/fireaxe/F = C - if(F.wielded) - open(1) - else - to_chat(user, "You need to be wielding \the [C] to do that.") - else - var/obj/item/material/twohanded/fireaxe/F = C - if(F.wielded) - close(1) - else - to_chat(user, "You need to be wielding \the [C] to do that.") - else if(istype(C, /obj/item/melee/hammer) && !arePowerSystemsOn()) - if(locked && user.a_intent != I_HURT) - to_chat(user, "The airlock's bolts prevent it from being forced.") - else if(locked && user.a_intent == I_HURT) - ..() - else if(!welded && !operating) - if(density) - open(1) - else - close(1) - else if(density && istype(C, /obj/item/material/twohanded/chainsaw)) - var/obj/item/material/twohanded/chainsaw/ChainSawVar = C - if(!ChainSawVar.wielded) - to_chat(user, "Cutting the airlock requires the strength of two hands.") - else if(ChainSawVar.cutting) - to_chat(user, "You are already cutting an airlock open.") - else if(!ChainSawVar.powered) - to_chat(user, "The [C] needs to be on in order to open this door.") - else if(bracer) //Has a magnetic lock - to_chat(user, "The bracer needs to be removed in order to cut through this door.") - else if(!arePowerSystemsOn()) - ChainSawVar.cutting = 1 - user.visible_message(\ - "[user.name] starts cutting the control pannel of the airlock with the [C]!",\ - "You start cutting the airlock control panel...",\ - "You hear a loud buzzing sound and metal grinding on metal..."\ - ) - if(do_after(user, ChainSawVar.opendelay SECONDS, act_target = user, extra_checks = CALLBACK(src, .proc/CanChainsaw, C))) - user.visible_message(\ - "[user.name] finishes cutting the control pannel of the airlock with the [C].",\ - "You finish cutting the airlock control panel.",\ - "You hear a metal clank and some sparks."\ - ) - set_broken() - sleep(1 SECONDS) - CreateAssembly() - ChainSawVar.cutting = 0 - take_damage(50) - else if(locked) - ChainSawVar.cutting = 1 - user.visible_message(\ - "[user.name] starts cutting below the airlock with the [C]!",\ - "You start cutting below the airlock...",\ - "You hear a loud buzzing sound and metal grinding on metal..."\ - ) - if(do_after(user, ChainSawVar.opendelay SECONDS, act_target = user, extra_checks = CALLBACK(src, .proc/CanChainsaw, C))) - user.visible_message(\ - "[user.name] finishes cutting below the airlock with the [C].",\ - "You finish cutting below the airlock.",\ - "You hear a metal clank and some sparks."\ - ) - unlock(1) - ChainSawVar.cutting = 0 - take_damage(50) - else - ChainSawVar.cutting = 1 - user.visible_message(\ - "[user.name] starts cutting between the airlock with the [C]!",\ - "You start cutting between the airlock...",\ - "You hear a loud buzzing sound and metal grinding on metal..."\ - ) - if(do_after(user, ChainSawVar.opendelay SECONDS, act_target = user, extra_checks = CALLBACK(src, .proc/CanChainsaw, C))) - user.visible_message(\ - "[user.name] finishes cutting between the airlock.",\ - "You finish cutting between the airlock.",\ - "You hear a metal clank and some sparks."\ - ) - open(1) - take_damage(50) - ChainSawVar.cutting = 0 - 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 = 1 - 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!") - - spark(src, 5, alldirs) - - 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 - if(arePowerSystemsOn()) - playsound(src.loc, open_sound_powered, 60, 1) - else - playsound(src.loc, open_sound_unpowered, 60, 1) - - 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() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR)) - return 0 - if (bracer) - do_animate("braced") - visible_message("[src]'s actuators whirr, but the door does not open.") - 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() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR)) - return 0 - return ..() - -/atom/movable/proc/blocks_airlock() - return density - -/obj/machinery/door/blocks_airlock() - return 0 - -/obj/structure/window/blocks_airlock() - return 0 - -/obj/machinery/mech_sensor/blocks_airlock() - return 0 - -/mob/living/blocks_airlock() - return mob_size > MOB_SMALL - -/atom/movable/proc/airlock_crush(var/crush_damage) - return 0 - -/obj/structure/window/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) - Stress(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) - . = ..() - for(var/i = 1, i <= AIRLOCK_CRUSH_DIVISOR, i++) - adjustBruteLoss(round(crush_damage / AIRLOCK_CRUSH_DIVISOR)) - SetStunned(5) - SetWeakened(5) - - var/turf/T = get_turf(src) - - var/list/valid_turfs = list() - for(var/dir_to_test in cardinal) - var/turf/new_turf = get_step(T, dir_to_test) - if(!new_turf.contains_dense_objects()) - valid_turfs |= new_turf - - while(valid_turfs.len) - T = pick(valid_turfs) - valid_turfs -= T - - if(src.forceMove(T)) - return - - -/mob/living/carbon/airlock_crush(var/crush_damage) - . = ..() - if (can_feel_pain()) - emote("scream") - -/mob/living/silicon/robot/airlock_crush(var/crush_damage) - return ..(round(crush_damage / CYBORG_AIRLOCKCRUSH_RESISTANCE)) - -/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()) - close_door_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 - if(arePowerSystemsOn()) - playsound(src.loc, close_sound_powered, 100, 1) - else - playsound(src.loc, close_sound_unpowered, 100, 1) - - ..() - -/obj/machinery/door/airlock/proc/lock(var/forced=0) - if(locked) - return 0 - if (operating && !forced) return 0 - if (bolt_cut_state == BOLTS_CUT) return 0 //what bolts? - src.locked = 1 - playsound(src, bolts_dropping, 30, 0, -6) - update_icon() - return 1 - -/obj/machinery/door/airlock/proc/unlock(var/forced=0) - if(!src.locked) - return - if (!forced) - if(operating || !src.arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) return - src.locked = 0 - playsound(src, bolts_rising, 30, 0, -6) - update_icon() - return 1 - -/obj/machinery/door/airlock/allowed(mob/M) - if(locked) - return 0 - return ..(M) - -/obj/machinery/door/airlock/Initialize(mapload, obj/structure/door_assembly/assembly = null) - var/on_admin_z = FALSE - //wires & hatch - this needs to be done up here so the hatch isn't generated by the parent Initialize(). - if(loc && isAdminLevel(z)) - on_admin_z = TRUE - hashatch = FALSE - - . = ..() - - //if assembly is given, create the new door from the assembly - if (istype(assembly)) - assembly_type = assembly.type - - electronics = assembly.electronics - electronics.forceMove(src) - - //update the door's access to match the electronics' - secured_wires = electronics.secure - if(electronics.one_access) - req_access = null - req_one_access = electronics.conf_access - else - req_one_access = null - req_access = 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) - - if (on_admin_z) - secured_wires = TRUE - - if (secured_wires) - wires = new/datum/wires/airlock/secure(src) - else - wires = new/datum/wires/airlock(src) - - if(mapload && src.closeOtherId != null) - for (var/obj/machinery/door/airlock/A in SSmachinery.processing_machines) - if(A.closeOtherId == src.closeOtherId && A != src) - src.closeOther = A - break - -/obj/machinery/door/airlock/Destroy() - qdel(wires) - wires = null - qdel(wifi_receiver) - wifi_receiver = 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/airlock_electronics/secure( src.loc ) - else - src.electronics = new/obj/item/airlock_electronics( src.loc ) - - //update the electronics to match the door's access - if(!src.req_access) - src.check_access() - if(LAZYLEN(req_access)) - electronics.conf_access = src.req_access - else if (LAZYLEN(req_one_access)) - electronics.conf_access = src.req_one_access - electronics.one_access = 1 - -/obj/machinery/door/airlock/emp_act(var/severity) - if(prob(40/severity)) - var/duration = SecondsToTicks(30 / severity) - if(electrified_until > -1 && (duration + world.time) > 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 - //if we lost power open 'er up - if(insecure) - INVOKE_ASYNC(src, /obj/machinery/door/.proc/open, 1) - securitylock = 1 - else if(securitylock) - INVOKE_ASYNC(src, /obj/machinery/door/.proc/close, 1) - securitylock = 0 - update_icon() - -/obj/machinery/door/airlock/proc/prison_open() - if(bracer) - return - - if(arePowerSystemsOn()) - src.unlock() - src.open() - src.lock() - return - -/obj/machinery/door/airlock/examine() - ..() - if (bolt_cut_state == BOLTS_EXPOSED) - to_chat(usr, "The bolt cover has been cut open.") - if (bolt_cut_state == BOLTS_CUT) - to_chat(usr, "The door bolts have been cut.") - if(bracer) - to_chat(usr, "\The [bracer] is installed on \the [src], preventing it from opening.") - to_chat(usr, bracer.health) - -#undef AIRLOCK_CRUSH_DIVISOR -#undef CYBORG_AIRLOCKCRUSH_RESISTANCE -#undef BOLTS_FINE -#undef BOLTS_EXPOSED -#undef BOLTS_CUT +#define AIRLOCK_CRUSH_DIVISOR 8 // Damage caused by airlock crushing a mob is split into multiple smaller hits. Prevents things like cut off limbs, etc, while still having quite dangerous injury. +#define CYBORG_AIRLOCKCRUSH_RESISTANCE 4 // Damage caused to silicon mobs (usually cyborgs) from being crushed by airlocks is divided by this number. Unlike organics cyborgs don't have passive regeneration, so even one hit can be devastating for them. + +#define BOLTS_FINE 0 +#define BOLTS_EXPOSED 1 +#define BOLTS_CUT 2 + +/obj/machinery/door/airlock + name = "Airlock" + icon = 'icons/obj/doors/Doorint.dmi' + icon_state = "door_closed" + power_channel = ENVIRON + hatch_colour = "#7d7d7d" + + explosion_resistance = 10 + 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/next_beep_at = 0 //World time when we may next beep due to doors being blocked by mobs + var/spawnPowerRestoreRunning = 0 + var/welded = null + var/locked = 0 + var/bolt_cut_state = BOLTS_FINE + 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/airlock_electronics/electronics = null + var/hasShocked = 0 //Prevents multiple shocks from happening + var/secured_wires = 0 + var/datum/wires/airlock/wires = null + var/obj/item/device/magnetic_lock/bracer = null + var/panel_visible_while_open = FALSE + + var/open_sound_powered = 'sound/machines/airlock.ogg' + var/close_sound_powered = 'sound/machines/airlockclose.ogg' + var/open_sound_unpowered = 'sound/machines/airlock_open_force.ogg' + var/close_sound_unpowered = 'sound/machines/airlock_close_force.ogg' + + var/bolts_dropping = 'sound/machines/boltsdown.ogg' + var/bolts_rising = 'sound/machines/boltsup.ogg' + + hashatch = 1 + + var/_wifi_id + var/datum/wifi/receiver/button/door/wifi_receiver + var/has_set_boltlight = FALSE + + var/insecure = 1 //if the door is insecure it will open when power runs out + var/securitylock = 0 + +/obj/machinery/door/airlock/attack_generic(var/mob/user, var/damage) + if(stat & (BROKEN|NOPOWER)) + if(damage >= 10) + 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/get_material() + if(mineral) + return get_material_by_name(mineral) + return get_material_by_name(DEFAULT_WALL_MATERIAL) + +/obj/machinery/door/airlock/command + name = "Airlock" + icon = 'icons/obj/doors/Doorcom.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_com + hatch_colour = "#446892" + +/obj/machinery/door/airlock/sac + name = "Airlock" + icon = 'icons/obj/doors/DoorSAC.dmi' + assembly_type = null + aiControlDisabled = 1 + hackProof = 1 + electrified_until = -1 + open_sound_powered = 'sound/machines/airlock_open_force.ogg' + +/obj/machinery/door/airlock/security + name = "Airlock" + icon = 'icons/obj/doors/Doorsec.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_sec + hatch_colour = "#677c97" + +/obj/machinery/door/airlock/engineering + name = "Airlock" + icon = 'icons/obj/doors/Dooreng.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_eng + hatch_colour = "#caa638" + +/obj/machinery/door/airlock/medical + name = "Airlock" + icon = 'icons/obj/doors/Doormed.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_med + hatch_colour = "#d2d2d2" + +/obj/machinery/door/airlock/maintenance + name = "Maintenance Access" + icon = 'icons/obj/doors/Doormaint.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_mai + hatch_colour = "#7d7d7d" + +/obj/machinery/door/airlock/external + name = "External Airlock" + icon = 'icons/obj/doors/Doorext.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_ext + hashatch = 0 + insecure = 0 + +/obj/machinery/door/airlock/science + name = "Airlock" + icon = 'icons/obj/doors/Doorsci.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_science + hatch_colour = "#d2d2d2" + +/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 + hatch_colour = "#d2d2d2" + +/obj/machinery/door/airlock/glass + name = "Glass Airlock" + icon = 'icons/obj/doors/Doorglass.dmi' + hitsound = 'sound/effects/Glasshit.ogg' + open_sound_powered = 'sound/machines/windowdoor.ogg' + close_sound_powered = 'sound/machines/windowdoor.ogg' + maxhealth = 300 + explosion_resistance = 5 + opacity = 0 + glass = 1 + panel_visible_while_open = TRUE + hatch_colour = "#eaeaea" + +/obj/machinery/door/airlock/centcom + name = "Airlock" + icon = 'icons/obj/doors/Doorele.dmi' + opacity = 0 + hatch_colour = "#606061" + hashatch = FALSE + +/obj/machinery/door/airlock/vaurca + name = "Alien Biomass Airlock" + icon = 'icons/obj/doors/Doorvaurca.dmi' + opacity = 0 + hatch_colour = "#606061" + hashatch = FALSE + +/obj/machinery/door/airlock/centcom/attackby(obj/item/I, mob/user) + if (operating) + return + + if (allowed(user) && operable()) + if (density) + open() + else + close() + else + do_animate("deny") + +/obj/machinery/door/airlock/centcom/attack_ai(mob/user) + return attackby(null, user) + +/obj/machinery/door/airlock/centcom/take_damage() + return // No. + +/obj/machinery/door/airlock/centcom/emag_act() + return NO_EMAG_ACT + +/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_vault + hashatch = 0 + maxhealth = 800 + panel_visible_while_open = TRUE + insecure = 0 + +/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' + desc = "An extra thick, double-insulated door to preserve the cold atmosphere. Keep closed at all times." + maxhealth = 800 + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_fre + hatch_colour = "#ffffff" + open_duration = 20 + +/obj/machinery/door/airlock/freezer_maint + name = "Freezer Maintenance Access" + icon = 'icons/obj/doors/Doormaintfreezer.dmi' + desc = "An extra thick, double-insulated door to preserve the cold atmosphere. Keep closed at all times." + opacity = 1 + assembly_type = /obj/structure/door_assembly/door_assembly_fma + hatch_colour = "#ffffff" + open_duration = 20 + +/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 + hatch_colour = "#5b5b5b" + var/hatch_colour_bolted = "#695a5a" + insecure = 0 + +/obj/machinery/door/airlock/hatch/update_icon()//Special hatch colour setting for this one snowflakey door that changes color when bolted + if (hashatch) + if(density && locked && lights && src.arePowerSystemsOn()) + hatch_image.color = hatch_colour_bolted + else + hatch_image.color = hatch_colour + ..() + +/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 + hatch_colour = "#7d7d7d" + +/obj/machinery/door/airlock/glass_command + name = "Glass 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 + hatch_colour = "#3e638c" + +/obj/machinery/door/airlock/glass_engineering + name = "Glass 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 + hatch_colour = "#caa638" + +/obj/machinery/door/airlock/glass_security + name = "Glass 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 + hatch_colour = "#677c97" + +/obj/machinery/door/airlock/glass_medical + name = "Glass 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 + hatch_colour = "#d2d2d2" + +/obj/machinery/door/airlock/mining + name = "Mining Airlock" + icon = 'icons/obj/doors/Doormining.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_min + hatch_colour = "#c29142" + +/obj/machinery/door/airlock/atmos + name = "Atmospherics Airlock" + icon = 'icons/obj/doors/Dooratmo.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_atmo + hatch_colour = "#caa638" + +/obj/machinery/door/airlock/research + name = "Airlock" + icon = 'icons/obj/doors/Doorresearch.dmi' + assembly_type = /obj/structure/door_assembly/door_assembly_research + hatch_colour = "#d2d2d2" + +/obj/machinery/door/airlock/glass_research + name = "Glass 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 + heat_proof = 1 + hatch_colour = "#d2d2d2" + +/obj/machinery/door/airlock/glass_mining + name = "Glass 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 + hatch_colour = "#c29142" + +/obj/machinery/door/airlock/glass_atmos + name = "Glass 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 + hatch_colour = "#caa638" + + + +/obj/machinery/door/airlock/gold + name = "Gold Airlock" + icon = 'icons/obj/doors/Doorgold.dmi' + mineral = "gold" + hatch_colour = "#dbbb2b" + +/obj/machinery/door/airlock/silver + name = "Silver Airlock" + icon = 'icons/obj/doors/Doorsilver.dmi' + mineral = "silver" + hatch_colour = "#ffffff" + +/obj/machinery/door/airlock/diamond + name = "Diamond Airlock" + icon = 'icons/obj/doors/Doordiamond.dmi' + mineral = "diamond" + hatch_colour = "#66eeee" + maxhealth = 2000 + + +/obj/machinery/door/airlock/sandstone + name = "Sandstone Airlock" + icon = 'icons/obj/doors/Doorsand.dmi' + mineral = "sandstone" + hatch_colour = "#efc8a8" + +/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 + hatch_colour = "#5a5a66" + maxhealth = 600 + insecure = 0 + + + +/obj/machinery/door/airlock/uranium/machinery_process() + if(world.time > last_event+20) + if(prob(50)) + radiate() + last_event = world.time + ..() + +//---Uranium doors +/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 + hatch_colour = "#004400" + +/obj/machinery/door/airlock/uranium/proc/radiate() + for(var/mob/living/L in range (3,src)) + L.apply_effect(15,IRRADIATE, blocked = L.getarmor(null, "rad")) + return + + +//---Phoron door +/obj/machinery/door/airlock/phoron + name = "Phoron Airlock" + desc = "No way this can end badly." + icon = 'icons/obj/doors/Doorphoron.dmi' + mineral = "phoron" + hatch_colour = "#891199" + +/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) + +//------------------------- + +/* +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(user)) + 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 && !user.is_diona() && !user.isSynthetic()) + to_chat(user, "You feel a powerful shock course through your body!") + user.adjustHalLoss(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/isWireCut(var/wireIndex) + // You can find the wires in the datum folder. + return QDELETED(wires) ? FALSE : wires.IsIndexCut(wireIndex) + +/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 !(src.isWireCut(AIRLOCK_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 src.isWireCut(AIRLOCK_WIRE_MAIN_POWER1) || src.isWireCut(AIRLOCK_WIRE_MAIN_POWER2) + +/obj/machinery/door/airlock/proc/backupPowerCablesCut() + return src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER1) || src.isWireCut(AIRLOCK_WIRE_BACKUP_POWER2) + +/obj/machinery/door/airlock/proc/loseMainPower() + main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + if (main_power_lost_until > 0) + addtimer(CALLBACK(src, .proc/regainMainPower), 60 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_NO_HASH_WAIT) + + // 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) + addtimer(CALLBACK(src, .proc/regainBackupPower), 10 SECONDS, TIMER_UNIQUE | TIMER_NO_HASH_WAIT) + + // Disable electricity if required + if(electrified_until && isAllPowerLoss()) + electrify(0) + +/obj/machinery/door/airlock/proc/loseBackupPower() + backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60) + if (backup_power_lost_until > 0) + addtimer(CALLBACK(src, .proc/regainBackupPower), 60 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_NO_HASH_WAIT) + + // Disable electricity if required + if(electrified_until && isAllPowerLoss()) + electrify(0) + +/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 + +/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 + +/obj/machinery/door/airlock/proc/electrify(var/duration, var/feedback = 0) + var/message = "" + if(src.isWireCut(AIRLOCK_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) + LAZYADD(shockedby, "\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") + usr.attack_log += text("\[[time_stamp()]\] Electrified the [name] at [x] [y] [z]") + else + LAZYADD(shockedby, "\[[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) + addtimer(CALLBACK(src, .proc/electrify, 0), duration SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_NO_HASH_WAIT) + + if(feedback && message) + to_chat(usr, message) + +/obj/machinery/door/airlock/proc/set_idscan(var/activate, var/feedback = 0) + var/message = "" + if(src.isWireCut(AIRLOCK_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 (src.isWireCut(AIRLOCK_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 + +// Only set_light() if there's a change, no need to waste processor cycles with lighting updates. +/obj/machinery/door/airlock/update_icon() + if (QDELING(src)) + return + cut_overlays() + var/list/new_overlays = list() + if(density) + if(locked && lights && src.arePowerSystemsOn()) + icon_state = "door_locked" + if (!has_set_boltlight) + set_light(2, 0.75, COLOR_RED_LIGHT) + has_set_boltlight = TRUE + else + icon_state = "door_closed" + if (has_set_boltlight) + set_light(0) + has_set_boltlight = FALSE + if(p_open || welded) + if(p_open) + new_overlays += "panel_open" + if (!(stat & NOPOWER)) + if(stat & BROKEN) + new_overlays += "sparks_broken" + else if (health < maxhealth * 3/4) + new_overlays += "sparks_damaged" + if(welded) + new_overlays += "welded" + else if (health < maxhealth * 3/4 && !(stat & NOPOWER)) + new_overlays += "sparks_damaged" + + if (hatch_image) + if (hatchstate) + hatch_image.icon_state = "[hatchstyle]_open" + else + hatch_image.icon_state = hatchstyle + new_overlays += hatch_image + else + if(p_open && panel_visible_while_open) + icon_state = "o_door_open" + else + icon_state = "door_open" + + if((stat & BROKEN) && !(stat & NOPOWER)) + add_overlay("sparks_open") + if (has_set_boltlight) + set_light(0) + has_set_boltlight = FALSE + + add_overlay(new_overlays) + +/obj/machinery/door/airlock/do_animate(animation) + switch(animation) + if("opening") + cut_overlays() + compile_overlays() // Flick will prevent SSoverlays from changing the overlay list mid-flick, so we force it. + if(p_open) + flick("o_door_opening", src) + update_icon() + else + flick(stat ? "door_opening_stat" : "door_opening", src) + update_icon() + if("closing") + if(overlays) + cut_overlays() + compile_overlays() + if(p_open) + flick("o_door_closing", src) + update_icon() + else + flick(stat ? "door_closing_stat" : "door_closing", src) + update_icon() + if("spark") + if(density) + flick("door_spark", src) + if("deny") + if(density && src.arePowerSystemsOn()) + flick("door_deny", src) + if(secured_wires) + playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) + if ("braced") + if (src.arePowerSystemsOn()) + flick("door_deny", src) + playsound(src.loc, 'sound/machines/hydraulic_short.ogg', 50, 0) + +/obj/machinery/door/airlock/attack_ai(mob/user as mob) + ui_interact(user) + +/obj/machinery/door/airlock/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + var/data[0] + + data["main_power_loss"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1) + data["backup_power_loss"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1) + data["electrified"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1) + data["open"] = !density + + var/commands[0] + commands[++commands.len] = list("name" = "IdScan", "command"= "idscan", "active" = !aiDisabledIdScanner, "enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1) + commands[++commands.len] = list("name" = "Bolts", "command"= "bolts", "active" = !locked, "enabled" = "Raised ", "disabled" = "Dropped", "danger" = 0, "act" = 0) + commands[++commands.len] = list("name" = "Bolt Lights", "command"= "lights", "active" = lights, "enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1) + commands[++commands.len] = list("name" = "Safeties", "command"= "safeties", "active" = safe, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0) + commands[++commands.len] = list("name" = "Timing", "command"= "timing", "active" = normalspeed, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0) + commands[++commands.len] = list("name" = "Door State", "command"= "open", "active" = density, "enabled" = "Closed", "disabled" = "Opened", "danger" = 0, "act" = 0) + + data["commands"] = commands + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "door_control.tmpl", "Door Controls", 450, 350, state = state) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/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, height=0, air_group=0) + if (src.isElectrified()) + if (istype(mover, /obj/item)) + var/obj/item/i = mover + if (i.matter && (DEFAULT_WALL_MATERIAL in i.matter) && i.matter[DEFAULT_WALL_MATERIAL] > 0) + spark(src, 5, alldirs) + 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(ishuman(user)) + var/mob/living/carbon/human/H = user + + if(H.getBrainLoss() >= 50) + if(prob(40) && src.density) + playsound(src.loc, 'sound/effects/bang.ogg', 25, 1) + if(!istype(H.head, /obj/item/clothing/head/helmet)) + user.visible_message("[user] headbutts the airlock.") + var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD) + H.Stun(8) + H.Weaken(5) + if(affecting.take_damage(10, 0)) + H.UpdateDamageIcon() + else + user.visible_message("[user] headbutts the airlock. Good thing they're wearing a helmet.") + return + + if(H.species.can_shred(H)) + + if(!src.density) + return + + H.visible_message("\The [H] begins to pry open \the [src]!", "You begin to pry open \the [src]!", "You hear the sound of an airlock being forced open.") + + if(!do_after(H, 120, 1, act_target = src)) + return + + src.do_animate("spark") + src.stat |= BROKEN + var/check = src.open(1) + H.visible_message("\The [H] slices \the [src]'s controls[check ? ", ripping it open!" : ", breaking it!"]", "You slice \the [src]'s controls[check ? ", ripping it open!" : ", breaking it!"]", "You hear something sparking.") + return + if(src.p_open) + user.set_machine(src) + wires.Interact(user) + else + ..(user) + return + +//returns 1 on success, 0 on failure +/obj/machinery/door/airlock/proc/cut_bolts(var/obj/item/tool, var/mob/user) + var/cut_delay = 200 + var/cut_verb + var/cut_sound + var/cutting = FALSE + + if(istype(tool,/obj/item/weldingtool)) + var/obj/item/weldingtool/WT = tool + if(!WT.isOn()) + return + if(!WT.remove_fuel(0,user)) + to_chat(user, span("notice", "You need more welding fuel to complete this task.")) + return + cut_verb = "cutting" + cut_sound = 'sound/items/Welder.ogg' + cut_delay *= 1.5/WT.toolspeed + cutting = TRUE + else if(istype(tool,/obj/item/gun/energy/plasmacutter)) + cut_verb = "cutting" + cut_sound = 'sound/items/Welder.ogg' + cut_delay *= 1 + cutting = TRUE + else if(istype(tool,/obj/item/melee/energy/blade) || istype(tool,/obj/item/melee/energy/sword)) + cut_verb = "slicing" + cut_sound = "sparks" + cut_delay *= 1 + cutting = TRUE + else if(istype(tool,/obj/item/circular_saw)) + cut_verb = "sawing" + cut_sound = 'sound/weapons/saw/circsawhit.ogg' + cut_delay *= 2 + cutting = TRUE + else if(istype(tool,/obj/item/material/twohanded/fireaxe)) + //fireaxe can smash open the bolt cover instantly + var/obj/item/material/twohanded/fireaxe/F = tool + if (!F.wielded) + return FALSE + if(src.bolt_cut_state == BOLTS_FINE) + to_chat(user, span("warning", "You smash the bolt cover open!")) + playsound(src, 'sound/weapons/smash.ogg', 100, 1) + src.bolt_cut_state = BOLTS_EXPOSED + else if(src.bolt_cut_state != BOLTS_FINE) + cut_verb = "smashing" + cut_sound = 'sound/weapons/smash.ogg' + cut_delay *= 1 + cutting = TRUE + if(cutting) + cut_procedure(user, cut_delay, cut_verb, cut_sound) + else + return FALSE + +/obj/machinery/door/airlock/proc/cut_procedure(var/mob/user, var/cut_delay, var/cut_verb, var/cut_sound) + if(src.bolt_cut_state == BOLTS_FINE) + to_chat(user, "You begin [cut_verb] through the bolt panel.") + else if(src.bolt_cut_state == BOLTS_EXPOSED) + to_chat(user, "You begin [cut_verb] through the door bolts.") + + cut_delay *= 0.25 + + var/i + for(i = 0; i < 4; i += 1) + if(i == 0) + if(do_after(user, cut_delay, src)) + to_chat(user, span("notice", "You're a quarter way through.")) + playsound(src, cut_sound, 100, 1) + else if(i == 1) + if(do_after(user, cut_delay, src)) + to_chat(user, span("notice", "You're halfway through.")) + playsound(src, cut_sound, 100, 1) + else if(i == 2) + if(do_after(user, cut_delay, src)) + to_chat(user, span("notice", "You're three quarters through.")) + playsound(src, cut_sound, 100, 1) + else if(i == 3) + if(do_after(user, cut_delay, src)) + playsound(src, cut_sound, 100, 1) + if(src.bolt_cut_state == BOLTS_FINE) + to_chat(user, span("notice", "You remove the cover and expose the door bolts.")) + src.bolt_cut_state = BOLTS_EXPOSED + else if(src.bolt_cut_state == BOLTS_EXPOSED) + to_chat(user, span("notice", "You sever the door bolts, unlocking the door.")) + src.bolt_cut_state = BOLTS_CUT + src.unlock(TRUE) //force it + +/obj/machinery/door/airlock/CanUseTopic(var/mob/user) + if(operating < 0) //emagged + to_chat(user, "Unable to interface: Internal error.") + return STATUS_CLOSE + if(issilicon(user) && !src.canAIControl()) + if(src.canAIHack(user)) + src.hack(user) + else + if (src.isAllPowerLoss()) //don't really like how this gets checked a second time, but not sure how else to do it. + to_chat(user, "Unable to interface: Connection timed out.") + else + to_chat(user, "Unable to interface: Connection refused.") + return STATUS_CLOSE + + return ..() + +/obj/machinery/door/airlock/Topic(href, href_list) + if(..()) + return 1 + + var/activate = text2num(href_list["activate"]) + switch (href_list["command"]) + if("idscan") + set_idscan(activate, 1) + if("main_power") + if(!main_power_lost_until) + src.loseMainPower() + if("backup_power") + if(!backup_power_lost_until) + src.loseBackupPower() + if("bolts") + if(src.isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) + to_chat(usr, "The door bolt control wire is cut - Door bolts permanently dropped.") + else if(activate && src.lock()) + to_chat(usr, "The door bolts have been dropped.") + else if(!activate && src.unlock()) + to_chat(usr, "The door bolts have been raised.") + if("electrify_temporary") + electrify(30 * activate, 1) + if("electrify_permanently") + electrify(-1 * activate, 1) + if("open") + if(src.welded) + to_chat(usr, text("The airlock has been welded shut!")) + else if(src.locked) + to_chat(usr, text("The door bolts are down!")) + else if(activate && density) + open() + if (isAI(usr)) + SSfeedback.IncrementSimpleStat("AI_DOOR") + else if(!activate && !density) + close() + if("safeties") + set_safeties(!activate, 1) + if("timing") + // Door speed control + if(src.isWireCut(AIRLOCK_WIRE_SPEED)) + to_chat(usr, text("The timing wire is cut - Cannot alter timing.")) + else if (activate && src.normalspeed) + normalspeed = 0 + else if (!activate && !src.normalspeed) + normalspeed = 1 + if("lights") + // Bolt lights + if(src.isWireCut(AIRLOCK_WIRE_LIGHT)) + to_chat(usr, "The bolt lights wire is cut - The door bolt lights are permanently disabled.") + else if (!activate && src.lights) + lights = 0 + to_chat(usr, "The door bolt lights have been disabled.") + else if (activate && !src.lights) + lights = 1 + to_chat(usr, "The door bolt lights have been enabled.") + update_icon() + return 1 + + + +/obj/machinery/door/airlock/proc/CreateAssembly() + 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 = 1 + if(mineral) + da.glass = mineral + 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/circuitboard/broken(src.loc) + operating = 0 + else + if (!electronics) create_electronics() + electronics.forceMove(src.loc) + electronics = null + qdel(src) + + +/obj/machinery/door/airlock/proc/CanChainsaw(var/obj/item/material/twohanded/chainsaw/ChainSawVar) + return (ChainSawVar.powered && density && hashatch) + +/obj/machinery/door/airlock/attackby(var/obj/item/C, mob/user as mob) + 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 (!repairing && (stat & BROKEN) && src.locked) //bolted and broken + if (!cut_bolts(C,user)) + ..() + return + if (istype(C, /obj/item/device/magnetic_lock)) + if (bracer) + to_chat(user, "There is already a [bracer] on [src]!") + return + var/obj/item/device/magnetic_lock/newbracer = C + newbracer.attachto(src, user) + return + if(!repairing && (C.iswelder() && !( src.operating > 0 ) && src.density)) + var/obj/item/weldingtool/WT = C + if(WT.isOn()) + user.visible_message( + "[user] begins welding [src] [welded ? "open" : "shut"].", + "You begin welding [src] [welded ? "open" : "shut"].", + "You hear a welding torch on metal." + ) + playsound(loc, 'sound/items/Welder.ogg', 50, 1) + if (!do_after(user, 2/C.toolspeed SECONDS, act_target = src, extra_checks = CALLBACK(src, .proc/is_open, src.density))) + return + if(!WT.remove_fuel(0,user)) + to_chat(user, "You need more welding fuel to complete this task.") + return + if(!src.welded) + src.welded = 1 + else + src.welded = null + src.update_icon() + return + else + return + else if(C.isscrewdriver()) + if (src.p_open) + if (stat & BROKEN) + to_chat(usr, "The panel is broken and cannot be closed.") + else + src.p_open = 0 + else + src.p_open = 1 + src.update_icon() + else if(C.iswirecutter()) + return src.attack_hand(user) + else if(C.ismultitool()) + 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/pai_cable)) // -- TLE + var/obj/item/pai_cable/cable = C + cable.plugin(src, user) + else if(!repairing && C.iscrowbar()) + if(istype(C, /obj/item/melee/arm_blade)) + if(!arePowerSystemsOn()) //if this check isn't done and empty, the armblade will never be used to hit the airlock + else if(!(stat & BROKEN)) + ..() + return + if(src.p_open && (operating < 0 || (!operating && welded && !src.arePowerSystemsOn() && density && !src.locked))) + playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 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!") + CreateAssembly() + 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) + open(1) + else + close(1) + else if(istype(C, /obj/item/material/twohanded/fireaxe) && !arePowerSystemsOn()) + if(locked && user.a_intent != I_HURT) + to_chat(user, "The airlock's bolts prevent it from being forced.") + else if(locked && user.a_intent == I_HURT) + ..() + else if(!welded && !operating) + if(density) + var/obj/item/material/twohanded/fireaxe/F = C + if(F.wielded) + open(1) + else + to_chat(user, "You need to be wielding \the [C] to do that.") + else + var/obj/item/material/twohanded/fireaxe/F = C + if(F.wielded) + close(1) + else + to_chat(user, "You need to be wielding \the [C] to do that.") + else if(istype(C, /obj/item/melee/hammer) && !arePowerSystemsOn()) + if(locked && user.a_intent != I_HURT) + to_chat(user, "The airlock's bolts prevent it from being forced.") + else if(locked && user.a_intent == I_HURT) + ..() + else if(!welded && !operating) + if(density) + open(1) + else + close(1) + else if(density && istype(C, /obj/item/material/twohanded/chainsaw)) + var/obj/item/material/twohanded/chainsaw/ChainSawVar = C + if(!ChainSawVar.wielded) + to_chat(user, "Cutting the airlock requires the strength of two hands.") + else if(ChainSawVar.cutting) + to_chat(user, "You are already cutting an airlock open.") + else if(!ChainSawVar.powered) + to_chat(user, "The [C] needs to be on in order to open this door.") + else if(bracer) //Has a magnetic lock + to_chat(user, "The bracer needs to be removed in order to cut through this door.") + else if(!arePowerSystemsOn()) + ChainSawVar.cutting = 1 + user.visible_message(\ + "[user.name] starts cutting the control pannel of the airlock with the [C]!",\ + "You start cutting the airlock control panel...",\ + "You hear a loud buzzing sound and metal grinding on metal..."\ + ) + if(do_after(user, ChainSawVar.opendelay SECONDS, act_target = user, extra_checks = CALLBACK(src, .proc/CanChainsaw, C))) + user.visible_message(\ + "[user.name] finishes cutting the control pannel of the airlock with the [C].",\ + "You finish cutting the airlock control panel.",\ + "You hear a metal clank and some sparks."\ + ) + set_broken() + sleep(1 SECONDS) + CreateAssembly() + ChainSawVar.cutting = 0 + take_damage(50) + else if(locked) + ChainSawVar.cutting = 1 + user.visible_message(\ + "[user.name] starts cutting below the airlock with the [C]!",\ + "You start cutting below the airlock...",\ + "You hear a loud buzzing sound and metal grinding on metal..."\ + ) + if(do_after(user, ChainSawVar.opendelay SECONDS, act_target = user, extra_checks = CALLBACK(src, .proc/CanChainsaw, C))) + user.visible_message(\ + "[user.name] finishes cutting below the airlock with the [C].",\ + "You finish cutting below the airlock.",\ + "You hear a metal clank and some sparks."\ + ) + unlock(1) + ChainSawVar.cutting = 0 + take_damage(50) + else + ChainSawVar.cutting = 1 + user.visible_message(\ + "[user.name] starts cutting between the airlock with the [C]!",\ + "You start cutting between the airlock...",\ + "You hear a loud buzzing sound and metal grinding on metal..."\ + ) + if(do_after(user, ChainSawVar.opendelay SECONDS, act_target = user, extra_checks = CALLBACK(src, .proc/CanChainsaw, C))) + user.visible_message(\ + "[user.name] finishes cutting between the airlock.",\ + "You finish cutting between the airlock.",\ + "You hear a metal clank and some sparks."\ + ) + open(1) + take_damage(50) + ChainSawVar.cutting = 0 + 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 = 1 + 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!") + + spark(src, 5, alldirs) + + 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 + if(arePowerSystemsOn()) + playsound(src.loc, open_sound_powered, 60, 1) + else + playsound(src.loc, open_sound_unpowered, 60, 1) + + 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() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR)) + return 0 + if (bracer) + do_animate("braced") + visible_message("[src]'s actuators whirr, but the door does not open.") + 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() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR)) + return 0 + return ..() + +/atom/movable/proc/blocks_airlock() + return density + +/obj/machinery/door/blocks_airlock() + return 0 + +/obj/structure/window/blocks_airlock() + return 0 + +/obj/machinery/mech_sensor/blocks_airlock() + return 0 + +/mob/living/blocks_airlock() + return mob_size > MOB_SMALL + +/atom/movable/proc/airlock_crush(var/crush_damage) + return 0 + +/obj/structure/window/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) + Stress(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) + . = ..() + for(var/i = 1, i <= AIRLOCK_CRUSH_DIVISOR, i++) + adjustBruteLoss(round(crush_damage / AIRLOCK_CRUSH_DIVISOR)) + SetStunned(5) + SetWeakened(5) + + var/turf/T = get_turf(src) + + var/list/valid_turfs = list() + for(var/dir_to_test in cardinal) + var/turf/new_turf = get_step(T, dir_to_test) + if(!new_turf.contains_dense_objects()) + valid_turfs |= new_turf + + while(valid_turfs.len) + T = pick(valid_turfs) + valid_turfs -= T + + if(src.forceMove(T)) + return + + +/mob/living/carbon/airlock_crush(var/crush_damage) + . = ..() + if (can_feel_pain()) + emote("scream") + +/mob/living/silicon/robot/airlock_crush(var/crush_damage) + return ..(round(crush_damage / CYBORG_AIRLOCKCRUSH_RESISTANCE)) + +/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()) + close_door_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 + if(arePowerSystemsOn()) + playsound(src.loc, close_sound_powered, 100, 1) + else + playsound(src.loc, close_sound_unpowered, 100, 1) + + ..() + +/obj/machinery/door/airlock/proc/lock(var/forced=0) + if(locked) + return 0 + if (operating && !forced) return 0 + if (bolt_cut_state == BOLTS_CUT) return 0 //what bolts? + src.locked = 1 + playsound(src, bolts_dropping, 30, 0, -6) + update_icon() + return 1 + +/obj/machinery/door/airlock/proc/unlock(var/forced=0) + if(!src.locked) + return + if (!forced) + if(operating || !src.arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) return + src.locked = 0 + playsound(src, bolts_rising, 30, 0, -6) + update_icon() + return 1 + +/obj/machinery/door/airlock/allowed(mob/M) + if(locked) + return 0 + return ..(M) + +/obj/machinery/door/airlock/Initialize(mapload, obj/structure/door_assembly/assembly = null) + var/on_admin_z = FALSE + //wires & hatch - this needs to be done up here so the hatch isn't generated by the parent Initialize(). + if(loc && isAdminLevel(z)) + on_admin_z = TRUE + hashatch = FALSE + + . = ..() + + //if assembly is given, create the new door from the assembly + if (istype(assembly)) + assembly_type = assembly.type + + electronics = assembly.electronics + electronics.forceMove(src) + + //update the door's access to match the electronics' + secured_wires = electronics.secure + if(electronics.one_access) + req_access = null + req_one_access = electronics.conf_access + else + req_one_access = null + req_access = 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) + + if (on_admin_z) + secured_wires = TRUE + + if (secured_wires) + wires = new/datum/wires/airlock/secure(src) + else + wires = new/datum/wires/airlock(src) + + if(mapload && src.closeOtherId != null) + for (var/obj/machinery/door/airlock/A in SSmachinery.processing_machines) + if(A.closeOtherId == src.closeOtherId && A != src) + src.closeOther = A + break + +/obj/machinery/door/airlock/Destroy() + qdel(wires) + wires = null + qdel(wifi_receiver) + wifi_receiver = 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/airlock_electronics/secure( src.loc ) + else + src.electronics = new/obj/item/airlock_electronics( src.loc ) + + //update the electronics to match the door's access + if(!src.req_access) + src.check_access() + if(LAZYLEN(req_access)) + electronics.conf_access = src.req_access + else if (LAZYLEN(req_one_access)) + electronics.conf_access = src.req_one_access + electronics.one_access = 1 + +/obj/machinery/door/airlock/emp_act(var/severity) + if(prob(40/severity)) + var/duration = SecondsToTicks(30 / severity) + if(electrified_until > -1 && (duration + world.time) > 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 + //if we lost power open 'er up + if(insecure) + INVOKE_ASYNC(src, /obj/machinery/door/.proc/open, 1) + securitylock = 1 + else if(securitylock) + INVOKE_ASYNC(src, /obj/machinery/door/.proc/close, 1) + securitylock = 0 + update_icon() + +/obj/machinery/door/airlock/proc/prison_open() + if(bracer) + return + + if(arePowerSystemsOn()) + src.unlock() + src.open() + src.lock() + return + +/obj/machinery/door/airlock/examine() + ..() + if (bolt_cut_state == BOLTS_EXPOSED) + to_chat(usr, "The bolt cover has been cut open.") + if (bolt_cut_state == BOLTS_CUT) + to_chat(usr, "The door bolts have been cut.") + if(bracer) + to_chat(usr, "\The [bracer] is installed on \the [src], preventing it from opening.") + to_chat(usr, bracer.health) + +#undef AIRLOCK_CRUSH_DIVISOR +#undef CYBORG_AIRLOCKCRUSH_RESISTANCE +#undef BOLTS_FINE +#undef BOLTS_EXPOSED +#undef BOLTS_CUT diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index 79cfd9379d2..99d4572ad7a 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -1,119 +1,119 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 -// Please do not use relative paths. - -/obj/item/airlock_electronics - name = "airlock electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - w_class = 2.0 //It should be tiny! -Agouri - - matter = list(DEFAULT_WALL_MATERIAL = 50,"glass" = 50) - - req_access = list(access_engine) - - 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/inuse = 0 // no double-spending - -/obj/item/airlock_electronics/attack_self(mob/user as mob) - if (!ishuman(user) && !istype(user,/mob/living/silicon/robot)) - return ..(user) - - var/mob/living/carbon/human/H = user - if(H.getBrainLoss() >= 60) - return - - var/t1 = text("Access control
    \n") - - - if (last_configurator) - t1 += "Operator: [last_configurator]
    " - - if (locked) - t1 += "Swipe ID
    " - else - t1 += "Block
    " - - t1 += "Access requirement is set to " - t1 += one_access ? "ONE
    " : "ALL
    " - - t1 += conf_access == null ? "All
    " : "All
    " - - t1 += "
    " - - var/list/accesses = get_all_station_access() - 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/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") - return - - if (href_list["login"]) - if(istype(usr,/mob/living/silicon)) - src.locked = 0 - src.last_configurator = usr.name - else - var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/device/pda)) - var/obj/item/device/pda/pda = I - I = pda.id - if (I && src.check_access(I)) - src.locked = 0 - src.last_configurator = I: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/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/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 +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 +// Please do not use relative paths. + +/obj/item/airlock_electronics + name = "airlock electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + w_class = 2.0 //It should be tiny! -Agouri + + matter = list(DEFAULT_WALL_MATERIAL = 50,"glass" = 50) + + req_access = list(access_engine) + + 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/inuse = 0 // no double-spending + +/obj/item/airlock_electronics/attack_self(mob/user as mob) + if (!ishuman(user) && !istype(user,/mob/living/silicon/robot)) + return ..(user) + + var/mob/living/carbon/human/H = user + if(H.getBrainLoss() >= 60) + return + + var/t1 = text("Access control
    \n") + + + if (last_configurator) + t1 += "Operator: [last_configurator]
    " + + if (locked) + t1 += "Swipe ID
    " + else + t1 += "Block
    " + + t1 += "Access requirement is set to " + t1 += one_access ? "ONE
    " : "ALL
    " + + t1 += conf_access == null ? "All
    " : "All
    " + + t1 += "
    " + + var/list/accesses = get_all_station_access() + 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/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") + return + + if (href_list["login"]) + if(istype(usr,/mob/living/silicon)) + src.locked = 0 + src.last_configurator = usr.name + else + var/obj/item/I = usr.get_active_hand() + if (istype(I, /obj/item/device/pda)) + var/obj/item/device/pda/pda = I + I = pda.id + if (I && src.check_access(I)) + src.locked = 0 + src.last_configurator = I: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/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/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 diff --git a/code/game/machinery/doors/alarmlock.dm b/code/game/machinery/doors/alarmlock.dm index 01741acfe0f..ca6ae3b9efd 100644 --- a/code/game/machinery/doors/alarmlock.dm +++ b/code/game/machinery/doors/alarmlock.dm @@ -1,42 +1,42 @@ -/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/Destroy() - if(SSradio) - SSradio.remove_object(src,air_frequency) - return ..() - -/obj/machinery/door/airlock/alarmlock/Initialize() - . = ..() - air_connection = new - SSradio.remove_object(src, air_frequency) - air_connection = SSradio.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/Destroy() + if(SSradio) + SSradio.remove_object(src,air_frequency) + return ..() + +/obj/machinery/door/airlock/alarmlock/Initialize() + . = ..() + air_connection = new + SSradio.remove_object(src, air_frequency) + air_connection = SSradio.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 4b2fef41e9f..ac9ccc984c3 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -1,438 +1,438 @@ -#define CHARS_PER_LINE 5 -#define FONT_SIZE "5pt" -#define FONT_COLOR "#09f" -#define FONT_STYLE "Arial Black" - -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -/////////////////////////////////////////////////////////////////////////////////////////////// -// 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" - desc = "A remote control for a door." - req_access = list(access_brig) - anchored = 1.0 // can't pick it up - density = 0 // can walk through it. - var/id = null // id of door it controls. - var/releasetime = 0 // when world.timeofday reaches it - release the prisoner - var/timing = 1 // boolean, true/1 timer is on, false/0 means it's not timing - var/picture_state // icon_state of alert picture, if not displaying text/numbers - var/list/obj/machinery/targets = list() - var/timetoset = 0 // Used to set releasetime upon starting the timer - - var/datum/crime_incident/incident - - var/menu_mode = "menu_timer" - - maptext_height = 26 - maptext_width = 32 - - var/datum/browser/menu = new( null, "brig_timer", "Brig Timer", 400, 300 ) - -/obj/machinery/door_timer/Initialize() - ..() - - return INITIALIZE_HINT_LATELOAD - -/obj/machinery/door_timer/LateInitialize() - for(var/obj/machinery/door/window/brigdoor/M in SSmachinery.all_machines) - if (M.id == src.id) - targets += M - - for(var/obj/machinery/flasher/F in SSmachinery.all_machines) - if(F.id == src.id) - targets += F - - for(var/obj/structure/closet/secure_closet/brig/C in brig_closets) - if(C.id == src.id) - targets += C - - if(targets.len==0) - stat |= BROKEN - update_icon() - - -//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/machinery_process() - if(stat & (NOPOWER|BROKEN)) return - - if(src.timing) - // poorly done midnight rollover - // (no seriously there's gotta be a better way to do this) - var/timeleft = timeleft() - if(timeleft > 1e5) - src.releasetime = 0 - - if(world.timeofday > src.releasetime) - if( src.timer_end() )// open doors, reset timer, clear status screen - var/message = "Criminal sentence complete. The criminal is free to go." - ping( "\The [src] pings, \"[message]\"" ) - - updateUsrDialog() - update_icon() - - return - -// has the door power situation changed, if so update icon. -/obj/machinery/door_timer/power_change() - ..() - update_icon() - return - -// 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 - - // Set releasetime - releasetime = world.timeofday + timetoset - - for(var/obj/machinery/door/window/brigdoor/door in targets) - if(door.density) continue - spawn(0) - door.close() - - for(var/obj/structure/closet/secure_closet/brig/C in targets) - if(C.broken) continue - if(C.opened && !C.close()) continue - C.locked = 1 - C.icon_state = C.icon_locked - - timing = 1 - - return 1 - - -// Opens and unlocks doors, power check -/obj/machinery/door_timer/proc/timer_end(var/early=0) - if(stat & (NOPOWER|BROKEN)) return 0 - - timing = 0 - - // Reset releasetime - releasetime = 0 - timeset( 0 ) - - for(var/obj/machinery/door/window/brigdoor/door in targets) - if(!door.density) continue - spawn(0) - door.open() - - for(var/obj/structure/closet/secure_closet/brig/C in targets) - if(C.broken) continue - if(C.opened) continue - C.locked = 0 - C.icon_state = C.icon_closed - - if(istype(incident)) - var/datum/record/general/R = SSrecords.find_record("name", incident.criminal.name) - if(istype(R) && istype(R.security)) - if(early == 1) - R.security.criminal = "Parolled" - else - R.security.criminal = "Released" - - qdel( incident ) - incident = null - - src.updateUsrDialog() - - return 1 - - -// Check for releasetime timeleft -/obj/machinery/door_timer/proc/timeleft() - var/timeleft = timetoset - - if( src.timing ) - timeleft = releasetime - world.timeofday - - . = round( timeleft/10 ) - if(. < 0) - . = 0 - -// Set timetoset -/obj/machinery/door_timer/proc/timeset(var/seconds) - timetoset = seconds * 10 - - if(timetoset <= 0) - timetoset = 0 - - return - -//Allows AIs to use door_timer, see human attack_hand function below -/obj/machinery/door_timer/attack_ai(var/mob/user as mob) - return src.attack_hand(user) - -// Allows humans to use door_timer -// Opens dialog window when someone clicks on door timer -// Flasher activation limited to 150 seconds -/obj/machinery/door_timer/attack_hand(var/mob/user as mob) - if(..()) - return - - user.set_machine( src ) - - . = "" - - switch( menu_mode ) - if( "menu_charges" ) - . = menu_charges() - else - . = menu_timer( user ) - - menu.set_user( user ) - menu.set_content( . ) - menu.open() - - onclose(user, "brig_timer") - - return - -/obj/machinery/door_timer/proc/menu_timer( var/mob/user as mob ) - // Used for the 'time left' display - var/second = round(timeleft() % 60) - var/minute = round((timeleft() - second) / 60) - . = "

    Timer System:

    " - . += "Controls [src.id]
    " - - if( !incident ) - . += "Insert a Securty Incident Report to load a criminal sentence
    " - else - // Time Left display (uses releasetime) - var/obj/item/card/id/card = incident.card.resolve() - . += "Criminal: [card]\t" - . += "Charges
    " - . += "Sentence: [add_zero( "[minute]", 2 )]:[add_zero( "[second]", 2 )]\t" - // Start/Stop timer - if( !src.timing ) - . += "Activate
    " - else - . += "Early Release
    " - . += "
    " - - - // Mounted flash controls - for(var/obj/machinery/flasher/F in targets) - . += "
    Flash: " - if(F.last_flash && (F.last_flash + 150) > world.time) - . += "Charging" - else - . += "Activate" - - . += "

    " - . += "
    Close
    " - - return . - -/obj/machinery/door_timer/proc/menu_charges() - . = "" - - if( !incident ) - . += "Insert a Securty Incident Report to load a criminal sentence
    " - else - // Charges list - . += "" - . += "" - . += "" - . += "" - for( var/datum/law/L in incident.charges ) - . += "" - . += "" - . += "" - . += "" - . += "" - . += "
    Charges
    [L.name][L.desc][L.min_brig_time] - [L.max_brig_time] minutes
    " - - . += "

    " - . += "
    Return
    " - - return . - -/obj/machinery/door_timer/attackby(obj/item/O as obj, var/mob/user as mob) - if( istype( O, /obj/item/paper/incident )) - if( !incident ) - if( import( O, user )) - ping( "\The [src] pings, \"Successfully imported incident report!\"" ) - user.drop_from_inventory(O,get_turf(src)) - qdel(O) - src.updateUsrDialog() - else - to_chat(user, "\The [src] buzzes, \"There's already an active sentence!\"") - return - else if( istype( O, /obj/item/paper )) - to_chat(user, "\The [src] buzzes, \"This console only accepts authentic incident reports. Copies are invalid.\"") - return - - ..() - -/obj/machinery/door_timer/proc/import( var/obj/item/paper/incident/I, var/user ) - if( !istype( I )) - to_chat(user, "\The [src] buzzes, \"Could not import the incident report.\"") - return 0 - - if( !istype( I.incident )) - to_chat(user, "\The [src] buzzes, \"Report has no incident encoded!\"") - return 0 - - if( !I.sentence ) - to_chat(user, "\The [src] buzzes, \"Report does not contain a guilty sentence!\"") - return 0 - - var/datum/crime_incident/crime = I.incident - - if( !istype( crime.criminal )) - to_chat(user, "\The [src] buzzes, \"Report has no criminal encoded!\"") - return 0 - - if( !crime.brig_sentence ) - to_chat(user, "\The [src] buzzes, \"Report had no brig sentence.\"") - return 0 - - if( crime.brig_sentence >= PERMABRIG_SENTENCE ) - to_chat(user, "\The [src] buzzes, \"The criminal has a HUT sentence and needs to be detained until transfer.\"") - return 0 - - var/addtime = timetoset - addtime += crime.brig_sentence MINUTES - addtime = min(max(round(addtime), 0), PERMABRIG_SENTENCE MINUTES) - addtime = addtime/10 - - timeset(addtime) - incident = crime - - return timetoset - -//Function for using door_timer dialog input, checks if user has permission -// href_list to -// "timing" turns on timer -// "tp" value to modify timer -// "fc" activates flasher -// "change" resets the timer to the timetoset amount while the timer is counting down -// Also updates dialog window and timer icon -/obj/machinery/door_timer/Topic(href, href_list) - if(..()) - return - if(!src.allowed(usr)) - return - - usr.set_machine(src) - - switch(href_list["button"]) - if( "menu_mode" ) - menu_mode = href_list["menu_choice"] - - if( "activate" ) - src.timer_start() - var/datum/record/general/R = SSrecords.find_record("name", incident.criminal.name) - if(R && R.security) - R.security.criminal = "Incarcerated" - - if ("early_release") - src.timer_end(1) - - if( "flash" ) - for(var/obj/machinery/flasher/F in targets) - F.flash() - - src.add_fingerprint(usr) - src.updateUsrDialog() - src.update_icon() - - return - - -//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(src.timing) - var/disp1 = id - var/timeleft = timeleft() - var/disp2 = "[add_zero(num2text((timeleft / 60) % 60),2)]~[add_zero(num2text(timeleft % 60), 2)]" - 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(var/state) - picture_state = state - cut_overlays() - add_overlay(picture_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(var/line1, var/line2) - var/new_text = {"
    [line1]
    [line2]
    "} - if(maptext != new_text) - maptext = new_text - - -//Actual string input to icon display for loop, with 5 pixel x offsets for each letter. -//Stolen from status_display -/obj/machinery/door_timer/proc/texticon(var/tn, var/px = 0, var/py = 0) - var/image/I = image('icons/obj/status_display.dmi', "blank") - var/len = lentext(tn) - - for(var/d = 1 to len) - var/char = copytext(tn, len-d+1, len-d+2) - if(char == " ") - continue - var/image/ID = image('icons/obj/status_display.dmi', icon_state=char) - ID.pixel_x = -(d-1)*5 + px - ID.pixel_y = py - I.add_overlay(ID) - return I - - -/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" - -#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 "Arial Black" + +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +/////////////////////////////////////////////////////////////////////////////////////////////// +// 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" + desc = "A remote control for a door." + req_access = list(access_brig) + anchored = 1.0 // can't pick it up + density = 0 // can walk through it. + var/id = null // id of door it controls. + var/releasetime = 0 // when world.timeofday reaches it - release the prisoner + var/timing = 1 // boolean, true/1 timer is on, false/0 means it's not timing + var/picture_state // icon_state of alert picture, if not displaying text/numbers + var/list/obj/machinery/targets = list() + var/timetoset = 0 // Used to set releasetime upon starting the timer + + var/datum/crime_incident/incident + + var/menu_mode = "menu_timer" + + maptext_height = 26 + maptext_width = 32 + + var/datum/browser/menu = new( null, "brig_timer", "Brig Timer", 400, 300 ) + +/obj/machinery/door_timer/Initialize() + ..() + + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/door_timer/LateInitialize() + for(var/obj/machinery/door/window/brigdoor/M in SSmachinery.all_machines) + if (M.id == src.id) + targets += M + + for(var/obj/machinery/flasher/F in SSmachinery.all_machines) + if(F.id == src.id) + targets += F + + for(var/obj/structure/closet/secure_closet/brig/C in brig_closets) + if(C.id == src.id) + targets += C + + if(targets.len==0) + stat |= BROKEN + update_icon() + + +//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/machinery_process() + if(stat & (NOPOWER|BROKEN)) return + + if(src.timing) + // poorly done midnight rollover + // (no seriously there's gotta be a better way to do this) + var/timeleft = timeleft() + if(timeleft > 1e5) + src.releasetime = 0 + + if(world.timeofday > src.releasetime) + if( src.timer_end() )// open doors, reset timer, clear status screen + var/message = "Criminal sentence complete. The criminal is free to go." + ping( "\The [src] pings, \"[message]\"" ) + + updateUsrDialog() + update_icon() + + return + +// has the door power situation changed, if so update icon. +/obj/machinery/door_timer/power_change() + ..() + update_icon() + return + +// 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 + + // Set releasetime + releasetime = world.timeofday + timetoset + + for(var/obj/machinery/door/window/brigdoor/door in targets) + if(door.density) continue + spawn(0) + door.close() + + for(var/obj/structure/closet/secure_closet/brig/C in targets) + if(C.broken) continue + if(C.opened && !C.close()) continue + C.locked = 1 + C.icon_state = C.icon_locked + + timing = 1 + + return 1 + + +// Opens and unlocks doors, power check +/obj/machinery/door_timer/proc/timer_end(var/early=0) + if(stat & (NOPOWER|BROKEN)) return 0 + + timing = 0 + + // Reset releasetime + releasetime = 0 + timeset( 0 ) + + for(var/obj/machinery/door/window/brigdoor/door in targets) + if(!door.density) continue + spawn(0) + door.open() + + for(var/obj/structure/closet/secure_closet/brig/C in targets) + if(C.broken) continue + if(C.opened) continue + C.locked = 0 + C.icon_state = C.icon_closed + + if(istype(incident)) + var/datum/record/general/R = SSrecords.find_record("name", incident.criminal.name) + if(istype(R) && istype(R.security)) + if(early == 1) + R.security.criminal = "Parolled" + else + R.security.criminal = "Released" + + qdel( incident ) + incident = null + + src.updateUsrDialog() + + return 1 + + +// Check for releasetime timeleft +/obj/machinery/door_timer/proc/timeleft() + var/timeleft = timetoset + + if( src.timing ) + timeleft = releasetime - world.timeofday + + . = round( timeleft/10 ) + if(. < 0) + . = 0 + +// Set timetoset +/obj/machinery/door_timer/proc/timeset(var/seconds) + timetoset = seconds * 10 + + if(timetoset <= 0) + timetoset = 0 + + return + +//Allows AIs to use door_timer, see human attack_hand function below +/obj/machinery/door_timer/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +// Allows humans to use door_timer +// Opens dialog window when someone clicks on door timer +// Flasher activation limited to 150 seconds +/obj/machinery/door_timer/attack_hand(var/mob/user as mob) + if(..()) + return + + user.set_machine( src ) + + . = "" + + switch( menu_mode ) + if( "menu_charges" ) + . = menu_charges() + else + . = menu_timer( user ) + + menu.set_user( user ) + menu.set_content( . ) + menu.open() + + onclose(user, "brig_timer") + + return + +/obj/machinery/door_timer/proc/menu_timer( var/mob/user as mob ) + // Used for the 'time left' display + var/second = round(timeleft() % 60) + var/minute = round((timeleft() - second) / 60) + . = "

    Timer System:

    " + . += "Controls [src.id]
    " + + if( !incident ) + . += "Insert a Securty Incident Report to load a criminal sentence
    " + else + // Time Left display (uses releasetime) + var/obj/item/card/id/card = incident.card.resolve() + . += "Criminal: [card]\t" + . += "Charges
    " + . += "Sentence: [add_zero( "[minute]", 2 )]:[add_zero( "[second]", 2 )]\t" + // Start/Stop timer + if( !src.timing ) + . += "Activate
    " + else + . += "Early Release
    " + . += "
    " + + + // Mounted flash controls + for(var/obj/machinery/flasher/F in targets) + . += "
    Flash: " + if(F.last_flash && (F.last_flash + 150) > world.time) + . += "Charging" + else + . += "Activate" + + . += "

    " + . += "
    Close
    " + + return . + +/obj/machinery/door_timer/proc/menu_charges() + . = "" + + if( !incident ) + . += "Insert a Securty Incident Report to load a criminal sentence
    " + else + // Charges list + . += "" + . += "" + . += "" + . += "" + for( var/datum/law/L in incident.charges ) + . += "" + . += "" + . += "" + . += "" + . += "" + . += "
    Charges
    [L.name][L.desc][L.min_brig_time] - [L.max_brig_time] minutes
    " + + . += "

    " + . += "
    Return
    " + + return . + +/obj/machinery/door_timer/attackby(obj/item/O as obj, var/mob/user as mob) + if( istype( O, /obj/item/paper/incident )) + if( !incident ) + if( import( O, user )) + ping( "\The [src] pings, \"Successfully imported incident report!\"" ) + user.drop_from_inventory(O,get_turf(src)) + qdel(O) + src.updateUsrDialog() + else + to_chat(user, "\The [src] buzzes, \"There's already an active sentence!\"") + return + else if( istype( O, /obj/item/paper )) + to_chat(user, "\The [src] buzzes, \"This console only accepts authentic incident reports. Copies are invalid.\"") + return + + ..() + +/obj/machinery/door_timer/proc/import( var/obj/item/paper/incident/I, var/user ) + if( !istype( I )) + to_chat(user, "\The [src] buzzes, \"Could not import the incident report.\"") + return 0 + + if( !istype( I.incident )) + to_chat(user, "\The [src] buzzes, \"Report has no incident encoded!\"") + return 0 + + if( !I.sentence ) + to_chat(user, "\The [src] buzzes, \"Report does not contain a guilty sentence!\"") + return 0 + + var/datum/crime_incident/crime = I.incident + + if( !istype( crime.criminal )) + to_chat(user, "\The [src] buzzes, \"Report has no criminal encoded!\"") + return 0 + + if( !crime.brig_sentence ) + to_chat(user, "\The [src] buzzes, \"Report had no brig sentence.\"") + return 0 + + if( crime.brig_sentence >= PERMABRIG_SENTENCE ) + to_chat(user, "\The [src] buzzes, \"The criminal has a HUT sentence and needs to be detained until transfer.\"") + return 0 + + var/addtime = timetoset + addtime += crime.brig_sentence MINUTES + addtime = min(max(round(addtime), 0), PERMABRIG_SENTENCE MINUTES) + addtime = addtime/10 + + timeset(addtime) + incident = crime + + return timetoset + +//Function for using door_timer dialog input, checks if user has permission +// href_list to +// "timing" turns on timer +// "tp" value to modify timer +// "fc" activates flasher +// "change" resets the timer to the timetoset amount while the timer is counting down +// Also updates dialog window and timer icon +/obj/machinery/door_timer/Topic(href, href_list) + if(..()) + return + if(!src.allowed(usr)) + return + + usr.set_machine(src) + + switch(href_list["button"]) + if( "menu_mode" ) + menu_mode = href_list["menu_choice"] + + if( "activate" ) + src.timer_start() + var/datum/record/general/R = SSrecords.find_record("name", incident.criminal.name) + if(R && R.security) + R.security.criminal = "Incarcerated" + + if ("early_release") + src.timer_end(1) + + if( "flash" ) + for(var/obj/machinery/flasher/F in targets) + F.flash() + + src.add_fingerprint(usr) + src.updateUsrDialog() + src.update_icon() + + return + + +//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(src.timing) + var/disp1 = id + var/timeleft = timeleft() + var/disp2 = "[add_zero(num2text((timeleft / 60) % 60),2)]~[add_zero(num2text(timeleft % 60), 2)]" + 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(var/state) + picture_state = state + cut_overlays() + add_overlay(picture_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(var/line1, var/line2) + var/new_text = {"
    [line1]
    [line2]
    "} + if(maptext != new_text) + maptext = new_text + + +//Actual string input to icon display for loop, with 5 pixel x offsets for each letter. +//Stolen from status_display +/obj/machinery/door_timer/proc/texticon(var/tn, var/px = 0, var/py = 0) + var/image/I = image('icons/obj/status_display.dmi', "blank") + var/len = length(tn) + + for(var/d = 1 to len) + var/char = copytext(tn, len-d+1, len-d+2) + if(char == " ") + continue + var/image/ID = image('icons/obj/status_display.dmi', icon_state=char) + ID.pixel_x = -(d-1)*5 + px + ID.pixel_y = py + I.add_overlay(ID) + return I + + +/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" + +#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 9005a7fda49..f7ad56cb134 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 fucking retarded +/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 fucking retarded return 1 \ No newline at end of file diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 2a431c8e228..1b5e40a8fc8 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -1,576 +1,576 @@ -//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 = 1 - opacity = 1 - density = 1 - layer = DOOR_OPEN_LAYER - 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/hitsound_light = 'sound/effects/Glasshit.ogg'//Sound door makes when hit very gently - var/obj/item/stack/material/steel/repairing - var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened. - var/open_duration = 150//How long it stays open - - var/hashatch = 0//If 1, this door has hatches, and certain small creatures can move through them without opening the door - var/hatchstate = 0//0: closed, 1: open - var/hatchstyle = "1x1" - var/hatch_offset_x = 0 - var/hatch_offset_y = 0 - var/hatch_colour = "#FFFFFF" - var/hatch_open_sound = 'sound/machines/hatch_open.ogg' - var/hatch_close_sound = 'sound/machines/hatch_close.ogg' - - var/image/hatch_image - - //Multi-tile doors - dir = SOUTH - var/width = 1 - - // turf animation - var/atom/movable/overlay/c_animation = null - - atmos_canpass = CANPASS_PROC - -/obj/machinery/door/attack_generic(var/mob/user, var/damage) - if(damage >= 10) - visible_message("\The [user] smashes into the [src]!") - playsound(src.loc, hitsound, 60, 1) - take_damage(damage) - else - visible_message("\The [user] bonks \the [src] harmlessly.") - playsound(src.loc, hitsound_light, 8, 1, -1) - user.do_attack_animation(src) - -/obj/machinery/door/Initialize() - . = ..() - 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_nearby_tiles(need_rebuild=1) - if (hashatch) - setup_hatch() - -/obj/machinery/door/proc/setup_hatch() - hatch_image = image('icons/obj/doors/hatches.dmi', src, hatchstyle, closed_layer+0.1) - hatch_image.color = hatch_colour - hatch_image.pixel_x = hatch_offset_x - hatch_image.pixel_y = hatch_offset_y - hatch_image.dir = dir - - update_icon() - -/obj/machinery/door/proc/open_hatch(var/atom/mover = null) - if (!hatchstate) - hatchstate = 1 - update_icon() - playsound(src.loc, hatch_open_sound, 40, 1, -1) - - - close_hatch_in(29) - - if (istype(mover, /mob/living)) - var/mob/living/S = mover - S.under_door() - - -/obj/machinery/door/proc/close_hatch() - hatchstate = 0//hatch stays open for 3 seconds - update_icon() - playsound(src.loc, hatch_close_sound, 30, 1, -1) - -/obj/machinery/door/Destroy() - density = 0 - update_nearby_tiles() - - return ..() - -/obj/machinery/door/proc/close_door_in(var/time = 5 SECONDS) - addtimer(CALLBACK(src, .proc/close), time, TIMER_UNIQUE | TIMER_OVERRIDE) - -/obj/machinery/door/proc/close_hatch_in(var/time = 3 SECONDS) - addtimer(CALLBACK(src, .proc/close_hatch), time, TIMER_UNIQUE | TIMER_OVERRIDE) - -/obj/machinery/door/proc/can_open() - if(!density || operating || !ROUND_IS_STARTED) - return 0 - return 1 - -/obj/machinery/door/proc/can_close() - if(density || operating || !ROUND_IS_STARTED) - return 0 - return 1 - -/obj/machinery/door/CollidedWith(atom/AM) - . = ..() - if(p_open || operating) return - if (!AM.simulated) 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() && (!issmall(M) || ishuman(M))) - bumpopen(M) - return - - if(istype(AM, /obj/machinery/bot)) - var/obj/machinery/bot/bot = AM - if(src.check_access(bot.botcard)) - if(density) - open() - return - - 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/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") - return - if(istype(AM, /obj/structure/janitorialcart)) - var/obj/structure/janitorialcart/cart = AM - if(density) - if(cart.pulling && (src.allowed(cart.pulling))) - open() - else - do_animate("deny") - return - return - - -/obj/machinery/door/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(air_group) return !block_air_zones - if (istype(mover)) - if(mover.checkpass(PASSGLASS)) - return !opacity - if(density && hashatch && mover.checkpass(PASSDOORHATCH)) - if (istype(mover, /mob/living/silicon/pai)) - var/mob/living/silicon/pai/P = mover - if (allowed(P)) - open_hatch(mover) - return 1 - else - open_hatch(mover) - return 1//If this door is closed, but it has hatches, and this creature can go through hatches. Then we let it through without opening - return !density - - -/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) - - if (tforce > 0) - var/volume = 100 - if (tforce < 20)//No more stupidly loud banging sound from throwing a piece of paper at a door - volume *= (tforce / 20) - playsound(src.loc, hitsound, volume, 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) - 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/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, /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/transfer - if (repairing) - transfer = stack.transfer_to(repairing, amount_needed - repairing.amount) - if (!transfer) - to_chat(user, "You must weld or remove \the [repairing] from \the [src] before you can add anything else.") - else - repairing = stack.split(amount_needed) - if (repairing) - repairing.forceMove(src) - transfer = repairing.amount - - if (transfer) - to_chat(user, "You fit [transfer] [stack.singular_name]\s to damaged and broken parts on \the [src].") - - return - - if(repairing && I.iswelder()) - if(!density) - to_chat(user, "\The [src] must be closed before you can repair it.") - return - - var/obj/item/weldingtool/welder = I - if(welder.remove_fuel(0,user)) - to_chat(user, "You start to fix dents and weld \the [repairing] into place.") - playsound(src, 'sound/items/Welder.ogg', 100, 1) - if(do_after(user, 5 * repairing.amount) && welder && welder.isOn()) - to_chat(user, "You finish repairing the damage to \the [src].") - health = between(health, health + repairing.amount*DOOR_REPAIR_AMOUNT, maxhealth) - update_icon() - qdel(repairing) - repairing = null - return - - if(repairing && I.iscrowbar()) - to_chat(user, "You remove \the [repairing].") - playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) - repairing.forceMove(user.loc) - repairing = null - 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) && user.a_intent == I_HURT && !istype(I, /obj/item/card)) - var/obj/item/W = I - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - 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.loc, 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/proc/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 < src.maxhealth / 4) - to_chat(user, "\The [src] looks like it's about to break!") - else if(src.health < src.maxhealth / 2) - to_chat(user, "\The [src] looks seriously damaged!") - else if(src.health < src.maxhealth * 3/4) - to_chat(user, "\The [src] 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)) ) - open() - ..() - - -/obj/machinery/door/ex_act(severity) - var/bolted = 0 - if (istype(src, /obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/A = src - bolted = A.locked - switch(severity) - if(1.0) - if((!bolted) || prob(80)) - qdel(src) - else - var/damage = rand(300,600) - if (bolted) - damage *= 0.8 //Bolted doors are a bit tougher - take_damage(damage) - if(2.0) - if((!bolted && prob(25)) || prob(20)) - qdel(src) - else - var/damage = rand(150,300) - if (bolted) - damage *= 0.8 //Bolted doors are a bit tougher - take_damage(damage) - if(3.0) - if(prob(80)) - spark(src, 2, alldirs) - var/damage = rand(100,150) - if (bolted) - damage *= 0.8 - take_damage(damage) - - if (health <= 0) - qdel(src) - return - - -/obj/machinery/door/update_icon() - if(density) - icon_state = "door1" - else - icon_state = "door0" - 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.loc, '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(3) - src.density = 0 - update_nearby_tiles() - sleep(7) - src.layer = open_layer - explosion_resistance = 0 - update_icon() - set_opacity(0) - operating = 0 - - if(autoclose) - close_door_in(next_close_time()) - - return 1 - -/obj/machinery/door/proc/next_close_time() - return (normalspeed ? open_duration : 5) - -/obj/machinery/door/proc/autoclose() - if (!QDELETED(src) && can_close(FALSE) && autoclose) - close() - -/obj/machinery/door/proc/close(var/forced = 0) - if(!can_close(forced)) - if (autoclose) - for (var/atom/movable/M in get_turf(src)) - if (M.density && M != src) - addtimer(CALLBACK(src, .proc/autoclose), 60) - operating = 1 - - do_animate("closing") - sleep(3) - src.density = 1 - explosion_resistance = initial(explosion_resistance) - src.layer = closed_layer - update_nearby_tiles() - sleep(7) - 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 - -/obj/machinery/door/proc/requiresID() - return 1 - -/obj/machinery/door/allowed(mob/M) - if(!requiresID()) - return 1 // Door doesn't require an ID. So obviously they're allowed. - return ..(M) - -/obj/machinery/door/update_nearby_tiles(need_rebuild) - for(var/turf/T in locs) - if (istype(T, /turf/simulated)) - var/turf/simulated/turf = T - update_heat_protection(turf) - SSair.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/proc/is_open(var/invert=0) - if(invert) - return src.density - return !src.density - -/obj/machinery/door/Move(new_loc, new_dir) - //update_nearby_tiles() - . = ..() - 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 = 1 + opacity = 1 + density = 1 + layer = DOOR_OPEN_LAYER + 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/hitsound_light = 'sound/effects/Glasshit.ogg'//Sound door makes when hit very gently + var/obj/item/stack/material/steel/repairing + var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened. + var/open_duration = 150//How long it stays open + + var/hashatch = 0//If 1, this door has hatches, and certain small creatures can move through them without opening the door + var/hatchstate = 0//0: closed, 1: open + var/hatchstyle = "1x1" + var/hatch_offset_x = 0 + var/hatch_offset_y = 0 + var/hatch_colour = "#FFFFFF" + var/hatch_open_sound = 'sound/machines/hatch_open.ogg' + var/hatch_close_sound = 'sound/machines/hatch_close.ogg' + + var/image/hatch_image + + //Multi-tile doors + dir = SOUTH + var/width = 1 + + // turf animation + var/atom/movable/overlay/c_animation = null + + atmos_canpass = CANPASS_PROC + +/obj/machinery/door/attack_generic(var/mob/user, var/damage) + if(damage >= 10) + visible_message("\The [user] smashes into the [src]!") + playsound(src.loc, hitsound, 60, 1) + take_damage(damage) + else + visible_message("\The [user] bonks \the [src] harmlessly.") + playsound(src.loc, hitsound_light, 8, 1, -1) + user.do_attack_animation(src) + +/obj/machinery/door/Initialize() + . = ..() + 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_nearby_tiles(need_rebuild=1) + if (hashatch) + setup_hatch() + +/obj/machinery/door/proc/setup_hatch() + hatch_image = image('icons/obj/doors/hatches.dmi', src, hatchstyle, closed_layer+0.1) + hatch_image.color = hatch_colour + hatch_image.pixel_x = hatch_offset_x + hatch_image.pixel_y = hatch_offset_y + hatch_image.dir = dir + + update_icon() + +/obj/machinery/door/proc/open_hatch(var/atom/mover = null) + if (!hatchstate) + hatchstate = 1 + update_icon() + playsound(src.loc, hatch_open_sound, 40, 1, -1) + + + close_hatch_in(29) + + if (istype(mover, /mob/living)) + var/mob/living/S = mover + S.under_door() + + +/obj/machinery/door/proc/close_hatch() + hatchstate = 0//hatch stays open for 3 seconds + update_icon() + playsound(src.loc, hatch_close_sound, 30, 1, -1) + +/obj/machinery/door/Destroy() + density = 0 + update_nearby_tiles() + + return ..() + +/obj/machinery/door/proc/close_door_in(var/time = 5 SECONDS) + addtimer(CALLBACK(src, .proc/close), time, TIMER_UNIQUE | TIMER_OVERRIDE) + +/obj/machinery/door/proc/close_hatch_in(var/time = 3 SECONDS) + addtimer(CALLBACK(src, .proc/close_hatch), time, TIMER_UNIQUE | TIMER_OVERRIDE) + +/obj/machinery/door/proc/can_open() + if(!density || operating || !ROUND_IS_STARTED) + return 0 + return 1 + +/obj/machinery/door/proc/can_close() + if(density || operating || !ROUND_IS_STARTED) + return 0 + return 1 + +/obj/machinery/door/CollidedWith(atom/AM) + . = ..() + if(p_open || operating) return + if (!AM.simulated) 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() && (!issmall(M) || ishuman(M))) + bumpopen(M) + return + + if(istype(AM, /obj/machinery/bot)) + var/obj/machinery/bot/bot = AM + if(src.check_access(bot.botcard)) + if(density) + open() + return + + 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/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") + return + if(istype(AM, /obj/structure/janitorialcart)) + var/obj/structure/janitorialcart/cart = AM + if(density) + if(cart.pulling && (src.allowed(cart.pulling))) + open() + else + do_animate("deny") + return + return + + +/obj/machinery/door/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(air_group) return !block_air_zones + if (istype(mover)) + if(mover.checkpass(PASSGLASS)) + return !opacity + if(density && hashatch && mover.checkpass(PASSDOORHATCH)) + if (istype(mover, /mob/living/silicon/pai)) + var/mob/living/silicon/pai/P = mover + if (allowed(P)) + open_hatch(mover) + return 1 + else + open_hatch(mover) + return 1//If this door is closed, but it has hatches, and this creature can go through hatches. Then we let it through without opening + return !density + + +/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) + + if (tforce > 0) + var/volume = 100 + if (tforce < 20)//No more stupidly loud banging sound from throwing a piece of paper at a door + volume *= (tforce / 20) + playsound(src.loc, hitsound, volume, 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) + 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/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, /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/transfer + if (repairing) + transfer = stack.transfer_to(repairing, amount_needed - repairing.amount) + if (!transfer) + to_chat(user, "You must weld or remove \the [repairing] from \the [src] before you can add anything else.") + else + repairing = stack.split(amount_needed) + if (repairing) + repairing.forceMove(src) + transfer = repairing.amount + + if (transfer) + to_chat(user, "You fit [transfer] [stack.singular_name]\s to damaged and broken parts on \the [src].") + + return + + if(repairing && I.iswelder()) + if(!density) + to_chat(user, "\The [src] must be closed before you can repair it.") + return + + var/obj/item/weldingtool/welder = I + if(welder.remove_fuel(0,user)) + to_chat(user, "You start to fix dents and weld \the [repairing] into place.") + playsound(src, 'sound/items/Welder.ogg', 100, 1) + if(do_after(user, 5 * repairing.amount) && welder && welder.isOn()) + to_chat(user, "You finish repairing the damage to \the [src].") + health = between(health, health + repairing.amount*DOOR_REPAIR_AMOUNT, maxhealth) + update_icon() + qdel(repairing) + repairing = null + return + + if(repairing && I.iscrowbar()) + to_chat(user, "You remove \the [repairing].") + playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) + repairing.forceMove(user.loc) + repairing = null + 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) && user.a_intent == I_HURT && !istype(I, /obj/item/card)) + var/obj/item/W = I + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + 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.loc, 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/proc/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 < src.maxhealth / 4) + to_chat(user, "\The [src] looks like it's about to break!") + else if(src.health < src.maxhealth / 2) + to_chat(user, "\The [src] looks seriously damaged!") + else if(src.health < src.maxhealth * 3/4) + to_chat(user, "\The [src] 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)) ) + open() + ..() + + +/obj/machinery/door/ex_act(severity) + var/bolted = 0 + if (istype(src, /obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/A = src + bolted = A.locked + switch(severity) + if(1.0) + if((!bolted) || prob(80)) + qdel(src) + else + var/damage = rand(300,600) + if (bolted) + damage *= 0.8 //Bolted doors are a bit tougher + take_damage(damage) + if(2.0) + if((!bolted && prob(25)) || prob(20)) + qdel(src) + else + var/damage = rand(150,300) + if (bolted) + damage *= 0.8 //Bolted doors are a bit tougher + take_damage(damage) + if(3.0) + if(prob(80)) + spark(src, 2, alldirs) + var/damage = rand(100,150) + if (bolted) + damage *= 0.8 + take_damage(damage) + + if (health <= 0) + qdel(src) + return + + +/obj/machinery/door/update_icon() + if(density) + icon_state = "door1" + else + icon_state = "door0" + 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.loc, '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(3) + src.density = 0 + update_nearby_tiles() + sleep(7) + src.layer = open_layer + explosion_resistance = 0 + update_icon() + set_opacity(0) + operating = 0 + + if(autoclose) + close_door_in(next_close_time()) + + return 1 + +/obj/machinery/door/proc/next_close_time() + return (normalspeed ? open_duration : 5) + +/obj/machinery/door/proc/autoclose() + if (!QDELETED(src) && can_close(FALSE) && autoclose) + close() + +/obj/machinery/door/proc/close(var/forced = 0) + if(!can_close(forced)) + if (autoclose) + for (var/atom/movable/M in get_turf(src)) + if (M.density && M != src) + addtimer(CALLBACK(src, .proc/autoclose), 60) + operating = 1 + + do_animate("closing") + sleep(3) + src.density = 1 + explosion_resistance = initial(explosion_resistance) + src.layer = closed_layer + update_nearby_tiles() + sleep(7) + 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 + +/obj/machinery/door/proc/requiresID() + return 1 + +/obj/machinery/door/allowed(mob/M) + if(!requiresID()) + return 1 // Door doesn't require an ID. So obviously they're allowed. + return ..(M) + +/obj/machinery/door/update_nearby_tiles(need_rebuild) + for(var/turf/T in locs) + if (istype(T, /turf/simulated)) + var/turf/simulated/turf = T + update_heat_protection(turf) + SSair.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/proc/is_open(var/invert=0) + if(invert) + return src.density + return !src.density + +/obj/machinery/door/Move(new_loc, new_dir) + //update_nearby_tiles() + . = ..() + 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/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 785afd333e8..5071d83d710 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -1,528 +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_atmospherics, access_engine_equip, access_paramedic) - opacity = 0 - density = 0 - layer = DOOR_OPEN_LAYER - 0.01 - open_layer = DOOR_OPEN_LAYER - 0.01 // Just below doors when open - closed_layer = DOOR_CLOSED_LAYER + 0.2 // 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 - hashatch = 1 - hatch_colour = "#f7d003" - - var/blocked = 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 = 1 - idle_power_usage = 5 - dir = SOUTH - - var/enable_smart_generation = TRUE - - 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" - ) - - var/door_directions = 0 - var/noair_directions = 0 - var/diffarea_directions = 0 - -/obj/machinery/door/firedoor/Initialize(var/mapload) - . = ..() - for(var/obj/machinery/door/firedoor/F in loc) - if(F != src) - crash_with("Duplicate firedoors at [x]-[y]-[z]. Deleting one.") - QDEL_IN(src, 1) - return . - var/area/A = get_area(src) - ASSERT(istype(A)) - - A.all_doors.Add(src) - areas_added = list(A) - - if(!mapload) - enable_smart_generation = 0 - - for(var/direction in cardinal) - - var/turf/T = get_step(src,direction) - - if(enable_smart_generation) - if(locate(src.type) in T) - door_directions |= direction - - if(T.oxygen <= 0) - noair_directions |= direction - - if(get_area(src.loc) != get_area(T)) - diffarea_directions |= direction - - A = get_area(T) - if(istype(A) && !(A in areas_added)) - A.all_doors.Add(src) - areas_added += A - - if(enable_smart_generation) - - var/turf/T = get_turf(src) - if(locate(/obj/structure/grille,T)) - hashatch = 0 - - if(locate(/obj/machinery/door/airlock,T)) - dir = SOUTH - else - if(door_directions & (EAST | WEST)) - if(noair_directions & NORTH) - dir = SOUTH - else if(noair_directions & SOUTH) - dir = NORTH - else if(diffarea_directions & NORTH) - dir = NORTH - else if(diffarea_directions & SOUTH) - dir = SOUTH - else if(door_directions & (NORTH | SOUTH) ) - if(noair_directions & EAST) - dir = WEST - else if(noair_directions & WEST) - dir = EAST - else if(diffarea_directions & EAST) - dir = EAST - else if(diffarea_directions & WEST) - dir = WEST - - if (!density) - cut_overlay(hatch_image) // Parent call adds this, but we don't want it just yet. - -/obj/machinery/door/firedoor/Destroy() - for(var/area/A in areas_added) - A.all_doors.Remove(src) - . = ..() - -/obj/machinery/door/firedoor/attack_generic(var/mob/user, var/damage) - if(stat & (BROKEN|NOPOWER)) - if(damage >= 10) - 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/firedoor/get_material() - return get_material_by_name(DEFAULT_WALL_MATERIAL) - -/obj/machinery/door/firedoor/examine(mob/user) - . = ..(user, 1) - if(!. || !density) - return - - if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) - to_chat(user, "WARNING: Current pressure differential is [pdiff]kPa! Opening door may result in injury!") - - to_chat(user, "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" - to_chat(user, o) - continue - var/celsius = convert_k2c(tile_info[index][1]) - var/pressure = tile_info[index][2] - o += "" - o += "[celsius]°C " - o += "" - o += "[pressure]kPa
  • " - to_chat(user, 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]]" - to_chat(user, "These people have opened \the [src] during an alert: [users_to_open_string].") - -/obj/machinery/door/firedoor/CollidedWith(atom/AM) - if(p_open || operating) - return - if(!density) - return ..() - return 0 - -/obj/machinery/door/firedoor/attack_hand(mob/user as mob) - add_fingerprint(user) - if(operating) - return//Already doing something. - - 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.fire || A.air_doors_activated) - alarmed = 1 - 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(ishuman(user)) - var/mob/living/carbon/human/H = user - - if(H.species.can_shred(H)) - - if(src.density) - visible_message("\The [H] forces \the [src] open!") - open(1) - else - visible_message("\The [H] forces \the [src] closed!") - close(1) - 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) - - open() - else - close() - - if(needs_to_close) - addtimer(CALLBACK(src, .proc/do_close), 50) - -/obj/machinery/door/firedoor/proc/do_close() - var/alarmed = FALSE - for (var/thing in areas_added) - var/area/A = thing - if (A.fire || A.air_doors_activated) - alarmed = TRUE - break - - if (alarmed) - nextstate = FIREDOOR_CLOSED - close() - -/obj/machinery/door/firedoor/attackby(obj/item/C as obj, mob/user as mob) - add_fingerprint(user) - if(operating) - return//Already doing something. - if(C.iswelder() && !repairing) - var/obj/item/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, 'sound/items/Welder.ogg', 100, 1) - update_icon() - return - - if(density && C.isscrewdriver()) - hatch_open = !hatch_open - user.visible_message("[user] has [hatch_open ? "opened" : "closed"] \the [src] maintenance panel.", - "You have [hatch_open ? "opened" : "closed"] the [src] maintenance panel.") - update_icon() - return - - if(blocked && C.iscrowbar() && !repairing) - if(!hatch_open) - to_chat(user, "You must open the maintenance panel 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/C.toolspeed)) - if(blocked && density && hatch_open) - playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 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/circuitboard/broken(src.loc) - else - new/obj/item/airalarm_electronics(src.loc) - - var/obj/structure/firedoor_assembly/FA = new/obj/structure/firedoor_assembly(src.loc) - FA.anchored = 1 - FA.density = 1 - FA.wired = 1 - FA.update_icon() - qdel(src) - return - - if(blocked) - to_chat(user, "\The [src] is welded shut!") - return - - if(C.iscrowbar() || istype(C,/obj/item/material/twohanded/fireaxe) || (istype(C, /obj/item/melee/hammer))) - if(operating) - return - - if(blocked && C.iscrowbar()) - 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/material/twohanded/fireaxe)) - var/obj/item/material/twohanded/fireaxe/F = C - if(!F.wielded) - 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.") - if(do_after(user,30/C.toolspeed)) - if(C.iscrowbar() || (istype(C, /obj/item/melee/hammer))) - 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) - open(1, user) - else - close() - return - - return ..() - -// CHECK PRESSURE -/obj/machinery/door/firedoor/machinery_process() - ..() - - if(density && 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) - pdiff = QUANTIZE(pdiff) // Hello, sanity. - 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() - cut_overlays() - latetoggle() - return ..() - -/obj/machinery/door/firedoor/open(forced = 0, user = usr) - cut_overlays() - if(hatch_open) - hatch_open = 0 - visible_message("The maintenance panel 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 - log_and_message_admins("has forced open an emergency shutter.", user, loc) - latetoggle() - return ..() - -/obj/machinery/door/firedoor/do_animate(animation) - compile_overlays() - switch(animation) - if("opening") - flick("door_opening", src) - if("closing") - flick("door_closing", src) - -/obj/machinery/door/firedoor/update_icon() - cut_overlays() - set_light(0) - var/do_set_light = 0 - - if(density) - icon_state = "door_closed" - if(hatch_open) - add_overlay("hatch") - if(blocked) - add_overlay("welded") - if(pdiff_alert) - add_overlay("palert") - do_set_light = 1 - if(dir_alerts) - for (var/d = 1; d <= 4; d++) - //1 = NORTH - //2 = SOUTH - //3 = EAST - //4 = WEST - var/cdir = cardinal[d] - if (!dir_alerts[d]) - continue - if (dir_alerts[d] & FIREDOOR_ALERT_COLD) - add_overlay("alert_cold_[cdir]") - if (dir_alerts[d] & FIREDOOR_ALERT_HOT) - add_overlay("alert_hot_[cdir]") - - do_set_light = TRUE - - if (hashatch) - if (hatchstate) - hatch_image.icon_state = "[hatchstyle]_open" - else - hatch_image.icon_state = hatchstyle - add_overlay(hatch_image) - else - icon_state = "door_open" - if(blocked) - add_overlay("welded_open") - - if(do_set_light) - set_light(2, 0.5, COLOR_SUN) - -//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, height=0, air_group=0) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) //Make sure looking at appropriate border - if(air_group) return 0 - 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(!SSair) return 0 - - var/turf/simulated/source = loc - var/turf/simulated/destination = get_step(source,dir) - - update_heat_protection(loc) - - if(istype(source)) SSair.tiles_to_update += source - if(istype(destination)) SSair.tiles_to_update += destination - return 1 -*/ - -/obj/machinery/door/firedoor/multi_tile - icon = 'icons/obj/doors/DoorHazard2x1.dmi' - width = 2 - dir = EAST +#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_atmospherics, access_engine_equip, access_paramedic) + opacity = 0 + density = 0 + layer = DOOR_OPEN_LAYER - 0.01 + open_layer = DOOR_OPEN_LAYER - 0.01 // Just below doors when open + closed_layer = DOOR_CLOSED_LAYER + 0.2 // 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 + hashatch = 1 + hatch_colour = "#f7d003" + + var/blocked = 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 = 1 + idle_power_usage = 5 + dir = SOUTH + + var/enable_smart_generation = TRUE + + 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" + ) + + var/door_directions = 0 + var/noair_directions = 0 + var/diffarea_directions = 0 + +/obj/machinery/door/firedoor/Initialize(var/mapload) + . = ..() + for(var/obj/machinery/door/firedoor/F in loc) + if(F != src) + crash_with("Duplicate firedoors at [x]-[y]-[z]. Deleting one.") + QDEL_IN(src, 1) + return . + var/area/A = get_area(src) + ASSERT(istype(A)) + + A.all_doors.Add(src) + areas_added = list(A) + + if(!mapload) + enable_smart_generation = 0 + + for(var/direction in cardinal) + + var/turf/T = get_step(src,direction) + + if(enable_smart_generation) + if(locate(src.type) in T) + door_directions |= direction + + if(T.oxygen <= 0) + noair_directions |= direction + + if(get_area(src.loc) != get_area(T)) + diffarea_directions |= direction + + A = get_area(T) + if(istype(A) && !(A in areas_added)) + A.all_doors.Add(src) + areas_added += A + + if(enable_smart_generation) + + var/turf/T = get_turf(src) + if(locate(/obj/structure/grille,T)) + hashatch = 0 + + if(locate(/obj/machinery/door/airlock,T)) + dir = SOUTH + else + if(door_directions & (EAST | WEST)) + if(noair_directions & NORTH) + dir = SOUTH + else if(noair_directions & SOUTH) + dir = NORTH + else if(diffarea_directions & NORTH) + dir = NORTH + else if(diffarea_directions & SOUTH) + dir = SOUTH + else if(door_directions & (NORTH | SOUTH) ) + if(noair_directions & EAST) + dir = WEST + else if(noair_directions & WEST) + dir = EAST + else if(diffarea_directions & EAST) + dir = EAST + else if(diffarea_directions & WEST) + dir = WEST + + if (!density) + cut_overlay(hatch_image) // Parent call adds this, but we don't want it just yet. + +/obj/machinery/door/firedoor/Destroy() + for(var/area/A in areas_added) + A.all_doors.Remove(src) + . = ..() + +/obj/machinery/door/firedoor/attack_generic(var/mob/user, var/damage) + if(stat & (BROKEN|NOPOWER)) + if(damage >= 10) + 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/firedoor/get_material() + return get_material_by_name(DEFAULT_WALL_MATERIAL) + +/obj/machinery/door/firedoor/examine(mob/user) + . = ..(user, 1) + if(!. || !density) + return + + if(pdiff >= FIREDOOR_MAX_PRESSURE_DIFF) + to_chat(user, "WARNING: Current pressure differential is [pdiff]kPa! Opening door may result in injury!") + + to_chat(user, "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" + to_chat(user, o) + continue + var/celsius = convert_k2c(tile_info[index][1]) + var/pressure = tile_info[index][2] + o += "" + o += "[celsius]°C " + o += "" + o += "[pressure]kPa" + to_chat(user, 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]]" + to_chat(user, "These people have opened \the [src] during an alert: [users_to_open_string].") + +/obj/machinery/door/firedoor/CollidedWith(atom/AM) + if(p_open || operating) + return + if(!density) + return ..() + return 0 + +/obj/machinery/door/firedoor/attack_hand(mob/user as mob) + add_fingerprint(user) + if(operating) + return//Already doing something. + + 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.fire || A.air_doors_activated) + alarmed = 1 + 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(ishuman(user)) + var/mob/living/carbon/human/H = user + + if(H.species.can_shred(H)) + + if(src.density) + visible_message("\The [H] forces \the [src] open!") + open(1) + else + visible_message("\The [H] forces \the [src] closed!") + close(1) + 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) + + open() + else + close() + + if(needs_to_close) + addtimer(CALLBACK(src, .proc/do_close), 50) + +/obj/machinery/door/firedoor/proc/do_close() + var/alarmed = FALSE + for (var/thing in areas_added) + var/area/A = thing + if (A.fire || A.air_doors_activated) + alarmed = TRUE + break + + if (alarmed) + nextstate = FIREDOOR_CLOSED + close() + +/obj/machinery/door/firedoor/attackby(obj/item/C as obj, mob/user as mob) + add_fingerprint(user) + if(operating) + return//Already doing something. + if(C.iswelder() && !repairing) + var/obj/item/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, 'sound/items/Welder.ogg', 100, 1) + update_icon() + return + + if(density && C.isscrewdriver()) + hatch_open = !hatch_open + user.visible_message("[user] has [hatch_open ? "opened" : "closed"] \the [src] maintenance panel.", + "You have [hatch_open ? "opened" : "closed"] the [src] maintenance panel.") + update_icon() + return + + if(blocked && C.iscrowbar() && !repairing) + if(!hatch_open) + to_chat(user, "You must open the maintenance panel 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/C.toolspeed)) + if(blocked && density && hatch_open) + playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 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/circuitboard/broken(src.loc) + else + new/obj/item/airalarm_electronics(src.loc) + + var/obj/structure/firedoor_assembly/FA = new/obj/structure/firedoor_assembly(src.loc) + FA.anchored = 1 + FA.density = 1 + FA.wired = 1 + FA.update_icon() + qdel(src) + return + + if(blocked) + to_chat(user, "\The [src] is welded shut!") + return + + if(C.iscrowbar() || istype(C,/obj/item/material/twohanded/fireaxe) || (istype(C, /obj/item/melee/hammer))) + if(operating) + return + + if(blocked && C.iscrowbar()) + 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/material/twohanded/fireaxe)) + var/obj/item/material/twohanded/fireaxe/F = C + if(!F.wielded) + 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.") + if(do_after(user,30/C.toolspeed)) + if(C.iscrowbar() || (istype(C, /obj/item/melee/hammer))) + 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) + open(1, user) + else + close() + return + + return ..() + +// CHECK PRESSURE +/obj/machinery/door/firedoor/machinery_process() + ..() + + if(density && 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) + pdiff = QUANTIZE(pdiff) // Hello, sanity. + 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() + cut_overlays() + latetoggle() + return ..() + +/obj/machinery/door/firedoor/open(forced = 0, user = usr) + cut_overlays() + if(hatch_open) + hatch_open = 0 + visible_message("The maintenance panel 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 + log_and_message_admins("has forced open an emergency shutter.", user, loc) + latetoggle() + return ..() + +/obj/machinery/door/firedoor/do_animate(animation) + compile_overlays() + switch(animation) + if("opening") + flick("door_opening", src) + if("closing") + flick("door_closing", src) + +/obj/machinery/door/firedoor/update_icon() + cut_overlays() + set_light(0) + var/do_set_light = 0 + + if(density) + icon_state = "door_closed" + if(hatch_open) + add_overlay("hatch") + if(blocked) + add_overlay("welded") + if(pdiff_alert) + add_overlay("palert") + do_set_light = 1 + if(dir_alerts) + for (var/d = 1; d <= 4; d++) + //1 = NORTH + //2 = SOUTH + //3 = EAST + //4 = WEST + var/cdir = cardinal[d] + if (!dir_alerts[d]) + continue + if (dir_alerts[d] & FIREDOOR_ALERT_COLD) + add_overlay("alert_cold_[cdir]") + if (dir_alerts[d] & FIREDOOR_ALERT_HOT) + add_overlay("alert_hot_[cdir]") + + do_set_light = TRUE + + if (hashatch) + if (hatchstate) + hatch_image.icon_state = "[hatchstyle]_open" + else + hatch_image.icon_state = hatchstyle + add_overlay(hatch_image) + else + icon_state = "door_open" + if(blocked) + add_overlay("welded_open") + + if(do_set_light) + set_light(2, 0.5, COLOR_SUN) + +//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, height=0, air_group=0) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) //Make sure looking at appropriate border + if(air_group) return 0 + 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(!SSair) return 0 + + var/turf/simulated/source = loc + var/turf/simulated/destination = get_step(source,dir) + + update_heat_protection(loc) + + if(istype(source)) SSair.tiles_to_update += source + if(istype(destination)) SSair.tiles_to_update += destination + return 1 +*/ + +/obj/machinery/door/firedoor/multi_tile + icon = 'icons/obj/doors/DoorHazard2x1.dmi' + width = 2 + dir = EAST enable_smart_generation = FALSE \ No newline at end of file diff --git a/code/game/machinery/doors/unpowered.dm b/code/game/machinery/doors/unpowered.dm index 847808c570e..de60a632539 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/CollidedWith(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/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.dmi' - name = "door" - icon_state = "door1" - opacity = 1 - density = 1 +/obj/machinery/door/unpowered + autoclose = 0 + var/locked = 0 + +/obj/machinery/door/unpowered/CollidedWith(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/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.dmi' + name = "door" + icon_state = "door1" + opacity = 1 + density = 1 diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 1b21400da52..8e48b3e8fb6 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -1,308 +1,308 @@ -/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 = 0 - flags = ON_BORDER - opacity = 0 - var/obj/item/airlock_electronics/electronics = null - explosion_resistance = 5 - air_properties_vary_with_direction = 1 - - atmos_canpass = CANPASS_PROC - -/obj/machinery/door/window/Initialize(mapload) - . = ..() - if (!mapload) - update_nearby_tiles() - - if (LAZYLEN(req_access)) - icon_state = "[icon_state]" - base_state = icon_state - -/obj/machinery/door/window/proc/shatter(var/display_message = 1) - new /obj/item/material/shard(src.loc) - var/obj/item/stack/cable_coil/CC = new /obj/item/stack/cable_coil(loc) - CC.amount = 2 - var/obj/item/airlock_electronics/ae - if(!electronics) - ae = new/obj/item/airlock_electronics(loc) - if(LAZYLEN(req_access)) - ae.conf_access = src.req_access - else if (LAZYLEN(req_one_access)) - ae.conf_access = src.req_one_access - ae.one_access = 1 - else - ae = electronics - electronics = null - ae.forceMove(src.loc) - if(operating == -1) - ae.icon_state = "door_electronics_smoked" - operating = 0 - src.density = 0 - playsound(src, "shatter", 70, 1) - if(display_message) - visible_message("[src] shatters!") - qdel(src) - -/obj/machinery/door/window/Destroy() - density = 0 - update_nearby_tiles() - return ..() - -/obj/machinery/door/window/CollidedWith(atom/movable/AM as mob|obj) - if (istype(AM, /obj)) - var/mob/living/bot/bot = AM - if(istype(bot)) - if(density && src.check_access(bot.botcard)) - open() - addtimer(CALLBACK(src, .proc/close), 50) - return - if (!( ROUND_IS_STARTED )) - return - if (src.operating) - return - if (src.density && (!issmall(AM) || ishuman(AM)) && src.allowed(AM)) - open() - //secure doors close faster - var/time = check_access(null) ? 50 : 20 - addtimer(CALLBACK(src, .proc/close), time) - -/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) //Make sure looking at appropriate border - if(air_group) return 0 - return !density - else - return 1 - -/obj/machinery/door/window/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 - -/obj/machinery/door/window/open() - if (src.operating == 1) //doors can still open when emag-disabled - return 0 - if (!ROUND_IS_STARTED) - return 0 - if(!src.operating) //in case of emag - src.operating = 1 - flick("[base_state]opening", src) - playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1) - icon_state = "[base_state]open" - sleep(10) - - explosion_resistance = 0 - src.density = 0 - update_nearby_tiles() - - if(operating == 1) //emag again - src.operating = 0 - return 1 - -/obj/machinery/door/window/close() - if (src.operating) - return 0 - src.operating = 1 - flick("[base_state]closing", src) - playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1) - src.icon_state = src.base_state - - src.density = 1 - explosion_resistance = initial(explosion_resistance) - update_nearby_tiles() - - sleep(10) - - src.operating = 0 - return 1 - -/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) - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species.can_shred(H)) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) - user.visible_message("[user] smashes against [src].", "You smash against [src]!") - take_damage(25) - return - return src.attackby(user, user) - -/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 - - //Emags and ninja swords? You may pass. - if (istype(I, /obj/item/melee/energy/blade)) - if(emag_act(10, user)) - spark(src.loc, 5) - playsound(src.loc, "sparks", 50, 1) - playsound(src.loc, 'sound/weapons/blade.ogg', 50, 1) - visible_message("The glass door was sliced open by [user]!") - return 1 - - //If it's emagged, crowbar can pry electronics out. - if (src.operating == -1 && I.iscrowbar()) - playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) - user.visible_message("[user] removes the electronics from the windoor.", "You start to remove electronics from the windoor.") - if (do_after(user,60/I.toolspeed)) - to_chat(user, "You removed the windoor electronics!") - - var/obj/structure/windoor_assembly/wa = new/obj/structure/windoor_assembly(src.loc) - if (istype(src, /obj/machinery/door/window/brigdoor)) - wa.secure = "secure_" - wa.name = "Secure Wired Windoor Assembly" - else - wa.name = "Wired Windoor Assembly" - if (src.base_state == "right" || src.base_state == "rightsecure") - wa.facing = "r" - wa.set_dir(src.dir) - wa.state = "02" - wa.update_icon() - - var/obj/item/airlock_electronics/ae - if(!electronics) - ae = new/obj/item/airlock_electronics( src.loc ) - if(LAZYLEN(req_access)) - ae.conf_access = src.req_access - else if (LAZYLEN(req_one_access)) - ae.conf_access = src.req_one_access - ae.one_access = 1 - else - ae = electronics - electronics = null - ae.forceMove(src.loc) - ae.icon_state = "door_electronics_smoked" - - operating = 0 - shatter(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) && !istype(I, /obj/item/card)) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - var/aforce = I.force - playsound(src.loc, '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("[base_state]deny", src) - -/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/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 = 0 + flags = ON_BORDER + opacity = 0 + var/obj/item/airlock_electronics/electronics = null + explosion_resistance = 5 + air_properties_vary_with_direction = 1 + + atmos_canpass = CANPASS_PROC + +/obj/machinery/door/window/Initialize(mapload) + . = ..() + if (!mapload) + update_nearby_tiles() + + if (LAZYLEN(req_access)) + icon_state = "[icon_state]" + base_state = icon_state + +/obj/machinery/door/window/proc/shatter(var/display_message = 1) + new /obj/item/material/shard(src.loc) + var/obj/item/stack/cable_coil/CC = new /obj/item/stack/cable_coil(loc) + CC.amount = 2 + var/obj/item/airlock_electronics/ae + if(!electronics) + ae = new/obj/item/airlock_electronics(loc) + if(LAZYLEN(req_access)) + ae.conf_access = src.req_access + else if (LAZYLEN(req_one_access)) + ae.conf_access = src.req_one_access + ae.one_access = 1 + else + ae = electronics + electronics = null + ae.forceMove(src.loc) + if(operating == -1) + ae.icon_state = "door_electronics_smoked" + operating = 0 + src.density = 0 + playsound(src, "shatter", 70, 1) + if(display_message) + visible_message("[src] shatters!") + qdel(src) + +/obj/machinery/door/window/Destroy() + density = 0 + update_nearby_tiles() + return ..() + +/obj/machinery/door/window/CollidedWith(atom/movable/AM as mob|obj) + if (istype(AM, /obj)) + var/mob/living/bot/bot = AM + if(istype(bot)) + if(density && src.check_access(bot.botcard)) + open() + addtimer(CALLBACK(src, .proc/close), 50) + return + if (!( ROUND_IS_STARTED )) + return + if (src.operating) + return + if (src.density && (!issmall(AM) || ishuman(AM)) && src.allowed(AM)) + open() + //secure doors close faster + var/time = check_access(null) ? 50 : 20 + addtimer(CALLBACK(src, .proc/close), time) + +/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) //Make sure looking at appropriate border + if(air_group) return 0 + return !density + else + return 1 + +/obj/machinery/door/window/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 + +/obj/machinery/door/window/open() + if (src.operating == 1) //doors can still open when emag-disabled + return 0 + if (!ROUND_IS_STARTED) + return 0 + if(!src.operating) //in case of emag + src.operating = 1 + flick("[base_state]opening", src) + playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1) + icon_state = "[base_state]open" + sleep(10) + + explosion_resistance = 0 + src.density = 0 + update_nearby_tiles() + + if(operating == 1) //emag again + src.operating = 0 + return 1 + +/obj/machinery/door/window/close() + if (src.operating) + return 0 + src.operating = 1 + flick("[base_state]closing", src) + playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1) + src.icon_state = src.base_state + + src.density = 1 + explosion_resistance = initial(explosion_resistance) + update_nearby_tiles() + + sleep(10) + + src.operating = 0 + return 1 + +/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) + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.species.can_shred(H)) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) + user.visible_message("[user] smashes against [src].", "You smash against [src]!") + take_damage(25) + return + return src.attackby(user, user) + +/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 + + //Emags and ninja swords? You may pass. + if (istype(I, /obj/item/melee/energy/blade)) + if(emag_act(10, user)) + spark(src.loc, 5) + playsound(src.loc, "sparks", 50, 1) + playsound(src.loc, 'sound/weapons/blade.ogg', 50, 1) + visible_message("The glass door was sliced open by [user]!") + return 1 + + //If it's emagged, crowbar can pry electronics out. + if (src.operating == -1 && I.iscrowbar()) + playsound(src.loc, 'sound/items/Crowbar.ogg', 100, 1) + user.visible_message("[user] removes the electronics from the windoor.", "You start to remove electronics from the windoor.") + if (do_after(user,60/I.toolspeed)) + to_chat(user, "You removed the windoor electronics!") + + var/obj/structure/windoor_assembly/wa = new/obj/structure/windoor_assembly(src.loc) + if (istype(src, /obj/machinery/door/window/brigdoor)) + wa.secure = "secure_" + wa.name = "Secure Wired Windoor Assembly" + else + wa.name = "Wired Windoor Assembly" + if (src.base_state == "right" || src.base_state == "rightsecure") + wa.facing = "r" + wa.set_dir(src.dir) + wa.state = "02" + wa.update_icon() + + var/obj/item/airlock_electronics/ae + if(!electronics) + ae = new/obj/item/airlock_electronics( src.loc ) + if(LAZYLEN(req_access)) + ae.conf_access = src.req_access + else if (LAZYLEN(req_one_access)) + ae.conf_access = src.req_one_access + ae.one_access = 1 + else + ae = electronics + electronics = null + ae.forceMove(src.loc) + ae.icon_state = "door_electronics_smoked" + + operating = 0 + shatter(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) && !istype(I, /obj/item/card)) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + var/aforce = I.force + playsound(src.loc, '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("[base_state]deny", src) + +/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/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 0f072710a42..51823ba7e72 100644 --- a/code/game/machinery/doppler_array.dm +++ b/code/game/machinery/doppler_array.dm @@ -1,56 +1,56 @@ -var/list/doppler_arrays = list() - -/obj/machinery/doppler_array - name = "tachyon-doppler array" - 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." - icon = 'icons/obj/computer.dmi' - icon_state = "old_cons" - - anchored = 1 - density = 1 - - -/obj/machinery/doppler_array/Initialize() - . = ..() - doppler_arrays += src - -/obj/machinery/doppler_array/Destroy() - doppler_arrays -= src - return ..() - -/obj/machinery/doppler_array/proc/sense_explosion(var/x0,var/y0,var/z0,var/devastation_range,var/heavy_impact_range,var/light_impact_range) - 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],[z0]). Epicenter radius: [devastation_range]. Outer radius: [heavy_impact_range]. Shockwave radius: [light_impact_range]." - global_announcer.autosay(message, src.name) - -/obj/machinery/doppler_array/power_change() - ..() - if(stat & BROKEN) - icon_state = "[initial(icon_state)]-broken" - else - if( !(stat & NOPOWER) ) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]-off" +var/list/doppler_arrays = list() + +/obj/machinery/doppler_array + name = "tachyon-doppler array" + 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." + icon = 'icons/obj/computer.dmi' + icon_state = "old_cons" + + anchored = 1 + density = 1 + + +/obj/machinery/doppler_array/Initialize() + . = ..() + doppler_arrays += src + +/obj/machinery/doppler_array/Destroy() + doppler_arrays -= src + return ..() + +/obj/machinery/doppler_array/proc/sense_explosion(var/x0,var/y0,var/z0,var/devastation_range,var/heavy_impact_range,var/light_impact_range) + 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],[z0]). Epicenter radius: [devastation_range]. Outer radius: [heavy_impact_range]. Shockwave radius: [light_impact_range]." + global_announcer.autosay(message, src.name) + +/obj/machinery/doppler_array/power_change() + ..() + if(stat & BROKEN) + icon_state = "[initial(icon_state)]-broken" + else + if( !(stat & NOPOWER) ) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]-off" diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index ef418d5cdb8..c37e6c7c78d 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -1,84 +1,84 @@ -/obj/machinery/embedded_controller - var/datum/computer/file/embedded_program/program //the currently executing program - - name = "Embedded Controller" - anchored = 1 - - use_power = 1 - idle_power_usage = 10 - - var/on = 1 - -obj/machinery/embedded_controller/radio/Destroy() - if(SSradio) - SSradio.remove_object(src,frequency) - 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) - //spawn(5) program.process() //no, program.process sends some signals and machines respond and we here again and we lag -rastaf0 - -/obj/machinery/embedded_controller/machinery_process() - if(program) - program.process() - - update_icon() - -/obj/machinery/embedded_controller/attack_ai(mob/user as mob) - src.ui_interact(user) - -/obj/machinery/embedded_controller/attack_hand(mob/user as mob) - - if(!user.IsAdvancedToolUser()) - return 0 - - src.ui_interact(user) - -/obj/machinery/embedded_controller/ui_interact() - return - -/obj/machinery/embedded_controller/radio - icon = 'icons/obj/airlock_machines.dmi' - icon_state = "airlock_control_standby" - power_channel = ENVIRON - density = 0 - - 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 - unacidable = 1 - -/obj/machinery/embedded_controller/radio/Initialize() - . = ..() - set_frequency(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/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, filter) - else - qdel(signal) - -/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, radio_filter) +/obj/machinery/embedded_controller + var/datum/computer/file/embedded_program/program //the currently executing program + + name = "Embedded Controller" + anchored = 1 + + use_power = 1 + idle_power_usage = 10 + + var/on = 1 + +obj/machinery/embedded_controller/radio/Destroy() + if(SSradio) + SSradio.remove_object(src,frequency) + 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) + //spawn(5) program.process() //no, program.process sends some signals and machines respond and we here again and we lag -rastaf0 + +/obj/machinery/embedded_controller/machinery_process() + if(program) + program.process() + + update_icon() + +/obj/machinery/embedded_controller/attack_ai(mob/user as mob) + src.ui_interact(user) + +/obj/machinery/embedded_controller/attack_hand(mob/user as mob) + + if(!user.IsAdvancedToolUser()) + return 0 + + src.ui_interact(user) + +/obj/machinery/embedded_controller/ui_interact() + return + +/obj/machinery/embedded_controller/radio + icon = 'icons/obj/airlock_machines.dmi' + icon_state = "airlock_control_standby" + power_channel = ENVIRON + density = 0 + + 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 + unacidable = 1 + +/obj/machinery/embedded_controller/radio/Initialize() + . = ..() + set_frequency(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/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, filter) + else + qdel(signal) + +/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, radio_filter) diff --git a/code/game/machinery/embedded_controller/embedded_program_base.dm b/code/game/machinery/embedded_controller/embedded_program_base.dm index a8be295ccd1..2a75090ea5c 100644 --- a/code/game/machinery/embedded_controller/embedded_program_base.dm +++ b/code/game/machinery/embedded_controller/embedded_program_base.dm @@ -1,27 +1,27 @@ - -/datum/computer/file/embedded_program - var/list/memory = list() - var/obj/machinery/embedded_controller/master - - var/id_tag - -/datum/computer/file/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/computer/file/embedded_program/proc/receive_user_command(command) - return - -/datum/computer/file/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param) - return - -/datum/computer/file/embedded_program/process() - return - -/datum/computer/file/embedded_program/proc/post_signal(datum/signal/signal, comm_line) - if(master) - master.post_signal(signal, comm_line) - else - qdel(signal) + +/datum/computer/file/embedded_program + var/list/memory = list() + var/obj/machinery/embedded_controller/master + + var/id_tag + +/datum/computer/file/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/computer/file/embedded_program/proc/receive_user_command(command) + return + +/datum/computer/file/embedded_program/proc/receive_signal(datum/signal/signal, receive_method, receive_param) + return + +/datum/computer/file/embedded_program/process() + return + +/datum/computer/file/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/flasher.dm b/code/game/machinery/flasher.dm index 83f2a4240bf..53e65417057 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -1,154 +1,154 @@ -// 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" - 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 = 1 - use_power = 1 - idle_power_usage = 2 - flags = PROXMOVE - var/_wifi_id - var/datum/wifi/receiver/button/flasher/wifi_receiver - -/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 = 0 - base_state = "pflash" - density = 1 - -/obj/machinery/flasher/Initialize() - . = ..() - if(_wifi_id) - wifi_receiver = new(_wifi_id, src) - -/obj/machinery/flasher/Destroy() - qdel(wifi_receiver) - wifi_receiver = null - return ..() - -/obj/machinery/flasher/power_change() - ..() - if ( !(stat & NOPOWER) ) - icon_state = "[base_state]1" -// src.sd_SetLuminosity(2) - else - icon_state = "[base_state]1-p" -// src.sd_SetLuminosity(0) - -//Don't want to render prison breaks impossible -/obj/machinery/flasher/attackby(obj/item/W as obj, mob/user as mob) - if (W.iswirecutter()) - add_fingerprint(user) - src.disable = !src.disable - if (src.disable) - user.visible_message("[user] has disconnected the [src]'s flashbulb!", "You disconnect the [src]'s flashbulb!") - if (!src.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 (src.anchored) - return src.flash() - else - return - -/obj/machinery/flasher/proc/flash() - if (!(powered())) - return - - if ((src.disable) || (src.last_flash && world.time < src.last_flash + 150)) - return - - playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) - flick("[base_state]_flash", src) - src.last_flash = world.time - use_power(1500) - - for (var/mob/O in viewers(src, null)) - if (get_dist(src, O) > src.range) - continue - - var/flash_time = strength - if (istype(O, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = O - if(!H.eyecheck(TRUE) <= 0) - continue - flash_time *= H.species.flash_mod - var/obj/item/organ/internal/eyes/E = H.get_eyes() - if(!E) - return - - E.flash_act() - - if(E.is_bruised() && prob(E.damage + 50)) - flick("e_flash", O:flash) - E.damage += rand(1, 5) - else - if(!O.blinded) - flick("flash", O:flash) - 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(atom/movable/AM as mob|obj) - if ((src.disable) || (src.last_flash && world.time < src.last_flash + 150)) - return - - if (src.anchored) - src.flash() - -/obj/machinery/flasher/portable/attackby(obj/item/W as obj, mob/user as mob) - if (W.iswrench()) - add_fingerprint(user) - src.anchored = !src.anchored - - if (!src.anchored) - user.show_message(text("[src] can now be moved.")) - cut_overlays() - - else if (src.anchored) - user.show_message(text("[src] is now secured.")) - add_overlay("[base_state]-s") - -/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 SSmachinery.all_machines) - if(M.id == src.id) - spawn() - M.flash() - - sleep(50) - - icon_state = "launcherbtt" - active = 0 - - return +// 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" + 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 = 1 + use_power = 1 + idle_power_usage = 2 + flags = PROXMOVE + var/_wifi_id + var/datum/wifi/receiver/button/flasher/wifi_receiver + +/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 = 0 + base_state = "pflash" + density = 1 + +/obj/machinery/flasher/Initialize() + . = ..() + if(_wifi_id) + wifi_receiver = new(_wifi_id, src) + +/obj/machinery/flasher/Destroy() + qdel(wifi_receiver) + wifi_receiver = null + return ..() + +/obj/machinery/flasher/power_change() + ..() + if ( !(stat & NOPOWER) ) + icon_state = "[base_state]1" +// src.sd_SetLuminosity(2) + else + icon_state = "[base_state]1-p" +// src.sd_SetLuminosity(0) + +//Don't want to render prison breaks impossible +/obj/machinery/flasher/attackby(obj/item/W as obj, mob/user as mob) + if (W.iswirecutter()) + add_fingerprint(user) + src.disable = !src.disable + if (src.disable) + user.visible_message("[user] has disconnected the [src]'s flashbulb!", "You disconnect the [src]'s flashbulb!") + if (!src.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 (src.anchored) + return src.flash() + else + return + +/obj/machinery/flasher/proc/flash() + if (!(powered())) + return + + if ((src.disable) || (src.last_flash && world.time < src.last_flash + 150)) + return + + playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) + flick("[base_state]_flash", src) + src.last_flash = world.time + use_power(1500) + + for (var/mob/O in viewers(src, null)) + if (get_dist(src, O) > src.range) + continue + + var/flash_time = strength + if (istype(O, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = O + if(!H.eyecheck(TRUE) <= 0) + continue + flash_time *= H.species.flash_mod + var/obj/item/organ/internal/eyes/E = H.get_eyes() + if(!E) + return + + E.flash_act() + + if(E.is_bruised() && prob(E.damage + 50)) + flick("e_flash", O:flash) + E.damage += rand(1, 5) + else + if(!O.blinded) + flick("flash", O:flash) + 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(atom/movable/AM as mob|obj) + if ((src.disable) || (src.last_flash && world.time < src.last_flash + 150)) + return + + if (src.anchored) + src.flash() + +/obj/machinery/flasher/portable/attackby(obj/item/W as obj, mob/user as mob) + if (W.iswrench()) + add_fingerprint(user) + src.anchored = !src.anchored + + if (!src.anchored) + user.show_message(text("[src] can now be moved.")) + cut_overlays() + + else if (src.anchored) + user.show_message(text("[src] is now secured.")) + add_overlay("[base_state]-s") + +/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 SSmachinery.all_machines) + if(M.id == src.id) + spawn() + M.flash() + + sleep(50) + + icon_state = "launcherbtt" + active = 0 + + return diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 4c56508234f..754b1d785cc 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -1,409 +1,409 @@ -/* 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." - icon_state = "holopad0" - - layer = TURF_LAYER+0.1 //Preventing rats and drones from sneaking under them. - - var/power_per_hologram = 500 //per usage per hologram - idle_power_usage = 5 - use_power = 1 - - 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 - var/hacked = 0 // if hacked the hologram doesn't display. It only gives audio feedback. - - var/incoming_connection = 0 - var/mob/living/caller_id - var/obj/machinery/hologram/holopad/sourcepad - var/obj/machinery/hologram/holopad/targetpad - var/forced - var/last_message - -/obj/machinery/hologram/holopad/check_eye(mob/user) - if (user && user == caller_id) - return 0 - - return -1 - -/obj/machinery/hologram/holopad/Initialize() - . = ..() - desc = "It's a floor-mounted device for projecting holographic images. Its ID is '[loc.loc]'" - -/obj/machinery/hologram/holopad/attack_hand(var/mob/living/carbon/human/user) //Carn: Hologram requests. - if(!istype(user)) - return - if(incoming_connection && caller_id) - visible_message("The pad hums quietly as it establishes a connection.") - if(caller_id.loc != sourcepad.loc) - visible_message("The pad flashes an error message. The caller has left their holopad.") - return - take_call(user) - return - else if(caller_id && !incoming_connection && forced) - audible_message("Access denied. Terminating a command-level transmission locally is not permitted.") - return - else if(caller_id && !incoming_connection) - visible_message("Severing connection to distant holopad.") - end_call(user) - return - switch(alert(user,"Would you like to request an AI's presence or establish communications with another pad?", "Holopad","AI","Holocomms","Cancel")) - if("AI") - 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 silicon_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.") - if("Holocomms") - if(user.loc != src.loc) - to_chat(user, "Please step unto the holopad.") - return - if(last_request + 200 < world.time) //don't spam other people with requests either, you jerk! - last_request = world.time - var/obj/item/card/id/I = user.GetIdCard() - if(!I) - to_chat(user, span("notice", "You need authorization to use the holocall system. Please equip a valid ID card.")) - return - var/forcedcall = 0 - if(access_heads in I.access) //Special functions for command level people - switch(alert(user,"Command level authorization detected. Additional functions available", "Command level menu", "Forced call","Regular call")) - if("Forced call") - forcedcall = 1 - var/list/holopadlist = list() - for(var/obj/machinery/hologram/holopad/H in SSmachinery.processing_machines) - if((H.z in current_map.map_levels) && H.operable()) - holopadlist["[H.loc.loc.name]"] = H //Define a list and fill it with the area of every holopad in the world - holopadlist = sortAssoc(holopadlist) - var/temppad = input(user, "Which holopad would you like to contact?", "holopad list") as null|anything in holopadlist - targetpad = holopadlist["[temppad]"] - if(targetpad == src) - to_chat(user, "Using such sophisticated technology, just to talk to yourself seems a bit silly.") - return - if(targetpad) - make_call(targetpad, user, forcedcall) - -/obj/machinery/hologram/holopad/proc/make_call(var/obj/machinery/hologram/holopad/targetpad, var/mob/living/carbon/user, forcedcall) - targetpad.last_request = world.time - targetpad.sourcepad = src //This marks the holopad you are making the call from - targetpad.caller_id = user //This marks you as the caller - targetpad.incoming_connection = 1 - playsound(targetpad.loc, 'sound/machines/chime.ogg', 25, 5) - targetpad.icon_state = "holopad1" - if(forcedcall) - targetpad.audible_message("\The [src] announces, \"Incoming call with command authorization from [targetpad.sourcepad.loc.loc].\"") - to_chat(user, "Establishing forced connection to the holopad in [targetpad.loc.loc]") - targetpad.forced = 1 - sleep(80) - targetpad.take_call(user) - else - targetpad.audible_message("\The [src] announces, \"Incoming communications request from [targetpad.sourcepad.loc.loc].\"") - to_chat(user, "Trying to establish a connection to the holopad in [targetpad.loc.loc]... Please await confirmation from recipient.") - -/obj/machinery/hologram/holopad/proc/take_call(mob/living/carbon/user) - incoming_connection = 0 - caller_id.set_machine(src) - caller_id.reset_view(src) - if(!masters[caller_id]) //If there is no hologram, possibly make one. - activate_holocall(caller_id) - -/obj/machinery/hologram/holopad/proc/end_call(mob/user) - if(caller_id) - caller_id.unset_machine() - caller_id.reset_view() //Send the caller back to his body - clear_holo(null, caller_id) // destroy the hologram - caller_id = null - - if(user) - user.unset_machine() - user.reset_view() //Send the caller back to his body - clear_holo(null, user) // destroy the hologram - user = null - -/obj/machinery/hologram/holopad/proc/activate_holocall(mob/living/carbon/caller_id) - if(caller_id) - create_holo(0, caller_id)//Create one. - visible_message("A holographic image of [caller_id] flicks to life right before your eyes!") - else - to_chat(caller_id, "ERROR: Unable to project hologram.") - return - -/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. - if(hacked == 0) - src.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/living/M, text, verb, datum/language/speaking) - if(M) - for(var/mob/living/silicon/ai/master in masters) - if(!master.say_understands(M, speaking))//The AI will be able to understand most mobs talking through the holopad. - if(speaking) - text = speaking.scramble(text) - else - text = stars(text) - var/name_used = M.GetVoice() - //This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only. - var/rendered - if(speaking) - rendered = "Holopad received, [name_used] [speaking.format_message(text, verb)]" - else - rendered = "Holopad received, [name_used] [verb], \"[text]\"" - master.show_message(rendered, 2) - var/name_used = M.GetVoice() - if(targetpad) //If this is the pad you're making the call from - var/message - if(speaking) - message = "Holopad received, [name_used] [speaking.format_message(text, verb)]" - targetpad.audible_message(message) - targetpad.last_message = message - else - message = "Holopad received, [name_used] [verb], \"[text]\"" - if(sourcepad) //If this is a pad receiving a call - if(name_used==caller_id||text==last_message||findtext(text, "Holopad received")) //prevent echoes - return - sourcepad.audible_message("Holopad received, [name_used] [speaking.format_message(text, 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) - for(var/mob/living/carbon/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) - if(targetpad) - targetpad.visible_message("[text]") - -/obj/machinery/hologram/holopad/show_message(msg, type, alt, alt_type) - for(var/mob/living/silicon/ai/master in masters) - var/rendered = "The holographic image of [msg]" - master.show_message(rendered, type) - if(findtext(msg, "Holopad received,")) - return - for(var/mob/living/carbon/master in masters) - var/rendered = "The holographic image of [msg]" - master.show_message(rendered, type) - if(targetpad) - for(var/mob/living/carbon/master in view(targetpad)) - var/rendered = "The holographic image of [msg]" - master.show_message(rendered, type) - -/obj/machinery/hologram/holopad/proc/create_holo(mob/living/silicon/ai/A, mob/living/carbon/caller_id, turf/T = loc) - if(hacked == 0) - var/obj/effect/overlay/hologram = new(T)//Spawn a blank effect at the location. - if(caller_id) - var/tempicon = getFlatIcon(caller_id) - - hologram.name = "[caller_id.name] (Hologram)" - hologram.forceMove(get_step(src,1)) - masters[caller_id] = hologram - hologram.icon = getHologramIcon(icon(tempicon)) // Add the callers image as an overlay to keep coloration! - else - hologram.icon = A.holo_icon // Add the AI's configured holo Icon - hologram.name = "[A.name] (Hologram)"//If someone decides to right click. - A.holo = src - masters[A] = hologram - - hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. - hologram.mouse_opacity = 0//So you can't click on it. - hologram.anchored = 1//So space wind cannot drag it. - hologram.set_light(2) //hologram lighting - hologram.color = color //painted holopad gives coloured holograms - set_light(2) //pad lighting - icon_state = "holopad1" - return 1 - else if(hacked == 1) - var/obj/effect/overlay/hologram = new(T)//Spawn a blank effect at the location. - hologram.mouse_opacity = 0//So you can't click on it. - hologram.anchored = 1//So space wind cannot drag it. - masters[A] = hologram - A.holo = src - return 1 - -/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/silicon/ai/user, mob/living/carbon/call_user) - if(user) - qdel(masters[user])//Get rid of user's hologram - user.holo = null - masters -= user //Discard AI from the list of those who use holopad - if(call_user) - qdel(masters[call_user]) //Get rid of user's hologram - masters -= call_user //Discard the caller 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" - if(sourcepad) - sourcepad.targetpad = null - sourcepad = null - caller_id = null - return 1 - -/obj/machinery/hologram/holopad/machinery_process() - for (var/mob/living/silicon/ai/master in masters) - var/active_ai = (master && !master.incapacitated() && master.client && master.eyeobj)//If there is an AI with an eye attached, it's not incapacitated, and it has a client - if((stat & NOPOWER) || !active_ai) - clear_holo(master) - continue - - if(!(masters[master] in view(src))) - clear_holo(master) - continue - - use_power(power_per_hologram) - if(last_request + 200 < world.time && incoming_connection==1) - incoming_connection = 0 - end_call() - if(sourcepad) - sourcepad.audible_message("The holopad connection timed out") - sourcepad = 0 - if (caller_id && sourcepad) - if(caller_id.loc != sourcepad.loc) - forced = 0 - sourcepad.visible_message("Severing connection to distant holopad.") - visible_message("The connection has been terminated by [caller_id].") - end_call() - return 1 - -/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/silicon/ai/user) - if(masters[user]) - step_to(masters[user], user.eyeobj) // So it turns. - var/obj/effect/overlay/H = masters[user] - H.forceMove(get_turf(user.eyeobj)) - masters[user] = H - - if(!(H in view(src))) - clear_holo(user) - return 0 - - if((HOLOPAD_MODE == RANGE_BASED && (get_dist(user.eyeobj, src) > holo_range))) - clear_holo(user) - - if(HOLOPAD_MODE == AREA_BASED) - var/area/holo_area = get_area(src) - var/area/hologram_area = get_area(H) - if(hologram_area != holo_area) - clear_holo(user) - return 1 - -/* - * Hologram - */ - -/obj/machinery/hologram - anchored = 1 - use_power = 1 - 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/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("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.dmi' - 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." + icon_state = "holopad0" + + layer = TURF_LAYER+0.1 //Preventing rats and drones from sneaking under them. + + var/power_per_hologram = 500 //per usage per hologram + idle_power_usage = 5 + use_power = 1 + + 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 + var/hacked = 0 // if hacked the hologram doesn't display. It only gives audio feedback. + + var/incoming_connection = 0 + var/mob/living/caller_id + var/obj/machinery/hologram/holopad/sourcepad + var/obj/machinery/hologram/holopad/targetpad + var/forced + var/last_message + +/obj/machinery/hologram/holopad/check_eye(mob/user) + if (user && user == caller_id) + return 0 + + return -1 + +/obj/machinery/hologram/holopad/Initialize() + . = ..() + desc = "It's a floor-mounted device for projecting holographic images. Its ID is '[loc.loc]'" + +/obj/machinery/hologram/holopad/attack_hand(var/mob/living/carbon/human/user) //Carn: Hologram requests. + if(!istype(user)) + return + if(incoming_connection && caller_id) + visible_message("The pad hums quietly as it establishes a connection.") + if(caller_id.loc != sourcepad.loc) + visible_message("The pad flashes an error message. The caller has left their holopad.") + return + take_call(user) + return + else if(caller_id && !incoming_connection && forced) + audible_message("Access denied. Terminating a command-level transmission locally is not permitted.") + return + else if(caller_id && !incoming_connection) + visible_message("Severing connection to distant holopad.") + end_call(user) + return + switch(alert(user,"Would you like to request an AI's presence or establish communications with another pad?", "Holopad","AI","Holocomms","Cancel")) + if("AI") + 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 silicon_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.") + if("Holocomms") + if(user.loc != src.loc) + to_chat(user, "Please step unto the holopad.") + return + if(last_request + 200 < world.time) //don't spam other people with requests either, you jerk! + last_request = world.time + var/obj/item/card/id/I = user.GetIdCard() + if(!I) + to_chat(user, span("notice", "You need authorization to use the holocall system. Please equip a valid ID card.")) + return + var/forcedcall = 0 + if(access_heads in I.access) //Special functions for command level people + switch(alert(user,"Command level authorization detected. Additional functions available", "Command level menu", "Forced call","Regular call")) + if("Forced call") + forcedcall = 1 + var/list/holopadlist = list() + for(var/obj/machinery/hologram/holopad/H in SSmachinery.processing_machines) + if((H.z in current_map.map_levels) && H.operable()) + holopadlist["[H.loc.loc.name]"] = H //Define a list and fill it with the area of every holopad in the world + holopadlist = sortAssoc(holopadlist) + var/temppad = input(user, "Which holopad would you like to contact?", "holopad list") as null|anything in holopadlist + targetpad = holopadlist["[temppad]"] + if(targetpad == src) + to_chat(user, "Using such sophisticated technology, just to talk to yourself seems a bit silly.") + return + if(targetpad) + make_call(targetpad, user, forcedcall) + +/obj/machinery/hologram/holopad/proc/make_call(var/obj/machinery/hologram/holopad/targetpad, var/mob/living/carbon/user, forcedcall) + targetpad.last_request = world.time + targetpad.sourcepad = src //This marks the holopad you are making the call from + targetpad.caller_id = user //This marks you as the caller + targetpad.incoming_connection = 1 + playsound(targetpad.loc, 'sound/machines/chime.ogg', 25, 5) + targetpad.icon_state = "holopad1" + if(forcedcall) + targetpad.audible_message("\The [src] announces, \"Incoming call with command authorization from [targetpad.sourcepad.loc.loc].\"") + to_chat(user, "Establishing forced connection to the holopad in [targetpad.loc.loc]") + targetpad.forced = 1 + sleep(80) + targetpad.take_call(user) + else + targetpad.audible_message("\The [src] announces, \"Incoming communications request from [targetpad.sourcepad.loc.loc].\"") + to_chat(user, "Trying to establish a connection to the holopad in [targetpad.loc.loc]... Please await confirmation from recipient.") + +/obj/machinery/hologram/holopad/proc/take_call(mob/living/carbon/user) + incoming_connection = 0 + caller_id.set_machine(src) + caller_id.reset_view(src) + if(!masters[caller_id]) //If there is no hologram, possibly make one. + activate_holocall(caller_id) + +/obj/machinery/hologram/holopad/proc/end_call(mob/user) + if(caller_id) + caller_id.unset_machine() + caller_id.reset_view() //Send the caller back to his body + clear_holo(null, caller_id) // destroy the hologram + caller_id = null + + if(user) + user.unset_machine() + user.reset_view() //Send the caller back to his body + clear_holo(null, user) // destroy the hologram + user = null + +/obj/machinery/hologram/holopad/proc/activate_holocall(mob/living/carbon/caller_id) + if(caller_id) + create_holo(0, caller_id)//Create one. + visible_message("A holographic image of [caller_id] flicks to life right before your eyes!") + else + to_chat(caller_id, "ERROR: Unable to project hologram.") + return + +/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. + if(hacked == 0) + src.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/living/M, text, verb, datum/language/speaking) + if(M) + for(var/mob/living/silicon/ai/master in masters) + if(!master.say_understands(M, speaking))//The AI will be able to understand most mobs talking through the holopad. + if(speaking) + text = speaking.scramble(text) + else + text = stars(text) + var/name_used = M.GetVoice() + //This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only. + var/rendered + if(speaking) + rendered = "Holopad received, [name_used] [speaking.format_message(text, verb)]" + else + rendered = "Holopad received, [name_used] [verb], \"[text]\"" + master.show_message(rendered, 2) + var/name_used = M.GetVoice() + if(targetpad) //If this is the pad you're making the call from + var/message + if(speaking) + message = "Holopad received, [name_used] [speaking.format_message(text, verb)]" + targetpad.audible_message(message) + targetpad.last_message = message + else + message = "Holopad received, [name_used] [verb], \"[text]\"" + if(sourcepad) //If this is a pad receiving a call + if(name_used==caller_id||text==last_message||findtext(text, "Holopad received")) //prevent echoes + return + sourcepad.audible_message("Holopad received, [name_used] [speaking.format_message(text, 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) + for(var/mob/living/carbon/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) + if(targetpad) + targetpad.visible_message("[text]") + +/obj/machinery/hologram/holopad/show_message(msg, type, alt, alt_type) + for(var/mob/living/silicon/ai/master in masters) + var/rendered = "The holographic image of [msg]" + master.show_message(rendered, type) + if(findtext(msg, "Holopad received,")) + return + for(var/mob/living/carbon/master in masters) + var/rendered = "The holographic image of [msg]" + master.show_message(rendered, type) + if(targetpad) + for(var/mob/living/carbon/master in view(targetpad)) + var/rendered = "The holographic image of [msg]" + master.show_message(rendered, type) + +/obj/machinery/hologram/holopad/proc/create_holo(mob/living/silicon/ai/A, mob/living/carbon/caller_id, turf/T = loc) + if(hacked == 0) + var/obj/effect/overlay/hologram = new(T)//Spawn a blank effect at the location. + if(caller_id) + var/tempicon = getFlatIcon(caller_id) + + hologram.name = "[caller_id.name] (Hologram)" + hologram.forceMove(get_step(src,1)) + masters[caller_id] = hologram + hologram.icon = getHologramIcon(icon(tempicon)) // Add the callers image as an overlay to keep coloration! + else + hologram.icon = A.holo_icon // Add the AI's configured holo Icon + hologram.name = "[A.name] (Hologram)"//If someone decides to right click. + A.holo = src + masters[A] = hologram + + hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. + hologram.mouse_opacity = 0//So you can't click on it. + hologram.anchored = 1//So space wind cannot drag it. + hologram.set_light(2) //hologram lighting + hologram.color = color //painted holopad gives coloured holograms + set_light(2) //pad lighting + icon_state = "holopad1" + return 1 + else if(hacked == 1) + var/obj/effect/overlay/hologram = new(T)//Spawn a blank effect at the location. + hologram.mouse_opacity = 0//So you can't click on it. + hologram.anchored = 1//So space wind cannot drag it. + masters[A] = hologram + A.holo = src + return 1 + +/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/silicon/ai/user, mob/living/carbon/call_user) + if(user) + qdel(masters[user])//Get rid of user's hologram + user.holo = null + masters -= user //Discard AI from the list of those who use holopad + if(call_user) + qdel(masters[call_user]) //Get rid of user's hologram + masters -= call_user //Discard the caller 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" + if(sourcepad) + sourcepad.targetpad = null + sourcepad = null + caller_id = null + return 1 + +/obj/machinery/hologram/holopad/machinery_process() + for (var/mob/living/silicon/ai/master in masters) + var/active_ai = (master && !master.incapacitated() && master.client && master.eyeobj)//If there is an AI with an eye attached, it's not incapacitated, and it has a client + if((stat & NOPOWER) || !active_ai) + clear_holo(master) + continue + + if(!(masters[master] in view(src))) + clear_holo(master) + continue + + use_power(power_per_hologram) + if(last_request + 200 < world.time && incoming_connection==1) + incoming_connection = 0 + end_call() + if(sourcepad) + sourcepad.audible_message("The holopad connection timed out") + sourcepad = 0 + if (caller_id && sourcepad) + if(caller_id.loc != sourcepad.loc) + forced = 0 + sourcepad.visible_message("Severing connection to distant holopad.") + visible_message("The connection has been terminated by [caller_id].") + end_call() + return 1 + +/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/silicon/ai/user) + if(masters[user]) + step_to(masters[user], user.eyeobj) // So it turns. + var/obj/effect/overlay/H = masters[user] + H.forceMove(get_turf(user.eyeobj)) + masters[user] = H + + if(!(H in view(src))) + clear_holo(user) + return 0 + + if((HOLOPAD_MODE == RANGE_BASED && (get_dist(user.eyeobj, src) > holo_range))) + clear_holo(user) + + if(HOLOPAD_MODE == AREA_BASED) + var/area/holo_area = get_area(src) + var/area/hologram_area = get_area(H) + if(hologram_area != holo_area) + clear_holo(user) + return 1 + +/* + * Hologram + */ + +/obj/machinery/hologram + anchored = 1 + use_power = 1 + 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/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("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.dmi' + icon_state = "hologram0" + + +#undef RANGE_BASED +#undef AREA_BASED +#undef HOLOPAD_PASSIVE_POWER_USAGE +#undef HOLOGRAM_POWER_USAGE diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index 6bc22567be0..5e65c57a3eb 100755 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -1,168 +1,168 @@ -/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 = 0 - anchored = 1 - use_power = 1 - idle_power_usage = 2 - active_power_usage = 4 - var/_wifi_id - var/datum/wifi/receiver/button/igniter/wifi_receiver - -/obj/machinery/igniter/Initialize() - . = ..() - update_icon() - if(_wifi_id) - wifi_receiver = new(_wifi_id, src) - -/obj/machinery/igniter/update_icon() - ..() - icon_state = "igniter[on]" - -/obj/machinery/igniter/Destroy() - qdel(wifi_receiver) - wifi_receiver = null - return ..() - -/obj/machinery/igniter/attack_ai(mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/igniter/attack_hand(mob/user as mob) - if(..()) - return - add_fingerprint(user) - ignite() - return - -/obj/machinery/igniter/machinery_process() //ugh why is this even in process()? - if (on && powered() ) - var/turf/location = src.loc - if (isturf(location)) - location.hotspot_expose(1000,500,1) - return 1 - -/obj/machinery/igniter/power_change() - ..() - update_icon() - -/obj/machinery/igniter/proc/ignite() - use_power(50) - on = !on - update_icon() - - -// 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" - var/id = null - var/disable = 0 - var/last_spark = 0 - var/base_state = "migniter" - layer = 3.3 - anchored = 1 - use_power = 1 - idle_power_usage = 2 - active_power_usage = 4 - var/_wifi_id - var/datum/wifi/receiver/button/sparker/wifi_receiver - -/obj/machinery/sparker/Initialize() - . = ..() - if(_wifi_id) - wifi_receiver = new(_wifi_id, src) - -/obj/machinery/sparker/Destroy() - qdel(wifi_receiver) - wifi_receiver = null - return ..() - -/obj/machinery/sparker/update_icon() - ..() - if(disable) - icon_state = "migniter-d" - else if(powered()) - icon_state = "migniter" -// src.sd_SetLuminosity(2) - else - icon_state = "migniter-p" -// src.sd_SetLuminosity(0) - -/obj/machinery/sparker/power_change() - ..() - update_icon() - -/obj/machinery/sparker/attackby(obj/item/W as obj, mob/user as mob) - if (W.isscrewdriver()) - add_fingerprint(user) - disable = !disable - if(disable) - user.visible_message("[user] has disabled the [src]!", "You disable the connection to the [src].") - else if(!disable) - user.visible_message("[user] has reconnected the [src]!", "You fix the connection to the [src].") - update_icon() - -/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("migniter-spark", src) - spark(src, 2, alldirs) - src.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 SSmachinery.all_machines) - if (M.id == id) - INVOKE_ASYNC(M, /obj/machinery/sparker/proc/ignite) - - for(var/obj/machinery/igniter/M in SSmachinery.all_machines) - if(M.id == id) - M.ignite() - - sleep(50) - - icon_state = "launcherbtt" - active = 0 - - return +/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 = 0 + anchored = 1 + use_power = 1 + idle_power_usage = 2 + active_power_usage = 4 + var/_wifi_id + var/datum/wifi/receiver/button/igniter/wifi_receiver + +/obj/machinery/igniter/Initialize() + . = ..() + update_icon() + if(_wifi_id) + wifi_receiver = new(_wifi_id, src) + +/obj/machinery/igniter/update_icon() + ..() + icon_state = "igniter[on]" + +/obj/machinery/igniter/Destroy() + qdel(wifi_receiver) + wifi_receiver = null + return ..() + +/obj/machinery/igniter/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/igniter/attack_hand(mob/user as mob) + if(..()) + return + add_fingerprint(user) + ignite() + return + +/obj/machinery/igniter/machinery_process() //ugh why is this even in process()? + if (on && powered() ) + var/turf/location = src.loc + if (isturf(location)) + location.hotspot_expose(1000,500,1) + return 1 + +/obj/machinery/igniter/power_change() + ..() + update_icon() + +/obj/machinery/igniter/proc/ignite() + use_power(50) + on = !on + update_icon() + + +// 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" + var/id = null + var/disable = 0 + var/last_spark = 0 + var/base_state = "migniter" + layer = 3.3 + anchored = 1 + use_power = 1 + idle_power_usage = 2 + active_power_usage = 4 + var/_wifi_id + var/datum/wifi/receiver/button/sparker/wifi_receiver + +/obj/machinery/sparker/Initialize() + . = ..() + if(_wifi_id) + wifi_receiver = new(_wifi_id, src) + +/obj/machinery/sparker/Destroy() + qdel(wifi_receiver) + wifi_receiver = null + return ..() + +/obj/machinery/sparker/update_icon() + ..() + if(disable) + icon_state = "migniter-d" + else if(powered()) + icon_state = "migniter" +// src.sd_SetLuminosity(2) + else + icon_state = "migniter-p" +// src.sd_SetLuminosity(0) + +/obj/machinery/sparker/power_change() + ..() + update_icon() + +/obj/machinery/sparker/attackby(obj/item/W as obj, mob/user as mob) + if (W.isscrewdriver()) + add_fingerprint(user) + disable = !disable + if(disable) + user.visible_message("[user] has disabled the [src]!", "You disable the connection to the [src].") + else if(!disable) + user.visible_message("[user] has reconnected the [src]!", "You fix the connection to the [src].") + update_icon() + +/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("migniter-spark", src) + spark(src, 2, alldirs) + src.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 SSmachinery.all_machines) + if (M.id == id) + INVOKE_ASYNC(M, /obj/machinery/sparker/proc/ignite) + + for(var/obj/machinery/igniter/M in SSmachinery.all_machines) + if(M.id == id) + M.ignite() + + sleep(50) + + icon_state = "launcherbtt" + active = 0 + + return diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index 378d3554bb8..da027b9d76b 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -1,194 +1,194 @@ -/obj/machinery/iv_drip - name = "\improper IV drip" - icon = 'icons/obj/iv_drip.dmi' - anchored = 0 - density = 1 - - -/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/transfer_amount = REM -/obj/machinery/iv_drip/var/obj/item/reagent_containers/beaker = null - -/obj/machinery/iv_drip/update_icon() - if(src.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(attached) - visible_message("[src.attached] is detached from \the [src]") - src.attached = null - src.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].") - src.attached = over_object - src.update_icon() - - -/obj/machinery/iv_drip/attackby(obj/item/W as obj, mob/user as mob) - - if (istype(W, /obj/item/reagent_containers/blood/ripped)) - to_chat(user, "You can't use a ripped bloodpack.") - return - if (istype(W, /obj/item/reagent_containers)) - if(!isnull(src.beaker)) - to_chat(user, "There is already a reagent container loaded!") - return - - user.drop_from_inventory(W,src) - src.beaker = W - to_chat(user, "You attach \the [W] to \the [src].") - src.update_icon() - return - else - return ..() - - -/obj/machinery/iv_drip/machinery_process() - set background = 1 - - if(src.attached) - - if(!(get_dist(src, src.attached) <= 1 && isturf(src.attached.loc))) - var/obj/item/organ/external/affecting = src.attached:get_organ(pick(BP_R_ARM, BP_L_ARM)) - src.attached.visible_message("The needle is ripped out of [src.attached]'s [affecting.limb_name == BP_R_ARM ? "right arm" : "left arm"].", "The needle painfully rips out of your [affecting.limb_name == BP_R_ARM ? "right arm" : "left arm"].") - affecting.take_damage(brute = 5, sharp = 1) - src.attached = null - src.update_icon() - return - - if(src.attached && src.beaker) - - var/mob/living/carbon/human/T = attached - - if(!istype(T)) - return - - if(!T.dna) - return - - if(NOCLONE in T.mutations) - return - - if(T.species.flags & NO_BLOOD) - return - - // Give blood - if(mode) - if(src.beaker.volume > 0) - src.beaker.reagents.trans_to_mob(src.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/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 (isAI(user)) - return - if(src.beaker) - src.beaker.forceMove(get_turf(src)) - 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) - ..(user) - if (!(user in view(2)) && user!=src.loc) return - - to_chat(user, "The IV drip is [mode ? "injecting" : "taking blood"].") - to_chat(user, "The transfer rate is set to [src.transfer_amount] u/sec") - - if(beaker) - if(beaker.reagents && beaker.reagents.reagent_list.len) - to_chat(usr, "Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid.") - else - to_chat(usr, "Attached is an empty [beaker].") - else - to_chat(usr, "No chemicals are attached.") - - to_chat(usr, "[attached ? attached : "No one"] is attached.") - -// Let's doctors set the rate of transfer. Useful if you want to set the rate at the rate of metabolisation. -// No longer have to take someone to dialysis because they have leftover sleeptox after surgery. -/obj/machinery/iv_drip/verb/transfer_rate() - set category = "Object" - set name = "Set Transfer Rate" - set src in view(1) - - if (!ishuman(usr) && !issilicon(usr)) - return - if (usr.stat || usr.restrained() || !Adjacent(usr)) - return - set_rate: - var/amount = input("Set transfer rate as u/sec (between 4 and 0.001)") as num - if ((0.001 > amount || amount > 4) && amount != 0) - to_chat(usr, "Entered value must be between 0.001 and 4.") - goto set_rate - if (transfer_amount == 0) - transfer_amount = REM - return - transfer_amount = amount - to_chat(usr, "Transfer rate set to [src.transfer_amount] u/sec") - -/obj/machinery/iv_drip/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) - if(height && istype(mover) && mover.checkpass(PASSTABLE)) //allow bullets, beams, thrown objects, rats, drones, and the like through. - return 1 - return ..() +/obj/machinery/iv_drip + name = "\improper IV drip" + icon = 'icons/obj/iv_drip.dmi' + anchored = 0 + density = 1 + + +/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/transfer_amount = REM +/obj/machinery/iv_drip/var/obj/item/reagent_containers/beaker = null + +/obj/machinery/iv_drip/update_icon() + if(src.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(attached) + visible_message("[src.attached] is detached from \the [src]") + src.attached = null + src.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].") + src.attached = over_object + src.update_icon() + + +/obj/machinery/iv_drip/attackby(obj/item/W as obj, mob/user as mob) + + if (istype(W, /obj/item/reagent_containers/blood/ripped)) + to_chat(user, "You can't use a ripped bloodpack.") + return + if (istype(W, /obj/item/reagent_containers)) + if(!isnull(src.beaker)) + to_chat(user, "There is already a reagent container loaded!") + return + + user.drop_from_inventory(W,src) + src.beaker = W + to_chat(user, "You attach \the [W] to \the [src].") + src.update_icon() + return + else + return ..() + + +/obj/machinery/iv_drip/machinery_process() + set background = 1 + + if(src.attached) + + if(!(get_dist(src, src.attached) <= 1 && isturf(src.attached.loc))) + var/obj/item/organ/external/affecting = src.attached:get_organ(pick(BP_R_ARM, BP_L_ARM)) + src.attached.visible_message("The needle is ripped out of [src.attached]'s [affecting.limb_name == BP_R_ARM ? "right arm" : "left arm"].", "The needle painfully rips out of your [affecting.limb_name == BP_R_ARM ? "right arm" : "left arm"].") + affecting.take_damage(brute = 5, sharp = 1) + src.attached = null + src.update_icon() + return + + if(src.attached && src.beaker) + + var/mob/living/carbon/human/T = attached + + if(!istype(T)) + return + + if(!T.dna) + return + + if(NOCLONE in T.mutations) + return + + if(T.species.flags & NO_BLOOD) + return + + // Give blood + if(mode) + if(src.beaker.volume > 0) + src.beaker.reagents.trans_to_mob(src.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/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 (isAI(user)) + return + if(src.beaker) + src.beaker.forceMove(get_turf(src)) + 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) + ..(user) + if (!(user in view(2)) && user!=src.loc) return + + to_chat(user, "The IV drip is [mode ? "injecting" : "taking blood"].") + to_chat(user, "The transfer rate is set to [src.transfer_amount] u/sec") + + if(beaker) + if(beaker.reagents && beaker.reagents.reagent_list.len) + to_chat(usr, "Attached is \a [beaker] with [beaker.reagents.total_volume] units of liquid.") + else + to_chat(usr, "Attached is an empty [beaker].") + else + to_chat(usr, "No chemicals are attached.") + + to_chat(usr, "[attached ? attached : "No one"] is attached.") + +// Let's doctors set the rate of transfer. Useful if you want to set the rate at the rate of metabolisation. +// No longer have to take someone to dialysis because they have leftover sleeptox after surgery. +/obj/machinery/iv_drip/verb/transfer_rate() + set category = "Object" + set name = "Set Transfer Rate" + set src in view(1) + + if (!ishuman(usr) && !issilicon(usr)) + return + if (usr.stat || usr.restrained() || !Adjacent(usr)) + return + set_rate: + var/amount = input("Set transfer rate as u/sec (between 4 and 0.001)") as num + if ((0.001 > amount || amount > 4) && amount != 0) + to_chat(usr, "Entered value must be between 0.001 and 4.") + goto set_rate + if (transfer_amount == 0) + transfer_amount = REM + return + transfer_amount = amount + to_chat(usr, "Transfer rate set to [src.transfer_amount] u/sec") + +/obj/machinery/iv_drip/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) + if(height && istype(mover) && mover.checkpass(PASSTABLE)) //allow bullets, beams, thrown objects, rats, drones, and the like through. + return 1 + return ..() diff --git a/code/game/machinery/kitchen/gibber.dm b/code/game/machinery/kitchen/gibber.dm index 3bb6d82da39..1b2a634c0ed 100644 --- a/code/game/machinery/kitchen/gibber.dm +++ b/code/game/machinery/kitchen/gibber.dm @@ -1,241 +1,241 @@ - -/obj/machinery/gibber - name = "gibber" - desc = "The name isn't descriptive enough?" - icon = 'icons/obj/kitchen.dmi' - icon_state = "grinder" - density = 1 - anchored = 1 - 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 = 1 - 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.") - return - -/obj/machinery/gibber/autogibber/CollidedWith(var/atom/A) - if(!input_plate) return - - if(ismob(A)) - var/mob/M = A - - if(M.loc == input_plate - ) - M.forceMove(src) - M.gib() - - -/obj/machinery/gibber/Initialize() - . = ..() - 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 [src] is locked and running, wait for it to finish.") - return - else - src.startgibbing(user) - -/obj/machinery/gibber/examine() - ..() - to_chat(usr, "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 [src]'s safety guard.") - return 1 - -/obj/machinery/gibber/attackby(var/obj/item/W, var/mob/user) - if(istype(W, /obj/item/grab)) - var/obj/item/grab/G = W - if(G.state < GRAB_AGGRESSIVE) - to_chat(user, "You need a better grip to do that!") - return - move_into_gibber(user,G.affecting) - user.drop_from_inventory(G) - - else if(istype(W, /obj/item/organ)) - user.drop_from_inventory(W) - //TODO: Gibber Animations - qdel(W) - user.visible_message("\The [user] feeds \the [W] into \the [src], obliterating it.") - - if(istype(user, /mob/living/carbon/human)) - var/mob/living/carbon/human/M = user - if(M.h_style == "Floorlength Braid" || M.h_style == "Very Long Hair") - if(prob(10)) - M.apply_damage(30, BRUTE, BP_HEAD) - M.apply_damage(45, HALLOSS) - M.visible_message("[user]'s hair catches in the [src]!", "Your hair gets caught in the [src]!") - M.say("*scream") - - -/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 [src] is full, empty it first!") - return - - if(operating) - to_chat(user, "\The [src] is locked and running, wait for it to finish.") - return - - if(!(istype(victim, /mob/living/carbon)) && !(istype(victim, /mob/living/simple_animal)) ) - to_chat(user, "This is not suitable for \the [src]!") - return - - if(istype(victim,/mob/living/carbon/human) && !emagged) - to_chat(user, "\The [src] safety guard is engaged!") - return - - - if(victim.abiotic(1)) - to_chat(user, "\The [victim] may not have abiotic items on.") - return - - user.visible_message("\The [user] starts to put \the [victim] into \the [src]!") - src.add_fingerprint(user) - if(do_after(user, 30 SECONDS, act_target = src) && 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.forceMove(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.forceMove(src.loc) - if (src.occupant.client) - src.occupant.client.eye = src.occupant.client.mob - src.occupant.client.perspective = MOB_PERSPECTIVE - src.occupant.forceMove(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_count = 3 - var/slab_type = /obj/item/reagent_containers/food/snacks/meat - var/slab_nutrition = src.occupant.nutrition / 15 - - // Some mobs have specific meat item types. - if(istype(src.occupant,/mob/living/simple_animal)) - var/mob/living/simple_animal/critter = src.occupant - if(critter.meat_amount) - slab_count = critter.meat_amount - if(critter.meat_type) - slab_type = critter.meat_type - else if(istype(src.occupant,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = occupant - slab_type = 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/reagent_containers/food/snacks/meat/new_meat = new slab_type(src, rand(3,8)) - if(istype(new_meat)) - 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/slab_count,1)) - - src.occupant.attack_log += "\[[time_stamp()]\] Was gibbed by [user]/[user.ckey]" //One shall not simply gib a mob unnoticed! - user.attack_log += "\[[time_stamp()]\] Gibbed [src.occupant]/[src.occupant.ckey]" - msg_admin_attack("[key_name_admin(user)] gibbed [src.occupant] ([src.occupant.ckey]) (JMP)",ckey=key_name(user),ckey_target=key_name(src.occupant)) - - src.occupant.ghostize() - - spawn(gib_time) - - src.operating = 0 - src.occupant.gib() - occupant = null - - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) - operating = 0 - for (var/obj/thing in contents) - // Todo: unify limbs and internal organs - // There's a chance that the gibber will fail to destroy some evidence. - if(istype(thing,/obj/item/organ) && prob(80)) - 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 = 1 + anchored = 1 + 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 = 1 + 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.") + return + +/obj/machinery/gibber/autogibber/CollidedWith(var/atom/A) + if(!input_plate) return + + if(ismob(A)) + var/mob/M = A + + if(M.loc == input_plate + ) + M.forceMove(src) + M.gib() + + +/obj/machinery/gibber/Initialize() + . = ..() + 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 [src] is locked and running, wait for it to finish.") + return + else + src.startgibbing(user) + +/obj/machinery/gibber/examine() + ..() + to_chat(usr, "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 [src]'s safety guard.") + return 1 + +/obj/machinery/gibber/attackby(var/obj/item/W, var/mob/user) + if(istype(W, /obj/item/grab)) + var/obj/item/grab/G = W + if(G.state < GRAB_AGGRESSIVE) + to_chat(user, "You need a better grip to do that!") + return + move_into_gibber(user,G.affecting) + user.drop_from_inventory(G) + + else if(istype(W, /obj/item/organ)) + user.drop_from_inventory(W) + //TODO: Gibber Animations + qdel(W) + user.visible_message("\The [user] feeds \the [W] into \the [src], obliterating it.") + + if(istype(user, /mob/living/carbon/human)) + var/mob/living/carbon/human/M = user + if(M.h_style == "Floorlength Braid" || M.h_style == "Very Long Hair") + if(prob(10)) + M.apply_damage(30, BRUTE, BP_HEAD) + M.apply_damage(45, PAIN) + M.visible_message("[user]'s hair catches in the [src]!", "Your hair gets caught in the [src]!") + M.say("*scream") + + +/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 [src] is full, empty it first!") + return + + if(operating) + to_chat(user, "\The [src] is locked and running, wait for it to finish.") + return + + if(!(istype(victim, /mob/living/carbon)) && !(istype(victim, /mob/living/simple_animal)) ) + to_chat(user, "This is not suitable for \the [src]!") + return + + if(istype(victim,/mob/living/carbon/human) && !emagged) + to_chat(user, "\The [src] safety guard is engaged!") + return + + + if(victim.abiotic(1)) + to_chat(user, "\The [victim] may not have abiotic items on.") + return + + user.visible_message("\The [user] starts to put \the [victim] into \the [src]!") + src.add_fingerprint(user) + if(do_after(user, 30 SECONDS, act_target = src) && 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.forceMove(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.forceMove(src.loc) + if (src.occupant.client) + src.occupant.client.eye = src.occupant.client.mob + src.occupant.client.perspective = MOB_PERSPECTIVE + src.occupant.forceMove(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_count = 3 + var/slab_type = /obj/item/reagent_containers/food/snacks/meat + var/slab_nutrition = src.occupant.nutrition / 15 + + // Some mobs have specific meat item types. + if(istype(src.occupant,/mob/living/simple_animal)) + var/mob/living/simple_animal/critter = src.occupant + if(critter.meat_amount) + slab_count = critter.meat_amount + if(critter.meat_type) + slab_type = critter.meat_type + else if(istype(src.occupant,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = occupant + slab_type = 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/reagent_containers/food/snacks/meat/new_meat = new slab_type(src, rand(3,8)) + if(istype(new_meat)) + 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/slab_count,1)) + + src.occupant.attack_log += "\[[time_stamp()]\] Was gibbed by [user]/[user.ckey]" //One shall not simply gib a mob unnoticed! + user.attack_log += "\[[time_stamp()]\] Gibbed [src.occupant]/[src.occupant.ckey]" + msg_admin_attack("[key_name_admin(user)] gibbed [src.occupant] ([src.occupant.ckey]) (JMP)",ckey=key_name(user),ckey_target=key_name(src.occupant)) + + src.occupant.ghostize() + + spawn(gib_time) + + src.operating = 0 + src.occupant.gib() + occupant = null + + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) + operating = 0 + for (var/obj/thing in contents) + // Todo: unify limbs and internal organs + // There's a chance that the gibber will fail to destroy some evidence. + if(istype(thing,/obj/item/organ) && prob(80)) + 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/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm index 9ff9d81758e..67e4d5c621d 100644 --- a/code/game/machinery/kitchen/smartfridge.dm +++ b/code/game/machinery/kitchen/smartfridge.dm @@ -1,457 +1,457 @@ -/* SmartFridge. Much todo -*/ -/obj/machinery/smartfridge - name = "\improper SmartFridge" - icon = 'icons/obj/vending.dmi' - icon_state = "smartfridge" - layer = 2.9 - density = 1 - anchored = 1 - use_power = 1 - idle_power_usage = 5 - active_power_usage = 100 - flags = NOREACT - var/global/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000. - var/icon_on = "smartfridge" - var/icon_off = "smartfridge-off" - var/icon_panel = "smartfridge-panel" - var/item_quants = list() - var/seconds_electrified = 0; - var/shoot_inventory = 0 - var/locked = 0 - var/scan_id = 1 - var/is_secure = 0 - var/machineselect = 0 - - var/cooling = 0 //Whether or not to vend products at the cooling temperature - var/heating = 0 //Whether or not to vend products at the heating temperature - var/cooling_temperature = T0C + 5 //Best temp for soda. - var/heating_temperature = T0C + 57 //Best temp for coffee. - - component_types = list( - /obj/item/circuitboard/smartfridge, - /obj/item/stock_parts/manipulator, - /obj/item/stock_parts/manipulator, - /obj/item/stock_parts/manipulator - ) - - var/datum/wires/smartfridge/wires = null - atmos_canpass = CANPASS_NEVER - -/obj/machinery/smartfridge/secure - is_secure = 1 - -/obj/machinery/smartfridge/Initialize() - . = ..() - if(is_secure) - wires = new/datum/wires/smartfridge/secure(src) - else - wires = new/datum/wires/smartfridge(src) - -/obj/machinery/smartfridge/Destroy() - qdel(wires) - wires = null - return ..() - -/obj/machinery/smartfridge/proc/accept_check(var/obj/item/O as obj) - if(istype(O,/obj/item/reagent_containers/food/snacks/grown/) || istype(O,/obj/item/seeds/)) - return 1 - return 0 - -/obj/machinery/smartfridge/foodheater - name = "\improper SmartHeater" - desc = "To keep the food warm!" - icon_state = "smartfridge_food" - icon_on = "smartfridge_food" - icon_off = "smartfridge_food-off" - -/obj/machinery/smartfridge/foodheater/accept_check(var/obj/item/O as obj) - if(istype(O,/obj/item/reagent_containers/food/snacks)) - return 1 - return 0 - -/obj/machinery/smartfridge/seeds - name = "\improper MegaSeed Storage" - desc = "When you need seeds fast!" - icon = 'icons/obj/vending.dmi' - icon_state = "nutrimat" - icon_on = "nutrimat" - icon_off = "nutrimat-off" - -/obj/machinery/smartfridge/seeds/accept_check(var/obj/item/O as obj) - if(istype(O,/obj/item/seeds/)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/extract - name = "\improper Slime Extract Storage" - desc = "A refrigerated storage unit for slime extracts" - req_access = list(access_research) - -/obj/machinery/smartfridge/secure/extract/Initialize() - . = ..() - new/obj/item/storage/bag/slimes(src) - -/obj/machinery/smartfridge/secure/extract/accept_check(var/obj/item/O as obj) - if(istype(O,/obj/item/slime_extract)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/medbay - name = "\improper Refrigerated Chemical Storage" - desc = "A refrigerated storage unit for storing medicine and chemicals." - icon_state = "smartfridge" //To fix the icon in the map editor. - icon_on = "smartfridge_chem" - req_one_access = list(access_medical,access_pharmacy) - -/obj/machinery/smartfridge/secure/medbay/accept_check(var/obj/item/O as obj) - if(istype(O,/obj/item/reagent_containers/glass/)) - return 1 - if(istype(O,/obj/item/storage/pill_bottle/)) - return 1 - if(istype(O,/obj/item/reagent_containers/pill/)) - return 1 - if(istype(O,/obj/item/reagent_containers/inhaler)) - return 1 - if(istype(O,/obj/item/reagent_containers/personal_inhaler_cartridge )) - return 1 - if(istype(O,/obj/item/reagent_containers/inhaler)) - return 1 - if(istype(O,/obj/item/reagent_containers/hypospray/autoinjector)) - return 1 - if(istype(O,/obj/item/personal_inhaler)) - return 1 - - return 0 - -/obj/machinery/smartfridge/secure/virology - name = "\improper Refrigerated Virus Storage" - desc = "A refrigerated storage unit for storing viral material." - req_access = list(access_virology) - icon_state = "smartfridge_virology" - icon_on = "smartfridge_virology" - icon_off = "smartfridge_virology-off" - -/obj/machinery/smartfridge/secure/virology/accept_check(var/obj/item/O as obj) - if(istype(O,/obj/item/reagent_containers/glass/beaker/vial/)) - return 1 - if(istype(O,/obj/item/virusdish/)) - return 1 - return 0 - -/obj/machinery/smartfridge/chemistry - name = "\improper Smart Chemical Storage" - desc = "A refrigerated storage unit for medicine and chemical storage." - -/obj/machinery/smartfridge/chemistry/accept_check(var/obj/item/O as obj) - if(istype(O,/obj/item/storage/pill_bottle) || istype(O,/obj/item/reagent_containers)) - return 1 - return 0 - -/obj/machinery/smartfridge/chemistry/virology - name = "\improper Smart Virus Storage" - desc = "A refrigerated storage unit for volatile sample storage." - - -/obj/machinery/smartfridge/drinks - name = "\improper Drink Showcase" - desc = "A refrigerated storage unit for tasty tasty alcohol." - cooling = TRUE - -/obj/machinery/smartfridge/drinks/accept_check(var/obj/item/O as obj) - if(istype(O,/obj/item/reagent_containers/glass) || istype(O,/obj/item/reagent_containers/food/drinks) || istype(O,/obj/item/reagent_containers/food/condiment)) - return 1 - -/obj/machinery/smartfridge/drying_rack - name = "\improper Drying Rack" - desc = "A machine for drying plants." - -/obj/machinery/smartfridge/drying_rack/accept_check(var/obj/item/O as obj) - if(istype(O, /obj/item/reagent_containers/food/snacks/)) - var/obj/item/reagent_containers/food/snacks/S = O - if (S.dried_type) - return 1 - return 0 - -/obj/machinery/smartfridge/drying_rack/machinery_process() - ..() - if (contents.len) - dry() - -/obj/machinery/smartfridge/drying_rack/proc/dry() - for(var/obj/item/reagent_containers/food/snacks/S in contents) - if(S.dry) continue - if(S.dried_type == S.type) - S.dry = 1 - item_quants[S.name]-- - S.name = "dried [S.name]" - S.color = "#AAAAAA" - S.forceMove(loc) - else - var/D = S.dried_type - new D(loc) - item_quants[S.name]-- - qdel(S) - return - return - -/obj/machinery/smartfridge/machinery_process() - if(stat & (BROKEN|NOPOWER)) - return - if(src.seconds_electrified > 0) - src.seconds_electrified-- - if(src.shoot_inventory && prob(2)) - src.throw_item() - if(cooling || heating) - - var/mod = cooling ? -1 : 1 - - for(var/obj/item/I in contents) - - if(!I.reagents) - continue - - var/r_temperature = I.reagents.get_temperature() - - if(mod == 1 && r_temperature <= heating_temperature) - continue - else if(mod == -1 && r_temperature <= cooling_temperature) - continue - - var/thermal_energy_change = 0 - - if(mod == 1) //GOING UP - thermal_energy_change = min(active_power_usage,I.reagents.get_thermal_energy_change(r_temperature,heating_temperature)) - else if (mod == -1) //GOING DOWN - thermal_energy_change = max(-active_power_usage,I.reagents.get_thermal_energy_change(r_temperature,cooling_temperature)) - - I.reagents.add_thermal_energy(thermal_energy_change) - use_power(active_power_usage) - - - -/obj/machinery/smartfridge/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/smartfridge/update_icon() - if(stat & (BROKEN|NOPOWER)) - icon_state = icon_off - else - icon_state = icon_on - -/******************* -* Item Adding -********************/ - -/obj/machinery/smartfridge/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(O.isscrewdriver()) - 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].") - cut_overlays() - if(panel_open) - add_overlay(icon_panel) - SSnanoui.update_uis(src) - return - - if(O.ismultitool()||O.iswirecutter()) - if(panel_open) - switch(input(user, "What would you like to select?", "Machine Debug Software") as null|anything in list("SmartHeater", "MegaSeed Storage", "Slime Extract Storage", "Refrigerated Chemical Storage", "Refrigerated Virus Storage", "Drink Showcase", "Drying Rack")) - if("SmartHeater") - new /obj/machinery/smartfridge/foodheater(loc, make_from=src) - qdel(src) - - if("MegaSeed Storage") - new /obj/machinery/smartfridge/seeds(loc, make_from=src) - qdel(src) - - if("Slime Extract Storage") - new /obj/machinery/smartfridge/secure/extract(loc, make_from=src) - qdel(src) - - if("Refrigerated Chemical Storage") - new /obj/machinery/smartfridge/secure/medbay(loc, make_from=src) - qdel(src) - - if("Refrigerated Virus Storage") - new /obj/machinery/smartfridge/secure/virology(loc, make_from=src) - qdel(src) - - if("Drink Showcase") - new /obj/machinery/smartfridge/drinks(loc, make_from=src) - qdel(src) - - if("Drying Rack") - new /obj/machinery/smartfridge/drying_rack(loc, make_from=src) - qdel(src) - - - attack_hand(user) - return - - - - if(stat & NOPOWER) - to_chat(user, "\The [src] is unpowered and useless.") - return - - if(accept_check(O)) - if(contents.len >= max_n_of_items) - to_chat(user, "\The [src] is full.") - return 1 - else - user.remove_from_mob(O) - O.forceMove(src) - if(item_quants[O.name]) - item_quants[O.name]++ - else - item_quants[O.name] = 1 - user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") - - SSnanoui.update_uis(src) - return - - if(istype(O, /obj/item/storage/bag) || istype(O, /obj/item/storage/box/produce)) - var/obj/item/storage/P = O - var/plants_loaded = 0 - for(var/obj/G in P.contents) - if(accept_check(G)) - if(contents.len >= max_n_of_items) - to_chat(user, "\The [src] is full.") - return 1 - else - P.remove_from_storage(G,src) - if(item_quants[G.name]) - item_quants[G.name]++ - else - item_quants[G.name] = 1 - plants_loaded++ - 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.") - - SSnanoui.update_uis(src) - - 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/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) - ui_interact(user) - -/******************* -* SmartFridge Menu -********************/ - -/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) - - var/data[0] - data["contents"] = null - data["electrified"] = seconds_electrified > 0 - data["shoot_inventory"] = shoot_inventory - data["locked"] = locked - data["secure"] = is_secure - - var/list/items[0] - for (var/i=1 to length(item_quants)) - var/K = item_quants[i] - var/count = item_quants[K] - if(count > 0) - items.Add(list(list("display_name" = html_encode(capitalize(K)), "vend" = i, "quantity" = count))) - - if(items.len > 0) - data["contents"] = items - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "smartfridge.tmpl", src.name, 400, 500) - ui.set_initial_data(data) - ui.open() - -/obj/machinery/smartfridge/Topic(href, href_list) - if(..()) return 0 - - var/mob/user = usr - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") - - src.add_fingerprint(user) - - if(href_list["close"]) - user.unset_machine() - ui.close() - return 0 - - if(href_list["vendItem"]) - var/index = text2num(href_list["vendItem"]) - var/amount = text2num(href_list["amount"]) - var/K = item_quants[index] - var/count = item_quants[K] - - // Sanity check, there are probably ways to press the button when it shouldn't be possible. - if(count > 0) - item_quants[K] = max(count - amount, 0) - - var/i = amount - for(var/obj/O in contents) - if(O.name == K) - O.forceMove(loc) - i-- - if(i <= 0) - return 1 - - return 1 - return 0 - -/obj/machinery/smartfridge/proc/throw_item() - var/obj/throw_item = null - var/mob/living/target = locate() in view(7,src) - if(!target) - return 0 - - for (var/O in item_quants) - if(item_quants[O] <= 0) //Try to use a record that actually has something to dump. - continue - - item_quants[O]-- - for(var/obj/T in contents) - if(T.name == O) - T.forceMove(src.loc) - throw_item = T - break - break - if(!throw_item) - return 0 - spawn(0) - throw_item.throw_at(target,16,3,src) - src.visible_message("[src] launches [throw_item.name] at [target.name]!") - return 1 - -/************************ -* Secure SmartFridges -*************************/ - -/obj/machinery/smartfridge/secure/Topic(href, href_list) - if(stat & (NOPOWER|BROKEN)) return 0 - if(usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) - if(!allowed(usr) && !emagged && locked != -1 && href_list["vendItem"]) - to_chat(usr, "Access denied.") - return 0 - return ..() +/* SmartFridge. Much todo +*/ +/obj/machinery/smartfridge + name = "\improper SmartFridge" + icon = 'icons/obj/vending.dmi' + icon_state = "smartfridge" + layer = 2.9 + density = 1 + anchored = 1 + use_power = 1 + idle_power_usage = 5 + active_power_usage = 100 + flags = NOREACT + var/global/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000. + var/icon_on = "smartfridge" + var/icon_off = "smartfridge-off" + var/icon_panel = "smartfridge-panel" + var/item_quants = list() + var/seconds_electrified = 0; + var/shoot_inventory = 0 + var/locked = 0 + var/scan_id = 1 + var/is_secure = 0 + var/machineselect = 0 + + var/cooling = 0 //Whether or not to vend products at the cooling temperature + var/heating = 0 //Whether or not to vend products at the heating temperature + var/cooling_temperature = T0C + 5 //Best temp for soda. + var/heating_temperature = T0C + 57 //Best temp for coffee. + + component_types = list( + /obj/item/circuitboard/smartfridge, + /obj/item/stock_parts/manipulator, + /obj/item/stock_parts/manipulator, + /obj/item/stock_parts/manipulator + ) + + var/datum/wires/smartfridge/wires = null + atmos_canpass = CANPASS_NEVER + +/obj/machinery/smartfridge/secure + is_secure = 1 + +/obj/machinery/smartfridge/Initialize() + . = ..() + if(is_secure) + wires = new/datum/wires/smartfridge/secure(src) + else + wires = new/datum/wires/smartfridge(src) + +/obj/machinery/smartfridge/Destroy() + qdel(wires) + wires = null + return ..() + +/obj/machinery/smartfridge/proc/accept_check(var/obj/item/O as obj) + if(istype(O,/obj/item/reagent_containers/food/snacks/grown/) || istype(O,/obj/item/seeds/)) + return 1 + return 0 + +/obj/machinery/smartfridge/foodheater + name = "\improper SmartHeater" + desc = "To keep the food warm!" + icon_state = "smartfridge_food" + icon_on = "smartfridge_food" + icon_off = "smartfridge_food-off" + +/obj/machinery/smartfridge/foodheater/accept_check(var/obj/item/O as obj) + if(istype(O,/obj/item/reagent_containers/food/snacks)) + return 1 + return 0 + +/obj/machinery/smartfridge/seeds + name = "\improper MegaSeed Storage" + desc = "When you need seeds fast!" + icon = 'icons/obj/vending.dmi' + icon_state = "nutrimat" + icon_on = "nutrimat" + icon_off = "nutrimat-off" + +/obj/machinery/smartfridge/seeds/accept_check(var/obj/item/O as obj) + if(istype(O,/obj/item/seeds/)) + return 1 + return 0 + +/obj/machinery/smartfridge/secure/extract + name = "\improper Slime Extract Storage" + desc = "A refrigerated storage unit for slime extracts" + req_access = list(access_research) + +/obj/machinery/smartfridge/secure/extract/Initialize() + . = ..() + new/obj/item/storage/bag/slimes(src) + +/obj/machinery/smartfridge/secure/extract/accept_check(var/obj/item/O as obj) + if(istype(O,/obj/item/slime_extract)) + return 1 + return 0 + +/obj/machinery/smartfridge/secure/medbay + name = "\improper Refrigerated Chemical Storage" + desc = "A refrigerated storage unit for storing medicine and chemicals." + icon_state = "smartfridge" //To fix the icon in the map editor. + icon_on = "smartfridge_chem" + req_one_access = list(access_medical,access_pharmacy) + +/obj/machinery/smartfridge/secure/medbay/accept_check(var/obj/item/O as obj) + if(istype(O,/obj/item/reagent_containers/glass/)) + return 1 + if(istype(O,/obj/item/storage/pill_bottle/)) + return 1 + if(istype(O,/obj/item/reagent_containers/pill/)) + return 1 + if(istype(O,/obj/item/reagent_containers/inhaler)) + return 1 + if(istype(O,/obj/item/reagent_containers/personal_inhaler_cartridge )) + return 1 + if(istype(O,/obj/item/reagent_containers/inhaler)) + return 1 + if(istype(O,/obj/item/reagent_containers/hypospray/autoinjector)) + return 1 + if(istype(O,/obj/item/personal_inhaler)) + return 1 + + return 0 + +/obj/machinery/smartfridge/secure/virology + name = "\improper Refrigerated Virus Storage" + desc = "A refrigerated storage unit for storing viral material." + req_access = list(access_virology) + icon_state = "smartfridge_virology" + icon_on = "smartfridge_virology" + icon_off = "smartfridge_virology-off" + +/obj/machinery/smartfridge/secure/virology/accept_check(var/obj/item/O as obj) + if(istype(O,/obj/item/reagent_containers/glass/beaker/vial/)) + return 1 + if(istype(O,/obj/item/virusdish/)) + return 1 + return 0 + +/obj/machinery/smartfridge/chemistry + name = "\improper Smart Chemical Storage" + desc = "A refrigerated storage unit for medicine and chemical storage." + +/obj/machinery/smartfridge/chemistry/accept_check(var/obj/item/O as obj) + if(istype(O,/obj/item/storage/pill_bottle) || istype(O,/obj/item/reagent_containers)) + return 1 + return 0 + +/obj/machinery/smartfridge/chemistry/virology + name = "\improper Smart Virus Storage" + desc = "A refrigerated storage unit for volatile sample storage." + + +/obj/machinery/smartfridge/drinks + name = "\improper Drink Showcase" + desc = "A refrigerated storage unit for tasty tasty alcohol." + cooling = TRUE + +/obj/machinery/smartfridge/drinks/accept_check(var/obj/item/O as obj) + if(istype(O,/obj/item/reagent_containers/glass) || istype(O,/obj/item/reagent_containers/food/drinks) || istype(O,/obj/item/reagent_containers/food/condiment)) + return 1 + +/obj/machinery/smartfridge/drying_rack + name = "\improper Drying Rack" + desc = "A machine for drying plants." + +/obj/machinery/smartfridge/drying_rack/accept_check(var/obj/item/O as obj) + if(istype(O, /obj/item/reagent_containers/food/snacks/)) + var/obj/item/reagent_containers/food/snacks/S = O + if (S.dried_type) + return 1 + return 0 + +/obj/machinery/smartfridge/drying_rack/machinery_process() + ..() + if (contents.len) + dry() + +/obj/machinery/smartfridge/drying_rack/proc/dry() + for(var/obj/item/reagent_containers/food/snacks/S in contents) + if(S.dry) continue + if(S.dried_type == S.type) + S.dry = 1 + item_quants[S.name]-- + S.name = "dried [S.name]" + S.color = "#AAAAAA" + S.forceMove(loc) + else + var/D = S.dried_type + new D(loc) + item_quants[S.name]-- + qdel(S) + return + return + +/obj/machinery/smartfridge/machinery_process() + if(stat & (BROKEN|NOPOWER)) + return + if(src.seconds_electrified > 0) + src.seconds_electrified-- + if(src.shoot_inventory && prob(2)) + src.throw_item() + if(cooling || heating) + + var/mod = cooling ? -1 : 1 + + for(var/obj/item/I in contents) + + if(!I.reagents) + continue + + var/r_temperature = I.reagents.get_temperature() + + if(mod == 1 && r_temperature <= heating_temperature) + continue + else if(mod == -1 && r_temperature <= cooling_temperature) + continue + + var/thermal_energy_change = 0 + + if(mod == 1) //GOING UP + thermal_energy_change = min(active_power_usage,I.reagents.get_thermal_energy_change(r_temperature,heating_temperature)) + else if (mod == -1) //GOING DOWN + thermal_energy_change = max(-active_power_usage,I.reagents.get_thermal_energy_change(r_temperature,cooling_temperature)) + + I.reagents.add_thermal_energy(thermal_energy_change) + use_power(active_power_usage) + + + +/obj/machinery/smartfridge/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/smartfridge/update_icon() + if(stat & (BROKEN|NOPOWER)) + icon_state = icon_off + else + icon_state = icon_on + +/******************* +* Item Adding +********************/ + +/obj/machinery/smartfridge/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(O.isscrewdriver()) + 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].") + cut_overlays() + if(panel_open) + add_overlay(icon_panel) + SSnanoui.update_uis(src) + return + + if(O.ismultitool()||O.iswirecutter()) + if(panel_open) + switch(input(user, "What would you like to select?", "Machine Debug Software") as null|anything in list("SmartHeater", "MegaSeed Storage", "Slime Extract Storage", "Refrigerated Chemical Storage", "Refrigerated Virus Storage", "Drink Showcase", "Drying Rack")) + if("SmartHeater") + new /obj/machinery/smartfridge/foodheater(loc, make_from=src) + qdel(src) + + if("MegaSeed Storage") + new /obj/machinery/smartfridge/seeds(loc, make_from=src) + qdel(src) + + if("Slime Extract Storage") + new /obj/machinery/smartfridge/secure/extract(loc, make_from=src) + qdel(src) + + if("Refrigerated Chemical Storage") + new /obj/machinery/smartfridge/secure/medbay(loc, make_from=src) + qdel(src) + + if("Refrigerated Virus Storage") + new /obj/machinery/smartfridge/secure/virology(loc, make_from=src) + qdel(src) + + if("Drink Showcase") + new /obj/machinery/smartfridge/drinks(loc, make_from=src) + qdel(src) + + if("Drying Rack") + new /obj/machinery/smartfridge/drying_rack(loc, make_from=src) + qdel(src) + + + attack_hand(user) + return + + + + if(stat & NOPOWER) + to_chat(user, "\The [src] is unpowered and useless.") + return + + if(accept_check(O)) + if(contents.len >= max_n_of_items) + to_chat(user, "\The [src] is full.") + return 1 + else + user.remove_from_mob(O) + O.forceMove(src) + if(item_quants[O.name]) + item_quants[O.name]++ + else + item_quants[O.name] = 1 + user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") + + SSnanoui.update_uis(src) + return + + if(istype(O, /obj/item/storage/bag) || istype(O, /obj/item/storage/box/produce)) + var/obj/item/storage/P = O + var/plants_loaded = 0 + for(var/obj/G in P.contents) + if(accept_check(G)) + if(contents.len >= max_n_of_items) + to_chat(user, "\The [src] is full.") + return 1 + else + P.remove_from_storage(G,src) + if(item_quants[G.name]) + item_quants[G.name]++ + else + item_quants[G.name] = 1 + plants_loaded++ + 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.") + + SSnanoui.update_uis(src) + + 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/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) + ui_interact(user) + +/******************* +* SmartFridge Menu +********************/ + +/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + user.set_machine(src) + + var/data[0] + data["contents"] = null + data["electrified"] = seconds_electrified > 0 + data["shoot_inventory"] = shoot_inventory + data["locked"] = locked + data["secure"] = is_secure + + var/list/items[0] + for (var/i=1 to length(item_quants)) + var/K = item_quants[i] + var/count = item_quants[K] + if(count > 0) + items.Add(list(list("display_name" = html_encode(capitalize(K)), "vend" = i, "quantity" = count))) + + if(items.len > 0) + data["contents"] = items + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + ui = new(user, src, ui_key, "smartfridge.tmpl", src.name, 400, 500) + ui.set_initial_data(data) + ui.open() + +/obj/machinery/smartfridge/Topic(href, href_list) + if(..()) return 0 + + var/mob/user = usr + var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") + + src.add_fingerprint(user) + + if(href_list["close"]) + user.unset_machine() + ui.close() + return 0 + + if(href_list["vendItem"]) + var/index = text2num(href_list["vendItem"]) + var/amount = text2num(href_list["amount"]) + var/K = item_quants[index] + var/count = item_quants[K] + + // Sanity check, there are probably ways to press the button when it shouldn't be possible. + if(count > 0) + item_quants[K] = max(count - amount, 0) + + var/i = amount + for(var/obj/O in contents) + if(O.name == K) + O.forceMove(loc) + i-- + if(i <= 0) + return 1 + + return 1 + return 0 + +/obj/machinery/smartfridge/proc/throw_item() + var/obj/throw_item = null + var/mob/living/target = locate() in view(7,src) + if(!target) + return 0 + + for (var/O in item_quants) + if(item_quants[O] <= 0) //Try to use a record that actually has something to dump. + continue + + item_quants[O]-- + for(var/obj/T in contents) + if(T.name == O) + T.forceMove(src.loc) + throw_item = T + break + break + if(!throw_item) + return 0 + spawn(0) + throw_item.throw_at(target,16,3,src) + src.visible_message("[src] launches [throw_item.name] at [target.name]!") + return 1 + +/************************ +* Secure SmartFridges +*************************/ + +/obj/machinery/smartfridge/secure/Topic(href, href_list) + if(stat & (NOPOWER|BROKEN)) return 0 + if(usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) + if(!allowed(usr) && !emagged && locked != -1 && href_list["vendItem"]) + to_chat(usr, "Access denied.") + return 0 + return ..() diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index bdfba08910c..2c284681dcd 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -1,74 +1,74 @@ -// 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.dmi' - icon_state = "light-p" - anchored = 1.0 - var/on = 1 - var/area/area = null - var/otherarea = null - // luminosity = 1 - -/obj/machinery/light_switch/Initialize() - . = ..() - src.area = get_area(src) - - if(otherarea) - src.area = locate(text2path("/area/[otherarea]")) - - if(!name) - name = "light switch ([area.name])" - - src.on = src.area.lightswitch - update_icon() - -/obj/machinery/light_switch/update_icon() - cut_overlays() - if(!(stat & NOPOWER)) - holographic_overlay(src, icon, "light[on]-overlay") - if (!light_range || light_color != on ? "#82ff4c" : "#f86060") - set_light(2, 0.3, on ? "#82ff4c" : "#f86060") - else if (light_range) - set_light(FALSE) - -/obj/machinery/light_switch/examine(mob/user) - if(..(user, 1)) - to_chat(user, "A light switch. It is [on? "on" : "off"].") - -/obj/machinery/light_switch/attack_hand(mob/user) - playsound(src, "switch", 30) - on = !on - - area.lightswitch = on - - for(var/obj/machinery/light_switch/L in area) - L.on = on - L.update_icon() - - for (var/obj/machinery/light/L in area) - if (on) - L.stat &= ~POWEROFF - else - L.stat |= POWEROFF - - L.update() - -/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) +// 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.dmi' + icon_state = "light-p" + anchored = 1.0 + var/on = 1 + var/area/area = null + var/otherarea = null + // luminosity = 1 + +/obj/machinery/light_switch/Initialize() + . = ..() + src.area = get_area(src) + + if(otherarea) + src.area = locate(text2path("/area/[otherarea]")) + + if(!name) + name = "light switch ([area.name])" + + src.on = src.area.lightswitch + update_icon() + +/obj/machinery/light_switch/update_icon() + cut_overlays() + if(!(stat & NOPOWER)) + holographic_overlay(src, icon, "light[on]-overlay") + if (!light_range || light_color != on ? "#82ff4c" : "#f86060") + set_light(2, 0.3, on ? "#82ff4c" : "#f86060") + else if (light_range) + set_light(FALSE) + +/obj/machinery/light_switch/examine(mob/user) + if(..(user, 1)) + to_chat(user, "A light switch. It is [on? "on" : "off"].") + +/obj/machinery/light_switch/attack_hand(mob/user) + playsound(src, "switch", 30) + on = !on + + area.lightswitch = on + + for(var/obj/machinery/light_switch/L in area) + L.on = on + L.update_icon() + + for (var/obj/machinery/light/L in area) + if (on) + L.stat &= ~POWEROFF + else + L.stat |= POWEROFF + + L.update() + +/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) diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index d38422b2f3d..78eb5356d61 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -1,421 +1,421 @@ -/* -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: - use_power (num) - current state of auto power use. - Possible Values: - 0 -- no auto power use - 1 -- machine is using power at its idle power level - 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' - - auto_use_power() 'game/machinery/machine.dm' - This proc determines how power mode power is deducted by the machine. - 'auto_use_power()' is called by the 'master_controller' game_controller every - tick. - - Return Value: - return:1 -- if object is powered - return:0 -- if object is not powered. - - Default definition uses 'use_power', 'power_channel', 'active_power_usage', - 'idle_power_usage', 'powered()', and 'use_power()' implement behavior. - - powered(chan = EQUIP) 'modules/power/power.dm' - Checks to see if area that contains the object has power available for power - channel given in 'chan'. - - use_power(amount, chan=EQUIP, autocalled) 'modules/power/power.dm' - Deducts 'amount' from the power channel 'chan' of the area that contains the object. - If it's autocalled then everything is normal, if something else calls use_power we are going to - need to recalculate the power two ticks in a row. - - power_change() 'modules/power/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 = 10 - - var/stat = 0 - var/emagged = 0 - var/use_power = 1 - //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 - /* List of types that should be spawned as component_parts for this machine. - Structure: - type -> num_objects - - num_objects is optional, and will be treated as 1 if omitted. - - example: - component_types = list( - /obj/foo/bar, - /obj/baz = 2 - ) - */ - var/list/component_types - 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 = 0 - var/global/gl_uid = 1 - var/interact_offline = 0 // Can the machine be interacted with while de-powered. - var/printing = 0 // Is this machine currently printing anything? - var/tmp/machinery_processing = FALSE // Are we process()ing in SSmachinery? - var/has_special_power_checks = FALSE // If true, call auto_use_power instead of doing it all in SSmachinery. - var/clicksound //played sound on usage - var/clickvol = 40 //volume - -/obj/machinery/Initialize(mapload, d = 0, populate_components = TRUE) - . = ..() - if(d) - set_dir(d) - - if (populate_components && component_types) - component_parts = list() - for (var/type in component_types) - var/count = component_types[type] - if (count > 1) - for (var/i in 1 to count) - component_parts += new type(src) - else - component_parts += new type(src) - - if(component_parts.len) - RefreshParts() - - add_machine(src) - -/obj/machinery/Destroy() - remove_machine(src, TRUE) - 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. - component_parts -= A - - return ..() - -/obj/machinery/proc/machinery_process() //If you dont use process or power why are you here - if(!(use_power || idle_power_usage || active_power_usage)) - return PROCESS_KILL - - return M_NO_PROCESS - -/obj/machinery/emp_act(severity) - if(use_power && stat == 0) - use_power(7500/severity) - - var/obj/effect/overlay/pulse2 = new(src.loc) - pulse2.icon = 'icons/effects/effects.dmi' - pulse2.icon_state = "empdisable" - pulse2.name = "emp sparks" - pulse2.anchored = 1 - pulse2.set_dir(pick(cardinal)) - - QDEL_IN(pulse2, 10) - ..() - -/obj/machinery/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(25)) - qdel(src) - return - else - return - -//sets the use_power var and then forces an area power update -/obj/machinery/proc/update_use_power(var/new_use_power) - use_power = new_use_power - -/obj/machinery/proc/auto_use_power() - if(!powered(power_channel)) - return 0 - if(src.use_power == 1) - use_power(idle_power_usage,power_channel, 1) - else if(src.use_power >= 2) - use_power(active_power_usage,power_channel, 1) - return 1 - -/proc/is_operable(var/obj/machinery/M, var/mob/user) - return istype(M) && M.operable() - -/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)) - -/obj/machinery/CanUseTopic(var/mob/user) - if(stat & BROKEN) - return STATUS_CLOSE - - if(!interact_offline && (stat & NOPOWER)) - return STATUS_CLOSE - - return ..() - -/obj/machinery/CouldUseTopic(var/mob/user) - ..() - if(istype (user, /mob/living/carbon)) - playsound(src, clicksound, clickvol) - 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 src.attack_hand(user) - else - return src.attack_hand(user) - -/obj/machinery/attack_hand(mob/user as mob) - if(inoperable(MAINT)) - return 1 - if(user.lying || user.stat) - return 1 - if ( ! (istype(usr, /mob/living/carbon/human) || \ - istype(usr, /mob/living/silicon))) - to_chat(usr, "You don't have the dexterity to do this!") - return 1 -/* - //distance checks are made by atom/proc/DblClick - if ((get_dist(src, user) > 1 || !istype(src.loc, /turf)) && !istype(user, /mob/living/silicon)) - return 1 -*/ - if (ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.getBrainLoss() >= 60) - visible_message("[H] stares cluelessly at [src] and drools.") - return 1 - else if(prob(H.getBrainLoss())) - to_chat(user, "You momentarily forget how to use [src].") - return 1 - - src.add_fingerprint(user) - - return ..() - -/obj/machinery/proc/RefreshParts() - -/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] [msg]", 2) - -/obj/machinery/proc/ping(text=null) - if (!text) - text = "\The [src] pings." - - state(text, "blue") - playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) - -/obj/machinery/proc/pingx3(text=null) - if (!text) - text = "\The [src] pings." - - state(text, "blue") - playsound(src.loc, 'sound/machines/pingx3.ogg', 50, 0) - -/obj/machinery/proc/buzz(text=null) - if (!text) - text = "\The [src] buzzes." - - state(text, "blue") - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) //TODO: Check if that one is the correct sound - -/obj/machinery/proc/shock(mob/user, prb) - if(inoperable()) - return 0 - if(!prob(prb)) - return 0 - spark(src, 5, alldirs) - 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_deconstruction_crowbar(var/mob/user, var/obj/item/C) - if(!istype(C) || !C.iscrowbar()) - return 0 - if(!panel_open) - return 0 - . = dismantle() - -/obj/machinery/proc/default_deconstruction_screwdriver(var/mob/user, var/obj/item/S) - if(!istype(S) || !S.isscrewdriver()) - return 0 - playsound(src.loc, 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/default_part_replacement(var/mob/user, var/obj/item/storage/part_replacer/R) - if(!istype(R)) - return 0 - if(!LAZYLEN(component_parts)) - return 0 - - if(panel_open) - var/obj/item/circuitboard/CB = locate(/obj/item/circuitboard) in component_parts - var/P - for(var/obj/item/reagent_containers/glass/G in component_parts) - for(var/D in CB.req_components) - var/T = text2path(D) - if(ispath(G.type, T)) - P = T - break - for(var/obj/item/reagent_containers/glass/B in R.contents) - if(B.reagents && B.reagents.total_volume > 0) continue - if(istype(B, P) && istype(G, P)) - if(B.volume > G.volume) - R.remove_from_storage(B, src) - R.handle_item_insertion(G, 1) - component_parts -= G - component_parts += B - B.forceMove(src) - to_chat(user, "[G.name] replaced with [B.name].") - break - for(var/obj/item/stock_parts/A in component_parts) - for(var/D in CB.req_components) - var/T = text2path(D) - if(ispath(A.type, T)) - P = T - break - for(var/obj/item/stock_parts/B in R.contents) - if(istype(B, P) && istype(A, P)) - if(B.rating > A.rating) - R.remove_from_storage(B, src) - R.handle_item_insertion(A, 1) - component_parts -= A - component_parts += B - B.forceMove(src) - to_chat(user, "[A.name] replaced with [B.name].") - break - RefreshParts() - update_icon() - else - to_chat(user, "Following parts detected in the machine:") - for(var/var/obj/item/C in component_parts) - to_chat(user, " [C.name]") - return 1 - -/obj/machinery/proc/dismantle() - playsound(loc, 'sound/items/Crowbar.ogg', 50, 1) - var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(loc) - M.set_dir(src.dir) - M.state = 3 - M.icon_state = "blueprint_1" - for(var/obj/I in component_parts) - I.forceMove(loc) - qdel(src) - return 1 - -/obj/machinery/proc/print(var/obj/paper, var/play_sound = 1, var/print_sfx = 'sound/items/polaroid1.ogg', var/print_delay = 10) - if( printing ) - return 0 - - printing = 1 - - if (play_sound) - playsound(src.loc, print_sfx, 50, 1) - - visible_message("[src] rattles to life and spits out a paper titled [paper].") - - addtimer(CALLBACK(src, .proc/print_move_paper, paper), print_delay) - - return 1 - -/obj/machinery/proc/print_move_paper(obj/paper) - paper.forceMove(loc) - printing = FALSE +/* +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: + use_power (num) + current state of auto power use. + Possible Values: + 0 -- no auto power use + 1 -- machine is using power at its idle power level + 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' + + auto_use_power() 'game/machinery/machine.dm' + This proc determines how power mode power is deducted by the machine. + 'auto_use_power()' is called by the 'master_controller' game_controller every + tick. + + Return Value: + return:1 -- if object is powered + return:0 -- if object is not powered. + + Default definition uses 'use_power', 'power_channel', 'active_power_usage', + 'idle_power_usage', 'powered()', and 'use_power()' implement behavior. + + powered(chan = EQUIP) 'modules/power/power.dm' + Checks to see if area that contains the object has power available for power + channel given in 'chan'. + + use_power(amount, chan=EQUIP, autocalled) 'modules/power/power.dm' + Deducts 'amount' from the power channel 'chan' of the area that contains the object. + If it's autocalled then everything is normal, if something else calls use_power we are going to + need to recalculate the power two ticks in a row. + + power_change() 'modules/power/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 = 10 + + var/stat = 0 + var/emagged = 0 + var/use_power = 1 + //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 + /* List of types that should be spawned as component_parts for this machine. + Structure: + type -> num_objects + + num_objects is optional, and will be treated as 1 if omitted. + + example: + component_types = list( + /obj/foo/bar, + /obj/baz = 2 + ) + */ + var/list/component_types + 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 = 0 + var/global/gl_uid = 1 + var/interact_offline = 0 // Can the machine be interacted with while de-powered. + var/printing = 0 // Is this machine currently printing anything? + var/tmp/machinery_processing = FALSE // Are we process()ing in SSmachinery? + var/has_special_power_checks = FALSE // If true, call auto_use_power instead of doing it all in SSmachinery. + var/clicksound //played sound on usage + var/clickvol = 40 //volume + +/obj/machinery/Initialize(mapload, d = 0, populate_components = TRUE) + . = ..() + if(d) + set_dir(d) + + if (populate_components && component_types) + component_parts = list() + for (var/type in component_types) + var/count = component_types[type] + if (count > 1) + for (var/i in 1 to count) + component_parts += new type(src) + else + component_parts += new type(src) + + if(component_parts.len) + RefreshParts() + + add_machine(src) + +/obj/machinery/Destroy() + remove_machine(src, TRUE) + 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. + component_parts -= A + + return ..() + +/obj/machinery/proc/machinery_process() //If you dont use process or power why are you here + if(!(use_power || idle_power_usage || active_power_usage)) + return PROCESS_KILL + + return M_NO_PROCESS + +/obj/machinery/emp_act(severity) + if(use_power && stat == 0) + use_power(7500/severity) + + var/obj/effect/overlay/pulse2 = new(src.loc) + pulse2.icon = 'icons/effects/effects.dmi' + pulse2.icon_state = "empdisable" + pulse2.name = "emp sparks" + pulse2.anchored = 1 + pulse2.set_dir(pick(cardinal)) + + QDEL_IN(pulse2, 10) + ..() + +/obj/machinery/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(25)) + qdel(src) + return + else + return + +//sets the use_power var and then forces an area power update +/obj/machinery/proc/update_use_power(var/new_use_power) + use_power = new_use_power + +/obj/machinery/proc/auto_use_power() + if(!powered(power_channel)) + return 0 + if(src.use_power == 1) + use_power(idle_power_usage,power_channel, 1) + else if(src.use_power >= 2) + use_power(active_power_usage,power_channel, 1) + return 1 + +/proc/is_operable(var/obj/machinery/M, var/mob/user) + return istype(M) && M.operable() + +/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)) + +/obj/machinery/CanUseTopic(var/mob/user) + if(stat & BROKEN) + return STATUS_CLOSE + + if(!interact_offline && (stat & NOPOWER)) + return STATUS_CLOSE + + return ..() + +/obj/machinery/CouldUseTopic(var/mob/user) + ..() + if(istype (user, /mob/living/carbon)) + playsound(src, clicksound, clickvol) + 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 src.attack_hand(user) + else + return src.attack_hand(user) + +/obj/machinery/attack_hand(mob/user as mob) + if(inoperable(MAINT)) + return 1 + if(user.lying || user.stat) + return 1 + if ( ! (istype(usr, /mob/living/carbon/human) || \ + istype(usr, /mob/living/silicon))) + to_chat(usr, "You don't have the dexterity to do this!") + return 1 +/* + //distance checks are made by atom/proc/DblClick + if ((get_dist(src, user) > 1 || !istype(src.loc, /turf)) && !istype(user, /mob/living/silicon)) + return 1 +*/ + if (ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.getBrainLoss() >= 60) + visible_message("[H] stares cluelessly at [src] and drools.") + return 1 + else if(prob(H.getBrainLoss())) + to_chat(user, "You momentarily forget how to use [src].") + return 1 + + src.add_fingerprint(user) + + return ..() + +/obj/machinery/proc/RefreshParts() + +/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] [msg]", 2) + +/obj/machinery/proc/ping(text=null) + if (!text) + text = "\The [src] pings." + + state(text, "blue") + playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) + +/obj/machinery/proc/pingx3(text=null) + if (!text) + text = "\The [src] pings." + + state(text, "blue") + playsound(src.loc, 'sound/machines/pingx3.ogg', 50, 0) + +/obj/machinery/proc/buzz(text=null) + if (!text) + text = "\The [src] buzzes." + + state(text, "blue") + playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) //TODO: Check if that one is the correct sound + +/obj/machinery/proc/shock(mob/user, prb) + if(inoperable()) + return 0 + if(!prob(prb)) + return 0 + spark(src, 5, alldirs) + 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_deconstruction_crowbar(var/mob/user, var/obj/item/C) + if(!istype(C) || !C.iscrowbar()) + return 0 + if(!panel_open) + return 0 + . = dismantle() + +/obj/machinery/proc/default_deconstruction_screwdriver(var/mob/user, var/obj/item/S) + if(!istype(S) || !S.isscrewdriver()) + return 0 + playsound(src.loc, 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/default_part_replacement(var/mob/user, var/obj/item/storage/part_replacer/R) + if(!istype(R)) + return 0 + if(!LAZYLEN(component_parts)) + return 0 + + if(panel_open) + var/obj/item/circuitboard/CB = locate(/obj/item/circuitboard) in component_parts + var/P + for(var/obj/item/reagent_containers/glass/G in component_parts) + for(var/D in CB.req_components) + var/T = text2path(D) + if(ispath(G.type, T)) + P = T + break + for(var/obj/item/reagent_containers/glass/B in R.contents) + if(B.reagents && B.reagents.total_volume > 0) continue + if(istype(B, P) && istype(G, P)) + if(B.volume > G.volume) + R.remove_from_storage(B, src) + R.handle_item_insertion(G, 1) + component_parts -= G + component_parts += B + B.forceMove(src) + to_chat(user, "[G.name] replaced with [B.name].") + break + for(var/obj/item/stock_parts/A in component_parts) + for(var/D in CB.req_components) + var/T = text2path(D) + if(ispath(A.type, T)) + P = T + break + for(var/obj/item/stock_parts/B in R.contents) + if(istype(B, P) && istype(A, P)) + if(B.rating > A.rating) + R.remove_from_storage(B, src) + R.handle_item_insertion(A, 1) + component_parts -= A + component_parts += B + B.forceMove(src) + to_chat(user, "[A.name] replaced with [B.name].") + break + RefreshParts() + update_icon() + else + to_chat(user, "Following parts detected in the machine:") + for(var/var/obj/item/C in component_parts) + to_chat(user, " [C.name]") + return 1 + +/obj/machinery/proc/dismantle() + playsound(loc, 'sound/items/Crowbar.ogg', 50, 1) + var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(loc) + M.set_dir(src.dir) + M.state = 3 + M.icon_state = "blueprint_1" + for(var/obj/I in component_parts) + I.forceMove(loc) + qdel(src) + return 1 + +/obj/machinery/proc/print(var/obj/paper, var/play_sound = 1, var/print_sfx = 'sound/items/polaroid1.ogg', var/print_delay = 10) + if( printing ) + return 0 + + printing = 1 + + if (play_sound) + playsound(src.loc, print_sfx, 50, 1) + + visible_message("[src] rattles to life and spits out a paper titled [paper].") + + addtimer(CALLBACK(src, .proc/print_move_paper, paper), print_delay) + + return 1 + +/obj/machinery/proc/print_move_paper(obj/paper) + paper.forceMove(loc) + printing = FALSE diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index c678aef8441..cd34b70ccc4 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -1,404 +1,404 @@ -// 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." - level = 1 // underfloor - layer = 2.5 - anchored = 1 - use_power = 1 - 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 - - New() - ..() - var/turf/T = loc - hide(!T.is_plating()) - center = T - - spawn(10) // must wait for map loading to finish - if(SSradio) - SSradio.add_object(src, freq, RADIO_MAGNETS) - - spawn() - magnetic_process() - - // update the invisibility and icon - hide(var/intact) - invisibility = intact ? 101 : 0 - updateicon() - - // update the icon_state - proc/updateicon() - 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]" - - 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) - - - - 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() - - - - 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) - use_power = 2 - active_power_usage = electricity_level*15 - else - use_power = 0 - - - // 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) - */ - - updateicon() - - - 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 & CONDUCT)) - 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(SSradio) - SSradio.remove_object(src, freq) - return ..() - -/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 = 1 - anchored = 1.0 - use_power = 1 - 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 - - - Initialize() - . = ..() - - if(autolink) - . = INITIALIZE_HINT_LATELOAD - radio_connection = SSradio.add_object(src, frequency, RADIO_MAGNETS) - - if(path) // check for default path - filter_path() // renders rpath - - LateInitialize() - for(var/obj/machinery/magnetic_module/M in SSmachinery.all_machines) - if(M.freq == frequency && M.code == code) - magnets += M - - process() - if(magnets.len == 0 && autolink) - for(var/obj/machinery/magnetic_module/M in SSmachinery.all_machines) - if(M.freq == frequency && M.code == code) - magnets += M - - - attack_ai(mob/user as mob) - return src.attack_hand(user) - - 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") - - Topic(href, href_list) - if(stat & (BROKEN|NOPOWER)) - return - usr.set_machine(src) - 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 = 1 // 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, 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(input(usr, "Please define a new path!",,path) as text|null) - 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() - - 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 = 1 // 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, filter = RADIO_MAGNETS) - - if(speed == 10) - sleep(1) - else - sleep(12-speed) - - looping = 0 - - - 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(SSradio) - SSradio.remove_object(src, frequency) - return ..() +// 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." + level = 1 // underfloor + layer = 2.5 + anchored = 1 + use_power = 1 + 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 + + New() + ..() + var/turf/T = loc + hide(!T.is_plating()) + center = T + + spawn(10) // must wait for map loading to finish + if(SSradio) + SSradio.add_object(src, freq, RADIO_MAGNETS) + + spawn() + magnetic_process() + + // update the invisibility and icon + hide(var/intact) + invisibility = intact ? 101 : 0 + updateicon() + + // update the icon_state + proc/updateicon() + 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]" + + 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) + + + + 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() + + + + 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) + use_power = 2 + active_power_usage = electricity_level*15 + else + use_power = 0 + + + // 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) + */ + + updateicon() + + + 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 & CONDUCT)) + 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(SSradio) + SSradio.remove_object(src, freq) + return ..() + +/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 = 1 + anchored = 1.0 + use_power = 1 + 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 + + + Initialize() + . = ..() + + if(autolink) + . = INITIALIZE_HINT_LATELOAD + radio_connection = SSradio.add_object(src, frequency, RADIO_MAGNETS) + + if(path) // check for default path + filter_path() // renders rpath + + LateInitialize() + for(var/obj/machinery/magnetic_module/M in SSmachinery.all_machines) + if(M.freq == frequency && M.code == code) + magnets += M + + process() + if(magnets.len == 0 && autolink) + for(var/obj/machinery/magnetic_module/M in SSmachinery.all_machines) + if(M.freq == frequency && M.code == code) + magnets += M + + + attack_ai(mob/user as mob) + return src.attack_hand(user) + + 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") + + Topic(href, href_list) + if(stat & (BROKEN|NOPOWER)) + return + usr.set_machine(src) + 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 = 1 // 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, 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(input(usr, "Please define a new path!",,path) as text|null) + 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() + + 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 = 1 // 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, filter = RADIO_MAGNETS) + + if(speed == 10) + sleep(1) + else + sleep(12-speed) + + looping = 0 + + + 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(SSradio) + SSradio.remove_object(src, frequency) + return ..() diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm index f7c5b561c87..8516066c666 100644 --- a/code/game/machinery/mass_driver.dm +++ b/code/game/machinery/mass_driver.dm @@ -1,169 +1,169 @@ -//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 = 1.0 - use_power = 1 - idle_power_usage = 2 - active_power_usage = 50 - - 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. - var/_wifi_id - var/datum/wifi/receiver/button/mass_driver/wifi_receiver - -/obj/machinery/mass_driver/Initialize() - . = ..() - if(_wifi_id) - wifi_receiver = new(_wifi_id, src) - -/obj/machinery/mass_driver/Destroy() - qdel(wifi_receiver) - wifi_receiver = null - 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) - 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) - -/obj/machinery/mass_driver/attackby(obj/item/W, mob/user) - - if(W.iswrench()) - if(!anchored) - playsound(src.loc, W.usesound, 75, 1) - user.visible_message("[user.name] secures [src] to the floor.", \ - "You secure the external reinforcing bolts to the floor.", \ - "You hear a ratchet") - src.anchored = 1 - else - playsound(src.loc, W.usesound, 75, 1) - user.visible_message("[user.name] unsecures [src] from the floor.", \ - "You unsecure the external reinforcing bolts from the floor.", \ - "You hear a ratchet") - src.anchored = 0 - -/obj/machinery/mass_driver/verb/rotate() - set name = "Rotate" - set category = "Object" - set src in oview(1) - - if (src.anchored || usr:stat) - to_chat(usr, "It is fastened to the floor!") - return 0 - src.set_dir(turn(src.dir, 90)) - return 1 - -/obj/item/mass_driver_diy - name = "Mass Driver Kit" - desc = "A do-it-yourself kit for constructing the finest of mass drivers." - icon = 'icons/obj/storage.dmi' - icon_state = "inf_box" - item_state = "syringe_kit" - -/obj/item/mass_driver_diy/attack_self(mob/user) - to_chat(user, "You start piecing together the kit...") - if(do_after(user, 80)) - var/master_id = "[user.name] - [pick("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")][rand(100,999)]" - var/obj/machinery/mass_driver/mining/R = new /obj/machinery/mass_driver/mining(user.loc) - R.id = master_id - R.power = 2.0 - var/obj/item/mass_driver_button/B = new /obj/item/mass_driver_button(user.loc) - B.id = master_id - user.visible_message("[user] assembles \a [R].\ - ", "You assemble \a [R].") - R.add_fingerprint(user) - qdel(src) - -/obj/item/mass_driver_button - name = "mass driver button" - desc = "An unscrewed mass driver button." - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - var/id - -/obj/item/mass_driver_button/afterattack(var/atom/A, var/mob/user, var/adjacent, var/clickparams) - if (!adjacent) - return - - //must place on a wall and user must not be inside a closet/mecha/whatever - var/turf/W = A - if (!iswall(W) || !isturf(user.loc)) - to_chat(user, "You can't place this here!") - return - - var/placement_dir = get_dir(user, W) - if (!(placement_dir in cardinal)) - to_chat(user, "You must stand directly in front of the wall you wish to place that on.") - return - - //just check if there is a poster on or adjacent to the wall - var/stuff_on_wall = 0 - if (locate(/obj/machinery/button) in W) - stuff_on_wall = 1 - - //crude, but will cover most cases. We could do stuff like check pixel_x/y but it's not really worth it. - for (var/dir in cardinal) - var/turf/T = get_step(W, dir) - if (locate(/obj/machinery/button) in T) - stuff_on_wall = 1 - break - - if (stuff_on_wall) - to_chat(user, "There is already a button there!") - return - - to_chat(user, "You place down the button.") - var/obj/machinery/button/mass_driver/B = new /obj/machinery/button/remote/driver(user.loc) - - switch (placement_dir) - if (NORTH) - B.pixel_x = 0 - B.pixel_y = 32 - if (SOUTH) - B.pixel_x = 0 - B.pixel_y = -32 - if (EAST) - B.pixel_x = 32 - B.pixel_y = 0 - if (WEST) - B.pixel_x = -32 - B.pixel_y = 0 - - B.id = id - B.use_power = 0 - B.add_fingerprint(user) - qdel(src) - -/obj/machinery/mass_driver/mining - name = "mining mass driver" - desc = "Shoots things really hard really fast." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "mass_driver" - anchored = 0 +//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 = 1.0 + use_power = 1 + idle_power_usage = 2 + active_power_usage = 50 + + 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. + var/_wifi_id + var/datum/wifi/receiver/button/mass_driver/wifi_receiver + +/obj/machinery/mass_driver/Initialize() + . = ..() + if(_wifi_id) + wifi_receiver = new(_wifi_id, src) + +/obj/machinery/mass_driver/Destroy() + qdel(wifi_receiver) + wifi_receiver = null + 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) + 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) + +/obj/machinery/mass_driver/attackby(obj/item/W, mob/user) + + if(W.iswrench()) + if(!anchored) + playsound(src.loc, W.usesound, 75, 1) + user.visible_message("[user.name] secures [src] to the floor.", \ + "You secure the external reinforcing bolts to the floor.", \ + "You hear a ratchet") + src.anchored = 1 + else + playsound(src.loc, W.usesound, 75, 1) + user.visible_message("[user.name] unsecures [src] from the floor.", \ + "You unsecure the external reinforcing bolts from the floor.", \ + "You hear a ratchet") + src.anchored = 0 + +/obj/machinery/mass_driver/verb/rotate() + set name = "Rotate" + set category = "Object" + set src in oview(1) + + if (src.anchored || usr:stat) + to_chat(usr, "It is fastened to the floor!") + return 0 + src.set_dir(turn(src.dir, 90)) + return 1 + +/obj/item/mass_driver_diy + name = "Mass Driver Kit" + desc = "A do-it-yourself kit for constructing the finest of mass drivers." + icon = 'icons/obj/storage.dmi' + icon_state = "inf_box" + item_state = "syringe_kit" + +/obj/item/mass_driver_diy/attack_self(mob/user) + to_chat(user, "You start piecing together the kit...") + if(do_after(user, 80)) + var/master_id = "[user.name] - [pick("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")][rand(100,999)]" + var/obj/machinery/mass_driver/mining/R = new /obj/machinery/mass_driver/mining(user.loc) + R.id = master_id + R.power = 2.0 + var/obj/item/mass_driver_button/B = new /obj/item/mass_driver_button(user.loc) + B.id = master_id + user.visible_message("[user] assembles \a [R].\ + ", "You assemble \a [R].") + R.add_fingerprint(user) + qdel(src) + +/obj/item/mass_driver_button + name = "mass driver button" + desc = "An unscrewed mass driver button." + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + var/id + +/obj/item/mass_driver_button/afterattack(var/atom/A, var/mob/user, var/adjacent, var/clickparams) + if (!adjacent) + return + + //must place on a wall and user must not be inside a closet/mecha/whatever + var/turf/W = A + if (!iswall(W) || !isturf(user.loc)) + to_chat(user, "You can't place this here!") + return + + var/placement_dir = get_dir(user, W) + if (!(placement_dir in cardinal)) + to_chat(user, "You must stand directly in front of the wall you wish to place that on.") + return + + //just check if there is a poster on or adjacent to the wall + var/stuff_on_wall = 0 + if (locate(/obj/machinery/button) in W) + stuff_on_wall = 1 + + //crude, but will cover most cases. We could do stuff like check pixel_x/y but it's not really worth it. + for (var/dir in cardinal) + var/turf/T = get_step(W, dir) + if (locate(/obj/machinery/button) in T) + stuff_on_wall = 1 + break + + if (stuff_on_wall) + to_chat(user, "There is already a button there!") + return + + to_chat(user, "You place down the button.") + var/obj/machinery/button/mass_driver/B = new /obj/machinery/button/remote/driver(user.loc) + + switch (placement_dir) + if (NORTH) + B.pixel_x = 0 + B.pixel_y = 32 + if (SOUTH) + B.pixel_x = 0 + B.pixel_y = -32 + if (EAST) + B.pixel_x = 32 + B.pixel_y = 0 + if (WEST) + B.pixel_x = -32 + B.pixel_y = 0 + + B.id = id + B.use_power = 0 + B.add_fingerprint(user) + qdel(src) + +/obj/machinery/mass_driver/mining + name = "mining mass driver" + desc = "Shoots things really hard really fast." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "mass_driver" + anchored = 0 use_power = 0 \ No newline at end of file diff --git a/code/game/machinery/mecha_fabricator.dm b/code/game/machinery/mecha_fabricator.dm index ceb637574ae..016f2199a75 100644 --- a/code/game/machinery/mecha_fabricator.dm +++ b/code/game/machinery/mecha_fabricator.dm @@ -91,7 +91,7 @@ if(M.h_style == "Floorlength Braid" || M.h_style == "Very Long Hair") if(prob(10)) M.apply_damage(30, BRUTE, "head") - M.apply_damage(45, HALLOSS) + M.apply_damage(45, PAIN) M.visible_message("[user]'s hair catches in the [src]!", "Your hair gets caught in the [src]!") M.say("*scream") ui_interact(user) @@ -223,7 +223,7 @@ if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) user.visible_message(span("warning", "[user] feeds the [target]'s hair into the [src] and flicks it on!"), span("alert", "You turn the [src] on!")) target.apply_damage(30, BRUTE, "head") - target.apply_damage(25, HALLOSS) + target.apply_damage(25, PAIN) target.say("*scream") user.attack_log += text("\[[time_stamp()]\] Has fed [target.name]'s ([target.ckey]) hair into a [src].") @@ -238,7 +238,7 @@ if(target != user && !user.restrained() && !user.stat && !user.weakened && !user.stunned && !user.paralysis) user.visible_message(span("alert", "[user] starts tugging on [target]'s head as the [src] keeps running!"), span("alert", "You start tugging on [target]'s head!")) target.apply_damage(25, BRUTE, "head") - target.apply_damage(10, HALLOSS) + target.apply_damage(10, PAIN) target.say("*scream") spawn(10) user.visible_message(span("alert", "[user] stops the [src] and leaves [target] resting as they are."), span("alert", "You turn the [src] off and let go of [target].")) diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index 62de5d859f7..f76bda7cb77 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -1,252 +1,252 @@ -// Navigation beacon for AI robots -// Functions as a transponder: looks for incoming signal matching - - -var/global/list/navbeacons // 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 radio beacon used for bot navigation." - level = 1 // underfloor - layer = 2.5 - anchored = 1 - - var/open = 0 // true if cover is open - var/locked = 1 // true if controls are locked - var/freq = 1445 // radio frequency - var/location = "" // location response text - var/list/codes // assoc. list of transponder codes - var/codes_txt = "" // codes as set on map: "tag1;tag2" or "tag1=value;tag2=value" - - req_access = list(access_engine) - - Initialize() - . = ..() - - set_codes() - - var/turf/T = loc - hide(!T.is_plating()) - - // add beacon to MULE bot beacon list - if(freq == 1400) - LAZYADD(navbeacons, src) - - if(SSradio) - SSradio.add_object(src, freq, RADIO_NAVBEACONS) - - // set the transponder codes assoc list from codes_txt - proc/set_codes() - if(!codes_txt) - return - - codes = new() - - var/list/entries = text2list(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" - - - // called when turf state changes - // hide the object if turf is intact - hide(var/intact) - invisibility = intact ? 101 : 0 - updateicon() - - // update the icon_state - proc/updateicon() - 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]" - - - // look for a signal of the form "findbeacon=X" - // where X is any - // or the location - // or one of the set transponder keys - // if found, return a signal - receive_signal(datum/signal/signal) - - var/request = signal.data["findbeacon"] - if(request && ((request in codes) || request == "any" || request == location)) - addtimer(CALLBACK(src, .proc/post_signal), 1) - - // return a signal giving location and transponder codes - - proc/post_signal() - - var/datum/radio_frequency/frequency = SSradio.return_frequency(freq) - - if(!frequency) return - - var/datum/signal/signal = new() - signal.source = src - signal.transmission_method = 1 - signal.data["beacon"] = location - - for(var/key in codes) - signal.data[key] = codes[key] - - frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) - - - 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.isscrewdriver()) - open = !open - - user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") - - updateicon() - - else if (istype(I, /obj/item/card/id)||istype(I, /obj/item/device/pda)) - if(open) - if (src.allowed(user)) - src.locked = !src.locked - to_chat(user, "Controls are now [src.locked ? "locked." : "unlocked."]") - else - to_chat(user, "Access denied.") - updateDialog() - else - to_chat(user, "You must open the cover first!") - return - - attack_ai(var/mob/user) - interact(user, 1) - - attack_hand(var/mob/user) - - if(!user.IsAdvancedToolUser()) - return 0 - - interact(user, 0) - - 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)
    -Frequency: [format_frequency(freq)]

    -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)
        -Frequency: -- -- -[format_frequency(freq)] -+ -+
        -
        -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 - - 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["freq"]) - freq = sanitize_frequency(freq + text2num(href_list["freq"])) - updateDialog() - - else if(href_list["locedit"]) - var/newloc = sanitize(input("Enter New Location", "Navigation Beacon", location) as text|null) - if(newloc) - location = newloc - updateDialog() - - else if(href_list["edit"]) - var/codekey = href_list["code"] - - var/newkey = input("Enter Transponder Code Key", "Navigation Beacon", codekey) as text|null - if(!newkey) - return - - var/codeval = codes[codekey] - var/newval = input("Enter Transponder Code Value", "Navigation Beacon", codeval) as text|null - 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 = input("Enter New Transponder Code Key", "Navigation Beacon") as text|null - if(!newkey) - return - - var/newval = input("Enter New Transponder Code Value", "Navigation Beacon") as text|null - if(!newval) - newval = "1" - return - - if(!codes) - codes = new() - - codes[newkey] = newval - - updateDialog() - -/obj/machinery/navbeacon/Destroy() - navbeacons.Remove(src) - if(SSradio) - SSradio.remove_object(src, freq) - return ..() +// Navigation beacon for AI robots +// Functions as a transponder: looks for incoming signal matching + + +var/global/list/navbeacons // 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 radio beacon used for bot navigation." + level = 1 // underfloor + layer = 2.5 + anchored = 1 + + var/open = 0 // true if cover is open + var/locked = 1 // true if controls are locked + var/freq = 1445 // radio frequency + var/location = "" // location response text + var/list/codes // assoc. list of transponder codes + var/codes_txt = "" // codes as set on map: "tag1;tag2" or "tag1=value;tag2=value" + + req_access = list(access_engine) + + Initialize() + . = ..() + + set_codes() + + var/turf/T = loc + hide(!T.is_plating()) + + // add beacon to MULE bot beacon list + if(freq == 1400) + LAZYADD(navbeacons, src) + + if(SSradio) + SSradio.add_object(src, freq, RADIO_NAVBEACONS) + + // set the transponder codes assoc list from codes_txt + proc/set_codes() + if(!codes_txt) + return + + codes = new() + + var/list/entries = text2list(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" + + + // called when turf state changes + // hide the object if turf is intact + hide(var/intact) + invisibility = intact ? 101 : 0 + updateicon() + + // update the icon_state + proc/updateicon() + 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]" + + + // look for a signal of the form "findbeacon=X" + // where X is any + // or the location + // or one of the set transponder keys + // if found, return a signal + receive_signal(datum/signal/signal) + + var/request = signal.data["findbeacon"] + if(request && ((request in codes) || request == "any" || request == location)) + addtimer(CALLBACK(src, .proc/post_signal), 1) + + // return a signal giving location and transponder codes + + proc/post_signal() + + var/datum/radio_frequency/frequency = SSradio.return_frequency(freq) + + if(!frequency) return + + var/datum/signal/signal = new() + signal.source = src + signal.transmission_method = 1 + signal.data["beacon"] = location + + for(var/key in codes) + signal.data[key] = codes[key] + + frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) + + + 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.isscrewdriver()) + open = !open + + user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") + + updateicon() + + else if (istype(I, /obj/item/card/id)||istype(I, /obj/item/device/pda)) + if(open) + if (src.allowed(user)) + src.locked = !src.locked + to_chat(user, "Controls are now [src.locked ? "locked." : "unlocked."]") + else + to_chat(user, "Access denied.") + updateDialog() + else + to_chat(user, "You must open the cover first!") + return + + attack_ai(var/mob/user) + interact(user, 1) + + attack_hand(var/mob/user) + + if(!user.IsAdvancedToolUser()) + return 0 + + interact(user, 0) + + 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)
            +Frequency: [format_frequency(freq)]

            +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)
                +Frequency: +- +- +[format_frequency(freq)] ++ ++
                +
                +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 + + 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["freq"]) + freq = sanitize_frequency(freq + text2num(href_list["freq"])) + updateDialog() + + else if(href_list["locedit"]) + var/newloc = sanitize(input("Enter New Location", "Navigation Beacon", location) as text|null) + if(newloc) + location = newloc + updateDialog() + + else if(href_list["edit"]) + var/codekey = href_list["code"] + + var/newkey = input("Enter Transponder Code Key", "Navigation Beacon", codekey) as text|null + if(!newkey) + return + + var/codeval = codes[codekey] + var/newval = input("Enter Transponder Code Value", "Navigation Beacon", codeval) as text|null + 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 = input("Enter New Transponder Code Key", "Navigation Beacon") as text|null + if(!newkey) + return + + var/newval = input("Enter New Transponder Code Value", "Navigation Beacon") as text|null + if(!newval) + newval = "1" + return + + if(!codes) + codes = new() + + codes[newkey] = newval + + updateDialog() + +/obj/machinery/navbeacon/Destroy() + navbeacons.Remove(src) + if(SSradio) + SSradio.remove_object(src, freq) + return ..() diff --git a/code/game/machinery/overview.dm b/code/game/machinery/overview.dm index 9ac3a8c2953..6665d9ff901 100644 --- a/code/game/machinery/overview.dm +++ b/code/game/machinery/overview.dm @@ -1,345 +1,345 @@ -//#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]",ckey=key_name(usr)) - - src.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