diff --git a/.github/AUTODOC_GUIDE.md b/.github/AUTODOC_GUIDE.md new file mode 100644 index 00000000000..6a7d521a04d --- /dev/null +++ b/.github/AUTODOC_GUIDE.md @@ -0,0 +1,108 @@ +# dmdoc +[DOCUMENTATION]: https://codedocs.paradisestation.org/ + +[BYOND]: https://secure.byond.com/ + +[DMDOC]: https://github.com/AffectedArc07/ParaSpacemanDMM/tree/master/src/dmdoc + +[DMDOC] is a documentation generator for DreamMaker, the scripting language +of the [BYOND] game engine. It produces simple static HTML files based on +documented files, macros, types, procs, and vars. + +We use **dmdoc** to generate [DOCUMENTATION] for our code, and that documentation +is automatically generated and built on every new commit to the master branch + +This gives new developers a clickable reference [DOCUMENTATION] they can browse to better help +gain understanding of the Paradise codebase structure and api reference. + +## Documenting code on Paradise +We use block comments to document procs and classes, and we use `///` line comments +when documenting individual variables. + +Documentation is not required at Paradise, but it is highly recommended that all new code be covered with DMdoc code, according to the [Specifications](#Specification) + +We also recommend that when you touch older code, you document the functions that you +have touched in the process of updating that code + +### Specification +A class *should* always be autodocumented, and all public functions *should* be documented + +All class level defined variables *should* be documented + +Internal functions *can* be documented, but may not be + +A public function is any function that a developer might reasonably call while using +or interacting with your object. Internal functions are helper functions that your +public functions rely on to implement logic + + +### Documenting a proc +When documenting a proc, we give a short one line description (as this is shown +next to the proc definition in the list of all procs for a type or global +namespace), then a longer paragraph which will be shown when the user clicks on +the proc to jump to it's definition +``` +/** + * Short description of the proc + * + * Longer detailed paragraph about the proc + * including any relevant detail + * Arguments: + * * arg1 - Relevance of this argument + * * arg2 - Relevance of this argument + */ +``` + +### Documenting a class +We first give the name of the class as a header, this can be omitted if the name is +just going to be the typepath of the class, as dmdoc uses that by default + +Then we give a short oneline description of the class + +Finally we give a longer multi paragraph description of the class and it's details +``` +/** + * # Classname (Can be omitted if it's just going to be the typepath) + * + * The short overview + * + * A longer + * paragraph of functionality about the class + * including any assumptions/special cases + * + */ +``` + +### Documenting a variable/define +Give a short explanation of what the variable, in the context of the class, or define is. +``` +/// Type path of item to go in suit slot +var/suit = null +``` + +## Module level description of code +Modules are the best way to describe the structure/intent of a package of code +where you don't want to be tied to the formal layout of the class structure. + +On Paradise we do this by adding markdown files inside the `code` directory +that will also be rendered and added to the modules tree. The structure for +these is deliberately not defined, so you can be as freeform and as wheeling as +you would like. + +[Here is a representative example of what you might write](https://codedocs.paradisestation.org/code/modules/keybindings/readme.html) + +## Special variables +You can use certain special template variables in DM DOC comments and they will be expanded +``` + [DEFINE_NAME] - Expands to a link to the define definition if documented + [/mob] - Expands to a link to the docs for the /mob class + [/mob/proc/Dizzy] - Expands to a link that will take you to the /mob class and anchor you to the dizzy proc docs + [/mob/var/stat] - Expands to a link that will take you to the /mob class and anchor you to the stat var docs +``` + +You can customise the link name by using `[link name][link shorthand].` + +eg. `[see more about dizzy here] [/mob/proc/Dizzy]` + +This is very useful to quickly link to other parts of the autodoc code to expand +upon a comment made, or reasoning about code diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 295cdfce5cb..1d1a1372bc5 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -36,13 +36,13 @@ actual development. Due to limitations of the engine, this may not always be possible; but do try your best. * Document and explain your pull requests thoroughly. Failure to do so will delay a PR as - we question why changes were made. This is especially important if you're porting a PR + we question why changes were made. This is especially important if you're porting a PR from another codebase (i.e. TG) and divert from the original. Explaining with single comment on why you've made changes will help us review the PR faster and understand your decision making process. - * Any pull request must have a changelog, this is to allow us to know when a PR is deployed - on the live server. Inline changelogs are supported through the format described + * Any pull request must have a changelog, this is to allow us to know when a PR is deployed + on the live server. Inline changelogs are supported through the format described [here](https://github.com/ParadiseSS13/Paradise/pull/3291#issuecomment-172950466) and should be used rather than manually edited .yml file changelogs. @@ -51,14 +51,14 @@ actual development. commits. Use `git rebase` or `git reset` to update your branches, not `git pull`. * Please explain why you are submitting the pull request, and how you think your change will be beneficial to the game. Failure to do so will be grounds for rejecting the PR. - + * If your pull request is not finished make sure it is at least testable in a live environment. Pull requests that do not at least meet this requirement may be closed at maintainer discretion. You may request a maintainer reopen the pull request when you're ready, or make a new one. - * While we have no issue helping contributors (and especially new contributors) bring reasonably sized contributions up to standards via the pull request review process, larger contributions are expected to pass a higher bar of completeness and code quality *before* you open a pull request. Maintainers may close such pull requests that are deemed to be substantially flawed. You should take some time to discuss with maintainers or other contributors on how to improve the changes. + * While we have no issue helping contributors (and especially new contributors) bring reasonably sized contributions up to standards via the pull request review process, larger contributions are expected to pass a higher bar of completeness and code quality *before* you open a pull request. Maintainers may close such pull requests that are deemed to be substantially flawed. You should take some time to discuss with maintainers or other contributors on how to improve the changes. #### Using Changelog - * Tags used in changelog include add/rscadd, del/rscdel, fix/fixes, typo/spellcheck. - * Without specifying a name it will default to using your GitHub name. + * Tags used in changelog include add/rscadd, del/rscdel, fix/fixes, typo/spellcheck. + * Without specifying a name it will default to using your GitHub name. Some examples ``` :cl: @@ -76,11 +76,11 @@ typo: Fixes some misspelled words under Using Changelog ## Specifications -As mentioned before, you are expected to follow these specifications in order to make everyone's lives easier. It'll save both your time and ours, by making +As mentioned before, you are expected to follow these specifications in order to make everyone's lives easier. It'll save both your time and ours, by making sure you don't have to make any changes and we don't have to ask you to. Thank you for reading this section! ### Object Oriented Code -As BYOND's Dream Maker (henceforth "DM") is an object-oriented language, code must be object-oriented when possible in order to be more flexible when adding +As BYOND's Dream Maker (henceforth "DM") is an object-oriented language, code must be object-oriented when possible in order to be more flexible when adding content to it. If you don't know what "object-oriented" means, we highly recommend you do some light research to grasp the basics. ### All BYOND paths must contain the full path @@ -113,7 +113,7 @@ datum code ``` -The use of this is not allowed in this project *unless the majority of the file is already relatively pathed* as it makes finding definitions via full text +The use of this is not allowed in this project *unless the majority of the file is already relatively pathed* as it makes finding definitions via full text searching next to impossible. The only exception is the variables of an object may be nested to the object, but must not nest further. The previous code made compliant: @@ -167,7 +167,7 @@ ever forming. ```DM //Good -var/atom/A +var/atom/A "[A]" //Bad @@ -177,7 +177,7 @@ var/atom/A ### Use the pronoun library instead of `\his` macros. We have a system in code/\_\_HELPERS/pronouns.dm for addressing all forms of pronouns. This is useful in a number of ways; * BYOND's \his macro can be unpredictable on what object it references. - Take this example: `"[user] waves \his [user.weapon] around, hitting \his opponents!"`. + Take this example: `"[user] waves \his [user.weapon] around, hitting \his opponents!"`. This will end up referencing the user's gender in the first occurence, but what about the second? It'll actually print the gender set on the weapon he's carrying, which is unintended - and there's no way around this. * It always prints the real `gender` variable of the atom it's referencing. This can lead to exposing a mob's gender even when their face is covered, @@ -217,7 +217,7 @@ You must use tabs to indent your code, NOT SPACES. Hacky code, such as adding specific checks (ex: `istype(src, /obj/whatever)`), is highly discouraged and only allowed when there is ***no*** other option. ( Protip: 'I couldn't immediately think of a proper way so thus there must be no other option' is not gonna cut it here! If you can't think of anything else, say that outright and admit that you need help with it. Maintainers exist for exactly that reason.) -You can avoid hacky code by using object-oriented methodologies, such as overriding a function (called "procs" in DM) or sectioning code into functions and +You can avoid hacky code by using object-oriented methodologies, such as overriding a function (called "procs" in DM) or sectioning code into functions and then overriding them as required. The same also applies to bugfix - If an invalid value is being passed into a proc from something that shouldn't have that value, don't fix it on the proc itself, fix it at its origin! (Where feasible) @@ -239,15 +239,15 @@ There are two key points here: Remember: although this tradeoff makes sense in many cases, it doesn't cover them all. Think carefully about your addition before deciding if you need to use it. ### Prefer `Initialize()` over `New()` for atoms -Our game controller is pretty good at handling long operations and lag, but it can't control what happens when the map is loaded, which calls `New` for all atoms on the map. If you're creating a new atom, use the `Initialize` proc to do what you would normally do in `New`. This cuts down on the number of proc calls needed when the world is loaded. +Our game controller is pretty good at handling long operations and lag, but it can't control what happens when the map is loaded, which calls `New` for all atoms on the map. If you're creating a new atom, use the `Initialize` proc to do what you would normally do in `New`. This cuts down on the number of proc calls needed when the world is loaded. While we normally encourage (and in some cases, even require) bringing out of date code up to date when you make unrelated changes near the out of date code, that is not the case for `New` -> `Initialize` conversions. These systems are generally more dependant on parent and children procs so unrelated random conversions of existing things can cause bugs that take months to figure out. ### No implicit var/ When you declare a parameter in a proc, the var/ is implicit. Do not include any implicit var/ when declaring a variable. -I.e. -Bad: +I.e. +Bad: ```` obj/item/proc1(var/input1, var/input2) ```` @@ -258,7 +258,7 @@ obj/item/proc1(input1, input2) ```` ### No magic numbers or strings -This means stuff like having a "mode" variable for an object set to "1" or "2" with no clear indicator of what that means. Make these #defines with a name that +This means stuff like having a "mode" variable for an object set to "1" or "2" with no clear indicator of what that means. Make these #defines with a name that more clearly states what it's for. For instance: ````DM /datum/proc/do_the_thing(thing_to_do) @@ -285,10 +285,10 @@ This is clearer and enhances readability of your code! Get used to doing it! (if, while, for, etc) * All control statements must not contain code on the same line as the statement (`if(condition) return`) -* All control statements comparing a variable to a number should use the formula of `thing` `operator` `number`, not the reverse +* All control statements comparing a variable to a number should use the formula of `thing` `operator` `number`, not the reverse (eg: `if(count <= 10)` not `if(10 >= count)`) -* All control statements must be spaced as `if()`, with the brackets touching the keyword. -* Do not use one-line control statements. +* All control statements must be spaced as `if()`, with the brackets touching the keyword. +* Do not use one-line control statements. Instead of doing ``` if(x) return @@ -305,7 +305,7 @@ you must use `to_chat(mob/client/world, "message")`. Failure to do so will lead ### Use early return Do not enclose a proc in an if-block when returning on a condition is more feasible. - + This is bad: ````DM /datum/datum1/proc/proc1() @@ -329,11 +329,11 @@ This prevents nesting levels from getting deeper then they need to be. ### Uses addtimer() instead of sleep() or spawn() If you need to call a proc after a set amount of time, use addtimer() instead of spawn() / sleep() where feasible. -Although it is more complex, it is more performant and unlike spawn() or sleep(), it can be cancelled. +Although it is more complex, it is more performant and unlike spawn() or sleep(), it can be cancelled. For more details, see https://github.com/tgstation/tgstation/pull/22933. Look for code example on how to properly use it. - + This is bad: ````DM /datum/datum1/proc/proc1() @@ -369,13 +369,13 @@ This prevents nesting levels from getting deeper then they need to be. #### Bitflags * We prefer using bitshift operators instead of directly typing out the value. I.E. - ``` + ``` #define MACRO_ONE (1<<0) #define MACRO_TWO (1<<1) #define MACRO_THREE (1<<2) ``` Is preferable to - ``` + ``` #define MACRO_ONE 1 #define MACRO_TWO 2 #define MACRO_THREE 4 @@ -455,7 +455,7 @@ SS13 has a lot of legacy code that's never been updated. Here are some examples * All changes to the database's layout(schema) must be specified in the database changelog in SQL, as well as reflected in the schema files -* Any time the schema is changed the `DB_MAJOR_VERSION` defines must be incremented, as well as the example config, with an appropriate conversion kit placed +* Any time the schema is changed the `DB_MAJOR_VERSION` defines must be incremented, as well as the example config, with an appropriate conversion kit placed in the SQL/updates folder. * Queries must never specify the database, be it in code, or in text files in the repo. @@ -463,7 +463,7 @@ in the SQL/updates folder. ### Mapping Standards * Map Merge * You MUST run Map Merge prior to opening your PR when updating existing maps to minimize the change differences (even when using third party mapping programs such as FastDMM.) - * Failure to run Map Merge on a map after using third party mapping programs (such as FastDMM) greatly increases the risk of the map's key dictionary + * Failure to run Map Merge on a map after using third party mapping programs (such as FastDMM) greatly increases the risk of the map's key dictionary becoming corrupted by future edits after running map merge. Resolving the corruption issue involves rebuilding the map's key dictionary; * Variable Editing (Var-edits) @@ -487,12 +487,12 @@ in the SQL/updates folder. Like all languages, Dream Maker has its quirks, some of them are beneficial to us, like these #### In-To for-loops -```for(var/i = 1, i <= some_value, i++)``` is a fairly standard way to write an incremental for loop in most languages (especially those in the C family), but +```for(var/i = 1, i <= some_value, i++)``` is a fairly standard way to write an incremental for loop in most languages (especially those in the C family), but DM's ```for(var/i in 1 to some_value)``` syntax is oddly faster than its implementation of the former syntax; where possible, it's advised to use DM's syntax. ( -Note, the ```to``` keyword is inclusive, so it automatically defaults to replacing ```<=```; if you want ```<``` then you should write it as ```1 to +Note, the ```to``` keyword is inclusive, so it automatically defaults to replacing ```<=```; if you want ```<``` then you should write it as ```1 to some_value-1```). -HOWEVER, if either ```some_value``` or ```i``` changes within the body of the for (underneath the ```for(...)``` header) or if you are looping over a list AND +HOWEVER, if either ```some_value``` or ```i``` changes within the body of the for (underneath the ```for(...)``` header) or if you are looping over a list AND changing the length of the list then you can NOT use this type of for-loop! ### for(var/A in list) VS for(var/i in 1 to list.len) @@ -510,9 +510,9 @@ for(var/obj/item/sword/S in bag_of_items) if(!best_sword || S.damage > best_sword.damage) best_sword = S ``` -The above is a simple proc for checking all swords in a container and returning the one with the highest damage, and it uses DM's standard syntax for a -for-loop by specifying a type in the variable of the for's header that DM interprets as a type to filter by. It performs this filter using ```istype()``` (or -some internal-magic similar to ```istype()``` - this is BYOND, after all). This is fine in its current state for ```bag_of_items```, but if ```bag_of_items``` +The above is a simple proc for checking all swords in a container and returning the one with the highest damage, and it uses DM's standard syntax for a +for-loop by specifying a type in the variable of the for's header that DM interprets as a type to filter by. It performs this filter using ```istype()``` (or +some internal-magic similar to ```istype()``` - this is BYOND, after all). This is fine in its current state for ```bag_of_items```, but if ```bag_of_items``` contained ONLY swords, or only SUBTYPES of swords, then the above is inefficient. For example: ```DM var/list/bag_of_swords = list(sword, sword, sword, sword) @@ -521,10 +521,10 @@ for(var/obj/item/sword/S in bag_of_swords) if(!best_sword || S.damage > best_sword.damage) best_sword = S ``` -specifies a type for DM to filter by. +specifies a type for DM to filter by. -With the previous example that's perfectly fine, we only want swords, but here the bag only contains swords? Is DM still going to try to filter because we gave -it a type to filter by? YES, and here comes the inefficiency. Wherever a list (or other container, such as an atom (in which case you're technically accessing +With the previous example that's perfectly fine, we only want swords, but here the bag only contains swords? Is DM still going to try to filter because we gave +it a type to filter by? YES, and here comes the inefficiency. Wherever a list (or other container, such as an atom (in which case you're technically accessing their special contents list, but that's irrelevant)) contains datums of the same datatype or subtypes of the datatype you require for your loop's body, you can circumvent DM's filtering and automatic ```istype()``` checks by writing the loop as such: ```DM @@ -535,7 +535,7 @@ for(var/s in bag_of_swords) if(!best_sword || S.damage > best_sword.damage) best_sword = S ``` -Of course, if the list contains data of a mixed type then the above optimisation is DANGEROUS, as it will blindly typecast all data in the list as the +Of course, if the list contains data of a mixed type then the above optimisation is DANGEROUS, as it will blindly typecast all data in the list as the specified type, even if it isn't really that type, causing runtime errors (AKA your shit won't work if this happens). #### Dot variable @@ -559,7 +559,7 @@ DM has a var keyword, called global. This var keyword is for vars inside of type ``` This does NOT mean that you can access it everywhere like a global var. Instead, it means that that var will only exist once for all instances of its type, in this case that var will only exist once for all mobs - it's shared across everything in its type. (Much more like the keyword `static` in other languages like PHP/C++/C#/Java) -Isn't that confusing? +Isn't that confusing? There is also an undocumented keyword called `static` that has the same behaviour as global but more correctly describes BYOND's behaviour. Therefore, we always use static instead of global where we need it, as it reduces suprise when reading BYOND code. @@ -576,7 +576,7 @@ To access it: GLOB.my_global_here = X ``` -There are a few other defines that do other things. `GLOBAL_REAL` shouldn't be used unless you know exactly what you're doing. +There are a few other defines that do other things. `GLOBAL_REAL` shouldn't be used unless you know exactly what you're doing. `GLOBAL_VAR_INIT` allows you to set an initial value on the var, like `GLOBAL_VAR_INIT(number_one, 1)`. `GLOBAL_LIST_INIT` allows you to define a list global var with an initial value. Etc. @@ -589,6 +589,7 @@ pull requests/issues, and merging/closing pull requests. * [Fox P McCloud](https://github.com/Fox-McCloud) * [Crazy Lemon](https://github.com/Crazylemon64) * [Ansari](https://github.com/variableundefined) +* [AffectedArc07](https://github.com/AffectedArc07) ### Maintainer instructions * Do not `self-merge`; this refers to the practice of opening a pull request, then @@ -604,6 +605,6 @@ pull requests/issues, and merging/closing pull requests. hours, to allow other coders and the community time to discuss the proposed changes. * If the discussion is active, or the change is controversial, the pull request is to be put on hold until a consensus is reached. -* To keep commit history easy to navigate for future contributors (e.g. Git Blame), squash merge -is to be preferred to normal merge where suitable. Ensure that the squashed commit name is easy +* To keep commit history easy to navigate for future contributors (e.g. Git Blame), squash merge +is to be preferred to normal merge where suitable. Ensure that the squashed commit name is easy to understand and read. Modify it if needed. diff --git a/.github/workflows/generate_autodoc.yml b/.github/workflows/generate_autodoc.yml new file mode 100644 index 00000000000..44eb69eb570 --- /dev/null +++ b/.github/workflows/generate_autodoc.yml @@ -0,0 +1,32 @@ +name: Generate Documentation + +on: + schedule: + - cron: "0 0 * * *" # Every day at the very start of the day + +jobs: + generate_docs: + name: 'Generate Documentation' + runs-on: ubuntu-18.04 + steps: + - name: 'Update Branch' + uses: actions/checkout@v2 + with: + fetch-depth: 1 + ref: master + + - name: 'Generate Documentation' + run: | + ./tools/github-actions/doc-generator + touch dmdoc/.nojekyll + # Nojekyll is important to disable jeykll syntax, which can mess with files that start with underscores + + - name: 'Deploy Documentation' + uses: crazy-max/ghaction-github-pages@v2 + with: + keep_history: false + build_dir: dmdoc + jekyll: false + fqdn: codedocs.paradisestation.org + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.travis.yml b/.travis.yml index e4443d14ee0..74ac9fedb9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,8 +52,6 @@ jobs: - libstdc++6:i386 - libssl-dev:i386 - gcc-multilib - - g++-7 - - g++-7-multilib - pkg-config:i386 - zlib1g-dev:i386 cache: @@ -61,6 +59,8 @@ jobs: - $HOME/.cargo - $HOME/.rustup - $HOME/BYOND + before_install: + - 'sudo apt-get -qq update' install: - tools/travis/install_byond.sh - source $HOME/BYOND/byond/bin/byondsetup diff --git a/README.md b/README.md index 2c0ababbe30..b484a8bccfc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [![Build Status](https://travis-ci.org/ParadiseSS13/Paradise.svg?branch=master)](https://travis-ci.org/ParadiseSS13/Paradise) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/paradisess13/paradise.svg)](http://isitmaintained.com/project/paradisess13/paradise "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/paradisess13/paradise.svg)](http://isitmaintained.com/project/paradisess13/paradise "Percentage of issues still open") -[![Krihelimeter](http://www.krihelinator.xyz/badge/paradisess13/paradise)](http://www.krihelinator.xyz) ![Render Nanomaps](https://github.com/ParadiseSS13/Paradise/workflows/Render%20Nanomaps/badge.svg) [![forthebadge](http://forthebadge.com/images/badges/60-percent-of-the-time-works-every-time.svg)](http://forthebadge.com) diff --git a/SpacemanDMM.toml b/SpacemanDMM.toml new file mode 100644 index 00000000000..81aff0d5576 --- /dev/null +++ b/SpacemanDMM.toml @@ -0,0 +1,9 @@ +[langserver] +dreamchecker = true + +[code_standards] +disallow_relative_type_definitions = true +disallow_relative_proc_definitions = true + +[dmdoc] +use_typepath_names = true diff --git a/_maps/cyberiad.dm b/_maps/cyberiad.dm index 81499ec8c75..3652328e8f4 100644 --- a/_maps/cyberiad.dm +++ b/_maps/cyberiad.dm @@ -17,7 +17,7 @@ z4 = lavaland #define MAP_TRANSITION_CONFIG list(\ DECLARE_LEVEL(MAIN_STATION, CROSSLINKED, list(STATION_LEVEL, STATION_CONTACT, REACHABLE, AI_OK)),\ DECLARE_LEVEL(CENTCOMM, SELFLOOPING, list(ADMIN_LEVEL, BLOCK_TELEPORT, IMPEDES_MAGIC)),\ -DECLARE_LEVEL(MINING, SELFLOOPING, list(REACHABLE, STATION_CONTACT, HAS_WEATHER, ORE_LEVEL, AI_OK))) +DECLARE_LEVEL(MINING, SELFLOOPING, list(ORE_LEVEL, REACHABLE, STATION_CONTACT, HAS_WEATHER, AI_OK))) #define USING_MAP_DATUM /datum/map/cyberiad #elif !defined(MAP_OVERRIDE) diff --git a/_maps/delta.dm b/_maps/delta.dm index cf6acbcddb1..f944092131b 100644 --- a/_maps/delta.dm +++ b/_maps/delta.dm @@ -22,7 +22,7 @@ Lovingly ported by Purpose2 to Paradise #define MAP_TRANSITION_CONFIG list(\ DECLARE_LEVEL(MAIN_STATION, CROSSLINKED, list(STATION_LEVEL, STATION_CONTACT, REACHABLE, AI_OK)),\ DECLARE_LEVEL(CENTCOMM, SELFLOOPING, list(ADMIN_LEVEL, BLOCK_TELEPORT, IMPEDES_MAGIC)),\ -DECLARE_LEVEL(MINING, SELFLOOPING, list(REACHABLE, STATION_CONTACT, HAS_WEATHER, ORE_LEVEL, AI_OK))) +DECLARE_LEVEL(MINING, SELFLOOPING, list(ORE_LEVEL, REACHABLE, STATION_CONTACT, HAS_WEATHER, AI_OK))) #define USING_MAP_DATUM /datum/map/delta diff --git a/_maps/map_files/Delta/delta.dmm b/_maps/map_files/Delta/delta.dmm index 02f36f5ea40..8436f932d4a 100644 --- a/_maps/map_files/Delta/delta.dmm +++ b/_maps/map_files/Delta/delta.dmm @@ -28449,7 +28449,7 @@ /obj/machinery/light{ dir = 1 }, -/obj/machinery/computer/ordercomp, +/obj/machinery/computer/supplycomp/public, /obj/effect/decal/warning_stripes/yellow/hollow, /turf/simulated/floor/plasteel{ dir = 5; @@ -29206,7 +29206,7 @@ }, /area/hallway/primary/fore) "bed" = ( -/obj/machinery/computer/ordercomp, +/obj/machinery/computer/supplycomp/public, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "purple" @@ -40663,40 +40663,16 @@ /turf/simulated/wall/r_wall, /area/engine/break_room) "byz" = ( -/obj/structure/cable{ - icon_state = "0-4"; - d2 = 4 - }, -/obj/machinery/modular_computer/console/preset/engineering, /obj/machinery/status_display{ pixel_x = -32 }, +/obj/machinery/computer/security/engineering, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "caution" }, /area/atmos) -"byA" = ( -/obj/structure/cable{ - d1 = 4; - d2 = 8; - icon_state = "4-8"; - tag = "" - }, -/obj/structure/chair/office/dark{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "neutralfull" - }, -/area/atmos) "byB" = ( -/obj/structure/cable{ - d1 = 1; - d2 = 8; - icon_state = "1-8" - }, /obj/structure/cable{ d1 = 1; d2 = 4; @@ -41937,12 +41913,7 @@ icon_state = "1-2"; tag = "" }, -/obj/machinery/modular_computer/console/preset/command, -/obj/structure/cable{ - icon_state = "0-2"; - pixel_y = 1; - d2 = 2 - }, +/obj/machinery/computer/shuttle/mining, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "darkpurple" @@ -47144,7 +47115,7 @@ pixel_y = 3 }, /obj/item/circuitboard/powermonitor, -/obj/item/circuitboard/stationalert_all{ +/obj/item/circuitboard/stationalert{ pixel_x = 3; pixel_y = -3 }, @@ -51367,18 +51338,6 @@ icon_state = "dark" }, /area/engine/break_room) -"bQf" = ( -/obj/structure/cable{ - d1 = 4; - d2 = 8; - icon_state = "4-8"; - pixel_x = 0 - }, -/turf/simulated/floor/plasteel{ - icon_state = "neutral"; - dir = 4 - }, -/area/crew_quarters/chief) "bQg" = ( /obj/structure/cable{ d1 = 1; @@ -51405,11 +51364,6 @@ }, /obj/structure/table/reinforced, /obj/item/clipboard, -/obj/structure/cable{ - d1 = 1; - d2 = 8; - icon_state = "1-8" - }, /obj/item/toy/figure/ce, /turf/simulated/floor/plasteel{ dir = 8; @@ -54089,12 +54043,6 @@ /area/crew_quarters/chief) "bUq" = ( /obj/machinery/hologram/holopad, -/obj/structure/cable{ - d1 = 4; - d2 = 8; - icon_state = "4-8"; - pixel_x = 0 - }, /obj/effect/decal/warning_stripes/yellow/hollow, /turf/simulated/floor/plasteel{ dir = 8; @@ -55398,11 +55346,6 @@ }, /area/crew_quarters/chief) "bWl" = ( -/obj/machinery/modular_computer/console/preset/engineering, -/obj/structure/cable{ - icon_state = "0-4"; - d2 = 4 - }, /obj/machinery/keycard_auth{ pixel_x = -24 }, @@ -55420,6 +55363,7 @@ pixel_y = -8; req_access_txt = "11" }, +/obj/machinery/computer/security/engineering, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -58725,9 +58669,6 @@ /area/blueshield) "cce" = ( /obj/structure/table/wood, -/obj/machinery/computer/skills{ - req_one_access = null - }, /turf/simulated/floor/plasteel{ dir = 2; icon_state = "bcarpet05" @@ -59666,9 +59607,6 @@ /area/ntrep) "cdK" = ( /obj/structure/table/wood, -/obj/machinery/computer/skills{ - req_access_txt = "57" - }, /turf/simulated/floor/carpet, /area/ntrep) "cdL" = ( @@ -60483,6 +60421,9 @@ icon_state = "4-8"; tag = "" }, +/obj/machinery/computer/monitor{ + name = "Grid Power Monitoring Computer" + }, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "dark"; @@ -61209,10 +61150,6 @@ /obj/machinery/tcomms/core/station, /turf/simulated/floor/bluegrid, /area/tcommsat/chamber) -"cgA" = ( -/obj/machinery/ntnet_relay, -/turf/simulated/floor/bluegrid, -/area/tcommsat/chamber) "cgB" = ( /obj/machinery/camera{ c_tag = "Singularity NorthEast"; @@ -62517,7 +62454,6 @@ dir = 1; network = list("Engineering","SS13") }, -/obj/machinery/modular_computer/console/preset/engineering, /obj/machinery/firealarm{ dir = 8; pixel_x = -26; @@ -62527,6 +62463,9 @@ icon_state = "0-4"; d2 = 4 }, +/obj/machinery/computer/monitor{ + name = "Grid Power Monitoring Computer" + }, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "dark"; @@ -88635,7 +88574,7 @@ /area/maintenance/starboard) "dey" = ( /obj/structure/table/wood, -/obj/item/modular_computer/tablet/preset/cheap, +/obj/effect/spawner/lootdrop/maintenance, /turf/simulated/floor/plasteel{ icon_state = "wood" }, @@ -97243,7 +97182,6 @@ }, /area/crew_quarters/hor) "dtZ" = ( -/obj/structure/cable, /obj/structure/cable{ d1 = 1; d2 = 4; @@ -97253,7 +97191,6 @@ pixel_x = 0; pixel_y = -30 }, -/obj/machinery/modular_computer/console/preset/research, /turf/simulated/floor/plasteel{ dir = 2; icon_state = "whitepurplecorner" @@ -114782,6 +114719,9 @@ "qrT" = ( /turf/simulated/wall/r_wall, /area/tcommsat/chamber) +"rSI" = ( +/turf/simulated/floor/bluegrid, +/area/tcommsat/chamber) "udT" = ( /obj/structure/chair/comfy/shuttle{ dir = 8 @@ -123168,9 +123108,9 @@ bVO bPI bNM qrT -eaH +rSI ceL -cgu +eaH qrT qrT bkt @@ -123940,7 +123880,7 @@ bXJ bZh caX ccW -ccW +cgu ccW chW qrT @@ -124710,9 +124650,9 @@ bVT bXL bNM qrT -hng +rSI vzz -cgA +hng qrT qrT bkt @@ -138067,7 +138007,7 @@ bIr bKe bMh bOa -bQf +bOa bOa bUh bWk @@ -142426,7 +142366,7 @@ btt buD bvO ban -byA +bzY bzY bBK aOg diff --git a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm index bcbbe8b2351..c07960eab59 100644 --- a/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm +++ b/_maps/map_files/MetaStation/MetaStation.v41A.II.dmm @@ -31415,7 +31415,7 @@ pixel_x = -2; pixel_y = 2 }, -/obj/item/circuitboard/stationalert_all{ +/obj/item/circuitboard/stationalert{ pixel_x = 1; pixel_y = -1 }, @@ -35305,7 +35305,7 @@ }, /area/engine/chiefs_office) "bjJ" = ( -/obj/machinery/computer/ordercomp, +/obj/machinery/computer/supplycomp/public, /obj/effect/decal/warning_stripes/yellow, /turf/simulated/floor/plasteel{ dir = 9; @@ -38185,7 +38185,7 @@ d2 = 2; icon_state = "1-2" }, -/obj/machinery/modular_computer/console/preset/command, +/obj/machinery/computer/card, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -41916,7 +41916,7 @@ d2 = 8; icon_state = "1-8" }, -/obj/machinery/computer/ordercomp, +/obj/machinery/computer/supplycomp/public, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -44531,7 +44531,7 @@ layer = 4; pixel_y = 32 }, -/obj/machinery/computer/ordercomp, +/obj/machinery/computer/supplycomp/public, /turf/simulated/floor/wood, /area/crew_quarters/heads) "byv" = ( @@ -57433,9 +57433,6 @@ }) "bVc" = ( /obj/structure/table/wood, -/obj/machinery/computer/skills{ - req_access_txt = "57" - }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/carpet, /area/ntrep) @@ -58264,9 +58261,6 @@ /area/blueshield) "bWI" = ( /obj/structure/table/wood, -/obj/machinery/computer/skills{ - req_one_access = null - }, /turf/simulated/floor/plasteel{ icon_state = "bcarpet05" }, @@ -96414,10 +96408,7 @@ d2 = 2; icon_state = "1-2" }, -/turf/simulated/floor/plasteel/dark, -/area/tcommsat/server) -"gIG" = ( -/obj/machinery/ntnet_relay, +/obj/machinery/tcomms/core/station, /turf/simulated/floor/bluegrid, /area/tcommsat/server) "gRw" = ( @@ -96649,10 +96640,6 @@ }, /turf/space, /area/solar/starboard) -"rUQ" = ( -/obj/machinery/tcomms/core/station, -/turf/simulated/floor/bluegrid, -/area/tcommsat/server) "sjF" = ( /obj/structure/window/reinforced{ dir = 1 @@ -151152,7 +151139,7 @@ bDf bFi bIz bKl -gIG +bOa bOb odO bKl @@ -152180,7 +152167,7 @@ bDj bFm bRo bKl -rUQ +bOa dRN kwJ bKl diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_winter.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_winter.dmm index 3d8d22218f6..671822abfeb 100644 --- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_winter.dmm +++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_biodome_winter.dmm @@ -167,7 +167,7 @@ /turf/simulated/floor/wood, /area/ruin/powered/snow_cabin) "aL" = ( -/obj/structure/displaycase/captain, +/obj/mecha/working/ripley/mining, /turf/simulated/floor/wood, /area/ruin/powered/snow_cabin) "aM" = ( @@ -392,12 +392,6 @@ /obj/structure/filingcabinet, /turf/simulated/floor/pod/dark, /area/ruin/powered/snow_biodome) -"UM" = ( -/obj/machinery/computer/monitor/secret{ - dir = 1 - }, -/turf/simulated/floor/pod/dark, -/area/ruin/powered/snow_biodome) "Wg" = ( /turf/simulated/wall/r_wall, /area/ruin/powered/snow_biodome) @@ -484,7 +478,7 @@ HP tl PK gz -UM +HP Wg ak ak diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm index 281346d9b8f..be8f849400f 100644 --- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm +++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm @@ -1205,9 +1205,9 @@ /turf/simulated/floor/plating/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) "dg" = ( -/obj/structure/bonfire/dense, /obj/structure/stone_tile/center, /obj/effect/mapping_helpers/no_lava, +/obj/structure/bonfire, /turf/simulated/floor/plating/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) "di" = ( diff --git a/_maps/map_files/RandomRuins/SpaceRuins/asteroid1.dmm b/_maps/map_files/RandomRuins/SpaceRuins/asteroid1.dmm index 5dae1cf1452..de19f1ecbc8 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/asteroid1.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/asteroid1.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /turf/simulated/floor/plating/asteroid/airless, /area/ruin/unpowered) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/asteroid2.dmm b/_maps/map_files/RandomRuins/SpaceRuins/asteroid2.dmm index aace1d84625..6a90a5dd587 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/asteroid2.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/asteroid2.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /turf/simulated/floor/plating/asteroid/airless, /area/ruin/unpowered) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/asteroid3.dmm b/_maps/map_files/RandomRuins/SpaceRuins/asteroid3.dmm index a569b957ebf..121b5cff321 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/asteroid3.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/asteroid3.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /turf/simulated/floor/plating/asteroid/airless, /area/ruin/unpowered) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/asteroid4.dmm b/_maps/map_files/RandomRuins/SpaceRuins/asteroid4.dmm index fb51d7aabc1..fb1039beb6f 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/asteroid4.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/asteroid4.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /turf/simulated/mineral, /area/ruin/unpowered) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/asteroid5.dmm b/_maps/map_files/RandomRuins/SpaceRuins/asteroid5.dmm index aae4ff9e9c7..d7f38907b30 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/asteroid5.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/asteroid5.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /turf/simulated/mineral, /area/ruin/unpowered) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/debris1.dmm b/_maps/map_files/RandomRuins/SpaceRuins/debris1.dmm new file mode 100644 index 00000000000..af01a5bc274 --- /dev/null +++ b/_maps/map_files/RandomRuins/SpaceRuins/debris1.dmm @@ -0,0 +1,614 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/template_noop, +/area/template_noop) +"b" = ( +/obj/structure/disposalpipe/segment, +/turf/space, +/area/space) +"c" = ( +/obj/structure/lattice, +/obj/structure/disposalpipe/broken{ + dir = 1 + }, +/turf/space, +/area/space) +"d" = ( +/obj/structure/lattice, +/turf/space, +/area/template_noop) +"e" = ( +/obj/structure/grille/broken, +/turf/space, +/area/space) +"g" = ( +/obj/structure/disposalpipe/broken{ + dir = 4 + }, +/turf/space, +/area/space) +"h" = ( +/obj/structure/disposalpipe/broken{ + dir = 8 + }, +/obj/item/stack/cable_coil/cut, +/turf/space, +/area/space) +"l" = ( +/obj/item/stack/cable_coil/cut, +/turf/space, +/area/space) +"m" = ( +/obj/effect/spawner/random_spawners/grille_often, +/turf/space, +/area/space) +"n" = ( +/obj/effect/spawner/random_spawners/grille_maybe, +/turf/space, +/area/space) +"o" = ( +/obj/random/tool, +/turf/space, +/area/space) +"p" = ( +/obj/structure/girder/displaced, +/turf/space, +/area/space) +"r" = ( +/turf/space, +/area/template_noop) +"t" = ( +/obj/structure/cable{ + d1 = 2; + d2 = 8; + icon_state = "2-8" + }, +/turf/space, +/area/space) +"u" = ( +/obj/structure/door_assembly/door_assembly_sec, +/obj/item/airlock_electronics, +/turf/space, +/area/space) +"v" = ( +/obj/structure/lattice, +/turf/space, +/area/space) +"x" = ( +/obj/structure/disposalpipe/junction, +/turf/space, +/area/space) +"y" = ( +/turf/simulated/floor/plating/burnt, +/area/space) +"z" = ( +/obj/item/stack/sheet/metal, +/turf/simulated/floor/plating, +/area/space) +"B" = ( +/obj/structure/lattice, +/obj/item/stack/sheet/metal, +/turf/space, +/area/space) +"C" = ( +/obj/structure/disposalpipe/broken{ + dir = 1 + }, +/turf/space, +/area/space) +"D" = ( +/obj/structure/cable{ + d1 = 4; + d2 = 8; + icon_state = "4-8"; + pixel_y = 0; + tag = "" + }, +/turf/simulated/floor/plating/burnt, +/area/space) +"E" = ( +/obj/item/stack/rods, +/turf/space, +/area/space) +"F" = ( +/turf/simulated/floor/plating, +/area/space) +"G" = ( +/obj/random/toolbox, +/turf/simulated/floor/plating/burnt, +/area/space) +"I" = ( +/obj/structure/grille/broken, +/obj/structure/lattice, +/turf/space, +/area/space) +"J" = ( +/obj/structure/disposalpipe/broken, +/turf/space, +/area/space) +"K" = ( +/obj/item/shard, +/turf/space, +/area/space) +"N" = ( +/obj/structure/lattice, +/obj/structure/lattice, +/turf/space, +/area/space) +"O" = ( +/obj/machinery/power/apc/worn_out{ + pixel_y = -24 + }, +/obj/structure/cable, +/turf/simulated/floor/plating, +/area/space) +"Q" = ( +/obj/effect/decal/cleanable/shreds{ + pixel_x = 4; + pixel_y = 3 + }, +/turf/simulated/floor/plating, +/area/space) +"T" = ( +/obj/structure/girder, +/turf/simulated/floor/plating/burnt, +/area/space) +"U" = ( +/obj/effect/spawner/random_barrier/wall_probably, +/turf/space, +/area/space) +"V" = ( +/turf/simulated/wall, +/area/space) +"X" = ( +/turf/space, +/area/space) +"Y" = ( +/obj/item/shard, +/turf/simulated/floor/plating, +/area/space) +"Z" = ( +/obj/item/stack/sheet/metal, +/turf/space, +/area/space) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +a +r +r +r +r +r +r +r +r +r +d +d +d +r +r +d +r +a +a +"} +(3,1,1) = {" +a +d +v +X +X +X +X +X +X +X +X +X +X +X +X +X +X +X +r +a +"} +(4,1,1) = {" +a +d +X +m +X +X +X +X +X +X +X +X +X +X +v +Z +X +X +d +a +"} +(5,1,1) = {" +a +r +X +g +F +X +E +I +X +X +v +X +F +v +v +X +X +X +r +a +"} +(6,1,1) = {" +a +r +J +x +C +E +v +F +X +E +X +v +g +Z +X +X +F +X +r +a +"} +(7,1,1) = {" +a +r +X +X +y +E +X +E +Y +X +n +X +h +E +F +X +v +X +r +a +"} +(8,1,1) = {" +a +r +X +X +X +B +l +p +X +X +y +p +X +X +X +E +X +X +r +a +"} +(9,1,1) = {" +a +r +X +v +X +o +X +X +Z +l +X +E +a +X +X +V +X +X +r +a +"} +(10,1,1) = {" +a +r +X +Z +Q +v +X +y +v +D +y +U +X +X +E +X +X +n +r +a +"} +(11,1,1) = {" +a +r +X +X +I +v +v +X +v +t +O +V +X +X +F +E +Z +X +d +a +"} +(12,1,1) = {" +a +r +X +X +X +X +J +b +c +v +G +V +X +E +e +X +X +X +r +a +"} +(13,1,1) = {" +a +r +X +X +X +V +X +Z +X +X +X +u +X +X +X +X +e +Z +r +a +"} +(14,1,1) = {" +a +r +X +X +X +X +v +X +v +N +E +E +K +X +v +o +X +X +r +a +"} +(15,1,1) = {" +a +r +X +E +X +X +X +X +E +J +C +X +X +X +X +X +X +X +d +a +"} +(16,1,1) = {" +a +r +X +X +E +X +T +X +Z +X +n +v +X +z +n +X +Z +X +d +a +"} +(17,1,1) = {" +a +r +X +X +X +X +X +X +X +X +F +X +X +X +p +X +X +X +r +a +"} +(18,1,1) = {" +a +r +X +X +X +X +X +X +X +X +X +X +X +v +X +X +X +X +r +a +"} +(19,1,1) = {" +a +a +r +r +r +r +d +r +d +r +r +r +r +r +r +r +r +r +a +a +"} +(20,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm b/_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm new file mode 100644 index 00000000000..6a6cd81f32a --- /dev/null +++ b/_maps/map_files/RandomRuins/SpaceRuins/debris2.dmm @@ -0,0 +1,631 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/template_noop, +/area/template_noop) +"b" = ( +/turf/simulated/floor/plating/airless, +/area/space) +"c" = ( +/turf/simulated/floor/plasteel/airless, +/area/space) +"d" = ( +/turf/simulated/wall/r_wall, +/area/space) +"e" = ( +/obj/item/stack/cable_coil/cut, +/turf/space, +/area/template_noop) +"h" = ( +/obj/structure/girder, +/turf/space, +/area/template_noop) +"j" = ( +/obj/structure/grille/broken, +/turf/space, +/area/space) +"l" = ( +/obj/structure/girder, +/turf/simulated/floor/plating, +/area/space) +"m" = ( +/obj/structure/grille/broken, +/turf/template_noop, +/area/space) +"n" = ( +/obj/item/stack/cable_coil/cut, +/turf/template_noop, +/area/space) +"o" = ( +/obj/machinery/door/airlock/command{ + max_integrity = 40 + }, +/turf/simulated/floor/plating/burnt, +/area/space) +"p" = ( +/mob/living/simple_animal/hostile/carp, +/turf/space, +/area/template_noop) +"q" = ( +/obj/structure/lattice, +/turf/space, +/area/space) +"s" = ( +/obj/item/stack/rods, +/turf/space, +/area/template_noop) +"t" = ( +/obj/structure/door_assembly/door_assembly_com, +/turf/space, +/area/template_noop) +"u" = ( +/obj/item/clothing/head/bio_hood/virology, +/turf/space, +/area/space) +"w" = ( +/obj/structure/lattice, +/turf/template_noop, +/area/space) +"x" = ( +/turf/template_noop, +/area/space) +"y" = ( +/obj/structure/lattice, +/obj/structure/door_assembly/door_assembly_vault{ + anchored = 1 + }, +/turf/simulated/floor/plating, +/area/space) +"z" = ( +/obj/item/stack/rods, +/turf/template_noop, +/area/space) +"A" = ( +/obj/structure/closet/crate, +/obj/item/reagent_containers/glass/beaker/bluespace, +/turf/simulated/floor/plasteel/airless, +/area/space) +"B" = ( +/obj/item/shard{ + icon_state = "small" + }, +/turf/space, +/area/space) +"C" = ( +/obj/item/stack/sheet/metal, +/turf/space, +/area/template_noop) +"D" = ( +/obj/effect/landmark/burnturf, +/turf/simulated/floor/plating/airless, +/area/space) +"E" = ( +/obj/item/reagent_containers/glass/beaker/large, +/turf/space, +/area/template_noop) +"F" = ( +/obj/effect/landmark/burnturf, +/turf/simulated/floor, +/area/space) +"G" = ( +/obj/structure/safe, +/obj/item/stack/sheet/mineral/bananium{ + amount = 5 + }, +/obj/item/stack/ore/bluespace_crystal/refined{ + amount = 3 + }, +/obj/item/dnainjector/comic, +/turf/simulated/floor/plasteel/airless, +/area/space) +"H" = ( +/obj/structure/table, +/obj/item/shard{ + icon_state = "medium" + }, +/turf/simulated/floor/plasteel/airless, +/area/space) +"I" = ( +/turf/space, +/area/template_noop) +"J" = ( +/mob/living/simple_animal/hostile/carp, +/turf/space, +/area/space) +"L" = ( +/obj/item/shard, +/turf/space, +/area/space) +"M" = ( +/turf/simulated/floor, +/area/space) +"N" = ( +/obj/item/clothing/suit/bio_suit/virology, +/turf/space, +/area/space) +"O" = ( +/obj/item/shard{ + icon_state = "small" + }, +/turf/space, +/area/template_noop) +"P" = ( +/obj/item/reagent_containers/spray/cleaner, +/turf/space, +/area/space) +"R" = ( +/obj/item/stack/sheet/metal, +/turf/template_noop, +/area/space) +"S" = ( +/obj/item/reagent_containers/spray/cleaner, +/turf/space, +/area/template_noop) +"T" = ( +/turf/simulated/wall, +/area/space) +"U" = ( +/obj/effect/landmark/burnturf, +/turf/simulated/floor/plasteel/airless, +/area/space) +"V" = ( +/obj/structure/lattice, +/turf/space, +/area/template_noop) +"W" = ( +/obj/effect/landmark/burnturf, +/turf/simulated/floor/plating/burnt, +/area/space) +"X" = ( +/turf/space, +/area/space) +"Y" = ( +/obj/structure/girder/reinforced, +/turf/simulated/floor/plating/damaged, +/area/space) +"Z" = ( +/obj/machinery/computer/pandemic{ + circuit = /obj/effect/decal/cleanable/shreds + }, +/turf/simulated/floor/plasteel/airless, +/area/space) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +d +d +d +d +d +Y +x +S +I +I +I +I +I +I +C +I +I +I +a +"} +(3,1,1) = {" +a +d +G +A +U +F +d +z +T +T +T +T +o +T +I +p +O +I +I +a +"} +(4,1,1) = {" +a +Y +c +U +c +U +d +X +w +M +c +U +M +T +V +I +I +I +I +a +"} +(5,1,1) = {" +a +Y +M +F +F +q +d +z +m +F +M +F +M +T +I +I +I +I +I +a +"} +(6,1,1) = {" +a +d +d +y +d +d +Y +X +w +X +n +X +w +T +I +I +I +C +I +a +"} +(7,1,1) = {" +a +X +X +X +X +X +P +J +X +T +T +T +x +T +I +I +V +I +I +a +"} +(8,1,1) = {" +a +X +X +X +X +X +X +X +R +X +w +X +b +x +x +I +I +I +I +a +"} +(9,1,1) = {" +a +p +s +T +m +w +T +R +X +B +X +w +X +x +z +I +s +I +I +a +"} +(10,1,1) = {" +a +s +I +T +M +F +T +X +X +X +X +X +X +x +x +I +I +I +I +a +"} +(11,1,1) = {" +a +I +I +T +c +F +T +X +X +c +F +b +j +D +T +I +I +I +t +a +"} +(12,1,1) = {" +a +C +I +T +X +M +N +s +I +I +L +q +q +b +T +I +s +I +I +a +"} +(13,1,1) = {" +a +I +I +l +T +T +T +I +I +u +j +W +c +U +T +I +I +I +I +a +"} +(14,1,1) = {" +a +I +s +p +I +I +I +I +p +I +I +c +H +Z +T +I +I +I +I +a +"} +(15,1,1) = {" +a +V +I +I +s +V +C +I +I +s +h +T +T +T +T +I +I +I +I +a +"} +(16,1,1) = {" +a +I +I +E +I +I +V +I +O +I +V +I +I +I +I +I +C +I +I +a +"} +(17,1,1) = {" +a +I +I +C +I +e +I +I +I +I +I +e +I +V +I +I +I +I +I +a +"} +(18,1,1) = {" +a +I +I +I +I +I +I +h +I +I +C +I +I +I +I +I +I +I +I +a +"} +(19,1,1) = {" +a +I +I +I +I +I +I +I +I +I +I +I +I +I +C +I +I +I +I +a +"} +(20,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/RandomRuins/SpaceRuins/debris3.dmm b/_maps/map_files/RandomRuins/SpaceRuins/debris3.dmm new file mode 100644 index 00000000000..54d8725b3c4 --- /dev/null +++ b/_maps/map_files/RandomRuins/SpaceRuins/debris3.dmm @@ -0,0 +1,824 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/template_noop, +/area/template_noop) +"b" = ( +/obj/item/stack/rods, +/turf/simulated/floor/plating/damaged, +/area/template_noop) +"c" = ( +/obj/item/trash/popcorn, +/turf/space, +/area/template_noop) +"d" = ( +/obj/effect/spawner/random_spawners/wall_rusted_maybe, +/turf/space, +/area/template_noop) +"e" = ( +/obj/item/poster/random_contraband, +/turf/space, +/area/template_noop) +"f" = ( +/obj/structure/lattice, +/obj/item/stack/sheet/metal, +/turf/space, +/area/template_noop) +"g" = ( +/obj/item/paperplane, +/turf/space, +/area/template_noop) +"h" = ( +/obj/item/trash/can, +/turf/space, +/area/template_noop) +"i" = ( +/obj/item/stack/rods, +/turf/template_noop, +/area/template_noop) +"k" = ( +/obj/item/shard, +/turf/space, +/area/template_noop) +"l" = ( +/obj/item/stack/cable_coil{ + amount = 10 + }, +/turf/space, +/area/template_noop) +"m" = ( +/turf/simulated/wall, +/area/template_noop) +"n" = ( +/obj/item/stack/sheet/glass, +/turf/space, +/area/template_noop) +"p" = ( +/obj/random/plushie, +/turf/space, +/area/template_noop) +"q" = ( +/obj/item/stack/rods, +/obj/item/stack/sheet/metal, +/turf/space, +/area/template_noop) +"t" = ( +/obj/machinery/computer/arcade/battle, +/turf/simulated/floor/plasteel/airless, +/area/template_noop) +"u" = ( +/obj/random/tool, +/turf/space, +/area/template_noop) +"v" = ( +/obj/item/trash/syndi_cakes, +/turf/space, +/area/template_noop) +"w" = ( +/obj/item/stack/tickets, +/turf/space, +/area/template_noop) +"x" = ( +/obj/structure/bookcase/random/fiction, +/turf/simulated/floor/plating/damaged, +/area/template_noop) +"y" = ( +/obj/item/storage/pill_bottle/random_meds/labelled, +/turf/space, +/area/template_noop) +"z" = ( +/turf/simulated/floor/plating/damaged, +/area/template_noop) +"A" = ( +/turf/space, +/area/template_noop) +"B" = ( +/obj/item/stack/rods, +/turf/space, +/area/template_noop) +"D" = ( +/obj/item/paper, +/obj/item/paper, +/turf/space, +/area/template_noop) +"F" = ( +/obj/random/tool, +/turf/simulated/floor/plating/damaged, +/area/template_noop) +"G" = ( +/obj/item/poster/random_official, +/turf/space, +/area/template_noop) +"H" = ( +/obj/item/stack/sheet/wood, +/turf/space, +/area/template_noop) +"I" = ( +/obj/item/stack/rods, +/obj/item/stack/rods, +/turf/space, +/area/template_noop) +"J" = ( +/obj/item/trash/pistachios, +/turf/space, +/area/template_noop) +"K" = ( +/obj/structure/lattice, +/obj/structure/girder, +/turf/space, +/area/template_noop) +"L" = ( +/obj/structure/girder, +/turf/space, +/area/template_noop) +"M" = ( +/obj/item/stack/sheet/metal, +/turf/space, +/area/template_noop) +"O" = ( +/obj/structure/girder/displaced, +/turf/space, +/area/template_noop) +"S" = ( +/obj/structure/lattice, +/turf/space, +/area/template_noop) +"U" = ( +/obj/item/stack/sheet/wood, +/obj/item/stack/sheet/wood, +/turf/space, +/area/template_noop) +"V" = ( +/obj/item/circuitboard/arcade, +/turf/space, +/area/template_noop) +"W" = ( +/obj/item/stack/tickets, +/obj/item/stack/tickets, +/obj/item/stack/tickets, +/turf/space, +/area/template_noop) +"Z" = ( +/obj/item/paper, +/turf/space, +/area/template_noop) + +(1,1,1) = {" +a +a +a +i +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +a +a +a +a +a +a +a +a +a +i +a +a +a +a +a +i +a +a +a +"} +(3,1,1) = {" +a +a +A +A +A +A +A +A +A +A +A +A +A +A +A +A +A +A +a +a +"} +(4,1,1) = {" +a +a +A +B +A +B +A +A +A +A +A +A +A +A +B +B +A +A +a +a +"} +(5,1,1) = {" +i +a +A +A +A +A +A +w +A +M +A +A +A +A +A +A +A +A +a +i +"} +(6,1,1) = {" +a +a +A +A +I +A +B +A +A +B +B +M +A +A +M +A +A +A +a +a +"} +(7,1,1) = {" +a +a +A +A +k +A +A +O +A +A +A +A +m +A +w +A +M +A +a +a +"} +(8,1,1) = {" +a +a +A +B +A +M +A +A +H +A +G +n +m +A +A +B +A +A +a +a +"} +(9,1,1) = {" +a +a +A +A +m +A +w +A +M +S +A +A +d +B +M +A +O +A +a +a +"} +(10,1,1) = {" +a +a +A +w +A +A +Z +A +W +B +S +h +M +L +A +c +A +A +a +a +"} +(11,1,1) = {" +a +i +B +w +A +S +A +F +A +A +S +A +Z +A +A +A +M +A +a +a +"} +(12,1,1) = {" +a +a +A +A +A +A +H +L +S +m +m +b +A +A +Z +w +B +A +i +a +"} +(13,1,1) = {" +a +a +A +A +B +O +A +B +A +M +A +D +K +z +S +H +B +A +a +a +"} +(14,1,1) = {" +a +a +A +n +A +y +f +B +A +z +B +A +m +t +A +A +A +A +a +a +"} +(15,1,1) = {" +a +a +A +A +z +H +A +A +A +S +A +q +z +z +A +A +O +A +a +a +"} +(16,1,1) = {" +a +a +A +A +S +A +S +U +B +B +A +A +S +A +M +B +u +A +a +i +"} +(17,1,1) = {" +a +a +A +M +H +w +Z +B +x +S +B +S +A +k +J +A +A +A +a +a +"} +(18,1,1) = {" +a +a +B +A +A +A +A +A +A +w +B +z +A +O +A +M +A +A +a +a +"} +(19,1,1) = {" +i +a +A +A +M +z +S +w +S +M +A +m +p +A +A +B +A +A +a +a +"} +(20,1,1) = {" +a +a +A +B +A +O +H +v +A +V +H +A +B +L +A +M +A +w +a +a +"} +(21,1,1) = {" +a +a +A +A +A +A +e +M +A +w +Z +w +B +A +w +A +A +A +a +a +"} +(22,1,1) = {" +a +a +A +A +M +O +m +m +L +A +B +A +A +q +A +A +A +A +a +a +"} +(23,1,1) = {" +a +a +A +B +k +A +B +A +B +g +n +A +O +A +A +B +B +A +a +a +"} +(24,1,1) = {" +a +a +A +A +A +A +A +M +A +A +l +A +w +A +A +A +A +A +a +a +"} +(25,1,1) = {" +a +a +A +w +B +A +A +A +A +A +A +B +A +A +A +w +A +A +a +a +"} +(26,1,1) = {" +a +a +A +A +A +A +A +A +B +A +B +A +A +B +A +B +A +A +a +a +"} +(27,1,1) = {" +a +a +B +A +w +A +B +A +w +A +B +A +A +A +A +A +A +w +a +a +"} +(28,1,1) = {" +a +a +A +A +A +A +A +A +A +w +A +A +A +A +A +A +A +A +a +i +"} +(29,1,1) = {" +i +a +a +a +a +a +a +i +a +a +a +a +a +a +a +a +a +a +a +a +"} +(30,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +i +a +a +"} diff --git a/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm b/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm index 8bb87d2026e..7d0db9f8eb3 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/deepstorage.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "aa" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "ab" = ( /turf/simulated/mineral/random/low_chance, /area/ruin/unpowered) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/derelict1.dmm b/_maps/map_files/RandomRuins/SpaceRuins/derelict1.dmm index 8b094749545..a78a8e88151 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/derelict1.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/derelict1.dmm @@ -1,11 +1,11 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /obj/structure/lattice, -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "c" = ( /turf/simulated/wall, /area/ruin/unpowered) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/derelict2.dmm b/_maps/map_files/RandomRuins/SpaceRuins/derelict2.dmm index 95ad761e62c..f3b7a74c0b3 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/derelict2.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/derelict2.dmm @@ -1,14 +1,14 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /obj/structure/window/reinforced{ tag = "icon-rwindow (EAST)"; icon_state = "rwindow"; dir = 4 }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "c" = ( /obj/machinery/door/airlock/external, @@ -20,7 +20,7 @@ icon_state = "rwindow"; dir = 8 }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "e" = ( /obj/structure/window/reinforced{ @@ -64,7 +64,7 @@ /area/ruin/powered) "k" = ( /obj/structure/window/reinforced, -/turf/space, +/turf/template_noop, /area/space/nearstation) "l" = ( /obj/structure/window/reinforced, @@ -112,7 +112,7 @@ icon_state = "rwindow"; dir = 1 }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "q" = ( /obj/machinery/light/small{ diff --git a/_maps/map_files/RandomRuins/SpaceRuins/derelict3.dmm b/_maps/map_files/RandomRuins/SpaceRuins/derelict3.dmm index be313cd7770..56cd71d71c6 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/derelict3.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/derelict3.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /turf/simulated/floor/plating/airless, /area/ruin/unpowered) @@ -10,12 +10,12 @@ /area/ruin/unpowered) "d" = ( /obj/structure/lattice, -/turf/space, +/turf/template_noop, /area/space/nearstation) "e" = ( /obj/structure/lattice, /obj/structure/lattice, -/turf/space, +/turf/template_noop, /area/space/nearstation) (1,1,1) = {" diff --git a/_maps/map_files/RandomRuins/SpaceRuins/derelict4.dmm b/_maps/map_files/RandomRuins/SpaceRuins/derelict4.dmm index 1c7d14bd6fa..ce660407cca 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/derelict4.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/derelict4.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /turf/simulated/mineral, /area/ruin/unpowered) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/derelict5.dmm b/_maps/map_files/RandomRuins/SpaceRuins/derelict5.dmm index ff8a5ac4cc2..fe74e127741 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/derelict5.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/derelict5.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /turf/simulated/mineral, /area/ruin/unpowered) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/emptyshell.dmm b/_maps/map_files/RandomRuins/SpaceRuins/emptyshell.dmm index 4c33095219a..0f30191dfb6 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/emptyshell.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/emptyshell.dmm @@ -1,11 +1,11 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /obj/structure/lattice, -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "c" = ( /turf/simulated/wall/r_wall, /area/ruin/unpowered) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/gasthelizards.dmm b/_maps/map_files/RandomRuins/SpaceRuins/gasthelizards.dmm index 0f7c36fe634..a8b394a6421 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/gasthelizards.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/gasthelizards.dmm @@ -1,11 +1,11 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /obj/structure/lattice, -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "c" = ( /turf/simulated/wall, /area/ruin/unpowered) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm b/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm index a993ab96190..ad1ca6cf96a 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/intactemptyship.dmm @@ -1,9 +1,9 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( -/turf/space, +/turf/template_noop, /turf/simulated/shuttle/wall{ dir = 8; icon_state = "diagonalWall3" @@ -15,7 +15,7 @@ }, /area/ruin/powered) "d" = ( -/turf/space, +/turf/template_noop, /turf/simulated/shuttle/wall{ dir = 2; icon_state = "diagonalWall3" @@ -198,7 +198,7 @@ }, /area/ruin/powered) "A" = ( -/turf/space, +/turf/template_noop, /turf/simulated/shuttle/wall{ dir = 1; icon_state = "diagonalWall3" @@ -212,7 +212,7 @@ }, /area/ruin/powered) "C" = ( -/turf/space, +/turf/template_noop, /turf/simulated/shuttle/wall{ dir = 4; icon_state = "diagonalWall3" diff --git a/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm b/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm index 17d49d666bc..5607083f0a8 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/listeningpost.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /turf/simulated/mineral, /area/ruin/powered) @@ -73,8 +73,7 @@ }, /mob/living/simple_animal/hostile/syndicate{ desc = "A weary looking syndicate operative."; - environment_smash = 0; - faction = "syndicate" + environment_smash = 0 }, /turf/simulated/floor/plasteel, /area/ruin/powered) @@ -100,6 +99,12 @@ /obj/item/tank/air, /turf/simulated/floor/plasteel, /area/ruin/powered) +"u" = ( +/obj/machinery/tcomms/relay/ruskie{ + network_id = "LISTENINGOUTPOST-RELAY" + }, +/turf/simulated/floor/plasteel, +/area/ruin/powered) "w" = ( /obj/machinery/light/small, /turf/simulated/floor/plasteel, @@ -923,7 +928,7 @@ b f f f -c +f f D m @@ -965,7 +970,7 @@ f f p Y -f +u f j i diff --git a/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm b/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm index b4e36942d6a..99b254e2684 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/mechtransport.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /turf/simulated/shuttle/wall{ tag = "icon-swall_s6"; @@ -168,7 +168,7 @@ /area/ruin/powered) "D" = ( /obj/structure/lattice, -/turf/space, +/turf/template_noop, /area/space/nearstation) "E" = ( /obj/item/stack/rods, @@ -202,7 +202,7 @@ /area/ruin/powered) "K" = ( /obj/item/stack/sheet/metal, -/turf/space, +/turf/template_noop, /area/space/nearstation) "L" = ( /obj/structure/mecha_wreckage/gygax, @@ -228,7 +228,7 @@ /area/ruin/powered) "P" = ( /obj/item/stack/rods, -/turf/space, +/turf/template_noop, /area/space/nearstation) "Q" = ( /turf/simulated/shuttle/wall{ diff --git a/_maps/map_files/RandomRuins/SpaceRuins/onehalf.dmm b/_maps/map_files/RandomRuins/SpaceRuins/onehalf.dmm index fa38a1a647b..93dc1fffed4 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/onehalf.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/onehalf.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "aa" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "ab" = ( /obj/structure/lattice, /obj/structure/cable{ @@ -10,7 +10,7 @@ icon_state = "2-4"; tag = "" }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "ac" = ( /obj/structure/lattice, @@ -20,7 +20,7 @@ icon_state = "4-8"; tag = "" }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "ad" = ( /obj/structure/lattice, @@ -29,7 +29,7 @@ d2 = 8; icon_state = "2-8" }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "ae" = ( /obj/structure/lattice, @@ -38,7 +38,7 @@ dir = 2; icon_state = "coil_red2" }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "af" = ( /obj/structure/lattice, @@ -48,7 +48,7 @@ icon_state = "1-8"; tag = "" }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "ag" = ( /turf/simulated/wall, @@ -65,7 +65,7 @@ d2 = 2; icon_state = "1-2" }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "aj" = ( /turf/unsimulated/floor/plating/airless, @@ -154,11 +154,11 @@ /area/ruin/onehalf/drone_bay) "aw" = ( /obj/structure/lattice, -/turf/space, +/turf/template_noop, /area/space/nearstation) "ax" = ( /obj/structure/lattice, -/turf/space, +/turf/template_noop, /area/ruin/onehalf/hallway) "ay" = ( /obj/structure/table_frame, @@ -224,7 +224,7 @@ /area/ruin/onehalf/drone_bay) "aH" = ( /obj/item/stack/rods, -/turf/space, +/turf/template_noop, /area/ruin/onehalf/hallway) "aI" = ( /obj/effect/landmark/damageturf, @@ -805,7 +805,7 @@ /area/ruin/onehalf/bridge) "bU" = ( /obj/item/stack/sheet/metal, -/turf/space, +/turf/template_noop, /area/space/nearstation) "bV" = ( /obj/structure/disposalpipe/segment, @@ -914,7 +914,7 @@ dir = 4 }, /obj/item/stack/rods, -/turf/space, +/turf/template_noop, /area/ruin/onehalf/hallway) "cn" = ( /obj/structure/lattice, @@ -925,7 +925,7 @@ tag = "icon-small"; icon_state = "small" }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "co" = ( /obj/structure/grille, @@ -1008,7 +1008,7 @@ /area/ruin/onehalf/bridge) "cv" = ( /obj/item/stack/rods, -/turf/space, +/turf/template_noop, /area/space/nearstation) "cw" = ( /turf/unsimulated/floor/plating/airless, @@ -1016,7 +1016,7 @@ "cx" = ( /obj/structure/lattice, /obj/item/stack/sheet/plasteel, -/turf/space, +/turf/template_noop, /area/space/nearstation) "cy" = ( /obj/structure/lattice, @@ -1024,7 +1024,7 @@ tag = "icon-medium"; icon_state = "medium" }, -/turf/space, +/turf/template_noop, /area/ruin/onehalf/hallway) "cz" = ( /obj/structure/grille/broken, @@ -1071,10 +1071,10 @@ /area/ruin/onehalf/bridge) "cE" = ( /obj/item/stack/tile/wood, -/turf/space, +/turf/template_noop, /area/space/nearstation) "cF" = ( -/turf/space, +/turf/template_noop, /area/ruin/onehalf/hallway) "cG" = ( /obj/structure/lattice, @@ -1083,7 +1083,7 @@ d2 = 8; icon_state = "2-8" }, -/turf/space, +/turf/template_noop, /area/ruin/onehalf/hallway) "cH" = ( /obj/structure/lattice, @@ -1097,7 +1097,7 @@ icon_state = "pipe-b"; dir = 8 }, -/turf/space, +/turf/template_noop, /area/ruin/onehalf/hallway) "cI" = ( /obj/machinery/door/airlock/command/glass{ @@ -1149,7 +1149,7 @@ dir = 2; icon_state = "coil_red2" }, -/turf/space, +/turf/template_noop, /area/ruin/onehalf/hallway) "cQ" = ( /obj/machinery/light{ @@ -1160,7 +1160,7 @@ /area/ruin/onehalf/bridge) "cR" = ( /obj/item/stack/sheet/plasteel, -/turf/space, +/turf/template_noop, /area/space/nearstation) "cS" = ( /obj/structure/lattice, @@ -1169,7 +1169,7 @@ d2 = 2; icon_state = "1-2" }, -/turf/space, +/turf/template_noop, /area/ruin/onehalf/hallway) "cU" = ( /obj/structure/grille, @@ -1221,7 +1221,7 @@ d2 = 8; icon_state = "2-8" }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "db" = ( /obj/structure/table, @@ -1256,7 +1256,7 @@ d2 = 2; icon_state = "1-2" }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "dg" = ( /obj/structure/girder/reinforced, @@ -1350,7 +1350,7 @@ tag = "icon-medium"; icon_state = "medium" }, -/turf/space, +/turf/template_noop, /area/space/nearstation) "dp" = ( /obj/structure/lattice, @@ -1366,7 +1366,7 @@ icon_state = "1-4" }, /obj/item/stack/rods, -/turf/space, +/turf/template_noop, /area/space/nearstation) "dq" = ( /obj/structure/lattice, @@ -1382,7 +1382,7 @@ icon_state = "1-8"; tag = "" }, -/turf/space, +/turf/template_noop, /area/space/nearstation) (1,1,1) = {" diff --git a/_maps/map_files/RandomRuins/SpaceRuins/spacebar.dmm b/_maps/map_files/RandomRuins/SpaceRuins/spacebar.dmm index 108c8103975..1f9725a3d03 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/spacebar.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/spacebar.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "aa" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "ab" = ( /turf/simulated/mineral, /area/space/nearstation) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm b/_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm new file mode 100644 index 00000000000..ec11a787510 --- /dev/null +++ b/_maps/map_files/RandomRuins/SpaceRuins/syndiecakesfactory.dmm @@ -0,0 +1,2284 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"aM" = ( +/obj/item/stack/rods, +/obj/item/stack/sheet/metal, +/turf/template_noop, +/area/space) +"bb" = ( +/obj/structure/table/reinforced, +/obj/item/stack/sheet/mineral/plasma, +/obj/item/stack/sheet/mineral/plasma, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"bk" = ( +/obj/structure/grille/broken, +/turf/template_noop, +/area/space) +"cc" = ( +/obj/item/reagent_containers/food/snacks/syndicake, +/obj/item/reagent_containers/food/snacks/syndicake, +/obj/item/reagent_containers/food/snacks/syndicake, +/turf/simulated/floor/plating, +/area/space) +"dy" = ( +/obj/structure/lattice, +/turf/unsimulated, +/area/template_noop) +"es" = ( +/obj/structure/cable{ + d1 = 1; + d2 = 8; + icon_state = "1-8" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"eY" = ( +/turf/simulated/floor/plating, +/area/ruin) +"fq" = ( +/obj/machinery/autolathe, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"fU" = ( +/obj/machinery/door/airlock/mining/glass{ + locked = 1; + name = "Egg processing" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"fY" = ( +/obj/item/stack/sheet/metal, +/turf/template_noop, +/area/template_noop) +"hm" = ( +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"ho" = ( +/turf/space, +/area/ruin/space) +"hs" = ( +/turf/unsimulated/wall/fakeglass{ + dir = 4 + }, +/area/ruin/unpowered) +"iK" = ( +/obj/item/stack/sheet/metal, +/obj/item/stack/sheet/metal, +/turf/space, +/area/ruin/space) +"jf" = ( +/obj/item/trash/syndi_cakes, +/turf/template_noop, +/area/template_noop) +"jv" = ( +/obj/item/stack/cable_coil/cut, +/turf/template_noop, +/area/space) +"jR" = ( +/obj/structure/table/reinforced, +/obj/item/storage/fancy/cigarettes/cigpack_random, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"ke" = ( +/obj/structure/lattice, +/turf/space, +/area/ruin) +"kh" = ( +/obj/structure/lattice, +/obj/item/stack/rods, +/turf/template_noop, +/area/space) +"ky" = ( +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2"; + tag = "" + }, +/turf/simulated/wall, +/area/ruin/unpowered) +"ls" = ( +/obj/structure/lattice, +/turf/template_noop, +/area/space) +"lK" = ( +/obj/machinery/power/apc/noalarm{ + area = null; + dir = 8; + keep_preset_name = 1; + light_power = 0; + locked = 0; + max_integrity = 0; + name = "Manufacturing control room APC"; + pixel_y = -24; + shorted = 1; + start_charge = -1 + }, +/obj/structure/cable{ + d2 = 4; + icon_state = "0-4" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"mc" = ( +/obj/structure/grille/broken, +/turf/space, +/area/ruin/space) +"mx" = ( +/obj/item/trash/syndi_cakes, +/turf/template_noop, +/area/space) +"nV" = ( +/obj/machinery/conveyor/east{ + id = null + }, +/turf/simulated/floor/plasteel, +/area/space) +"ob" = ( +/obj/structure/lattice, +/turf/template_noop, +/area/ruin/unpowered) +"om" = ( +/obj/item/reagent_containers/food/snacks/syndicake, +/obj/item/reagent_containers/food/snacks/syndicake, +/obj/item/reagent_containers/food/snacks/syndicake, +/obj/item/reagent_containers/food/snacks/syndicake, +/obj/item/reagent_containers/food/snacks/syndicake, +/obj/item/reagent_containers/food/snacks/syndicake, +/turf/simulated/floor/plasteel, +/area/space) +"oJ" = ( +/obj/item/stack/rods, +/turf/template_noop, +/area/space) +"oO" = ( +/obj/machinery/door/airlock/mining/glass{ + name = "Nexus" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"pb" = ( +/obj/structure/lattice/catwalk, +/turf/template_noop, +/area/space) +"pd" = ( +/turf/template_noop, +/area/space) +"pp" = ( +/turf/unsimulated, +/area/space) +"pR" = ( +/obj/machinery/conveyor/northeast{ + id = null + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"qS" = ( +/obj/machinery/door/airlock/mining/glass{ + name = "EVA Storage" + }, +/obj/structure/cable{ + d1 = 4; + d2 = 8; + icon_state = "4-8"; + pixel_y = 0; + tag = "" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"rJ" = ( +/obj/structure/lattice, +/turf/space, +/area/ruin/space) +"rL" = ( +/obj/item/reagent_containers/food/snacks/syndicake, +/turf/simulated/floor/plasteel/airless, +/area/ruin) +"rU" = ( +/obj/machinery/space_heater, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"sd" = ( +/turf/simulated/floor/plating, +/area/space) +"sN" = ( +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"ub" = ( +/obj/item/stack/rods, +/turf/space, +/area/space) +"uA" = ( +/obj/structure/table/reinforced, +/obj/item/reagent_containers/food/snacks/syndicake, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"uF" = ( +/obj/machinery/conveyor/east{ + id = null + }, +/obj/machinery/gibber/autogibber, +/obj/structure/plasticflaps{ + canmove = 0 + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"wB" = ( +/obj/item/trash/syndi_cakes, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"xT" = ( +/turf/unsimulated/floor/plating/airless, +/area/space) +"yU" = ( +/obj/item/stack/sheet/metal, +/obj/item/stack/sheet/metal, +/turf/space, +/area/space) +"zd" = ( +/obj/machinery/power/apc/noalarm{ + area = null; + dir = 8; + keep_preset_name = 1; + locked = 0; + name = "EVA room APC"; + pixel_x = -24; + start_charge = -1 + }, +/obj/structure/cable{ + d2 = 4; + icon_state = "0-4" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"zo" = ( +/turf/simulated/wall, +/area/space) +"zt" = ( +/obj/item/stack/sheet/metal, +/turf/template_noop, +/area/space) +"zF" = ( +/turf/unsimulated/wall/fakeglass{ + dir = 8 + }, +/area/ruin/unpowered) +"zP" = ( +/obj/structure/rack{ + dir = 4 + }, +/obj/random/tool, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"zY" = ( +/obj/structure/table/reinforced, +/obj/machinery/light, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"AH" = ( +/obj/structure/lattice, +/obj/item/stack/sheet/metal, +/turf/template_noop, +/area/template_noop) +"AJ" = ( +/obj/effect/spawner/random_spawners/wall_rusted_probably, +/turf/simulated/wall, +/area/space) +"BG" = ( +/obj/machinery/door/airlock/mining/glass{ + locked = 1; + name = "Manufacturing Control Room" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"BI" = ( +/obj/structure/reagent_dispensers/fueltank, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Cp" = ( +/obj/structure/cable{ + d1 = 1; + d2 = 2; + icon_state = "1-2"; + tag = "" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"CY" = ( +/obj/item/stack/rods, +/turf/template_noop, +/area/template_noop) +"Da" = ( +/turf/space, +/area/space) +"Dz" = ( +/mob/living/simple_animal/pet/dog/corgi, +/obj/machinery/conveyor/east{ + id = null + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Ej" = ( +/obj/structure/closet/emcloset, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"EJ" = ( +/turf/simulated/wall, +/area/ruin/unpowered) +"Fw" = ( +/obj/item/stack/cable_coil/cut, +/turf/template_noop, +/area/template_noop) +"FO" = ( +/obj/machinery/suit_storage_unit/syndicate, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Gm" = ( +/obj/structure/cable{ + d1 = 4; + d2 = 8; + icon_state = "4-8"; + pixel_y = 0; + tag = "" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"GB" = ( +/mob/living/simple_animal/pet/dog/corgi, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Hx" = ( +/mob/living/simple_animal/pet/dog/corgi, +/mob/living/simple_animal/pet/dog/corgi, +/obj/machinery/conveyor/east{ + id = null + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Hy" = ( +/turf/template_noop, +/area/template_noop) +"HQ" = ( +/obj/structure/table/reinforced, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Ic" = ( +/turf/simulated/floor/plating/burnt, +/area/ruin) +"Id" = ( +/obj/effect/spawner/random_spawners/oil_maybe, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"IB" = ( +/obj/structure/lattice, +/turf/space, +/area/ruin/unpowered) +"JI" = ( +/obj/effect/spawner/random_spawners/syndicate/loot, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Ki" = ( +/obj/structure/lattice, +/turf/template_noop, +/area/template_noop) +"Kr" = ( +/obj/structure/girder, +/turf/simulated/floor/plating, +/area/ruin/unpowered) +"KD" = ( +/turf/simulated/floor/plating, +/area/ruin/unpowered) +"KS" = ( +/turf/unsimulated/floor/plating/airless, +/area/ruin) +"Le" = ( +/obj/structure/cable{ + d2 = 4; + icon_state = "0-4" + }, +/obj/machinery/power/port_gen/pacman, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Mi" = ( +/turf/unsimulated, +/area/template_noop) +"Mz" = ( +/turf/simulated/floor/plasteel, +/area/ruin) +"Nw" = ( +/obj/machinery/power/smes, +/obj/structure/cable/blue{ + d2 = 2; + icon_state = "0-2" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"NQ" = ( +/obj/structure/lattice, +/turf/space, +/area/space) +"OA" = ( +/obj/structure/computerframe, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"OK" = ( +/obj/item/reagent_containers/food/snacks/syndicake, +/turf/space, +/area/space) +"OU" = ( +/turf/simulated/floor/plasteel/airless, +/area/ruin) +"Qv" = ( +/obj/machinery/light{ + dir = 8 + }, +/obj/item/trash/syndi_cakes, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"QF" = ( +/obj/machinery/conveyor/east{ + dir = 2; + id = null + }, +/turf/simulated/floor/plasteel, +/area/ruin) +"QI" = ( +/obj/effect/spawner/random_spawners/wall_rusted_probably, +/turf/simulated/wall, +/area/ruin) +"Ra" = ( +/obj/machinery/power/terminal{ + dir = 4 + }, +/obj/structure/cable{ + d2 = 8; + icon_state = "0-8" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Rg" = ( +/obj/machinery/door/airlock/mining/glass{ + locked = 1; + max_integrity = 300000; + name = "Corgi processing"; + normal_integrity = 300000 + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Ry" = ( +/obj/item/stack/cable_coil/random, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"RB" = ( +/mob/living/simple_animal/hostile/alien/sentinel{ + animal_species = /mob/living/simple_animal/pet/dog; + attack_sound = 'sound/weapons/bite.ogg'; + attacktext = "bites"; + damage_coeff = list("brute" = 1, "fire" = 1, "tox" = 1, "clone" = 1, "stamina" = 1, "oxy" = 1); + death_sound = null; + deathmessage = ""; + desc = "This is no longer a goodboy. Not anymore. He has seen too much."; + icon = 'icons/mob/pets.dmi'; + icon_living = "corgi"; + icon_state = "corgi"; + name = "angry corgi"; + retreat_distance = 2 + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Tj" = ( +/obj/structure/table/reinforced, +/obj/item/storage/toolbox/syndicate, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Tt" = ( +/obj/machinery/door/airlock/external, +/turf/simulated/floor/plating, +/area/ruin/powered) +"TM" = ( +/obj/structure/chair/office/dark{ + dir = 4; + icon_state = "officechair_dark"; + tag = "icon-officechair_dark (EAST)" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"TT" = ( +/obj/structure/cable{ + d1 = 1; + d2 = 8; + icon_state = "1-8"; + tag = "" + }, +/obj/structure/cable{ + d1 = 1; + d2 = 4; + icon_state = "1-4"; + tag = "90Curve" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Uq" = ( +/obj/machinery/conveyor/east{ + id = null + }, +/turf/simulated/floor/plasteel, +/area/ruin) +"UC" = ( +/obj/machinery/tcomms/relay/ruskie{ + network_id = "SYNDICAKES-FACTORY" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"UR" = ( +/obj/item/trash/syndi_cakes, +/turf/space, +/area/space) +"Vf" = ( +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Vt" = ( +/obj/machinery/conveyor/north/ccw{ + id = null + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"VH" = ( +/obj/structure/lattice, +/obj/random/tool, +/turf/space, +/area/space) +"VR" = ( +/obj/structure/lattice, +/turf/unsimulated, +/area/space) +"WL" = ( +/turf/space, +/area/ruin/unpowered) +"Xd" = ( +/obj/structure/cable{ + d1 = 2; + d2 = 8; + icon_state = "2-8" + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"XZ" = ( +/obj/machinery/light/small{ + dir = 1 + }, +/obj/structure/dispenser/oxygen, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"YR" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"YU" = ( +/obj/structure/chair/office/dark, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Zs" = ( +/obj/machinery/conveyor/east{ + id = null + }, +/turf/simulated/floor/plasteel, +/area/ruin/unpowered) +"Zt" = ( +/obj/machinery/conveyor/east{ + dir = 2; + id = null + }, +/turf/simulated/floor/plasteel, +/area/space) + +(1,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(2,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(3,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +ob +ob +ob +ob +ob +ob +EJ +EJ +EJ +EJ +EJ +EJ +EJ +EJ +Ki +Ki +Ki +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(4,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +EJ +EJ +EJ +EJ +EJ +EJ +Le +Vf +zd +rU +sN +FO +EJ +EJ +EJ +pb +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(5,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +JI +GB +Vf +sN +sN +EJ +Ra +sN +Gm +sN +sN +sN +Tt +KD +Tt +pb +pb +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(6,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +sN +Hx +GB +Dz +sN +EJ +Nw +hm +TT +Id +sN +Ej +EJ +EJ +EJ +pb +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(7,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +sN +Dz +sN +Dz +sN +EJ +UC +sN +Gm +sN +sN +zY +EJ +pd +pd +ls +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(8,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +sN +Zs +sN +Zs +sN +EJ +fq +sN +Gm +sN +YU +OA +EJ +pd +pd +ls +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(9,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +sN +Zs +sN +Zs +Id +EJ +sN +GB +Gm +TM +sN +sN +EJ +pd +pd +ls +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(10,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +pR +Vt +Vt +Vt +sN +EJ +XZ +BI +Gm +uA +OA +jR +EJ +pd +pd +ls +pd +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(11,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +Zs +EJ +EJ +EJ +Rg +EJ +EJ +EJ +qS +EJ +EJ +EJ +EJ +EJ +EJ +EJ +EJ +pd +pd +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(12,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +Zs +zF +TM +Qv +lK +EJ +sN +Vf +Gm +sN +sN +Ry +Kr +OU +OU +OU +EJ +pd +pd +pd +OK +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(13,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +Zs +hs +Tj +zP +Xd +ky +Cp +Cp +es +sN +rJ +rJ +Kr +rL +eY +OU +EJ +pd +pd +pd +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(14,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +Zs +EJ +sN +wB +sN +BG +sN +sN +sN +sN +sN +sN +oO +OU +eY +eY +EJ +pd +pd +pd +pd +oJ +Hy +Hy +Hy +Hy +Hy +fY +Hy +Hy +Hy +Hy +"} +(15,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +Zs +zF +TM +RB +wB +EJ +IB +sN +sN +sN +sN +sN +EJ +eY +eY +rJ +EJ +pd +pd +mx +pd +pd +pd +Hy +fY +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(16,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +Zs +hs +bb +OA +sN +EJ +WL +IB +sN +YR +HQ +HQ +EJ +OU +eY +rJ +EJ +zt +oJ +pd +pd +pd +jv +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(17,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +EJ +uF +EJ +EJ +EJ +fU +EJ +EJ +EJ +oO +EJ +EJ +EJ +EJ +OU +eY +OK +Da +pd +pd +NQ +zt +pd +pd +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(18,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +VR +Uq +OU +OU +KS +KS +Mz +ho +OU +OU +OU +OU +ho +iK +eY +Da +OK +OK +pd +pd +NQ +zo +pd +pd +pd +pd +OK +Hy +Hy +fY +Hy +Hy +Hy +"} +(19,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +pd +rJ +rJ +rJ +KS +rJ +KS +rJ +Ic +Ic +ub +NQ +OU +eY +NQ +NQ +Da +OK +yU +Da +NQ +AJ +pd +OK +pd +pd +pd +Hy +Hy +Hy +Hy +Hy +Hy +"} +(20,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +pd +nV +xT +mc +KS +mc +KS +KS +Da +NQ +Da +NQ +Da +Da +UR +ub +Da +Da +pd +pd +NQ +NQ +pd +pd +pd +zt +pd +pd +Hy +Hy +Hy +Hy +Hy +"} +(21,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Ki +pp +pd +Zt +rJ +Zt +Zt +NQ +Ic +Da +Da +Da +Da +OK +Da +Da +OK +OK +Da +pd +NQ +Da +Da +pd +pd +pd +pd +pd +pd +pd +Hy +Ki +Hy +Hy +"} +(22,1,1) = {" +Hy +Hy +CY +Hy +Hy +Hy +Hy +pp +pd +pd +zo +pd +zo +zo +NQ +ub +Da +Da +Da +Da +Da +Da +Da +Da +Da +ub +zt +pd +pd +pd +mx +pd +pd +pd +pd +OK +Hy +Hy +Hy +Hy +"} +(23,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Ki +Mi +dy +VR +pd +pd +pd +pd +pd +zt +pd +pd +Ic +OU +OU +Da +NQ +pd +pd +pd +pd +pd +pd +oJ +pd +zt +ls +pd +pd +pd +Hy +Hy +Hy +Hy +"} +(24,1,1) = {" +Hy +Hy +Hy +Hy +Ki +Hy +Hy +Hy +Ki +Hy +pd +oJ +OK +pd +pd +bk +NQ +QF +QF +QF +QF +Da +sd +oJ +pd +pd +pd +cc +VH +pd +pd +pd +pd +pd +pd +ls +Hy +Hy +Hy +Hy +"} +(25,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +CY +Hy +Hy +Hy +Hy +pd +pd +ke +OK +pd +zo +NQ +NQ +QI +NQ +NQ +pd +pd +zt +NQ +NQ +om +zo +pd +OK +pd +oJ +pd +pd +zt +Hy +Hy +Hy +Hy +"} +(26,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Ki +Hy +Hy +Hy +Hy +Hy +Hy +pd +pd +pd +pd +pd +pd +pd +pd +NQ +ls +pd +pd +pd +pd +pd +zo +zo +Da +Da +pd +oJ +pd +pd +pd +Hy +Hy +Hy +Hy +"} +(27,1,1) = {" +Hy +Hy +CY +Hy +Hy +Hy +Hy +AH +CY +CY +Hy +fY +Hy +pd +pd +pd +pd +pd +pd +pd +pd +pd +pd +OK +pd +oJ +pd +mx +pd +pd +OK +pd +pd +pd +pd +pd +Hy +Hy +Hy +Hy +"} +(28,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +pd +NQ +OK +pd +pd +aM +pd +mx +pd +pd +pd +oJ +pd +pd +pd +pd +pd +pd +aM +pd +pd +Hy +Hy +Hy +Hy +jf +"} +(29,1,1) = {" +Hy +Hy +Hy +Hy +CY +Hy +Hy +Hy +Hy +CY +Hy +Hy +Hy +Fw +Hy +pd +oJ +pd +pd +pd +pd +pd +pd +pd +OK +oJ +pd +kh +pd +pd +pd +pd +pd +pd +Hy +Hy +Hy +fY +Hy +Hy +"} +(30,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +CY +CY +Hy +pd +pd +pd +pd +ls +pd +OK +pd +pd +pd +pd +pd +pd +pd +oJ +zt +pd +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(31,1,1) = {" +Hy +Hy +Hy +fY +Hy +Hy +Ki +Hy +Hy +Hy +Ki +Hy +CY +Hy +Hy +Hy +Hy +pd +pd +pd +pd +pd +ls +pd +pd +oJ +pd +pd +aM +pd +pd +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(32,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +fY +Hy +Hy +Hy +Hy +kh +pd +pd +zt +pd +pd +ls +pd +pd +pd +pd +pd +pd +Hy +Hy +Fw +Hy +Hy +Hy +Hy +Hy +Hy +"} +(33,1,1) = {" +Hy +Hy +Hy +Hy +CY +Hy +CY +Hy +fY +Hy +Hy +Hy +Hy +Hy +jf +Hy +Hy +Hy +Hy +pd +pd +pd +pd +pd +NQ +zt +pd +pd +oJ +pd +fY +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(34,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +CY +Hy +Hy +Hy +CY +Hy +fY +Hy +CY +Hy +Hy +pd +oJ +pd +pd +OK +pd +pd +pd +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(35,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Ki +Hy +Hy +Hy +CY +CY +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +pd +pd +ls +pd +pd +pd +Hy +Hy +Hy +Hy +Hy +Hy +fY +Hy +Hy +Hy +Hy +Hy +"} +(36,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +CY +Hy +Hy +Hy +Hy +fY +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(37,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Fw +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(38,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(39,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +jf +Hy +Hy +Hy +Hy +fY +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} +(40,1,1) = {" +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +Hy +"} diff --git a/_maps/map_files/RandomRuins/SpaceRuins/syndiedepot.dmm b/_maps/map_files/RandomRuins/SpaceRuins/syndiedepot.dmm index 1dc60364d99..ceb8067f7ac 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/syndiedepot.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/syndiedepot.dmm @@ -68,7 +68,6 @@ "an" = ( /obj/machinery/navbeacon/invisible{ codes_txt = "patrol;next_patrol=SDNW"; - invisibility = 100; location = "SDNE" }, /turf/simulated/floor/plating/asteroid/airless, @@ -292,15 +291,13 @@ "aU" = ( /obj/machinery/navbeacon/invisible{ codes_txt = "patrol;next_patrol=SDSW"; - invisibility = 100; location = "SDNW" }, /turf/simulated/floor/plating/asteroid/airless, /area/syndicate_depot/outer) "aV" = ( /obj/machinery/light{ - dir = 4; - icon_state = "tube1" + dir = 4 }, /turf/simulated/floor/plating/asteroid/airless, /area/syndicate_depot/outer) @@ -429,8 +426,7 @@ /area/syndicate_depot/core) "bo" = ( /obj/machinery/light{ - dir = 4; - icon_state = "tube1" + dir = 4 }, /obj/effect/spawner/random_spawners/syndicate/loot, /turf/simulated/floor/plasteel{ @@ -512,7 +508,6 @@ id = "syndi_depot_rear"; idle_power_usage = 0; name = "mysterious button"; - normaldoorcontrol = 0; pixel_x = 24; use_power = 0 }, @@ -663,8 +658,8 @@ /obj/machinery/turretid/syndicate{ name = "external turret controls"; pixel_x = -32; - pixel_y = 0; - req_access = list(150) + req_access = null; + req_access_txt = "150" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -745,9 +740,8 @@ /area/syndicate_depot/core) "ce" = ( /obj/machinery/light/small{ - tag = "icon-bulb1 (EAST)"; - icon_state = "bulb1"; - dir = 4 + dir = 4; + tag = "icon-bulb1 (EAST)" }, /turf/simulated/floor/mineral/silver, /area/syndicate_depot/core) @@ -757,7 +751,6 @@ id = "syndi_depot_rear"; idle_power_usage = 0; name = "mysterious button"; - normaldoorcontrol = 0; use_power = 0 }, /obj/structure/sign/poster/contraband/syndicate_recruitment, @@ -774,8 +767,8 @@ /area/syndicate_depot/core) "ci" = ( /obj/structure/cable{ - icon_state = "0-2"; - d2 = 2 + d2 = 2; + icon_state = "0-2" }, /obj/machinery/power/smes/upgraded{ charge = 5e+006; @@ -845,7 +838,6 @@ "cr" = ( /obj/structure/sink{ dir = 4; - icon_state = "sink"; pixel_x = 12 }, /turf/simulated/floor/mineral/silver, @@ -1154,7 +1146,6 @@ "dc" = ( /obj/machinery/navbeacon/invisible{ codes_txt = "patrol;next_patrol=SDSE"; - invisibility = 100; location = "SDSW" }, /turf/simulated/floor/plating/asteroid/airless, @@ -1179,7 +1170,6 @@ "df" = ( /obj/machinery/navbeacon/invisible{ codes_txt = "patrol;next_patrol=SDNE"; - invisibility = 100; location = "SDSE" }, /turf/simulated/floor/plating/asteroid/airless, diff --git a/_maps/map_files/RandomRuins/SpaceRuins/turretedoutpost.dmm b/_maps/map_files/RandomRuins/SpaceRuins/turretedoutpost.dmm index 96e43773146..67dc298eb20 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/turretedoutpost.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/turretedoutpost.dmm @@ -1,10 +1,10 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /obj/structure/lattice, -/turf/space, +/turf/template_noop, /area/space/nearstation) "c" = ( /obj/structure/grille, diff --git a/_maps/map_files/RandomRuins/SpaceRuins/way_home.dmm b/_maps/map_files/RandomRuins/SpaceRuins/way_home.dmm index b0613eef965..dc1e468624b 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/way_home.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/way_home.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "b" = ( /turf/simulated/mineral/random, /area/ruin/unpowered/no_grav/way_home) diff --git a/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm index ebfaf3b94b4..3add952e8e2 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/wizardcrash.dmm @@ -1,7 +1,7 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "aa" = ( -/turf/space, -/area/space) +/turf/template_noop, +/area/template_noop) "ab" = ( /turf/simulated/floor/plating/asteroid/airless, /area/ruin/unpowered) @@ -152,12 +152,7 @@ /turf/simulated/floor/wood, /area/ruin/unpowered) "az" = ( -/obj/item/spellbook/oneuse/charge{ - desc = "For the technology-hating wizard."; - spell = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech; - spellname = "Disable Technology"; - used = 1 - }, +/obj/item/spellbook/oneuse/emp/used, /turf/simulated/floor/wood, /area/ruin/unpowered) "aA" = ( diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index 2c12ac11e44..6458f106529 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -26723,10 +26723,6 @@ dir = 4 }, /area/security/nuke_storage) -"aXS" = ( -/obj/machinery/vending/modularpc, -/turf/simulated/floor/plasteel, -/area/storage/primary) "aXU" = ( /obj/item/flag/mime, /obj/machinery/power/apc{ @@ -26847,23 +26843,13 @@ }, /area/crew_quarters/bar) "aYf" = ( -/obj/machinery/modular_computer/console/preset/command, -/obj/structure/cable{ - icon_state = "0-2"; - pixel_y = 1; - d2 = 2 - }, +/obj/machinery/computer/card, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "green" }, /area/bridge) "aYg" = ( -/obj/structure/cable{ - d1 = 1; - d2 = 2; - icon_state = "1-2" - }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "greencorner" @@ -27432,7 +27418,6 @@ /area/storage/primary) "aZf" = ( /obj/structure/table/reinforced, -/obj/machinery/computer/skills, /turf/simulated/floor/plasteel{ icon_state = "red" }, @@ -35244,7 +35229,7 @@ }, /area/bridge) "bnW" = ( -/obj/machinery/computer/station_alert/all, +/obj/machinery/computer/station_alert, /turf/simulated/floor/plasteel{ dir = 0; icon_state = "yellow" @@ -38278,11 +38263,6 @@ /turf/simulated/floor/plasteel, /area/bridge) "bup" = ( -/obj/structure/cable{ - d1 = 1; - d2 = 2; - icon_state = "1-2" - }, /obj/machinery/atmospherics/unary/vent_pump/on, /turf/simulated/floor/plasteel, /area/bridge) @@ -38895,11 +38875,6 @@ icon_state = "4-8"; tag = "" }, -/obj/structure/cable{ - d1 = 1; - d2 = 4; - icon_state = "1-4" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /turf/simulated/floor/plasteel, /area/bridge) @@ -48824,7 +48799,7 @@ }, /area/engine/gravitygenerator) "bOt" = ( -/obj/machinery/computer/ordercomp, +/obj/machinery/computer/supplycomp/public, /obj/item/radio/intercom{ broadcasting = 0; name = "station intercom (General)"; @@ -51723,9 +51698,6 @@ pixel_x = 0; pixel_y = 32 }, -/obj/machinery/computer/skills{ - req_access_txt = "57" - }, /turf/simulated/floor/plasteel, /area/crew_quarters/heads) "bTd" = ( @@ -61633,9 +61605,6 @@ /area/ntrep) "cjf" = ( /obj/structure/table/wood, -/obj/machinery/computer/skills{ - req_access_txt = "57" - }, /turf/simulated/floor/carpet, /area/ntrep) "cjg" = ( @@ -66311,9 +66280,6 @@ /area/blueshield) "cqJ" = ( /obj/structure/table/wood, -/obj/machinery/computer/skills{ - req_one_access = null - }, /turf/simulated/floor/plasteel{ icon_state = "bcarpet05" }, @@ -72578,7 +72544,7 @@ pixel_x = -2; pixel_y = 2 }, -/obj/item/circuitboard/stationalert_all{ +/obj/item/circuitboard/stationalert{ pixel_x = 1; pixel_y = -1 }, @@ -75846,7 +75812,7 @@ pixel_x = 32; pixel_y = 0 }, -/obj/machinery/computer/station_alert/all, +/obj/machinery/computer/station_alert, /obj/structure/cable/yellow{ d1 = 4; d2 = 8; @@ -77210,6 +77176,10 @@ pixel_x = -22 }, /obj/item/stack/tape_roll, +/obj/machinery/camera/motion{ + c_tag = "EVA Motion Sensor"; + dir = 4 + }, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -95350,6 +95320,11 @@ icon_state = "vault" }, /area/shuttle/escape) +"kIR" = ( +/obj/structure/lattice/catwalk, +/obj/machinery/atmospherics/pipe/simple/hidden/yellow, +/turf/space, +/area/space/nearstation) "kLF" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 9 @@ -95983,11 +95958,6 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/wall, /area/crew_quarters/dorms) -"rOX" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/yellow, -/obj/machinery/ntnet_relay, -/turf/simulated/floor/plasteel/dark, -/area/tcommsat/chamber) "rSv" = ( /obj/structure/shuttle/engine/propulsion{ dir = 8; @@ -96034,10 +96004,6 @@ icon_state = "floor4" }, /area/shuttle/administration) -"sag" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/turf/simulated/floor/plasteel/dark, -/area/tcommsat/chamber) "sbp" = ( /turf/simulated/shuttle/wall{ icon_state = "wall3" @@ -115862,7 +115828,7 @@ aKb aNx aVi aWt -aXS +aXQ aZr bbp bdk @@ -131635,7 +131601,7 @@ dmW aaa aab aaa -iUc +aaa iUc iUc iUc @@ -131892,10 +131858,10 @@ ddC doE ddM ddO +kIR oOZ dQC fAw -rOX mkE dpe dpl @@ -132150,8 +132116,8 @@ dmB bfu dnT dpf +iUc rTy -sag wbr hyv dpd diff --git a/_maps/map_files/cyberiad/z2.dmm b/_maps/map_files/cyberiad/z2.dmm index 4c916fe365c..987c1ee28b4 100644 --- a/_maps/map_files/cyberiad/z2.dmm +++ b/_maps/map_files/cyberiad/z2.dmm @@ -4296,55 +4296,20 @@ dir = 2 }, /area/centcom/control) -"mE" = ( -/obj/structure/flora/ausbushes/lavendergrass, -/turf/unsimulated/floor{ - icon_state = "grass1"; - name = "grass" - }, -/area/centcom/specops) -"mF" = ( -/obj/structure/flora/ausbushes/stalkybush, -/turf/unsimulated/floor{ - icon_state = "grass1"; - name = "grass" - }, -/area/centcom/specops) -"mG" = ( -/obj/structure/flora/ausbushes/pointybush, -/turf/unsimulated/floor{ - icon_state = "grass1"; - name = "grass" - }, -/area/centcom/specops) -"mH" = ( -/obj/structure/flora/ausbushes/ywflowers, -/turf/unsimulated/floor{ - icon_state = "grass1"; - name = "grass" - }, -/area/centcom/specops) "mI" = ( /obj/structure/flora/ausbushes/brflowers, /turf/unsimulated/floor{ icon_state = "grass1"; name = "grass" }, -/area/centcom/specops) -"mJ" = ( -/obj/structure/flora/ausbushes/sparsegrass, -/turf/unsimulated/floor{ - icon_state = "grass1"; - name = "grass" - }, -/area/centcom/specops) +/area/centcom/control) "mK" = ( /obj/structure/flora/ausbushes/reedbush, /turf/unsimulated/floor{ icon_state = "grass1"; name = "grass" }, -/area/centcom/specops) +/area/centcom/control) "mL" = ( /turf/unsimulated/floor{ dir = 1; @@ -9891,7 +9856,7 @@ }, /area/admin) "xV" = ( -/obj/machinery/computer/ordercomp, +/obj/machinery/computer/supplycomp/public, /turf/unsimulated/floor{ tag = "icon-floor"; icon_state = "floor" @@ -36255,7 +36220,7 @@ lH lH mu su -mF +mZ mY su nD @@ -36512,7 +36477,7 @@ mc lH lH su -mE +nd mX su nD @@ -36769,7 +36734,7 @@ mc lH lH su -mH +mY na su nD @@ -37026,7 +36991,7 @@ lH lH lH su -mG +WW mZ su nD @@ -37283,7 +37248,7 @@ lH mk mw su -mJ +na ne su nD diff --git a/_maps/map_files/generic/Lavaland.dmm b/_maps/map_files/generic/Lavaland.dmm index e44b9f52458..86ec920c658 100644 --- a/_maps/map_files/generic/Lavaland.dmm +++ b/_maps/map_files/generic/Lavaland.dmm @@ -681,7 +681,7 @@ req_access_txt = "2" }, /obj/structure/cable{ - icon_state = "1-10" + icon_state = "1-2" }, /turf/simulated/floor/plating, /area/mine/laborcamp) @@ -858,18 +858,11 @@ /turf/simulated/floor/plasteel, /area/mine/laborcamp/security) "ce" = ( -/obj/structure/cable{ - icon_state = "2-4" +/obj/machinery/power/smes{ + charge = 5e+006 }, /obj/structure/cable{ - icon_state = "5-6" - }, -/turf/simulated/floor/plating, -/area/mine/laborcamp) -"cf" = ( -/obj/machinery/power/terminal, -/obj/structure/cable{ - icon_state = "0-8" + icon_state = "0-4" }, /turf/simulated/floor/plating, /area/mine/laborcamp) @@ -880,6 +873,9 @@ /turf/simulated/floor/plating, /area/mine/laborcamp) "ch" = ( +/obj/structure/cable{ + icon_state = "1-8" + }, /turf/simulated/floor/plating, /area/mine/laborcamp) "ci" = ( @@ -1033,21 +1029,22 @@ /turf/simulated/floor/plasteel, /area/mine/laborcamp) "cz" = ( -/obj/machinery/power/port_gen/pacman{ - anchored = 1 - }, -/obj/structure/cable, -/turf/simulated/floor/plating, -/area/mine/laborcamp) -"cA" = ( -/obj/machinery/power/smes{ - charge = 5e+006 +/obj/machinery/power/terminal{ + dir = 1 }, /obj/structure/cable{ icon_state = "0-4" }, +/obj/structure/reagent_dispensers/fueltank, +/turf/simulated/floor/plating, +/area/mine/laborcamp) +"cA" = ( /obj/structure/cable{ - icon_state = "0-9" + d1 = 4; + d2 = 8; + icon_state = "4-8"; + pixel_x = 0; + tag = "" }, /turf/simulated/floor/plating, /area/mine/laborcamp) @@ -1056,7 +1053,13 @@ /turf/simulated/floor/plating, /area/mine/laborcamp) "cC" = ( -/obj/structure/reagent_dispensers/fueltank, +/obj/machinery/power/port_gen/pacman{ + anchored = 1 + }, +/obj/structure/cable{ + d2 = 8; + icon_state = "0-8" + }, /turf/simulated/floor/plating, /area/mine/laborcamp) "cD" = ( @@ -11202,7 +11205,7 @@ bm aq bB bM -cf +ch cA aq aj @@ -11460,7 +11463,7 @@ aq aq aq cg -cB +cA aq aj cQ @@ -11716,7 +11719,7 @@ aD aD aD aq -ch +cB cC aq aj diff --git a/_maps/map_files/shuttles/admin_armory.dmm b/_maps/map_files/shuttles/admin_armory.dmm new file mode 100644 index 00000000000..837f867a52b --- /dev/null +++ b/_maps/map_files/shuttles/admin_armory.dmm @@ -0,0 +1,1691 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"ae" = ( +/obj/structure/sign/securearea, +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall4"; + tag = "icon-swall14" + }, +/area/shuttle/administration) +"al" = ( +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/table/reinforced, +/obj/item/folder, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"aI" = ( +/obj/structure/table/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/recharger, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"aP" = ( +/obj/machinery/vending/security, +/obj/machinery/light/spot{ + dir = 8; + icon_state = "tube1"; + tag = "icon-tube1 (WEST)" + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"bi" = ( +/obj/structure/table/reinforced, +/obj/item/storage/firstaid, +/obj/item/storage/firstaid/brute, +/obj/item/storage/box/bodybags, +/obj/item/storage/firstaid/surgery, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"bm" = ( +/obj/structure/grille, +/obj/machinery/door/poddoor/shutters/preopen{ + dir = 8; + id_tag = "adminarmoryshutters" + }, +/obj/structure/window/full/shuttle, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"ce" = ( +/obj/machinery/autolathe/upgraded{ + hacked = 1 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"cl" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"cV" = ( +/obj/structure/closet/emcloset, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"de" = ( +/obj/machinery/suit_storage_unit/security/secure, +/obj/machinery/light/spot, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"df" = ( +/obj/structure/rack, +/obj/item/storage/box/buck{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/storage/box/buck, +/obj/structure/window/reinforced, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"dn" = ( +/obj/machinery/computer/camera_advanced/shuttle_docker/admin{ + name = "NRV Sparta navigation computer" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"dH" = ( +/obj/structure/rack, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/gun/energy/gun, +/obj/item/gun/energy/gun{ + pixel_x = 3; + pixel_y = -3 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"dP" = ( +/obj/machinery/recharge_station/upgraded, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"eI" = ( +/obj/machinery/turretid/stun{ + check_access = 0; + name = "Shuttle Turret Control"; + pixel_y = 30 + }, +/obj/machinery/computer/camera_advanced, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"eL" = ( +/obj/structure/chair{ + dir = 8 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"fo" = ( +/obj/structure/chair{ + dir = 1 + }, +/obj/machinery/light/spot, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"fF" = ( +/obj/machinery/light/spot{ + dir = 4; + icon_state = "tube1"; + tag = "icon-tube1 (EAST)" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"fQ" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 1 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"gd" = ( +/obj/machinery/door/airlock/shuttle, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"gh" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"gj" = ( +/obj/machinery/computer/communications{ + name = "NRV Sparta Communications Console" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"hN" = ( +/obj/structure/chair{ + dir = 4 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"ir" = ( +/obj/structure/closet/emcloset, +/obj/machinery/light/spot{ + dir = 1; + icon_state = "tube1"; + tag = "icon-tube1 (NORTH)" + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"iz" = ( +/obj/machinery/porta_turret{ + installation = /obj/item/gun/energy/gun + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"jn" = ( +/obj/machinery/door/poddoor/shutters{ + density = 0; + dir = 2; + icon_state = "open"; + id_tag = "Asclshutters"; + name = "Blast Shutters"; + opacity = 0 + }, +/obj/structure/grille, +/obj/structure/window/full/shuttle, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"jB" = ( +/obj/structure/table/reinforced, +/obj/item/storage/lockbox/mindshield, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"jU" = ( +/obj/machinery/suit_storage_unit/security/secure, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"kf" = ( +/obj/structure/sign/securearea, +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall8"; + tag = "icon-swall12" + }, +/area/shuttle/administration) +"mq" = ( +/obj/structure/grille, +/obj/machinery/door/poddoor/shutters{ + density = 0; + dir = 2; + icon_state = "open"; + id_tag = "Asclshutters"; + name = "Blast Shutters"; + opacity = 0 + }, +/obj/structure/window/full/shuttle, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"mw" = ( +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"mM" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall13" + }, +/area/shuttle/administration) +"nG" = ( +/obj/structure/window/full/shuttle, +/obj/structure/grille, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"oj" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 1 + }, +/obj/machinery/light/spot, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"oY" = ( +/obj/structure/chair, +/obj/machinery/light/spot{ + dir = 1; + icon_state = "tube1"; + tag = "icon-tube1 (NORTH)" + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"pa" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall11" + }, +/area/shuttle/administration) +"pK" = ( +/obj/structure/shuttle/engine/propulsion{ + dir = 4; + icon_state = "propulsion" + }, +/turf/simulated/shuttle/plating, +/area/shuttle/administration) +"pQ" = ( +/obj/structure/chair{ + dir = 8 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"qQ" = ( +/obj/machinery/recharger/wallcharger{ + pixel_y = 25 + }, +/obj/machinery/recharger/wallcharger{ + pixel_y = 35 + }, +/obj/machinery/light/spot{ + dir = 1; + icon_state = "tube1"; + tag = "icon-tube1 (NORTH)" + }, +/obj/machinery/door_control{ + id = "adminshuttlebridge"; + name = "Bridge Privacy Shutters"; + pixel_x = 25; + req_access_txt = "19" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"rb" = ( +/obj/machinery/door/window/brigdoor/southleft{ + req_one_access_txt = "2;19" + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"rW" = ( +/obj/structure/grille, +/obj/machinery/door/poddoor/shutters/preopen{ + id_tag = "adminshuttlebridge" + }, +/obj/structure/window/full/shuttle, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"sq" = ( +/obj/structure/table/reinforced, +/obj/item/kitchen/knife/combat{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/kitchen/knife/combat, +/obj/item/kitchen/knife/combat{ + pixel_x = 3; + pixel_y = -3 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"tx" = ( +/obj/machinery/sleeper/upgraded{ + dir = 4 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"ua" = ( +/obj/structure/chair{ + dir = 8 + }, +/obj/machinery/door_control{ + dir = 4; + id = "Asclshutters"; + name = "External Window Shutter Control"; + pixel_x = -5; + pixel_y = 35; + req_access_txt = "19" + }, +/obj/machinery/keycard_auth{ + pixel_y = 24 + }, +/obj/machinery/door_control{ + id = "asclblast"; + name = "Airlock Blast Door Control"; + pixel_x = 5; + pixel_y = 35; + req_access = null; + req_access_txt = "19" + }, +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"uz" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall4"; + tag = "icon-swall14" + }, +/area/shuttle/administration) +"uW" = ( +/obj/structure/shuttle/engine/heater{ + dir = 4; + icon_state = "heater" + }, +/obj/structure/window/plasmareinforced{ + dir = 8 + }, +/turf/simulated/shuttle/plating, +/area/shuttle/administration) +"uY" = ( +/obj/structure/window/reinforced, +/obj/structure/table/reinforced, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"vk" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall_s6" + }, +/area/shuttle/administration) +"vy" = ( +/obj/structure/table/reinforced, +/obj/item/stack/sheet/metal/fifty, +/obj/item/stack/sheet/glass/fifty, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"vA" = ( +/obj/machinery/light/spot{ + dir = 1; + icon_state = "tube1"; + tag = "icon-tube1 (NORTH)" + }, +/obj/machinery/portable_atmospherics/canister/oxygen, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"vW" = ( +/obj/structure/table/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/storage/box/handcuffs, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"wG" = ( +/obj/structure/rack, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/item/clothing/suit/armor/bulletproof{ + pixel_x = 0; + pixel_y = 0 + }, +/obj/item/clothing/head/helmet/alt, +/obj/item/clothing/suit/armor/bulletproof{ + pixel_x = 0; + pixel_y = 0 + }, +/obj/item/clothing/head/helmet/alt, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"wH" = ( +/obj/machinery/light/spot{ + dir = 8; + icon_state = "tube1"; + tag = "icon-tube1 (WEST)" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"wQ" = ( +/obj/machinery/door/poddoor/shutters{ + density = 0; + dir = 8; + icon_state = "open"; + id_tag = "Asclshutters"; + name = "Blast Shutters"; + opacity = 0 + }, +/obj/structure/grille, +/obj/structure/window/full/shuttle, +/turf/space, +/area/shuttle/administration) +"xm" = ( +/obj/structure/table/reinforced, +/obj/item/rcd/preloaded, +/obj/item/rcd_ammo, +/obj/item/rcd_ammo{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/rcd_ammo{ + pixel_x = -3; + pixel_y = 3 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"xF" = ( +/obj/structure/rack, +/obj/item/storage/box/buck{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/storage/box/buck, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"xH" = ( +/obj/machinery/door/airlock/shuttle, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"xK" = ( +/obj/structure/sign/nosmoking_2, +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall3"; + tag = "icon-swall3" + }, +/area/shuttle/administration) +"xQ" = ( +/obj/structure/chair{ + dir = 4 + }, +/obj/machinery/light/spot{ + dir = 1; + icon_state = "tube1"; + tag = "icon-tube1 (NORTH)" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"xU" = ( +/obj/structure/table/reinforced, +/obj/item/stack/sheet/metal/fifty, +/obj/item/stack/sheet/glass/fifty, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"xZ" = ( +/turf/space, +/area/shuttle/administration) +"yg" = ( +/obj/structure/table/reinforced, +/obj/machinery/door/window/brigdoor/eastleft{ + name = "Armory"; + req_access_txt = "3" + }, +/obj/machinery/door/window/brigdoor/westleft{ + name = "Armory" + }, +/obj/machinery/door/poddoor/shutters/preopen{ + dir = 8; + id_tag = "adminarmoryshutters" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"yu" = ( +/obj/structure/table/reinforced, +/obj/machinery/recharger{ + pixel_x = -5 + }, +/obj/machinery/recharger{ + pixel_x = 8 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"yF" = ( +/obj/machinery/status_display, +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall3" + }, +/area/shuttle/administration) +"zY" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall14" + }, +/area/shuttle/administration) +"AH" = ( +/obj/machinery/light/spot, +/obj/machinery/porta_turret{ + installation = /obj/item/gun/energy/gun + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Bc" = ( +/obj/structure/rack, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/shield/riot, +/obj/item/shield/riot{ + pixel_x = 2; + pixel_y = -4 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Bj" = ( +/obj/structure/fans/tiny, +/obj/machinery/door/airlock/external{ + req_one_access_txt = "2;19" + }, +/obj/machinery/door/poddoor{ + density = 0; + icon_state = "open"; + id_tag = "asclblast"; + name = "Blast Door"; + opacity = 0 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Bn" = ( +/obj/machinery/door/window/brigdoor/southleft{ + req_one_access_txt = "2;19" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Bq" = ( +/obj/structure/sign/nosmoking_2, +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall1"; + tag = "icon-swall1" + }, +/area/shuttle/administration) +"BZ" = ( +/obj/machinery/light/spot, +/obj/machinery/porta_turret{ + installation = /obj/item/gun/energy/gun + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Cl" = ( +/obj/structure/table/reinforced, +/obj/item/storage/toolbox/emergency{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/storage/toolbox/mechanical, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"Du" = ( +/obj/structure/rack, +/obj/structure/window/reinforced, +/obj/item/gun/energy/gun, +/obj/item/gun/energy/gun{ + pixel_x = 3; + pixel_y = -3 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"DZ" = ( +/obj/machinery/light/spot, +/obj/structure/closet/emcloset, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Eq" = ( +/obj/structure/sign/securearea, +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall12" + }, +/area/shuttle/administration) +"EQ" = ( +/obj/machinery/door/airlock/shuttle/glass, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"Fd" = ( +/obj/structure/rack, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/gun/energy/gun, +/obj/item/gun/energy/gun{ + pixel_x = 3; + pixel_y = -3 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Fo" = ( +/obj/structure/rack, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/gun/energy/immolator/multi{ + pixel_y = -3 + }, +/obj/item/gun/energy/immolator/multi, +/obj/item/gun/energy/immolator/multi{ + pixel_y = 3 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"FF" = ( +/obj/structure/sign/poster/official/enlist, +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall12" + }, +/area/shuttle/administration) +"FT" = ( +/obj/structure/rack, +/obj/item/storage/box/buck{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/storage/box/buck, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Gf" = ( +/obj/structure/sign/vacuum{ + pixel_x = -32 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Gn" = ( +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/rack, +/obj/item/gun/projectile/shotgun/riot/buckshot{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/projectile/shotgun/riot/buckshot, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Gx" = ( +/obj/structure/fans/tiny, +/obj/machinery/door/airlock/external, +/obj/docking_port/mobile{ + dir = 2; + dwidth = 9; + height = 18; + id = "admin"; + name = "armory"; + roundstart_move = "cc_bay_1"; + timid = 1; + width = 19 + }, +/obj/machinery/door/poddoor{ + density = 0; + icon_state = "open"; + id_tag = "asclblast"; + name = "Blast Door"; + opacity = 0 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Gy" = ( +/obj/structure/chair{ + dir = 1 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"Hc" = ( +/obj/machinery/porta_turret{ + installation = /obj/item/gun/energy/gun + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"HN" = ( +/obj/structure/sign/poster/official/do_not_question, +/obj/item/tank/emergency_oxygen/engi, +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall3" + }, +/area/shuttle/administration) +"Ir" = ( +/obj/machinery/computer/shuttle/admin{ + name = "NRV Sparta shuttle console" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"IC" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall3" + }, +/area/shuttle/administration) +"Jt" = ( +/obj/machinery/suit_storage_unit/standard_unit, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Ke" = ( +/obj/machinery/door/window/brigdoor/southright{ + req_one_access_txt = "2;19" + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"Kh" = ( +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Ki" = ( +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/clothing/suit/armor/bulletproof{ + pixel_x = 0; + pixel_y = 0 + }, +/obj/structure/rack, +/obj/item/clothing/head/helmet/alt, +/obj/item/clothing/suit/armor/bulletproof{ + pixel_x = 0; + pixel_y = 0 + }, +/obj/item/clothing/head/helmet/alt, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"KM" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall7" + }, +/area/shuttle/administration) +"Lh" = ( +/obj/structure/rack, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/gun/energy/ionrifle{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/gun/energy/ionrifle, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"LK" = ( +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/computer/secure_data, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"LP" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall_s9" + }, +/area/shuttle/administration) +"Mi" = ( +/obj/structure/table/reinforced, +/obj/machinery/recharger, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Mj" = ( +/obj/machinery/vending/medical, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"Mo" = ( +/obj/structure/rack, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/clothing/suit/armor/bulletproof{ + pixel_x = 0; + pixel_y = 0 + }, +/obj/item/clothing/head/helmet/alt, +/obj/item/clothing/suit/armor/bulletproof{ + pixel_x = 0; + pixel_y = 0 + }, +/obj/item/clothing/head/helmet/alt, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"MN" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall12" + }, +/area/shuttle/administration) +"MZ" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall3"; + tag = "icon-swall3" + }, +/area/shuttle/administration) +"Ni" = ( +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/rack, +/obj/item/gun/projectile/shotgun/riot/buckshot{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/projectile/shotgun/riot/buckshot, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Ns" = ( +/obj/machinery/recharger/wallcharger{ + pixel_y = 25 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"NF" = ( +/obj/structure/sign/poster/official/here_for_your_safety, +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall3"; + tag = "icon-swall3" + }, +/area/shuttle/administration) +"NQ" = ( +/obj/machinery/door/poddoor/shutters{ + density = 0; + dir = 1; + icon_state = "open"; + id_tag = "Asclshutters"; + name = "Blast Shutters"; + opacity = 0 + }, +/obj/structure/grille, +/obj/structure/window/full/shuttle, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Op" = ( +/obj/structure/dispenser/oxygen, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Ou" = ( +/obj/machinery/autolathe/upgraded{ + hacked = 1 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"Pe" = ( +/obj/machinery/door/window/brigdoor/southright{ + req_one_access_txt = "2;19" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Pn" = ( +/obj/structure/rack, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/clothing/suit/armor/riot, +/obj/item/clothing/suit/armor/riot{ + pixel_x = 3; + pixel_y = -3 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"PL" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall_s10" + }, +/area/shuttle/administration) +"PR" = ( +/obj/machinery/light/spot{ + dir = 8; + tag = "icon-tube1 (WEST)" + }, +/obj/machinery/door_control{ + id = "adminarmoryshutters"; + name = "Armory Internal Shutters"; + pixel_x = -26; + req_access_txt = "3" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"PS" = ( +/obj/structure/rack, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/gun/energy/gun/nuclear{ + pixel_y = -3 + }, +/obj/item/gun/energy/gun/nuclear, +/obj/item/gun/energy/gun/nuclear{ + pixel_y = 3 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"PV" = ( +/obj/structure/rack, +/obj/item/gun/energy/sniperrifle{ + pixel_y = -4 + }, +/obj/item/gun/energy/sniperrifle, +/obj/item/gun/energy/sniperrifle{ + pixel_y = 3 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"RM" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall8"; + tag = "icon-swall12" + }, +/area/shuttle/administration) +"TD" = ( +/obj/machinery/door/airlock/command{ + name = "Command Center"; + req_access = null; + req_access_txt = "19" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"TF" = ( +/obj/structure/table/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced, +/obj/item/storage/fancy/donut_box, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Un" = ( +/obj/structure/table/reinforced, +/obj/item/tank/emergency_oxygen/engi{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/tank/emergency_oxygen/engi, +/obj/item/tank/emergency_oxygen/engi{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/machinery/light/spot{ + dir = 8; + icon_state = "tube1"; + tag = "icon-tube1 (WEST)" + }, +/obj/item/clothing/mask/breath{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath{ + pixel_x = 3; + pixel_y = -3 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"Ur" = ( +/obj/machinery/porta_turret{ + installation = /obj/item/gun/energy/gun + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/administration) +"UR" = ( +/obj/machinery/status_display, +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall12" + }, +/area/shuttle/administration) +"UX" = ( +/obj/structure/fans/tiny, +/obj/machinery/door/airlock/external, +/obj/machinery/door/poddoor{ + density = 0; + icon_state = "open"; + id_tag = "asclblast"; + name = "Blast Door"; + opacity = 0 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"Vc" = ( +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swall_s5" + }, +/area/shuttle/administration) +"VC" = ( +/obj/machinery/door/airlock/shuttle{ + name = "Shuttle Armory"; + req_access_txt = "3" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"VQ" = ( +/obj/structure/rack, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/gun/energy/gun/advtaser{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/gun/energy/gun/advtaser{ + pixel_x = 0; + pixel_y = 0 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"WV" = ( +/obj/structure/table/reinforced, +/obj/item/restraints/handcuffs{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/restraints/handcuffs, +/obj/item/restraints/handcuffs{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/machinery/light/spot{ + dir = 4; + icon_state = "tube1"; + tag = "icon-tube1 (EAST)" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) +"XL" = ( +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/rack, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/gun/projectile/shotgun/riot/buckshot{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/projectile/shotgun/riot/buckshot, +/turf/simulated/shuttle/floor{ + icon_state = "floor4" + }, +/area/shuttle/administration) + +(1,1,1) = {" +vk +MZ +HN +IC +IC +IC +IC +KM +wQ +wQ +wQ +wQ +KM +IC +IC +IC +IC +Vc +"} +(2,1,1) = {" +MN +Un +xm +Mj +aP +dP +tx +MN +eI +Ir +dn +gj +MN +vA +cl +cl +DZ +Eq +"} +(3,1,1) = {" +MN +Ns +mw +mw +mw +mw +mw +MN +ua +gh +gh +gh +FF +Op +Kh +Kh +Kh +Bj +"} +(4,1,1) = {" +MN +Ns +mw +mw +mw +mw +Gy +MN +qQ +Kh +Kh +Kh +UR +Jt +Kh +Kh +Kh +Bj +"} +(5,1,1) = {" +MN +Ns +mw +mw +mw +mw +fo +zY +MZ +TD +rW +rW +pa +MZ +xH +nG +nG +Eq +"} +(6,1,1) = {" +MN +bi +Cl +vy +Ou +mw +mw +MN +cV +Kh +Kh +Kh +Bn +Gf +Kh +Mi +Mi +MN +"} +(7,1,1) = {" +zY +nG +nG +nG +nG +gd +nG +MN +xQ +Kh +hN +Kh +Pe +Kh +Kh +Kh +oj +MN +"} +(8,1,1) = {" +RM +oY +mw +mw +mw +mw +Ur +kf +vW +al +aI +LK +TF +Kh +Kh +Kh +fQ +mq +"} +(9,1,1) = {" +UX +mw +mw +mw +mw +mw +mw +EQ +mw +mw +mw +mw +rb +Kh +Kh +Kh +fQ +jn +"} +(10,1,1) = {" +Gx +mw +mw +mw +mw +mw +mw +EQ +mw +mw +mw +mw +Ke +Kh +Kh +Kh +Kh +jn +"} +(11,1,1) = {" +uz +oY +mw +mw +mw +mw +Hc +ae +ir +pQ +pQ +pQ +uY +Kh +Kh +Kh +BZ +MN +"} +(12,1,1) = {" +zY +MZ +Bq +yg +VC +yg +yF +pa +bm +bm +bm +MZ +MZ +xK +VC +NF +MZ +mM +"} +(13,1,1) = {" +MN +Kh +wH +eL +Kh +eL +Kh +PR +PV +PS +Fo +wH +VQ +Bc +Kh +Kh +AH +MN +"} +(14,1,1) = {" +NQ +Kh +dH +Ni +Kh +FT +Mo +Kh +Kh +Kh +Kh +Kh +Lh +Pn +Kh +Kh +Kh +mq +"} +(15,1,1) = {" +NQ +Kh +Du +Gn +Kh +df +wG +Kh +Kh +Kh +Kh +Kh +Kh +Kh +Kh +Kh +Kh +mq +"} +(16,1,1) = {" +NQ +Kh +Fd +XL +Kh +xF +Ki +Kh +Kh +Kh +Kh +Kh +Kh +Kh +Kh +Kh +Kh +mq +"} +(17,1,1) = {" +MN +fF +Kh +Kh +fF +Kh +Kh +fF +iz +yu +sq +WV +xU +ce +jB +jU +de +MN +"} +(18,1,1) = {" +PL +KM +uW +uW +KM +IC +IC +KM +uW +uW +KM +IC +IC +KM +uW +uW +KM +LP +"} +(19,1,1) = {" +xZ +PL +pK +pK +LP +xZ +xZ +PL +pK +pK +LP +xZ +xZ +PL +pK +pK +LP +xZ +"} diff --git a/_maps/map_files/shuttles/emergency_dept.dmm b/_maps/map_files/shuttles/emergency_dept.dmm index 739056d20d6..fb479c23995 100644 --- a/_maps/map_files/shuttles/emergency_dept.dmm +++ b/_maps/map_files/shuttles/emergency_dept.dmm @@ -17,81 +17,152 @@ "ad" = ( /turf/space, /turf/simulated/shuttle/wall{ - tag = "icon-swall_f10"; icon_state = "swall_f10"; dir = 2 }, /area/shuttle/escape) "ae" = ( -/turf/simulated/shuttle/wall{ - icon_state = "swall12"; - dir = 2 +/obj/structure/table/reinforced, +/obj/item/paper_bin, +/obj/item/pen, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "vault" }, /area/shuttle/escape) "af" = ( -/turf/simulated/shuttle/wall{ - icon_state = "swallc4" +/obj/structure/table/reinforced, +/obj/item/folder/red, +/obj/item/restraints/handcuffs, +/obj/item/flash, +/turf/simulated/floor/plasteel{ + icon_state = "dark" }, /area/shuttle/escape) "ag" = ( -/obj/machinery/computer/emergency_shuttle, -/turf/simulated/shuttle/floor, +/obj/machinery/computer/robotics, +/turf/simulated/floor/plasteel{ + dir = 9; + icon_state = "darkblue" + }, /area/shuttle/escape) "ah" = ( -/turf/simulated/shuttle/wall{ - icon_state = "swallc3" +/obj/machinery/computer/crew, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkblue" }, /area/shuttle/escape) "ai" = ( -/obj/machinery/computer/station_alert, -/turf/simulated/shuttle/floor, +/obj/machinery/computer/emergency_shuttle, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkblue" + }, /area/shuttle/escape) "aj" = ( /obj/machinery/computer/communications, -/obj/machinery/light/spot{ - tag = "icon-tube1 (NORTH)"; - icon_state = "tube1"; - dir = 1 +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "darkblue" }, -/turf/simulated/shuttle/floor, /area/shuttle/escape) "ak" = ( -/obj/machinery/computer/security{ - network = list("SS13","Telecomms","Research Outpost","Mining Outpost") +/obj/structure/table/reinforced, +/obj/item/storage/fancy/donut_box, +/turf/simulated/floor/plasteel{ + icon_state = "dark" }, -/turf/simulated/shuttle/floor, /area/shuttle/escape) "al" = ( +/obj/structure/table/reinforced, +/obj/machinery/cell_charger, +/obj/item/stock_parts/cell/high, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "vault" + }, +/area/shuttle/escape) +"am" = ( +/obj/structure/table/reinforced, +/obj/machinery/recharger, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "vault" + }, +/area/shuttle/escape) +"an" = ( +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/shuttle/escape) +"ao" = ( /obj/structure/chair/comfy/shuttle{ dir = 1 }, -/turf/simulated/shuttle/floor, -/area/shuttle/escape) -"am" = ( -/obj/structure/table, -/turf/simulated/shuttle/floor, -/area/shuttle/escape) -"an" = ( -/obj/machinery/computer/crew, -/obj/machinery/light/spot{ - tag = "icon-tube1 (NORTH)"; - icon_state = "tube1"; - dir = 1 +/turf/simulated/floor/plasteel{ + icon_state = "dark" }, -/turf/simulated/shuttle/floor, -/area/shuttle/escape) -"ao" = ( -/obj/machinery/computer/robotics, -/turf/simulated/shuttle/floor, /area/shuttle/escape) "ap" = ( +/obj/structure/table/reinforced, +/obj/item/storage/toolbox/mechanical, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "vault" + }, +/area/shuttle/escape) +"aq" = ( /turf/simulated/shuttle/wall{ icon_state = "swall3"; dir = 2 }, /area/shuttle/escape) -"aq" = ( -/turf/simulated/shuttle/floor, +"ar" = ( +/obj/machinery/computer/security{ + network = list("SS13","Research Outpost","Mining Outpost","Telecomms") + }, +/obj/structure/sign/poster/official/nanotrasen_logo{ + pixel_x = -32; + pixel_y = 0 + }, +/turf/simulated/floor/plasteel{ + dir = 9; + icon_state = "darkred" + }, +/area/shuttle/escape) +"as" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/shuttle/escape) +"at" = ( +/obj/item/radio/intercom{ + dir = 4; + name = "Station Intercom (General)"; + pixel_x = 29; + pixel_y = -60 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/shuttle/escape) +"au" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/shuttle/escape) +"av" = ( +/turf/simulated/shuttle/wall{ + icon_state = "swall12"; + dir = 2 + }, /area/shuttle/escape) "aw" = ( /turf/simulated/shuttle/wall{ @@ -100,49 +171,240 @@ }, /area/shuttle/escape) "ax" = ( -/turf/simulated/shuttle/wall{ - icon_state = "swall14"; - dir = 2 +/obj/machinery/computer/station_alert, +/obj/structure/sign/poster/official/nanotrasen_logo{ + pixel_x = 32; + pixel_y = 0 + }, +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "darkyellow" }, /area/shuttle/escape) "ay" = ( -/obj/machinery/door/airlock/command/glass{ - name = "Escape Shuttle Cockpit"; - req_access_txt = "19" +/obj/machinery/computer/secure_data, +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "darkred" }, -/turf/simulated/shuttle/floor, /area/shuttle/escape) "az" = ( -/obj/structure/chair/comfy/shuttle, +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, /turf/simulated/shuttle/floor4, /area/shuttle/escape) "aA" = ( -/obj/machinery/status_display{ - pixel_y = 30 +/obj/machinery/light, +/obj/structure/chair/comfy/shuttle{ + dir = 8 }, -/obj/machinery/light/spot{ - tag = "icon-tube1 (NORTH)"; - icon_state = "tube1"; - dir = 1 +/turf/simulated/floor/plasteel{ + icon_state = "dark" }, -/obj/structure/chair/comfy/shuttle, -/turf/simulated/shuttle/floor4, /area/shuttle/escape) "aB" = ( -/obj/structure/grille, -/obj/structure/window/full/shuttle{ - icon_state = "16" +/obj/machinery/light, +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" }, -/turf/simulated/floor/plating, /area/shuttle/escape) "aC" = ( +/obj/machinery/computer/atmos_alert, +/turf/simulated/floor/plasteel{ + dir = 6; + icon_state = "darkyellow" + }, +/area/shuttle/escape) +"aD" = ( +/obj/machinery/status_display, +/turf/simulated/shuttle/wall{ + icon_state = "swall12"; + dir = 2 + }, +/area/shuttle/escape) +"aE" = ( +/obj/machinery/door/airlock/command/glass{ + name = "Escape Shuttle Cockpit"; + normalspeed = 0; + req_access_txt = "19" + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/shuttle/escape) +"aF" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"aG" = ( +/obj/machinery/recharger/wallcharger{ + pixel_x = 32; + pixel_y = 0 + }, +/turf/simulated/shuttle/floor4, +/area/shuttle/escape) +"aH" = ( +/turf/simulated/shuttle/floor4, +/area/shuttle/escape) +"aI" = ( +/obj/machinery/door/airlock/medical/glass{ + id_tag = null; + name = "Escape Shuttle Infirmary"; + req_access_txt = "0" + }, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"aJ" = ( +/obj/structure/table/reinforced, +/obj/item/storage/fancy/donut_box, +/obj/item/restraints/handcuffs, +/turf/simulated/shuttle/floor4, +/area/shuttle/escape) +"aL" = ( +/obj/machinery/door/airlock/medical/glass{ + id_tag = null; + name = "Escape Shuttle Infirmary"; + normalspeed = 0; + req_access_txt = "5" + }, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"aM" = ( +/obj/machinery/light{ + icon_state = "tube1"; + dir = 8 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"aN" = ( +/obj/structure/sign/directions/security{ + dir = 1; + pixel_y = 7 + }, +/turf/simulated/shuttle/wall{ + icon_state = "swall12"; + dir = 2 + }, +/area/shuttle/escape) +"aO" = ( +/mob/living/simple_animal/bot/secbot, /turf/simulated/floor/plasteel{ icon_state = "cafeteria"; dir = 2 }, /area/shuttle/escape) -"aD" = ( +"aP" = ( +/turf/simulated/shuttle/wall{ + tag = "icon-swall7"; + icon_state = "swall7"; + dir = 2 + }, +/area/shuttle/escape) +"aR" = ( /obj/structure/chair/comfy/shuttle{ + dir = 1 + }, +/turf/simulated/shuttle/floor4, +/area/shuttle/escape) +"aS" = ( +/turf/simulated/floor/plasteel{ + icon_state = "cafeteria"; + dir = 2 + }, +/area/shuttle/escape) +"aT" = ( +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "neutralfull" + }, +/area/shuttle/escape) +"aU" = ( +/obj/structure/chair/stool/bar, +/turf/simulated/floor/plasteel{ + icon_state = "cafeteria"; + dir = 2 + }, +/area/shuttle/escape) +"aV" = ( +/obj/structure/table/wood, +/turf/simulated/floor/plasteel{ + icon_state = "cafeteria"; + dir = 2 + }, +/area/shuttle/escape) +"aW" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Escape Shuttle Brig"; + normalspeed = 0; + req_access_txt = "63" + }, +/turf/simulated/shuttle/floor4, +/area/shuttle/escape) +"aX" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/simulated/shuttle/floor4, +/area/shuttle/escape) +"aZ" = ( +/turf/simulated/shuttle/wall{ + tag = "icon-swall14"; + icon_state = "swall14"; + dir = 2 + }, +/area/shuttle/escape) +"ba" = ( +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "neutralfull" + }, +/area/shuttle/escape) +"bb" = ( +/obj/structure/sign/greencross, +/turf/simulated/shuttle/wall{ + dir = 2; + icon_state = "swallc3" + }, +/area/shuttle/escape) +"bc" = ( +/obj/structure/bed/roller, +/obj/machinery/light{ + dir = 1; + on = 1 + }, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"bd" = ( +/obj/item/storage/box/drinkingglasses, +/obj/item/reagent_containers/food/drinks/shaker, +/obj/structure/table/wood, +/obj/machinery/light{ + icon_state = "tube1"; dir = 4 }, /turf/simulated/floor/plasteel{ @@ -150,60 +412,7 @@ dir = 2 }, /area/shuttle/escape) -"aE" = ( -/obj/structure/table/wood, -/turf/simulated/floor/plasteel{ - icon_state = "cafeteria"; - dir = 2 - }, -/area/shuttle/escape) -"aF" = ( -/obj/machinery/status_display{ - pixel_y = 30 - }, -/obj/machinery/light/spot{ - tag = "icon-tube1 (NORTH)"; - icon_state = "tube1"; - dir = 1 - }, -/turf/simulated/floor/plasteel{ - icon_state = "cafeteria"; - dir = 2 - }, -/area/shuttle/escape) -"aG" = ( -/obj/item/storage/box/drinkingglasses, -/obj/item/reagent_containers/food/drinks/shaker, -/obj/structure/table/wood, -/turf/simulated/floor/plasteel{ - icon_state = "cafeteria"; - dir = 2 - }, -/area/shuttle/escape) -"aH" = ( -/obj/structure/closet/walllocker/emerglocker{ - pixel_x = -28 - }, -/turf/simulated/shuttle/floor4, -/area/shuttle/escape) -"aI" = ( -/turf/simulated/shuttle/floor4, -/area/shuttle/escape) -"aJ" = ( -/obj/structure/grille, -/obj/structure/window/full/shuttle{ - icon_state = "15" - }, -/turf/simulated/floor/plating, -/area/shuttle/escape) -"aK" = ( -/obj/structure/chair/comfy/shuttle, -/turf/simulated/floor/plasteel{ - icon_state = "cafeteria"; - dir = 2 - }, -/area/shuttle/escape) -"aL" = ( +"be" = ( /obj/machinery/chem_dispenser/beer, /obj/structure/table/wood, /turf/simulated/floor/plasteel{ @@ -211,24 +420,40 @@ dir = 2 }, /area/shuttle/escape) -"aM" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Escape Shuttle Cell"; - req_access_txt = "2" +"bf" = ( +/obj/structure/table/reinforced, +/obj/item/storage/firstaid/o2{ + pixel_x = -3; + pixel_y = -3 + }, +/obj/item/storage/firstaid/regular, +/obj/item/storage/firstaid/toxin{ + pixel_x = 5; + pixel_y = 5 }, -/turf/simulated/floor/plating, -/area/shuttle/escape) -"aN" = ( -/obj/item/storage/box/drinkingglasses, -/obj/item/reagent_containers/food/drinks/shaker, -/obj/item/clothing/mask/cigarette/cigar, -/obj/structure/table/wood, /turf/simulated/floor/plasteel{ - icon_state = "cafeteria"; - dir = 2 + tag = "icon-whiteblue (NORTHEAST)"; + icon_state = "whiteblue"; + dir = 5 }, /area/shuttle/escape) -"aO" = ( +"bg" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 1 + }, +/obj/machinery/light, +/turf/simulated/shuttle/floor4, +/area/shuttle/escape) +"bh" = ( +/obj/machinery/recharger/wallcharger{ + pixel_x = 5; + pixel_y = -28 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/shuttle/escape) +"bi" = ( /obj/machinery/chem_dispenser/soda, /obj/structure/table/wood, /turf/simulated/floor/plasteel{ @@ -236,842 +461,1093 @@ dir = 2 }, /area/shuttle/escape) -"aP" = ( -/obj/machinery/door/airlock/shuttle{ - aiControlDisabled = 1; - hackProof = 1; - id_tag = "s_docking_airlock"; - name = "Shuttle Hatch" - }, -/turf/simulated/floor/plating, -/area/shuttle/escape) -"aQ" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 1 - }, -/turf/simulated/shuttle/floor4, -/area/shuttle/escape) -"aR" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 1 - }, -/turf/simulated/floor/plasteel{ - icon_state = "cafeteria"; - dir = 2 - }, -/area/shuttle/escape) -"aS" = ( -/turf/simulated/shuttle/wall{ - tag = "icon-swall7"; - icon_state = "swall7"; - dir = 2 - }, -/area/shuttle/escape) -"aT" = ( -/obj/docking_port/mobile/emergency{ - dir = 4; - dwidth = 11; - height = 13; - timid = 1; - width = 24 - }, -/obj/machinery/door/airlock/shuttle{ - aiControlDisabled = 1; - hackProof = 1; - id_tag = "s_docking_airlock"; - name = "Shuttle Hatch" - }, -/turf/simulated/shuttle/floor, -/area/shuttle/escape) -"aU" = ( -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"aV" = ( -/obj/machinery/door/window/southright{ - autoclose = 1; - name = "engineering door"; - req_access_txt = "32" - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor2" - }, -/area/shuttle/escape) -"aW" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, -/turf/simulated/shuttle/floor4, -/area/shuttle/escape) -"aX" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor2" - }, -/area/shuttle/escape) -"aY" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"aZ" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"ba" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor5" - }, -/area/shuttle/escape) -"bb" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/shuttle/floor4, -/area/shuttle/escape) -"bc" = ( -/obj/machinery/door/window/southright{ - autoclose = 1; - name = "science door"; - req_access_txt = "47" - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor5" - }, -/area/shuttle/escape) -"bd" = ( -/obj/machinery/door/window/northleft{ - autoclose = 1; - name = "security door"; +"bj" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Escape Shuttle Cell"; + normalspeed = 0; req_access_txt = "2" }, /turf/simulated/shuttle/floor4, /area/shuttle/escape) -"be" = ( -/obj/structure/window/reinforced{ - dir = 1 +"bk" = ( +/obj/machinery/door/airlock/mining/glass{ + name = "Escape Shuttle Cargo"; + normalspeed = 0; + req_access_txt = "50" }, -/turf/simulated/shuttle/floor{ - icon_state = "floor2" +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "neutralfull" }, /area/shuttle/escape) -"bf" = ( -/obj/item/radio/intercom{ - dir = 8; - name = "station intercom (General)"; - pixel_x = -28 +"bl" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 }, -/obj/machinery/light/spot{ - tag = "icon-tube1 (WEST)"; +/obj/machinery/light{ icon_state = "tube1"; dir = 8 }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, /turf/simulated/shuttle/floor4, /area/shuttle/escape) -"bg" = ( -/obj/structure/grille, -/obj/structure/window/full/shuttle{ - icon_state = "8" - }, -/turf/simulated/floor/plating, -/area/shuttle/escape) -"bh" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bi" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bj" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor2" - }, -/area/shuttle/escape) -"bk" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/shuttle/floor4, -/area/shuttle/escape) -"bl" = ( -/obj/machinery/door/window/northleft{ - autoclose = 1; - name = "medical door"; - req_access_txt = "5" - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor3" - }, -/area/shuttle/escape) "bm" = ( -/obj/structure/window/reinforced, -/obj/structure/chair/comfy/shuttle{ +/obj/machinery/sleeper/upgraded{ dir = 4 }, -/turf/simulated/shuttle/floor{ - icon_state = "floor2" +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" }, /area/shuttle/escape) "bn" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, +/obj/structure/table/reinforced, /turf/simulated/shuttle/floor{ - icon_state = "floor5" + icon_state = "floor2" }, /area/shuttle/escape) "bo" = ( -/obj/structure/window/reinforced, -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor2" +/obj/machinery/door/airlock/shuttle{ + aiControlDisabled = 1; + hackProof = 1; + id_tag = "s_docking_airlock"; + name = "Shuttle Hatch" }, +/turf/simulated/floor/plasteel, /area/shuttle/escape) "bp" = ( -/obj/structure/window/reinforced, /obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/obj/machinery/light{ + icon_state = "tube1"; dir = 4 }, -/turf/simulated/floor/plasteel, +/turf/simulated/shuttle/floor4, /area/shuttle/escape) "bq" = ( -/obj/structure/window/reinforced, -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"br" = ( -/obj/structure/window/reinforced, -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor5" - }, -/area/shuttle/escape) -"bs" = ( -/obj/structure/closet/walllocker/emerglocker{ - pixel_x = 28 - }, -/obj/machinery/light/spot{ - tag = "icon-tube1 (EAST)"; - icon_state = "tube1"; - dir = 4 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor3" - }, -/area/shuttle/escape) -"bt" = ( -/obj/structure/window/reinforced, -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor5" - }, -/area/shuttle/escape) -"bu" = ( -/obj/machinery/light/spot{ - tag = "icon-tube1 (WEST)"; - icon_state = "tube1"; - dir = 8 - }, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bv" = ( -/obj/machinery/light/spot{ - tag = "icon-tube1 (EAST)"; - icon_state = "tube1"; - dir = 4 - }, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bw" = ( -/obj/machinery/door/airlock/shuttle{ - aiControlDisabled = 1; - hackProof = 1; - id_tag = "s_docking_airlock"; - name = "Shuttle Hatch" - }, -/turf/simulated/shuttle/floor, -/area/shuttle/escape) -"bx" = ( -/obj/structure/noticeboard, -/turf/simulated/shuttle/wall{ - icon_state = "swall12"; - dir = 2 - }, -/area/shuttle/escape) -"by" = ( -/obj/machinery/door/airlock/shuttle{ - aiControlDisabled = 1; - hackProof = 1; - id_tag = null; - name = "Shuttle Cargo Hatch" - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor3" - }, -/area/shuttle/escape) -"bz" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor3" - }, -/area/shuttle/escape) -"bA" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor5" - }, -/area/shuttle/escape) -"bB" = ( -/obj/structure/table, -/obj/item/storage/firstaid/o2{ - layer = 2.8; - pixel_x = 4; - pixel_y = 6 - }, -/obj/item/storage/firstaid/regular{ - pixel_x = 2; - pixel_y = 6 - }, -/obj/item/storage/firstaid/regular{ - pixel_x = -2; - pixel_y = 4 - }, -/obj/item/bodybag{ - pixel_x = 5 - }, -/obj/item/storage/firstaid/fire, -/obj/item/storage/firstaid/regular{ - pixel_x = 2; - pixel_y = 3 - }, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bC" = ( -/obj/machinery/door/airlock/medical/glass{ - id_tag = null; - name = "Escape Shuttle Infirmary"; - req_access_txt = "0" - }, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bD" = ( -/turf/simulated/shuttle/wall{ - icon_state = "swall11"; - dir = 2 - }, -/area/shuttle/escape) -"bE" = ( -/obj/machinery/door/airlock/shuttle{ - aiControlDisabled = 1; - hackProof = 1; - id_tag = "s_docking_airlock"; - name = "Shuttle Hatch" - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor2" - }, -/area/shuttle/escape) -"bF" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor3" - }, -/area/shuttle/escape) -"bG" = ( -/turf/simulated/shuttle/floor{ - icon_state = "floor3" - }, -/area/shuttle/escape) -"bH" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/shuttle/floor{ - icon_state = "floor3" - }, -/area/shuttle/escape) -"bI" = ( -/obj/machinery/vending/wallmed{ - layer = 3.3; - name = "Emergency NanoMed"; - pixel_x = 28; - pixel_y = 0; - req_access_txt = "0" - }, -/obj/machinery/sleeper, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bJ" = ( -/obj/item/radio/intercom{ - dir = 4; - name = "station intercom (General)"; - pixel_x = 28 - }, -/obj/item/extinguisher, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bK" = ( -/obj/structure/closet/cardboard, -/obj/machinery/light/spot{ - tag = "icon-tube1 (WEST)"; - icon_state = "tube1"; - dir = 8 - }, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bL" = ( -/obj/machinery/recharge_station, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bN" = ( -/obj/structure/bed/roller, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bO" = ( -/obj/machinery/sleeper, -/obj/machinery/light/spot{ - tag = "icon-tube1 (EAST)"; - icon_state = "tube1"; - dir = 4 - }, -/turf/simulated/floor/plasteel, -/area/shuttle/escape) -"bP" = ( /turf/space, /turf/simulated/shuttle/wall{ icon_state = "swall_f5"; dir = 2 }, /area/shuttle/escape) -"bQ" = ( -/obj/structure/window/reinforced{ +"br" = ( +/obj/structure/chair/comfy/shuttle{ dir = 1 }, -/obj/structure/shuttle/engine/heater, -/turf/simulated/shuttle/plating, +/turf/simulated/shuttle/floor{ + icon_state = "floor2" + }, /area/shuttle/escape) -"bR" = ( +"bs" = ( +/turf/simulated/shuttle/wall{ + icon_state = "swall11"; + dir = 2 + }, +/area/shuttle/escape) +"bt" = ( +/turf/simulated/shuttle/wall{ + icon_state = "swall7"; + dir = 2 + }, +/area/shuttle/escape) +"bu" = ( /obj/structure/shuttle/engine/heater, /obj/structure/window/reinforced{ dir = 1 }, /turf/simulated/shuttle/plating, /area/shuttle/escape) -"bS" = ( +"bv" = ( /turf/space, /turf/simulated/shuttle/wall{ icon_state = "swall_f9"; dir = 2 }, /area/shuttle/escape) -"bT" = ( +"bw" = ( /obj/structure/shuttle/engine/propulsion, /turf/simulated/shuttle/plating, /area/shuttle/escape) +"bx" = ( +/obj/structure/bed/roller, +/obj/machinery/iv_drip, +/obj/machinery/light, +/turf/simulated/floor/plasteel{ + tag = "icon-whiteblue (EAST)"; + icon_state = "whiteblue"; + dir = 4 + }, +/area/shuttle/escape) +"by" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor2" + }, +/area/shuttle/escape) +"bz" = ( +/turf/simulated/shuttle/floor{ + icon_state = "floor2" + }, +/area/shuttle/escape) +"bA" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor2" + }, +/area/shuttle/escape) +"bB" = ( +/obj/structure/table/reinforced, +/turf/simulated/shuttle/floor{ + icon_state = "floor5" + }, +/area/shuttle/escape) +"bC" = ( +/obj/structure/sign/directions/engineering, +/turf/simulated/shuttle/wall{ + icon_state = "swall12"; + dir = 2 + }, +/area/shuttle/escape) +"bD" = ( +/obj/machinery/door/airlock/shuttle{ + aiControlDisabled = 1; + hackProof = 1; + id_tag = "s_docking_airlock"; + name = "Shuttle Hatch" + }, +/obj/docking_port/mobile/emergency{ + dir = 4; + dwidth = 11; + height = 18; + timid = 1; + width = 29 + }, +/turf/simulated/floor/plasteel, +/area/shuttle/escape) +"bE" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 1 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor5" + }, +/area/shuttle/escape) +"bF" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"bG" = ( +/obj/machinery/status_display, +/turf/simulated/shuttle/wall{ + tag = "icon-swall14"; + icon_state = "swall14"; + dir = 2 + }, +/area/shuttle/escape) +"bH" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/obj/machinery/light{ + icon_state = "tube1"; + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"bI" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor5" + }, +/area/shuttle/escape) +"bJ" = ( +/turf/simulated/shuttle/floor{ + icon_state = "floor5" + }, +/area/shuttle/escape) +"bK" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor5" + }, +/area/shuttle/escape) +"bL" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/obj/machinery/light{ + icon_state = "tube1"; + dir = 8 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor2" + }, +/area/shuttle/escape) +"bM" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/obj/machinery/light{ + icon_state = "tube1"; + dir = 4 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor2" + }, +/area/shuttle/escape) +"bO" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/obj/machinery/light{ + icon_state = "tube1"; + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "bot"; + dir = 1 + }, +/area/shuttle/escape) +"bP" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "bot"; + dir = 1 + }, +/area/shuttle/escape) +"bQ" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "bot"; + dir = 1 + }, +/area/shuttle/escape) +"bR" = ( +/obj/structure/flora/ausbushes/grassybush, +/obj/structure/flora/ausbushes/lavendergrass, +/obj/structure/flora/ausbushes/ppflowers, +/obj/structure/flora/ausbushes/sunnybush, +/obj/structure/window/full/shuttle, +/turf/simulated/floor/grass, +/area/shuttle/escape) +"bS" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/obj/machinery/light{ + icon_state = "tube1"; + dir = 8 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor5" + }, +/area/shuttle/escape) +"bT" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/obj/machinery/light{ + icon_state = "tube1"; + dir = 4 + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor5" + }, +/area/shuttle/escape) "bU" = ( -/turf/space, +/obj/structure/sign/directions/science, +/turf/simulated/shuttle/wall{ + icon_state = "swall12"; + dir = 2 + }, +/area/shuttle/escape) +"bV" = ( +/obj/structure/sign/directions/cargo, +/turf/simulated/shuttle/wall{ + icon_state = "swall12"; + dir = 2 + }, +/area/shuttle/escape) +"bW" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/obj/machinery/recharger/wallcharger{ + pixel_x = -24 + }, +/turf/simulated/shuttle/floor4, +/area/shuttle/escape) +"bX" = ( +/obj/machinery/ai_status_display, +/turf/simulated/shuttle/wall{ + icon_state = "swall3"; + dir = 2 + }, +/area/shuttle/escape) +"bY" = ( +/obj/structure/flora/ausbushes/grassybush, +/obj/structure/flora/ausbushes/lavendergrass, +/obj/structure/flora/ausbushes/ywflowers, +/obj/structure/flora/ausbushes/fernybush, +/obj/structure/window/full/shuttle, +/turf/simulated/floor/grass, +/area/shuttle/escape) +"bZ" = ( +/obj/machinery/recharge_station/upgraded, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "neutralfull" + }, +/area/shuttle/escape) +"ca" = ( +/obj/machinery/light, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "neutralfull" + }, +/area/shuttle/escape) +"cb" = ( +/obj/machinery/door/airlock/engineering/glass{ + name = "Escape Shuttle Engineering"; + normalspeed = 0; + req_access_txt = "32"; + req_one_access_txt = "0" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor2" + }, +/area/shuttle/escape) +"cc" = ( +/obj/structure/extinguisher_cabinet, +/turf/simulated/shuttle/wall{ + icon_state = "swall3"; + dir = 2 + }, +/area/shuttle/escape) +"cd" = ( +/obj/machinery/door/airlock/research/glass{ + name = "Escape Shuttle Research"; + normalspeed = 0; + req_access_txt = "47" + }, +/turf/simulated/shuttle/floor{ + icon_state = "floor5" + }, +/area/shuttle/escape) +"cf" = ( +/turf/simulated/shuttle/wall{ + icon_state = "swallc4" + }, +/area/shuttle/escape) +"ck" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/obj/machinery/light{ + icon_state = "tube1"; + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "bot"; + dir = 1 + }, +/area/shuttle/escape) +"cp" = ( +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"cw" = ( +/obj/structure/bed/roller, +/obj/machinery/iv_drip, +/turf/simulated/floor/plasteel{ + tag = "icon-whiteblue (EAST)"; + icon_state = "whiteblue"; + dir = 4 + }, +/area/shuttle/escape) +"cA" = ( +/obj/machinery/light{ + icon_state = "tube1"; + dir = 8 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "bot"; + dir = 1 + }, +/area/shuttle/escape) +"cD" = ( +/obj/structure/table/reinforced, +/obj/item/storage/backpack/medic, +/obj/item/storage/belt/medical, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"cE" = ( +/obj/structure/bed/roller, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"cG" = ( +/obj/structure/table/reinforced, +/obj/item/clothing/gloves/color/latex/nitrile, +/obj/item/clothing/mask/breath, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"cH" = ( +/turf/simulated/shuttle/wall{ + icon_state = "wall3" + }, +/area/shuttle/escape) +"sc" = ( +/obj/machinery/door/airlock/medical/glass{ + id_tag = null; + name = "Escape Shuttle Medical Seating"; + normalspeed = 0; + req_access_txt = "5" + }, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"xD" = ( +/obj/structure/table/reinforced, +/obj/item/reagent_containers/iv_bag/blood/random, +/obj/item/reagent_containers/iv_bag/blood/random, +/obj/item/reagent_containers/iv_bag/blood/random, +/obj/item/reagent_containers/iv_bag/blood/random, +/obj/item/reagent_containers/iv_bag/blood/random, +/obj/item/reagent_containers/iv_bag/blood/random, +/turf/simulated/floor/plasteel{ + tag = "icon-whiteblue (EAST)"; + icon_state = "whiteblue"; + dir = 4 + }, +/area/shuttle/escape) +"PY" = ( +/obj/structure/table/reinforced, +/obj/item/storage/firstaid/machine{ + pixel_x = -5; + pixel_y = -5 + }, +/obj/item/storage/firstaid/regular, +/obj/item/storage/firstaid/adv{ + pixel_x = 5; + pixel_y = 5 + }, +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "cmo" + }, +/area/shuttle/escape) +"Qf" = ( +/obj/machinery/defibrillator_mount/loaded, +/turf/simulated/shuttle/wall{ + icon_state = "swall12"; + dir = 2 + }, +/area/shuttle/escape) +"UN" = ( +/obj/structure/chair/comfy/shuttle, +/turf/simulated/shuttle/floor4, +/area/shuttle/escape) +"Wy" = ( +/obj/structure/chair/comfy/shuttle, +/obj/machinery/light{ + dir = 1; + on = 1 + }, +/turf/simulated/shuttle/floor4, /area/shuttle/escape) (1,1,1) = {" aa -aa -aa -aa -aa ab -ap -ap -ap -aP -aS -aT -ap -aB -bg -aJ -ap -bw -aS -bE +aq ac -ap -bP +aq +aP +aq +aq +aq +bo +aP +bD +aq +ac +ac +ac +aq +bo +aq +bo +bt +aq +ac +ac +aq +aq +bt +bq aa "} (2,1,1) = {" aa -aa -ab -ap -ap -aw +av +az +bl +az +ac +az +bW az aH -aI -aI -ae -aU -bf -aW -bj -bm -bu -aU -ae -aU -aU -bK -ax +aN +ba bP +bO +bP +bP +bP +ba +aT +ba +bR +bP +cA +bP +bP +bP +av +cH +cH "} (3,1,1) = {" aa -ab -af -aq -aq -ae -aA -aI -aI -aI -ae -aU -bd -aI -be -aV -aU -aU -bx -aU -aU -bL -bQ -bT +ac +aH +aH +aH +bj +aH +aH +aH +aH +av +ba +ba +ba +ba +ba +ba +ba +ba +ba +bV +ba +ba +ba +ba +ba +av +bu +bw "} (4,1,1) = {" aa -ae -ai -al -aq -ae -az -aI -aI -aQ -ae -aU -bk -bb +av aX -bo -aU -aU -by -aU -aU -bL +bp +aX +ac +aH +aH +aH +aR +av +ba bQ -bT +bQ +bQ +bQ +bQ +bQ +ba +ba +bk +ba +ba +ba +ba +ba +ac +bu +bw "} (5,1,1) = {" -aa -ae -aj -al +ab +bs +aq +aq aq -ae -az -aI -aI -aQ -ae -aU -aB -bg -bg -aJ -aU -aU -aB -bg -bg -aJ aw -bS +Wy +aH +aH +bg +av +ba +bP +bP +bP +bP +bP +bP +ba +ba +bR +bQ +ck +bQ +bQ +bQ +av +cH +cH "} (6,1,1) = {" -ab -af -ak -al -aq -ax -aB -aJ -aM -aB -aJ -aU -aY -bh -bh -bp -aU -aU -aY -bh -bh -bh +av ae -bU +am +ar +ay +aD +aJ +aH +aH +aH +aW +ba +ba +ba +ba +ba +ba +ba +ba +ca +bG +bY +cc +bR +bX +bY +aw +cH +cH "} (7,1,1) = {" ac -ag -al -aq -aq -ay -aC -aC -aC -aC -aC -aU -aU -aU -aU -aU -aU -aU -aU -aU -aU -aU -ae -bU +af +an +as +aA +av +UN +aG +aH +aH +aW +ba +bQ +bQ +bQ +bQ +bQ +bQ +ba +ba +bR +by +bL +by +by +bn +av +cH +cH "} (8,1,1) = {" -ad -ah -am +ac +ag +ao +an +bh +aZ aq aq -ae -aD -aD -aD -aD -aD -aU -aZ -bi -bi -bq -aU -aU -aZ -bi -bi -bi -ae -bU +aW +ac +cf +ba +bP +bP +bP +bP +bP +bP +ba +ba +bC +bz +bz +bz +bz +br +av +bu +bw "} (9,1,1) = {" -aa -ae +ac +ah +ao +an an -al -aq -ae aE +an +an +an +an aE -aE -aE -aE -aU -aB -bg -bg -aJ -aU -aU -aB -bg -bg -aJ -aw -bP +ba +ba +ba +ba +ba +ba +ba +ba +ba +cb +bz +bz +bz +bz +br +ac +bu +bw "} (10,1,1) = {" -aa -ae +ac +ai ao -al -aq -ae -aC -aC -aC -aC -aC -aU -bz -bF +an +an +aE +an +an +an +an +aE +ba +ba +ba +ba +ba +ba +ba +ba ba -br -aU -aU -bC -aU -aU -bN bR -bT +bz +bz +bz +bz +br +av +bu +bw "} (11,1,1) = {" -aa -ad -ah -aq -aq -ae -aF -aK -aN -aR -aC -aU -bl -bG -bn -bc -aU -aU ac -aU -aU -bB +aj +ao +at +an +aZ +aq +aq +aL +ac +bb +ba +bQ +bQ +bQ +bQ +bQ +bQ +ba +ba bR -bT +bA +bM +bA +bA +bn +av +bu +bw "} (12,1,1) = {" -aa -aa -ad -ap -ap +ac +ak +an +au +aB +av +bm +cp +cp +cp +aI +ba +bP +bP +bP +bP +bP +bP +ba +ca +bG +bY +cc +bR +bX +bY aw -aG -aL -aO -aR -aC -aU -bs -bH -bA -bt -bv -aU -ae -bI -bJ -bO -ax -bS +cH +cH "} (13,1,1) = {" +av +al +ap +ax +aC +aD +bm +cp +cp +cp +aI +ba +ba +ba +ba +ba +ba +ba +ba +ba +bR +bI +bS +bI +bI +bB +av +cH +cH +"} +(14,1,1) = {" +ad +bt +aq +aq +aq +aw +bc +cp +cp +bx +av +ba +aS +aU +aU +aU +aU +aS +ba +ba +bU +bJ +bJ +bJ +bJ +bE +av +cH +cH +"} +(15,1,1) = {" aa +av +bF +aM +bF +Qf +cE +cp +cp +cw +av +ba +aO +aV +aV +aV +aV +aU +ba +ba +cd +bJ +bJ +bJ +bJ +bE +ac +bu +bw +"} +(16,1,1) = {" aa +ac +cp +cp +cp +sc +cp +cp +cp +xD +av +ba +aS +aS +aS +aS +aV +aU +ba +ba +bR +bJ +bJ +bJ +bJ +bE +av +bu +bw +"} +(17,1,1) = {" aa -aa +av +aF +bH +aF +av +cG +PY +cD +bf +av +aT +aS +bd +be +bi +aV +aU +bZ +bZ +bR +bK +bT +bK +bK +bB +av +cH +cH +"} +(18,1,1) = {" aa ad -ap -ap -ap +aq ac -ap -ap -ap -aB -bg -aJ -ap +aq +bs +aq +aq +aq +aq +bs ac -bD -ap -ap -ap -bS +ac +ac +ac +ac +ac +aq +ac +ac +bs +ac +aq +ac +ac +ac +bs +bv aa "} diff --git a/_maps/metastation.dm b/_maps/metastation.dm index 7747ad79fb8..0941ffc453a 100644 --- a/_maps/metastation.dm +++ b/_maps/metastation.dm @@ -23,7 +23,7 @@ z7 = empty space #define MAP_TRANSITION_CONFIG list(\ DECLARE_LEVEL(MAIN_STATION, CROSSLINKED, list(STATION_LEVEL, STATION_CONTACT, REACHABLE, AI_OK)),\ DECLARE_LEVEL(CENTCOMM, SELFLOOPING, list(ADMIN_LEVEL, BLOCK_TELEPORT, IMPEDES_MAGIC)),\ -DECLARE_LEVEL(MINING, SELFLOOPING, list(REACHABLE, STATION_CONTACT, HAS_WEATHER, ORE_LEVEL, AI_OK))) +DECLARE_LEVEL(MINING, SELFLOOPING, list(ORE_LEVEL, REACHABLE, STATION_CONTACT, HAS_WEATHER, AI_OK))) #define USING_MAP_DATUM /datum/map/metastation diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index 8dd95119aff..6f65661e2d4 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -173,6 +173,7 @@ GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new()) add_fingerprint(user) var/unsafe_wrenching = FALSE + var/safefromgusts = FALSE var/I = int_air ? int_air.return_pressure() : 0 var/E = env_air ? env_air.return_pressure() : 0 var/internal_pressure = I - E @@ -190,9 +191,16 @@ GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new()) "You hear ratchet.") investigate_log("was REMOVED by [key_name(usr)]", "atmos") + for(var/obj/item/clothing/shoes/magboots/usermagboots in user.get_equipped_items()) + if(usermagboots.gustprotection && usermagboots.magpulse) + safefromgusts = TRUE + //You unwrenched a pipe full of pressure? let's splat you into the wall silly. if(unsafe_wrenching) - unsafe_pressure_release(user,internal_pressure) + if(safefromgusts) + to_chat(user, "Your magboots cling to the floor as a great burst of wind bellows against you.") + else + unsafe_pressure_release(user,internal_pressure) deconstruct(TRUE) else return ..() diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm index 7e7a6e9eb49..ae099bf6d36 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -137,51 +137,56 @@ return add_fingerprint(user) - ui_interact(user) + tgui_interact(user) /obj/machinery/atmospherics/binary/passive_gate/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) +/obj/machinery/atmospherics/binary/passive_gate/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) + ui = new(user, src, ui_key, "AtmosPump", name, 310, 110, master_ui, state) ui.open() -/obj/machinery/atmospherics/binary/passive_gate/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) +/obj/machinery/atmospherics/binary/passive_gate/tgui_data(mob/user) + var/list/data = list( + "on" = on, + "rate" = round(target_pressure), + "max_rate" = MAX_OUTPUT_PRESSURE, + "gas_unit" = "kPa", + "step" = 10 // This is for the TGUI step. It's here since multiple pumps share the same UI, but need different values. + ) return data -/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list) +/obj/machinery/atmospherics/binary/passive_gate/tgui_act(action, list/params) if(..()) - return 1 + return - if(href_list["power"]) + switch(action) + if("power") + toggle() + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + return TRUE + + if("max_rate") + target_pressure = MAX_OUTPUT_PRESSURE + . = TRUE + + if("min_rate") + target_pressure = 0 + . = TRUE + + if("custom_rate") + target_pressure = clamp(text2num(params["rate"]), 0 , MAX_OUTPUT_PRESSURE) + . = TRUE + if(.) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + +/obj/machinery/atmospherics/binary/passive_gate/proc/toggle() + if(powered()) on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure)) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - - update_icon() - SSnanoui.update_uis(src) + update_icon() /obj/machinery/atmospherics/binary/passive_gate/attackby(obj/item/W, mob/user, params) if(!istype(W, /obj/item/wrench)) diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index 550fd140d1d..5178507bbc4 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -192,51 +192,51 @@ Thus, the two variables affect pump operation are set in New(): return add_fingerprint(user) - ui_interact(user) + tgui_interact(user) /obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) +/obj/machinery/atmospherics/binary/pump/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) + ui = new(user, src, ui_key, "AtmosPump", name, 310, 110, master_ui, state) ui.open() -/obj/machinery/atmospherics/binary/pump/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) +/obj/machinery/atmospherics/binary/pump/tgui_data(mob/user) + var/list/data = list( + "on" = on, + "rate" = round(target_pressure), + "max_rate" = MAX_OUTPUT_PRESSURE, + "gas_unit" = "kPa", + "step" = 10 // This is for the TGUI step. It's here since multiple pumps share the same UI, but need different values. + ) return data -/obj/machinery/atmospherics/binary/pump/Topic(href,href_list) +/obj/machinery/atmospherics/binary/pump/tgui_act(action, list/params) if(..()) - return 1 + return - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure)) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + switch(action) + if("power") + toggle() + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + return TRUE - update_icon() - SSnanoui.update_uis(src) + if("max_rate") + target_pressure = MAX_OUTPUT_PRESSURE + . = TRUE + + if("min_rate") + target_pressure = 0 + . = TRUE + + if("custom_rate") + target_pressure = clamp(text2num(params["rate"]), 0 , MAX_OUTPUT_PRESSURE) + . = TRUE + if(.) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") /obj/machinery/atmospherics/binary/pump/power_change() var/old_stat = stat diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index 804d25cfa1b..12079341143 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -188,51 +188,51 @@ Thus, the two variables affect pump operation are set in New(): return add_fingerprint(user) - ui_interact(user) + tgui_interact(user) /obj/machinery/atmospherics/binary/volume_pump/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/atmospherics/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) +/obj/machinery/atmospherics/binary/volume_pump/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 310, 115, state = state) + ui = new(user, src, ui_key, "AtmosPump", name, 310, 110, master_ui, state) ui.open() -/obj/machinery/atmospherics/binary/volume_pump/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["rate"] = round(transfer_rate) - data["max_rate"] = round(MAX_TRANSFER_RATE) +/obj/machinery/atmospherics/binary/volume_pump/tgui_data(mob/user) + var/list/data = list( + "on" = on, + "rate" = round(transfer_rate), + "max_rate" = round(MAX_TRANSFER_RATE), + "gas_unit" = "L/s", + "step" = 1 // This is for the TGUI step. It's here since multiple pumps share the same UI, but need different values. + ) return data -/obj/machinery/atmospherics/binary/volume_pump/Topic(href,href_list) +/obj/machinery/atmospherics/binary/volume_pump/tgui_act(action, list/params) if(..()) - return 1 + return - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["rate"]) - var/rate = href_list["rate"] - if(rate == "max") - rate = MAX_TRANSFER_RATE - . = TRUE - else if(rate == "input") - rate = input("New transfer rate (0-[MAX_TRANSFER_RATE] L/s):", name, transfer_rate) as num|null - if(!isnull(rate)) - . = TRUE - else if(text2num(rate) != null) - rate = text2num(rate) - . = TRUE - if(.) - transfer_rate = clamp(rate, 0, MAX_TRANSFER_RATE) - investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos") + switch(action) + if("power") + toggle() + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + return TRUE - update_icon() - SSnanoui.update_uis(src) + if("max_rate") + transfer_rate = MAX_TRANSFER_RATE + . = TRUE + + if("min_rate") + transfer_rate = 0 + . = TRUE + + if("custom_rate") + transfer_rate = clamp(text2num(params["rate"]), 0 , MAX_TRANSFER_RATE) + . = TRUE + if(.) + investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos") /obj/machinery/atmospherics/binary/volume_pump/power_change() var/old_stat = stat diff --git a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm deleted file mode 100644 index d03329e24fc..00000000000 --- a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm +++ /dev/null @@ -1,94 +0,0 @@ -//-------------------------------------------- -// Omni device port types -//-------------------------------------------- -#define ATM_NONE 0 -#define ATM_INPUT 1 -#define ATM_OUTPUT 2 - -#define ATM_O2 3 -#define ATM_N2 4 -#define ATM_CO2 5 -#define ATM_P 6 //Plasma -#define ATM_N2O 7 - -//-------------------------------------------- -// Omni port datum -// -// Used by omni devices to manage connections -// to other atmospheric objects. -//-------------------------------------------- -/datum/omni_port - var/obj/machinery/atmospherics/omni/master - var/dir - var/update = 1 - var/mode = 0 - var/concentration = 0 - var/con_lock = 0 - var/transfer_moles = 0 - var/datum/gas_mixture/air - var/obj/machinery/atmospherics/node - var/datum/pipeline/parent - -/datum/omni_port/New(var/obj/machinery/atmospherics/omni/M, var/direction = NORTH) - ..() - dir = direction - if(istype(M)) - master = M - air = new - air.volume = 200 - -/datum/omni_port/proc/connect() - if(node) - return - master.atmos_init() - if(node) - node.atmos_init() - node.addMember(master) - master.build_network() - -/datum/omni_port/proc/disconnect() - if(node) - node.disconnect(master) - node = null - master.nullifyPipenet(parent) - -//-------------------------------------------- -// Need to find somewhere else for these -//-------------------------------------------- - -//returns a text string based on the direction flag input -// if capitalize is true, it will return the string capitalized -// otherwise it will return the direction string in lower case -/proc/dir_name(var/dir, var/capitalize = 0) - var/string = null - switch(dir) - if(NORTH) - string = "North" - if(SOUTH) - string = "South" - if(EAST) - string = "East" - if(WEST) - string = "West" - - if(!capitalize && string) - string = lowertext(string) - - return string - -//returns a direction flag based on the string passed to it -// case insensitive -/proc/dir_flag(var/dir) - dir = lowertext(dir) - switch(dir) - if("north") - return NORTH - if("south") - return SOUTH - if("east") - return EAST - if("west") - return WEST - else - return 0 - diff --git a/code/ATMOSPHERICS/components/omni_devices/filter.dm b/code/ATMOSPHERICS/components/omni_devices/filter.dm deleted file mode 100644 index 0803d34e522..00000000000 --- a/code/ATMOSPHERICS/components/omni_devices/filter.dm +++ /dev/null @@ -1,273 +0,0 @@ -//-------------------------------------------- -// Gas filter - omni variant -//-------------------------------------------- -/obj/machinery/atmospherics/omni/filter - name = "omni gas filter" - icon_state = "map_filter" - - var/list/o_filters = new() - var/datum/omni_port/input - var/datum/omni_port/output - -/obj/machinery/atmospherics/omni/filter/Destroy() - input = null - output = null - o_filters.Cut() - return ..() - -/obj/machinery/atmospherics/omni/filter/sort_ports() - for(var/datum/omni_port/P in ports) - if(P.update) - if(output == P) - output = null - if(input == P) - input = null - if(o_filters.Find(P)) - o_filters -= P - - P.air.volume = 200 - switch(P.mode) - if(ATM_INPUT) - input = P - if(ATM_OUTPUT) - output = P - if(ATM_O2 to ATM_N2O) - o_filters += P - -/obj/machinery/atmospherics/omni/filter/error_check() - if(!input || !output || !o_filters) - return 1 - if(o_filters.len < 1 || o_filters.len > 2) //requires 1 or 2 o_filters ~otherwise why are you using a filter? - return 1 - - return 0 - -/obj/machinery/atmospherics/omni/filter/process_atmos() - ..() - if(!on) - return 0 - - if(!input || !output) - return 0 - - var/datum/gas_mixture/output_air = output.air //BYOND doesn't like referencing "output.air.return_pressure()" so we need to make a direct reference - var/datum/gas_mixture/input_air = input.air // it's completely happy with them if they're in a loop though i.e. "P.air.return_pressure()"... *shrug* - - var/output_pressure = output_air.return_pressure() - - if(output_pressure >= target_pressure) - return 1 - for(var/datum/omni_port/P in o_filters) - if(P.air.return_pressure() >= target_pressure) - return 1 - - var/pressure_delta = target_pressure - output_pressure - - if(input_air.return_temperature() > 0) - input.transfer_moles = pressure_delta * output_air.volume / (input_air.return_temperature() * R_IDEAL_GAS_EQUATION) - - if(input.transfer_moles > 0) - var/datum/gas_mixture/removed = input_air.remove(input.transfer_moles) - - if(!removed) - return 1 - - for(var/datum/omni_port/P in o_filters) - var/datum/gas_mixture/filtered_out = new - filtered_out.temperature = removed.return_temperature() - - switch(P.mode) - if(ATM_O2) - filtered_out.oxygen = removed.oxygen - removed.oxygen = 0 - if(ATM_N2) - filtered_out.nitrogen = removed.nitrogen - removed.nitrogen = 0 - if(ATM_CO2) - filtered_out.carbon_dioxide = removed.carbon_dioxide - removed.carbon_dioxide = 0 - if(ATM_P) - filtered_out.toxins = removed.toxins - removed.toxins = 0 - - filtered_out.agent_b = removed.agent_b - removed.agent_b = 0 - if(ATM_N2O) - filtered_out.sleeping_agent = removed.sleeping_agent - removed.sleeping_agent = 0 - else - filtered_out = null - - P.air.merge(filtered_out) - P.parent.update = 1 - - output_air.merge(removed) - output.parent.update = 1 - - input.transfer_moles = 0 - input.parent.update = 1 - - return 1 - -/obj/machinery/atmospherics/omni/filter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) - usr.set_machine(src) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "omni_filter.tmpl", "Omni Filter Control", 330, 330) - ui.open() - -/obj/machinery/atmospherics/omni/filter/ui_data(mob/user, datum/topic_state/state) - var/data[0] - - data["power"] = on - data["config"] = configuring - - var/portData[0] - for(var/datum/omni_port/P in ports) - if(!configuring && P.mode == 0) - continue - - var/input = 0 - var/output = 0 - var/filter = 1 - var/f_type = null - switch(P.mode) - if(ATM_INPUT) - input = 1 - filter = 0 - if(ATM_OUTPUT) - output = 1 - filter = 0 - if(ATM_O2 to ATM_N2O) - f_type = mode_send_switch(P.mode) - - portData[++portData.len] = list("dir" = dir_name(P.dir, capitalize = 1), \ - "input" = input, \ - "output" = output, \ - "filter" = filter, \ - "f_type" = f_type) - - if(portData.len) - data["ports"] = portData - if(output) - data["pressure"] = target_pressure - - return data - -/obj/machinery/atmospherics/omni/filter/proc/mode_send_switch(var/mode = ATM_NONE) - switch(mode) - if(ATM_O2) - return "Oxygen" - if(ATM_N2) - return "Nitrogen" - if(ATM_CO2) - return "Carbon Dioxide" - if(ATM_P) - return "Plasma" //*cough* Plasma *cough* - if(ATM_N2O) - return "Nitrous Oxide" - else - return null - -/obj/machinery/atmospherics/omni/filter/Topic(href, href_list) - if(..()) - return 1 - switch(href_list["command"]) - if("power") - if(!configuring) - on = !on - else - on = 0 - if("configure") - configuring = !configuring - if(configuring) - on = 0 - - //only allows config changes when in configuring mode ~otherwise you'll get weird pressure stuff going on - if(configuring && !on) - switch(href_list["command"]) - if("set_pressure") - var/new_pressure = input(usr,"Enter new output pressure (0-4500kPa)","Pressure control",target_pressure) as num - target_pressure = between(0, new_pressure, 4500) - if("switch_mode") - switch_mode(dir_flag(href_list["dir"]), mode_return_switch(href_list["mode"])) - if("switch_filter") - var/new_filter = input(usr,"Select filter mode:","Change filter",href_list["mode"]) in list("None", "Oxygen", "Nitrogen", "Carbon Dioxide", "Plasma", "Nitrous Oxide") - switch_filter(dir_flag(href_list["dir"]), mode_return_switch(new_filter)) - - update_icon() - SSnanoui.update_uis(src) - return - -/obj/machinery/atmospherics/omni/filter/proc/mode_return_switch(var/mode) - switch(mode) - if("Oxygen") - return ATM_O2 - if("Nitrogen") - return ATM_N2 - if("Carbon Dioxide") - return ATM_CO2 - if("Plasma") - return ATM_P - if("Nitrous Oxide") - return ATM_N2O - if("in") - return ATM_INPUT - if("out") - return ATM_OUTPUT - if("None") - return ATM_NONE - else - return null - -/obj/machinery/atmospherics/omni/filter/proc/switch_filter(var/dir, var/mode) - //check they aren't trying to disable the input or output ~this can only happen if they hack the cached tmpl file - for(var/datum/omni_port/P in ports) - if(P.dir == dir) - if(P.mode == ATM_INPUT || P.mode == ATM_OUTPUT) - return - - switch_mode(dir, mode) - -/obj/machinery/atmospherics/omni/filter/proc/switch_mode(var/port, var/mode) - if(mode == null || !port) - return - var/datum/omni_port/target_port = null - var/list/other_ports = new() - - for(var/datum/omni_port/P in ports) - if(P.dir == port) - target_port = P - else - other_ports += P - - var/previous_mode = null - if(target_port) - previous_mode = target_port.mode - target_port.mode = mode - if(target_port.mode != previous_mode) - handle_port_change(target_port) - else - return - else - return - - for(var/datum/omni_port/P in other_ports) - if(P.mode == mode) - var/old_mode = P.mode - P.mode = previous_mode - if(P.mode != old_mode) - handle_port_change(P) - - update_ports() - -/obj/machinery/atmospherics/omni/filter/proc/handle_port_change(var/datum/omni_port/P) - switch(P.mode) - if(ATM_NONE) - initialize_directions &= ~P.dir - P.disconnect() - else - initialize_directions |= P.dir - P.connect() - P.update = 1 diff --git a/code/ATMOSPHERICS/components/omni_devices/mixer.dm b/code/ATMOSPHERICS/components/omni_devices/mixer.dm deleted file mode 100644 index 5586bdc3a98..00000000000 --- a/code/ATMOSPHERICS/components/omni_devices/mixer.dm +++ /dev/null @@ -1,291 +0,0 @@ -//-------------------------------------------- -// Gas mixer - omni variant -//-------------------------------------------- -/obj/machinery/atmospherics/omni/mixer - name = "omni gas mixer" - icon_state = "map_mixer" - - var/list/inputs = new() - var/datum/omni_port/output - - //setup tags for initial concentration values (must be decimal) - var/tag_north_con - var/tag_south_con - var/tag_east_con - var/tag_west_con - -/obj/machinery/atmospherics/omni/mixer/New() - ..() - if(mapper_set()) - var/con = 0 - for(var/datum/omni_port/P in ports) - switch(P.dir) - if(NORTH) - if(tag_north_con && tag_north == 1) - P.concentration = tag_north_con - con += max(0, tag_north_con) - if(SOUTH) - if(tag_south_con && tag_south == 1) - P.concentration = tag_south_con - con += max(0, tag_south_con) - if(EAST) - if(tag_east_con && tag_east == 1) - P.concentration = tag_east_con - con += max(0, tag_east_con) - if(WEST) - if(tag_west_con && tag_west == 1) - P.concentration = tag_west_con - con += max(0, tag_west_con) - - //mappers who are bad at maths will be punished (total concentration must be 100%) - if(con != 1) - tag_north_con = null - tag_south_con = null - tag_east_con = null - tag_west_con = null - -/obj/machinery/atmospherics/omni/mixer/Destroy() - inputs.Cut() - output = null - return ..() - -/obj/machinery/atmospherics/omni/mixer/sort_ports() - for(var/datum/omni_port/P in ports) - if(P.update) - if(output == P) - output = null - if(inputs.Find(P)) - inputs -= P - - P.air.volume = 200 - switch(P.mode) - if(ATM_INPUT) - inputs += P - if(ATM_OUTPUT) - output = P - - if(!mapper_set()) - for(var/datum/omni_port/P in inputs) - P.concentration = 1 / max(1, inputs.len) - - if(output) - output.air.volume *= 0.75 * inputs.len - output.concentration = 1 - -/obj/machinery/atmospherics/omni/mixer/proc/mapper_set() - return (tag_north_con || tag_south_con || tag_east_con || tag_west_con) - -/obj/machinery/atmospherics/omni/mixer/error_check() - if(!output || !inputs) - return 1 - if(inputs.len < 2 || inputs.len > 3) //requires 2 or 3 inputs ~otherwise why are you using a mixer? - return 1 - - return 0 - -/obj/machinery/atmospherics/omni/mixer/process_atmos() - ..() - if(!on) - return 0 - - var/datum/gas_mixture/output_air = output.air - var/output_pressure = output_air.return_pressure() - - - if(output_pressure >= target_pressure * 0.999) - //No need to mix if target is already full! - 0.1% margin of error so we minimize processing minor gas volumes - return 1 - - //Calculate necessary moles to transfer using PV=nRT - - var/pressure_delta = target_pressure - output_pressure - - for(var/datum/omni_port/P in inputs) - if(P.air.return_temperature() > 0) - P.transfer_moles = (P.concentration * pressure_delta) * output_air.return_volume() / (P.air.return_temperature() * R_IDEAL_GAS_EQUATION) - - var/ratio_check = null - - for(var/datum/omni_port/P in inputs) - if(!P.transfer_moles) - return 1 - if(P.air.total_moles() < P.transfer_moles) - ratio_check = 1 - continue - - if(ratio_check) - var/list/ratio_list = new() - for(var/datum/omni_port/P in inputs) - ratio_list.Add(P.air.total_moles() / P.transfer_moles) - - var/ratio = min(ratio_list) - - for(var/datum/omni_port/P in inputs) - P.transfer_moles *= ratio - - for(var/datum/omni_port/P in inputs) - if(P.transfer_moles > 0) - output_air.merge(P.air.remove(P.transfer_moles)) - P.parent.update = 1 - P.transfer_moles = 0 - - output.parent.update = 1 - - return 1 - -/obj/machinery/atmospherics/omni/mixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, force_open = 0) - usr.set_machine(src) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "omni_mixer.tmpl", "Omni Mixer Control", 360, 330) - ui.open() - -/obj/machinery/atmospherics/omni/mixer/ui_data(mob/user, datum/topic_state/state) - var/data[0] - - data["power"] = on - data["config"] = configuring - - var/portData[0] - for(var/datum/omni_port/P in ports) - if(!configuring && P.mode == 0) - continue - - var/input = 0 - var/output = 0 - switch(P.mode) - if(ATM_INPUT) - input = 1 - if(ATM_OUTPUT) - output = 1 - - portData[++portData.len] = list("dir" = dir_name(P.dir, capitalize = 1), \ - "concentration" = P.concentration, \ - "input" = input, \ - "output" = output, \ - "con_lock" = P.con_lock) - - if(portData.len) - data["ports"] = portData - if(output) - data["pressure"] = target_pressure - - return data - -/obj/machinery/atmospherics/omni/mixer/Topic(href, href_list) - if(..()) - return 1 - - switch(href_list["command"]) - if("power") - if(!configuring) - on = !on - else - on = 0 - if("configure") - configuring = !configuring - if(configuring) - on = 0 - - //only allows config changes when in configuring mode ~otherwise you'll get weird pressure stuff going on - if(configuring && !on) - switch(href_list["command"]) - if("set_pressure") - var/new_pressure = input(usr,"Enter new output pressure (0-4500kPa)","Pressure control",target_pressure) as num - target_pressure = between(0, new_pressure, 4500) - if("switch_mode") - switch_mode(dir_flag(href_list["dir"]), href_list["mode"]) - if("switch_con") - change_concentration(dir_flag(href_list["dir"])) - if("switch_conlock") - con_lock(dir_flag(href_list["dir"])) - - update_icon() - SSnanoui.update_uis(src) - return - -/obj/machinery/atmospherics/omni/mixer/proc/switch_mode(var/port = NORTH, var/mode = ATM_NONE) - if(mode != ATM_INPUT && mode != ATM_OUTPUT) - switch(mode) - if("in") - mode = ATM_INPUT - if("out") - mode = ATM_OUTPUT - else - mode = ATM_NONE - - for(var/datum/omni_port/P in ports) - var/old_mode = P.mode - if(P.dir == port) - switch(mode) - if(ATM_INPUT) - if(P.mode == ATM_OUTPUT) - return - P.mode = mode - if(ATM_OUTPUT) - P.mode = mode - if(ATM_NONE) - if(P.mode == ATM_OUTPUT) - return - if(P.mode == ATM_INPUT && inputs.len > 2) - P.mode = mode - else if(P.mode == ATM_OUTPUT && mode == ATM_OUTPUT) - P.mode = ATM_INPUT - if(P.mode != old_mode) - switch(P.mode) - if(ATM_NONE) - initialize_directions &= ~P.dir - P.disconnect() - else - initialize_directions |= P.dir - P.connect() - P.update = 1 - - update_ports() - -/obj/machinery/atmospherics/omni/mixer/proc/change_concentration(var/port = NORTH) - tag_north_con = null - tag_south_con = null - tag_east_con = null - tag_west_con = null - - var/old_con = 0 - var/non_locked = 0 - var/remain_con = 1 - - for(var/datum/omni_port/P in inputs) - if(P.dir == port) - old_con = P.concentration - else if(!P.con_lock) - non_locked++ - else - remain_con -= P.concentration - - //return if no adjustable ports - if(non_locked < 1) - return - - var/new_con = (input(usr,"Enter a new concentration (0-[round(remain_con * 100, 0.5)])%","Concentration control", min(remain_con, old_con)*100) as num) / 100 - - //cap it between 0 and the max remaining concentration - new_con = between(0, new_con, remain_con) - - //new_con = min(remain_con, new_con) - - //clamp remaining concentration so we don't go into negatives - remain_con = max(0, remain_con - new_con) - - //distribute remaining concentration between unlocked ports evenly - remain_con /= max(1, non_locked) - - for(var/datum/omni_port/P in inputs) - if(P.dir == port) - P.concentration = new_con - else if(!P.con_lock) - P.concentration = remain_con - -/obj/machinery/atmospherics/omni/mixer/proc/con_lock(var/port = NORTH) - for(var/datum/omni_port/P in inputs) - if(P.dir == port) - P.con_lock = !P.con_lock diff --git a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm deleted file mode 100644 index d75e2e683c1..00000000000 --- a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm +++ /dev/null @@ -1,302 +0,0 @@ -//-------------------------------------------- -// Base omni device -//-------------------------------------------- -/obj/machinery/atmospherics/omni - name = "omni device" - icon = 'icons/atmos/omni_devices.dmi' - icon_state = "base" - use_power = IDLE_POWER_USE - initialize_directions = 0 - - can_unwrench = 1 - - var/on = 0 - var/configuring = 0 - var/target_pressure = ONE_ATMOSPHERE - - var/tag_north = ATM_NONE - var/tag_south = ATM_NONE - var/tag_east = ATM_NONE - var/tag_west = ATM_NONE - - var/overlays_on[5] - var/overlays_off[5] - var/overlays_error[2] - var/underlays_current[4] - - var/list/ports = new() - -/obj/machinery/atmospherics/omni/New() - ..() - icon_state = "base" - - ports = new() - for(var/d in GLOB.cardinal) - var/datum/omni_port/new_port = new(src, d) - switch(d) - if(NORTH) - new_port.mode = tag_north - if(SOUTH) - new_port.mode = tag_south - if(EAST) - new_port.mode = tag_east - if(WEST) - new_port.mode = tag_west - if(new_port.mode > 0) - initialize_directions |= d - ports += new_port - - build_icons() - -/obj/machinery/atmospherics/omni/Destroy() - for(var/datum/omni_port/P in ports) - if(P.node) - P.node.disconnect(src) - P.node = null - nullifyPipenet(P.parent) - return ..() - -/obj/machinery/atmospherics/omni/atmos_init() - ..() - for(var/datum/omni_port/P in ports) - if(P.node || P.mode == 0) - continue - for(var/obj/machinery/atmospherics/target in get_step(src, P.dir)) - if(target.initialize_directions & get_dir(target,src)) - P.node = target - break - - for(var/datum/omni_port/P in ports) - P.update = 1 - - update_ports() - -/obj/machinery/atmospherics/omni/update_icon() - ..() - - if(stat & NOPOWER) - overlays = overlays_off - on = 0 - else if(error_check()) - overlays = overlays_error - on = 0 - else - overlays = on ? (overlays_on) : (overlays_off) - - underlays = underlays_current - -/obj/machinery/atmospherics/omni/proc/error_check() - return - -/obj/machinery/atmospherics/omni/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/omni/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - if(!istype(W, /obj/item/wrench)) - return ..() - - if(can_unwrench) - var/int_pressure = 0 - for(var/datum/omni_port/P in ports) - int_pressure += P.air.return_pressure() - var/datum/gas_mixture/env_air = loc.return_air() - if((int_pressure - env_air.return_pressure()) > 2*ONE_ATMOSPHERE) - to_chat(user, "You cannot unwrench [src], it is too exerted due to internal pressure.") - add_fingerprint(user) - return 1 - playsound(loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if(do_after(user, 40 * W.toolspeed, target = src)) - user.visible_message( \ - "[user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear a ratchet.") - new /obj/item/pipe(loc, make_from=src) - qdel(src) - else - return ..() - -/obj/machinery/atmospherics/omni/attack_hand(mob/user) - if(..()) - return - - add_fingerprint(usr) - ui_interact(user) - -/obj/machinery/atmospherics/omni/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/omni/proc/build_icons() - if(!check_icon_cache()) - return - - var/core_icon = null - if(istype(src, /obj/machinery/atmospherics/omni/mixer)) - core_icon = "mixer" - else if(istype(src, /obj/machinery/atmospherics/omni/filter)) - core_icon = "filter" - else - return - - //directional icons are layers 1-4, with the core icon on layer 5 - if(core_icon) - overlays_off[5] = GLOB.pipe_icon_manager.get_atmos_icon("omni", , , core_icon) - overlays_on[5] = GLOB.pipe_icon_manager.get_atmos_icon("omni", , , core_icon + "_glow") - - overlays_error[1] = GLOB.pipe_icon_manager.get_atmos_icon("omni", , , core_icon) - overlays_error[2] = GLOB.pipe_icon_manager.get_atmos_icon("omni", , , "error") - -/obj/machinery/atmospherics/omni/proc/update_port_icons() - if(!check_icon_cache()) - return - - for(var/datum/omni_port/P in ports) - if(P.update) - var/ref_layer = 0 - switch(P.dir) - if(NORTH) - ref_layer = 1 - if(SOUTH) - ref_layer = 2 - if(EAST) - ref_layer = 3 - if(WEST) - ref_layer = 4 - - if(!ref_layer) - continue - - var/list/port_icons = select_port_icons(P) - if(port_icons) - if(P.node) - underlays_current[ref_layer] = port_icons["pipe_icon"] - else - underlays_current[ref_layer] = null - overlays_off[ref_layer] = port_icons["off_icon"] - overlays_on[ref_layer] = port_icons["on_icon"] - else - underlays_current[ref_layer] = null - overlays_off[ref_layer] = null - overlays_on[ref_layer] = null - - update_icon() - -/obj/machinery/atmospherics/omni/proc/select_port_icons(var/datum/omni_port/P) - if(!istype(P)) - return - - if(P.mode > 0) - var/ic_dir = dir_name(P.dir) - var/ic_on = ic_dir - var/ic_off = ic_dir - switch(P.mode) - if(ATM_INPUT) - ic_on += "_in_glow" - ic_off += "_in" - if(ATM_OUTPUT) - ic_on += "_out_glow" - ic_off += "_out" - if(ATM_O2 to ATM_N2O) - ic_on += "_filter" - ic_off += "_out" - - ic_on = GLOB.pipe_icon_manager.get_atmos_icon("omni", , , ic_on) - ic_off = GLOB.pipe_icon_manager.get_atmos_icon("omni", , , ic_off) - - var/pipe_state - var/turf/T = get_turf(src) - if(!istype(T)) - return - if(T.intact && istype(P.node, /obj/machinery/atmospherics/pipe) && P.node.level == 1 ) - //pipe_state = GLOB.pipe_icon_manager.get_atmos_icon("underlay_down", P.dir, color_cache_name(P.node)) - pipe_state = GLOB.pipe_icon_manager.get_atmos_icon("underlay", P.dir, color_cache_name(P.node), "down") - else - //pipe_state = GLOB.pipe_icon_manager.get_atmos_icon("underlay_intact", P.dir, color_cache_name(P.node)) - pipe_state = GLOB.pipe_icon_manager.get_atmos_icon("underlay", P.dir, color_cache_name(P.node), "intact") - - return list("on_icon" = ic_on, "off_icon" = ic_off, "pipe_icon" = pipe_state) - -/obj/machinery/atmospherics/omni/update_underlays() - for(var/datum/omni_port/P in ports) - P.update = 1 - update_ports() - -/obj/machinery/atmospherics/omni/hide(var/i) - update_underlays() - -/obj/machinery/atmospherics/omni/proc/update_ports() - sort_ports() - update_port_icons() - for(var/datum/omni_port/P in ports) - P.update = 0 - -/obj/machinery/atmospherics/omni/proc/sort_ports() - return - -// Pipenet procs -/obj/machinery/atmospherics/omni/build_network(remove_deferral = FALSE) - for(var/datum/omni_port/P in ports) - if(!P.parent) - P.parent = new /datum/pipeline() - P.parent.build_pipeline(src) - ..() - -/obj/machinery/atmospherics/omni/disconnect(obj/machinery/atmospherics/reference) - for(var/datum/omni_port/P in ports) - if(reference == P.node) - if(istype(P.node, /obj/machinery/atmospherics/pipe)) - qdel(P.parent) - P.node = null - update_ports() - -/obj/machinery/atmospherics/omni/nullifyPipenet(datum/pipeline/P) - ..() - if(!P) - return - for(var/datum/omni_port/PO in ports) - if(P == PO.parent) - PO.parent.other_airs -= PO.air - PO.parent = null - -/obj/machinery/atmospherics/omni/returnPipenetAir(datum/pipeline/P) - for(var/datum/omni_port/PO in ports) - if(P == PO.parent) - return PO.air - -/obj/machinery/atmospherics/omni/pipeline_expansion(datum/pipeline/P) - if(P) - for(var/datum/omni_port/PO in ports) - if(PO.parent == P) - return list(PO.node) - else - var/list/nodes = list() - for(var/datum/omni_port/PO in ports) - nodes += PO.node - - return nodes - -/obj/machinery/atmospherics/omni/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) - for(var/datum/omni_port/PO in ports) - if(A == PO.node) - PO.parent = P - -/obj/machinery/atmospherics/omni/returnPipenet(obj/machinery/atmospherics/A) - for(var/datum/omni_port/P in ports) - if(A == P.node) - return P.parent - -/obj/machinery/atmospherics/omni/replacePipenet(datum/pipeline/Old, datum/pipeline/New) - for(var/datum/omni_port/P in ports) - if(Old == P.parent) - P.parent = New - - -/obj/machinery/atmospherics/omni/process_atmos() - ..() - for(var/datum/omni_port/port in ports) - if(!port.parent) - return 0 - return 1 diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index 64fb308c2db..1ba338e7d88 100755 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -1,26 +1,39 @@ + +/// Nothing will be filtered. +#define FILTER_NOTHING -1 +/// Plasma, and Oxygen Agent B. +#define FILTER_TOXINS 0 +/// Oxygen only. +#define FILTER_OXYGEN 1 +/// Nitrogen only. +#define FILTER_NITROGEN 2 +/// Carbon dioxide only. +#define FILTER_CO2 3 +/// Nitrous oxide only. +#define FILTER_N2O 4 + /obj/machinery/atmospherics/trinary/filter + name = "gas filter" icon = 'icons/atmos/filter.dmi' icon_state = "map" - - can_unwrench = 1 - - name = "gas filter" - + can_unwrench = TRUE + /// The amount of pressure the filter wants to operate at. var/target_pressure = ONE_ATMOSPHERE - - var/filter_type = 0 -/* -Filter types: --1: Nothing - 0: Toxins: Toxins, Oxygen Agent B - 1: Oxygen: Oxygen ONLY - 2: Nitrogen: Nitrogen ONLY - 3: Carbon Dioxide: Carbon Dioxide ONLY - 4: Sleeping Agent (N2O) -*/ - - var/frequency = 0 + /// The type of gas we want to filter. Valid values that go here are from the `FILTER` defines at the top of the file. + var/filter_type = FILTER_NOTHING + /// The frequency of the filter. Used with `radio_connection`. + var/frequency = NONE + /// A reference to the filter's `datum/radio_frequency`. var/datum/radio_frequency/radio_connection + /// A list of available filter options. Used with `tgui_data`. + var/list/filter_list = list( + "Nothing" = FILTER_NOTHING, + "Plasma" = FILTER_TOXINS, + "O2" = FILTER_OXYGEN, + "N2" = FILTER_NITROGEN, + "CO2" = FILTER_CO2, + "N2O" = FILTER_N2O + ) /obj/machinery/atmospherics/trinary/filter/CtrlClick(mob/living/user) if(!istype(user) || user.incapacitated()) @@ -146,26 +159,26 @@ Filter types: filtered_out.temperature = removed.temperature switch(filter_type) - if(0) //removing hydrocarbons + if(FILTER_TOXINS) filtered_out.toxins = removed.toxins removed.toxins = 0 filtered_out.agent_b = removed.agent_b removed.agent_b = 0 - if(1) //removing O2 + if(FILTER_OXYGEN) filtered_out.oxygen = removed.oxygen removed.oxygen = 0 - if(2) //removing N2 + if(FILTER_NITROGEN) filtered_out.nitrogen = removed.nitrogen removed.nitrogen = 0 - if(3) //removing CO2 + if(FILTER_CO2) filtered_out.carbon_dioxide = removed.carbon_dioxide removed.carbon_dioxide = 0 - if(4)//removing N2O + if(FILTER_N2O) filtered_out.sleeping_agent = removed.sleeping_agent removed.sleeping_agent = 0 else @@ -188,7 +201,7 @@ Filter types: ..() /obj/machinery/atmospherics/trinary/filter/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) /obj/machinery/atmospherics/trinary/filter/attack_hand(mob/user) if(..()) @@ -199,53 +212,56 @@ Filter types: return add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/atmospherics/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) +/obj/machinery/atmospherics/trinary/filter/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_filter.tmpl", name, 475, 155, state = state) + ui = new(user, src, ui_key, "AtmosFilter", name, 380, 140, master_ui, state) ui.open() -/obj/machinery/atmospherics/trinary/filter/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - data["filter_type"] = filter_type +/obj/machinery/atmospherics/trinary/filter/tgui_data(mob/user) + var/list/data = list( + "on" = on, + "pressure" = round(target_pressure), + "max_pressure" = round(MAX_OUTPUT_PRESSURE), + "filter_type" = filter_type + ) + data["filter_type_list"] = list() + for(var/label in filter_list) + data["filter_type_list"] += list(list("label" = label, "gas_type" = filter_list[label])) + return data -/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE +/obj/machinery/atmospherics/trinary/filter/tgui_act(action, list/params) if(..()) - return 1 + return - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure) && !..()) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - if(href_list["filter"]) - filter_type = text2num(href_list["filter"]) - investigate_log("was set to filter [filter_type] by [key_name(usr)]", "atmos") - . = TRUE + switch(action) + if("power") + toggle() + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + return TRUE - update_icon() - SSnanoui.update_uis(src) + if("set_filter") + filter_type = text2num(params["filter"]) + investigate_log("was set to filter [filter_type] by [key_name(usr)]", "atmos") + return TRUE + + if("max_pressure") + target_pressure = MAX_OUTPUT_PRESSURE + . = TRUE + + if("min_pressure") + target_pressure = 0 + . = TRUE + + if("custom_pressure") + target_pressure = clamp(text2num(params["pressure"]), 0, MAX_OUTPUT_PRESSURE) + . = TRUE + if(.) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") /obj/machinery/atmospherics/trinary/filter/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/pen)) @@ -258,3 +274,10 @@ Filter types: return else return ..() + +#undef FILTER_NOTHING +#undef FILTER_TOXINS +#undef FILTER_OXYGEN +#undef FILTER_NITROGEN +#undef FILTER_CO2 +#undef FILTER_N2O diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm index be4bf969009..52845f1154d 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm @@ -152,7 +152,7 @@ return 1 /obj/machinery/atmospherics/trinary/mixer/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) /obj/machinery/atmospherics/trinary/mixer/attack_hand(mob/user) if(..()) @@ -163,62 +163,62 @@ return add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/atmospherics/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) +/obj/machinery/atmospherics/trinary/mixer/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_mixer.tmpl", name, 370, 165, state = state) + ui = new(user, src, ui_key, "AtmosMixer", name, 330, 165, master_ui, state) ui.open() -/obj/machinery/atmospherics/trinary/mixer/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - data["node1_concentration"] = round(node1_concentration*100) - data["node2_concentration"] = round(node2_concentration*100) +/obj/machinery/atmospherics/trinary/mixer/tgui_data(mob/user) + var/list/data = list( + "on" = on, + "pressure" = round(target_pressure, 0.01), + "max_pressure" = MAX_OUTPUT_PRESSURE, + "node1_concentration" = round(node1_concentration * 100), + "node2_concentration" = round(node2_concentration * 100) + ) return data -/obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list) + + +/obj/machinery/atmospherics/trinary/mixer/tgui_act(action, list/params) if(..()) - return 1 + return - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure) && !..()) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - if(href_list["node1"]) - var/value = text2num(href_list["node1"]) - node1_concentration = max(0, min(1, node1_concentration + value)) - node2_concentration = max(0, min(1, node2_concentration - value)) - investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["node2"]) - var/value = text2num(href_list["node2"]) - node2_concentration = max(0, min(1, node2_concentration + value)) - node1_concentration = max(0, min(1, node1_concentration - value)) - investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos") - . = TRUE + switch(action) + if("power") + toggle() + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + return TRUE - update_icon() - SSnanoui.update_uis(src) + if("set_node") + if(params["node_name"] == "Node 1") + node1_concentration = clamp(round(text2num(params["concentration"]), 0.01), 0, 1) + node2_concentration = round(1 - node1_concentration, 0.01) + investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos") + return TRUE + else + node2_concentration = clamp(round(text2num(params["concentration"]), 0.01), 0, 1) + node1_concentration = round(1 - node2_concentration, 0.01) + investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos") + return TRUE + + if("max_pressure") + target_pressure = MAX_OUTPUT_PRESSURE + . = TRUE + + if("min_pressure") + target_pressure = 0 + . = TRUE + + if("custom_pressure") + target_pressure = clamp(text2num(params["pressure"]), 0, MAX_OUTPUT_PRESSURE) + . = TRUE + if(.) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") /obj/machinery/atmospherics/trinary/mixer/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/pen)) diff --git a/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm b/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm index 66ffca0aef5..5725a7a8e9b 100644 --- a/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm @@ -128,66 +128,56 @@ /obj/machinery/atmospherics/unary/vent_pump/process_atmos() ..() - if((stat & (NOPOWER|BROKEN))) - return 0 + if(stat & (NOPOWER|BROKEN)) + return FALSE if(!node) - on = 0 + on = FALSE //broadcast_status() // from now air alarm/control computer should request update purposely --rastaf0 if(!on) - return 0 + return FALSE if(welded) if(air_contents.return_pressure() >= weld_burst_pressure && prob(5)) //the weld is on but the cover is welded shut, can it withstand the internal pressure? visible_message("The welded cover of [src] bursts open!") - for(var/mob/M in range(1, src)) + for(var/mob/living/M in range(1)) unsafe_pressure_release(M, air_contents.return_pressure()) //let's send everyone flying welded = FALSE update_icon() - return 0 + return FALSE var/datum/gas_mixture/environment = loc.return_air() var/environment_pressure = environment.return_pressure() - if(pump_direction) //internal -> external var/pressure_delta = 10000 - - if(pressure_checks&1) + if(pressure_checks & 1) pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure)) - if(pressure_checks&2) + if(pressure_checks & 2) pressure_delta = min(pressure_delta, (air_contents.return_pressure() - internal_pressure_bound)) - if(pressure_delta > 0.5) - if(air_contents.temperature > 0) - var/transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) - - var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) - - loc.assume_air(removed) - air_update_turf() - - parent.update = 1 + if(pressure_delta > 0.5 && air_contents.temperature > 0) + var/transfer_moles = pressure_delta * environment.volume / (air_contents.temperature * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) + loc.assume_air(removed) + air_update_turf() + parent.update = TRUE else //external -> internal var/pressure_delta = 10000 - if(pressure_checks&1) + if(pressure_checks & 1) pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound)) - if(pressure_checks&2) + if(pressure_checks & 2) pressure_delta = min(pressure_delta, (internal_pressure_bound - air_contents.return_pressure())) - if(pressure_delta > 0.5) - if(environment.temperature > 0) - var/transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) + if(pressure_delta > 0.5 && environment.temperature > 0) + var/transfer_moles = pressure_delta * air_contents.volume / (environment.temperature * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = loc.remove_air(transfer_moles) + if(isnull(removed)) //in space + return + air_contents.merge(removed) + air_update_turf() + parent.update = TRUE - var/datum/gas_mixture/removed = loc.remove_air(transfer_moles) - if(isnull(removed)) //in space - return - - air_contents.merge(removed) - air_update_turf() - - parent.update = 1 - - return 1 + return TRUE //Radio remote control @@ -386,11 +376,11 @@ if(I.use_tool(src, user, 20, volume = I.tool_volume)) if(!welded) welded = TRUE - visible_message("[user] welds [src] shut!",\ + user.visible_message("[user] welds [src] shut!",\ "You weld [src] shut!") else welded = FALSE - visible_message("[user] unwelds [src]!",\ + user.visible_message("[user] unwelds [src]!",\ "You unweld [src]!") update_icon() diff --git a/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm index ed06dd68ce6..bbd5e760d2f 100644 --- a/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm @@ -404,10 +404,10 @@ if(I.use_tool(src, user, 20, volume = I.tool_volume)) if(!welded) welded = TRUE - visible_message("[user] welds [src] shut!",\ + user.visible_message("[user] welds [src] shut!",\ "You weld [src] shut!") else welded = FALSE - visible_message("[user] unwelds [src]!",\ + user.visible_message("[user] unwelds [src]!",\ "You unweld [src]!") update_icon() diff --git a/code/ATMOSPHERICS/datum_icon_manager.dm b/code/ATMOSPHERICS/datum_icon_manager.dm index 2286b3523a0..014fafc5c18 100644 --- a/code/ATMOSPHERICS/datum_icon_manager.dm +++ b/code/ATMOSPHERICS/datum_icon_manager.dm @@ -33,7 +33,6 @@ //var/list/underlays_intact[] //var/list/pipe_underlays_exposed[] //var/list/pipe_underlays_intact[] - var/list/omni_icons[] /datum/pipe_icon_manager/New() check_icons() @@ -53,8 +52,6 @@ return manifold_icons[state + color] if("device") return device_icons[state] - if("omni") - return omni_icons[state] if("underlay") return underlays[state + dir + color] //if("underlay_intact") @@ -75,8 +72,6 @@ gen_manifold_icons() if(!device_icons) gen_device_icons() - if(!omni_icons) - gen_omni_icons() //if(!underlays_intact || !underlays_down || !underlays_exposed || !pipe_underlays_exposed || !pipe_underlays_intact) if(!underlays) gen_underlay_icons() @@ -150,18 +145,6 @@ continue device_icons["scrubber" + state] = image('icons/atmos/vent_scrubber.dmi', icon_state = state) -/datum/pipe_icon_manager/proc/gen_omni_icons() - if(!omni_icons) - omni_icons = new() - - var/icon/omni = new('icons/atmos/omni_devices.dmi') - - for(var/state in omni.IconStates()) - if(!state || findtext(state, "map")) - continue - omni_icons[state] = image('icons/atmos/omni_devices.dmi', icon_state = state) - - /datum/pipe_icon_manager/proc/gen_underlay_icons() if(!underlays) diff --git a/code/LINDA/LINDA_system.dm b/code/LINDA/LINDA_system.dm index 3c8633c2b91..8d1ff95d3e9 100644 --- a/code/LINDA/LINDA_system.dm +++ b/code/LINDA/LINDA_system.dm @@ -32,13 +32,13 @@ if(!R) return 1 -atom/movable/proc/CanAtmosPass() +/atom/movable/proc/CanAtmosPass() return 1 -atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5) +/atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5) return (!density || !height) -turf/CanPass(atom/movable/mover, turf/target, height=1.5) +/turf/CanPass(atom/movable/mover, turf/target, height=1.5) if(!target) return 0 if(istype(mover)) // turf/Enter(...) will perform more advanced checks diff --git a/code/LINDA/LINDA_turf_tile.dm b/code/LINDA/LINDA_turf_tile.dm index acfa1983f72..8d71323cfb8 100644 --- a/code/LINDA/LINDA_turf_tile.dm +++ b/code/LINDA/LINDA_turf_tile.dm @@ -473,7 +473,7 @@ SSair.active_super_conductivity -= src return 0 -turf/simulated/proc/consider_superconductivity(starting) +/turf/simulated/proc/consider_superconductivity(starting) if(!thermal_conductivity) return 0 @@ -489,7 +489,7 @@ turf/simulated/proc/consider_superconductivity(starting) SSair.active_super_conductivity |= src return 1 -turf/simulated/proc/radiate_to_spess() //Radiate excess tile heat to space +/turf/simulated/proc/radiate_to_spess() //Radiate excess tile heat to space if(temperature > T0C) //Considering 0 degC as te break even point for radiation in and out var/delta_temperature = (temperature_archived - TCMB) //hardcoded space temperature if((heat_capacity > 0) && (abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)) diff --git a/code/_DATASTRUCTURES/stacks.dm b/code/_DATASTRUCTURES/stacks.dm deleted file mode 100644 index b310a3b9401..00000000000 --- a/code/_DATASTRUCTURES/stacks.dm +++ /dev/null @@ -1,56 +0,0 @@ -/datum/stack - var/list/stack = list() - var/max_elements = 0 - -/datum/stack/New(list/elements,max) - ..() - if(elements) - stack = elements.Copy() - if(max) - max_elements = max - -/datum/stack/proc/Pop() - if(is_empty()) - return null - . = stack[stack.len] - stack.Cut(stack.len,0) - -/datum/stack/proc/Push(element) - if(max_elements && (stack.len+1 > max_elements)) - return null - stack += element - -/datum/stack/proc/Top() - if(is_empty()) - return null - . = stack[stack.len] - -/datum/stack/proc/is_empty() - . = stack.len ? 0 : 1 - -//Rotate entire stack left with the leftmost looping around to the right -/datum/stack/proc/RotateLeft() - if(is_empty()) - return 0 - . = stack[1] - stack.Cut(1,2) - Push(.) - -//Rotate entire stack to the right with the rightmost looping around to the left -/datum/stack/proc/RotateRight() - if(is_empty()) - return 0 - . = stack[stack.len] - stack.Cut(stack.len,0) - stack.Insert(1,.) - - -/datum/stack/proc/Copy() - var/datum/stack/S=new() - S.stack = stack.Copy() - S.max_elements = max_elements - return S - - -/datum/stack/proc/Clear() - stack.Cut() diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index de20ccd1a4c..0c7e32a0a76 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -3,6 +3,7 @@ #define GAS_PL (1 << 2) #define GAS_CO2 (1 << 3) #define GAS_N2O (1 << 4) +#define GAS_A_B (1 << 5) //ATMOS //stuff you should probably leave well alone! diff --git a/code/__DEFINES/construction.dm b/code/__DEFINES/construction.dm index fc5b6eb74b9..1ef94c8ba65 100644 --- a/code/__DEFINES/construction.dm +++ b/code/__DEFINES/construction.dm @@ -28,6 +28,12 @@ #define AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS 1 #define AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER 2 +//used by airlocks and airlock wires. +#define AICONTROLDISABLED_OFF 0 // Silicons can control the airlock normally. +#define AICONTROLDISABLED_ON 1 // Silicons cannot control the airlock, but can hack the airlock. +#define AICONTROLDISABLED_BYPASS 2 // Silicons can control the airlock because they succeeded on the hack +#define AICONTROLDISABLED_PERMA 3 // Wire cutting an airlock on AICONTROLDISABLED_BYPASS toggles it between AICONTROLDISABLED_BYPASS and this. + //plastic flaps construction states #define PLASTIC_FLAPS_NORMAL 0 #define PLASTIC_FLAPS_DETACHED 1 diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 10aa1718dda..4b10bee2a2b 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -198,6 +198,8 @@ // /area signals +///from base of area/proc/power_change(): () +#define COMSIG_AREA_POWER_CHANGE "area_power_change" ///from base of area/Entered(): (atom/movable/M) #define COMSIG_AREA_ENTERED "area_entered" ///from base of area/Exited(): (atom/movable/M) @@ -726,3 +728,7 @@ #define COMSIG_XENO_TURF_CLICK_CTRL "xeno_turf_click_alt" ///from monkey CtrlClickOn(): (/mob) #define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" + +///SSalarm signals +#define COMSIG_TRIGGERED_ALARM "ssalarm_triggered" +#define COMSIG_CANCELLED_ALARM "ssalarm_cancelled" diff --git a/code/__DEFINES/instruments.dm b/code/__DEFINES/instruments.dm new file mode 100644 index 00000000000..64c77abc9fa --- /dev/null +++ b/code/__DEFINES/instruments.dm @@ -0,0 +1,29 @@ +#define INSTRUMENT_MIN_OCTAVE 1 +#define INSTRUMENT_MAX_OCTAVE 9 +#define INSTRUMENT_MIN_KEY 0 +#define INSTRUMENT_MAX_KEY 127 + +/// Max number of playing notes per instrument. +#define CHANNELS_PER_INSTRUMENT 128 + +/// Distance multiplier that makes us not be impacted by 3d sound as much. This is a multiplier so lower it is the closer we will pretend to be to people. +#define INSTRUMENT_DISTANCE_FALLOFF_BUFF 0.2 +/// How many tiles instruments have no falloff for +#define INSTRUMENT_DISTANCE_NO_FALLOFF 3 + +/// Maximum length a note should ever go for +#define INSTRUMENT_MAX_TOTAL_SUSTAIN (5 SECONDS) + +/// These are per decisecond. +#define INSTRUMENT_EXP_FALLOFF_MIN 1.025 //100/(1.025^50) calculated for [INSTRUMENT_MIN_SUSTAIN_DROPOFF] to be 30. +#define INSTRUMENT_EXP_FALLOFF_MAX 10 + +/// Minimum volume for when the sound is considered dead. +#define INSTRUMENT_MIN_SUSTAIN_DROPOFF 0.1 + +#define SUSTAIN_LINEAR 1 +#define SUSTAIN_EXPONENTIAL 2 + +// /datum/instrument instrument_flags +#define INSTRUMENT_LEGACY (1<<0) //Legacy instrument. Implies INSTRUMENT_DO_NOT_AUTOSAMPLE +#define INSTRUMENT_DO_NOT_AUTOSAMPLE (1<<1) //Do not automatically sample diff --git a/code/__DEFINES/js.dm b/code/__DEFINES/js.dm index e1a86e664d4..eeca4da55dd 100644 --- a/code/__DEFINES/js.dm +++ b/code/__DEFINES/js.dm @@ -36,7 +36,7 @@ Be sure to include required js functions in your page, or it'll raise an excepti And yes I know this is a proc in a defines file, but its highly relevant so it can be here */ -proc/send_byjax(receiver, control_id, target_element, new_content=null, callback=null, list/callback_args=null) +/proc/send_byjax(receiver, control_id, target_element, new_content=null, callback=null, list/callback_args=null) if(receiver && target_element && control_id) // && winexists(receiver, control_id)) var/list/argums = list(target_element, new_content) if(callback) diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index ed4cb0d5940..ee1cd3fa762 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -7,6 +7,7 @@ #define PLANE_SPACE_PARALLAX -90 #define FLOOR_PLANE -2 +#define FLOOR_OVERLAY_PLANE -1.5 #define GAME_PLANE -1 #define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals diff --git a/code/__DEFINES/logs.dm b/code/__DEFINES/logs.dm index 25d1fac7323..96fac582283 100644 --- a/code/__DEFINES/logs.dm +++ b/code/__DEFINES/logs.dm @@ -4,3 +4,8 @@ #define SAY_LOG "Say" #define EMOTE_LOG "Emote" #define MISC_LOG "Misc" +#define DEADCHAT_LOG "Deadchat" +#define OOC_LOG "OOC" +#define LOOC_LOG "LOOC" + +#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, DEADCHAT_LOG, OOC_LOG, LOOC_LOG, MISC_LOG) diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index b56024bfab8..521cf632388 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -92,3 +92,9 @@ // Firelock states #define FD_OPEN 1 #define FD_CLOSED 2 + +// Computer login types +#define LOGIN_TYPE_NORMAL 1 +#define LOGIN_TYPE_AI 2 +#define LOGIN_TYPE_ROBOT 3 +#define LOGIN_TYPE_ADMIN 4 diff --git a/code/__DEFINES/martial_arts.dm b/code/__DEFINES/martial_arts.dm new file mode 100644 index 00000000000..4eb139cc44a --- /dev/null +++ b/code/__DEFINES/martial_arts.dm @@ -0,0 +1,16 @@ +#define MARTIAL_COMBO_FAIL 0 // If the combo failed +#define MARTIAL_COMBO_CONTINUE 1 // If the combo should continue +#define MARTIAL_COMBO_DONE 2 // If the combo is successful and done +#define MARTIAL_COMBO_DONE_NO_CLEAR 3 // If the combo is successful and done but the others should have a chance to finish +#define MARTIAL_COMBO_DONE_BASIC_HIT 4 // If the combo should do a basic hit after it's done +#define MARTIAL_COMBO_DONE_CLEAR_COMBOS 5 // If the combo should do a basic hit after it's done + +#define MARTIAL_ARTS_CANNOT_USE -1 + +#define MARTIAL_COMBO_STEP_HARM "Harm" +#define MARTIAL_COMBO_STEP_DISARM "Disarm" +#define MARTIAL_COMBO_STEP_GRAB "Grab" +#define MARTIAL_COMBO_STEP_HELP "Help" + +// A check used for all act types. Such as disarm_act +#define MARTIAL_ARTS_ACT_CHECK if((. = ..()) != FALSE) return . diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index ad5e5df7583..0a36db1b185 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -69,9 +69,6 @@ #define ZONE_ACTIVE 1 #define ZONE_SLEEPING 0 -#define shuttle_time_in_station 1800 // 3 minutes in the station -#define shuttle_time_to_arrive 6000 // 10 minutes to arrive - #define EVENT_LEVEL_MUNDANE 1 #define EVENT_LEVEL_MODERATE 2 #define EVENT_LEVEL_MAJOR 3 @@ -245,28 +242,75 @@ 0.4,0.6,0.0,\ 0.2,0.2,0.6) -#define LIST_REPLACE_RENAME list("rebeccapurple" = "dark purple", "darkslategrey" = "dark grey", "darkolivegreen" = "dark green", "darkslateblue" = "dark blue",\ - "darkkhaki" = "khaki", "darkseagreen" = "light green", "midnightblue" = "blue", "lightgrey" = "light grey", "darkgrey" = "dark grey",\ - "steelblue" = "blue", "goldenrod" = "gold") +/* + Used for wire name appearances. Replaces the color name on the left with the one on the right. + The color on the left is the one used as the actual color of the wire, but it doesn't look good when written. + So, we need to replace the name to something that looks better. +*/ +#define LIST_COLOR_RENAME \ + list( \ + "rebeccapurple" = "dark purple",\ + "darkslategrey" = "dark grey", \ + "darkolivegreen"= "dark green", \ + "darkslateblue" = "dark blue", \ + "darkkhaki" = "khaki", \ + "darkseagreen" = "light green",\ + "midnightblue" = "blue", \ + "lightgrey" = "light grey", \ + "darkgrey" = "dark grey", \ + "steelblue" = "blue", \ + "goldenrod" = "gold" \ + ) -#define LIST_GREYSCALE_REPLACE list("red" = "lightgrey", "blue" = "grey", "green" = "grey", "orange" = "lightgrey", "brown" = "grey",\ - "gold" = "lightgrey", "cyan" = "lightgrey", "navy" = "grey", "purple" = "grey", "pink"= "lightgrey") +/// Pure Black and white colorblindness. Every species except Vulpkanins and Tajarans will have this. +#define GREYSCALE_COLOR_REPLACE \ + list( \ + "red" = "grey", \ + "blue" = "grey", \ + "green" = "grey", \ + "orange" = "light grey", \ + "brown" = "grey", \ + "gold" = "light grey", \ + "cyan" = "silver", \ + "magenta" = "grey", \ + "purple" = "grey", \ + "pink" = "light grey" \ + ) -#define LIST_VULP_REPLACE list("pink" = "beige", "orange" = "goldenrod", "gold" = "goldenrod", "red" = "darkolivegreen", "brown" = "darkolivegreen",\ - "green" = "darkslategrey", "cyan" = "steelblue", "purple" = "darkslategrey", "navy" = "midnightblue") - -#define LIST_TAJ_REPLACE list("red" = "rebeccapurple", "brown" = "rebeccapurple", "purple" = "darkslateblue", "blue" = "darkslateblue",\ - "green" = "darkolivegreen", "orange" = "darkkhaki", "gold" = "darkkhaki", "cyan" = "darkseagreen", \ - "navy" = "midnightblue", "pink" = "lightgrey") +/// Red colorblindness. Vulpkanins/Wolpins have this. +#define PROTANOPIA_COLOR_REPLACE \ + list( \ + "red" = "darkolivegreen", \ + "green" = "darkslategrey", \ + "orange" = "goldenrod", \ + "gold" = "goldenrod", \ + "brown" = "darkolivegreen", \ + "cyan" = "steelblue", \ + "magenta" = "blue", \ + "purple" = "darkslategrey", \ + "pink" = "beige" \ + ) +/// Yellow-Blue colorblindness. Tajarans/Farwas have this. +#define TRITANOPIA_COLOR_REPLACE \ + list( \ + "red" = "rebeccapurple", \ + "blue" = "darkslateblue", \ + "green" = "darkolivegreen", \ + "orange" = "darkkhaki", \ + "gold" = "darkkhaki", \ + "brown" = "rebeccapurple", \ + "cyan" = "darkseagreen", \ + "magenta" = "darkslateblue", \ + "purple" = "darkslateblue", \ + "pink" = "lightgrey" \ + ) //Gun trigger guards #define TRIGGER_GUARD_ALLOW_ALL -1 #define TRIGGER_GUARD_NONE 0 #define TRIGGER_GUARD_NORMAL 1 -#define CLIENT_FROM_VAR(I) (ismob(I) ? I:client : (istype(I, /client) ? I : (istype(I, /datum/mind) ? I:current?:client : null))) - // Macro to get the current elapsed round time, rather than total world runtime #define ROUND_TIME (SSticker.round_start_time ? (world.time - SSticker.round_start_time) : 0) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 719274857ec..933cf12dcfc 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -208,6 +208,7 @@ #define isguardian(A) (istype((A), /mob/living/simple_animal/hostile/guardian)) #define isnymph(A) (istype((A), /mob/living/simple_animal/diona)) #define ishostile(A) (istype(A, /mob/living/simple_animal/hostile)) +#define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider)) #define issilicon(A) (istype((A), /mob/living/silicon)) #define isAI(A) (istype((A), /mob/living/silicon/ai)) @@ -240,3 +241,12 @@ #define is_admin(user) (check_rights(R_ADMIN, 0, (user)) != 0) #define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; + +// Locations +#define is_ventcrawling(A) (istype(A.loc, /obj/machinery/atmospherics)) + +// Hearing protection +#define HEARING_PROTECTION_NONE 0 +#define HEARING_PROTECTION_MINOR 1 +#define HEARING_PROTECTION_MAJOR 2 +#define HEARING_PROTECTION_TOTAL 3 diff --git a/code/__DEFINES/muzzle_flash.dm b/code/__DEFINES/muzzle_flash.dm new file mode 100644 index 00000000000..e35e1c73ca0 --- /dev/null +++ b/code/__DEFINES/muzzle_flash.dm @@ -0,0 +1,7 @@ +#define MUZZLE_FLASH_STRENGTH_WEAK 1 +#define MUZZLE_FLASH_STRENGTH_NORMAL 2 +#define MUZZLE_FLASH_STRENGTH_STRONG 3 + +#define MUZZLE_FLASH_RANGE_WEAK 1 +#define MUZZLE_FLASH_RANGE_NORMAL 2 +#define MUZZLE_FLASH_RANGE_STRONG 3 diff --git a/code/__DEFINES/pipes.dm b/code/__DEFINES/pipes.dm index c855a5e4260..d56d446ca1d 100644 --- a/code/__DEFINES/pipes.dm +++ b/code/__DEFINES/pipes.dm @@ -21,8 +21,6 @@ #define PIPE_TVALVE 18 #define PIPE_MANIFOLD4W 19 #define PIPE_CAP 20 -#define PIPE_OMNI_MIXER 21 -#define PIPE_OMNI_FILTER 22 #define PIPE_UNIVERSAL 23 #define PIPE_SUPPLY_STRAIGHT 24 #define PIPE_SUPPLY_BENT 25 diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index c15dfd78b0f..3fcb9cc3410 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -12,6 +12,7 @@ #define CHANNEL_HIGHEST_AVAILABLE 1017 +#define MAX_INSTRUMENT_CHANNELS (128 * 6) #define SOUND_MINIMUM_PRESSURE 10 #define FALLOFF_SOUNDS 0.5 diff --git a/code/__DEFINES/stat.dm b/code/__DEFINES/stat.dm index c9763e4836c..9c115bed86f 100644 --- a/code/__DEFINES/stat.dm +++ b/code/__DEFINES/stat.dm @@ -22,16 +22,9 @@ // these define the time taken for the shuttle to get to SS13 // and the time before it leaves again -#define SHUTTLE_PREPTIME 300 // 5 minutes = 300 seconds - after this time, the shuttle departs centcom and cannot be recalled -#define SHUTTLE_LEAVETIME 180 // 3 minutes = 180 seconds - the duration for which the shuttle will wait at the station after arriving -#define SHUTTLE_TRANSIT_DURATION 300 // 5 minutes = 300 seconds - how long it takes for the shuttle to get to the station -#define SHUTTLE_TRANSIT_DURATION_RETURN 120 // 2 minutes = 120 seconds - for some reason it takes less time to come back, go figure. - -//Ferry shuttle processing status -#define IDLE_STATE 0 -#define WAIT_LAUNCH 1 -#define WAIT_ARRIVE 2 -#define WAIT_FINISH 3 +#define SHUTTLE_CALLTIME 6000 //10 minutes = 6000 deciseconds - time taken for emergency shuttle to reach the station when called (in deciseconds) +#define SHUTTLE_DOCKTIME 1800 //3 minutes = 1800 deciseconds - time taken for emergency shuttle to leave again once it has docked (in deciseconds) +#define SHUTTLE_ESCAPETIME 1200 //2 minutes = 1200 deciseconds - time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds) //shuttle mode defines #define SHUTTLE_IGNITING 0 diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 153eca7f373..11732a22375 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -45,11 +45,13 @@ // Subsystems shutdown in the reverse of the order they initialize in // The numbers just define the ordering, they are meaningless otherwise. #define INIT_ORDER_TITLE 100 // This **MUST** load first or people will se blank lobby screens -#define INIT_ORDER_GARBAGE 19 -#define INIT_ORDER_DBCORE 18 -#define INIT_ORDER_BLACKBOX 17 -#define INIT_ORDER_SERVER_MAINT 16 -#define INIT_ORDER_INPUT 15 +#define INIT_ORDER_GARBAGE 21 +#define INIT_ORDER_DBCORE 20 +#define INIT_ORDER_BLACKBOX 19 +#define INIT_ORDER_CLEANUP 18 +#define INIT_ORDER_INPUT 17 +#define INIT_ORDER_SOUNDS 16 +#define INIT_ORDER_INSTRUMENTS 15 #define INIT_ORDER_RESEARCH 14 #define INIT_ORDER_EVENTS 13 #define INIT_ORDER_JOBS 12 @@ -80,8 +82,7 @@ #define INIT_ORDER_NANOMOB -23 #define INIT_ORDER_SQUEAK -40 #define INIT_ORDER_PATH -50 -#define INIT_ORDER_PERSISTENCE -95 -#define INIT_ORDER_CHAT -100 //Should be last to ensure chat remains smooth during init. +#define INIT_ORDER_PERSISTENCE -95 // Subsystem fire priority, from lowest to highest priority // If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) @@ -89,7 +90,7 @@ #define FIRE_PRIORITY_NANOMOB 10 #define FIRE_PRIORITY_NIGHTSHIFT 10 #define FIRE_PRIORITY_IDLE_NPC 10 -#define FIRE_PRIORITY_SERVER_MAINT 10 +#define FIRE_PRIORITY_CLEANUP 10 #define FIRE_PRIORITY_TICKETS 10 #define FIRE_PRIORITY_RESEARCH 10 #define FIRE_PRIORITY_GARBAGE 15 @@ -113,7 +114,6 @@ #define FIRE_PRIORITY_MOBS 100 #define FIRE_PRIORITY_NANOUI 110 #define FIRE_PRIORITY_TICKER 200 -#define FIRE_PRIORITY_CHAT 400 #define FIRE_PRIORITY_OVERLAYS 500 #define FIRE_PRIORITY_INPUT 1000 // This must always always be the max highest priority. Player input must never be lost. diff --git a/code/__DEFINES/tgui.dm b/code/__DEFINES/tgui.dm new file mode 100644 index 00000000000..0ef159f11c2 --- /dev/null +++ b/code/__DEFINES/tgui.dm @@ -0,0 +1,8 @@ +// TGUI defines +#define TGUI_MODAL_INPUT_MAX_LENGTH 1024 +#define TGUI_MODAL_INPUT_MAX_LENGTH_NAME 64 // Names for generally anything don't go past 32, let alone 64. + +#define TGUI_MODAL_OPEN 1 +#define TGUI_MODAL_DELEGATE 2 +#define TGUI_MODAL_ANSWER 3 +#define TGUI_MODAL_CLOSE 4 diff --git a/code/__DEFINES/wires.dm b/code/__DEFINES/wires.dm new file mode 100644 index 00000000000..f7ea1c3cf38 --- /dev/null +++ b/code/__DEFINES/wires.dm @@ -0,0 +1,77 @@ +// Wire defines for all machines/items. + +// Miscellaneous +#define WIRE_DUD_PREFIX "__dud" + +// General +#define WIRE_IDSCAN "ID Scan" +#define WIRE_MAIN_POWER1 "Primary Power" +#define WIRE_MAIN_POWER2 "Secondary Power" +#define WIRE_AI_CONTROL "AI Control" +#define WIRE_ELECTRIFY "Electrification" +#define WIRE_SAFETY "Safety" + +// Vendors and smartfridges +#define WIRE_THROW_ITEM "Item Throw" +#define WIRE_CONTRABAND "Contraband" + +// Airlock +#define WIRE_DOOR_BOLTS "Door Bolts" +#define WIRE_BACKUP_POWER1 "Primary Backup Power" +#define WIRE_OPEN_DOOR "Door State" +#define WIRE_SPEED "Door Timing" +#define WIRE_BOLT_LIGHT "Bolt Lights" + +// Air alarm +#define WIRE_SYPHON "Siphon" +#define WIRE_AALARM "Atmospherics Alarm" + +// Camera +#define WIRE_FOCUS "Focus" + +// Mulebot +#define WIRE_MOB_AVOIDANCE "Mob Avoidance" +#define WIRE_LOADCHECK "Load Checking" +#define WIRE_MOTOR1 "Primary Motor" +#define WIRE_MOTOR2 "Secondary Motor" +#define WIRE_REMOTE_RX "Signal Receiver" +#define WIRE_REMOTE_TX "Signal Sender" +#define WIRE_BEACON_RX "Beacon Receiver" + +// Explosives, bombs +#define WIRE_EXPLODE "Explode" // Explodes if pulsed or cut while active, defuses a bomb that isn't active on cut. +#define WIRE_BOMB_UNBOLT "Unbolt" // Unbolts the bomb if cut, hint on pulsed. +#define WIRE_BOMB_DELAY "Delay" // Raises the timer on pulse, does nothing on cut. +#define WIRE_BOMB_PROCEED "Proceed" // Lowers the timer, explodes if cut while the bomb is active. +#define WIRE_BOMB_ACTIVATE "Activate" // Will start a bombs timer if pulsed, will hint if pulsed while already active, will stop a timer a bomb on cut. + +// Nuclear bomb +#define WIRE_BOMB_LIGHT "Bomb Light" +#define WIRE_BOMB_TIMING "Bomb Timing" +#define WIRE_BOMB_SAFETY "Bomb Safety" + +// Particle accelerator +#define WIRE_PARTICLE_POWER "Power Toggle" // Toggles whether the PA is on or not. +#define WIRE_PARTICLE_STRENGTH "Strength" // Determines the strength of the PA. +#define WIRE_PARTICLE_INTERFACE "Interface" // Determines the interface showing up. +#define WIRE_PARTICLE_POWER_LIMIT "Maximum Power" // Determines how strong the PA can be. + +// Autolathe +#define WIRE_AUTOLATHE_HACK "Hack" +#define WIRE_AUTOLATHE_DISABLE "Disable" + +// Radio +#define WIRE_RADIO_SIGNAL "Signal" +#define WIRE_RADIO_RECEIVER "Receiver" +#define WIRE_RADIO_TRANSMIT "Transmitter" + +// Cyborg +#define WIRE_BORG_LOCKED "Lockdown" +#define WIRE_BORG_CAMERA "Camera" +#define WIRE_BORG_LAWCHECK "Law Check" + +// Suit storage unit +#define WIRE_SSU_UV "UV wire" + +// Tesla coil +#define WIRE_TESLACOIL_ZAP "Zap" diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 34d36412229..8fb1c88feef 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -5,11 +5,11 @@ var/turf/T = get_turf(A) return T ? T.loc : null -/proc/get_area_name(N) //get area by its name - for(var/area/A in world) - if(A.name == N) - return A - return 0 +/proc/get_area_name(atom/X, format_text = FALSE) + var/area/A = isarea(X) ? X : get_area(X) + if(!A) + return null + return format_text ? format_text(A.name) : A.name /proc/get_location_name(atom/X, format_text = FALSE) var/area/A = isarea(X) ? X : get_area(X) @@ -31,6 +31,24 @@ areas |= T.loc return areas +/proc/get_open_turf_in_dir(atom/center, dir) + var/turf/T = get_ranged_target_turf(center, dir, 1) + if(T && !T.density) + return T + +/proc/get_adjacent_open_turfs(atom/center) + . = list(get_open_turf_in_dir(center, NORTH), + get_open_turf_in_dir(center, SOUTH), + get_open_turf_in_dir(center, EAST), + get_open_turf_in_dir(center, WEST)) + listclearnulls(.) + +/proc/get_adjacent_open_areas(atom/center) + . = list() + var/list/adjacent_turfs = get_adjacent_open_turfs(center) + for(var/I in adjacent_turfs) + . |= get_area(I) + // Like view but bypasses luminosity check /proc/hear(var/range, var/atom/source) @@ -425,66 +443,8 @@ if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE) . = TRUE -proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) - var/roletext = be_special_type ? get_roletext(be_special_type) : null - var/list/mob/dead/observer/candidates = list() - var/time_passed = world.time - if(!Question) - Question = "Would you like to be a special role?" - - for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list)) - if(!G.key || !G.client) - continue - if(be_special_type) - if(!(be_special_type in G.client.prefs.be_special)) - continue - if(antag_age_check) - if(!player_old_enough_antag(G.client, be_special_type)) - continue - if(roletext) - if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate")) - continue - if(config.use_exp_restrictions && min_hours) - if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60) - continue - if(check_antaghud && cannotPossess(G)) - continue - spawn(0) - G << 'sound/misc/notice2.ogg'//Alerting them to their consideration - if(flashwindow) - window_flash(G.client) - var/ans = alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round") - if(!G?.client) - return - switch(ans) - if("Yes") - to_chat(G, "Choice registered: Yes.") - if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed. - to_chat(G, "Sorry, you were too late for the consideration!") - G << 'sound/machines/buzz-sigh.ogg' - return - candidates += G - if("No") - to_chat(G, "Choice registered: No.") - return - if("Not This Round") - to_chat(G, "Choice registered: No.") - to_chat(G, "You will no longer receive notifications for the role '[roletext]' for the rest of the round.") - G.client.prefs.be_special -= be_special_type - return - else - return - sleep(poll_time) - - //Check all our candidates, to make sure they didn't log off during the 30 second wait period. - for(var/mob/dead/observer/G in candidates) - if(!G.key || !G.client) - candidates.Remove(G) - - return candidates - -/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) - var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud) +/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = FALSE, flashwindow = TRUE, check_antaghud = TRUE, source, role_cleanname) + var/list/willing_ghosts = SSghost_spawns.poll_candidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud, source, role_cleanname) var/list/selected_ghosts = list() if(!willing_ghosts.len) return selected_ghosts @@ -568,3 +528,24 @@ proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_tim if(length(vent.parent.other_atmosmch) <= min_network_size) continue . += vent + +/** + * Get a bounding box of a list of atoms. + * + * Arguments: + * - atoms - List of atoms. Can accept output of view() and range() procs. + * + * Returns: list(x1, y1, x2, y2) + */ +/proc/get_bbox_of_atoms(list/atoms) + var/list/list_x = list() + var/list/list_y = list() + for(var/_a in atoms) + var/atom/a = _a + list_x += a.x + list_y += a.y + return list( + min(list_x), + min(list_y), + max(list_x), + max(list_y)) diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 8ab919f9061..cbc691e3c2a 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -28,6 +28,9 @@ // Different tails __init_body_accessory(/datum/body_accessory/tail) + // Setup species:accessory relations + initialize_body_accessory_by_species() + for(var/path in (subtypesof(/datum/surgery))) GLOB.surgeries_list += new path() diff --git a/code/_DATASTRUCTURES/heap.dm b/code/__HELPERS/heap.dm similarity index 80% rename from code/_DATASTRUCTURES/heap.dm rename to code/__HELPERS/heap.dm index 7246eff8771..b878969571e 100644 --- a/code/_DATASTRUCTURES/heap.dm +++ b/code/__HELPERS/heap.dm @@ -1,28 +1,28 @@ ////////////////////// -//Heap object +//datum/heap object ////////////////////// -/Heap +/datum/heap var/list/L var/cmp -/Heap/New(compare) +/datum/heap/New(compare) L = new() cmp = compare -/Heap/proc/IsEmpty() +/datum/heap/proc/IsEmpty() return !L.len //Insert and place at its position a new node in the heap -/Heap/proc/Insert(atom/A) +/datum/heap/proc/Insert(atom/A) L.Add(A) Swim(L.len) //removes and returns the first element of the heap //(i.e the max or the min dependant on the comparison function) -/Heap/proc/Pop() +/datum/heap/proc/Pop() if(!L.len) return 0 . = L[1] @@ -33,7 +33,7 @@ Sink(1) //Get a node up to its right position in the heap -/Heap/proc/Swim(var/index) +/datum/heap/proc/Swim(var/index) var/parent = round(index * 0.5) while(parent > 0 && (call(cmp)(L[index],L[parent]) > 0)) @@ -42,7 +42,7 @@ parent = round(index * 0.5) //Get a node down to its right position in the heap -/Heap/proc/Sink(var/index) +/datum/heap/proc/Sink(var/index) var/g_child = GetGreaterChild(index) while(g_child > 0 && (call(cmp)(L[index],L[g_child]) < 0)) @@ -52,7 +52,7 @@ //Returns the greater (relative to the comparison proc) of a node children //or 0 if there's no child -/Heap/proc/GetGreaterChild(var/index) +/datum/heap/proc/GetGreaterChild(var/index) if(index * 2 > L.len) return 0 @@ -65,11 +65,11 @@ return index * 2 //Replaces a given node so it verify the heap condition -/Heap/proc/ReSort(atom/A) +/datum/heap/proc/ReSort(atom/A) var/index = L.Find(A) Swim(index) Sink(index) -/Heap/proc/List() +/datum/heap/proc/List() . = L.Copy() diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index f9b9850bd2d..075f3370965 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -672,9 +672,6 @@ proc/dd_sortedObjectList(list/incoming) /obj/machinery/camera/dd_SortValue() return "[c_tag]" -/datum/alarm/dd_SortValue() - return "[sanitize(last_name)]" - //Picks from the list, with some safeties, and returns the "default" arg if it fails #define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default) @@ -693,6 +690,9 @@ proc/dd_sortedObjectList(list/incoming) // Lazying Episode 3 #define LAZYSET(L, K, V) LAZYINITLIST(L); L[K] = V; +/// Returns whether a numerical index is within a given list's bounds. Faster than isnull(LAZYACCESS(L, I)). +#define ISINDEXSAFE(L, I) (I >= 1 && I <= length(L)) + //same, but returns nothing and acts on list in place /proc/shuffle_inplace(list/L) if(!L) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index f7c66c3378b..410f66b3056 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -1,4 +1,4 @@ -proc/GetOppositeDir(var/dir) +/proc/GetOppositeDir(var/dir) switch(dir) if(NORTH) return SOUTH if(SOUTH) return NORTH @@ -10,7 +10,7 @@ proc/GetOppositeDir(var/dir) if(SOUTHEAST) return NORTHWEST return 0 -proc/random_underwear(gender, species = "Human") +/proc/random_underwear(gender, species = "Human") var/list/pick_list = list() switch(gender) if(MALE) pick_list = GLOB.underwear_m @@ -18,7 +18,7 @@ proc/random_underwear(gender, species = "Human") else pick_list = GLOB.underwear_list return pick_species_allowed_underwear(pick_list, species) -proc/random_undershirt(gender, species = "Human") +/proc/random_undershirt(gender, species = "Human") var/list/pick_list = list() switch(gender) if(MALE) pick_list = GLOB.undershirt_m @@ -26,7 +26,7 @@ proc/random_undershirt(gender, species = "Human") else pick_list = GLOB.undershirt_list return pick_species_allowed_underwear(pick_list, species) -proc/random_socks(gender, species = "Human") +/proc/random_socks(gender, species = "Human") var/list/pick_list = list() switch(gender) if(MALE) pick_list = GLOB.socks_m @@ -34,7 +34,7 @@ proc/random_socks(gender, species = "Human") else pick_list = GLOB.socks_list return pick_species_allowed_underwear(pick_list, species) -proc/pick_species_allowed_underwear(list/all_picks, species) +/proc/pick_species_allowed_underwear(list/all_picks, species) var/list/valid_picks = list() for(var/test in all_picks) var/datum/sprite_accessory/S = all_picks[test] @@ -46,7 +46,7 @@ proc/pick_species_allowed_underwear(list/all_picks, species) return pick(valid_picks) -proc/random_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) +/proc/random_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) var/h_style = "Bald" var/list/valid_hairstyles = list() for(var/hairstyle in GLOB.hair_styles_public_list) @@ -75,7 +75,7 @@ proc/random_hair_style(var/gender, species = "Human", var/datum/robolimb/robohea return h_style -proc/random_facial_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) +/proc/random_facial_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) var/f_style = "Shaved" var/list/valid_facial_hairstyles = list() for(var/facialhairstyle in GLOB.facial_hair_styles_list) @@ -104,7 +104,7 @@ proc/random_facial_hair_style(var/gender, species = "Human", var/datum/robolimb/ return f_style -proc/random_head_accessory(species = "Human") +/proc/random_head_accessory(species = "Human") var/ha_style = "None" var/list/valid_head_accessories = list() for(var/head_accessory in GLOB.head_accessory_styles_list) @@ -119,7 +119,7 @@ proc/random_head_accessory(species = "Human") return ha_style -proc/random_marking_style(var/location = "body", species = "Human", var/datum/robolimb/robohead, var/body_accessory, var/alt_head) +/proc/random_marking_style(var/location = "body", species = "Human", var/datum/robolimb/robohead, var/body_accessory, var/alt_head) var/m_style = "None" var/list/valid_markings = list() for(var/marking in GLOB.marking_styles_list) @@ -158,7 +158,7 @@ proc/random_marking_style(var/location = "body", species = "Human", var/datum/ro return m_style -proc/random_body_accessory(species = "Vulpkanin") +/proc/random_body_accessory(species = "Vulpkanin") var/body_accessory = null var/list/valid_body_accessories = list() for(var/B in GLOB.body_accessory_by_name) @@ -174,7 +174,7 @@ proc/random_body_accessory(species = "Vulpkanin") return body_accessory -proc/random_name(gender, species = "Human") +/proc/random_name(gender, species = "Human") var/datum/species/current_species if(species) @@ -188,7 +188,7 @@ proc/random_name(gender, species = "Human") else return current_species.get_random_name(gender) -proc/random_skin_tone(species = "Human") +/proc/random_skin_tone(species = "Human") if(species == "Human" || species == "Drask") switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino")) if("caucasian") . = -10 @@ -202,7 +202,7 @@ proc/random_skin_tone(species = "Human") . = rand(1, 6) return . -proc/skintone2racedescription(tone, species = "Human") +/proc/skintone2racedescription(tone, species = "Human") if(species == "Human") switch(tone) if(30 to INFINITY) return "albino" @@ -225,7 +225,7 @@ proc/skintone2racedescription(tone, species = "Human") else return "unknown" -proc/age2agedescription(age) +/proc/age2agedescription(age) switch(age) if(0 to 1) return "infant" if(1 to 3) return "toddler" @@ -238,36 +238,37 @@ proc/age2agedescription(age) if(70 to INFINITY) return "elderly" else return "unknown" -/proc/set_criminal_status(mob/living/user, datum/data/record/target_records , criminal_status, comment, user_rank, list/authcard_access = list()) +/proc/set_criminal_status(mob/living/user, datum/data/record/target_records , criminal_status, comment, user_rank, list/authcard_access = list(), user_name) var/status = criminal_status var/their_name = target_records.fields["name"] var/their_rank = target_records.fields["rank"] switch(criminal_status) - if("arrest") + if("arrest", SEC_RECORD_STATUS_ARREST) status = SEC_RECORD_STATUS_ARREST - if("none") + if("none", SEC_RECORD_STATUS_NONE) status = SEC_RECORD_STATUS_NONE - if("execute") + if("execute", SEC_RECORD_STATUS_EXECUTE) if((ACCESS_MAGISTRATE in authcard_access) || (ACCESS_ARMORY in authcard_access)) status = SEC_RECORD_STATUS_EXECUTE message_admins("[ADMIN_FULLMONTY(usr)] authorized EXECUTION for [their_rank] [their_name], with comment: [comment]") else return 0 - if("search") + if("search", SEC_RECORD_STATUS_SEARCH) status = SEC_RECORD_STATUS_SEARCH - if("monitor") + if("monitor", SEC_RECORD_STATUS_MONITOR) status = SEC_RECORD_STATUS_MONITOR - if ("demote") + if("demote", SEC_RECORD_STATUS_DEMOTE) + message_admins("[ADMIN_FULLMONTY(usr)] set criminal status to DEMOTE for [their_rank] [their_name], with comment: [comment]") status = SEC_RECORD_STATUS_DEMOTE - if("incarcerated") + if("incarcerated", SEC_RECORD_STATUS_INCARCERATED) status = SEC_RECORD_STATUS_INCARCERATED - if("parolled") + if("parolled", SEC_RECORD_STATUS_PAROLLED) status = SEC_RECORD_STATUS_PAROLLED - if("released") + if("released", SEC_RECORD_STATUS_RELEASED) status = SEC_RECORD_STATUS_RELEASED target_records.fields["criminal"] = status log_admin("[key_name_admin(user)] set secstatus of [their_rank] [their_name] to [status], comment: [comment]") - target_records.fields["comments"] += "Set to [status] by [user.name] ([user_rank]) on [GLOB.current_date_string] [station_time_timestamp()], comment: [comment]" + target_records.fields["comments"] += "Set to [status] by [user_name || user.name] ([user_rank]) on [GLOB.current_date_string] [station_time_timestamp()], comment: [comment]" update_all_mob_security_hud() return 1 @@ -289,11 +290,14 @@ This is always put in the attack log. var/user_str = key_name_log(user) + COORD(user) var/target_str + var/target_info if(isatom(target)) var/atom/AT = target target_str = key_name_log(AT) + COORD(AT) + target_info = key_name_admin(target) else target_str = target + target_info = target var/mob/MU = user var/mob/MT = target if(istype(MU)) @@ -317,10 +321,13 @@ This is always put in the attack log. var/area/A = get_area(MT) if(A && A.hide_attacklogs) loglevel = ATKLOG_ALMOSTALL + else + loglevel = ATKLOG_ALL // Hitting an object. Not a mob if(isLivingSSD(target)) // Attacks on SSDs are shown to admins with any log level except ATKLOG_NONE. Overrides custom level loglevel = ATKLOG_FEW - msg_admin_attack("[key_name_admin(user)] vs [key_name_admin(target)]: [what_done]", loglevel) + + msg_admin_attack("[key_name_admin(user)] vs [target_info]: [what_done]", loglevel) /proc/do_mob(mob/user, mob/target, time = 30, uninterruptible = 0, progress = 1, list/extra_checks = list()) if(!user || !target) @@ -535,7 +542,7 @@ GLOBAL_LIST_INIT(do_after_once_tracker, list()) to_chat(user, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];") to_chat(user, "Location = [location_description];") to_chat(user, "[special_role_description]") - to_chat(user, "(PM) ([ADMIN_PP(M,"PP")]) ([ADMIN_VV(M,"VV")]) ([ADMIN_TP(M,"TP")]) ([ADMIN_SM(M,"SM")]) ([ADMIN_FLW(M,"FLW")])") + to_chat(user, "(PM) ([ADMIN_PP(M,"PP")]) ([ADMIN_VV(M,"VV")]) ([ADMIN_TP(M,"TP")]) ([ADMIN_SM(M,"SM")]) ([ADMIN_FLW(M,"FLW")])") // Gets the first mob contained in an atom, and warns the user if there's not exactly one /proc/get_mob_in_atom_with_warning(atom/A, mob/user = usr) @@ -579,7 +586,8 @@ GLOBAL_LIST_INIT(do_after_once_tracker, list()) LogMouseMacro(".mouse", params) /proc/update_all_mob_security_hud() - for(var/mob/living/carbon/human/H in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing H.sec_hud_set_security_status() /proc/getviewsize(view) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index ecbebf813df..7019ee8b2c4 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -196,7 +196,7 @@ //checks text for html tags //if tag is not in whitelist (var/list/paper_tag_whitelist in global.dm) //relpaces < with < -proc/checkhtml(var/t) +/proc/checkhtml(var/t) t = sanitize_simple(t, list("&#"=".")) var/p = findtext(t,"<",1) while(p) //going through all the tags @@ -533,7 +533,7 @@ proc/checkhtml(var/t) text = replacetext(text, "\[/grid\]", "") text = replacetext(text, "\[row\]", "") text = replacetext(text, "\[cell\]", "") - text = replacetext(text, "\[logo\]", "") + text = replacetext(text, "\[logo\]", "​") text = replacetext(text, "\[time\]", "[station_time_timestamp()]") // TO DO if(!no_font) if(P) @@ -615,5 +615,3 @@ proc/checkhtml(var/t) text = replacetext(text, "", "\[cell\]") text = replacetext(text, "", "\[logo\]") return text - -#define string2charlist(string) (splittext(string, regex("(\\x0A|.)")) - splittext(string, "")) diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm index 83347c20d3b..a1da66b1373 100644 --- a/code/__HELPERS/time.dm +++ b/code/__HELPERS/time.dm @@ -60,7 +60,7 @@ return time2text(station_time(time, TRUE), format) /* Returns 1 if it is the selected month and day */ -proc/isDay(var/month, var/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 @@ -95,7 +95,7 @@ proc/isDay(var/month, var/day) /proc/seconds_to_time(var/seconds as num) var/numSeconds = seconds % 60 var/numMinutes = (seconds - numSeconds) / 60 - return "[numMinutes] [numMinutes > 1 ? "minutes" : "minute"] and [numSeconds] seconds." + return "[numMinutes] [numMinutes > 1 ? "minutes" : "minute"] and [numSeconds] seconds" //Take a value in seconds and makes it display like a clock /proc/seconds_to_clock(var/seconds as num) diff --git a/code/__HELPERS/unique_ids.dm b/code/__HELPERS/unique_ids.dm index b66b1a11b90..188918429e9 100644 --- a/code/__HELPERS/unique_ids.dm +++ b/code/__HELPERS/unique_ids.dm @@ -14,16 +14,33 @@ // var/myUID = mydatum.UID() // var/datum/D = locateUID(myUID) +/// The next UID to be used (Increments by 1 for each UID) GLOBAL_VAR_INIT(next_unique_datum_id, 1) +/// Log of all UIDs created in the round. Assoc list with type as key and amount as value +GLOBAL_LIST_EMPTY(uid_log) +/** + * Gets or creates the UID of a datum + * + * BYOND refs are recycled, so this system prevents that. If a datum does not have a UID when this proc is ran, one will be created + * Returns the UID of the datum + */ /datum/proc/UID() if(!unique_datum_id) var/tag_backup = tag tag = null // Grab the raw ref, not the tag - unique_datum_id = "\ref[src]_[GLOB.next_unique_datum_id++]" + // num2text can output 8 significant figures max. If we go above 10 million UIDs in a round, shit breaks + unique_datum_id = "\ref[src]_[num2text(GLOB.next_unique_datum_id++, 8)]" tag = tag_backup + GLOB.uid_log[type]++ return unique_datum_id +/** + * Locates a datum based off of the UID + * + * Replacement for locate() which takes a UID instead of a ref + * Returns the datum, if found + */ /proc/locateUID(uid) if(!istext(uid)) return null @@ -38,3 +55,24 @@ GLOBAL_VAR_INIT(next_unique_datum_id, 1) if(D && D.unique_datum_id == uid) return D return null + +/** + * Opens a lof of UIDs + * + * In-round ability to view what has created a UID, and how many times a UID for that path has been declared + */ +/client/proc/uid_log() + set name = "View UID Log" + set category = "Debug" + set desc = "Shows the log of created UIDs this round" + + if(!check_rights(R_DEBUG)) + return + + var/list/sorted = sortTim(GLOB.uid_log, cmp=/proc/cmp_numeric_dsc, associative = TRUE) + var/list/text = list("

UID Log

", "

Current UID: [GLOB.next_unique_datum_id]

", "
    ") + for(var/key in sorted) + text += "
  • [key] - [sorted[key]]
  • " + + text += "
" + usr << browse(text.Join(), "window=uidlog") diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 7e57a394988..46559eb6b5c 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -20,31 +20,6 @@ return 0 -//Inverts the colour of an HTML string -/proc/invertHTML(HTMLstring) - - if(!( istext(HTMLstring) )) - CRASH("Given non-text argument!") - else - if(length(HTMLstring) != 7) - CRASH("Given non-HTML argument!") - 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) - //Returns the middle-most value /proc/dd_range(var/low, var/high, var/num) return max(low,min(high,num)) @@ -449,6 +424,18 @@ Turf and target are seperate in case you want to teleport some distance from a t if(M.ckey == key) return M +/proc/get_client_by_ckey(ckey) + if(cmptext(copytext(ckey, 1, 2),"@")) + ckey = findStealthKey(ckey) + return GLOB.directory[ckey] + + +/proc/findStealthKey(txt) + if(txt) + for(var/P in GLOB.stealthminID) + if(GLOB.stealthminID[P] == txt) + return P + // Returns the atom sitting on the turf. // For example, using this on a disk, which is in a bag, on a mob, will return the mob because it's on the turf. /proc/get_atom_on_turf(var/atom/movable/M) @@ -529,7 +516,7 @@ Returns 1 if the chain up to the area contains the given typepath return max(min(middle, high), low) //returns random gauss number -proc/GaussRand(var/sigma) +/proc/GaussRand(var/sigma) var/x,y,rsq do x=2*rand()-1 @@ -539,7 +526,7 @@ proc/GaussRand(var/sigma) return sigma*y*sqrt(-2*log(rsq)/rsq) //returns random gauss number, rounded to 'roundto' -proc/GaussRandRound(var/sigma,var/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' @@ -583,7 +570,7 @@ proc/GaussRandRound(var/sigma,var/roundto) return 1 -proc/is_blocked_turf(turf/T, exclude_mobs) +/proc/is_blocked_turf(turf/T, exclude_mobs) if(T.density) return 1 for(var/i in T) @@ -984,16 +971,16 @@ proc/is_blocked_turf(turf/T, exclude_mobs) -proc/get_cardinal_dir(atom/A, atom/B) +/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) +/proc/anyprob(value) return (rand(1,value)==value) -proc/view_or_range(distance = world.view , center = usr , type) +/proc/view_or_range(distance = world.view , center = usr , type) switch(type) if("view") . = view(distance,center) @@ -1001,7 +988,7 @@ proc/view_or_range(distance = world.view , center = usr , type) . = range(distance,center) return -proc/oview_or_orange(distance = world.view , center = usr , type) +/proc/oview_or_orange(distance = world.view , center = usr , type) switch(type) if("view") . = oview(distance,center) @@ -1009,7 +996,7 @@ proc/oview_or_orange(distance = world.view , center = usr , type) . = orange(distance,center) return -proc/get_mob_with_client_list() +/proc/get_mob_with_client_list() var/list/mobs = list() for(var/mob/M in GLOB.mob_list) if(M.client) @@ -1233,10 +1220,10 @@ GLOBAL_LIST_INIT(wall_items, typecacheof(list(/obj/machinery/power/apc, /obj/mac return 0 -proc/get_angle(atom/a, atom/b) +/proc/get_angle(atom/a, atom/b) return atan2(b.y - a.y, b.x - a.x) -proc/atan2(x, y) +/proc/atan2(x, y) if(!x && !y) return 0 return y >= 0 ? arccos(x / sqrt(x * x + y * y)) : -arccos(x / sqrt(x * x + y * y)) @@ -1329,7 +1316,7 @@ Standard way to write links -Sayu return FACING_INIT_FACING_TARGET_TARGET_FACING_PERPENDICULAR -atom/proc/GetTypeInAllContents(typepath) +/atom/proc/GetTypeInAllContents(typepath) var/list/processing_list = list(src) var/list/processed = list() @@ -2017,5 +2004,31 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) set waitfor = FALSE return call(source, proctype)(arglist(arguments)) +/proc/IsFrozen(atom/A) + if(A in GLOB.frozen_atom_list) + return TRUE + return FALSE + +/** + * Proc which gets all adjacent turfs to `src`, including the turf that `src` is on. + * + * This is similar to doing `for(var/turf/T in range(1, src))`. However it is slightly more performant. + * Additionally, the above proc becomes more costly the more atoms there are nearby. This proc does not care about that. + */ +/atom/proc/get_all_adjacent_turfs() + var/turf/src_turf = get_turf(src) + var/list/_list = list( + src_turf, + get_step(src_turf, NORTH), + get_step(src_turf, NORTHEAST), + get_step(src_turf, NORTHWEST), + get_step(src_turf, SOUTH), + get_step(src_turf, SOUTHEAST), + get_step(src_turf, SOUTHWEST), + get_step(src_turf, EAST), + get_step(src_turf, WEST) + ) + return _list + /// Waits at a line of code until X is true #define UNTIL(X) while(!(X)) stoplag() diff --git a/code/_globalvars/lists/misc.dm b/code/_globalvars/lists/misc.dm index 53040777467..3a0c58bffb3 100644 --- a/code/_globalvars/lists/misc.dm +++ b/code/_globalvars/lists/misc.dm @@ -40,6 +40,7 @@ GLOBAL_LIST_INIT(round_end_sounds, list( 'sound/AI/newroundsexy.ogg' = 2.3 SECONDS, 'sound/misc/apcdestroyed.ogg' = 3 SECONDS, 'sound/misc/bangindonk.ogg' = 1.6 SECONDS, + 'sound/misc/berightback.ogg' = 2.9 SECONDS, 'sound/goonstation/misc/newround1.ogg' = 6.9 SECONDS, 'sound/goonstation/misc/newround2.ogg' = 14.8 SECONDS )) // Maps available round end sounds to their duration diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 21af95cab48..ab3bfa85451 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -47,7 +47,10 @@ GLOBAL_LIST_EMPTY(ladders) GLOBAL_LIST_INIT(active_diseases, list()) //List of Active disease in all mobs; purely for quick referencing. GLOBAL_LIST_EMPTY(mob_spawners) // All mob_spawn objects - +GLOBAL_LIST_EMPTY(alert_consoles) // Station alert consoles, /obj/machinery/computer/station_alert GLOBAL_LIST_EMPTY(explosive_walls) GLOBAL_LIST_EMPTY(engine_beacon_list) + +/// List of wire colors for each object type of that round. One for airlocks, one for vendors, etc. +GLOBAL_LIST_EMPTY(wire_color_directory) // This is an associative list with the `holder_type` as the key, and a list of colors as the value. diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index f5f71e65c57..50e70bc62a4 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -34,4 +34,6 @@ GLOBAL_PROTECT(IClog) GLOBAL_LIST_EMPTY(OOClog) GLOBAL_PROTECT(OOClog) +GLOBAL_DATUM_INIT(logging, /datum/logging, new /datum/logging()) + GLOBAL_LIST_INIT(investigate_log_subjects, list("notes", "watchlist", "hrefs")) diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index 283d92a954f..9c6fa81c1a2 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -10,12 +10,12 @@ GLOBAL_DATUM_INIT(command_announcer, /obj/item/radio/intercom/command, create_co // Load order issues means this can't be new'd until other code runs // This is probably not the way I should be doing this, but I don't know how to do it right! -proc/create_global_announcer() +/proc/create_global_announcer() spawn(0) GLOB.global_announcer = new(null) return -proc/create_command_announcer() +/proc/create_command_announcer() spawn(0) GLOB.command_announcer = new(null) return @@ -92,6 +92,7 @@ GLOBAL_VAR(map_name) // Self explanatory GLOBAL_DATUM_INIT(data_core, /datum/datacore, new) // Station datacore, manifest, etc GLOBAL_VAR_INIT(panic_bunker_enabled, FALSE) // Is the panic bunker enabled +GLOBAL_VAR_INIT(pending_server_update, FALSE) //Database connections //A connection is established on world creation. Ideally, the connection dies when the server restarts (After feedback logging.). diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index fbb04d4c021..03d34395479 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -9,7 +9,7 @@ Note that AI have no need for the adjacency proc, and so this proc is a lot cleaner. */ -/mob/living/silicon/ai/DblClickOn(var/atom/A, params) +/mob/living/silicon/ai/DblClickOn(atom/A, params) if(client.click_intercept) // Not doing a click intercept here, because otherwise we double-tap with the `ClickOn` proc. // But we return here since we don't want to do regular dblclick handling @@ -23,7 +23,7 @@ A.move_camera_by_click() -/mob/living/silicon/ai/ClickOn(var/atom/A, params) +/mob/living/silicon/ai/ClickOn(atom/A, params) if(client.click_intercept) client.click_intercept.InterceptClickOn(src, params, A) return @@ -123,7 +123,7 @@ /mob/living/silicon/ai/RangedAttack(atom/A, params) A.attack_ai(src) -/atom/proc/attack_ai(mob/user as mob) +/atom/proc/attack_ai(mob/user) return /* @@ -132,25 +132,23 @@ for AI shift, ctrl, and alt clicking. */ -/mob/living/silicon/ai/CtrlShiftClickOn(var/atom/A) +/mob/living/silicon/ai/CtrlShiftClickOn(atom/A) A.AICtrlShiftClick(src) -/mob/living/silicon/ai/AltShiftClickOn(var/atom/A) +/mob/living/silicon/ai/AltShiftClickOn(atom/A) A.AIAltShiftClick(src) -/mob/living/silicon/ai/ShiftClickOn(var/atom/A) +/mob/living/silicon/ai/ShiftClickOn(atom/A) A.AIShiftClick(src) -/mob/living/silicon/ai/CtrlClickOn(var/atom/A) +/mob/living/silicon/ai/CtrlClickOn(atom/A) A.AICtrlClick(src) -/mob/living/silicon/ai/AltClickOn(var/atom/A) +/mob/living/silicon/ai/AltClickOn(atom/A) A.AIAltClick(src) -/mob/living/silicon/ai/MiddleClickOn(var/atom/A) +/mob/living/silicon/ai/MiddleClickOn(atom/A) A.AIMiddleClick(src) -/* - The following criminally helpful code is just the previous code cleaned up; - I have no idea why it was in atoms.dm instead of respective files. -*/ -/atom/proc/AICtrlShiftClick(var/mob/user) // Examines +// DEFAULT PROCS TO OVERRIDE + +/atom/proc/AICtrlShiftClick(mob/user) // Examines if(user.client) user.examinate(src) return @@ -158,74 +156,78 @@ /atom/proc/AIAltShiftClick() return -/obj/machinery/door/airlock/AIAltShiftClick() // Sets/Unsets Emergency Access Override - if(density) - Topic(src, list("src" = UID(), "command"="emergency", "activate" = "1"), 1) // 1 meaning no window (consistency!) - else - Topic(src, list("src" = UID(), "command"="emergency", "activate" = "0"), 1) - return - -/atom/proc/AIShiftClick(var/mob/user) +/atom/proc/AIShiftClick(mob/living/user) // borgs use this too if(user.client) user.examinate(src) return -/obj/machinery/door/airlock/AIShiftClick() // Opens and closes doors! - if(density) - Topic(src, list("src" = UID(), "command"="open", "activate" = "1"), 1) // 1 meaning no window (consistency!) - else - Topic(src, list("src" = UID(), "command"="open", "activate" = "0"), 1) +/atom/proc/AICtrlClick(mob/living/silicon/user) return -/atom/proc/AICtrlClick(var/mob/living/silicon/ai/user) - return - -/obj/machinery/door/airlock/AICtrlClick() // Bolts doors - if(locked) - Topic(src, list("src" = UID(), "command"="bolts", "activate" = "0"), 1)// 1 meaning no window (consistency!) - else - Topic(src, list("src" = UID(), "command"="bolts", "activate" = "1"), 1) - -/obj/machinery/power/apc/AICtrlClick() // turns off/on APCs. - Topic("breaker=1", list("breaker"="1"), 0) // 0 meaning no window (consistency! wait...) - -/obj/machinery/turretid/AICtrlClick() //turns off/on Turrets - Topic(src, list("src" = UID(), "command"="enable", "value"="[!enabled]"), 1) // 1 meaning no window (consistency!) - -/atom/proc/AIAltClick(var/atom/A) +/atom/proc/AIAltClick(atom/A) AltClick(A) -/obj/machinery/door/airlock/AIAltClick() // Electrifies doors. - if(!electrified_until) - // permanent shock - Topic(src, list("src" = UID(), "command"="electrify_permanently", "activate" = "1"), 1) // 1 meaning no window (consistency!) - else - // disable/6 is not in Topic; disable/5 disables both temporary and permanent shock - Topic(src, list("src" = UID(), "command"="electrify_permanently", "activate" = "0"), 1) +/atom/proc/AIMiddleClick(mob/living/user) return +/mob/living/silicon/ai/TurfAdjacent(turf/T) + return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T)) + + +// APC + +/obj/machinery/power/apc/AICtrlClick(mob/living/user) // turns off/on APCs. + toggle_breaker(user) + + +// TURRETCONTROL + +/obj/machinery/turretid/AICtrlClick(mob/living/silicon/user) //turns off/on Turrets + enabled = !enabled + updateTurrets() + /obj/machinery/turretid/AIAltClick() //toggles lethal on turrets - Topic(src, list("src" = UID(), "command"="lethal", "value"="[!lethal]"), 1) // 1 meaning no window (consistency!) + if(lethal_is_configurable) + lethal = !lethal + updateTurrets() -/atom/proc/AIMiddleClick() - return +// AIRLOCKS -/obj/machinery/door/airlock/AIMiddleClick() // Toggles door bolt lights. - if(!src.lights) - Topic(src, list("src" = UID(), "command"="lights", "activate" = "1"), 1) // 1 meaning no window (consistency!) +/obj/machinery/door/airlock/AIAltShiftClick(mob/user) // Sets/Unsets Emergency Access Override + if(!ai_control_check(user)) + return + toggle_emergency_status(user) + +/obj/machinery/door/airlock/AIShiftClick(mob/user) // Opens and closes doors! + if(!ai_control_check(user)) + return + open_close(user) + +/obj/machinery/door/airlock/AICtrlClick(mob/living/silicon/user) // Bolts doors + if(!ai_control_check(user)) + return + toggle_bolt(user) + +/obj/machinery/door/airlock/AIAltClick(mob/living/silicon/user) // Electrifies doors. + if(!ai_control_check(user)) + return + if(wires.is_cut(WIRE_ELECTRIFY)) + to_chat(user, "The electrification wire is cut - Cannot electrify the door.") + if(isElectrified()) + electrify(0, user, TRUE) // un-shock else - Topic(src, list("src" = UID(), "command"="lights", "activate" = "0"), 1) - return + electrify(-1, user, TRUE) // permanent shock -/obj/machinery/ai_slipper/AICtrlClick() //Turns liquid dispenser on or off + +/obj/machinery/door/airlock/AIMiddleClick(mob/living/user) // Toggles door bolt lights. + if(!ai_control_check(user)) + return + toggle_light(user) + +// AI-CONTROLLED SLIP GENERATOR IN AI CORE + +/obj/machinery/ai_slipper/AICtrlClick(mob/living/silicon/ai/user) //Turns liquid dispenser on or off ToggleOn() /obj/machinery/ai_slipper/AIAltClick() //Dispenses liquid if on Activate() - -// -// Override AdjacentQuick for AltClicking -// - -/mob/living/silicon/ai/TurfAdjacent(var/turf/T) - return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T)) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index d840f64bb4a..36b6d647b7d 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -73,7 +73,9 @@ var/dragged = modifiers["drag"] if(dragged && !modifiers[dragged]) return - + if(IsFrozen(A) && !is_admin(usr)) + to_chat(usr, "Interacting with admin-frozen players is not permitted.") + return if(modifiers["middle"] && modifiers["shift"] && modifiers["ctrl"]) MiddleShiftControlClickOn(A) return diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index f9e5cbf3e5a..fa29e857ab7 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -6,7 +6,7 @@ adjacency code. */ -/mob/living/silicon/robot/ClickOn(var/atom/A, var/params) +/mob/living/silicon/robot/ClickOn(atom/A, params) if(client.click_intercept) client.click_intercept.InterceptClickOn(src, params, A) return @@ -15,6 +15,8 @@ return changeNext_click(1) + if(is_ventcrawling(src)) // To stop drones interacting with anything while ventcrawling + return var/list/modifiers = params2list(params) if(modifiers["shift"] && modifiers["ctrl"]) @@ -98,12 +100,12 @@ return //Ctrl+Middle click cycles through modules -/mob/living/silicon/robot/proc/CtrlMiddleClickOn(var/atom/A) +/mob/living/silicon/robot/proc/CtrlMiddleClickOn(atom/A) cycle_modules() return //Middle click points -/mob/living/silicon/robot/MiddleClickOn(var/atom/A) +/mob/living/silicon/robot/MiddleClickOn(atom/A) if(istype(src, /mob/living/silicon/robot/drone)) // Drones cannot point. return @@ -112,18 +114,31 @@ //Give cyborgs hotkey clicks without breaking existing uses of hotkey clicks // for non-doors/apcs -/mob/living/silicon/robot/CtrlShiftClickOn(var/atom/A) - A.BorgCtrlShiftClick(src) -/mob/living/silicon/robot/AltShiftClickOn(var/atom/A) - A.BorgAltShiftClick(src) -/mob/living/silicon/robot/ShiftClickOn(var/atom/A) +/mob/living/silicon/robot/ShiftClickOn(atom/A) A.BorgShiftClick(src) -/mob/living/silicon/robot/CtrlClickOn(var/atom/A) +/mob/living/silicon/robot/CtrlClickOn(atom/A) A.BorgCtrlClick(src) -/mob/living/silicon/robot/AltClickOn(var/atom/A) +/mob/living/silicon/robot/AltClickOn(atom/A) A.BorgAltClick(src) +/mob/living/silicon/robot/CtrlShiftClickOn(atom/A) + A.BorgCtrlShiftClick(src) +/mob/living/silicon/robot/AltShiftClickOn(atom/A) + A.BorgAltShiftClick(src) -/atom/proc/BorgCtrlShiftClick(var/mob/user) // Examines + +/atom/proc/BorgShiftClick(mob/user) + if(user.client && user.client.eye == user) + user.examinate(src) + return + +/atom/proc/BorgCtrlClick(mob/living/silicon/robot/user) //forward to human click if not overriden + CtrlClick(user) + +/atom/proc/BorgAltClick(mob/living/silicon/robot/user) + AltClick(user) + return + +/atom/proc/BorgCtrlShiftClick(mob/user) // Examines if(user.client && user.client.eye == user) user.examinate(src) return @@ -131,45 +146,45 @@ /atom/proc/BorgAltShiftClick() return -/obj/machinery/door/airlock/BorgAltShiftClick() // Enables emergency override on doors! Forwards to AI code. - AIAltShiftClick() -/atom/proc/BorgShiftClick(var/mob/user) - if(user.client && user.client.eye == user) - user.examinate(src) - return +// AIRLOCKS -/obj/machinery/door/airlock/BorgShiftClick() // Opens and closes doors! Forwards to AI code. - AIShiftClick() +/obj/machinery/door/airlock/BorgShiftClick(mob/living/silicon/robot/user) // Opens and closes doors! Forwards to AI code. + AIShiftClick(user) -/atom/proc/BorgCtrlClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden - CtrlClick(user) +/obj/machinery/door/airlock/BorgCtrlClick(mob/living/silicon/robot/user) // Bolts doors. Forwards to AI code. + AICtrlClick(user) -/obj/machinery/door/airlock/BorgCtrlClick() // Bolts doors. Forwards to AI code. - AICtrlClick() +/obj/machinery/door/airlock/BorgAltClick(mob/living/silicon/robot/user) // Eletrifies doors. Forwards to AI code. + AIAltClick(user) -/obj/machinery/power/apc/BorgCtrlClick() // turns off/on APCs. Forwards to AI code. - AICtrlClick() +/obj/machinery/door/airlock/BorgAltShiftClick(mob/living/silicon/robot/user) // Enables emergency override on doors! Forwards to AI code. + AIAltShiftClick(user) -/obj/machinery/turretid/BorgCtrlClick() //turret control on/off. Forwards to AI code. - AICtrlClick() -/atom/proc/BorgAltClick(var/mob/living/silicon/robot/user) - AltClick(user) - return +// APC -/obj/machinery/door/airlock/BorgAltClick() // Eletrifies doors. Forwards to AI code. - AIAltClick() +/obj/machinery/power/apc/BorgCtrlClick(mob/living/silicon/robot/user) // turns off/on APCs. Forwards to AI code. + AICtrlClick(user) -/obj/machinery/turretid/BorgAltClick() //turret lethal on/off. Forwards to AI code. - AIAltClick() -/obj/machinery/ai_slipper/BorgCtrlClick() //Turns liquid dispenser on or off +// AI SLIPPER + +/obj/machinery/ai_slipper/BorgCtrlClick(mob/living/silicon/robot/user) //Turns liquid dispenser on or off ToggleOn() -/obj/machinery/ai_slipper/BorgAltClick() //Dispenses liquid if on +/obj/machinery/ai_slipper/BorgAltClick(mob/living/silicon/robot/user) //Dispenses liquid if on Activate() + +// TURRETCONTROL + +/obj/machinery/turretid/BorgCtrlClick(mob/living/silicon/robot/user) //turret control on/off. Forwards to AI code. + AICtrlClick(user) + +/obj/machinery/turretid/BorgAltClick(mob/living/silicon/robot/user) //turret lethal on/off. Forwards to AI code. + AIAltClick(user) + /* As with AI, these are not used in click code, because the code for robots is specific, not generic. @@ -184,6 +199,6 @@ /mob/living/silicon/robot/RangedAttack(atom/A, params) A.attack_robot(src) -/atom/proc/attack_robot(mob/user as mob) +/atom/proc/attack_robot(mob/user) attack_ai(user) return diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index ada0ce8cd30..856f724f925 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -64,7 +64,7 @@ /obj/screen/ai/alerts/Click() if(isAI(usr)) var/mob/living/silicon/ai/AI = usr - AI.subsystem_alarm_monitor() + AI.ai_alerts() /obj/screen/ai/announcement name = "Make Announcement" diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index a93e65c762b..a07a29ccc53 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -3,7 +3,7 @@ //PUBLIC - call these wherever you want -/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE) +/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE, timeout_override, no_anim) /* Proc to create or update an alert. Returns the alert if the alert is new or updated, 0 if it was thrown already @@ -59,14 +59,16 @@ LAZYSET(alerts, category, alert) // This also creates the list if it doesn't exist if(client && hud_used) hud_used.reorganize_alerts() - alert.transform = matrix(32, 6, MATRIX_TRANSLATE) - animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING) - if(alert.timeout) - spawn(alert.timeout) - if(alert.timeout && alerts[category] == alert && world.time >= alert.timeout) - clear_alert(category) - alert.timeout = world.time + alert.timeout - world.tick_lag + if(!no_anim) + alert.transform = matrix(32, 6, MATRIX_TRANSLATE) + animate(alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING) + + var/timeout = timeout_override || alert.timeout + if(timeout) + addtimer(CALLBACK(alert, /obj/screen/alert/.proc/do_timeout, src, category), timeout) + alert.timeout = world.time + timeout - world.tick_lag + return alert // Proc to clear an existing alert. @@ -94,7 +96,6 @@ var/alerttooltipstyle = "" var/override_alerts = FALSE //If it is overriding other alerts of the same type - /obj/screen/alert/MouseEntered(location,control,params) openToolTip(usr, src, params, title = name, content = desc, theme = alerttooltipstyle) @@ -102,6 +103,12 @@ /obj/screen/alert/MouseExited() closeToolTip(usr) +/obj/screen/alert/proc/do_timeout(mob/M, category) + if(!M || !M.alerts) + return + + if(timeout && M.alerts[category] == src && world.time >= timeout) + M.clear_alert(category) //Gas alerts /obj/screen/alert/not_enough_oxy @@ -507,6 +514,34 @@ so as to remain in compliance with the most up-to-date laws." timeout = 300 var/atom/target = null var/action = NOTIFY_JUMP + var/show_time_left = FALSE // If true you need to call START_PROCESSING manually + var/image/time_left_overlay // The last image showing the time left + var/datum/candidate_poll/poll // If set, on Click() it'll register the player as a candidate + +/obj/screen/alert/notify_action/process() + if(show_time_left) + var/timeleft = timeout - world.time + if(timeleft <= 0) + return PROCESS_KILL + + if(time_left_overlay) + overlays -= time_left_overlay + + var/obj/O = new + O.maptext = "[CEILING(timeleft / 10, 1)]" + O.maptext_width = O.maptext_height = 128 + var/matrix/M = new + M.Translate(4, 16) + O.transform = M + + var/image/I = image(O) + I.layer = FLOAT_LAYER + I.plane = FLOAT_PLANE + 1 + overlays += I + + time_left_overlay = I + qdel(O) + ..() /obj/screen/alert/notify_action/Destroy() target = null @@ -515,20 +550,52 @@ so as to remain in compliance with the most up-to-date laws." /obj/screen/alert/notify_action/Click() if(!usr || !usr.client) return - if(!target) - return var/mob/dead/observer/G = usr if(!istype(G)) return - switch(action) - if(NOTIFY_ATTACK) - target.attack_ghost(G) - if(NOTIFY_JUMP) - var/turf/T = get_turf(target) - if(T && isturf(T)) - G.loc = T - if(NOTIFY_FOLLOW) - G.ManualFollow(target) + + if(poll) + if(poll.sign_up(G)) + // Add a small overlay to indicate we've signed up + display_signed_up() + else if(target) + switch(action) + if(NOTIFY_ATTACK) + target.attack_ghost(G) + if(NOTIFY_JUMP) + var/turf/T = get_turf(target) + if(T && isturf(T)) + G.loc = T + if(NOTIFY_FOLLOW) + G.ManualFollow(target) + +/obj/screen/alert/notify_action/Topic(href, href_list) + if(href_list["signup"] && poll?.sign_up(usr)) + display_signed_up() + +/obj/screen/alert/notify_action/proc/display_signed_up() + var/image/I = image('icons/mob/screen_gen.dmi', icon_state = "selector") + I.layer = FLOAT_LAYER + I.plane = FLOAT_PLANE + 2 + overlays += I + +/obj/screen/alert/notify_action/proc/display_stacks(stacks = 1) + if(stacks <= 1) + return + + var/obj/O = new + O.maptext = "[stacks]x" + O.maptext_width = O.maptext_height = 128 + var/matrix/M = new + M.Translate(4, 2) + O.transform = M + + var/image/I = image(O) + I.layer = FLOAT_LAYER + I.plane = FLOAT_PLANE + 1 + overlays += I + + qdel(O) /obj/screen/alert/notify_soulstone name = "Soul Stone" diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm index a291120fb85..67df5eb3013 100644 --- a/code/_onclick/hud/ghost.dm +++ b/code/_onclick/hud/ghost.dm @@ -81,6 +81,9 @@ /obj/screen/ghost/respawn_pai/Click() var/mob/dead/observer/G = usr + if(!GLOB.paiController.check_recruit(G)) + to_chat(G, "You are not eligible to become a pAI.") + return GLOB.paiController.recruitWindow(G) /datum/hud/ghost diff --git a/code/_onclick/hud/guardian.dm b/code/_onclick/hud/guardian.dm index d628124e052..6c9e95f457e 100644 --- a/code/_onclick/hud/guardian.dm +++ b/code/_onclick/hud/guardian.dm @@ -13,7 +13,7 @@ using.icon_state = mymob.a_intent static_inventory += using action_intent = using - + using = new /obj/screen/guardian/Manifest() using.screen_loc = ui_rhand static_inventory += using @@ -49,8 +49,8 @@ /obj/screen/guardian/Manifest/Click() if(isguardian(usr)) var/mob/living/simple_animal/hostile/guardian/G = usr - G.Manifest() - + if(G.loc == G.summoner) + G.Manifest() /obj/screen/guardian/Recall icon_state = "recall" diff --git a/code/_onclick/hud/map_popups.dm b/code/_onclick/hud/map_popups.dm new file mode 100644 index 00000000000..dc9e255cba9 --- /dev/null +++ b/code/_onclick/hud/map_popups.dm @@ -0,0 +1,170 @@ +/client + /** + * Assoc list with all the active maps - when a screen obj is added to + * a map, it's put in here as well. + * + * Format: list( = list(/obj/screen)) + */ + var/list/screen_maps = list() + +/obj/screen + /** + * Map name assigned to this object. + * Automatically set by /client/proc/add_obj_to_map. + */ + var/assigned_map + /** + * Mark this object as garbage-collectible after you clean the map + * it was registered on. + * + * This could probably be changed to be a proc, for conditional removal. + * But for now, this works. + */ + var/del_on_map_removal = TRUE + +/** + * A screen object, which acts as a container for turfs and other things + * you want to show on the map, which you usually attach to "vis_contents". + */ +/obj/screen/map_view + // Map view has to be on the lowest plane to enable proper lighting + layer = GAME_PLANE + plane = GAME_PLANE + +/** + * A generic background object. + * It is also implicitly used to allocate a rectangle on the map, which will + * be used for auto-scaling the map. + */ +/obj/screen/background + name = "background" + icon = 'icons/mob/map_backgrounds.dmi' + icon_state = "clear" + layer = GAME_PLANE + plane = GAME_PLANE + +/** + * Sets screen_loc of this screen object, in form of point coordinates, + * with optional pixel offset (px, py). + * + * If applicable, "assigned_map" has to be assigned before this proc call. + */ +/obj/screen/proc/set_position(x, y, px = 0, py = 0) + if(assigned_map) + screen_loc = "[assigned_map]:[x]:[px],[y]:[py]" + else + screen_loc = "[x]:[px],[y]:[py]" + +/** + * Sets screen_loc to fill a rectangular area of the map. + * + * If applicable, "assigned_map" has to be assigned before this proc call. + */ +/obj/screen/proc/fill_rect(x1, y1, x2, y2) + if(assigned_map) + screen_loc = "[assigned_map]:[x1],[y1] to [x2],[y2]" + else + screen_loc = "[x1],[y1] to [x2],[y2]" + +/** + * Registers screen obj with the client, which makes it visible on the + * assigned map, and becomes a part of the assigned map's lifecycle. + */ +/client/proc/register_map_obj(obj/screen/screen_obj) + if(!screen_obj.assigned_map) + CRASH("Can't register [screen_obj] without 'assigned_map' property.") + if(!screen_maps[screen_obj.assigned_map]) + screen_maps[screen_obj.assigned_map] = list() + // NOTE: Possibly an expensive operation + var/list/screen_map = screen_maps[screen_obj.assigned_map] + if(!screen_map.Find(screen_obj)) + screen_map += screen_obj + if(!screen.Find(screen_obj)) + screen += screen_obj + +/** + * Clears the map of registered screen objects. + * + * Not really needed most of the time, as the client's screen list gets reset + * on relog. any of the buttons are going to get caught by garbage collection + * anyway. they're effectively qdel'd. + */ +/client/proc/clear_map(map_name) + if(!map_name || !(map_name in screen_maps)) + return FALSE + for(var/obj/screen/screen_obj in screen_maps[map_name]) + screen_maps[map_name] -= screen_obj + if(screen_obj.del_on_map_removal) + qdel(screen_obj) + screen_maps -= map_name + +/** + * Clears all the maps of registered screen objects. + */ +/client/proc/clear_all_maps() + for(var/map_name in screen_maps) + clear_map(map_name) + +/** + * Creates a popup window with a basic map element in it, without any + * further initialization. + * + * Ratio is how many pixels by how many pixels (keep it simple). + * + * Returns a map name. + */ +/client/proc/create_popup(name, ratiox = 100, ratioy = 100) + winclone(src, "popupwindow", name) + var/list/winparams = list() + winparams["size"] = "[ratiox]x[ratioy]" + winparams["on-close"] = "handle-popup-close [name]" + winset(src, "[name]", list2params(winparams)) + winshow(src, "[name]", 1) + + var/list/params = list() + params["parent"] = "[name]" + params["type"] = "map" + params["size"] = "[ratiox]x[ratioy]" + params["anchor1"] = "0,0" + params["anchor2"] = "[ratiox],[ratioy]" + winset(src, "[name]_map", list2params(params)) + + return "[name]_map" + +/** + * Create the popup, and get it ready for generic use by giving + * it a background. + * + * Width and height are multiplied by 64 by default. + */ +/client/proc/setup_popup(popup_name, width = 9, height = 9, \ + tilesize = 2, bg_icon) + if(!popup_name) + return + clear_map("[popup_name]_map") + var/x_value = world.icon_size * tilesize * width + var/y_value = world.icon_size * tilesize * height + var/map_name = create_popup(popup_name, x_value, y_value) + + var/obj/screen/background/background = new + background.assigned_map = map_name + background.fill_rect(1, 1, width, height) + if(bg_icon) + background.icon_state = bg_icon + register_map_obj(background) + + return map_name + +/** + * Closes a popup. + */ +/client/proc/close_popup(popup) + winshow(src, popup, 0) + handle_popup_close(popup) + +/** + * When the popup closes in any way (player or proc call) it calls this. + */ +/client/verb/handle_popup_close(window_id as text) + set hidden = TRUE + clear_map("[window_id]_map") diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 1423720f8f6..9795975a090 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -483,7 +483,7 @@ var/list/cached_healthdoll_overlays = list() // List of icon states (strings) for overlays /obj/screen/healthdoll/Click() - if(ishuman(usr)) + if(ishuman(usr) && !usr.is_dead()) var/mob/living/carbon/H = usr H.check_self_for_injuries() diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index f88244c0cd8..4fd5b956df5 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -81,7 +81,7 @@ user.do_attack_animation(M) . = M.attacked_by(src, user, def_zone) - add_attack_logs(user, M, "Attacked with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL) + add_attack_logs(user, M, "Attacked with [name] ([uppertext(user.a_intent)]) ([uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL) add_fingerprint(user) diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index 47857ee4fa1..4497f170a2d 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -17,6 +17,11 @@ if(A.attack_hulk(src)) return + if(buckled && isstructure(buckled)) + var/obj/structure/S = buckled + if(S.prevents_buckled_mobs_attacking()) + return + A.attack_hand(src) /atom/proc/attack_hand(mob/user as mob) diff --git a/code/_onclick/rig.dm b/code/_onclick/rig.dm deleted file mode 100644 index ff55f35cb57..00000000000 --- a/code/_onclick/rig.dm +++ /dev/null @@ -1,84 +0,0 @@ - -#define MIDDLE_CLICK 0 -#define ALT_CLICK 1 -#define CTRL_CLICK 2 -#define MAX_HARDSUIT_CLICK_MODE 2 - -/client - var/hardsuit_click_mode = MIDDLE_CLICK - -/client/verb/toggle_hardsuit_mode() - set name = "Toggle Hardsuit Activation Mode" - set desc = "Switch between hardsuit activation modes." - set category = "OOC" - - hardsuit_click_mode++ - if(hardsuit_click_mode > MAX_HARDSUIT_CLICK_MODE) - hardsuit_click_mode = 0 - - switch(hardsuit_click_mode) - if(MIDDLE_CLICK) - to_chat(src, "Hardsuit activation mode set to middle-click.") - if(ALT_CLICK) - to_chat(src, "Hardsuit activation mode set to alt-click.") - if(CTRL_CLICK) - to_chat(src, "Hardsuit activation mode set to control-click.") - else - // should never get here, but just in case: - log_runtime(EXCEPTION("Bad hardsuit click mode: [hardsuit_click_mode] - expected 0 to [MAX_HARDSUIT_CLICK_MODE]"), src) - to_chat(src, "Somehow you bugged the system. Setting your hardsuit mode to middle-click.") - hardsuit_click_mode = MIDDLE_CLICK - -/mob/living/MiddleClickOn(atom/A) - if(client && client.hardsuit_click_mode == MIDDLE_CLICK) - if(HardsuitClickOn(A)) - return - ..() - -/mob/living/AltClickOn(atom/A) - if(client && client.hardsuit_click_mode == ALT_CLICK) - if(HardsuitClickOn(A)) - return - ..() - -/mob/living/CtrlClickOn(atom/A) - if(client && client.hardsuit_click_mode == CTRL_CLICK) - if(HardsuitClickOn(A)) - return - ..() - -/mob/living/proc/can_use_rig() - return 0 - -/mob/living/carbon/human/can_use_rig() - return 1 - -/mob/living/carbon/brain/can_use_rig() - return istype(loc, /obj/item/mmi) - -/mob/living/silicon/ai/can_use_rig() - return istype(loc, /obj/item/aicard) - -/mob/living/silicon/pai/can_use_rig() - return loc == card - -/mob/living/proc/HardsuitClickOn(var/atom/A, var/alert_ai = 0) - if(!can_use_rig() || (next_move > world.time)) - return 0 - var/obj/item/rig/rig = get_rig() - if(istype(rig) && !rig.offline && rig.selected_module) - if(src != rig.wearer) - if(rig.ai_can_move_suit(src, check_user_module = 1)) - message_admins("[key_name_admin(src)] is trying to force \the [key_name_admin(rig.wearer)] to use a hardsuit module.") - else - return 0 - rig.selected_module.engage(A, alert_ai) - if(ismob(A)) // No instant mob attacking - though modules have their own cooldowns - changeNext_move(CLICK_CD_MELEE) - return 1 - return 0 - -#undef MIDDLE_CLICK -#undef ALT_CLICK -#undef CTRL_CLICK -#undef MAX_HARDSUIT_CLICK_MODE diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index f4f2f72b16b..756ce6f8527 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -61,7 +61,6 @@ var/usewhitelist = 0 var/mods_are_mentors = 0 var/load_jobs_from_txt = 0 - var/ToRban = 0 var/automute_on = 0 //enables automuting/spam prevention var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. var/round_abandon_penalty_period = 30 MINUTES // Time from round start during which ghosting out is penalized @@ -578,9 +577,6 @@ if("humans_need_surnames") humans_need_surnames = 1 - if("tor_ban") - ToRban = 1 - if("automute_on") automute_on = 1 diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 03e54df61c5..c9934b128cf 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -35,7 +35,7 @@ var/static/list/failure_strikes //How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out! - var/offline_implications = "None" // What are the implications of this SS being offlined? + var/offline_implications = "None. No immediate action is needed." // What are the implications of this SS being offlined? //Do not override ///datum/controller/subsystem/New() diff --git a/code/controllers/subsystem/afk.dm b/code/controllers/subsystem/afk.dm index 951a7fb1786..25ccda3349c 100644 --- a/code/controllers/subsystem/afk.dm +++ b/code/controllers/subsystem/afk.dm @@ -21,7 +21,8 @@ SUBSYSTEM_DEF(afk) /datum/controller/subsystem/afk/fire() var/list/toRemove = list() - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing if(!H?.ckey) // Useless non ckey creatures continue diff --git a/code/controllers/subsystem/alarm.dm b/code/controllers/subsystem/alarm.dm index 289adf6de40..3d91d763be3 100644 --- a/code/controllers/subsystem/alarm.dm +++ b/code/controllers/subsystem/alarm.dm @@ -1,31 +1,31 @@ -SUBSYSTEM_DEF(alarms) - name = "Alarms" - init_order = INIT_ORDER_ALARMS // 2 - offline_implications = "Alarms (Power, camera, fire, etc) will no longer be checked. No immediate action is needed." - var/datum/alarm_handler/atmosphere/atmosphere_alarm = new() - var/datum/alarm_handler/burglar/burglar_alarm = new() - var/datum/alarm_handler/camera/camera_alarm = new() - var/datum/alarm_handler/fire/fire_alarm = new() - var/datum/alarm_handler/motion/motion_alarm = new() - var/datum/alarm_handler/power/power_alarm = new() - var/list/datum/alarm/all_handlers +SUBSYSTEM_DEF(alarm) + name = "Alarm" + flags = SS_NO_INIT | SS_NO_FIRE + var/list/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list(), "Burglar" = list()) -/datum/controller/subsystem/alarms/Initialize(start_timeofday) - all_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm) - return ..() +/datum/controller/subsystem/alarm/proc/triggerAlarm(class, area/A, list/O, obj/alarmsource) + var/list/L = alarms[class] + for(var/I in L) + if(I == A.name) + var/list/alarm = L[I] + var/list/sources = alarm[3] + if(!(alarmsource.UID() in sources)) + sources += alarmsource.UID() + return TRUE + L[A.name] = list(get_area_name(A, TRUE), O, list(alarmsource.UID())) + SEND_SIGNAL(SSalarm, COMSIG_TRIGGERED_ALARM, class, A, O, alarmsource) + return TRUE -/datum/controller/subsystem/alarms/fire() - for(var/datum/alarm_handler/AH in all_handlers) - AH.process() +/datum/controller/subsystem/alarm/proc/cancelAlarm(class, area/A, obj/origin) + var/list/L = alarms[class] + var/cleared = FALSE + for(var/I in L) + if(I == A.name) + var/list/alarm = L[I] + var/list/srcs = alarm[3] + srcs -= origin.UID() + if(!length(srcs)) + cleared = TRUE + L -= I -/datum/controller/subsystem/alarms/proc/active_alarms() - var/list/all_alarms = new () - for(var/datum/alarm_handler/AH in all_handlers) - var/list/alarms = AH.alarms - all_alarms += alarms - - return all_alarms - -/datum/controller/subsystem/alarms/proc/number_of_active_alarms() - var/list/alarms = active_alarms() - return alarms.len + SEND_SIGNAL(SSalarm, COMSIG_CANCELLED_ALARM, class, A, origin, cleared) diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm deleted file mode 100644 index 4eb468a0952..00000000000 --- a/code/controllers/subsystem/chat.dm +++ /dev/null @@ -1,67 +0,0 @@ -SUBSYSTEM_DEF(chat) - name = "Chat" - flags = SS_TICKER|SS_NO_INIT - wait = 1 - priority = FIRE_PRIORITY_CHAT - init_order = INIT_ORDER_CHAT - offline_implications = "Chat messages will no longer be cleanly queued. No immediate action is needed." - - var/list/payload = list() - - -/datum/controller/subsystem/chat/fire() - for(var/i in payload) - var/client/C = i - if(C) - C << output(payload[C], "browseroutput:output") - payload -= C - - if(MC_TICK_CHECK) - return - - -/datum/controller/subsystem/chat/proc/queue(target, message, flag) - if(!target || !message) - return - - if(!istext(message)) - stack_trace("to_chat called with invalid input type") - return - - if(target == world) - target = GLOB.clients - - //Some macros remain in the string even after parsing and fuck up the eventual output - message = replacetext(message, "\improper", "") - message = replacetext(message, "\proper", "") - message += "
" - - - //url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript. - //Do the double-encoding here to save nanoseconds - var/twiceEncoded = url_encode(url_encode(message)) - - if(islist(target)) - for(var/I in target) - var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible - - if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file. - continue - - if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue - C.chatOutput.messageQueue += message - continue - - payload[C] += twiceEncoded - - else - var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible - - if(!C?.chatOutput || C.chatOutput.broken) //A player who hasn't updated his skin file. - return - - if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue - C.chatOutput.messageQueue += message - return - - payload[C] += twiceEncoded diff --git a/code/controllers/subsystem/cleanup.dm b/code/controllers/subsystem/cleanup.dm new file mode 100644 index 00000000000..f5962c45547 --- /dev/null +++ b/code/controllers/subsystem/cleanup.dm @@ -0,0 +1,44 @@ +/** + * # Cleanup Subsystem + * + * For now, all it does is periodically clean the supplied global lists of any null values they may contain. + * + * Why is this important? + * + * Sometimes, these lists can gain nulls due to errors. + * For example, when a dead player trasitions from the `dead_mob_list` to the `alive_mob_list`, a null value may get stuck in the dead mob list. + * This can cause issues when other code tries to do things with the values in the list, but are instead met with null values. + * These problems are incredibly hard to track down and fix, so this subsystem is a solution to that. + */ +SUBSYSTEM_DEF(cleanup) + name = "Null cleanup" + wait = 30 SECONDS + flags = SS_POST_FIRE_TIMING + priority = FIRE_PRIORITY_CLEANUP + init_order = INIT_ORDER_CLEANUP + runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + offline_implications = "Certain global lists will no longer be cleared of nulls, which may result in runtimes. No immediate action is needed." + /// A list of global lists we want the subsystem to clean. + var/list/lists_to_clean + +/datum/controller/subsystem/cleanup/Initialize(start_timeofday) + // If you want this subsystem to clean out nulls from a specific list, add it here. + lists_to_clean = list( + GLOB.clients = "clients", + GLOB.player_list = "player_list", + GLOB.mob_list = "mob_list", + GLOB.alive_mob_list = "alive_mob_list", + GLOB.dead_mob_list = "dead_mob_list", + GLOB.human_list = "human_list", + GLOB.carbon_list = "carbon_list" + ) + return ..() + +/datum/controller/subsystem/cleanup/fire(resumed) + for(var/L in lists_to_clean) + var/list/_list = L + var/prev_length = length(_list) + listclearnulls(_list) + + if(length(_list) < prev_length) + stack_trace("Found a null value in GLOB.[lists_to_clean[_list]]!") diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm index 1a6e073c808..4b2ed4a71fd 100644 --- a/code/controllers/subsystem/events.dm +++ b/code/controllers/subsystem/events.dm @@ -40,7 +40,7 @@ SUBSYSTEM_DEF(events) var/datum/event_container/EC = event_containers[i] EC.process() -/datum/controller/subsystem/events/proc/event_complete(var/datum/event/E) +/datum/controller/subsystem/events/proc/event_complete(datum/event/E) if(!E.event_meta) // datum/event is used here and there for random reasons, maintaining "backwards compatibility" log_debug("Event of '[E.type]' with missing meta-data has completed.") return @@ -65,11 +65,11 @@ SUBSYSTEM_DEF(events) log_debug("Event '[EM.name]' has completed at [station_time_timestamp()].") -/datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay) +/datum/controller/subsystem/events/proc/delay_events(severity, delay) var/datum/event_container/EC = event_containers[severity] EC.next_event_time += delay -/datum/controller/subsystem/events/proc/Interact(var/mob/living/user) +/datum/controller/subsystem/events/proc/Interact(mob/living/user) var/html = GetInteractWindow() @@ -113,7 +113,7 @@ SUBSYSTEM_DEF(events) html += "[EM.name]" html += "[EM.weight]" html += "[EM.min_weight]" - html += "[EM.max_weight]" + html += "[EM.max_weight == INFINITY ? "No max" : EM.max_weight]" html += "[EM.one_shot]" html += "[EM.enabled]" html += "[EM.get_weight(number_active_with_role())]" @@ -212,38 +212,38 @@ SUBSYSTEM_DEF(events) if(href_list["toggle_report"]) report_at_round_end = !report_at_round_end - admin_log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.") + log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.") else if(href_list["dec_timer"]) var/datum/event_container/EC = locate(href_list["event"]) var/decrease = (60 * RaiseToPower(10, text2num(href_list["dec_timer"]))) EC.next_event_time -= decrease - admin_log_and_message_admins("decreased timer for [GLOB.severity_to_string[EC.severity]] events by [decrease/600] minute(s).") + log_and_message_admins("decreased timer for [GLOB.severity_to_string[EC.severity]] events by [decrease/600] minute(s).") else if(href_list["inc_timer"]) var/datum/event_container/EC = locate(href_list["event"]) var/increase = (60 * RaiseToPower(10, text2num(href_list["inc_timer"]))) EC.next_event_time += increase - admin_log_and_message_admins("increased timer for [GLOB.severity_to_string[EC.severity]] events by [increase/600] minute(s).") + log_and_message_admins("increased timer for [GLOB.severity_to_string[EC.severity]] events by [increase/600] minute(s).") else if(href_list["select_event"]) var/datum/event_container/EC = locate(href_list["select_event"]) var/datum/event_meta/EM = EC.SelectEvent() if(EM) - admin_log_and_message_admins("has queued the [GLOB.severity_to_string[EC.severity]] event '[EM.name]'.") + log_and_message_admins("has queued the [GLOB.severity_to_string[EC.severity]] event '[EM.name]'.") else if(href_list["pause"]) var/datum/event_container/EC = locate(href_list["pause"]) EC.delayed = !EC.delayed - admin_log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [GLOB.severity_to_string[EC.severity]] events.") + log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [GLOB.severity_to_string[EC.severity]] events.") else if(href_list["interval"]) var/delay = input("Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") as num|null if(delay && delay > 0) var/datum/event_container/EC = locate(href_list["interval"]) EC.delay_modifier = delay - admin_log_and_message_admins("has set the interval modifier for [GLOB.severity_to_string[EC.severity]] events to [EC.delay_modifier].") + log_and_message_admins("has set the interval modifier for [GLOB.severity_to_string[EC.severity]] events to [EC.delay_modifier].") else if(href_list["stop"]) if(alert("Stopping an event may have unintended side-effects. Continue?","Stopping Event!","Yes","No") != "Yes") return var/datum/event/E = locate(href_list["stop"]) var/datum/event_meta/EM = E.event_meta - admin_log_and_message_admins("has stopped the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + log_and_message_admins("has stopped the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") E.kill() else if(href_list["view_events"]) selected_event_container = locate(href_list["view_events"]) @@ -265,23 +265,23 @@ SUBSYSTEM_DEF(events) var/datum/event_meta/EM = locate(href_list["set_weight"]) EM.weight = weight if(EM != new_event) - admin_log_and_message_admins("has changed the weight of the [GLOB.severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].") + log_and_message_admins("has changed the weight of the [GLOB.severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].") else if(href_list["toggle_oneshot"]) var/datum/event_meta/EM = locate(href_list["toggle_oneshot"]) EM.one_shot = !EM.one_shot if(EM != new_event) - admin_log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") else if(href_list["toggle_enabled"]) var/datum/event_meta/EM = locate(href_list["toggle_enabled"]) EM.enabled = !EM.enabled - admin_log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") else if(href_list["remove"]) if(alert("This will remove the event from rotation. Continue?","Removing Event!","Yes","No") != "Yes") return var/datum/event_meta/EM = locate(href_list["remove"]) var/datum/event_container/EC = locate(href_list["EC"]) EC.available_events -= EM - admin_log_and_message_admins("has removed the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + log_and_message_admins("has removed the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") else if(href_list["add"]) if(!new_event.name || !new_event.event_type) return @@ -289,12 +289,12 @@ SUBSYSTEM_DEF(events) return new_event.severity = selected_event_container.severity selected_event_container.available_events += new_event - admin_log_and_message_admins("has added \a [GLOB.severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].") + log_and_message_admins("has added \a [GLOB.severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].") new_event = new else if(href_list["clear"]) var/datum/event_container/EC = locate(href_list["clear"]) if(EC.next_event) - admin_log_and_message_admins("has dequeued the [GLOB.severity_to_string[EC.severity]] event '[EC.next_event.name]'.") + log_and_message_admins("has dequeued the [GLOB.severity_to_string[EC.severity]] event '[EC.next_event.name]'.") EC.next_event = null Interact(usr) diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index 619508544a6..e50c372584a 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -4,7 +4,7 @@ SUBSYSTEM_DEF(garbage) wait = 2 SECONDS flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY - init_order = INIT_ORDER_GARBAGE + init_order = INIT_ORDER_GARBAGE // Why does this have an init order if it has SS_NO_INIT? offline_implications = "Garbage collection is no longer functional, and objects will not be qdel'd. Immediate server restart recommended." var/list/collection_timeout = list(2 MINUTES, 10 SECONDS) // deciseconds to wait before moving something up in the queue to the next level diff --git a/code/controllers/subsystem/ghost_spawns.dm b/code/controllers/subsystem/ghost_spawns.dm new file mode 100644 index 00000000000..d8a4f29e20d --- /dev/null +++ b/code/controllers/subsystem/ghost_spawns.dm @@ -0,0 +1,276 @@ +SUBSYSTEM_DEF(ghost_spawns) + name = "Ghost Spawns" + init_order = INIT_ORDER_EVENTS + flags = SS_BACKGROUND + wait = 1 SECONDS + runlevels = RUNLEVEL_GAME + offline_implications = "Ghosts will no longer be able to respawn as event mobs (Blob, etc..). Shuttle call recommended." + + /// List of polls currently ongoing, to be checked on next fire() + var/list/datum/candidate_poll/currently_polling + /// Whether there are active polls or not + var/polls_active = FALSE + /// Number of polls performed since the start + var/total_polls = 0 + /// The poll that's closest to finishing + var/datum/candidate_poll/next_poll_to_finish + +/datum/controller/subsystem/ghost_spawns/fire() + if(!polls_active) + return + if(!currently_polling) // if polls_active is TRUE then this shouldn't happen, but still.. + currently_polling = list() + + for(var/poll in currently_polling) + var/datum/candidate_poll/P = poll + if(P.time_left() <= 0) + polling_finished(P) + +/** + * Polls for candidates with a question and a preview of the role + * + * This proc replaces /proc/pollCandidates. + * Should NEVER be used in a proc that has waitfor set to FALSE/0 (due to #define UNTIL) + * Arguments: + * * question - The question to ask to potential candidates + * * role - The role to poll for. Should be a ROLE_x enum. If set, potential candidates who aren't eligible will be ignored + * * antag_age_check - Whether to filter out potential candidates who don't have an old enough account + * * poll_time - How long to poll for in deciseconds + * * ignore_respawnability - Whether to ignore the player's respawnability + * * min_hours - The amount of hours needed for a potential candidate to be eligible + * * flash_window - Whether the poll should flash a potential candidate's game window + * * check_antaghud - Whether to filter out potential candidates who enabled AntagHUD + * * source - The atom, atom prototype, icon or mutable appearance to display as an icon in the alert + * * role_cleanname - The name override to display to clients + */ +/datum/controller/subsystem/ghost_spawns/proc/poll_candidates(question = "Would you like to play a special role?", role, antag_age_check = FALSE, poll_time = 30 SECONDS, ignore_respawnability = FALSE, min_hours = 0, flash_window = TRUE, check_antaghud = TRUE, source, role_cleanname) + log_debug("Polling candidates [role ? "for [role_cleanname || get_roletext(role)]" : "\"[question]\""] for [poll_time / 10] seconds") + + // Start firing + polls_active = TRUE + total_polls++ + + var/datum/candidate_poll/P = new(role, question, poll_time) + LAZYADD(currently_polling, P) + + // We're the poll closest to completion + if(!next_poll_to_finish || poll_time < next_poll_to_finish.time_left()) + next_poll_to_finish = P + + var/category = "[P.hash]_notify_action" + + for(var/mob/dead/observer/M in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list)) + if(!is_eligible(M, role, antag_age_check, role, min_hours, check_antaghud)) + continue + + SEND_SOUND(M, 'sound/misc/notice2.ogg') + if(flash_window) + window_flash(M.client) + + // If we somehow send two polls for the same mob type, but with a duration on the second one shorter than the time left on the first one, + // we need to keep the first one's timeout rather than use the shorter one + var/obj/screen/alert/notify_action/current_alert = LAZYACCESS(M.alerts, category) + var/alert_time = poll_time + var/alert_poll = P + if(current_alert && current_alert.timeout > (world.time + poll_time - world.tick_lag)) + alert_time = current_alert.timeout - world.time + world.tick_lag + alert_poll = current_alert.poll + + // Send them an on-screen alert + var/obj/screen/alert/notify_action/A = M.throw_alert(category, /obj/screen/alert/notify_action, timeout_override = alert_time, no_anim = TRUE) + if(!A) + continue + + A.icon = ui_style2icon(M.client?.prefs.UI_style) + A.name = "Looking for candidates" + A.desc = "[question]\n\n(expires in [poll_time / 10] seconds)" + A.show_time_left = TRUE + A.poll = alert_poll + + // Sign up inheritance and stacking + var/inherited_sign_up = FALSE + var/num_stack = 1 + for(var/existing_poll in currently_polling) + var/datum/candidate_poll/P2 = existing_poll + if(P != P2 && P.hash == P2.hash) + // If there's already a poll for an identical mob type ongoing and the client is signed up for it, sign them up for this one + if(!inherited_sign_up && (M in P2.signed_up) && P.sign_up(M, TRUE)) + A.display_signed_up() + inherited_sign_up = TRUE + // This number is used to display the number of polls the alert regroups + num_stack++ + if(num_stack > 1) + A.display_stacks(num_stack) + + // Image to display + var/image/I + if(source) + if(!ispath(source)) + var/atom/S = source + var/old_layer = S.layer + var/old_plane = S.plane + + S.layer = FLOAT_LAYER + S.plane = FLOAT_PLANE + A.overlays += S + S.layer = old_layer + S.plane = old_plane + else + I = image(source, layer = FLOAT_LAYER, dir = SOUTH) + else + // Just use a generic image + I = image('icons/effects/effects.dmi', icon_state = "static", layer = FLOAT_LAYER, dir = SOUTH) + + if(I) + I.layer = FLOAT_LAYER + I.plane = FLOAT_PLANE + A.overlays += I + + // Chat message + var/act_jump = "" + if(isatom(source)) + act_jump = "\[Teleport]" + var/act_signup = "\[Sign Up]" + to_chat(M, "Now looking for candidates [role ? "to play as \an [role_cleanname || get_roletext(role)]" : "\"[question]\""]. [act_jump] [act_signup]") + + // Start processing it so it updates visually the timer + START_PROCESSING(SSprocessing, A) + A.process() + + // Sleep until the time is up + UNTIL(P.finished) + return P.signed_up + +/** + * Returns whether an observer is eligible to be an event mob + * + * Arguments: + * * M - The mob to check eligibility + * * role - The role to check eligibility for. Checks 1. the client has enabled the role 2. the account's age for this role if antag_age_check is TRUE + * * antag_age_check - Whether to check the account's age or not for the given role. + * * role_text - The role's clean text. Used for checking job bans to determine eligibility + * * min_hours - The amount of minimum hours the client needs before being eligible + * * check_antaghud - Whether to consider a client who enabled AntagHUD ineligible or not + */ +/datum/controller/subsystem/ghost_spawns/proc/is_eligible(mob/M, role, antag_age_check, role_text, min_hours, check_antaghud) + . = FALSE + if(!M.key || !M.client) + return + if(role) + if(!(role in M.client.prefs.be_special)) + return + if(antag_age_check) + if(!player_old_enough_antag(M.client, role)) + return + if(role_text) + if(jobban_isbanned(M, role_text) || jobban_isbanned(M, "Syndicate")) + return + if(config.use_exp_restrictions && min_hours) + if(M.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60) + return + if(check_antaghud && cannotPossess(M)) + return + + return TRUE + +/** + * Called by the subsystem when a poll's timer runs out + * + * Can be called manually to finish a poll prematurely + * Arguments: + * * P - The poll to finish + */ +/datum/controller/subsystem/ghost_spawns/proc/polling_finished(datum/candidate_poll/P) + // Trim players who aren't eligible anymore + var/len_pre_trim = length(P.signed_up) + P.trim_candidates() + log_debug("Candidate poll [P.role ? "for [get_roletext(P.role)]" : "\"[P.question]\""] finished. [len_pre_trim] players signed up, [length(P.signed_up)] after trimming") + + P.finished = TRUE + currently_polling -= P + + // Determine which is the next poll closest the completion or "disable" firing if there's none + if(!length(currently_polling)) + polls_active = FALSE + next_poll_to_finish = null + else if(P == next_poll_to_finish) + next_poll_to_finish = null + for(var/poll in currently_polling) + var/datum/candidate_poll/P2 = poll + if(!next_poll_to_finish || P2.time_left() < next_poll_to_finish.time_left()) + next_poll_to_finish = P2 + +/datum/controller/subsystem/ghost_spawns/stat_entry(msg) + msg += "Active: [length(currently_polling)] | Total: [total_polls]" + if(next_poll_to_finish) + msg += " | Next: [DisplayTimeText(next_poll_to_finish.time_left())] ([length(next_poll_to_finish.signed_up)] candidates)" + ..(msg) + +// The datum that describes one instance of candidate polling +/datum/candidate_poll + var/role // The role the poll is for + var/question // The question asked to observers + var/duration // The duration of the poll + var/list/mob/dead/observer/signed_up // The players who signed up to this poll + var/time_started // The world.time at which the poll was created + var/finished = FALSE // Whether the polling is finished + var/hash // Used to categorize in the alerts system + +/datum/candidate_poll/New(polled_role, polled_question, poll_duration) + role = polled_role + question = polled_question + duration = poll_duration + signed_up = list() + time_started = world.time + hash = copytext(md5("[question]_[role ? role : "0"]"), 1, 7) + return ..() + +/** + * Attempts to sign a (controlled) mob up + * + * Will fail if the mob is already signed up or the poll's timer ran out. + * Does not check for eligibility + * Arguments: + * * M - The (controlled) mob to sign up + * * silent - Whether no messages should appear or not. If not TRUE, signing up to this poll will also sign the mob up for identical polls + */ +/datum/candidate_poll/proc/sign_up(mob/dead/observer/M, silent = FALSE) + . = FALSE + if(!istype(M) || !M.key || !M.client) + return + if(M in signed_up) + if(!silent) + to_chat(M, "You have already signed up for this!") + return + if(time_left() <= 0) + if(!silent) + to_chat(M, "Sorry, you were too late for the consideration!") + SEND_SOUND(M, 'sound/machines/buzz-sigh.ogg') + return + + signed_up += M + if(!silent) + to_chat(M, "You have signed up for this role! A candidate will be picked randomly soon..") + // Sign them up for any other polls with the same mob type + for(var/existing_poll in SSghost_spawns.currently_polling) + var/datum/candidate_poll/P = existing_poll + if(src != P && hash == P.hash && !(M in P.signed_up)) + P.sign_up(M, TRUE) + + return TRUE + +/** + * Deletes any candidates who may have disconnected from the list + */ +/datum/candidate_poll/proc/trim_candidates() + listclearnulls(signed_up) + for(var/mob in signed_up) + var/mob/M = mob + if(!M.key || !M.client) + signed_up -= M + +/** + * Returns the time left for a poll + */ +/datum/candidate_poll/proc/time_left() + return duration - (world.time - time_started) diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm index ef89317d0f5..729c4dbe67b 100644 --- a/code/controllers/subsystem/jobs.dm +++ b/code/controllers/subsystem/jobs.dm @@ -27,7 +27,7 @@ SUBSYSTEM_DEF(jobs) /datum/controller/subsystem/jobs/fire() if(!config.sql_enabled || !config.use_exp_tracking) return - update_exp(5,0) + INVOKE_ASYNC(GLOBAL_PROC, /.proc/update_exp, 5, 0) /datum/controller/subsystem/jobs/proc/SetupOccupations(var/list/faction = list("Station")) occupations = list() @@ -620,20 +620,32 @@ SUBSYSTEM_DEF(jobs) var/mob/M = tgtcard.getPlayer() for(var/datum/job/job in occupations) if(tgtcard.assignment && tgtcard.assignment == job.title) - jobs_to_formats[job.title] = "disabled" // the job they already have is pre-selected + jobs_to_formats[job.title] = "green" // the job they already have is pre-selected + else if(tgtcard.assignment == "Demoted" || tgtcard.assignment == "Terminated") + jobs_to_formats[job.title] = "grey" else if(!job.would_accept_job_transfer_from_player(M)) - jobs_to_formats[job.title] = "linkDiscourage" // jobs which are karma-locked and not unlocked for this player are discouraged + jobs_to_formats[job.title] = "grey" // jobs which are karma-locked and not unlocked for this player are discouraged else if((job.title in GLOB.command_positions) && istype(M) && M.client && job.available_in_playtime(M.client)) - jobs_to_formats[job.title] = "linkDiscourage" // command jobs which are playtime-locked and not unlocked for this player are discouraged + jobs_to_formats[job.title] = "grey" // command jobs which are playtime-locked and not unlocked for this player are discouraged else if(job.total_positions && !job.current_positions && job.title != "Civilian") - jobs_to_formats[job.title] = "linkEncourage" // jobs with nobody doing them at all are encouraged + jobs_to_formats[job.title] = "teal" // jobs with nobody doing them at all are encouraged else if(job.total_positions >= 0 && job.current_positions >= job.total_positions) - jobs_to_formats[job.title] = "linkDiscourage" // jobs that are full (no free positions) are discouraged + jobs_to_formats[job.title] = "grey" // jobs that are full (no free positions) are discouraged + if(tgtcard.assignment == "Demoted" || tgtcard.assignment == "Terminated") + jobs_to_formats["Custom"] = "grey" return jobs_to_formats -/datum/controller/subsystem/jobs/proc/log_job_transfer(transferee, oldvalue, newvalue, whodidit) - id_change_records["[id_change_counter]"] = list("transferee" = transferee, "oldvalue" = oldvalue, "newvalue" = newvalue, "whodidit" = whodidit, "timestamp" = station_time_timestamp()) + +/datum/controller/subsystem/jobs/proc/log_job_transfer(transferee, oldvalue, newvalue, whodidit, reason) + id_change_records["[id_change_counter]"] = list( + "transferee" = transferee, + "oldvalue" = oldvalue, + "newvalue" = newvalue, + "whodidit" = whodidit, + "timestamp" = station_time_timestamp(), + "reason" = reason + ) id_change_counter++ /datum/controller/subsystem/jobs/proc/slot_job_transfer(oldtitle, newtitle) @@ -644,43 +656,54 @@ SUBSYSTEM_DEF(jobs) oldjobdatum.current_positions-- newjobdatum.current_positions++ +/datum/controller/subsystem/jobs/proc/notify_dept_head(jobtitle, antext) + // Used to notify the department head of jobtitle X that their employee was brigged, demoted or terminated + if(!jobtitle || !antext) + return + var/datum/job/tgt_job = GetJob(jobtitle) + if(!tgt_job) + return + if(!tgt_job.department_head[1]) + return + var/boss_title = tgt_job.department_head[1] + var/obj/item/pda/target_pda + for(var/obj/item/pda/check_pda in GLOB.PDAs) + if(check_pda.ownrank == boss_title) + target_pda = check_pda + break + if(!target_pda) + return + var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger) + if(PM && PM.can_receive()) + PM.notify("Automated Notification: \"[antext]\" (Unable to Reply)", 0) // the 0 means don't make the PDA flash -/datum/controller/subsystem/jobs/proc/fetch_transfer_record_html(var/centcom) - var/record_html = "" +/datum/controller/subsystem/jobs/proc/notify_by_name(target_name, antext) + // Used to notify a specific crew member based on their real_name + if(!target_name || !antext) + return + var/obj/item/pda/target_pda + for(var/obj/item/pda/check_pda in GLOB.PDAs) + if(check_pda.owner == target_name) + target_pda = check_pda + break + if(!target_pda) + return + var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger) + if(PM && PM.can_receive()) + PM.notify("Automated Notification: \"[antext]\" (Unable to Reply)", 0) // the 0 means don't make the PDA flash - var/table_headers = list("Crewman", "Old Rank", "New Rank", "Authorized By", "Time") - var/hidden_fields = list("deletedby") - if(centcom) - table_headers += "Deleted By" - record_html += "" - for(var/thisheader in table_headers) - record_html += "" - record_html += "" - - var/visible_record_count = 0 +/datum/controller/subsystem/jobs/proc/format_job_change_records(centcom) + var/list/formatted = list() for(var/thisid in id_change_records) var/thisrecord = id_change_records[thisid] - if(thisrecord["deletedby"] && !centcom) continue - - record_html += "" + var/list/newlist = list() for(var/lkey in thisrecord) - if(lkey in hidden_fields) - if(centcom) - record_html += "" - else - continue - else - record_html += "" - record_html += "" - visible_record_count++ + newlist[lkey] = thisrecord[lkey] + formatted.Add(list(newlist)) + return formatted - record_html += "
[thisheader]
[thisrecord[lkey]][thisrecord[lkey]]
" - - if(!visible_record_count) - return "No records on file yet." - return record_html /datum/controller/subsystem/jobs/proc/delete_log_records(sourceuser, delete_all) . = 0 diff --git a/code/controllers/subsystem/nano_mob_hunter.dm b/code/controllers/subsystem/nano_mob_hunter.dm index 19b57f59a70..b8a5d09e0b5 100644 --- a/code/controllers/subsystem/nano_mob_hunter.dm +++ b/code/controllers/subsystem/nano_mob_hunter.dm @@ -2,6 +2,7 @@ SUBSYSTEM_DEF(mob_hunt) name = "Nano-Mob Hunter GO Server" init_order = INIT_ORDER_NANOMOB priority = FIRE_PRIORITY_NANOMOB // Low priority, no need for MC_TICK_CHECK due to extremely low performance impact. + flags = SS_NO_INIT offline_implications = "Nano-Mob Hunter will no longer spawn mobs. No immediate action is needed." var/max_normal_spawns = 15 //change this to adjust the number of normal spawns that can exist at one time. trapped spawns (from traitors) don't count towards this var/list/normal_spawns = list() diff --git a/code/controllers/subsystem/parallax.dm b/code/controllers/subsystem/parallax.dm index 9135f62b03f..1e3c8aca0d9 100644 --- a/code/controllers/subsystem/parallax.dm +++ b/code/controllers/subsystem/parallax.dm @@ -34,7 +34,7 @@ SUBSYSTEM_DEF(parallax) for (A; isloc(A.loc) && !isturf(A.loc); A = A.loc); if(A != C.movingmob) - if(C.movingmob != null) + if(C.movingmob != null && C.movingmob.client_mobs_in_contents) C.movingmob.client_mobs_in_contents -= C.mob UNSETEMPTY(C.movingmob.client_mobs_in_contents) LAZYINITLIST(A.client_mobs_in_contents) diff --git a/code/controllers/subsystem/dcs.dm b/code/controllers/subsystem/processing/dcs.dm similarity index 90% rename from code/controllers/subsystem/dcs.dm rename to code/controllers/subsystem/processing/dcs.dm index 09dea24071f..a223f4676f6 100644 --- a/code/controllers/subsystem/dcs.dm +++ b/code/controllers/subsystem/processing/dcs.dm @@ -3,6 +3,8 @@ PROCESSING_SUBSYSTEM_DEF(dcs) flags = SS_NO_INIT var/list/elements_by_type = list() + // Update this if you add in components which actually use this as a processor + offline_implications = "This SS doesnt actually process anything yet. No immediate action is needed." /datum/controller/subsystem/processing/dcs/Recover() comp_lookup = SSdcs.comp_lookup diff --git a/code/controllers/subsystem/processing/fastprocess.dm b/code/controllers/subsystem/processing/fastprocess.dm index 9622e021469..37761ca8d60 100644 --- a/code/controllers/subsystem/processing/fastprocess.dm +++ b/code/controllers/subsystem/processing/fastprocess.dm @@ -4,3 +4,4 @@ PROCESSING_SUBSYSTEM_DEF(fastprocess) name = "Fast Processing" wait = 2 stat_tag = "FP" + offline_implications = "Objects using the 'Fast Processing' processor will no longer process. Shuttle call recommended." diff --git a/code/controllers/subsystem/processing/instruments.dm b/code/controllers/subsystem/processing/instruments.dm new file mode 100644 index 00000000000..3d571d2a13d --- /dev/null +++ b/code/controllers/subsystem/processing/instruments.dm @@ -0,0 +1,86 @@ +PROCESSING_SUBSYSTEM_DEF(instruments) + name = "Instruments" + init_order = INIT_ORDER_INSTRUMENTS + wait = 1 + flags = SS_TICKER|SS_BACKGROUND|SS_KEEP_TIMING + offline_implications = "Instruments will no longer play. No immediate action is needed." + + /// List of all instrument data, associative id = datum + var/list/datum/instrument/instrument_data + /// List of all song datums. + var/list/datum/song/songs + /// Max lines in songs + var/musician_maxlines = 600 + /// Max characters per line in songs + var/musician_maxlinechars = 300 + /// Deciseconds between hearchecks. Too high and instruments seem to lag when people are moving around in terms of who can hear it. Too low and the server lags from this. + var/musician_hearcheck_mindelay = 5 + /// Maximum instrument channels total instruments are allowed to use. This is so you don't have instruments deadlocking all sound channels. + var/max_instrument_channels = MAX_INSTRUMENT_CHANNELS + /// Current number of channels allocated for instruments + var/current_instrument_channels = 0 + /// Single cached list for synthesizer instrument ids, so you don't have to have a new list with every synthesizer. + var/list/synthesizer_instrument_ids + +/datum/controller/subsystem/processing/instruments/Initialize() + initialize_instrument_data() + synthesizer_instrument_ids = get_allowed_instrument_ids() + return ..() + +/** + * Initializes all instrument datums + */ +/datum/controller/subsystem/processing/instruments/proc/initialize_instrument_data() + instrument_data = list() + for(var/path in subtypesof(/datum/instrument)) + var/datum/instrument/I = path + if(initial(I.abstract_type) == path) + continue + I = new path + I.Initialize() + if(!I.id) + qdel(I) + continue + else + instrument_data[I.id] = I + CHECK_TICK + +/** + * Reserves a sound channel for a given instrument datum + * + * Arguments: + * * I - The instrument datum + */ +/datum/controller/subsystem/processing/instruments/proc/reserve_instrument_channel(datum/instrument/I) + if(current_instrument_channels > max_instrument_channels) + return + . = SSsounds.reserve_sound_channel(I) + if(!isnull(.)) + current_instrument_channels++ + +/** + * Called when a datum/song is created + * + * Arguments: + * * S - The created datum/song + */ +/datum/controller/subsystem/processing/instruments/proc/on_song_new(datum/song/S) + LAZYADD(songs, S) + +/** + * Called when a datum/song is deleted + * + * Arguments: + * * S - The deleted datum/song + */ +/datum/controller/subsystem/processing/instruments/proc/on_song_del(datum/song/S) + LAZYREMOVE(songs, S) + +/** + * Returns the instrument datum at the given ID or path + * + * Arguments: + * * id_or_path - The ID or path of the instrument + */ +/datum/controller/subsystem/processing/instruments/proc/get_instrument(id_or_path) + return instrument_data["[id_or_path]"] diff --git a/code/controllers/subsystem/processing/obj.dm b/code/controllers/subsystem/processing/obj.dm index 26021fb267a..2a05c04af58 100644 --- a/code/controllers/subsystem/processing/obj.dm +++ b/code/controllers/subsystem/processing/obj.dm @@ -3,3 +3,4 @@ PROCESSING_SUBSYSTEM_DEF(obj) priority = FIRE_PRIORITY_OBJ flags = SS_NO_INIT wait = 20 + offline_implications = "Objects using the 'Objects' processor will no longer process. Shuttle call recommended." diff --git a/code/controllers/subsystem/processing/processing.dm b/code/controllers/subsystem/processing/processing.dm index a8bc823bbbe..5302314589d 100644 --- a/code/controllers/subsystem/processing/processing.dm +++ b/code/controllers/subsystem/processing/processing.dm @@ -9,6 +9,7 @@ SUBSYSTEM_DEF(processing) var/stat_tag = "P" //Used for logging var/list/processing = list() var/list/currentrun = list() + offline_implications = "Objects using the default processor will no longer process. Shuttle call recommended." /datum/controller/subsystem/processing/stat_entry() ..("[stat_tag]:[processing.len]") diff --git a/code/controllers/subsystem/shuttles.dm b/code/controllers/subsystem/shuttles.dm index 247d71d05b8..c86f988bcd4 100644 --- a/code/controllers/subsystem/shuttles.dm +++ b/code/controllers/subsystem/shuttles.dm @@ -14,9 +14,9 @@ SUBSYSTEM_DEF(shuttle) //emergency shuttle stuff var/obj/docking_port/mobile/emergency/emergency var/obj/docking_port/mobile/emergency/backup/backup_shuttle - var/emergencyCallTime = 6000 //time taken for emergency shuttle to reach the station when called (in deciseconds) - var/emergencyDockTime = 1800 //time taken for emergency shuttle to leave again once it has docked (in deciseconds) - var/emergencyEscapeTime = 1200 //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds) + var/emergencyCallTime = SHUTTLE_CALLTIME //time taken for emergency shuttle to reach the station when called (in deciseconds) + var/emergencyDockTime = SHUTTLE_DOCKTIME //time taken for emergency shuttle to leave again once it has docked (in deciseconds) + var/emergencyEscapeTime = SHUTTLE_ESCAPETIME //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds) var/emergency_sec_level_time = 0 // time sec level was last raised to red or higher var/area/emergencyLastCallLoc var/emergencyNoEscape @@ -31,7 +31,7 @@ SUBSYSTEM_DEF(shuttle) var/points_per_intel = 250 //points gained per intel returned var/points_per_plasma = 5 //points gained per plasma returned var/points_per_design = 25 //points gained per research design returned - var/centcom_message = "" //Remarks from Centcom on how well you checked the last order. + var/centcom_message = null //Remarks from Centcom on how well you checked the last order. var/list/discoveredPlants = list() //Typepaths for unusual plants we've already sent CentComm, associated with their potencies var/list/techLevels = list() var/list/researchDesigns = list() @@ -61,6 +61,8 @@ SUBSYSTEM_DEF(shuttle) supply_packs["[P.type]"] = P initial_move() + centcom_message = "
---[station_time_timestamp()]---

Remember to stamp and send back the supply manifests.
" + return ..() /datum/controller/subsystem/shuttle/stat_entry(msg) @@ -93,6 +95,11 @@ SUBSYSTEM_DEF(shuttle) return S WARNING("couldn't find dock with id: [id]") +/datum/controller/subsystem/shuttle/proc/secondsToRefuel() + var/elapsed = world.time - SSticker.round_start_time + var/remaining = round((config.shuttle_refuel_delay - elapsed) / 10) + return remaining > 0 ? remaining : 0 + /datum/controller/subsystem/shuttle/proc/requestEvac(mob/user, call_reason) if(!emergency) WARNING("requestEvac(): There is no emergency shuttle, but the shuttle was called. Using the backup shuttle instead.") @@ -107,7 +114,7 @@ SUBSYSTEM_DEF(shuttle) return emergency = backup_shuttle - if(world.time - SSticker.round_start_time < config.shuttle_refuel_delay) + if(secondsToRefuel()) to_chat(user, "The emergency shuttle is refueling. Please wait another [abs(round(((world.time - SSticker.round_start_time) - config.shuttle_refuel_delay)/600))] minutes before trying again.") return @@ -131,7 +138,7 @@ SUBSYSTEM_DEF(shuttle) call_reason = trim(html_encode(call_reason)) if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH) - to_chat(user, "You must provide a reason.") + to_chat(user, "Reason is too short. [CALL_SHUTTLE_REASON_LENGTH] character minimum.") return var/area/signal_origin = get_area(user) @@ -192,7 +199,7 @@ SUBSYSTEM_DEF(shuttle) var/obj/machinery/computer/communications/C = thing if(C.stat & BROKEN) continue - else if(istype(thing, /datum/computer_file/program/comm) || istype(thing, /obj/item/circuitboard/communications)) + else if(istype(thing, /obj/item/circuitboard/communications)) continue var/turf/T = get_turf(thing) @@ -247,7 +254,7 @@ SUBSYSTEM_DEF(shuttle) /datum/controller/subsystem/shuttle/proc/generateSupplyOrder(packId, _orderedby, _orderedbyRank, _comment, _crates) if(!packId) return - var/datum/supply_packs/P = supply_packs["[packId]"] + var/datum/supply_packs/P = locateUID(packId) if(!P) return diff --git a/code/controllers/subsystem/sounds.dm b/code/controllers/subsystem/sounds.dm new file mode 100644 index 00000000000..33d97fcfe04 --- /dev/null +++ b/code/controllers/subsystem/sounds.dm @@ -0,0 +1,165 @@ +#define DATUMLESS "NO_DATUM" + +SUBSYSTEM_DEF(sounds) + name = "Sounds" + init_order = INIT_ORDER_SOUNDS + flags = SS_NO_FIRE + offline_implications = "Sounds may not play correctly. Shuttle call recommended." + + var/using_channels_max = CHANNEL_HIGHEST_AVAILABLE // BYOND max channels + /// Amount of channels to reserve for random usage rather than reservations being allowed to reserve all channels. Also a nice safeguard for when someone screws up. + var/random_channels_min = 50 + // Hey uh these two needs to be initialized fast because the whole "things get deleted before init" thing. + /// Assoc list, "[channel]" = either the datum using it or TRUE for an unsafe-reserved (datumless reservation) channel + var/list/using_channels + /// Assoc list datum = list(channel1, channel2, ...) for what channels something reserved. + var/list/using_channels_by_datum + // Special datastructure for fast channel management + /// List of all channels as numbers + var/list/channel_list + /// Associative list of all reserved channels associated to their position. "[channel_number]" = index as number + var/list/reserved_channels + /// lower iteration position - Incremented and looped to get "random" sound channels for normal sounds. The channel at this index is returned when asking for a random channel. + var/channel_random_low + /// higher reserve position - decremented and incremented to reserve sound channels, anything above this is reserved. The channel at this index is the highest unreserved channel. + var/channel_reserve_high + +/datum/controller/subsystem/sounds/Initialize() + setup_available_channels() + return ..() + +/** + * Sets up all available sound channels + */ +/datum/controller/subsystem/sounds/proc/setup_available_channels() + channel_list = list() + reserved_channels = list() + using_channels = list() + using_channels_by_datum = list() + for(var/i in 1 to using_channels_max) + channel_list += i + channel_random_low = 1 + channel_reserve_high = length(channel_list) + +/** + * Removes a channel from using list + * + * Arguments: + * * channel - The channel number + */ +/datum/controller/subsystem/sounds/proc/free_sound_channel(channel) + var/text_channel = num2text(channel) + var/using = using_channels[text_channel] + using_channels -= text_channel + if(!using) // datum channel + using_channels_by_datum[using] -= channel + if(!length(using_channels_by_datum[using])) + using_channels_by_datum -= using + free_channel(channel) + +/** + * Frees all the channels a datum is using + * + * Arguments: + * * D - The datum + */ +/datum/controller/subsystem/sounds/proc/free_datum_channels(datum/D) + var/list/L = using_channels_by_datum[D] + if(!L) + return + for(var/channel in L) + using_channels -= num2text(channel) + free_channel(channel) + using_channels_by_datum -= D + +/** + * Frees all datumless channels + */ +/datum/controller/subsystem/sounds/proc/free_datumless_channels() + free_datum_channels(DATUMLESS) + +/** + * NO AUTOMATIC CLEANUP - If you use this, you better manually free it later! + * + * Returns an integer for channel + */ +/datum/controller/subsystem/sounds/proc/reserve_sound_channel_datumless() + . = reserve_channel() + if(!.) // oh no.. + return FALSE + var/text_channel = num2text(.) + using_channels[text_channel] = DATUMLESS + LAZYADD(using_channels_by_datum[DATUMLESS], .) + +/** + * Reserves a channel for a datum. Automatic cleanup only when the datum is deleted. + * + * Returns an integer for channel + * Arguments: + * * D - The datum + */ +/datum/controller/subsystem/sounds/proc/reserve_sound_channel(datum/D) + if(!D) // i don't like typechecks but someone will fuck it up + CRASH("Attempted to reserve sound channel without datum using the managed proc.") + . = reserve_channel() + if(!.) + return FALSE + var/text_channel = num2text(.) + using_channels[text_channel] = D + LAZYADD(using_channels_by_datum[D], .) + +/** + * Reserves a channel and updates the datastructure. Private proc. + */ +/datum/controller/subsystem/sounds/proc/reserve_channel() + PRIVATE_PROC(TRUE) + if(channel_reserve_high <= random_channels_min) // out of channels + return + var/channel = channel_list[channel_reserve_high] + reserved_channels[num2text(channel)] = channel_reserve_high-- + return channel + +/** + * Frees a channel and updates the datastructure. Private proc. + */ +/datum/controller/subsystem/sounds/proc/free_channel(number) + PRIVATE_PROC(TRUE) + var/text_channel = num2text(number) + var/index = reserved_channels[text_channel] + if(!index) + CRASH("Attempted to (internally) free a channel that wasn't reserved.") + reserved_channels -= text_channel + // push reserve index up, which makes it now on a channel that is reserved + channel_reserve_high++ + // swap the reserved channel with the unreserved channel so the reserve index is now on an unoccupied channel and the freed channel is next to be used. + channel_list.Swap(channel_reserve_high, index) + // now, an existing reserved channel will likely (exception: unreserving last reserved channel) be at index + // get it, and update position. + var/text_reserved = num2text(channel_list[index]) + if(!reserved_channels[text_reserved]) // if it isn't already reserved make sure we don't accidently mistakenly put it on reserved list! + return + reserved_channels[text_reserved] = index + +/** + * Random available channel, returns text + */ +/datum/controller/subsystem/sounds/proc/random_available_channel_text() + if(channel_random_low > channel_reserve_high) + channel_random_low = 1 + . = "[channel_list[channel_random_low++]]" + +/** + * Random available channel, returns number + */ +/datum/controller/subsystem/sounds/proc/random_available_channel() + if(channel_random_low > channel_reserve_high) + channel_random_low = 1 + . = channel_list[channel_random_low++] + +/** + * How many channels we have left + */ +/datum/controller/subsystem/sounds/proc/available_channels_left() + return length(channel_list) - random_channels_min + +#undef DATUMLESS diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm index c4240021137..5c4c6ec6ff3 100644 --- a/code/controllers/subsystem/tgui.dm +++ b/code/controllers/subsystem/tgui.dm @@ -126,6 +126,8 @@ SUBSYSTEM_DEF(tgui) * return int The number of UIs closed. **/ /datum/controller/subsystem/tgui/proc/close_uis(datum/src_object) + if(!src_object.unique_datum_id) // First check if the datum has an UID set + return 0 var/src_object_key = "[src_object.UID()]" if(isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) return 0 // Couldn't find any UIs for this object. diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 5d0a53c221e..3a1ba7595c0 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -61,10 +61,12 @@ SUBSYSTEM_DEF(ticker) if(GAME_STATE_STARTUP) // This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails round_start_time = world.time + (config.pregame_timestart * 10) - to_chat(world, "Welcome to the pre-game lobby!") + to_chat(world, "Welcome to the pre-game lobby!") to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds") current_state = GAME_STATE_PREGAME fire() // TG says this is a good idea + for(var/mob/new_player/N in GLOB.player_list) + N.new_player_panel_proc() // to enable the observe option if(GAME_STATE_PREGAME) if(!SSticker.ticker_going) // This has to be referenced like this, and I dont know why. If you dont put SSticker. it will break return @@ -217,11 +219,11 @@ SUBSYSTEM_DEF(ticker) for(var/obj/effect/landmark/spacepod/random/R in L) qdel(R) - to_chat(world, "Enjoy the game!") + to_chat(world, "Enjoy the game!") world << sound('sound/AI/welcome.ogg')// Skie if(SSholiday.holidays) - to_chat(world, "and...") + to_chat(world, "and...") for(var/holidayname in SSholiday.holidays) var/datum/holiday/holiday = SSholiday.holidays[holidayname] to_chat(world, "

[holiday.greet()]

") diff --git a/code/controllers/subsystem/tickets/mentor_tickets.dm b/code/controllers/subsystem/tickets/mentor_tickets.dm index 76c7ed2dc28..706a498aa64 100644 --- a/code/controllers/subsystem/tickets/mentor_tickets.dm +++ b/code/controllers/subsystem/tickets/mentor_tickets.dm @@ -6,16 +6,29 @@ GLOBAL_REAL(SSmentor_tickets, /datum/controller/subsystem/tickets/mentor_tickets /datum/controller/subsystem/tickets/mentor_tickets name = "Mentor Tickets" + offline_implications = "Mentor tickets will no longer be marked as stale. No immediate action is needed." ticket_system_name = "Mentor Tickets" ticket_name = "Mentor Ticket" span_class = "mentorhelp" + anchor_link_extra = ";is_mhelp=1" + ticket_help_type = "Mentorhelp" + ticket_help_span = "mentorhelp" + other_ticket_name = "Admin" + other_ticket_permission = R_ADMIN close_rights = R_MENTOR | R_ADMIN - -/datum/controller/subsystem/tickets/mentor_tickets/message_staff(var/msg) - message_mentorTicket(msg) + rights_needed = R_MENTOR | R_ADMIN | R_MOD /datum/controller/subsystem/tickets/mentor_tickets/Initialize() close_messages = list("- [ticket_name] Closed -", "Please try to be as descriptive as possible in mentor helps. Mentors do not know the full situation you're in and need more information to give you a helpful response.", "Your [ticket_name] has now been closed.") return ..() + +/datum/controller/subsystem/tickets/mentor_tickets/message_staff(msg, prefix_type = NONE, important = FALSE) + message_mentorTicket(msg, important) + +/datum/controller/subsystem/tickets/mentor_tickets/create_other_system_ticket(datum/ticket/T) + SStickets.newTicket(get_client_by_ckey(T.client_ckey), T.content, T.raw_title) + +/datum/controller/subsystem/tickets/mentor_tickets/autoRespond(N) + return diff --git a/code/controllers/subsystem/tickets/tickets.dm b/code/controllers/subsystem/tickets/tickets.dm index 3aa0a8846c3..30c4c13166c 100644 --- a/code/controllers/subsystem/tickets/tickets.dm +++ b/code/controllers/subsystem/tickets/tickets.dm @@ -10,25 +10,40 @@ #define TICKET_RESOLVED 3 #define TICKET_STALE 4 +#define TICKET_STAFF_MESSAGE_ADMIN_CHANNEL 1 +#define TICKET_STAFF_MESSAGE_PREFIX 2 + SUBSYSTEM_DEF(tickets) name = "Admin Tickets" + init_order = INIT_ORDER_TICKETS + wait = 300 + priority = FIRE_PRIORITY_TICKETS + offline_implications = "Admin tickets will no longer be marked as stale. No immediate action is needed." + flags = SS_BACKGROUND + var/span_class = "adminticket" var/ticket_system_name = "Admin Tickets" var/ticket_name = "Admin Ticket" var/close_rights = R_ADMIN + var/rights_needed = R_ADMIN | R_MOD + + /// Text that will be added to the anchor link + var/anchor_link_extra = "" + + var/ticket_help_type = "Adminhelp" + var/ticket_help_span = "adminhelp" + /// The name of the other ticket type to convert to + var/other_ticket_name = "Mentor" + /// Which permission to look for when seeing if there is staff available for the other ticket type + var/other_ticket_permission = R_MENTOR var/list/close_messages - init_order = INIT_ORDER_TICKETS - wait = 300 - priority = FIRE_PRIORITY_TICKETS - - flags = SS_BACKGROUND - var/list/allTickets = list() //make it here because someone might ahelp before the system has initialized var/ticketCounter = 1 /datum/controller/subsystem/tickets/Initialize() - close_messages = list("- [ticket_name] Rejected! -", + if(!close_messages) + close_messages = list("- [ticket_name] Rejected! -", "Please try to be calm, clear, and descriptive in admin helps, do not assume the staff member has seen any related events, and clearly state the names of anybody you are reporting. If you asked a question, please ensure it was clear what you were asking.", "Your [ticket_name] has now been closed.") return ..() @@ -70,24 +85,58 @@ SUBSYSTEM_DEF(tickets) var/datum/ticket/T = i resolveTicket(T.ticketNum) +/** + * Will either make a new ticket using the given text or will add the text to an existing ticket. + * Staff will get a message + * Arguments: + * C - The client who requests help + * text - The text the client send + */ +/datum/controller/subsystem/tickets/proc/newHelpRequest(client/C, text) + var/ticketNum // Holder for the ticket number + var/datum/ticket/T + // Get the open ticket assigned to the client and add a response. If no open tickets then make a new one + if((T = checkForOpenTicket(C))) + ticketNum = T.ticketNum + T.addResponse(C, text) + T.setCooldownPeriod() + to_chat(C.mob, "Your [ticket_name] #[ticketNum] remains open! Visit \"My tickets\" under the Admin Tab to view it.") + var/url_message = makeUrlMessage(C, text, ticketNum) + message_staff(url_message, NONE, TRUE) + else + newTicket(C, text, text) + +/** + * Will add the URLs usable by staff to the message and return it + * Arguments: + * C - The client who send the message + * msg - The raw message + * ticketNum - Which ticket number the ticket has + */ +/datum/controller/subsystem/tickets/proc/makeUrlMessage(client/C, msg, ticketNum) + var/list/L = list() + L += "[ticket_help_type]: [key_name(C, TRUE, ticket_help_type)] " + L += "([ADMIN_QUE(C.mob,"?")]) ([ADMIN_PP(C.mob,"PP")]) ([ADMIN_VV(C.mob,"VV")]) ([ADMIN_TP(C.mob,"TP")]) ([ADMIN_SM(C.mob,"SM")]) " + L += "([admin_jump_link(C.mob)]) (TICKET) " + L += "[isAI(C.mob) ? "(CL)" : ""] (TAKE) " + L += "(RESOLVE) (AUTO) " + L += "(CONVERT) : [msg]" + return L.Join() + //Open a new ticket and populate details then add to the list of open tickets /datum/controller/subsystem/tickets/proc/newTicket(client/C, passedContent, title) if(!C || !passedContent) return - //Check if the user has an open ticket already within the cooldown period, if so we don't create a new one and re-set the cooldown period - var/datum/ticket/existingTicket = checkForOpenTicket(C) - if(existingTicket) - existingTicket.setCooldownPeriod() - to_chat(C.mob, "Your [ticket_name] #[existingTicket.ticketNum] remains open! Visit \"My tickets\" under the Admin Tab to view it.") - return - if(!title) title = passedContent - var/datum/ticket/T = new(title, passedContent, getTicketCounterAndInc()) + var/new_ticket_num = getTicketCounterAndInc() + var/url_title = makeUrlMessage(C, title, new_ticket_num) + + var/datum/ticket/T = new(url_title, title, passedContent, new_ticket_num) allTickets += T - T.clientName = C + T.client_ckey = C.ckey T.locationSent = C.mob.loc.name T.mobControlled = C.mob @@ -96,6 +145,8 @@ SUBSYSTEM_DEF(tickets) var/ticket_open_sound = sound('sound/effects/adminticketopen.ogg') SEND_SOUND(C, ticket_open_sound) + message_staff(url_title, NONE, TRUE) + //Set ticket state with key N to open /datum/controller/subsystem/tickets/proc/openTicket(N) var/datum/ticket/T = allTickets[N] @@ -113,9 +164,39 @@ SUBSYSTEM_DEF(tickets) to_chat_safe(returnClient(N), "Your [ticket_name] has now been resolved.") return TRUE +/datum/controller/subsystem/tickets/proc/convert_to_other_ticket(ticketId) + if(!check_rights(rights_needed)) + return + if(alert("Are you sure to convert this ticket to an '[other_ticket_name]' ticket?",,"Yes","No") != "Yes") + return + if(!other_ticket_system_staff_check()) + return + var/datum/ticket/T = allTickets[ticketId] + convert_ticket(T) + +/datum/controller/subsystem/tickets/proc/other_ticket_system_staff_check() + var/list/staff = staff_countup(other_ticket_permission) + if(!staff[1]) + if(alert("No active staff online to answer the ticket. Are you sure you want to convert the ticket?",, "No", "Yes") != "Yes") + return FALSE + return TRUE + +/datum/controller/subsystem/tickets/proc/convert_ticket(datum/ticket/T) + T.ticketState = TICKET_CLOSED + var/client/C = usr.client + var/client/owner = get_client_by_ckey(T.client_ckey) + to_chat_safe(owner, list("[key_name_hidden(C)] has converted your ticket to a [other_ticket_name] ticket.",\ + "Be sure to use the correct type of help next time!")) + message_staff("[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.") + log_game("[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.") + create_other_system_ticket(T) + +/datum/controller/subsystem/tickets/proc/create_other_system_ticket(datum/ticket/T) + var/client/C = get_client_by_ckey(T.client_ckey) + SSmentor_tickets.newTicket(C, T.content, T.raw_title) /datum/controller/subsystem/tickets/proc/autoRespond(N) - if(!check_rights(R_ADMIN|R_MOD)) + if(!check_rights(rights_needed)) return var/datum/ticket/T = allTickets[N] @@ -130,6 +211,7 @@ SUBSYSTEM_DEF(tickets) "Already Resolved" = "The problem has been resolved already.", "Mentorhelp" = "Please redirect your question to Mentorhelp, as they are better experienced with these types of questions.", "Happens Again" = "Thanks, let us know if it continues to happen.", + "Github Issue Report" = "To report a bug, please go to our Github page. Then go to 'Issues'. Then 'New Issue'. Then fill out the report form. If the report would reveal current-round information, file it after the round ends.", "Clear Cache" = "To fix a blank screen, go to the 'Special Verbs' tab and press 'Reload UI Resources'. If that fails, clear your BYOND cache (instructions provided with 'Reload UI Resources'). If that still fails, please adminhelp again, stating you have already done the following." , "IC Issue" = "This is an In Character (IC) issue and will not be handled by admins. You could speak to Security, Internal Affairs, a Departmental Head, Nanotrasen Representetive, or any other relevant authority currently on station.", "Reject" = "Reject", @@ -142,7 +224,7 @@ SUBSYSTEM_DEF(tickets) sorted_responses += key var/message_key = input("Select an autoresponse. This will mark the ticket as resolved.", "Autoresponse") as null|anything in sortTim(sorted_responses, /proc/cmp_text_asc) //use sortTim and cmp_text_asc to sort alphabetically - + var/client/ticket_owner = get_client_by_ckey(T.client_ckey) switch(message_key) if(null) //they cancelled T.staffAssigned = initial(T.staffAssigned) //if they cancel we dont need to hold this ticket anymore @@ -154,16 +236,19 @@ SUBSYSTEM_DEF(tickets) C.man_up(returnClient(N)) T.lastStaffResponse = "Autoresponse: [message_key]" resolveTicket(N) - message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with: [message_key] ") - log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]") + message_staff("[C] has auto responded to [ticket_owner]\'s adminhelp with: [message_key] ") + log_game("[C] has auto responded to [ticket_owner]\'s adminhelp with: [response_phrases[message_key]]") + if("Mentorhelp") + convert_ticket(T) else var/msg_sound = sound('sound/effects/adminhelp.ogg') SEND_SOUND(returnClient(N), msg_sound) - to_chat(returnClient(N), "[key_name_hidden(C)] is autoresponding with: [response_phrases[message_key]]")//for this we want the full value of whatever key this is to tell the player so we do response_phrases[message_key] - message_staff("[C] has auto responded to [T.clientName]\'s adminhelp with: [message_key] ") //we want to use the short named keys for this instead of the full sentence which is why we just do message_key + to_chat_safe(returnClient(N), "[key_name_hidden(C)] is autoresponding with: [response_phrases[message_key]]")//for this we want the full value of whatever key this is to tell the player so we do response_phrases[message_key] + message_staff("[C] has auto responded to [ticket_owner]\'s adminhelp with: [message_key] ") //we want to use the short named keys for this instead of the full sentence which is why we just do message_key T.lastStaffResponse = "Autoresponse: [message_key]" resolveTicket(N) - log_game("[C] has auto responded to [T.clientName]\'s adminhelp with: [response_phrases[message_key]]") + log_game("[C] has auto responded to [ticket_owner]\'s adminhelp with: [response_phrases[message_key]]") + //Set ticket state with key N to closed /datum/controller/subsystem/tickets/proc/closeTicket(N) var/datum/ticket/T = allTickets[N] @@ -176,7 +261,7 @@ SUBSYSTEM_DEF(tickets) //Check if the user already has a ticket open and within the cooldown period. /datum/controller/subsystem/tickets/proc/checkForOpenTicket(client/C) for(var/datum/ticket/T in allTickets) - if(T.clientName == C && T.ticketState == TICKET_OPEN && (T.ticketCooldown > world.time)) + if(T.client_ckey == C.ckey && T.ticketState == TICKET_OPEN && (T.ticketCooldown > world.time)) return T return FALSE @@ -184,7 +269,7 @@ SUBSYSTEM_DEF(tickets) /datum/controller/subsystem/tickets/proc/checkForTicket(client/C) var/list/tickets = list() for(var/datum/ticket/T in allTickets) - if(T.clientName == C && (T.ticketState == TICKET_OPEN || T.ticketState == TICKET_STALE)) + if(T.client_ckey == C.ckey && (T.ticketState == TICKET_OPEN || T.ticketState == TICKET_STALE)) tickets += T if(tickets.len) return tickets @@ -193,7 +278,7 @@ SUBSYSTEM_DEF(tickets) //return the client of a ticket number /datum/controller/subsystem/tickets/proc/returnClient(N) var/datum/ticket/T = allTickets[N] - return T.clientName + return get_client_by_ckey(T.client_ckey) /datum/controller/subsystem/tickets/proc/assignStaffToTicket(client/C, N) var/datum/ticket/T = allTickets[N] @@ -206,9 +291,11 @@ SUBSYSTEM_DEF(tickets) /datum/ticket var/ticketNum // Ticket number - var/clientName // Client which opened the ticket + /// ckey of the client who opened the ticket + var/client_ckey var/timeOpened // Time the ticket was opened var/title //The initial message with links + var/raw_title // The title without URLs added var/list/content // content of the staff help var/lastStaffResponse // Last staff member who responded var/lastResponseTime // When the staff last responded @@ -219,8 +306,9 @@ SUBSYSTEM_DEF(tickets) var/ticketCooldown // Cooldown before allowing the user to open another ticket. var/client/staffAssigned // Staff member who has assigned themselves to this ticket -/datum/ticket/New(tit, cont, num) +/datum/ticket/New(tit, raw_tit, cont, num) title = tit + raw_title = raw_tit content = list() content += cont timeOpened = worldtime2text() @@ -341,7 +429,7 @@ UI STUFF dat += "

Ticket #[T.ticketNum]

" - dat += "

[T.clientName] / [T.mobControlled] opened this [ticket_name] at [T.timeOpened] at location [T.locationSent]

" + dat += "

[T.client_ckey] / [T.mobControlled] opened this [ticket_name] at [T.timeOpened] at location [T.locationSent]

" dat += "

Ticket Status: [status]" dat += "" dat += "" @@ -351,7 +439,7 @@ UI STUFF dat += "" dat += "
[T.title]
[T.content[i]]


" - dat += "Re-Open[check_rights(R_ADMIN|R_MOD, 0) ? "Auto": ""]Resolve

" + dat += "Re-Open[check_rights(rights_needed, 0) ? "Auto": ""]Resolve

" if(!T.staffAssigned) dat += "No staff member assigned to this [ticket_name] - Take Ticket
" @@ -366,6 +454,7 @@ UI STUFF dat += "

" dat += "Close Ticket" + dat += "Convert Ticket" var/datum/browser/popup = new(user, "[ticket_system_name]detail", "[ticket_system_name] #[T.ticketNum]", 1000, 600) popup.set_content(dat) @@ -398,9 +487,21 @@ UI STUFF to_chat(target, text) return TRUE -//Sends a message to the designated staff -/datum/controller/subsystem/tickets/proc/message_staff(var/msg, var/alt = FALSE) - message_adminTicket(msg, alt) +/** + * Sends a message to the designated staff + * Arguments: + * msg - The message being send + * alt - If an alternative prefix should be used or not. Defaults to TICKET_STAFF_MESSAGE_PREFIX + * important - If the message is important. If TRUE it will ignore the CHAT_NO_TICKETLOGS preferences, + send a sound and flash the window. Defaults to FALSE + */ +/datum/controller/subsystem/tickets/proc/message_staff(msg, prefix_type = TICKET_STAFF_MESSAGE_PREFIX, important = FALSE) + switch(prefix_type) + if(TICKET_STAFF_MESSAGE_ADMIN_CHANNEL) + msg = "ADMIN TICKET: [msg]" + if(TICKET_STAFF_MESSAGE_PREFIX) + msg = "ADMIN TICKET: [msg]" + message_adminTicket(msg, important) /datum/controller/subsystem/tickets/Topic(href, href_list) @@ -448,7 +549,6 @@ UI STUFF if(closeTicket(indexNum)) showDetailUI(usr, indexNum) - if(href_list["detailreopen"]) var/indexNum = text2num(href_list["detailreopen"]) if(openTicket(indexNum)) @@ -468,6 +568,10 @@ UI STUFF var/indexNum = text2num(href_list["autorespond"]) autoRespond(indexNum) + if(href_list["convert_ticket"]) + var/indexNum = text2num(href_list["convert_ticket"]) + convert_to_other_ticket(indexNum) + if(href_list["resolveall"]) if(ticket_system_name == "Mentor Tickets") usr.client.resolveAllMentorTickets() @@ -479,7 +583,7 @@ UI STUFF if(span_class == "mentorhelp") message_staff("[usr.client] / ([usr]) has taken [ticket_name] number [index]") else - message_staff("[usr.client] / ([usr]) has taken [ticket_name] number [index]", TRUE) + message_staff("[usr.client] / ([usr]) has taken [ticket_name] number [index]", TICKET_STAFF_MESSAGE_ADMIN_CHANNEL) to_chat_safe(returnClient(index), "Your [ticket_name] is being handled by [usr.client].") /datum/controller/subsystem/tickets/proc/unassignTicket(index) @@ -490,4 +594,7 @@ UI STUFF if(span_class == "mentorhelp") message_staff("[usr.client] / ([usr]) has unassigned [ticket_name] number [index]") else - message_staff("[usr.client] / ([usr]) has unassigned [ticket_name] number [index]", TRUE) + message_staff("[usr.client] / ([usr]) has unassigned [ticket_name] number [index]", TICKET_STAFF_MESSAGE_ADMIN_CHANNEL) + +#undef TICKET_STAFF_MESSAGE_ADMIN_CHANNEL +#undef TICKET_STAFF_MESSAGE_PREFIX diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index b0110258da7..0b91e6b3467 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -367,7 +367,7 @@ SUBSYSTEM_DEF(vote) var/votedesc = capitalize(mode) if(mode == "custom") votedesc += " ([question])" - admin_log_and_message_admins("cancelled the running [votedesc] vote.") + log_and_message_admins("cancelled the running [votedesc] vote.") reset() if("toggle_restart") if(admin) diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 3eaed85c303..8d02529ae6b 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -7,7 +7,7 @@ set name = "Restart Controller" set desc = "Restart one of the various periodic loop controllers for the game (be careful!)" - if(!holder) + if(!check_rights(R_DEBUG)) return switch(controller) if("Master") @@ -20,13 +20,14 @@ message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") /client/proc/debug_controller(controller in list("failsafe", "Master", "Ticker", "Air", "Jobs", "Sun", "Radio", "Configuration", "pAI", - "Cameras", "Garbage", "Event", "Alarm", "Nano", "Vote", "Fires", + "Cameras", "Garbage", "Event", "Nano", "Vote", "Fires", "Mob", "NPC Pool", "Shuttle", "Timer", "Weather", "Space", "Mob Hunt Server","Input")) set category = "Debug" set name = "Debug Controller" set desc = "Debug the various periodic loop controllers for the game (be careful!)" - if(!holder) return + if(!check_rights(R_DEBUG)) + return switch(controller) if("failsafe") debug_variables(Failsafe) @@ -64,9 +65,6 @@ if("Event") debug_variables(SSevents) feedback_add_details("admin_verb","DEvent") - if("Alarm") - debug_variables(SSalarms) - feedback_add_details("admin_verb", "DAlarm") if("Nano") debug_variables(SSnanoui) feedback_add_details("admin_verb","DNano") diff --git a/code/datums/action.dm b/code/datums/action.dm index c1802da47c1..84702757e86 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -162,6 +162,14 @@ /datum/action/item_action/print_report name = "Print Report" +/datum/action/item_action/print_forensic_report + name = "Print Report" + button_icon_state = "scanner_print" + use_itemicon = FALSE + +/datum/action/item_action/clear_records + name = "Clear Scanner Records" + /datum/action/item_action/toggle_gunlight name = "Toggle Gunlight" @@ -190,9 +198,6 @@ /datum/action/item_action/toggle_mister name = "Toggle Mister" -/datum/action/item_action/toggle_headphones - name = "Toggle Headphones" - /datum/action/item_action/toggle_helmet_light name = "Toggle Helmet Light" @@ -232,19 +237,6 @@ button.name = name ..() -/datum/action/item_action/synthswitch - name = "Change Synthesizer Instrument" - desc = "Change the type of instrument your synthesizer is playing as." - -/datum/action/item_action/synthswitch/Trigger() - if(istype(target, /obj/item/instrument/piano_synth)) - var/obj/item/instrument/piano_synth/synth = target - var/chosen = input("Choose the type of instrument you want to use", "Instrument Selection", "piano") as null|anything in synth.insTypes - if(!synth.insTypes[chosen]) - return - return synth.changeInstrument(chosen) - return ..() - /datum/action/item_action/vortex_recall name = "Vortex Recall" desc = "Recall yourself, and anyone nearby, to an attuned hierophant beacon at any time.
If the beacon is still attached, will detach it." @@ -257,6 +249,9 @@ return 0 return ..() +/datum/action/item_action/change_headphones_song + name = "Change Headphones Song" + /datum/action/item_action/toggle /datum/action/item_action/toggle/New(Target) @@ -463,6 +458,7 @@ /datum/action/spell_action check_flags = 0 background_icon_state = "bg_spell" + var/recharge_text_color = "#FFFFFF" /datum/action/spell_action/New(Target) ..() @@ -500,6 +496,18 @@ return spell.can_cast(owner) return 0 +/datum/action/spell_action/UpdateButtonIcon() + if(button && !(. = ..())) + var/obj/effect/proc_holder/spell/S = target + if(!istype(S)) + return + var/progress = S.get_availability_percentage() + var/col_val_high = 72 * progress + 128 + var/col_val_low = 200 * progress + button.maptext = "
[round_down(progress * 100)]%
" + button.color = rgb(col_val_high, col_val_low, col_val_low, col_val_high) + else + button.maptext = null /* /datum/action/spell_action/alien diff --git a/code/datums/beam.dm b/code/datums/beam.dm index 19935bda130..3b5c9d2f5fb 100644 --- a/code/datums/beam.dm +++ b/code/datums/beam.dm @@ -131,6 +131,5 @@ /atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=50, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=3) var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type,beam_sleep_time) - spawn(0) - newbeam.Start() + INVOKE_ASYNC(newbeam, /datum/beam.proc/Start) return newbeam diff --git a/code/datums/cache/air_alarm.dm b/code/datums/cache/air_alarm.dm index 2edc0792a34..fd9e529d70a 100644 --- a/code/datums/cache/air_alarm.dm +++ b/code/datums/cache/air_alarm.dm @@ -1,3 +1,5 @@ +#define AIR_ALARM_DATA_CACHE_DURATION 10 SECONDS + GLOBAL_DATUM_INIT(air_alarm_repository, /datum/repository/air_alarm, new()) /datum/repository/air_alarm/proc/air_alarm_data(var/list/monitored_alarms, var/refresh = 0, var/obj/machinery/alarm/passed_alarm) @@ -8,7 +10,7 @@ GLOBAL_DATUM_INIT(air_alarm_repository, /datum/repository/air_alarm, new()) cache_entry = new/datum/cache_entry cache_data = cache_entry - if(!refresh) + if(!refresh && cache_entry.timestamp + AIR_ALARM_DATA_CACHE_DURATION > world.time) return cache_entry.data if(SSticker && SSticker.current_state < GAME_STATE_PLAYING && istype(passed_alarm)) // Generating the list for the first time as the game hasn't started - no need to run through the machines list everything every time @@ -29,3 +31,5 @@ GLOBAL_DATUM_INIT(air_alarm_repository, /datum/repository/air_alarm, new()) /datum/repository/air_alarm/proc/update_cache(var/obj/machinery/alarm/alarm) return air_alarm_data(refresh = 1, passed_alarm = alarm) + +#undef AIR_ALARM_DATA_CACHE_DURATION diff --git a/code/datums/cache/crew.dm b/code/datums/cache/crew.dm index f92bab9cf7a..84308dd4262 100644 --- a/code/datums/cache/crew.dm +++ b/code/datums/cache/crew.dm @@ -1,5 +1,8 @@ GLOBAL_DATUM_INIT(crew_repository, /datum/repository/crew, new()) +/datum/repository/crew + var/static/list/bold_jobs + /datum/repository/crew/New() cache_data = list() ..() @@ -18,6 +21,13 @@ GLOBAL_DATUM_INIT(crew_repository, /datum/repository/crew, new()) if(world.time < cache_entry.timestamp) return cache_entry.data + // Initialize the jobs here because in New(), GLOB.command_positions may not be inited yet + if(!bold_jobs) + bold_jobs = list() + bold_jobs += GLOB.command_positions + bold_jobs += get_all_centcom_jobs() + bold_jobs += list("Nanotrasen Representative", "Blueshield", "Magistrate") + for(var/thing in GLOB.human_list) var/mob/living/carbon/human/H = thing var/obj/item/clothing/under/C = H.w_uniform @@ -32,11 +42,13 @@ GLOBAL_DATUM_INIT(crew_repository, /datum/repository/crew, new()) 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") + crewmemberData["is_command"] = (crewmemberData["assignment"] in bold_jobs) if(C.sensor_mode >= SUIT_SENSOR_BINARY) - crewmemberData["dead"] = H.stat > UNCONSCIOUS + crewmemberData["dead"] = H.stat == DEAD if(C.sensor_mode >= SUIT_SENSOR_VITAL) + crewmemberData["stat"] = H.stat crewmemberData["oxy"] = round(H.getOxyLoss(), 1) crewmemberData["tox"] = round(H.getToxLoss(), 1) crewmemberData["fire"] = round(H.getFireLoss(), 1) diff --git a/code/datums/cache/powermonitor.dm b/code/datums/cache/powermonitor.dm index 606dd500eda..a11e9b76a41 100644 --- a/code/datums/cache/powermonitor.dm +++ b/code/datums/cache/powermonitor.dm @@ -13,7 +13,7 @@ GLOBAL_DATUM_INIT(powermonitor_repository, /datum/repository/powermonitor, new() for(var/obj/machinery/computer/monitor/pMon in GLOB.power_monitors) if( !(pMon.stat & (NOPOWER|BROKEN)) && !pMon.is_secret_monitor ) - pMonData[++pMonData.len] = list ("Name" = pMon.name, "ref" = "\ref[pMon]") + pMonData[++pMonData.len] = list ("Name" = pMon.name, "uid" = "[pMon.UID()]") cache_entry.timestamp = world.time //+ 30 SECONDS cache_entry.data = pMonData diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm index 3bd740973a3..fc781b288a9 100644 --- a/code/datums/components/caltrop.dm +++ b/code/datums/components/caltrop.dm @@ -42,7 +42,7 @@ if(!(flags & CALTROP_BYPASS_SHOES) && (H.shoes || feetCover)) return - if((H.flying) || H.buckled) + if(H.flying || H.floating || H.buckled) return var/damage = rand(min_damage, max_damage) diff --git a/code/datums/components/ducttape.dm b/code/datums/components/ducttape.dm index 49931db8edc..b41066d375d 100644 --- a/code/datums/components/ducttape.dm +++ b/code/datums/components/ducttape.dm @@ -47,23 +47,30 @@ return if(!isturf(target)) return - if(!user.unEquip(I)) - return var/turf/source_turf = get_turf(I) var/turf/target_turf = target - var/list/clickparams = params2list(params) - var/x_offset = text2num(clickparams["icon-x"]) - 16 - var/y_offset = text2num(clickparams["icon-y"]) - 16 + var/x_offset + var/y_offset if(target_turf != get_turf(I)) //Trying to stick it on a wall, don't move it to the actual wall or you can move the item through it. Instead set the pixels as appropriate var/target_direction = get_dir(source_turf, target_turf)//The direction we clicked + // Snowflake diagonal handling + if(target_direction in GLOB.diagonals) + to_chat(user, "You cant reach [target_turf].") + return if(target_direction & EAST) - x_offset += 32 + x_offset = 16 + y_offset = rand(-12, 12) else if(target_direction & WEST) - x_offset -= 32 - if(target_direction & NORTH) - y_offset += 32 + x_offset = -16 + y_offset = rand(-12, 12) + else if(target_direction & NORTH) + x_offset = rand(-12, 12) + y_offset = 16 else if(target_direction & SOUTH) - y_offset -= 32 + x_offset = rand(-12, 12) + y_offset = -16 + if(!user.unEquip(I)) + return to_chat(user, "You stick [I] to [target_turf].") I.pixel_x = x_offset I.pixel_y = y_offset diff --git a/code/datums/components/proximity_monitor.dm b/code/datums/components/proximity_monitor.dm new file mode 100644 index 00000000000..cf5de38b387 --- /dev/null +++ b/code/datums/components/proximity_monitor.dm @@ -0,0 +1,139 @@ +/** + * # Proximity monitor component + * + * Attaching this component to an atom means that the atom will be able to detect mobs/objs moving within a 1 tile of it. + * + * The component creates several `obj/effect/abstract/proximity_checker` objects, which follow the parent atom around, always making sure it's at the center. + * When something crosses one of these `proximiy_checker`s, the parent has the `HasProximity()` proc called on it, with the crossing mob/obj as the argument. + */ +/datum/component/proximity_monitor + var/atom/owner + /// A list of currently created `/obj/effect/abstract/proximity_checker`s in use with this component. + var/list/proximity_checkers + +/datum/component/proximity_monitor/Initialize() + . = ..() + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + owner = parent + create_prox_checkers() + +/datum/component/proximity_monitor/Destroy(force, silent) + QDEL_LIST(proximity_checkers) + owner = null + return ..() + +/datum/component/proximity_monitor/RegisterWithParent() + . = ..() + if(ismovable(parent)) + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/HandleMove) + +/datum/component/proximity_monitor/UnregisterFromParent() + . = ..() + if(ismovable(parent)) + UnregisterSignal(parent, COMSIG_MOVABLE_MOVED) + +/** + * Called when the `parent` receives the `COMSIG_MOVABLE_MOVED` signal, which occurs when it `Move()`s + * + * Code is only ran when there is no `Dir`, which occurs when the parent is teleported, gets placed into a storage item, dropped, or picked up. + * Normal movement, for example moving 1 tile to the west, is handled by the `proximity_checker` objects. + * + * Arguments: + * * source - this will be the `parent` + * * OldLoc - the location the parent just moved from + * * Dir - the direction the parent just moved in + * * forced - if we were forced to move + */ +/datum/component/proximity_monitor/proc/HandleMove(datum/source, atom/OldLoc, Dir, forced) + if(!Dir) // No dir means the parent teleported, or moved in a non-standard way like getting placed into disposals, onto a table, dropped, picked up, etc. + recenter_prox_checkers() + +/** + * Called in Initialize(). Generates a set of `/obj/effect/abstract/proximity_checker` objects around the parent, and registers signals to them. + */ +/datum/component/proximity_monitor/proc/create_prox_checkers() + proximity_checkers = list() + for(var/turf/T in range(1, get_turf(parent))) + var/obj/effect/abstract/proximity_checker/P = new(T, parent) + proximity_checkers += P + // Basic movement for the proximity_checker objects. The objects will move 1 tile in the direction the parent just moved. + P.RegisterSignal(parent, COMSIG_MOVABLE_MOVED, /obj/effect/abstract/proximity_checker/.proc/HandleMove) + +/** + * Re-centers all of the parent's `proximity_checker`s around its current location. + */ +/datum/component/proximity_monitor/proc/recenter_prox_checkers() + var/list/prox_checkers = owner.get_all_adjacent_turfs() + for(var/checker in proximity_checkers) + var/obj/effect/abstract/proximity_checker/P = checker + P.loc = pick_n_take(prox_checkers) + +/** + * # Proximity checker abstract object + * + * Inteded for use with the proximity checker component (/datum/component/proximity_monitor). + * Whenever a movable atom crosses this object, it calls `HasProximity()` on the object which is listening for proximity (`hasprox_receiver`). + */ +/obj/effect/abstract/proximity_checker + name = "Proximity checker" + /// Whether or not the proximity checker is listening for things crossing it. + var/active + /// The linked atom which has the proximity_monitor component, and will recieve the `HasProximity()` calls. + var/atom/hasprox_receiver + +// If this object is initialized without a `_hasprox_receiver` arg, it is qdel'd. +/obj/effect/abstract/proximity_checker/Initialize(mapload, atom/_hasprox_receiver) + if(_hasprox_receiver) + hasprox_receiver = _hasprox_receiver + RegisterSignal(hasprox_receiver, COMSIG_PARENT_QDELETING, .proc/OnParentDeletion) + if(isturf(hasprox_receiver.loc)) // if the reciever is inside a locker/crate/etc, they don't detect proximity + active = TRUE + else + stack_trace("/obj/effect/abstract/proximity_checker created without a receiver") + return INITIALIZE_HINT_QDEL + return ..() + +/obj/effect/abstract/proximity_checker/Destroy() + hasprox_receiver = null + return ..() + +/** + * Called when the `hasprox_receiver` receives the `COMSIG_PARENT_QDELETING` signal. When the receiver is deleted, so is this object. + * + * Arugments: + * * source - this will be the `hasprox_receiver` + * * force - the force flag taken from the qdel proc currently running on `hasprox_receiver` + */ +/obj/effect/abstract/proximity_checker/proc/OnParentDeletion(datum/source, force = FALSE) + qdel(src) + +/** + * Something crossed over the proximity_checker. Notify the `hasprox_receiver` it has proximity with something. Only fires if the checker is `active`. + */ +/obj/effect/abstract/proximity_checker/Crossed(atom/movable/AM, oldloc) + set waitfor = FALSE + if(active) + hasprox_receiver.HasProximity(AM) + +/** + * Moves the proximity_checker 1 tile in the `Dir` direction. + * + * If `Dir` is null it will be recentered around the receiver via the `recenter_prox_checkers()` proc. + * If the new location of the receiver is NOT a turf, set `active` to FALSE, so that it does not receive proximity calls. + * If the new location of the receiver IS a turf, set `active` to TRUE, so that it can receive proximity calls again. + * + * Arguments: + * * source - this will be the `hasprox_receiver` + * * OldLoc - the location the `hasprox_receiver` just moved from + * * Dir - the direction the `hasprox_receiver` just moved in + * * forced - if we were forced to move + */ +/obj/effect/abstract/proximity_checker/proc/HandleMove(datum/source, atom/OldLoc, Dir, forced) + if(Dir) + loc = get_step(src, Dir) // Basic movement 1 tile in some direction. + return + if(!isturf(hasprox_receiver.loc)) + active = FALSE // Receiver shouldn't detect proximity while picked up, in a backpack, closet, etc. + else + active = TRUE // Receiver can detect proximity again because it's on a turf. diff --git a/code/datums/components/slippery.dm b/code/datums/components/slippery.dm index 88e51209afb..a7b81856468 100644 --- a/code/datums/components/slippery.dm +++ b/code/datums/components/slippery.dm @@ -51,6 +51,6 @@ Additionally calls the parent's `after_slip()` proc on the `victim`. */ /datum/component/slippery/proc/Slip(datum/source, mob/living/carbon/human/victim) - if(istype(victim) && prob(slip_chance) && victim.slip(description, stun, weaken, slip_tiles, walking_is_safe, slip_always, slip_verb)) + if(istype(victim) && !victim.flying && prob(slip_chance) && victim.slip(description, stun, weaken, slip_tiles, walking_is_safe, slip_always, slip_verb)) var/atom/movable/owner = parent owner.after_slip(victim) diff --git a/code/datums/components/spooky.dm b/code/datums/components/spooky.dm new file mode 100644 index 00000000000..f5ee9c94666 --- /dev/null +++ b/code/datums/components/spooky.dm @@ -0,0 +1,58 @@ +/datum/component/spooky + var/too_spooky = TRUE //will it spawn a new instrument? + +/datum/component/spooky/Initialize() + RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/spectral_attack) + +/datum/component/spooky/proc/spectral_attack(datum/source, mob/living/carbon/C, mob/user) + if(ishuman(user)) //this weapon wasn't meant for mortals. + var/mob/living/carbon/human/U = user + if(!istype(U.dna.species, /datum/species/skeleton)) + U.adjustStaminaLoss(35) //Extra Damage + U.Jitter(35) + U.stuttering = 20 + if(U.getStaminaLoss() > 95) + to_chat(U, "Your ears weren't meant for this spectral sound.") + spectral_change(U) + return + + if(ishuman(C)) + var/mob/living/carbon/human/H = C + if(istype(H.dna.species, /datum/species/skeleton)) + return //undeads are unaffected by the spook-pocalypse. + C.Jitter(35) + C.stuttering = 20 + if(!istype(H.dna.species, /datum/species/diona) && !istype(H.dna.species, /datum/species/machine) && !istype(H.dna.species, /datum/species/slime) && !istype(H.dna.species, /datum/species/golem) && !istype(H.dna.species, /datum/species/plasmaman)) + C.adjustStaminaLoss(25) //boneless humanoids don't lose the will to live + to_chat(C, "DOOT") + spectral_change(H) + + else //the sound will spook monkeys. + C.Jitter(15) + C.stuttering = 20 + +/datum/component/spooky/proc/spectral_change(mob/living/carbon/human/H, mob/user) + if((H.getStaminaLoss() > 95) && (!istype(H.dna.species, /datum/species/diona) && !istype(H.dna.species, /datum/species/machine) && !istype(H.dna.species, /datum/species/slime) && !istype(H.dna.species, /datum/species/golem) && !istype(H.dna.species, /datum/species/plasmaman) && !istype(H.dna.species, /datum/species/skeleton))) + H.Stun(20) + H.set_species(/datum/species/skeleton) + H.visible_message("[H] has given up on life as a mortal.") + var/T = get_turf(H) + if(too_spooky) + if(prob(30)) + new/obj/item/instrument/saxophone/spectral(T) + else if(prob(30)) + new/obj/item/instrument/trumpet/spectral(T) + else if(prob(30)) + new/obj/item/instrument/trombone/spectral(T) + else + to_chat(H, "The spooky gods forgot to ship your instrument. Better luck next unlife.") + to_chat(H, "You are the spooky skeleton!") + to_chat(H, "A new life and identity has begun. Help your fellow skeletons into bringing out the spooky-pocalypse. You haven't forgotten your past life, and are still beholden to past loyalties.") + change_name(H) //time for a new name! + +/datum/component/spooky/proc/change_name(mob/living/carbon/human/H) + var/t = stripped_input(H, "Enter your new skeleton name", H.real_name, null, MAX_NAME_LEN) + if(!t) + t = "spooky skeleton" + H.real_name = t + H.name = t diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm index 4c55ad2ad82..f79439f2b3b 100644 --- a/code/datums/components/squeak.dm +++ b/code/datums/components/squeak.dm @@ -67,6 +67,14 @@ var/obj/item/projectile/P = AM if(P.original != parent) return + if(ismob(AM)) + var/mob/M = AM + if(M.flying) + return + if(isliving(AM)) + var/mob/living/L = M + if(L.floating) + return var/atom/current_parent = parent if(isturf(current_parent.loc)) play_squeak() diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 0e98179e2f9..1042d9a8ddd 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -35,12 +35,18 @@ var/rank = t.fields["rank"] var/real_rank = t.fields["real_rank"] if(OOC) - var/active = 0 - for(var/mob/M in GLOB.player_list) - if(M.real_name == name && M.client && M.client.inactivity <= 10 * 60 * 10) - active = 1 + var/activetext = "Inactive" + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing + if(H.real_name != name) + continue + if(H.client && H.client.inactivity <= 6000) + activetext = "Active" break - isactive[name] = active ? "Active" : "Inactive" + if(isLivingSSD(H)) + activetext = "SSD" + break + isactive[name] = activetext else isactive[name] = t.fields["p_stat"] var/department = 0 @@ -278,8 +284,8 @@ GLOBAL_VAR_INIT(record_id_num, 1001) G.fields["sex"] = capitalize(H.gender) G.fields["species"] = H.dna.species.name G.fields["photo"] = get_id_photo(H) - G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = SOUTH))]'" - G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = WEST))]'" + G.fields["photo-south"] = "data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = SOUTH))]" + G.fields["photo-west"] = "data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = WEST))]" if(H.gen_record && !jobban_isbanned(H, "Records")) G.fields["notes"] = H.gen_record else @@ -319,7 +325,7 @@ GLOBAL_VAR_INIT(record_id_num, 1001) if(H.sec_record && !jobban_isbanned(H, "Records")) S.fields["notes"] = H.sec_record else - S.fields["notes"] = "No notes." + S.fields["notes"] = "No notes found." LAZYINITLIST(S.fields["comments"]) security += S @@ -518,8 +524,8 @@ GLOBAL_VAR_INIT(record_id_num, 1001) clothes_s = new /icon('icons/mob/uniform.dmi', "cargotech_s") clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) if("Shaft Miner") - clothes_s = new /icon('icons/mob/uniform.dmi', "miner_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s = new /icon('icons/mob/uniform.dmi', "explorer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "explorer"), ICON_UNDERLAY) if("Lawyer") clothes_s = new /icon('icons/mob/uniform.dmi', "internalaffairs_s") clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index 34c41c581f2..318dd176e45 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -395,8 +395,9 @@ GLOBAL_LIST_INIT(advance_cures, list( for(var/datum/disease/advance/AD in GLOB.active_diseases) AD.Refresh() - for(var/mob/living/carbon/human/H in shuffle(GLOB.alive_mob_list)) - if(!is_station_level(H.z)) + for(var/thing in shuffle(GLOB.human_list)) + var/mob/living/carbon/human/H = thing + if(H.stat == DEAD || !is_station_level(H.z)) continue if(!H.HasDisease(D)) H.ForceContractDisease(D) diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm index f619e9aa9bd..3e3e1696e61 100644 --- a/code/datums/diseases/transformation.dm +++ b/code/datums/diseases/transformation.dm @@ -57,6 +57,9 @@ W.plane = initial(W.plane) W.loc = affected_mob.loc W.dropped(affected_mob) + if(isobj(affected_mob.loc)) + var/obj/O = affected_mob.loc + O.force_eject_occupant() var/mob/living/new_mob = new new_form(affected_mob.loc) if(istype(new_mob)) new_mob.a_intent = "harm" diff --git a/code/datums/dog_fashion.dm b/code/datums/dog_fashion.dm index 3bf109eabd9..14705ae7375 100644 --- a/code/datums/dog_fashion.dm +++ b/code/datums/dog_fashion.dm @@ -191,6 +191,10 @@ ..() desc = "That's Definitely Not [M.real_name]." +/datum/dog_fashion/head/cone + name = "REAL_NAME" + desc = "Omnicone's Chosen Champion" + /datum/dog_fashion/back/hardsuit name = "Space Explorer REAL_NAME" desc = "That's one small step for a corgi. One giant yap for corgikind." @@ -200,3 +204,7 @@ D.mutations.Add(BREATHLESS) D.atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) D.minbodytemp = 0 + +/datum/dog_fashion/head/fried_vox_empty + name = "Colonel REAL_NAME" + desc = "Keep away from live vox." diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 4e20d2a675e..4609075ddd7 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -152,13 +152,11 @@ return doTeleport() return 0 -/datum/teleport/instant //teleports when datum is created - - start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) - if(..()) - if(teleport()) - return 1 - return 0 +/datum/teleport/instant/start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) + if(..()) + if(teleport()) + return 1 + return 0 /datum/teleport/instant/science diff --git a/code/datums/log_record.dm b/code/datums/log_record.dm index 9b7d52b5390..2a659477b47 100644 --- a/code/datums/log_record.dm +++ b/code/datums/log_record.dm @@ -3,32 +3,51 @@ var/raw_time // When did this happen? var/what // What happened var/who // Who did it - var/target // Who/what was targeted (can be a string) - var/turf/where // Where did it happen + var/target // Who/what was targeted + var/where // Where did it happen /datum/log_record/New(_log_type, _who, _what, _target, _where, _raw_time) log_type = _log_type - - who = get_subject_text(_who) + + who = get_subject_text(_who, _log_type) what = _what - target = get_subject_text(_target) - if(!_where) + target = get_subject_text(_target, _log_type) + if(!istext(_where) && !isturf(_where)) _where = get_turf(_who) - where = _where + if(isturf(_where)) + var/turf/T = _where + where = ADMIN_COORDJMP(T) + else + where = _where if(!_raw_time) _raw_time = world.time raw_time = _raw_time -/datum/log_record/proc/get_subject_text(subject) +/datum/log_record/proc/get_subject_text(subject, log_type) if(ismob(subject) || isclient(subject) || istype(subject, /datum/mind)) - return key_name_admin(subject) - if(isatom(subject)) + . = key_name_admin(subject) + if(should_log_health(log_type) && isliving(subject)) + . += get_health_string(subject) + else if(isatom(subject)) var/atom/A = subject - return A.name - if(istype(subject, /datum)) + . = A.name + else if(istype(subject, /datum)) var/datum/D = subject return D.type - return subject + else + . = subject + +/datum/log_record/proc/get_health_string(var/mob/living/L) + var/OX = L.getOxyLoss() > 50 ? "[L.getOxyLoss()]" : L.getOxyLoss() + var/TX = L.getToxLoss() > 50 ? "[L.getToxLoss()]" : L.getToxLoss() + var/BU = L.getFireLoss() > 50 ? "[L.getFireLoss()]" : L.getFireLoss() + var/BR = L.getBruteLoss() > 50 ? "[L.getBruteLoss()]" : L.getBruteLoss() + return " ([L.health]: [OX] - [TX] - [BU] - [BR])" + +/datum/log_record/proc/should_log_health(log_type) + if(log_type == ATTACK_LOG || log_type == DEFENSE_LOG) + return TRUE + return FALSE /proc/compare_log_record(datum/log_record/A, datum/log_record/B) var/time_diff = A.raw_time - B.raw_time diff --git a/code/datums/log_viewer.dm b/code/datums/log_viewer.dm index 67eb3d21d86..c5a17507606 100644 --- a/code/datums/log_viewer.dm +++ b/code/datums/log_viewer.dm @@ -1,31 +1,45 @@ -#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, MISC_LOG) +#define UPDATE_CKEY_MOB(__ckey) var/mob/result = selected_ckeys_mobs[__ckey];\ +if(!result || result.ckey != __ckey){\ + result = get_mob_by_ckey(__ckey);\ + selected_ckeys_mobs[__ckey] = result;\ +} + +#define RECORD_WARN_LIMIT 1000 +#define RECORD_HARD_LIMIT 2500 /datum/log_viewer var/time_from = 0 var/time_to = 4 HOURS // 4 Hours should be enough. INFINITY would screw the UI up - var/list/selected_mobs = list() // The mobs in question - var/list/selected_log_types = list() // The log types being searched for - + var/list/selected_mobs = list() // The mobs in question. + var/list/selected_ckeys = list() // The ckeys selected to search for. Will show all mobs the ckey is attached to + var/list/mob/selected_ckeys_mobs = list() + var/list/selected_log_types = ALL_LOGS // The log types being searched for var/list/log_records = list() // Found and sorted records /datum/log_viewer/proc/clear_all() selected_mobs.Cut() - selected_log_types.Cut() + selected_log_types = ALL_LOGS + selected_ckeys.Cut() + selected_ckeys_mobs.Cut() time_from = initial(time_from) time_to = initial(time_to) log_records.Cut() return -/datum/log_viewer/proc/search() +/datum/log_viewer/proc/search(user) log_records.Cut() // Empty the old results var/list/invalid_mobs = list() + var/list/ckeys = selected_ckeys.Copy() for(var/i in selected_mobs) var/mob/M = i - if(!M || QDELETED(M)) + if(!M || QDELETED(M) || !M.last_known_ckey) invalid_mobs |= M continue + ckeys |= M.last_known_ckey + + for(var/ckey in ckeys) for(var/log_type in selected_log_types) - var/list/logs = M.logs[log_type] + var/list/logs = GLOB.logging.get_logs_by_type(ckey, log_type) var/len_logs = length(logs) if(len_logs) var/start_index = get_earliest_log_index(logs) @@ -36,8 +50,8 @@ continue log_records.Add(logs.Copy(start_index, end_index + 1)) - if(invalid_mobs.len) - to_chat(usr, "The search criteria contained invalid mobs. They have been removed from the criteria.") + if(length(invalid_mobs)) + to_chat(user, "The search criteria contained invalid mobs. They have been removed from the criteria.") for(var/i in invalid_mobs) selected_mobs -= i // Cleanup @@ -91,9 +105,23 @@ return start return 0 -/datum/log_viewer/proc/add_mob(mob/user, mob/M) +/datum/log_viewer/proc/add_mobs(list/mob/mobs) + if(!length(mobs)) + return + for(var/i in mobs) + add_mob(usr, i, FALSE) + +/datum/log_viewer/proc/add_ckey(mob/user, ckey) + if(!user || !ckey) + return + selected_ckeys |= ckey + UPDATE_CKEY_MOB(ckey) + show_ui(user) + +/datum/log_viewer/proc/add_mob(mob/user, mob/M, show_the_ui = TRUE) if(!M || !user) return + selected_mobs |= M show_ui(user) @@ -102,9 +130,9 @@ var/all_log_types = ALL_LOGS var/trStyleTop = "border-top:2px solid; border-bottom:2px solid; padding-top: 5px; padding-bottom: 5px;" var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;" - var/dat - dat += "" - dat += "
" + var/list/dat = list() + dat += "" + dat += "
" dat += "Time Search Range: [gameTimestamp(wtime = time_from)]" dat += " To: [gameTimestamp(wtime = time_to)]" dat += "
" @@ -115,20 +143,26 @@ if(QDELETED(M)) selected_mobs -= i continue - dat += "[M.name]" + dat += "[get_display_name(M)]" dat += "Add Mob" dat += "Clear All Mobs" dat += "
" + dat += "Ckeys being used:" + for(var/ckey in selected_ckeys) + dat += "[get_ckey_name(ckey)]" + dat += "Add ckey" + dat += "Clear All ckeys" + dat += "
" + dat += "Log Types:" - for(var/i in all_log_types) - var/log_type = i + for(var/log_type in all_log_types) var/enabled = (log_type in selected_log_types) var/text var/style if(enabled) text = "[log_type]" - style = "background: [get_logtype_color(i)]" + style = "background: [get_logtype_color(log_type)]" else text = log_type @@ -142,9 +176,9 @@ // Search results var/tdStyleTime = "width:80px; text-align:center;" var/tdStyleType = "width:80px; text-align:center;" - var/tdStyleWho = "width:300px; text-align:center;" + var/tdStyleWho = "width:400px; text-align:center;" var/tdStyleWhere = "width:150px; text-align:center;" - dat += "
" + dat += "
" dat += "" dat += "" for(var/i in log_records) @@ -153,13 +187,12 @@ dat +="\ \ - " - + " dat += "
WhenTypeWhoWhatTargetWhere
[time][L.log_type][L.who][L.what][L.target][ADMIN_COORDJMP(L.where)]
[L.target][L.where]
" dat += "
" - var/datum/browser/popup = new(user, "Log viewer", "Log viewer", 1400, 600) - popup.set_content(dat) + var/datum/browser/popup = new(user, "Log Viewer", "Log Viewer", 1500, 600) + popup.set_content(dat.Join()) popup.open() /datum/log_viewer/Topic(href, href_list) @@ -188,6 +221,19 @@ return if(href_list["search"]) search(usr) + var/records_len = length(log_records) + if(records_len > RECORD_WARN_LIMIT) + var/datum/log_record/last_record = log_records[RECORD_WARN_LIMIT] + var/last_time = gameTimestamp(wtime = last_record.raw_time - 9.99) + var/answer = alert(usr, "More than [RECORD_WARN_LIMIT] records were found. continuing will take a long time. This won't cause much lag for the server. Time at the [RECORD_WARN_LIMIT]th record '[last_time]'", "Warning", "Continue", "Limit to [RECORD_WARN_LIMIT]", "Cancel") + if(answer == "Limit to [RECORD_WARN_LIMIT]") + log_records.Cut(RECORD_WARN_LIMIT) + else if(answer == "Cancel") + log_records.Cut() + else + if(records_len > RECORD_HARD_LIMIT) + to_chat(usr, "Record limit reached. Limiting to [RECORD_HARD_LIMIT].") + log_records.Cut(RECORD_HARD_LIMIT) show_ui(usr) return if(href_list["clear_all"]) @@ -198,17 +244,31 @@ selected_mobs.Cut() show_ui(usr) return + if(href_list["clear_ckeys"]) + selected_ckeys.Cut() + selected_ckeys_mobs.Cut() + show_ui(usr) + return if(href_list["add_mob"]) var/list/mobs = getpois(TRUE, TRUE) var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs) A.on_close(CALLBACK(src, .proc/add_mob, usr)) return + if(href_list["add_ckey"]) + var/list/ckeys = GLOB.logging.get_ckeys_logged() + var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a ckey: ", ckeys) + A.on_close(CALLBACK(src, .proc/add_ckey, usr)) + return if(href_list["remove_mob"]) var/mob/M = locate(href_list["remove_mob"]) if(M) selected_mobs -= M show_ui(usr) return + if(href_list["remove_ckey"]) + selected_ckeys -= href_list["remove_ckey"] + show_ui(usr) + return if(href_list["toggle_log_type"]) var/log_type = href_list["toggle_log_type"] if(log_type in selected_log_types) @@ -232,4 +292,28 @@ return "deepskyblue" if(MISC_LOG) return "gray" + if(DEADCHAT_LOG) + return "#cc00c6" + if(OOC_LOG) + return "#002eb8" + if(LOOC_LOG) + return "#6699CC" return "slategray" + +/datum/log_viewer/proc/get_display_name(mob/M) + var/name = M.name + if(M.name != M.real_name) + name = "[name] ([M.real_name])" + if(isobserver(M)) + name = "[name] (DEAD)" + return "\[[M.last_known_ckey]\] [name]" + +/datum/log_viewer/proc/get_ckey_name(ckey) + UPDATE_CKEY_MOB(ckey) + var/mob/M = selected_ckeys_mobs[ckey] + + return get_display_name(M) + +#undef UPDATE_CKEY_MOB +#undef RECORD_WARN_LIMIT +#undef RECORD_HARD_LIMIT diff --git a/code/datums/logging.dm b/code/datums/logging.dm new file mode 100644 index 00000000000..bac26610e5e --- /dev/null +++ b/code/datums/logging.dm @@ -0,0 +1,47 @@ +/datum/logging + var/list/datum/log_record/logs = list() // Assoc list of assoc lists (ckey, (log_type, list/logs)) + +/datum/logging/proc/add_log(ckey, datum/log_record/log) + if(!ckey) + log_debug("GLOB.logging.add_log called with an invalid ckey") + return + + if(!logs[ckey]) + logs[ckey] = list() + + var/list/log_types_list = logs[ckey] + + if(!log_types_list[log.log_type]) + log_types_list[log.log_type] = list() + + var/list/datum/log_record/log_records = log_types_list[log.log_type] + log_records.Add(log) + +/datum/logging/proc/get_ckeys_logged() + var/list/ckeys = list() + for(var/ckey in logs) + ckeys.Add(ckey) + return ckeys + +/* Returns the logs of a given ckey and log_type + * If no logs exist it will return an empty list +*/ +/datum/logging/proc/get_logs_by_type(ckey, log_type) + if(!ckey) + log_debug("GLOB.logging.get_logs_by_type called with an invalid ckey") + return + if(!log_type || !(log_type in ALL_LOGS)) + log_debug("GLOB.logging.get_logs_by_type called with an invalid log_type '[log_type]'") + return + + var/list/log_types_list = logs[ckey] + // Check if logs exist for the ckey + if(!length(log_types_list)) + return list() + + var/list/datum/log_record/log_records = log_types_list[log_type] + + // Check if logs exist for this type + if(!log_records) + return list() + return log_records diff --git a/code/datums/looping_sounds/looping_sound.dm b/code/datums/looping_sounds/looping_sound.dm index f44a87bdd7a..006e92c305c 100644 --- a/code/datums/looping_sounds/looping_sound.dm +++ b/code/datums/looping_sounds/looping_sound.dm @@ -71,7 +71,7 @@ var/list/atoms_cache = output_atoms var/sound/S = sound(soundfile) if(direct) - S.channel = open_sound_channel() + S.channel = SSsounds.random_available_channel() S.volume = volume for(var/i in 1 to atoms_cache.len) var/atom/thing = atoms_cache[i] diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 2f22e58d37b..cc8b2dd5e97 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -35,6 +35,7 @@ var/list/restricted_roles = list() var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button. + var/datum/martial_art/martial_art var/role_alt_title @@ -100,15 +101,8 @@ current.mind = null leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it - for(var/log_type in current.logs) // Copy the old logs - var/list/logs = current.logs[log_type] - if(new_character.logs[log_type]) - new_character.logs[log_type] += logs.Copy() // Append the old ones - new_character.logs[log_type] = sortTim(new_character.logs[log_type], /proc/compare_log_record) // Sort them on time - else - new_character.logs[log_type] = logs.Copy() // Just copy them - SSnanoui.user_transferred(current, new_character) + SStgui.on_transfer(current, new_character) if(new_character.mind) //remove any mind currently in our new body's mind variable new_character.mind.current = null @@ -119,6 +113,11 @@ A.on_body_transfer(old_current, current) transfer_antag_huds(hud_to_transfer) //inherit the antag HUD transfer_actions(new_character) + if(martial_art) + if(martial_art.temporary) + martial_art.remove(current) + else + martial_art.teach(current) if(active) new_character.key = key //now transfer the key to link the client to our new body @@ -922,7 +921,7 @@ log_admin("[key_name(usr)] has equipped [key_name(current)] as a wizard") message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a wizard") if("name") - SSticker.mode.name_wizard(current) + INVOKE_ASYNC(SSticker.mode, /datum/game_mode/wizard.proc/name_wizard, current) log_admin("[key_name(usr)] has allowed wizard [key_name(current)] to name themselves") message_admins("[key_name_admin(usr)] has allowed wizard [key_name_admin(current)] to name themselves") if("autoobjectives") @@ -1110,8 +1109,8 @@ special_role = null to_chat(current,"Your infernal link has been severed! You are no longer a devil!") RemoveSpell(/obj/effect/proc_holder/spell/targeted/infernal_jaunt) - RemoveSpell(/obj/effect/proc_holder/spell/fireball/hellish) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_contract) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/click/fireball/hellish) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/click/summon_contract) RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork) RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/greater) RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/ascended) @@ -1419,9 +1418,10 @@ return A /datum/mind/proc/announce_objectives() - to_chat(current, "Your current objectives:") - for(var/line in splittext(gen_objective_text(), "
")) - to_chat(current, line) + if(current) + to_chat(current, "Your current objectives:") + for(var/line in splittext(gen_objective_text(), "
")) + to_chat(current, line) /datum/mind/proc/find_syndicate_uplink() var/list/L = current.get_contents() @@ -1508,7 +1508,7 @@ SSticker.mode.equip_wizard(current) for(var/obj/item/spellbook/S in current.contents) S.op = 0 - SSticker.mode.name_wizard(current) + INVOKE_ASYNC(SSticker.mode, /datum/game_mode/wizard.proc/name_wizard, current) SSticker.mode.forge_wizard_objectives(src) SSticker.mode.greet_wizard(src) SSticker.mode.update_wiz_icons_added(src) diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm index 14bc959fbb1..d5be5f50f35 100644 --- a/code/datums/outfits/outfit_admin.dm +++ b/code/datums/outfits/outfit_admin.dm @@ -222,10 +222,10 @@ name = "NT Undercover Operative" // Disguised NT special forces, sent to quietly eliminate or keep tabs on people in high positions (e.g: captain) - uniform = /obj/item/clothing/under/color/black + uniform = /obj/item/clothing/under/color/random back = /obj/item/storage/backpack belt = /obj/item/storage/belt/utility/full/multitool - gloves = /obj/item/clothing/gloves/combat + gloves = /obj/item/clothing/gloves/color/yellow shoes = /obj/item/clothing/shoes/chameleon/noslip l_ear = /obj/item/radio/headset/centcom id = /obj/item/card/id @@ -242,7 +242,7 @@ /obj/item/organ/internal/cyberimp/eyes/shield, /obj/item/organ/internal/cyberimp/eyes/hud/security, /obj/item/organ/internal/cyberimp/eyes/xray, - /obj/item/organ/internal/cyberimp/brain/anti_stun, + /obj/item/organ/internal/cyberimp/brain/anti_stun/hardened, /obj/item/organ/internal/cyberimp/chest/nutriment/plus, /obj/item/organ/internal/cyberimp/arm/combat/centcom ) diff --git a/code/datums/pipe_datums.dm b/code/datums/pipe_datums.dm index 83b77425447..8d3b0807f9c 100644 --- a/code/datums/pipe_datums.dm +++ b/code/datums/pipe_datums.dm @@ -351,16 +351,6 @@ GLOBAL_LIST_EMPTY(rpd_pipe_list) //Some pipes we don't want to be dispensable pipe_id = PIPE_CIRCULATOR pipe_icon = "circ" -/datum/pipes/atmospheric/omni_filter - pipe_name = "omni filter" - pipe_id = PIPE_OMNI_FILTER - pipe_icon = "omni_filter" - -/datum/pipes/atmospheric/omni_mixer - pipe_name = "omni mixer" - pipe_id = PIPE_OMNI_MIXER - pipe_icon = "omni_mixer" - /datum/pipes/atmospheric/insulated pipe_name = "insulated pipe" pipe_id = PIPE_INSULATED_STRAIGHT diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm index f826b6ff0d2..2143fb5ad5c 100644 --- a/code/datums/ruins/lavaland.dm +++ b/code/datums/ruins/lavaland.dm @@ -16,7 +16,7 @@ name = "Biodome Winter" id = "biodome-winter" description = "For those getaways where you want to get back to nature, but you don't want to leave the fortified military compound where you spend your days. \ - Includes a unique(*) laser pistol display case, and the recently introduced I.C.E(tm)." + Includes the recently introduced I.C.E(tm)." suffix = "lavaland_biodome_winter.dmm" /datum/map_template/ruin/lavaland/biodome/clown @@ -42,7 +42,7 @@ cost = 10 allow_duplicates = FALSE -datum/map_template/ruin/lavaland/ash_walker +/datum/map_template/ruin/lavaland/ash_walker name = "Ash Walker Nest" id = "ash-walker" description = "A race of unbreathing lizards live here, that run faster than a human can, worship a broken dead city, and are capable of reproducing by something involving tentacles? \ diff --git a/code/datums/ruins/space.dm b/code/datums/ruins/space.dm index b81587440d4..3541be89983 100644 --- a/code/datums/ruins/space.dm +++ b/code/datums/ruins/space.dm @@ -175,6 +175,7 @@ description = "The crew of a space station awaken one hundred years after a crisis. Awaking to a derelict space station on the verge of collapse, and a hostile force of invading \ hivebots. Can the surviving crew overcome the odds and survive and rebuild, or will the cold embrace of the stars become their new home?" cost = 2 + allow_duplicates = FALSE /datum/map_template/ruin/space/wizardcrash id = "wizardcrash" @@ -247,3 +248,29 @@ allow_duplicates = FALSE // I dont even want to think about what happens if you have 2 shuttles with the same ID. Likely scary stuff. always_place = TRUE // Its designed to make exploring other space ruins more accessible cost = 0 // Force spawned so shouldnt have a cost + +/datum/map_template/ruin/space/syndiecakesfactory + id = "Syndiecakes Factory" + suffix = "syndiecakesfactory.dmm" + name = "Syndicakes Factory" + description = "Syndicate used to get funds selling corgi cakes produced here. Was it hit by meteors or by a Nanotrasen comando?" + allow_duplicates = FALSE + cost = 2 //telecomms + multiple mobs + +/datum/map_template/ruin/space/debris1 + id = "debris1" + suffix = "debris1.dmm" + name = "Debris field 1" + description = "A bunch of metal chunks, wires and space waste" + +/datum/map_template/ruin/space/debris2 + id = "debris2" + suffix = "debris2.dmm" + name = "Debris field 2" + description = "A bunch of metal chunks, wires and space waste that used to be some kind of secure storage facility" + +/datum/map_template/ruin/space/debris3 + id = "debris3" + suffix = "debris3.dmm" + name = "Debris field 3" + description = "A bunch of metal chunks, wires and space waste. It used to be an arcade." diff --git a/code/datums/shuttles.dm b/code/datums/shuttles.dm index 53517d5dbc9..dacc08dee31 100644 --- a/code/datums/shuttles.dm +++ b/code/datums/shuttles.dm @@ -9,8 +9,9 @@ var/admin_notes /datum/map_template/shuttle/New() - shuttle_id = "[port_id]_[suffix]" - mappath = "[prefix][shuttle_id].dmm" + if(port_id && suffix) + shuttle_id = "[port_id]_[suffix]" + mappath = "[prefix][shuttle_id].dmm" . = ..() /datum/map_template/shuttle/emergency @@ -132,3 +133,8 @@ suffix = "admin" name = "NTV Argos" description = "Default Admin ship. An older ship used for special operations." + +/datum/map_template/shuttle/admin/armory + suffix = "armory" + name = "NRV Sparta" + description = "Armory Shuttle, with plenty of guns to hand out and some general supplies." diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm index a651edbc79c..a196a60fcc5 100644 --- a/code/datums/spawners_menu.dm +++ b/code/datums/spawners_menu.dm @@ -6,44 +6,47 @@ qdel(src) owner = new_owner -/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = FALSE, datum/topic_state/state = GLOB.ghost_state, datum/nanoui/master_ui = null) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/datum/spawners_menu/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui_state/state = GLOB.tgui_observer_state, datum/tgui/master_ui = null) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "spawners_menu.tmpl", "Spawners Menu", 700, 600, master_ui, state = state) + ui = new(user, src, ui_key, "SpawnersMenu", "Spawners Menu", 700, 600, master_ui, state = state) ui.open() -/datum/spawners_menu/ui_data(mob/user) +/datum/spawners_menu/tgui_data(mob/user) var/list/data = list() data["spawners"] = list() for(var/spawner in GLOB.mob_spawners) var/list/this = list() this["name"] = spawner this["desc"] = "" + this["important_info"] = "" + this["fluff"] = "" this["uids"] = list() - for(var/spawner_obj in GLOB.mob_spawners[spawner]) + for(var/spawner_obj in GLOB.mob_spawners[spawner])//each spawner can contain multiple actual spawners, we use only one desc/info this["uids"] += "\ref[spawner_obj]" - if(!this["desc"]) + if(!this["desc"]) //haven't set descriptions yet if(istype(spawner_obj, /obj/effect/mob_spawn)) var/obj/effect/mob_spawn/MS = spawner_obj - this["desc"] = MS.flavour_text + this["desc"] = MS.description + this["important_info"] = MS.important_info + this["fluff"] = MS.flavour_text else var/obj/O = spawner_obj this["desc"] = O.desc this["amount_left"] = LAZYLEN(GLOB.mob_spawners[spawner]) data["spawners"] += list(this) - return data -/datum/spawners_menu/Topic(href, href_list) +/datum/spawners_menu/tgui_act(action, params) if(..()) - return 1 - var/spawners = replacetext(href_list["uid"], ",", ";") + return + var/spawners = replacetext(params["ID"], ",", ";") var/list/possible_spawners = params2list(spawners) var/obj/effect/mob_spawn/MS = locate(pick(possible_spawners)) if(!MS || !istype(MS)) log_runtime(EXCEPTION("A ghost tried to interact with an invalid spawner, or the spawner didn't exist.")) return - switch(href_list["action"]) + switch(action) if("jump") owner.forceMove(get_turf(MS)) . = TRUE diff --git a/code/datums/spell.dm b/code/datums/spell.dm index 3855dbcfac3..abb9fdc37c9 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -24,6 +24,20 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) user.face_atom(A) return FALSE +/datum/click_intercept/proc_holder + var/obj/effect/proc_holder/spell + +/datum/click_intercept/proc_holder/New(client/C, obj/effect/proc_holder/spell_to_cast) + . = ..() + spell = spell_to_cast + +/datum/click_intercept/proc_holder/InterceptClickOn(user, params, atom/object) + spell.InterceptClickOn(user, params, object) + +/datum/click_intercept/proc_holder/quit() + spell.remove_ranged_ability(spell.ranged_ability_user) + return ..() + /obj/effect/proc_holder/proc/add_ranged_ability(mob/living/user, var/msg) if(!user || !user.client) return @@ -32,7 +46,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) user.ranged_ability.remove_ranged_ability(user) user.ranged_ability = src ranged_ability_user = user - user.client.click_intercept = user.ranged_ability + user.client.click_intercept = new /datum/click_intercept/proc_holder(user.client, user.ranged_ability) add_mousepointer(user.client) active = TRUE if(msg) @@ -48,15 +62,17 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) C.mouse_pointer_icon = initial(C.mouse_pointer_icon) /obj/effect/proc_holder/proc/remove_ranged_ability(mob/living/user, var/msg) - if(!user || !user.client || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability + if(!user || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability return user.ranged_ability = null ranged_ability_user = null - user.client.click_intercept = null - remove_mousepointer(user.client) active = FALSE - if(msg) - to_chat(user, msg) + if(user.client) + qdel(user.client.click_intercept) + user.client.click_intercept = null + remove_mousepointer(user.client) + if(msg) + to_chat(user, msg) update_icon() /obj/effect/proc_holder/spell @@ -114,10 +130,14 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) var/sound = null //The sound the spell makes when it is cast -/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell - if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) - to_chat(user, "You shouldn't have this spell! Something's wrong.") - return 0 +/* Checks if the user can cast the spell + * @param charge_check If the proc should do the cooldown check + * @param start_recharge If the proc should set the cooldown + * @param user The caster of the spell +*/ +/obj/effect/proc_holder/spell/proc/cast_check(charge_check = TRUE, start_recharge = TRUE, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell + if(!can_cast(user, charge_check, TRUE)) + return FALSE if(ishuman(user)) var/mob/living/carbon/human/caster = user @@ -126,49 +146,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) caster.reset_perspective(0) return 0 - if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel - return 0 - - if(!skipcharge) - switch(charge_type) - if("recharge") - if(charge_counter < charge_max) - to_chat(user, still_recharging_msg) - return 0 - if("charges") - if(!charge_counter) - to_chat(user, "[name] has no charges left.") - return 0 - - if(!ghost) - if(user.stat && !stat_allowed) - to_chat(user, "You can't cast this spell while incapacitated.") - return 0 - if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled()) - to_chat(user, "Mmmf mrrfff!") - return 0 - - var/obj/effect/proc_holder/spell/noclothes/clothes_spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list())) - if((ishuman(user) && clothes_req) && !istype(clothes_spell))//clothes check - var/mob/living/carbon/human/H = user - var/obj/item/clothing/robe = H.wear_suit - var/obj/item/clothing/hat = H.head - var/obj/item/clothing/shoes = H.shoes - if(!robe || !hat || !shoes) - to_chat(user, "Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.") - return 0 - if(!robe.magical || !hat.magical || !shoes.magical) - to_chat(user, "Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.") - return 0 - else if(!ishuman(user)) - if(clothes_req || human_req) - to_chat(user, "This spell can only be cast by humans!") - return 0 - if(nonabstract_req && (isbrain(user) || ispAI(user))) - to_chat(user, "This spell can only be cast by physical beings!") - return 0 - - if(!skipcharge) + if(start_recharge) switch(charge_type) if("recharge") charge_counter = 0 //doesn't start recharging until the targets selecting ends @@ -230,12 +208,12 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) /obj/effect/proc_holder/spell/process() charge_counter += 2 + if(action) + action.UpdateButtonIcon() if(charge_counter < charge_max) return STOP_PROCESSING(SSfastprocess, src) charge_counter = charge_max - if(action) - action.UpdateButtonIcon() /obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr, make_attack_logs = TRUE) //if recharge is started is important for the trigger spells before_cast(targets) @@ -339,6 +317,19 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existant vars return +/obj/effect/proc_holder/spell/proc/get_availability_percentage() + switch(charge_type) + if("recharge") + if(charge_counter == 0) + return 0 + return charge_counter / charge_max + if("charges") + if(charge_counter) + return 1 + return 0 + if("holdervar") + return 1 + /obj/effect/proc_holder/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob var/max_targets = 1 //leave 0 for unlimited targets in range, 1 for one selectable target in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast @@ -429,6 +420,100 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) return +/obj/effect/proc_holder/spell/targeted/click + var/click_radius = 1 // How big the radius around the clicked atom is to find a suitable target. -1 is only the selected atom is considered + var/selection_activated_message = "Click on a target to cast the spell." + var/selection_deactivated_message = "You choose to not cast this spell." + var/allowed_type = /mob/living // Which type the targets have to be + var/auto_target_single = TRUE // If the spell should auto select a target if only one is found + +/obj/effect/proc_holder/spell/targeted/click/Click() + var/mob/living/user = usr + if(!istype(user)) + return + + if(active) + remove_ranged_ability(user, selection_deactivated_message) + else + if(cast_check(TRUE, FALSE, user)) + if(auto_target_single && attempt_auto_target(user)) + return + + add_ranged_ability(user, selection_activated_message) + else + to_chat(user, "[src] is not ready to be used yet.") + +/obj/effect/proc_holder/spell/targeted/click/proc/attempt_auto_target(mob/user) + var/atom/target + for(var/atom/A in view_or_range(range, user, selection_type)) + if(valid_target(A, user)) + if(target) + return FALSE // Two targets found. ABORT + target = A + + if(target && cast_check(TRUE, TRUE, user)) // Singular target found. Cast it instantly + to_chat(user, "Only one target found. Casting [src] on [target]!") + perform(list(target), user = user) + return TRUE + return FALSE + +/obj/effect/proc_holder/spell/targeted/click/InterceptClickOn(mob/living/user, params, atom/A) + if(..() || !cast_check(TRUE, TRUE, user)) + remove_ranged_ability(user) + revert_cast(user) + return TRUE + + var/list/targets = list() + if(valid_target(A, user)) + targets.Add(A) + + if((!max_targets || max_targets > targets.len) && click_radius >= 0) + var/list/found_others = list() + for(var/atom/target in range(click_radius, A)) + if(valid_target(target, user)) + found_others |= target + if(!max_targets) + targets.Add(found_others) + else + if(max_targets <= found_others.len + targets.len) + targets.Add(found_others) + else + switch(random_target_priority) //Add in the rest + if(TARGET_RANDOM) + while(targets.len < max_targets && found_others.len) // Add the others + targets.Add(pick_n_take(found_others)) + if(TARGET_CLOSEST) + var/list/distances = list() + for(var/target in found_others) + distances[target] = get_dist(user, target) + sortTim(distances, /proc/cmp_numeric_asc, TRUE) // Sort on distance + for(var/target in distances) + targets.Add(target) + if(targets.len >= max_targets) + break + + + if(!targets.len) + to_chat(user, "No suitable target found.") + revert_cast(user) + return FALSE + + perform(targets, user = user) + remove_ranged_ability(user) + return TRUE + +/* Checks if a target is valid + * Should not include to_chats or other types of messages since this is used often on tons of targets. + * @param target The target to check + * @param user The user of the spell +*/ +/obj/effect/proc_holder/spell/targeted/click/proc/valid_target(target, user) + return istype(target, allowed_type) && (include_user || target != user) && \ + (target in view_or_range(range, user, selection_type)) + +/obj/effect/proc_holder/spell/targeted/click/choose_targets(mob/living/user, atom/A) // Not used + return + /obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr) var/list/targets = list() @@ -444,6 +529,11 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) return +// Normally, AoE spells will generate an attack log for every turf they loop over, while searching for targets. +// With this override, all /aoe_turf type spells will only generate 1 log, saying that the user has cast the spell. +/obj/effect/proc_holder/spell/aoe_turf/perform(list/targets, recharge, mob/user, make_attack_logs) + add_attack_logs(user, null, "Cast the AoE spell [name]", ATKLOG_ALL) + return ..(targets, recharge, user, FALSE) /obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B) //Checks for obstacles from A to B @@ -457,30 +547,39 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) qdel(dummy) return 1 -/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr) +/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE) if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) + if(show_message) + to_chat(user, "You shouldn't have this spell! Something's wrong.") return 0 if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel return 0 - switch(charge_type) - if("recharge") - if(charge_counter < charge_max) - return 0 - if("charges") - if(!charge_counter) - return 0 - - if(user.stat && !stat_allowed) - return 0 + if(charge_check) + switch(charge_type) + if("recharge") + if(charge_counter < charge_max) + if(show_message) + to_chat(user, still_recharging_msg) + return 0 + if("charges") + if(!charge_counter) + if(show_message) + to_chat(user, "[name] has no charges left.") + return 0 + if(!ghost) + if(user.stat && !stat_allowed) + if(show_message) + to_chat(user, "You can't cast this spell while incapacitated.") + return 0 + if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled()) + if(show_message) + to_chat(user, "Mmmf mrrfff!") + return 0 if(ishuman(user)) var/mob/living/carbon/human/H = user - - if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled()) - return 0 - var/clothcheck = locate(/obj/effect/proc_holder/spell/noclothes) in user.mob_spell_list var/clothcheck2 = user.mind && (locate(/obj/effect/proc_holder/spell/noclothes) in user.mind.spell_list) if(clothes_req && !clothcheck && !clothcheck2) //clothes check @@ -488,12 +587,20 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) var/obj/item/clothing/hat = H.head var/obj/item/clothing/shoes = H.shoes if(!robe || !hat || !shoes) + if(show_message) + to_chat(user, "Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.") return 0 if(!robe.magical || !hat.magical || !shoes.magical) + if(show_message) + to_chat(user, "Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.") return 0 else if(clothes_req || human_req) + if(show_message) + to_chat(user, "This spell can only be cast by humans!") return 0 if(nonabstract_req && (isbrain(user) || ispAI(user))) + if(show_message) + to_chat(user, "This spell can only be cast by physical beings!") return 0 return 1 diff --git a/code/datums/spells/area_teleport.dm b/code/datums/spells/area_teleport.dm index 0d24a984014..5986da3a412 100644 --- a/code/datums/spells/area_teleport.dm +++ b/code/datums/spells/area_teleport.dm @@ -11,7 +11,7 @@ /obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1, mob/living/user = usr) var/thearea = before_cast(targets) - if(!thearea || !cast_check(1)) + if(!thearea || !cast_check(TRUE, FALSE, user)) revert_cast() return invocation(thearea) diff --git a/code/datums/spells/chaplain.dm b/code/datums/spells/chaplain.dm index bdb7bb3f551..f16a2a3fbac 100644 --- a/code/datums/spells/chaplain.dm +++ b/code/datums/spells/chaplain.dm @@ -1,25 +1,31 @@ -/obj/effect/proc_holder/spell/targeted/chaplain_bless +/obj/effect/proc_holder/spell/targeted/click/chaplain_bless name = "Bless" desc = "Blesses a single person." school = "transmutation" charge_max = 60 - clothes_req = 0 + clothes_req = FALSE invocation = "none" invocation_type = "none" max_targets = 1 - include_user = 0 - humans_only = 1 - + include_user = FALSE + allowed_type = /mob/living/carbon/human + selection_activated_message = "You prepare a blessing. Click on a target to start blessing." + selection_deactivated_message = "The crew will be blessed another time." range = 1 + click_radius = -1 // Only precision clicking cooldown_min = 20 action_icon_state = "shield" +/obj/effect/proc_holder/spell/targeted/click/chaplain_bless/valid_target(mob/living/carbon/human/target, user) + if(!..()) + return FALSE -/obj/effect/proc_holder/spell/targeted/chaplain_bless/cast(list/targets, mob/living/user = usr, distanceoverride) + return target.mind && target.ckey && !target.stat +/obj/effect/proc_holder/spell/targeted/click/chaplain_bless/cast(list/targets, mob/living/user = usr) if(!istype(user)) to_chat(user, "Somehow, you are not a living mob. This should never happen. Report this bug.") revert_cast() @@ -35,32 +41,7 @@ revert_cast() return - var/mob/living/carbon/human/target = targets[range] - - if(!istype(target)) - to_chat(user, "No target.") - revert_cast() - return - - if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it. - to_chat(user, "[target] is too far away!") - revert_cast() - return - - if(!target.mind) - to_chat(user, "[target] appears to be catatonic. Your blessing would have no effect.") - revert_cast() - return - - if(!target.ckey) - to_chat(user, "[target] appears to be too out of it to benefit from this.") - revert_cast() - return - - if(target.stat == DEAD) - to_chat(user, "[target] is already dead. There is no point.") - revert_cast() - return + var/mob/living/carbon/human/target = targets[1] spawn(0) // allows cast to complete even if recipient ignores the prompt if(alert(target, "[user] wants to bless you, in the name of [user.p_their()] religion. Accept?", "Accept Blessing?", "Yes", "No") == "Yes") // prevents forced conversions diff --git a/code/datums/spells/devil.dm b/code/datums/spells/devil.dm index 27b2fcb8851..f9e075bdf81 100644 --- a/code/datums/spells/devil.dm +++ b/code/datums/spells/devil.dm @@ -21,13 +21,18 @@ action_background_icon_state = "bg_demon" -/obj/effect/proc_holder/spell/targeted/summon_contract +/obj/effect/proc_holder/spell/targeted/click/summon_contract name = "Summon infernal contract" desc = "Skip making a contract by hand, just do it by magic." invocation_type = "whisper" invocation = "Just sign on the dotted line." - include_user = 0 + selection_activated_message = "You prepare a detailed contract. Click on a target to summon the contract in his hands." + selection_deactivated_message = "You archive the contract for later use." + include_user = FALSE range = 5 + auto_target_single = FALSE // Prevent an accidental contract from summoning + click_radius = -1 // Precision clicking required + allowed_type = /mob/living/carbon clothes_req = FALSE school = "conjuration" charge_max = 150 @@ -35,8 +40,9 @@ action_icon_state = "spell_default" action_background_icon_state = "bg_demon" -/obj/effect/proc_holder/spell/targeted/summon_contract/cast(list/targets, mob/user = usr) - for(var/mob/living/carbon/C in targets) +/obj/effect/proc_holder/spell/targeted/click/summon_contract/cast(list/targets, mob/user = usr) + for(var/target in targets) + var/mob/living/carbon/C = target if(C.mind && user.mind) if(C.stat == DEAD) if(user.drop_item()) @@ -63,7 +69,7 @@ to_chat(user,"[C] seems to not be sentient. You are unable to summon a contract for them.") -/obj/effect/proc_holder/spell/fireball/hellish +/obj/effect/proc_holder/spell/targeted/click/fireball/hellish name = "Hellfire" desc = "This spell launches hellfire at the target." school = "evocation" @@ -74,8 +80,8 @@ fireball_type = /obj/item/projectile/magic/fireball/infernal action_background_icon_state = "bg_demon" -/obj/effect/proc_holder/spell/fireball/hellish/cast(list/targets, mob/living/user = usr) - msg_admin_attack("[key_name_admin(usr)] has fired a fireball.", ATKLOG_FEW) +/obj/effect/proc_holder/spell/targeted/click/fireball/hellish/cast(list/targets, mob/living/user = usr) + add_attack_logs(user, targets, "has fired a Hellfire ball", ATKLOG_FEW) .=..() diff --git a/code/datums/spells/horsemask.dm b/code/datums/spells/horsemask.dm index 0140fbdf951..d5f686fb763 100644 --- a/code/datums/spells/horsemask.dm +++ b/code/datums/spells/horsemask.dm @@ -1,39 +1,31 @@ -/obj/effect/proc_holder/spell/targeted/horsemask +/obj/effect/proc_holder/spell/targeted/click/horsemask name = "Curse of the Horseman" desc = "This spell triggers a curse on a target, causing them to wield an unremovable horse head mask. They will speak like a horse! Any masks they are wearing will be disintegrated. This spell does not require robes." school = "transmutation" charge_type = "recharge" charge_max = 150 charge_counter = 0 - clothes_req = 0 - stat_allowed = 0 + clothes_req = FALSE + stat_allowed = FALSE invocation = "KN'A FTAGHU, PUCK 'BTHNK!" invocation_type = "shout" range = 7 cooldown_min = 30 //30 deciseconds reduction per rank selection_type = "range" + selection_activated_message = "You start to quietly neigh an incantation. Click on or near a target to cast the spell." + selection_deactivated_message = "You stop neighing to yourself." + allowed_type = /mob/living/carbon/human + action_icon_state = "barn" sound = 'sound/magic/HorseHead_curse.ogg' -/obj/effect/proc_holder/spell/targeted/horsemask/cast(list/targets, mob/user = usr) +/obj/effect/proc_holder/spell/targeted/click/horsemask/cast(list/targets, mob/user = usr) if(!targets.len) to_chat(user, "No target found in range.") return - var/mob/living/carbon/target = targets[1] - - if(!target) - return - - - if(!ishuman(target)) - to_chat(user, "It'd be stupid to curse [target] with a horse's head!") - return - - if(!(target in oview(range)))//If they are not in overview after selection. - to_chat(user, "They are too far away!") - return + var/mob/living/carbon/human/target = targets[1] var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead magichead.flags |= NODROP | DROPDEL //curses! diff --git a/code/datums/spells/knock.dm b/code/datums/spells/knock.dm index 88f5eec9474..e4c652ae1d3 100644 --- a/code/datums/spells/knock.dm +++ b/code/datums/spells/knock.dm @@ -13,11 +13,6 @@ action_icon_state = "knock" sound = 'sound/magic/knock.ogg' -// Knock doesn't need to generate an attack log for every turf, set `make_attack_logs` to FALSE and just create a custom one. -/obj/effect/proc_holder/spell/aoe_turf/knock/perform(list/targets, recharge, mob/user) - add_attack_logs(user, user, "cast the spell [name]", ATKLOG_ALL) - return ..(targets, recharge, user, make_attack_logs = FALSE) - /obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets, mob/user = usr) for(var/turf/T in targets) for(var/obj/machinery/door/door in T.contents) diff --git a/code/datums/spells/lightning.dm b/code/datums/spells/lightning.dm index e7821aed6c2..20cd8dccdf5 100644 --- a/code/datums/spells/lightning.dm +++ b/code/datums/spells/lightning.dm @@ -25,10 +25,10 @@ /obj/effect/proc_holder/spell/targeted/lightning/Click() if(!ready && start_time == 0) - if(cast_check()) + if(cast_check(TRUE, FALSE, usr)) StartChargeup() else - if(ready && cast_check(skipcharge=1)) + if(ready && cast_check(TRUE, TRUE, usr)) choose_targets() return 1 @@ -44,7 +44,7 @@ if(ready) Discharge() -obj/effect/proc_holder/spell/targeted/lightning/proc/Reset(mob/user = usr) +/obj/effect/proc_holder/spell/targeted/lightning/proc/Reset(mob/user = usr) ready = 0 start_time = 0 if(halo) diff --git a/code/datums/spells/magnet.dm b/code/datums/spells/magnet.dm index fea6780085c..155a3cb0b45 100644 --- a/code/datums/spells/magnet.dm +++ b/code/datums/spells/magnet.dm @@ -20,10 +20,10 @@ /obj/effect/proc_holder/spell/targeted/magnet/Click() if(!ready && start_time == 0) - if(cast_check()) + if(cast_check(TRUE, FALSE, usr)) StartChargeup() else - if(ready && cast_check(skipcharge=1)) + if(ready && cast_check(TRUE, TRUE, usr)) choose_targets() return 1 @@ -39,7 +39,7 @@ if(ready) Discharge() -obj/effect/proc_holder/spell/targeted/magnet/proc/Reset(mob/user = usr) +/obj/effect/proc_holder/spell/targeted/magnet/proc/Reset(mob/user = usr) ready = 0 energy = 0 start_time = 0 diff --git a/code/datums/spells/mind_transfer.dm b/code/datums/spells/mind_transfer.dm index 3d9031a8e03..e4924ee1c2f 100644 --- a/code/datums/spells/mind_transfer.dm +++ b/code/datums/spells/mind_transfer.dm @@ -1,4 +1,4 @@ -/obj/effect/proc_holder/spell/targeted/mind_transfer +/obj/effect/proc_holder/spell/targeted/click/mind_transfer name = "Mind Transfer" desc = "This spell allows the user to switch bodies with a target." @@ -8,33 +8,29 @@ invocation = "GIN'YU CAPAN" invocation_type = "whisper" range = 1 + click_radius = 0 // Still gotta be pretty accurate + selection_activated_message = "You prepare to transfer your mind. Click on a target to cast the spell." + selection_deactivated_message = "You decide that your current form is good enough." cooldown_min = 200 //100 deciseconds reduction per rank var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell var/paralysis_amount_victim = 20 //how much the victim is paralysed for after the spell action_icon_state = "mindswap" +/obj/effect/proc_holder/spell/targeted/click/mind_transfer/valid_target(mob/living/target, user) + if(!..()) + return FALSE + return target.stat != DEAD && target.key && target.mind + /* Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do. Make sure spells that are removed from spell_list are actually removed and deleted when mind transfering. Also, you never added distance checking after target is selected. I've went ahead and did that. */ -/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/user = usr, distanceoverride) +/obj/effect/proc_holder/spell/targeted/click/mind_transfer/cast(list/targets, mob/user = usr) var/mob/living/target = targets[range] - if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it. - to_chat(user, "They are too far away!") - return - - if(target.stat == DEAD) - to_chat(user, "You don't particularly want to be dead.") - return - - if(!target.key || !target.mind) - to_chat(user, "[target.p_they(TRUE)] appear[target.p_s()] to be catatonic. Not even magic can affect [target.p_their()] vacant mind.") - return - if(user.suiciding) to_chat(user, "You're killing yourself! You can't concentrate enough to do this!") return diff --git a/code/datums/spells/wizard.dm b/code/datums/spells/wizard.dm index 94383f1b1e2..a99655222a4 100644 --- a/code/datums/spells/wizard.dm +++ b/code/datums/spells/wizard.dm @@ -308,76 +308,50 @@ duration = 300 sound = 'sound/magic/blind.ogg' -/obj/effect/proc_holder/spell/fireball +/obj/effect/proc_holder/spell/targeted/click/fireball name = "Fireball" desc = "This spell fires a fireball at a target and does not require wizard garb." school = "evocation" charge_max = 60 - clothes_req = 0 + clothes_req = FALSE invocation = "ONI SOMA" invocation_type = "shout" + auto_target_single = FALSE // Having this true won't ever find a single target and is just lost processing power range = 20 cooldown_min = 20 //10 deciseconds reduction per rank + + click_radius = -1 + selection_activated_message = "Your prepare to cast your fireball spell! Left-click to cast at a target!" + selection_deactivated_message = "You extinguish your fireball...for now." + allowed_type = /atom // FIRE AT EVERYTHING + var/fireball_type = /obj/item/projectile/magic/fireball action_icon_state = "fireball0" sound = 'sound/magic/fireball.ogg' active = FALSE -/obj/effect/proc_holder/spell/fireball/Click() - var/mob/living/user = usr - if(!istype(user)) - return - - var/msg - - if(!can_cast(user)) - msg = "You can no longer cast Fireball." - remove_ranged_ability(user, msg) - return - - if(active) - msg = "You extinguish your fireball...for now." - remove_ranged_ability(user, msg) - else - msg = "Your prepare to cast your fireball spell! Left-click to cast at a target!" - add_ranged_ability(user, msg) - -/obj/effect/proc_holder/spell/fireball/update_icon() +/obj/effect/proc_holder/spell/targeted/click/fireball/update_icon() if(!action) return action.button_icon_state = "fireball[active]" action.UpdateButtonIcon() -/obj/effect/proc_holder/spell/fireball/InterceptClickOn(mob/living/user, params, atom/target) - if(..()) - return FALSE - - if(!cast_check(0, user)) - remove_ranged_ability(user) - return FALSE - - var/list/targets = list(target) - perform(targets, user = user) - - return TRUE - -/obj/effect/proc_holder/spell/fireball/cast(list/targets, mob/living/user = usr) +/obj/effect/proc_holder/spell/targeted/click/fireball/cast(list/targets, mob/living/user = usr) var/target = targets[1] //There is only ever one target for fireball var/turf/T = user.loc var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction if(!isturf(U) || !isturf(T)) - return 0 + return FALSE var/obj/item/projectile/magic/fireball/FB = new fireball_type(user.loc) FB.current = get_turf(user) FB.preparePixelProjectile(target, get_turf(target), user) FB.fire() user.newtonian_move(get_dir(U, T)) - remove_ranged_ability(user) - return 1 + return TRUE /obj/effect/proc_holder/spell/aoe_turf/repulse name = "Repulse" diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index a18a9d74f28..98272825e2f 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -55,6 +55,8 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY var/list/announce_beacons = list() // Particular beacons that we'll notify the relevant department when we reach var/special = FALSE //Event/Station Goals/Admin enabled packs var/special_enabled = FALSE + /// List of names for being done in TGUI + var/list/tgui_manifest = list() /datum/supply_packs/New() @@ -63,8 +65,12 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY if(!path) continue var/atom/movable/AM = path manifest += "
  • [initial(AM.name)]
  • " + // Add the name to the UI manifest + tgui_manifest += "[initial(AM.name)]" manifest += "" + + ////// Use the sections to keep things tidy please /Malkevin ////////////////////////////////////////////////////////////////////////////// @@ -344,6 +350,14 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY cost = 15 containername = "tactical armor crate" +/datum/supply_packs/security/armory/webbing + name = "Webbing Crate" + contains = list(/obj/item/storage/belt/security/webbing, + /obj/item/storage/belt/security/webbing, + /obj/item/storage/belt/security/webbing) + cost = 15 + containername = "tactical webbing crate" + /datum/supply_packs/security/armory/swat name = "SWAT gear crate" contains = list(/obj/item/clothing/head/helmet/swat, @@ -633,7 +647,6 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY contains = list(/obj/machinery/power/emitter, /obj/machinery/power/emitter) cost = 10 - containertype = /obj/structure/closet/crate/secure containername = "emitter crate" access = ACCESS_CE containertype = /obj/structure/closet/crate/secure/engineering @@ -717,7 +730,7 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY name = "Supermatter Shard Crate" contains = list(/obj/machinery/power/supermatter_shard) cost = 50 //So cargo thinks twice before killing themselves with it - containertype = /obj/structure/closet/crate/secure + containertype = /obj/structure/closet/crate/secure/engineering containername = "supermatter shard crate" access = ACCESS_CE @@ -728,7 +741,7 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY /obj/item/pipe/circulator, /obj/item/pipe/circulator) cost = 25 - containertype = /obj/structure/closet/crate/secure + containertype = /obj/structure/closet/crate/secure/engineering containername = "thermo-electric generator crate" access = ACCESS_CE announce_beacons = list("Engineering" = list("Chief Engineer's Desk", "Atmospherics")) @@ -981,12 +994,6 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY containername = "shield generators crate" access = ACCESS_TELEPORTER -/datum/supply_packs/science/modularpc - name = "Deluxe Silicate Selections restocking unit" - cost = 15 - contains = list(/obj/item/vending_refill/modularpc) - containername = "computer supply crate" - /datum/supply_packs/science/transfer_valves name = "Tank Transfer Valves Crate" contains = list(/obj/item/transfer_valve, @@ -1174,11 +1181,10 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY containername = "fox crate" /datum/supply_packs/organic/butterfly - name = "Butterflies Crate" + name = "Butterfly Crate" cost = 50 containertype = /obj/structure/closet/critter/butterfly - containername = "butterflies crate" - contraband = 1 + containername = "butterfly crate" /datum/supply_packs/organic/deer name = "Deer Crate" @@ -1767,6 +1773,16 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY cost = 20 containername = "polo supply crate" +/datum/supply_packs/misc/boxing //For non log spamming cargo brawls! + name = "Boxing Supply Crate" + // 4 boxing gloves + contains = list(/obj/item/clothing/gloves/boxing/blue, + /obj/item/clothing/gloves/boxing/green, + /obj/item/clothing/gloves/boxing/yellow, + /obj/item/clothing/gloves/boxing) + cost = 15 + containername = "boxing supply crate" + ///////////// Station Goals /datum/supply_packs/misc/station_goal diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm index 4814e27380b..389b21dd6d1 100644 --- a/code/datums/uplink_item.dm +++ b/code/datums/uplink_item.dm @@ -59,10 +59,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) return uplink_items -/datum/nano_item_lists - var/list/items_nano - var/list/items_reference - // You can change the order of the list by putting datums before/after one another OR // you can use the last variable to make sure it appears last, well have the category appear last. @@ -412,9 +408,9 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/jobspecific/energizedfireaxe name = "Energized Fire Axe" - desc = "A fire axe with a massive electrical charge built into it. It can release this charge on its first victim and will be rather plain after that." + desc = "A fire axe with a massive energy charge built into it. Upon striking someone while charged it will throw them backwards while stunning them briefly, but will take some time to charge up again. It is also much sharper than a regular axe and can pierce light armor." reference = "EFA" - item = /obj/item/twohanded/energizedfireaxe + item = /obj/item/twohanded/fireaxe/energized cost = 10 job = list("Life Support Specialist") @@ -736,6 +732,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 12 // normally 18 gamemodes = list(/datum/game_mode/nuclear) +/datum/uplink_item/ammo/bulldog_XLmagsbag + name = "Bulldog - 12g XL Magazine Duffel Bag" + desc = "A duffel bag containing three 16 round drum magazines(Slug, Buckshot, Dragon's Breath)." + reference = "12XLDB" + item = /obj/item/storage/backpack/duffel/syndie/ammo/shotgunXLmags + cost = 12 // normally 18 + gamemodes = list(/datum/game_mode/nuclear) + /datum/uplink_item/ammo/smg name = "C-20r - .45 Magazine" desc = "An additional 20-round .45 magazine for use in the C-20r submachine gun. These bullets pack a lot of punch that can knock most targets down, but do limited overall damage." @@ -1175,7 +1179,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/stealthy_tools/camera_bug name = "Camera Bug" - desc = "Enables you to view all cameras on the network and track a target. Bugging cameras allows you to disable them remotely." + desc = "Enables you to view all cameras on the network to track a target." reference = "CB" item = /obj/item/camera_bug cost = 1 @@ -1555,11 +1559,11 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 10 /datum/uplink_item/cyber_implants/antistun - name = "CNS Rebooter Implant" - desc = "This implant will help you get back up on your feet faster after being stunned. \ + name = "Hardened CNS Rebooter Implant" + desc = "This implant will help you get back up on your feet faster after being stunned. It is invulnerable to EMPs. \ Comes with an automated implanting tool." reference = "CIAS" - item = /obj/item/organ/internal/cyberimp/brain/anti_stun + item = /obj/item/organ/internal/cyberimp/brain/anti_stun/hardened cost = 12 /datum/uplink_item/cyber_implants/reviver @@ -1725,8 +1729,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) U.purchase_log += "[bicon(C)]" for(var/item in bought_items) - new item(C) - U.purchase_log += "[bicon(item)]" + var/obj/purchased = new item(C) + U.purchase_log += "[bicon(purchased)]" log_game("[key_name(usr)] purchased a surplus crate with [jointext(itemlog, ", ")]") /datum/uplink_item/bundles_TC/telecrystal diff --git a/code/datums/weather/weather_types/floor_is_lava.dm b/code/datums/weather/weather_types/floor_is_lava.dm index 9f98a481dbf..0233e0aee71 100644 --- a/code/datums/weather/weather_types/floor_is_lava.dm +++ b/code/datums/weather/weather_types/floor_is_lava.dm @@ -35,6 +35,8 @@ return if(!L.client) //Only sentient people are going along with it! return + if(L.flying) + return L.adjustFireLoss(3) /datum/weather/floor_is_lava/fake diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm index dcde08d7ec1..ff7a0169c15 100644 --- a/code/datums/wires/airlock.dm +++ b/code/datums/wires/airlock.dm @@ -1,67 +1,29 @@ // Wires for airlocks /datum/wires/airlock/secure - random = 1 + randomize = TRUE /datum/wires/airlock holder_type = /obj/machinery/door/airlock - wire_count = 12 - window_x = 410 - window_y = 570 + wire_count = 12 // 10 actual, 2 duds. + proper_name = "Airlock" + window_x = 400 + window_y = 101 -#define AIRLOCK_WIRE_IDSCAN 1 -#define AIRLOCK_WIRE_MAIN_POWER1 2 -#define AIRLOCK_WIRE_DOOR_BOLTS 4 -#define AIRLOCK_WIRE_BACKUP_POWER1 8 -#define AIRLOCK_WIRE_OPEN_DOOR 16 -#define AIRLOCK_WIRE_AI_CONTROL 32 -#define AIRLOCK_WIRE_ELECTRIFY 64 -#define AIRLOCK_WIRE_SAFETY 128 -#define AIRLOCK_WIRE_SPEED 256 -#define AIRLOCK_WIRE_LIGHT 512 +/datum/wires/airlock/New(atom/_holder) + wires = list( + WIRE_IDSCAN, WIRE_MAIN_POWER1, WIRE_DOOR_BOLTS, WIRE_BACKUP_POWER1, WIRE_OPEN_DOOR, + WIRE_AI_CONTROL, WIRE_ELECTRIFY, WIRE_SAFETY, WIRE_SPEED, WIRE_BOLT_LIGHT + ) + return ..() -/datum/wires/airlock/GetWireName(index) - switch(index) - if(AIRLOCK_WIRE_IDSCAN) - return "ID Scan" - - if(AIRLOCK_WIRE_MAIN_POWER1) - return "Primary Power" - - if(AIRLOCK_WIRE_DOOR_BOLTS) - return "Door Bolts" - - if(AIRLOCK_WIRE_BACKUP_POWER1) - return "Primary Backup Power" - - if(AIRLOCK_WIRE_OPEN_DOOR) - return "Door State" - - if(AIRLOCK_WIRE_AI_CONTROL) - return "AI Control" - - if(AIRLOCK_WIRE_ELECTRIFY) - return "Electrification" - - if(AIRLOCK_WIRE_ELECTRIFY) - return "Door Safeties" - - if(AIRLOCK_WIRE_ELECTRIFY) - return "Door Timing" - - if(AIRLOCK_WIRE_ELECTRIFY) - return "Bolt Lights" - -/datum/wires/airlock/CanUse(mob/living/L) +/datum/wires/airlock/interactable(mob/user) var/obj/machinery/door/airlock/A = holder - if(iscarbon(L)) - if(A.Adjacent(L)) - if(A.isElectrified()) - if(A.shock(L, 100)) - return 0 + if(iscarbon(user) && A.Adjacent(user) && A.isElectrified() && A.shock(user, 100)) + return FALSE if(A.panel_open) - return 1 - return 0 + return TRUE + return FALSE /datum/wires/airlock/get_status() . = ..() @@ -70,21 +32,20 @@ . += "The door bolts [A.locked ? "have fallen!" : "look up."]" . += "The door bolt lights are [(A.lights && haspower) ? "on." : "off!"]" - . += "The test light is [haspower ? "on." : "off!"]" - . += "The 'AI control allowed' light is [(A.aiControlDisabled == 0 && !A.emagged && haspower) ? "on" : "off"]." + . += "The test light is [haspower ? "on." : "off!"]" + . += "The 'AI control allowed' light is [(A.aiControlDisabled == AICONTROLDISABLED_OFF && !A.emagged && haspower) ? "on" : "off"]." . += "The 'Check Wiring' light is [(A.safe == 0 && haspower) ? "on" : "off"]." . += "The 'Check Timing Mechanism' light is [(A.normalspeed == 0 && haspower) ? "on" : "off"]." . += "The emergency lights are [(A.emergency && haspower) ? "on" : "off"]." -/datum/wires/airlock/UpdateCut(index, mended) - +/datum/wires/airlock/on_cut(wire, mend) var/obj/machinery/door/airlock/A = holder - switch(index) - if(AIRLOCK_WIRE_IDSCAN) - A.aiDisabledIdScanner = !mended - if(AIRLOCK_WIRE_MAIN_POWER1) + switch(wire) + if(WIRE_IDSCAN) + A.aiDisabledIdScanner = !mend + if(WIRE_MAIN_POWER1) - if(!mended) + if(!mend) //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 crowbarred open, but bolts-raising will not work. Cutting these wires may electocute the user. A.loseMainPower() A.shock(usr, 50) @@ -92,9 +53,9 @@ A.regainMainPower() A.shock(usr, 50) - if(AIRLOCK_WIRE_BACKUP_POWER1) + if(WIRE_BACKUP_POWER1) - if(!mended) + if(!mend) //Cutting either one disables the backup door power (allowing it to be crowbarred open, but disabling bolts-raising), but may electocute the user. A.loseBackupPower() A.shock(usr, 50) @@ -102,66 +63,66 @@ A.regainBackupPower() A.shock(usr, 50) - if(AIRLOCK_WIRE_DOOR_BOLTS) + if(WIRE_DOOR_BOLTS) - if(!mended) + if(!mend) //Cutting this wire also drops the door bolts, and mending it does not raise them. (This is what happens now, except there are a lot more wires going to door bolts at present) A.lock(1) A.update_icon() - if(AIRLOCK_WIRE_AI_CONTROL) + if(WIRE_AI_CONTROL) - if(!mended) + if(!mend) //one wire for AI control. 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. - //aiControlDisabled: 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. - if(A.aiControlDisabled == 0) - A.aiControlDisabled = 1 - else if(A.aiControlDisabled == -1) - A.aiControlDisabled = 2 + //aiControlDisabled: see explanation in code\__DEFINES\construction.dm#32 + if(A.aiControlDisabled == AICONTROLDISABLED_OFF) + A.aiControlDisabled = AICONTROLDISABLED_ON + else if(A.aiControlDisabled == AICONTROLDISABLED_PERMA) + A.aiControlDisabled = AICONTROLDISABLED_BYPASS else - if(A.aiControlDisabled == 1) - A.aiControlDisabled = 0 - else if(A.aiControlDisabled == 2) - A.aiControlDisabled = -1 + if(A.aiControlDisabled == AICONTROLDISABLED_ON) + A.aiControlDisabled = AICONTROLDISABLED_OFF + else if(A.aiControlDisabled == AICONTROLDISABLED_BYPASS) + A.aiControlDisabled = AICONTROLDISABLED_PERMA - if(AIRLOCK_WIRE_ELECTRIFY) - if(!mended) + if(WIRE_ELECTRIFY) + if(!mend) //Cutting this wire electrifies the door, so that the next person to touch the door without insulated gloves gets electrocuted. A.electrify(-1) else A.electrify(0) return // Don't update the dialog. - if(AIRLOCK_WIRE_SAFETY) - A.safe = mended + if(WIRE_SAFETY) + A.safe = mend - if(AIRLOCK_WIRE_SPEED) - A.autoclose = mended - if(mended) + if(WIRE_SPEED) + A.autoclose = mend + if(mend) if(!A.density) - spawn(0) - A.close() + INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/close) - if(AIRLOCK_WIRE_LIGHT) - A.lights = mended + if(WIRE_BOLT_LIGHT) + A.lights = mend A.update_icon() ..() -/datum/wires/airlock/UpdatePulsed(index) - +/datum/wires/airlock/on_pulse(wire) var/obj/machinery/door/airlock/A = holder - switch(index) - if(AIRLOCK_WIRE_IDSCAN) + switch(wire) + if(WIRE_IDSCAN) //Sending a pulse through flashes the red light on the door (if the door has power). if(A.arePowerSystemsOn() && A.density) A.do_animate("deny") if(A.emergency) A.emergency = 0 A.update_icon() - if(AIRLOCK_WIRE_MAIN_POWER1) + + if(WIRE_MAIN_POWER1) //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). A.loseMainPower() - if(AIRLOCK_WIRE_DOOR_BOLTS) + + if(WIRE_DOOR_BOLTS) //one wire for door bolts. Sending a pulse through this drops door bolts if they're not down (whether power's on or not), //raises them if they are down (only if power's on) if(!A.locked) @@ -170,45 +131,41 @@ else if(A.unlock()) A.audible_message("You hear a click from the bottom of the door.", hearing_distance = 1) - if(AIRLOCK_WIRE_BACKUP_POWER1) + if(WIRE_BACKUP_POWER1) //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). A.loseBackupPower() - if(AIRLOCK_WIRE_AI_CONTROL) - if(A.aiControlDisabled == 0) - A.aiControlDisabled = 1 - else if(A.aiControlDisabled == -1) - A.aiControlDisabled = 2 - spawn(10) - if(A) - if(A.aiControlDisabled == 1) - A.aiControlDisabled = 0 - else if(A.aiControlDisabled == 2) - A.aiControlDisabled = -1 + if(WIRE_AI_CONTROL) + if(A.aiControlDisabled == AICONTROLDISABLED_OFF) + A.aiControlDisabled = AICONTROLDISABLED_ON + else if(A.aiControlDisabled == AICONTROLDISABLED_PERMA) + A.aiControlDisabled = AICONTROLDISABLED_BYPASS - if(AIRLOCK_WIRE_ELECTRIFY) + addtimer(CALLBACK(A, /obj/machinery/door/airlock/.proc/ai_control_callback), 1 SECONDS) + + if(WIRE_ELECTRIFY) //one wire for electrifying the door. Sending a pulse through this electrifies the door for 30 seconds. A.electrify(30) - if(AIRLOCK_WIRE_OPEN_DOOR) + + if(WIRE_OPEN_DOOR) //tries to open the door without ID //will succeed only if the ID wire is cut or the door requires no access and it's not emagged if(A.emagged) return if(!A.requiresID() || A.check_access(null)) - spawn(0) - if(A.density) - A.open() - else - A.close() - if(AIRLOCK_WIRE_SAFETY) + if(A.density) + INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/open) + else + INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/close) + + if(WIRE_SAFETY) A.safe = !A.safe if(!A.density) - spawn(0) - A.close() + INVOKE_ASYNC(A, /obj/machinery/door/airlock/.proc/close) - if(AIRLOCK_WIRE_SPEED) + if(WIRE_SPEED) A.normalspeed = !A.normalspeed - if(AIRLOCK_WIRE_LIGHT) + if(WIRE_BOLT_LIGHT) A.lights = !A.lights A.update_icon() diff --git a/code/datums/wires/alarm.dm b/code/datums/wires/alarm.dm index f6b49b0dd99..cd13c9fe1d8 100644 --- a/code/datums/wires/alarm.dm +++ b/code/datums/wires/alarm.dm @@ -2,35 +2,22 @@ /datum/wires/alarm holder_type = /obj/machinery/alarm wire_count = 5 + window_x = 385 + window_y = 90 + proper_name = "Air alarm" -#define AALARM_WIRE_IDSCAN 1 -#define AALARM_WIRE_POWER 2 -#define AALARM_WIRE_SYPHON 4 -#define AALARM_WIRE_AI_CONTROL 8 -#define AALARM_WIRE_AALARM 16 +/datum/wires/alarm/New(atom/_holder) + wires = list( + WIRE_IDSCAN , WIRE_MAIN_POWER1 , WIRE_SYPHON, + WIRE_AI_CONTROL, WIRE_AALARM + ) + return ..() -/datum/wires/alarm/GetWireName(index) - switch(index) - if(AALARM_WIRE_IDSCAN) - return "ID Scan" - - if(AALARM_WIRE_POWER) - return "Power" - - if(AALARM_WIRE_SYPHON) - return "Syphon" - - if(AALARM_WIRE_AI_CONTROL) - return "AI Control" - - if(AALARM_WIRE_AALARM) - return "Atmospherics Alarm" - -/datum/wires/alarm/CanUse(mob/living/L) +/datum/wires/alarm/interactable(mob/user) var/obj/machinery/alarm/A = holder if(A.wiresexposed) - return 1 - return 0 + return TRUE + return FALSE /datum/wires/alarm/get_status() . = ..() @@ -39,75 +26,60 @@ . += "The Air Alarm is [(A.shorted || (A.stat & (NOPOWER|BROKEN))) ? "offline." : "working properly!"]" . += "The 'AI control allowed' light is [A.aidisabled ? "off" : "on"]." -/datum/wires/alarm/UpdateCut(index, mended) +/datum/wires/alarm/on_cut(wire, mend) var/obj/machinery/alarm/A = holder - switch(index) - if(AALARM_WIRE_IDSCAN) - if(!mended) + switch(wire) + if(WIRE_IDSCAN) + if(!mend) A.locked = 1 -// to_chat(world, "Idscan wire cut") - if(AALARM_WIRE_POWER) + if(WIRE_MAIN_POWER1) A.shock(usr, 50) - A.shorted = !mended + A.shorted = !mend A.update_icon() -// to_chat(world, "Power wire cut") - if(AALARM_WIRE_AI_CONTROL) - A.aidisabled = !mended -// to_chat(world, "AI Control Wire Cut") + if(WIRE_AI_CONTROL) + A.aidisabled = !mend - if(AALARM_WIRE_SYPHON) - if(!mended) + if(WIRE_SYPHON) + if(!mend) A.mode = 3 // AALARM_MODE_PANIC A.apply_mode() -// to_chat(world, "Syphon Wire Cut") - if(AALARM_WIRE_AALARM) - if(A.alarm_area.atmosalert(2, A)) - A.post_alert(2) + if(WIRE_AALARM) + if(A.alarm_area.atmosalert(ATMOS_ALARM_DANGER, A)) + A.post_alert(ATMOS_ALARM_DANGER) A.update_icon() ..() -/datum/wires/alarm/UpdatePulsed(index) +/datum/wires/alarm/on_pulse(wire) var/obj/machinery/alarm/A = holder - switch(index) - if(AALARM_WIRE_IDSCAN) + switch(wire) + if(WIRE_IDSCAN) A.locked = !A.locked -// to_chat(world, "Idscan wire pulsed") - if(AALARM_WIRE_POWER) -// to_chat(world, "Power wire pulsed") - if(A.shorted == 0) - A.shorted = 1 + if(WIRE_MAIN_POWER1) + if(!A.shorted) + A.shorted = TRUE A.update_icon() + addtimer(CALLBACK(A, /obj/machinery/alarm/.proc/unshort_callback), 120 SECONDS) - spawn(12000) - if(A.shorted == 1) - A.shorted = 0 - A.update_icon() - - - if(AALARM_WIRE_AI_CONTROL) -// to_chat(world, "AI Control wire pulsed") - if(A.aidisabled == 0) - A.aidisabled = 1 + if(WIRE_AI_CONTROL) + if(!A.aidisabled) + A.aidisabled = TRUE A.updateDialog() - spawn(100) - if(A.aidisabled == 1) - A.aidisabled = 0 + addtimer(CALLBACK(A, /obj/machinery/alarm/.proc/enable_ai_control_callback), 10 SECONDS) - if(AALARM_WIRE_SYPHON) -// to_chat(world, "Syphon wire pulsed") + + if(WIRE_SYPHON) if(A.mode == 1) // AALARM_MODE_SCRUB A.mode = 3 // AALARM_MODE_PANIC else A.mode = 1 // AALARM_MODE_SCRUB A.apply_mode() - if(AALARM_WIRE_AALARM) -// to_chat(world, "Aalarm wire pulsed") - if(A.alarm_area.atmosalert(0, A)) - A.post_alert(0) + if(WIRE_AALARM) + if(A.alarm_area.atmosalert(ATMOS_ALARM_NONE, A)) + A.post_alert(ATMOS_ALARM_NONE) A.update_icon() ..() diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm index b1076c8e29b..49931d8fa31 100644 --- a/code/datums/wires/apc.dm +++ b/code/datums/wires/apc.dm @@ -1,25 +1,13 @@ /datum/wires/apc holder_type = /obj/machinery/power/apc wire_count = 4 + proper_name = "APC" + window_x = 355 + window_y = 97 -#define APC_WIRE_IDSCAN 1 -#define APC_WIRE_MAIN_POWER1 2 -#define APC_WIRE_MAIN_POWER2 4 -#define APC_WIRE_AI_CONTROL 8 - -/datum/wires/apc/GetWireName(index) - switch(index) - if(APC_WIRE_IDSCAN) - return "ID Scan" - - if(APC_WIRE_MAIN_POWER1) - return "Primary Power" - - if(APC_WIRE_MAIN_POWER2) - return "Secondary Power" - - if(APC_WIRE_AI_CONTROL) - return "AI Control" +/datum/wires/apc/New(atom/_holder) + wires = list(WIRE_IDSCAN, WIRE_MAIN_POWER1, WIRE_MAIN_POWER2, WIRE_AI_CONTROL) + return ..() /datum/wires/apc/get_status() . = ..() @@ -29,66 +17,53 @@ . += "The 'AI control allowed' light is [A.aidisabled ? "off" : "on"]." -/datum/wires/apc/CanUse(mob/living/L) +/datum/wires/apc/interactable(mob/user) var/obj/machinery/power/apc/A = holder if(A.panel_open && !A.opened) return TRUE return FALSE -/datum/wires/apc/UpdatePulsed(index) +/datum/wires/apc/on_pulse(wire) var/obj/machinery/power/apc/A = holder - switch(index) + switch(wire) + if(WIRE_IDSCAN) + A.locked = FALSE + addtimer(CALLBACK(A, /obj/machinery/power/apc/.proc/relock_callback), 30 SECONDS) - if(APC_WIRE_IDSCAN) - A.locked = 0 - spawn(300) - if(A) - A.locked = 1 - A.updateDialog() + if(WIRE_MAIN_POWER1, WIRE_MAIN_POWER2) + if(!A.shorted) + A.shorted = TRUE + addtimer(CALLBACK(A, /obj/machinery/power/apc/.proc/check_main_power_callback), 120 SECONDS) - if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2) - if(A.shorted == 0) - A.shorted = 1 - spawn(1200) - if(A && !IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2)) - A.shorted = 0 - A.updateDialog() - - if(APC_WIRE_AI_CONTROL) - if(A.aidisabled == 0) - A.aidisabled = 1 - - spawn(10) - if(A && !IsIndexCut(APC_WIRE_AI_CONTROL)) - A.aidisabled = 0 - A.updateDialog() + if(WIRE_AI_CONTROL) + if(!A.aidisabled) + A.aidisabled = TRUE + addtimer(CALLBACK(A, /obj/machinery/power/apc/.proc/check_ai_control_callback), 1 SECONDS) ..() -/datum/wires/apc/UpdateCut(index, mended) +/datum/wires/apc/on_cut(wire, mend) var/obj/machinery/power/apc/A = holder - switch(index) - if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2) - - if(!mended) + switch(wire) + if(WIRE_MAIN_POWER1, WIRE_MAIN_POWER2) + if(!mend) A.shock(usr, 50) - A.shorted = 1 + A.shorted = TRUE - else if(!IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2)) - A.shorted = 0 + else if(!is_cut(WIRE_MAIN_POWER1) && !is_cut(WIRE_MAIN_POWER2)) + A.shorted = FALSE A.shock(usr, 50) - if(APC_WIRE_AI_CONTROL) - - if(!mended) - if(A.aidisabled == 0) - A.aidisabled = 1 + if(WIRE_AI_CONTROL) + if(!mend) + if(!A.aidisabled) + A.aidisabled = TRUE else - if(A.aidisabled == 1) - A.aidisabled = 0 + if(A.aidisabled) + A.aidisabled = FALSE ..() diff --git a/code/datums/wires/autolathe.dm b/code/datums/wires/autolathe.dm index c1413abd0cc..0d2aab5a81f 100644 --- a/code/datums/wires/autolathe.dm +++ b/code/datums/wires/autolathe.dm @@ -1,21 +1,13 @@ /datum/wires/autolathe holder_type = /obj/machinery/autolathe wire_count = 10 + proper_name = "Autolathe" + window_x = 340 + window_y = 55 -#define AUTOLATHE_HACK_WIRE 1 -#define AUTOLATHE_SHOCK_WIRE 2 -#define AUTOLATHE_DISABLE_WIRE 4 - -/datum/wires/autolathe/GetWireName(index) - switch(index) - if(AUTOLATHE_HACK_WIRE) - return "Hack" - - if(AUTOLATHE_SHOCK_WIRE) - return "Shock" - - if(AUTOLATHE_DISABLE_WIRE) - return "Disable" +/datum/wires/autolathe/New(atom/_holder) + wires = list(WIRE_AUTOLATHE_HACK, WIRE_ELECTRIFY, WIRE_AUTOLATHE_DISABLE) + return ..() /datum/wires/autolathe/get_status() . = ..() @@ -24,51 +16,38 @@ . += "The green light is [A.shocked ? "off" : "on"]." . += "The blue light is [A.hacked ? "off" : "on"]." -/datum/wires/autolathe/CanUse() +/datum/wires/autolathe/interactable(mob/user) var/obj/machinery/autolathe/A = holder + if(iscarbon(user) && A.Adjacent(user) && A.shocked && A.shock(user, 100)) + return FALSE if(A.panel_open) - return 1 - return 0 + return TRUE + return FALSE -/datum/wires/autolathe/UpdateCut(index, mended) +/datum/wires/autolathe/on_cut(wire, mend) var/obj/machinery/autolathe/A = holder - switch(index) - if(AUTOLATHE_HACK_WIRE) - A.adjust_hacked(!mended) - if(AUTOLATHE_SHOCK_WIRE) - A.shocked = !mended - if(AUTOLATHE_DISABLE_WIRE) - A.disabled = !mended + switch(wire) + if(WIRE_AUTOLATHE_HACK) + A.adjust_hacked(!mend) + if(WIRE_ELECTRIFY) + A.shocked = !mend + if(WIRE_AUTOLATHE_DISABLE) + A.disabled = !mend ..() -/datum/wires/autolathe/UpdatePulsed(index) - if(IsIndexCut(index)) +/datum/wires/autolathe/on_pulse(wire) + if(is_cut(wire)) return var/obj/machinery/autolathe/A = holder - switch(index) - if(AUTOLATHE_HACK_WIRE) + switch(wire) + if(WIRE_AUTOLATHE_HACK) A.adjust_hacked(!A.hacked) - updateUIs() - spawn(50) - if(A && !IsIndexCut(index)) - A.adjust_hacked(0) - updateUIs() - if(AUTOLATHE_SHOCK_WIRE) - A.shocked = !A.shocked - updateUIs() - spawn(50) - if(A && !IsIndexCut(index)) - A.shocked = 0 - updateUIs() - if(AUTOLATHE_DISABLE_WIRE) - A.disabled = !A.disabled - updateUIs() - spawn(50) - if(A && !IsIndexCut(index)) - A.disabled = 0 - updateUIs() + addtimer(CALLBACK(A, /obj/machinery/autolathe/.proc/check_hacked_callback), 5 SECONDS) -/datum/wires/autolathe/proc/updateUIs() - SSnanoui.update_uis(src) - if(holder) - SSnanoui.update_uis(holder) + if(WIRE_ELECTRIFY) + A.shocked = !A.shocked + addtimer(CALLBACK(A, /obj/machinery/autolathe/.proc/check_electrified_callback), 5 SECONDS) + + if(WIRE_AUTOLATHE_DISABLE) + A.disabled = !A.disabled + addtimer(CALLBACK(A, /obj/machinery/autolathe/.proc/check_disabled_callback), 5 SECONDS) diff --git a/code/datums/wires/camera.dm b/code/datums/wires/camera.dm index a6b1549fbe1..39e0cb2c3e1 100644 --- a/code/datums/wires/camera.dm +++ b/code/datums/wires/camera.dm @@ -1,9 +1,15 @@ // Wires for cameras. /datum/wires/camera - random = 0 holder_type = /obj/machinery/camera wire_count = 2 + proper_name = "Camera" + window_x = 350 + window_y = 95 + +/datum/wires/camera/New(atom/_holder) + wires = list(WIRE_FOCUS, WIRE_MAIN_POWER1) + return ..() /datum/wires/camera/get_status() . = ..() @@ -11,51 +17,40 @@ . += "The focus light is [(C.view_range == initial(C.view_range)) ? "on" : "off"]." . += "The power link light is [C.can_use() ? "on" : "off"]." -/datum/wires/camera/CanUse(mob/living/L) +/datum/wires/camera/interactable(mob/user) var/obj/machinery/camera/C = holder if(!C.panel_open) return FALSE return TRUE -#define CAMERA_WIRE_FOCUS 1 -#define CAMERA_WIRE_POWER 2 - -/datum/wires/camera/GetWireName(index) - switch(index) - if(CAMERA_WIRE_FOCUS) - return "Focus" - - if(CAMERA_WIRE_POWER) - return "Power" - -/datum/wires/camera/UpdateCut(index, mended) +/datum/wires/camera/on_cut(wire, mend) var/obj/machinery/camera/C = holder - switch(index) - if(CAMERA_WIRE_FOCUS) - var/range = (mended ? initial(C.view_range) : C.short_range) + switch(wire) + if(WIRE_FOCUS) + var/range = (mend ? initial(C.view_range) : C.short_range) C.setViewRange(range) - if(CAMERA_WIRE_POWER) - if(C.status && !mended || !C.status && mended) + if(WIRE_MAIN_POWER1) + if(C.status && !mend || !C.status && mend) C.toggle_cam(usr, TRUE) C.obj_integrity = C.max_integrity //this is a pretty simplistic way to heal the camera, but there's no reason for this to be complex. ..() -/datum/wires/camera/UpdatePulsed(index) +/datum/wires/camera/on_pulse(wire) var/obj/machinery/camera/C = holder - if(IsIndexCut(index)) + if(is_cut(wire)) return - switch(index) - if(CAMERA_WIRE_FOCUS) + switch(wire) + if(WIRE_FOCUS) var/new_range = (C.view_range == initial(C.view_range) ? C.short_range : initial(C.view_range)) C.setViewRange(new_range) - if(CAMERA_WIRE_POWER) + if(WIRE_MAIN_POWER1) C.toggle_cam(null) // Deactivate the camera ..() /datum/wires/camera/proc/CanDeconstruct() - if(IsIndexCut(CAMERA_WIRE_POWER) && IsIndexCut(CAMERA_WIRE_FOCUS)) + if(is_cut(WIRE_MAIN_POWER1) && is_cut(WIRE_FOCUS)) return TRUE else return FALSE diff --git a/code/datums/wires/explosive.dm b/code/datums/wires/explosive.dm index 47fd627d597..988aa0f37a8 100644 --- a/code/datums/wires/explosive.dm +++ b/code/datums/wires/explosive.dm @@ -1,36 +1,36 @@ /datum/wires/explosive wire_count = 1 + proper_name = "Explosive" + window_x = 320 + window_y = 50 -#define WIRE_EXPLODE 1 - -/datum/wires/explosive/GetWireName(index) - switch(index) - if(WIRE_EXPLODE) - return "Explode" +/datum/wires/explosive/New(atom/_holder) + wires = list(WIRE_EXPLODE) + return ..() /datum/wires/explosive/proc/explode() return -/datum/wires/explosive/UpdatePulsed(index) - switch(index) +/datum/wires/explosive/on_pulse(wire) + switch(wire) if(WIRE_EXPLODE) explode() ..() -/datum/wires/explosive/UpdateCut(index, mended) - switch(index) +/datum/wires/explosive/on_cut(wire, mend) + switch(wire) if(WIRE_EXPLODE) - if(!mended) + if(!mend) explode() ..() /datum/wires/explosive/gibtonite holder_type = /obj/item/twohanded/required/gibtonite -/datum/wires/explosive/gibtonite/CanUse(mob/L) - return 1 +/datum/wires/explosive/gibtonite/interactable(mob/user) + return TRUE -/datum/wires/explosive/gibtonite/UpdateCut(index, mended) +/datum/wires/explosive/gibtonite/on_cut(wire, mend) return /datum/wires/explosive/gibtonite/explode() diff --git a/code/datums/wires/mulebot.dm b/code/datums/wires/mulebot.dm index 988b520854f..cb13b0d6718 100644 --- a/code/datums/wires/mulebot.dm +++ b/code/datums/wires/mulebot.dm @@ -1,90 +1,35 @@ /datum/wires/mulebot - random = 1 + randomize = TRUE holder_type = /mob/living/simple_animal/bot/mulebot wire_count = 10 - window_x = 410 + proper_name = "Mulebot" + window_x = 370 + window_y = -12 -#define MULEBOT_WIRE_POWER1 1 // power connections -#define MULEBOT_WIRE_POWER2 2 -#define MULEBOT_WIRE_AVOIDANCE 4 // mob avoidance -#define MULEBOT_WIRE_LOADCHECK 8 // load checking (non-crate) -#define MULEBOT_WIRE_MOTOR1 16 // motor wires -#define MULEBOT_WIRE_MOTOR2 32 // -#define MULEBOT_WIRE_REMOTE_RX 64 // remote recv functions -#define MULEBOT_WIRE_REMOTE_TX 128 // remote trans status -#define MULEBOT_WIRE_BEACON_RX 256 // beacon ping recv +/datum/wires/mulebot/New(atom/_holder) + wires = list( + WIRE_MAIN_POWER1, WIRE_MAIN_POWER2, WIRE_MOB_AVOIDANCE, + WIRE_LOADCHECK, WIRE_MOTOR1, WIRE_MOTOR2, + WIRE_REMOTE_RX, WIRE_REMOTE_TX, WIRE_BEACON_RX + ) + return ..() -/datum/wires/mulebot/GetWireName(index) - switch(index) - if(MULEBOT_WIRE_POWER1) - return "Primary Power" - - if(MULEBOT_WIRE_POWER2) - return "Secondary Power" - - if(MULEBOT_WIRE_AVOIDANCE) - return "Mob Avoidance" - - if(MULEBOT_WIRE_LOADCHECK) - return "Load Checking" - - if(MULEBOT_WIRE_MOTOR1) - return "Primary Motor" - - if(MULEBOT_WIRE_MOTOR2) - return "Secondary Motor" - - if(MULEBOT_WIRE_REMOTE_RX) - return "Remote Signal Receiver" - - if(MULEBOT_WIRE_REMOTE_TX) - return "Remote Signal Sender" - - if(MULEBOT_WIRE_BEACON_RX) - return "Navigation Beacon Receiver" - -/datum/wires/mulebot/CanUse(mob/living/L) +/datum/wires/mulebot/interactable(mob/user) var/mob/living/simple_animal/bot/mulebot/M = holder if(M.open) - return 1 - return 0 + return TRUE + return FALSE -/datum/wires/mulebot/UpdatePulsed(index) - switch(index) - if(MULEBOT_WIRE_POWER1, MULEBOT_WIRE_POWER2) +/datum/wires/mulebot/on_pulse(wire) + switch(wire) + if(WIRE_MAIN_POWER1, WIRE_MAIN_POWER2) holder.visible_message("[bicon(holder)] The charge light flickers.") - if(MULEBOT_WIRE_AVOIDANCE) + if(WIRE_MOB_AVOIDANCE) holder.visible_message("[bicon(holder)] The external warning lights flash briefly.") - if(MULEBOT_WIRE_LOADCHECK) + if(WIRE_LOADCHECK) holder.visible_message("[bicon(holder)] The load platform clunks.") - if(MULEBOT_WIRE_MOTOR1, MULEBOT_WIRE_MOTOR2) + if(WIRE_MOTOR1, WIRE_MOTOR2) holder.visible_message("[bicon(holder)] The drive motor whines briefly.") else holder.visible_message("[bicon(holder)] You hear a radio crackle.") ..() - -// HELPER PROCS - -/datum/wires/mulebot/proc/Motor1() - return !(wires_status & MULEBOT_WIRE_MOTOR1) - -/datum/wires/mulebot/proc/Motor2() - return !(wires_status & MULEBOT_WIRE_MOTOR2) - -/datum/wires/mulebot/proc/HasPower() - return !(wires_status & MULEBOT_WIRE_POWER1) && !(wires_status & MULEBOT_WIRE_POWER2) - -/datum/wires/mulebot/proc/LoadCheck() - return !(wires_status & MULEBOT_WIRE_LOADCHECK) - -/datum/wires/mulebot/proc/MobAvoid() - return !(wires_status & MULEBOT_WIRE_AVOIDANCE) - -/datum/wires/mulebot/proc/RemoteTX() - return !(wires_status & MULEBOT_WIRE_REMOTE_TX) - -/datum/wires/mulebot/proc/RemoteRX() - return !(wires_status & MULEBOT_WIRE_REMOTE_RX) - -/datum/wires/mulebot/proc/BeaconRX() - return !(wires_status & MULEBOT_WIRE_BEACON_RX) diff --git a/code/datums/wires/nuclearbomb.dm b/code/datums/wires/nuclearbomb.dm index 3b8ff5db098..51918c02ff4 100644 --- a/code/datums/wires/nuclearbomb.dm +++ b/code/datums/wires/nuclearbomb.dm @@ -1,28 +1,20 @@ /datum/wires/nuclearbomb holder_type = /obj/machinery/nuclearbomb - random = 1 - wire_count = 7 + randomize = TRUE + wire_count = 7 // 3 actual, 4 duds. + proper_name = "Nuclear bomb" + window_x = 345 + window_y = 75 -#define NUCLEARBOMB_WIRE_LIGHT 1 -#define NUCLEARBOMB_WIRE_TIMING 2 -#define NUCLEARBOMB_WIRE_SAFETY 4 +/datum/wires/nuclearbomb/New(atom/_holder) + wires = list(WIRE_BOMB_LIGHT, WIRE_BOMB_TIMING, WIRE_BOMB_SAFETY) + return ..() -/datum/wires/nuclearbomb/GetWireName(index) - switch(index) - if(NUCLEARBOMB_WIRE_LIGHT) - return "Bomb Light" - - if(NUCLEARBOMB_WIRE_TIMING) - return "Bomb Timing" - - if(NUCLEARBOMB_WIRE_SAFETY) - return "Bomb Safety" - -/datum/wires/nuclearbomb/CanUse(mob/living/L) +/datum/wires/nuclearbomb/interactable(mob/user) var/obj/machinery/nuclearbomb/N = holder if(N.panel_open) - return 1 - return 0 + return TRUE + return FALSE /datum/wires/nuclearbomb/get_status() . = ..() @@ -31,52 +23,36 @@ . += "The device is is [N.safety ? "quiet" : "whirring"]." . += "The lights are [N.lighthack ? "static" : "functional"]." -/datum/wires/nuclearbomb/UpdatePulsed(index) +/datum/wires/nuclearbomb/on_pulse(wire) var/obj/machinery/nuclearbomb/N = holder - switch(index) - if(NUCLEARBOMB_WIRE_LIGHT) + switch(wire) + if(WIRE_BOMB_LIGHT) N.lighthack = !N.lighthack - updateUIs() - spawn(100) - N.lighthack = !N.lighthack - updateUIs() - if(NUCLEARBOMB_WIRE_TIMING) + addtimer(CALLBACK(N, /obj/machinery/nuclearbomb/.proc/reset_lighthack_callback), 10 SECONDS) + + if(WIRE_BOMB_TIMING) if(N.timing) message_admins("[key_name_admin(usr)] pulsed a nuclear bomb's detonation wire, causing it to explode (JMP)") N.explode() - if(NUCLEARBOMB_WIRE_SAFETY) - N.safety = !N.safety - updateUIs() - spawn(100) - N.safety = !N.safety - if(N.safety == 1) - if(!N.is_syndicate) - set_security_level(N.previous_level) - N.visible_message("The [N] quiets down.") - if(!N.lighthack) - if(N.icon_state == "nuclearbomb2") - N.icon_state = "nuclearbomb1" - else - N.visible_message("The [N] emits a quiet whirling noise!") - updateUIs() -/datum/wires/nuclearbomb/UpdateCut(index, mended) + if(WIRE_BOMB_SAFETY) + N.safety = !N.safety + addtimer(CALLBACK(N, /obj/machinery/nuclearbomb/.proc/reset_safety_callback), 10 SECONDS) + +/datum/wires/nuclearbomb/on_cut(wire, mend) var/obj/machinery/nuclearbomb/N = holder - switch(index) - if(NUCLEARBOMB_WIRE_SAFETY) + switch(wire) + if(WIRE_BOMB_SAFETY) if(N.timing) message_admins("[key_name_admin(usr)] cut a nuclear bomb's timing wire, causing it to explode (JMP)") N.explode() - if(NUCLEARBOMB_WIRE_TIMING) + + if(WIRE_BOMB_TIMING) if(!N.lighthack) if(N.icon_state == "nuclearbomb2") N.icon_state = "nuclearbomb1" N.timing = 0 - GLOB.bomb_set = 0 - if(NUCLEARBOMB_WIRE_LIGHT) - N.lighthack = !N.lighthack + GLOB.bomb_set = FALSE -/datum/wires/nuclearbomb/proc/updateUIs() - SSnanoui.update_uis(src) - if(holder) - SSnanoui.update_uis(holder) + if(WIRE_BOMB_LIGHT) + N.lighthack = !N.lighthack diff --git a/code/datums/wires/particle_accelerator.dm b/code/datums/wires/particle_accelerator.dm index d6e68a79cfb..e285f4cf898 100644 --- a/code/datums/wires/particle_accelerator.dm +++ b/code/datums/wires/particle_accelerator.dm @@ -1,67 +1,52 @@ /datum/wires/particle_acc/control_box wire_count = 5 holder_type = /obj/machinery/particle_accelerator/control_box + proper_name = "Particle accelerator control" + window_x = 361 + window_y = 22 -#define PARTICLE_TOGGLE_WIRE 1 // Toggles whether the PA is on or not. -#define PARTICLE_STRENGTH_WIRE 2 // Determines the strength of the PA. -#define PARTICLE_INTERFACE_WIRE 4 // Determines the interface showing up. -#define PARTICLE_LIMIT_POWER_WIRE 8 // Determines how strong the PA can be. +/datum/wires/particle_acc/control_box/New(atom/_holder) + wires = list(WIRE_PARTICLE_POWER, WIRE_PARTICLE_STRENGTH, WIRE_PARTICLE_INTERFACE, WIRE_PARTICLE_POWER_LIMIT) + return ..() -/datum/wires/particle_acc/control_box/GetWireName(index) - switch(index) - if(PARTICLE_TOGGLE_WIRE) - return "Power Toggle" - - if(PARTICLE_STRENGTH_WIRE) - return "Strength" - - if(PARTICLE_INTERFACE_WIRE) - return "Interface" - - if(PARTICLE_LIMIT_POWER_WIRE) - return "Maximum Power" - -/datum/wires/particle_acc/control_box/CanUse(mob/living/L) +/datum/wires/particle_acc/control_box/interactable(mob/user) var/obj/machinery/particle_accelerator/control_box/C = holder if(C.construction_state == 2) - return 1 - return 0 + return TRUE + return FALSE -/datum/wires/particle_acc/control_box/UpdatePulsed(index) +/datum/wires/particle_acc/control_box/on_pulse(wire) var/obj/machinery/particle_accelerator/control_box/C = holder - switch(index) - - if(PARTICLE_TOGGLE_WIRE) + switch(wire) + if(WIRE_PARTICLE_POWER) C.toggle_power() - if(PARTICLE_STRENGTH_WIRE) + if(WIRE_PARTICLE_STRENGTH) C.add_strength() - if(PARTICLE_INTERFACE_WIRE) + if(WIRE_PARTICLE_INTERFACE) C.interface_control = !C.interface_control - if(PARTICLE_LIMIT_POWER_WIRE) + if(WIRE_PARTICLE_POWER_LIMIT) C.visible_message("[bicon(C)][C] makes a large whirring noise.") ..() -/datum/wires/particle_acc/control_box/UpdateCut(index, mended) +/datum/wires/particle_acc/control_box/on_cut(wire, mend) var/obj/machinery/particle_accelerator/control_box/C = holder - switch(index) - - if(PARTICLE_TOGGLE_WIRE) - if(C.active == !mended) + switch(wire) + if(WIRE_PARTICLE_POWER) + if(C.active == !mend) C.toggle_power() - if(PARTICLE_STRENGTH_WIRE) - - for(var/i = 1; i < 3; i++) + if(WIRE_PARTICLE_STRENGTH) + for(var/i in 1 to 2) C.remove_strength() - if(PARTICLE_INTERFACE_WIRE) - C.interface_control = mended + if(WIRE_PARTICLE_INTERFACE) + C.interface_control = mend - if(PARTICLE_LIMIT_POWER_WIRE) - C.strength_upper_limit = (mended ? 2 : 3) + if(WIRE_PARTICLE_POWER_LIMIT) + C.strength_upper_limit = (mend ? 2 : 3) if(C.strength_upper_limit < C.strength) C.remove_strength() ..() diff --git a/code/datums/wires/radio.dm b/code/datums/wires/radio.dm index 90a72935c53..1efee71692d 100644 --- a/code/datums/wires/radio.dm +++ b/code/datums/wires/radio.dm @@ -1,52 +1,44 @@ /datum/wires/radio holder_type = /obj/item/radio wire_count = 3 + proper_name = "Radio" + window_x = 330 + window_y = 37 -#define RADIO_WIRE_SIGNAL 1 -#define RADIO_WIRE_RECEIVE 2 -#define RADIO_WIRE_TRANSMIT 4 +/datum/wires/radio/New(atom/_holder) + wires = list(WIRE_RADIO_SIGNAL, WIRE_RADIO_RECEIVER, WIRE_RADIO_TRANSMIT) + return ..() -/datum/wires/radio/GetWireName(index) - switch(index) - if(RADIO_WIRE_SIGNAL) - return "Signal" - - if(RADIO_WIRE_RECEIVE) - return "Receiver" - - if(RADIO_WIRE_TRANSMIT) - return "Transmitter" - -/datum/wires/radio/CanUse(mob/living/L) +/datum/wires/radio/interactable(mob/user) var/obj/item/radio/R = holder if(R.b_stat) - return 1 - return 0 + return TRUE + return FALSE -/datum/wires/radio/UpdatePulsed(index) +/datum/wires/radio/on_pulse(wire) var/obj/item/radio/R = holder - switch(index) - if(RADIO_WIRE_SIGNAL) - R.listening = !R.listening && !IsIndexCut(RADIO_WIRE_RECEIVE) - R.broadcasting = R.listening && !IsIndexCut(RADIO_WIRE_TRANSMIT) + switch(wire) + if(WIRE_RADIO_SIGNAL) + R.listening = !R.listening && !is_cut(WIRE_RADIO_RECEIVER) + R.broadcasting = R.listening && !is_cut(WIRE_RADIO_TRANSMIT) - if(RADIO_WIRE_RECEIVE) - R.listening = !R.listening && !IsIndexCut(RADIO_WIRE_SIGNAL) + if(WIRE_RADIO_RECEIVER) + R.listening = !R.listening && !is_cut(WIRE_RADIO_SIGNAL) - if(RADIO_WIRE_TRANSMIT) - R.broadcasting = !R.broadcasting && !IsIndexCut(RADIO_WIRE_SIGNAL) + if(WIRE_RADIO_TRANSMIT) + R.broadcasting = !R.broadcasting && !is_cut(WIRE_RADIO_SIGNAL) ..() -/datum/wires/radio/UpdateCut(index, mended) +/datum/wires/radio/on_cut(wire, mend) var/obj/item/radio/R = holder - switch(index) - if(RADIO_WIRE_SIGNAL) - R.listening = mended && !IsIndexCut(RADIO_WIRE_RECEIVE) - R.broadcasting = mended && !IsIndexCut(RADIO_WIRE_TRANSMIT) + switch(wire) + if(WIRE_RADIO_SIGNAL) + R.listening = mend && !is_cut(WIRE_RADIO_RECEIVER) + R.broadcasting = mend && !is_cut(WIRE_RADIO_TRANSMIT) - if(RADIO_WIRE_RECEIVE) - R.listening = mended && !IsIndexCut(RADIO_WIRE_SIGNAL) + if(WIRE_RADIO_RECEIVER) + R.listening = mend && !is_cut(WIRE_RADIO_SIGNAL) - if(RADIO_WIRE_TRANSMIT) - R.broadcasting = mended && !IsIndexCut(RADIO_WIRE_SIGNAL) + if(WIRE_RADIO_TRANSMIT) + R.broadcasting = mend && !is_cut(WIRE_RADIO_SIGNAL) ..() diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index 04a9509cfe2..927980c57fa 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -1,32 +1,14 @@ /datum/wires/robot - random = 1 + randomize = TRUE holder_type = /mob/living/silicon/robot wire_count = 5 + window_x = 340 + window_y = 106 + proper_name = "Cyborg" -// /vg/ ordering - -#define BORG_WIRE_MAIN_POWER 1 // The power wires do nothing whyyyyyyyyyyyyy -#define BORG_WIRE_LOCKED_DOWN 2 -#define BORG_WIRE_CAMERA 4 -#define BORG_WIRE_AI_CONTROL 8 // Not used on MoMMIs -#define BORG_WIRE_LAWCHECK 16 // Not used on MoMMIs - -/datum/wires/robot/GetWireName(index) - switch(index) - if(BORG_WIRE_MAIN_POWER) - return "Main Power" - - if(BORG_WIRE_LOCKED_DOWN) - return "Lockdown" - - if(BORG_WIRE_CAMERA) - return "Camera" - - if(BORG_WIRE_AI_CONTROL) - return "AI Control" - - if(BORG_WIRE_LAWCHECK) - return "Law Check" +/datum/wires/robot/New(atom/_holder) + wires = list(WIRE_AI_CONTROL, WIRE_BORG_CAMERA, WIRE_BORG_LAWCHECK, WIRE_BORG_LOCKED) + return ..() /datum/wires/robot/get_status() . = ..() @@ -36,70 +18,53 @@ . += "The Camera light is [(R.camera && R.camera.status == 1) ? "on" : "off"]." . += "The lockdown light is [R.lockcharge ? "on" : "off"]." -/datum/wires/robot/UpdateCut(index, mended) - +/datum/wires/robot/on_cut(wire, mend) var/mob/living/silicon/robot/R = holder - switch(index) - if(BORG_WIRE_LAWCHECK) //Cut the law wire, and the borg will no longer receive law updates from its AI - if(!mended) - if(R.lawupdate == 1) + switch(wire) + if(WIRE_BORG_LAWCHECK) //Cut the law wire, and the borg will no longer receive law updates from its AI + if(!mend) + if(R.lawupdate) to_chat(R, "LawSync protocol engaged.") + R.lawsync() R.show_laws() else - if(R.lawupdate == 0 && !R.emagged) - R.lawupdate = 1 + if(!R.lawupdate && !R.emagged) + R.lawupdate = TRUE - if(BORG_WIRE_AI_CONTROL) //Cut the AI wire to reset AI control - if(!mended) + if(WIRE_AI_CONTROL) //Cut the AI wire to reset AI control + if(!mend) if(R.connected_ai) R.disconnect_from_ai() - if(BORG_WIRE_CAMERA) + if(WIRE_BORG_CAMERA) if(!isnull(R.camera) && !R.scrambledcodes) - R.camera.status = mended + R.camera.status = mend R.camera.toggle_cam(usr, 0) // Will kick anyone who is watching the Cyborg's camera. - if(BORG_WIRE_LAWCHECK) //Forces a law update if the borg is set to receive them. Since an update would happen when the borg checks its laws anyway, not much use, but eh - if(R.lawupdate) - R.lawsync() - - if(BORG_WIRE_LOCKED_DOWN) - R.SetLockdown(!mended) + if(WIRE_BORG_LOCKED) + R.SetLockdown(!mend) ..() -/datum/wires/robot/UpdatePulsed(index) - +/datum/wires/robot/on_pulse(wire) var/mob/living/silicon/robot/R = holder - switch(index) - if(BORG_WIRE_AI_CONTROL) //pulse the AI wire to make the borg reselect an AI + switch(wire) + if(WIRE_AI_CONTROL) //pulse the AI wire to make the borg reselect an AI if(!R.emagged) R.connect_to_ai(select_active_ai()) - if(BORG_WIRE_CAMERA) + if(WIRE_BORG_CAMERA) if(!isnull(R.camera) && R.camera.can_use() && !R.scrambledcodes) R.camera.toggle_cam(usr, 0) // Kick anyone watching the Cyborg's camera, doesn't display you disconnecting the camera. R.visible_message("[R]'s camera lense focuses loudly.") to_chat(R, "Your camera lense focuses loudly.") - if(BORG_WIRE_LOCKED_DOWN) + if(WIRE_BORG_LOCKED) R.SetLockdown(!R.lockcharge) // Toggle ..() -/datum/wires/robot/CanUse(mob/living/L) +/datum/wires/robot/interactable(mob/user) var/mob/living/silicon/robot/R = holder if(R.wiresexposed) - return 1 - return 0 - -/datum/wires/robot/proc/IsCameraCut() - return wires_status & BORG_WIRE_CAMERA - -/datum/wires/robot/proc/LockedCut() - return wires_status & BORG_WIRE_LOCKED_DOWN - -/datum/wires/robot/proc/CanLawCheck() - return wires_status & BORG_WIRE_LAWCHECK - -/datum/wires/robot/proc/AIHasControl() - return wires_status & BORG_WIRE_AI_CONTROL + return TRUE + return FALSE diff --git a/code/datums/wires/smartfridge.dm b/code/datums/wires/smartfridge.dm index a65377ead1a..3955b1849b0 100644 --- a/code/datums/wires/smartfridge.dm +++ b/code/datums/wires/smartfridge.dm @@ -1,35 +1,26 @@ /datum/wires/smartfridge holder_type = /obj/machinery/smartfridge wire_count = 3 + proper_name = "Smartfridge" + window_x = 340 + window_y = 103 + +/datum/wires/smartfridge/New(atom/_holder) + wires = list(WIRE_ELECTRIFY, WIRE_IDSCAN, WIRE_THROW_ITEM) + return ..() /datum/wires/smartfridge/secure - random = 1 - wire_count = 4 + randomize = TRUE + wire_count = 4 // 3 actual, 1 dud. + window_y = 97 -#define SMARTFRIDGE_WIRE_ELECTRIFY 1 -#define SMARTFRIDGE_WIRE_THROW 2 -#define SMARTFRIDGE_WIRE_IDSCAN 4 - -/datum/wires/smartfridge/GetWireName(index) - switch(index) - if(SMARTFRIDGE_WIRE_ELECTRIFY) - return "Electrification" - - if(SMARTFRIDGE_WIRE_THROW) - return "Item Throw" - - if(SMARTFRIDGE_WIRE_IDSCAN) - return "ID Scan" - -/datum/wires/smartfridge/CanUse(mob/living/L) +/datum/wires/smartfridge/interactable(mob/user) var/obj/machinery/smartfridge/S = holder - if(!issilicon(L)) - if(S.seconds_electrified) - if(S.shock(L, 100)) - return 0 + if(iscarbon(user) && S.Adjacent(user) && S.seconds_electrified && S.shock(user, 100)) + return FALSE if(S.panel_open) - return 1 - return 0 + return TRUE + return FALSE /datum/wires/smartfridge/get_status() . = ..() @@ -38,27 +29,27 @@ . += "The red light is [S.shoot_inventory ? "off" : "blinking"]." . += "A [S.scan_id ? "purple" : "yellow"] light is on." -/datum/wires/smartfridge/UpdatePulsed(index) +/datum/wires/smartfridge/on_pulse(wire) var/obj/machinery/smartfridge/S = holder - switch(index) - if(SMARTFRIDGE_WIRE_THROW) + switch(wire) + if(WIRE_THROW_ITEM) S.shoot_inventory = !S.shoot_inventory - if(SMARTFRIDGE_WIRE_ELECTRIFY) + if(WIRE_ELECTRIFY) S.seconds_electrified = 30 - if(SMARTFRIDGE_WIRE_IDSCAN) + if(WIRE_IDSCAN) S.scan_id = !S.scan_id ..() -/datum/wires/smartfridge/UpdateCut(index, mended) +/datum/wires/smartfridge/on_cut(wire, mend) var/obj/machinery/smartfridge/S = holder - switch(index) - if(SMARTFRIDGE_WIRE_THROW) - S.shoot_inventory = !mended - if(SMARTFRIDGE_WIRE_ELECTRIFY) - if(mended) + switch(wire) + if(WIRE_THROW_ITEM) + S.shoot_inventory = !mend + if(WIRE_ELECTRIFY) + if(mend) S.seconds_electrified = 0 else S.seconds_electrified = -1 - if(SMARTFRIDGE_WIRE_IDSCAN) - S.scan_id = 1 + if(WIRE_IDSCAN) + S.scan_id = TRUE ..() diff --git a/code/datums/wires/suitstorage.dm b/code/datums/wires/suitstorage.dm index 175b36e6081..ec651002747 100644 --- a/code/datums/wires/suitstorage.dm +++ b/code/datums/wires/suitstorage.dm @@ -1,24 +1,13 @@ /datum/wires/suitstorage holder_type = /obj/machinery/suit_storage_unit wire_count = 8 + proper_name = "Suit storage unit" + window_x = 350 + window_y = 85 -#define SSU_WIRE_ID 1 -#define SSU_WIRE_SHOCK 2 -#define SSU_WIRE_SAFETY 4 -#define SSU_WIRE_UV 8 - - -/datum/wires/suitstorage/GetWireName(index) - switch(index) - if(SSU_WIRE_ID) - return "ID lock" - if(SSU_WIRE_SHOCK) - return "Shock wire" - if(SSU_WIRE_SAFETY) - return "Safety wire" - if(SSU_WIRE_UV) - return "UV wire" - +/datum/wires/suitstorage/New(atom/_holder) + wires = list(WIRE_IDSCAN, WIRE_ELECTRIFY, WIRE_SAFETY, WIRE_SSU_UV) + return ..() /datum/wires/suitstorage/get_status() . = ..() @@ -28,42 +17,48 @@ . += "The green light is [A.shocked ? "on" : "off"]." . += "The UV display shows [A.uv_super ? "15 nm" : "185 nm"]." -datum/wires/suitstorage/CanUse() +/datum/wires/suitstorage/interactable(mob/user) var/obj/machinery/suit_storage_unit/A = holder + if(iscarbon(user) && A.Adjacent(user) && A.shocked) + return A.shock(user, 100) if(A.panel_open) - return 1 - return 0 + return TRUE + return FALSE -/datum/wires/suitstorage/UpdateCut(index, mended) +/datum/wires/suitstorage/on_cut(wire, mend) var/obj/machinery/suit_storage_unit/A = holder - switch(index) - if(SSU_WIRE_ID) - A.secure = mended - if(SSU_WIRE_SAFETY) - A.safeties = mended - if(SSU_WIRE_SHOCK) - A.shocked = !mended + switch(wire) + if(WIRE_IDSCAN) + A.secure = mend + + if(WIRE_SAFETY) + A.safeties = mend + + if(WIRE_ELECTRIFY) + A.shocked = !mend A.shock(usr, 50) - if(SSU_WIRE_UV) - A.uv_super = !mended + + if(WIRE_SSU_UV) + A.uv_super = !mend ..() -datum/wires/suitstorage/UpdatePulsed(index) +/datum/wires/suitstorage/on_pulse(wire) var/obj/machinery/suit_storage_unit/A = holder - if(IsIndexCut(index)) + if(is_cut(wire)) return - switch(index) - if(SSU_WIRE_ID) + switch(wire) + if(WIRE_IDSCAN) A.secure = !A.secure - if(SSU_WIRE_SAFETY) + + if(WIRE_SAFETY) A.safeties = !A.safeties - if(SSU_WIRE_SHOCK) + + if(WIRE_ELECTRIFY) A.shocked = !A.shocked if(A.shocked) A.shock(usr, 100) - spawn(50) - if(A && !IsIndexCut(index)) - A.shocked = FALSE - if(SSU_WIRE_UV) + addtimer(CALLBACK(A, /obj/machinery/suit_storage_unit/.proc/check_electrified_callback), 5 SECONDS) + + if(WIRE_SSU_UV) A.uv_super = !A.uv_super ..() diff --git a/code/datums/wires/syndicatebomb.dm b/code/datums/wires/syndicatebomb.dm index 430b5ce88aa..bd996f72fcc 100644 --- a/code/datums/wires/syndicatebomb.dm +++ b/code/datums/wires/syndicatebomb.dm @@ -1,47 +1,31 @@ /datum/wires/syndicatebomb - random = TRUE + randomize = TRUE holder_type = /obj/machinery/syndicatebomb wire_count = 5 + proper_name = "Syndicate bomb" + window_x = 320 + window_y = 22 -#define BOMB_WIRE_BOOM 1 // Explodes if pulsed or cut while active, defuses a bomb that isn't active on cut -#define BOMB_WIRE_UNBOLT 2 // Unbolts the bomb if cut, hint on pulsed -#define BOMB_WIRE_DELAY 4 // Raises the timer on pulse, does nothing on cut -#define BOMB_WIRE_PROCEED 8 // Lowers the timer, explodes if cut while the bomb is active -#define BOMB_WIRE_ACTIVATE 16 // Will start a bombs timer if pulsed, will hint if pulsed while already active, will stop a timer a bomb on cut +/datum/wires/syndicatebomb/New(atom/_holder) + wires = list(WIRE_BOMB_DELAY, WIRE_EXPLODE, WIRE_BOMB_UNBOLT,WIRE_BOMB_PROCEED, WIRE_BOMB_ACTIVATE) + return ..() -/datum/wires/syndicatebomb/GetWireName(index) - switch(index) - if(BOMB_WIRE_BOOM) - return "Explode" - - if(BOMB_WIRE_UNBOLT) - return "Unbolt" - - if(BOMB_WIRE_DELAY) - return "Delay" - - if(BOMB_WIRE_PROCEED) - return "Proceed" - - if(BOMB_WIRE_ACTIVATE) - return "Activate" - -/datum/wires/syndicatebomb/CanUse(mob/living/L) +/datum/wires/syndicatebomb/interactable(mob/user) var/obj/machinery/syndicatebomb/P = holder if(P.open_panel) return TRUE return FALSE -/datum/wires/syndicatebomb/UpdatePulsed(index) +/datum/wires/syndicatebomb/on_pulse(wire) var/obj/machinery/syndicatebomb/B = holder - switch(index) - if(BOMB_WIRE_BOOM) + switch(wire) + if(WIRE_EXPLODE) if(B.active) holder.visible_message("[bicon(B)] An alarm sounds! It's go-") B.explode_now = TRUE - if(BOMB_WIRE_UNBOLT) + if(WIRE_BOMB_UNBOLT) holder.visible_message("[bicon(holder)] The bolts spin in place for a moment.") - if(BOMB_WIRE_DELAY) + if(WIRE_BOMB_DELAY) if(B.delayedbig) holder.visible_message("[bicon(B)] The bomb has already been delayed.") else @@ -49,7 +33,7 @@ playsound(B, 'sound/machines/chime.ogg', 30, 1) B.detonation_timer += 300 B.delayedbig = TRUE - if(BOMB_WIRE_PROCEED) + if(WIRE_BOMB_PROCEED) holder.visible_message("[bicon(B)] The bomb buzzes ominously!") playsound(B, 'sound/machines/buzz-sigh.ogg', 30, 1) var/seconds = B.seconds_remaining() @@ -59,7 +43,7 @@ B.detonation_timer -= 100 else if(seconds >= 11) // Both to prevent negative timers and to have a little mercy. B.detonation_timer = world.time + 100 - if(BOMB_WIRE_ACTIVATE) + if(WIRE_BOMB_ACTIVATE) if(!B.active && !B.defused) holder.visible_message("[bicon(B)] You hear the bomb start ticking!") B.activate() @@ -72,11 +56,11 @@ B.delayedlittle = TRUE ..() -/datum/wires/syndicatebomb/UpdateCut(index, mended) +/datum/wires/syndicatebomb/on_cut(wire, mend) var/obj/machinery/syndicatebomb/B = holder - switch(index) - if(BOMB_WIRE_BOOM) - if(mended) + switch(wire) + if(WIRE_EXPLODE) + if(mend) B.defused = FALSE // Cutting and mending all the wires of an inactive bomb will thus cure any sabotage. else if(B.active) @@ -84,17 +68,17 @@ B.explode_now = TRUE else B.defused = TRUE - if(BOMB_WIRE_UNBOLT) - if(!mended && B.anchored) + if(WIRE_BOMB_UNBOLT) + if(!mend && B.anchored) holder.visible_message("[bicon(B)] The bolts lift out of the ground!") playsound(B, 'sound/effects/stealthoff.ogg', 30, 1) B.anchored = FALSE - if(BOMB_WIRE_PROCEED) - if(!mended && B.active) + if(WIRE_BOMB_PROCEED) + if(!mend && B.active) holder.visible_message("[bicon(B)] An alarm sounds! It's go-") B.explode_now = TRUE - if(BOMB_WIRE_ACTIVATE) - if(!mended && B.active) + if(WIRE_BOMB_ACTIVATE) + if(!mend && B.active) holder.visible_message("[bicon(B)] The timer stops! The bomb has been defused!") B.defused = TRUE B.update_icon() diff --git a/code/datums/wires/tesla_coil.dm b/code/datums/wires/tesla_coil.dm index bc513c6c6c0..f4dbf39017a 100644 --- a/code/datums/wires/tesla_coil.dm +++ b/code/datums/wires/tesla_coil.dm @@ -1,23 +1,23 @@ /datum/wires/tesla_coil wire_count = 1 holder_type = /obj/machinery/power/tesla_coil + proper_name = "Tesla coil" + window_x = 320 + window_y = 50 -#define TESLACOIL_WIRE_ZAP 1 +/datum/wires/tesla_coil/New(atom/_holder) + wires = list(WIRE_TESLACOIL_ZAP) + return ..() -/datum/wires/tesla_coil/GetWireName(index) - switch(index) - if(TESLACOIL_WIRE_ZAP) - return "Zap" - -/datum/wires/tesla_coil/CanUse(mob/living/L) +/datum/wires/tesla_coil/interactable(mob/user) var/obj/machinery/power/tesla_coil/T = holder if(T && T.panel_open) - return 1 - return 0 + return TRUE + return FALSE -/datum/wires/tesla_coil/UpdatePulsed(index) +/datum/wires/tesla_coil/on_pulse(wire) var/obj/machinery/power/tesla_coil/T = holder - switch(index) - if(TESLACOIL_WIRE_ZAP) + switch(wire) + if(WIRE_TESLACOIL_ZAP) T.zap() ..() diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm index c9e6032afee..299e18d815a 100644 --- a/code/datums/wires/vending.dm +++ b/code/datums/wires/vending.dm @@ -1,35 +1,21 @@ /datum/wires/vending holder_type = /obj/machinery/vending wire_count = 4 + window_y = 112 + window_x = 350 + proper_name = "Vending machine" -#define VENDING_WIRE_THROW 1 -#define VENDING_WIRE_CONTRABAND 2 -#define VENDING_WIRE_ELECTRIFY 4 -#define VENDING_WIRE_IDSCAN 8 +/datum/wires/vending/New(atom/_holder) + wires = list(WIRE_THROW_ITEM, WIRE_IDSCAN, WIRE_ELECTRIFY, WIRE_CONTRABAND) + return ..() -/datum/wires/vending/GetWireName(index) - switch(index) - if(VENDING_WIRE_THROW) - return "Item Throw" - - if(VENDING_WIRE_CONTRABAND) - return "Contraband" - - if(VENDING_WIRE_ELECTRIFY) - return "Electrification" - - if(VENDING_WIRE_IDSCAN) - return "ID Scan" - -/datum/wires/vending/CanUse(mob/living/L) +/datum/wires/vending/interactable(mob/user) var/obj/machinery/vending/V = holder - if(!istype(L, /mob/living/silicon)) - if(V.seconds_electrified) - if(V.shock(L, 100)) - return 0 + if(!istype(user, /mob/living/silicon) && V.seconds_electrified && V.shock(user, 100)) + return FALSE if(V.panel_open) - return 1 - return 0 + return TRUE + return FALSE /datum/wires/vending/get_status() . = ..() @@ -39,31 +25,31 @@ . += "The green light is [V.extended_inventory ? "on" : "off"]." . += "A [V.scan_id ? "purple" : "yellow"] light is on." -/datum/wires/vending/UpdatePulsed(index) +/datum/wires/vending/on_pulse(wire) var/obj/machinery/vending/V = holder - switch(index) - if(VENDING_WIRE_THROW) + switch(wire) + if(WIRE_THROW_ITEM) V.shoot_inventory = !V.shoot_inventory - if(VENDING_WIRE_CONTRABAND) + if(WIRE_CONTRABAND) V.extended_inventory = !V.extended_inventory - if(VENDING_WIRE_ELECTRIFY) + if(WIRE_ELECTRIFY) V.seconds_electrified = 30 - if(VENDING_WIRE_IDSCAN) + if(WIRE_IDSCAN) V.scan_id = !V.scan_id ..() -/datum/wires/vending/UpdateCut(index, mended) +/datum/wires/vending/on_cut(wire, mend) var/obj/machinery/vending/V = holder - switch(index) - if(VENDING_WIRE_THROW) - V.shoot_inventory = !mended - if(VENDING_WIRE_CONTRABAND) + switch(wire) + if(WIRE_THROW_ITEM) + V.shoot_inventory = !mend + if(WIRE_CONTRABAND) V.extended_inventory = FALSE - if(VENDING_WIRE_ELECTRIFY) - if(mended) + if(WIRE_ELECTRIFY) + if(mend) V.seconds_electrified = 0 else V.seconds_electrified = -1 - if(VENDING_WIRE_IDSCAN) - V.scan_id = 1 + if(WIRE_IDSCAN) + V.scan_id = TRUE ..() diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index 09c874ce8a8..d2c092211ec 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -1,331 +1,456 @@ -// Wire datums. Created by Giacomand. -// Was created to replace a horrible case of copy and pasted code with no care for maintability. -// Goodbye Door wires, Cyborg wires, Vending Machine wires, Autolathe wires -// Protolathe wires, APC wires and Camera wires! - -#define MAX_FLAG 65535 - -GLOBAL_LIST_EMPTY(same_wires) -// 12 colours, if you're adding more than 12 wires then add more colours here -GLOBAL_LIST_INIT(wireColours, list("red", "blue", "green", "black", "orange", "brown", "gold", "gray", "cyan", "navy", "purple", "pink")) /datum/wires + /// TRUE if the wires will be different every time a new wire datum is created. + var/randomize = FALSE + /// The atom the wires belong too. For example: an airlock. + var/atom/holder + /// The holder type; used to make sure that the holder is the correct type. + var/holder_type + /// The display name for the TGUI window. For example, given the var is "APC"... + /// When the TGUI window is opened, "wires" will be appended to it's title, and it would become "APC wires". + var/proper_name = "Unknown" + /// The total number of wires that our holder atom has. + var/wire_count = NONE + /// A list of all wires. For a list of valid wires defines that can go here, see `code/__DEFINES/wires.dm` + var/list/wires + /// A list of all cut wires. The same values that can go into `wires` will get added and removed from this list. + var/list/cut_wires + /// An associative list with the wire color as the key, and the wire define as the value. + var/list/colors + /// An associative list of signalers attached to the wires. The wire color is the key, and the signaler object reference is the value. + var/list/assemblies + /// The width of the wire TGUI window. + var/window_x = 300 + /// The height of the wire TGUI window. Will get longer as needed, based on the `wire_count`. + var/window_y = 100 - var/random = 0 // Will the wires be different for every single instance. - var/atom/holder = null // The holder - var/holder_type = null // The holder type; used to make sure that the holder is the correct type. - var/wire_count = 0 // Max is 16 - var/wires_status = 0 // BITFLAG OF WIRES - - var/list/wires = list() - var/list/signallers = list() - - var/table_options = " align='center'" - var/row_options1 = " width='80px'" - var/row_options2 = " width='260px'" - var/window_x = 370 - var/window_y = 470 - -/datum/wires/New(atom/holder) +/datum/wires/New(atom/_holder) ..() - src.holder = holder - if(!istype(holder, holder_type)) + if(!istype(_holder, holder_type)) CRASH("Our holder is null/the wrong type!") - // Generate new wires - if(random) - GenerateWires() - // Get the same wires + holder = _holder + cut_wires = list() + colors = list() + assemblies = list() + + // Add in the appropriate amount of dud wires. + var/wire_len = length(wires) + if(wire_len < wire_count) // If the amount of "real" wires is less than the total we're suppose to have... + add_duds(wire_count - wire_len) // Add in the appropriate amount of duds to reach `wire_count`. + + // If the randomize is true, we need to generate a new set of wires and ignore any wire color directories. + if(randomize) + randomize() + return + + if(!GLOB.wire_color_directory[holder_type]) + randomize() + GLOB.wire_color_directory[holder_type] = colors else - // We don't have any wires to copy yet, generate some and then copy it. - if(!GLOB.same_wires[holder_type]) - GenerateWires() - GLOB.same_wires[holder_type] = src.wires.Copy() - else - var/list/wires = GLOB.same_wires[holder_type] - src.wires = wires // Reference the wires list. + colors = GLOB.wire_color_directory[holder_type] /datum/wires/Destroy() holder = null + for(var/color in colors) + detach_assembly(color) return ..() -/datum/wires/proc/GenerateWires() - var/list/colours_to_pick = GLOB.wireColours.Copy() // Get a copy, not a reference. - var/list/indexes_to_pick = list() - //Generate our indexes - for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i) - indexes_to_pick += i - colours_to_pick.len = wire_count // Downsize it to our specifications. +/** + * Randomly generates a new set of wires. and corresponding colors from the given pool. Assigns the information as an associative list, to `colors`. + * + * In the `colors` list, the name of the color is the key, and the wire is the value. + * For example: `colors["red"] = WIRE_ELECTRIFY`. This will look like `list("red" = WIRE_ELECTRIFY)` internally. + */ +/datum/wires/proc/randomize() + var/static/list/possible_colors = list("red", "blue", "green", "silver", "orange", "brown", "gold", "white", "cyan", "magenta", "purple", "pink") + var/list/my_possible_colors = possible_colors.Copy() - while(colours_to_pick.len && indexes_to_pick.len) - // Pick and remove a colour - var/colour = pick_n_take(colours_to_pick) - - // Pick and remove an index - var/index = pick_n_take(indexes_to_pick) - - src.wires[colour] = index - //wires = shuffle(wires) - -/datum/wires/proc/get_status() - return list() + for(var/wire in shuffle(wires)) + colors[pick_n_take(my_possible_colors)] = wire +/** + * Proc called when the user attempts to interact with wires UI. + * + * Checks if the user exists, is a mob, the wires are attached to something (`holder`) and makes sure `interactable(user)` returns TRUE. + * If all the checks succeed, open the TGUI interface for the user. + * + * Arugments: + * * user - the mob trying to interact with the wires. + */ /datum/wires/proc/Interact(mob/user) - if(user && istype(user) && holder && CanUse(user)) - ui_interact(user) + if(user && istype(user) && holder && interactable(user)) + tgui_interact(user) -/datum/wires/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/** + * Base proc, intended to be overriden. Wire datum specific checks you want to run before the TGUI is shown to the user should go here. + */ +/datum/wires/proc/interactable(mob/user) + return TRUE + +/// Users will be interacting with our holder object and not the wire datum directly, therefore we need to return the holder. +/datum/wires/tgui_host() + return holder + +/datum/wires/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_physical_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "wires.tmpl", holder.name, window_x, window_y) + ui = new(user, src, ui_key, "Wires", "[proper_name] wires", window_x, window_y + wire_count * 30, master_ui, state) ui.open() -/datum/wires/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state) - var/data[0] - var/list/replace_colours = null +/datum/wires/tgui_data(mob/user) + var/list/data = list() + var/list/replace_colors + if(ishuman(user)) var/mob/living/carbon/human/H = user var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes) - if(eyes && (COLOURBLIND in H.mutations)) - replace_colours = eyes.replace_colours + if(eyes && (COLOURBLIND in H.mutations)) // Check if the human has colorblindness. + replace_colors = eyes.replace_colours // Get the colorblind replacement colors list. + var/list/wires_list = list() - var/list/W[0] - for(var/colour in wires) - var/new_colour = colour - var/colour_name = colour - if(colour in replace_colours) - new_colour = replace_colours[colour] - if(new_colour in LIST_REPLACE_RENAME) - colour_name = LIST_REPLACE_RENAME[new_colour] + for(var/color in colors) + var/replaced_color = color + var/color_name = color + + if(color in replace_colors) // If this color is one that needs to be replaced using the colorblindness list. + replaced_color = replace_colors[color] + if(replaced_color in LIST_COLOR_RENAME) // If its an ugly written color name like "darkolivegreen", rename it to something like "dark green". + color_name = LIST_COLOR_RENAME[replaced_color] else - colour_name = new_colour - else - new_colour = colour - colour_name = new_colour - W[++W.len] = list("colour_name" = capitalize(colour_name), "seen_colour" = capitalize(new_colour),"colour" = capitalize(colour), "cut" = IsColourCut(colour), "index" = can_see_wire_index(user) ? GetWireName(GetIndex(colour)) : null, "attached" = IsAttached(colour)) + color_name = replaced_color // Else just keep the normal color name - if(W.len > 0) - data["wires"] = W + wires_list += list(list( + "seen_color" = replaced_color, // The color of the wire that the mob will see. This will be the same as `color` if the user is NOT colorblind. + "color_name" = color_name, // The wire's name. This will be the same as `color` if the user is NOT colorblind. + "color" = color, // The "real" color of the wire. No replacements. + "wire" = can_see_wire_info(user) && !is_dud_color(color) ? get_wire(color) : null, // Wire define information like "Contraband" or "Door Bolts". + "cut" = is_color_cut(color), // Whether the wire is cut or not. Used to display "cut" or "mend". + "attached" = is_attached(color) // Whether or not a signaler is attached to this wire. + )) + data["wires"] = wires_list + // Get the information shown at the bottom of wire TGUI window, such as "The red light is blinking", etc. + // If the user is colorblind, we need to replace these colors as well. var/list/status = get_status() - if(replace_colours) - var/i - for(i=1, i<=status.len, i++) - for(var/colour in replace_colours) - var/new_colour = replace_colours[colour] - if(new_colour in LIST_REPLACE_RENAME) - new_colour = LIST_REPLACE_RENAME[new_colour] - if(findtext(status[i],colour)) - status[i] = replacetext(status[i],colour,new_colour) - break - data["status_len"] = status.len - data["status"] = status + if(replace_colors) + var/i + for(i in 1 to length(status)) + for(var/color in replace_colors) + var/new_color = replace_colors[color] + if(new_color in LIST_COLOR_RENAME) + new_color = LIST_COLOR_RENAME[new_color] + if(findtext(status[i], color)) + status[i] = replacetext(status[i], color, new_color) + break + + data["status"] = status return data -/datum/wires/nano_host() - return holder +/datum/wires/tgui_act(action, list/params) + if(..()) + return -/datum/wires/proc/can_see_wire_index(mob/user) + var/mob/user = usr + if(!interactable(user)) + return + + var/obj/item/I = user.get_active_hand() + var/color = lowertext(params["wire"]) + holder.add_hiddenprint(user) + + switch(action) + // Toggles the cut/mend status. + if("cut") + if(!istype(I, /obj/item/wirecutters) && !user.can_admin_interact()) + to_chat(user, "You need wirecutters!") + return + + if(istype(I)) + playsound(holder, I.usesound, 20, 1) + cut_color(color) + return TRUE + + // Pulse a wire. + if("pulse") + if(!istype(I, /obj/item/multitool) && !user.can_admin_interact()) + to_chat(user, "You need a multitool!") + return + + playsound(holder, 'sound/weapons/empty.ogg', 20, 1) + pulse_color(color) + + // If they pulse the electrify wire, call interactable() and try to shock them. + if(get_wire(color) == WIRE_ELECTRIFY) + interactable(user) + + return TRUE + + // Attach a signaler to a wire. + if("attach") + if(is_attached(color)) + var/obj/item/O = detach_assembly(color) + if(O) + user.put_in_hands(O) + return TRUE + + if(!istype(I, /obj/item/assembly/signaler)) + to_chat(user, "You need a remote signaller!") + return + + if(user.drop_item()) + attach_assembly(color, I) + return TRUE + else + to_chat(user, "[user.get_active_hand()] is stuck to your hand!") + +/** + * Proc called to determine if the user can see wire define information, such as "Contraband", "Door Bolts", etc. + * + * If the user is an admin, or has a multitool which reveals wire information in their active hand, the proc returns TRUE. + * + * Arguments: + * * user - the mob who is interacting with the wires. + */ +/datum/wires/proc/can_see_wire_info(mob/user) if(user.can_admin_interact()) return TRUE else if(istype(user.get_active_hand(), /obj/item/multitool)) var/obj/item/multitool/M = user.get_active_hand() if(M.shows_wire_information) return TRUE - return FALSE -/datum/wires/Topic(href, href_list) - if(..()) - return 1 - var/mob/L = usr - if(CanUse(L) && href_list["action"]) - var/obj/item/I = L.get_active_hand() - var/colour = lowertext(href_list["wire"]) - holder.add_hiddenprint(L) - switch(href_list["action"]) - if("cut") // Toggles the cut/mend status - if(istype(I, /obj/item/wirecutters) || L.can_admin_interact()) - if(istype(I)) - playsound(holder, I.usesound, 20, 1) - CutWireColour(colour) - else - to_chat(L, "You need wirecutters!") - if("pulse") - if(istype(I, /obj/item/multitool) || L.can_admin_interact()) - playsound(holder, 'sound/weapons/empty.ogg', 20, 1) - PulseColour(colour) - else - to_chat(L, "You need a multitool!") - if("attach") - if(IsAttached(colour)) - var/obj/item/O = Detach(colour) - if(O) - L.put_in_hands(O) - else - if(istype(I, /obj/item/assembly/signaler)) - if(L.drop_item()) - Attach(colour, I) - else - to_chat(L, "[L.get_active_hand()] is stuck to your hand!") - else - to_chat(L, "You need a remote signaller!") +/** + * Base proc, intended to be overwritten. Put wire information you'll see at the botton of the TGUI window here, such as "The red light is blinking". + */ +/datum/wires/proc/get_status() + return list() - SSnanoui.update_uis(src) - return 1 +/** + * Clears the `colors` list, and randomizes it to a new set of color-to-wire relations. + */ +/datum/wires/proc/shuffle_wires() + colors.Cut() + randomize() -// -// Overridable Procs -// +/** + * Repairs all cut wires. + */ +/datum/wires/proc/repair() + cut_wires.Cut() -// Called when wires cut/mended. -/datum/wires/proc/UpdateCut(index, mended) - if(holder) - SSnanoui.update_uis(holder) +/** + * Adds in dud wires, which do nothing when cut/pulsed. + * + * Arguments: + * * duds - the amount of dud wires to generate. + */ +/datum/wires/proc/add_duds(duds) + while(duds) + var/dud = WIRE_DUD_PREFIX + "[--duds]" + if(dud in wires) + continue + wires += dud -// Called when wire pulsed. Add code here. -/datum/wires/proc/UpdatePulsed(index) - if(holder) - SSnanoui.update_uis(holder) +/** + * Determines if the passed in wire is a dud or not. Returns TRUE if the wire is a dud, FALSE otherwise. + * + * Arugments: + * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`. + */ +/datum/wires/proc/is_dud(wire) + return findtext(wire, WIRE_DUD_PREFIX, 1, length(WIRE_DUD_PREFIX) + 1) -/datum/wires/proc/CanUse(mob/L) - return 1 +/** + * Returns TRUE if the wire that corresponds to the passed in color is a dud. FALSE otherwise. + * + * Arugments: + * * color - a wire color. + */ +/datum/wires/proc/is_dud_color(color) + return is_dud(get_wire(color)) -/datum/wires/CanUseTopic(mob/user, datum/topic_state/state) - if(!CanUse(user)) - return STATUS_CLOSE - return ..() +/** + * Gets the wire associated with the color passed in. + * + * Arugments: + * * color - a wire color. + */ +/datum/wires/proc/get_wire(color) + return colors[color] -// Example of use: -/* +/** + * Determines if the passed in wire is cut or not. Returns TRUE if it's cut, FALSE otherwise. + * + * Arugments: + * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`. + */ +/datum/wires/proc/is_cut(wire) + return (wire in cut_wires) -#define NAME_WIRE_BOLTED 1 -#define NAME_WIRE_SHOCKED 2 -#define NAME_WIRE_SAFETY 4 -#define NAME_WIRE_POWER 8 +/** + * Determines if the wire associated with the passed in color, is cut or not. Returns TRUE if it's cut, FALSE otherwise. + * + * Arugments: + * * wire - a wire color. + */ +/datum/wires/proc/is_color_cut(color) + return is_cut(get_wire(color)) -/datum/wires/door/UpdateCut(var/index, var/mended) - var/obj/machinery/door/airlock/A = holder - switch(index) - if(NAME_WIRE_BOLTED) - if(!mended) - A.bolt() - if(NAME_WIRE_SHOCKED) - A.shock() - if(NAME_WIRE_SAFETY) - A.safety() +/** + * Determines if all of the wires are cut. Returns TRUE they're all cut, FALSE otherwise. + */ +/datum/wires/proc/is_all_cut() + return (length(cut_wires) == length(wires)) -*/ - - -// -// Helper Procs -// - -/datum/wires/proc/PulseColour(colour) - PulseIndex(GetIndex(colour)) - -/datum/wires/proc/PulseIndex(index) - if(IsIndexCut(index)) - return - UpdatePulsed(index) - -/datum/wires/proc/GetIndex(colour) - if(wires[colour]) - var/index = wires[colour] - return index +/** + * Cut or mend a wire. Calls `on_cut()`. + * + * Arugments: + * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`. + */ +/datum/wires/proc/cut(wire) + if(is_cut(wire)) + cut_wires -= wire + on_cut(wire, mend = TRUE) else - CRASH("[colour] is not a key in wires.") + cut_wires += wire + on_cut(wire, mend = FALSE) -/datum/wires/proc/GetWireName(index) +/** + * Cut the wire which corresponds with the passed in color. + * + * Arugments: + * * color - a wire color. + */ +/datum/wires/proc/cut_color(color) + cut(get_wire(color)) + +/** + * Cuts a random wire. + */ +/datum/wires/proc/cut_random() + cut(wires[rand(1, length(wires))]) + +/** + * Cuts all wires. + */ +/datum/wires/proc/cut_all() + for(var/wire in wires) + cut(wire) + +/** + * Proc called when any wire is cut. + * + * Base proc, intended to be overriden. + * Place an behavior you want to happen when certain wires are cut, into this proc. + * + * Arugments: + * * wire - a wire define, NOT color. For example 'WIRE_ELECTRIFY'. + * * mend - TRUE if we're mending the wire. FALSE if we're cutting. + */ +/datum/wires/proc/on_cut(wire, mend = FALSE) return -// -// Is Index/Colour Cut procs -// +/** + * Pulses the given wire. Calls `on_pulse()`. + * + * Arugments: + * * wire - a wire define, NOT a color. For example `WIRE_ELECTRIFY`. + */ +/datum/wires/proc/pulse(wire) + if(is_cut(wire)) + return + on_pulse(wire) -/datum/wires/proc/IsColourCut(colour) - var/index = GetIndex(colour) - return IsIndexCut(index) +/** + * Pulses the wire associated with the given color. + * + * Arugments: + * * wire - a wire color. + */ +/datum/wires/proc/pulse_color(color) + pulse(get_wire(color)) -/datum/wires/proc/IsIndexCut(index) - return (index & wires_status) +/** + * Proc called when any wire is pulsed. + * + * Base proc, intended to be overriden. + * Place behavior you want to happen when certain wires are pulsed, into this proc. + * + * Arugments: + * * wire - a wire define, NOT color. For example 'WIRE_ELECTRIFY'. + */ +/datum/wires/proc/on_pulse(wire) + return -// -// Signaller Procs -// +/** + * Proc called when an attached signaler receives a signal. + * + * Searches through the `assemblies` list for the wire that the signaler is attached to. Pulses the wire when it's found. + * + * Arugments: + * * S - the attached signaler receiving the signal. + */ +/datum/wires/proc/pulse_assembly(obj/item/assembly/signaler/S) + for(var/color in assemblies) + if(S == assemblies[color]) + pulse_color(color) + return TRUE -/datum/wires/proc/IsAttached(colour) - if(signallers[colour]) - return 1 - return 0 +/** + * Proc called when a mob tries to attach a signaler to a wire. + * + * Makes sure that `S` is actually a signaler and that something is not already attached to the wire. + * Adds the signaler to the `assemblies` list as a value, with the `color` as a the key. + * + * Arguments: + * * color - the wire color. + * * S - the signaler that a mob is trying to attach. + */ +/datum/wires/proc/attach_assembly(color, obj/item/assembly/signaler/S) + if(S && istype(S) && !is_attached(color)) + assemblies[color] = S + S.forceMove(holder) + S.connected = src + return S -/datum/wires/proc/GetAttached(colour) - if(signallers[colour]) - return signallers[colour] +/** + * Proc called when a mob tries to detach a signaler from a wire. + * + * First checks if there is a signaler on the wire. If so, removes the signaler, and clears it from `assemblies` list. + * + * Arguments: + * * color - the wire color. + */ +/datum/wires/proc/detach_assembly(color) + var/obj/item/assembly/signaler/S = get_attached(color) + if(S && istype(S)) + assemblies -= color + S.connected = null + S.forceMove(holder.drop_location()) + return S + +/** + * Gets the signaler attached to the given wire color, if there is one. + * + * Arguments: + * * color - the wire color. + */ +/datum/wires/proc/get_attached(color) + if(assemblies[color]) + return assemblies[color] return null -/datum/wires/proc/Attach(colour, obj/item/assembly/signaler/S) - if(colour && S) - if(!IsAttached(colour)) - signallers[colour] = S - S.loc = holder - S.connected = src - return S - -/datum/wires/proc/Detach(colour) - if(colour) - var/obj/item/assembly/signaler/S = GetAttached(colour) - if(S) - signallers -= colour - S.connected = null - S.loc = holder.loc - return S - - -/datum/wires/proc/Pulse(obj/item/assembly/signaler/S) - - for(var/colour in signallers) - if(S == signallers[colour]) - PulseColour(colour) - break - - -// -// Cut Wire Colour/Index procs -// - -/datum/wires/proc/CutWireColour(colour) - var/index = GetIndex(colour) - CutWireIndex(index) - -/datum/wires/proc/CutWireIndex(index) - if(IsIndexCut(index)) - wires_status &= ~index - UpdateCut(index, 1) - else - wires_status |= index - UpdateCut(index, 0) - -/datum/wires/proc/RandomCut() - var/r = rand(1, wires.len) - CutWireIndex(r) - -/datum/wires/proc/CutAll() - for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i) - CutWireIndex(i) - -/datum/wires/proc/IsAllCut() - if(wires_status == (1 << wire_count) - 1) - return 1 - return 0 - -// -//Shuffle and Mend -// - -/datum/wires/proc/Shuffle() - wires_status = 0 - GenerateWires() +/** + * Checks if the given wire has a signaler on it. + * + * Arguments: + * * color - the wire color. + */ +/datum/wires/proc/is_attached(color) + if(assemblies[color]) + return TRUE diff --git a/code/defines/procs/AStar.dm b/code/defines/procs/AStar.dm index 1f8c83f5f04..1d6fa5619e2 100644 --- a/code/defines/procs/AStar.dm +++ b/code/defines/procs/AStar.dm @@ -29,15 +29,15 @@ Actual Adjacent procs : ////////////////////// //A* nodes variables -/PathNode +/datum/pathnode var/turf/source //turf associated with the PathNode - var/PathNode/prevNode //link to the parent PathNode + var/datum/pathnode/prevNode //link to the parent PathNode var/f //A* Node weight (f = g + h) var/g //A* movement cost variable var/h //A* heuristic variable var/nt //count the number of Nodes traversed -/PathNode/New(s,p,pg,ph,pnt) +/datum/pathnode/New(s,p,pg,ph,pnt) source = s prevNode = p g = pg @@ -46,7 +46,7 @@ Actual Adjacent procs : source.PNode = src nt = pnt -/PathNode/proc/calc_f() +/datum/pathnode/proc/calc_f() f = g + h ////////////////////// @@ -54,11 +54,11 @@ Actual Adjacent procs : ////////////////////// //the weighting function, used in the A* algorithm -/proc/PathWeightCompare(PathNode/a, PathNode/b) +/proc/PathWeightCompare(datum/pathnode/a, datum/pathnode/b) return a.f - b.f //reversed so that the Heap is a MinHeap rather than a MaxHeap -/proc/HeapPathWeightCompare(PathNode/a, PathNode/b) +/proc/HeapPathWeightCompare(datum/pathnode/a, datum/pathnode/b) return b.f - a.f //wrapper that returns an empty list if A* failed to find a path @@ -82,13 +82,13 @@ Actual Adjacent procs : return 0 maxnodedepth = maxnodes //no need to consider path longer than maxnodes - var/Heap/open = new /Heap(/proc/HeapPathWeightCompare) //the open list + var/datum/heap/open = new /datum/heap(/proc/HeapPathWeightCompare) //the open list var/list/closed = new() //the closed list var/list/path = null //the returned path, if any - var/PathNode/cur //current processed turf + var/datum/pathnode/cur //current processed turf //initialization - open.Insert(new /PathNode(start,null,0,call(start,dist)(end),0)) + open.Insert(new /datum/pathnode(start,null,0,call(start,dist)(end),0)) //then run the main loop while(!open.IsEmpty() && !path) @@ -125,7 +125,7 @@ Actual Adjacent procs : var/newg = cur.g + call(cur.source,dist)(T) if(!T.PNode) //is not already in open list, so add it - open.Insert(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1)) + open.Insert(new /datum/pathnode(T,cur,newg,call(T,dist)(end),cur.nt+1)) else //is already in open list, check if it's a better way from the current turf if(newg < T.PNode.g) T.PNode.prevNode = cur @@ -137,7 +137,7 @@ Actual Adjacent procs : } //cleaning after us - for(var/PathNode/PN in open.L) + for(var/datum/pathnode/PN in open.L) PN.source.PNode = null for(var/turf/T in closed) T.PNode = null diff --git a/code/defines/procs/admin.dm b/code/defines/procs/admin.dm index fb8ca665906..fa5556b4e40 100644 --- a/code/defines/procs/admin.dm +++ b/code/defines/procs/admin.dm @@ -42,11 +42,11 @@ if(key) if(C && C.holder && C.holder.fakekey && !include_name) if(include_link) - . += "" + . += "" . += "Administrator" else if(include_link && C) - . += "" + . += "" . += key if(include_link) @@ -83,10 +83,6 @@ var/message = "[key_name(whom, 0)][isAntag(whom) ? "(ANTAG)" : ""][isLivingSSD(whom) ? "(SSD!)": ""]" return message -/proc/log_and_message_admins(var/message as text) +/proc/log_and_message_admins(message) log_admin("[key_name(usr)] " + message) message_admins("[key_name_admin(usr)] " + message) - -/proc/admin_log_and_message_admins(var/message as text) - log_admin("[key_name(usr)] " + message) - message_admins("[key_name_admin(usr)] " + message, 1) diff --git a/code/defines/procs/dbcore.dm b/code/defines/procs/dbcore.dm index 90a5b0db40c..945ab8af23a 100644 --- a/code/defines/procs/dbcore.dm +++ b/code/defines/procs/dbcore.dm @@ -29,7 +29,7 @@ #define BLOB 14 // TODO: Investigate more recent type additions and see if I can handle them. - Nadrew -DBConnection +/DBConnection var/_db_con // This variable contains a reference to the actual database connection. var/dbi // This variable is a string containing the DBI MySQL requires. var/user // This variable contains the username data. @@ -39,14 +39,14 @@ DBConnection var/server = "" var/port = 3306 -DBConnection/New(dbi_handler,username,password_handler,cursor_handler) +/DBConnection/New(dbi_handler,username,password_handler,cursor_handler) src.dbi = dbi_handler src.user = username src.password = password_handler src.default_cursor = cursor_handler _db_con = _dm_db_new_con() -DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) +/DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) if(!config.sql_enabled) return 0 if(!src) return 0 @@ -54,24 +54,24 @@ DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_han 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/Disconnect() return _dm_db_close(_db_con) -DBConnection/proc/IsConnected() +/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/Quote(str) return _dm_db_quote(_db_con,str) -DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) -DBConnection/proc/SelectDB(database_name,dbi) +/DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) +/DBConnection/proc/SelectDB(database_name,dbi) if(IsConnected()) Disconnect() //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) -DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) +/DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) -DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) +/DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) if(IsAdminAdvancedProcCall()) to_chat(usr, "DB query blocked: Advanced ProcCall detected.") message_admins("[key_name(usr)] attempted to create a DB query via advanced proc-call") @@ -83,12 +83,12 @@ DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) _db_query = _dm_db_new_query() return ..() -DBQuery/CanProcCall() +/DBQuery/CanProcCall() // dont even try it return FALSE -DBQuery +/DBQuery var/sql // The sql query being executed. var/default_cursor var/list/columns //list of DB Columns populated by Columns() @@ -98,26 +98,26 @@ DBQuery var/DBConnection/db_connection var/_db_query -DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler +/DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler -DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) +/DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) Close() return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) -DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) +/DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) -DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) +/DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) -DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) +/DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) -DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) +/DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) -DBQuery/proc/Columns() +/DBQuery/proc/Columns() if(!columns) columns = _dm_db_columns(_db_query,/DBColumn) return columns -DBQuery/proc/GetRowData() +/DBQuery/proc/GetRowData() var/list/columns = Columns() var/list/results if(columns.len) @@ -128,23 +128,23 @@ DBQuery/proc/GetRowData() results[C] = src.item[(cur_col.position+1)] return results -DBQuery/proc/Close() +/DBQuery/proc/Close() item.len = 0 columns = null conversions = null return _dm_db_close(_db_query) -DBQuery/proc/Quote(str) +/DBQuery/proc/Quote(str) return db_connection.Quote(str) -DBQuery/proc/SetConversion(column,conversion) +/DBQuery/proc/SetConversion(column,conversion) if(istext(column)) column = columns.Find(column) if(!conversions) conversions = new/list(column) else if(conversions.len < column) conversions.len = column conversions[column] = conversion -DBColumn +/DBColumn var/name var/table var/position //1-based index into item data @@ -153,7 +153,7 @@ DBColumn var/length var/max_length -DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) +/DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) src.name = name_handler src.table = table_handler src.position = position_handler @@ -164,7 +164,7 @@ DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handl return ..() -DBColumn/proc/SqlTypeName(type_handler=src.sql_type) +/DBColumn/proc/SqlTypeName(type_handler=src.sql_type) switch(type_handler) if(TINYINT) return "TINYINT" if(SMALLINT) return "SMALLINT" diff --git a/code/defines/vox_sounds.dm b/code/defines/vox_sounds.dm index 1d7bb9753a0..5b44f80895b 100644 --- a/code/defines/vox_sounds.dm +++ b/code/defines/vox_sounds.dm @@ -1,7 +1,16 @@ // List is required to compile the resources into the game when it loads. // Dynamically loading it has bad results with sounds overtaking each other, even with the wait variable. -GLOBAL_LIST_INIT(vox_sounds, list("," = 'sound/vox_fem/,.ogg', +GLOBAL_LIST_INIT(vox_alerts, list( +"bizwarn" = 'sound/vox_fem/bizwarn.ogg', +"bloop" = 'sound/vox_fem/bloop.ogg', +"buzwarn" = 'sound/vox_fem/buzwarn.ogg', +"dadeda" = 'sound/vox_fem/dadeda.ogg', +"deeoo" = 'sound/vox_fem/deeoo.ogg' +)) + +GLOBAL_LIST_INIT(vox_sounds, list( +"," = 'sound/vox_fem/,.ogg', "." = 'sound/vox_fem/..ogg', "a" = 'sound/vox_fem/a.ogg', "abortions" = 'sound/vox_fem/abortions.ogg', diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index c33e7609685..9a333d268f3 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -50,10 +50,10 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/space/atmosalert() return -/area/space/fire_alert() +/area/space/firealert(obj/source) return -/area/space/fire_reset() +/area/space/firereset(obj/source) return /area/space/readyalert() diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm index 8f65ac634f4..abfcb7210b0 100644 --- a/code/game/area/ai_monitored.dm +++ b/code/game/area/ai_monitored.dm @@ -1,24 +1,31 @@ /area/ai_monitored name = "AI Monitored Area" - var/obj/machinery/camera/motioncamera = null + var/list/motioncameras = list() + var/list/motionTargets = list() - -/area/ai_monitored/LateInitialize() +/area/ai_monitored/Initialize(mapload) . = ..() - // locate and store the motioncamera - for(var/obj/machinery/camera/M in src) - if(M.isMotion()) - motioncamera = M - M.area_motion = src - break + if(mapload) + for(var/obj/machinery/camera/M in src) + if(M.isMotion()) + motioncameras.Add(M) + M.AddComponent(/datum/component/proximity_monitor) + M.set_area_motion(src) /area/ai_monitored/Entered(atom/movable/O) ..() - if(ismob(O) && motioncamera) - motioncamera.newTarget(O) + if(ismob(O) && length(motioncameras)) + for(var/X in motioncameras) + var/obj/machinery/camera/cam = X + cam.newTarget(O) + return /area/ai_monitored/Exited(atom/movable/O) - if(ismob(O) && motioncamera) - motioncamera.lostTarget(O) + ..() + if(ismob(O) && length(motioncameras)) + for(var/X in motioncameras) + var/obj/machinery/camera/cam = X + cam.lostTargetRef(O.UID()) + return diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index da80a6e775c..aec3ea58f01 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -57,6 +57,11 @@ var/list/ambientsounds = GENERIC_SOUNDS + var/list/firedoors + var/list/cameras + var/list/firealarms + var/firedoors_last_closed_on = 0 + var/fast_despawn = FALSE var/can_get_auto_cryod = TRUE var/hide_attacklogs = FALSE // For areas such as thunderdome, lavaland syndiebase, etc which generate a lot of spammy attacklogs. Reduces log priority. @@ -125,35 +130,6 @@ cameras += C return cameras - -/area/proc/atmosalert(danger_level, var/alarm_source, var/force = FALSE) - if(report_alerts) - if(danger_level == ATMOS_ALARM_NONE) - SSalarms.atmosphere_alarm.clearAlarm(src, alarm_source) - else - SSalarms.atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) - - //Check all the alarms before lowering atmosalm. Raising is perfectly fine. If force = 1 we don't care. - for(var/obj/machinery/alarm/AA in src) - if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level && !force) - danger_level = max(danger_level, AA.danger_level) - - if(danger_level != atmosalm) - if(danger_level < ATMOS_ALARM_WARNING && atmosalm >= ATMOS_ALARM_WARNING) - //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 >= ATMOS_ALARM_DANGER && atmosalm < ATMOS_ALARM_DANGER) - air_doors_close() - - atmosalm = danger_level - for(var/obj/machinery/alarm/AA in src) - AA.update_icon() - - GLOB.air_alarm_repository.update_cache(src) - return 1 - GLOB.air_alarm_repository.update_cache(src) - return 0 - /area/proc/air_doors_close() if(!air_doors_activated) air_doors_activated = TRUE @@ -179,44 +155,151 @@ D.open() -/area/proc/fire_alert() - if(!fire) - fire = 1 //used for firedoor checks - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - air_doors_close() +/area/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() -/area/proc/fire_reset() - if(fire) - fire = 0 //used for firedoor checks - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - air_doors_open() +/** + * Generate a power alert for this area + * + * Sends to all ai players, alert consoles, drones and alarm monitor programs in the world + */ +/area/proc/poweralert(state, obj/source) + if(state != poweralm) + poweralm = state + if(istype(source)) //Only report power alarms on the z-level where the source is located. + for(var/thing in cameras) + var/obj/machinery/camera/C = locateUID(thing) + if(!QDELETED(C) && is_station_level(C.z)) + if(state) + C.network -= "Power Alarms" + else + C.network |= "Power Alarms" - return + if(state) + SSalarm.cancelAlarm("Power", src, source) + else + SSalarm.triggerAlarm("Power", src, cameras, source) -/area/proc/burglaralert(var/obj/trigger) - if(always_unpowered == 1) //no burglar alarms in space/asteroid +/** + * Generate an atmospheric alert for this area + * + * Sends to all ai players, alert consoles, drones and alarm monitor programs in the world + */ +/area/proc/atmosalert(danger_level, obj/source) + if(danger_level != atmosalm) + if(danger_level == ATMOS_ALARM_DANGER) + + for(var/thing in cameras) + var/obj/machinery/camera/C = locateUID(thing) + if(!QDELETED(C) && is_station_level(C.z)) + C.network |= "Atmosphere Alarms" + + + SSalarm.triggerAlarm("Atmosphere", src, cameras, source) + + else if(atmosalm == ATMOS_ALARM_DANGER) + for(var/thing in cameras) + var/obj/machinery/camera/C = locateUID(thing) + if(!QDELETED(C) && is_station_level(C.z)) + C.network -= "Atmosphere Alarms" + + SSalarm.cancelAlarm("Atmosphere", src, source) + + atmosalm = danger_level + return TRUE + return FALSE + +/** + * Try to close all the firedoors in the area + */ +/area/proc/ModifyFiredoors(opening) + if(firedoors) + firedoors_last_closed_on = world.time + for(var/FD in firedoors) + var/obj/machinery/door/firedoor/D = FD + var/cont = !D.welded + if(cont && opening) //don't open if adjacent area is on fire + for(var/I in D.affecting_areas) + var/area/A = I + if(A.fire) + cont = FALSE + break + if(cont && D.is_operational()) + if(D.operating) + D.nextstate = opening ? FD_OPEN : FD_CLOSED + else if(!(D.density ^ opening)) + INVOKE_ASYNC(D, (opening ? /obj/machinery/door/firedoor.proc/open : /obj/machinery/door/firedoor.proc/close)) + +/** + * Generate a firealarm alert for this area + * + * Sends to all ai players, alert consoles, drones and alarm monitor programs in the world + * + * Also starts the area processing on SSobj + */ +/area/proc/firealert(obj/source) + if(always_unpowered) //no fire alarms in space/asteroid return - //Trigger alarm effect - set_fire_alarm_effect() + if(!fire) + set_fire_alarm_effect() + ModifyFiredoors(FALSE) + for(var/item in firealarms) + var/obj/machinery/firealarm/F = item + F.update_icon() - //Lockdown airlocks - for(var/obj/machinery/door/airlock/A in src) - spawn(0) - A.close() - if(A.density) - A.lock() + for(var/thing in cameras) + var/obj/machinery/camera/C = locateUID(thing) + if(!QDELETED(C) && is_station_level(C.z)) + C.network |= "Fire Alarms" - SSalarms.burglar_alarm.triggerAlarm(src, trigger) - spawn(600) - SSalarms.burglar_alarm.clearAlarm(src, trigger) + SSalarm.triggerAlarm("Fire", src, cameras, source) -/area/proc/set_fire_alarm_effect() - fire = 1 - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT + START_PROCESSING(SSobj, src) + +/** + * Reset the firealarm alert for this area + * + * resets the alert sent to all ai players, alert consoles, drones and alarm monitor programs + * in the world + * + * Also cycles the icons of all firealarms and deregisters the area from processing on SSOBJ + */ +/area/proc/firereset(obj/source) + if(fire) + unset_fire_alarm_effects() + ModifyFiredoors(TRUE) + for(var/item in firealarms) + var/obj/machinery/firealarm/F = item + F.update_icon() + + for(var/thing in cameras) + var/obj/machinery/camera/C = locateUID(thing) + if(!QDELETED(C) && is_station_level(C.z)) + C.network -= "Fire Alarms" + + SSalarm.cancelAlarm("Fire", src, source) + + STOP_PROCESSING(SSobj, src) + +/** + * If 100 ticks has elapsed, toggle all the firedoors closed again + */ +/area/process() + if(firedoors_last_closed_on + 100 < world.time) //every 10 seconds + ModifyFiredoors(FALSE) + +/** + * Close and lock a door passed into this proc + * + * Does this need to exist on area? probably not + */ +/area/proc/close_and_lock_door(obj/machinery/door/DOOR) + set waitfor = FALSE + DOOR.close() + if(DOOR.density) + DOOR.lock() /area/proc/readyalert() if(!eject) @@ -240,13 +323,62 @@ mouse_opacity = MOUSE_OPACITY_TRANSPARENT updateicon() +/** + * Raise a burglar alert for this area + * + * Close and locks all doors in the area and alerts silicon mobs of a break in + * + * Alarm auto resets after 600 ticks + */ +/area/proc/burglaralert(obj/trigger) + if(always_unpowered) //no burglar alarms in space/asteroid + return + + //Trigger alarm effect + set_fire_alarm_effect() + //Lockdown airlocks + for(var/obj/machinery/door/DOOR in src) + close_and_lock_door(DOOR) + + if(SSalarm.triggerAlarm("Burglar", src, cameras, trigger)) + //Cancel silicon alert after 1 minute + addtimer(CALLBACK(SSalarm, /datum/controller/subsystem/alarm.proc/cancelAlarm, "Burglar", src, trigger), 600) + +/** + * Trigger the fire alarm visual affects in an area + * + * Updates the fire light on fire alarms in the area and sets all lights to emergency mode + */ +/area/proc/set_fire_alarm_effect() + fire = TRUE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + for(var/alarm in firealarms) + var/obj/machinery/firealarm/F = alarm + F.update_fire_light(fire) + for(var/obj/machinery/light/L in src) + L.update() + +/** + * unset the fire alarm visual affects in an area + * + * Updates the fire light on fire alarms in the area and sets all lights to emergency mode + */ +/area/proc/unset_fire_alarm_effects() + fire = FALSE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + for(var/alarm in firealarms) + var/obj/machinery/firealarm/F = alarm + F.update_fire_light(fire) + for(var/obj/machinery/light/L in src) + L.update() + /area/proc/updateicon() - if((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc. - if(fire && !eject && !party) + if((eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc. + if(!eject && !party) icon_state = "red" - else if(!fire && eject && !party) + else if(eject && !party) icon_state = "red" - else if(party && !fire && !eject) + else if(party && !eject) icon_state = "party" else icon_state = "blue-red" @@ -288,11 +420,15 @@ /area/space/powered(chan) //Nope.avi return 0 -// called when power status changes - +/** + * Called when the area power status changes + * + * Updates the area icon, calls power change on all machines in the area, and sends the `COMSIG_AREA_POWER_CHANGE` signal. + */ /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.) + SEND_SIGNAL(src, COMSIG_AREA_POWER_CHANGE) updateicon() /area/proc/usage(var/chan) @@ -438,7 +574,7 @@ /area/proc/prison_break() for(var/obj/machinery/power/apc/temp_apc in src) - temp_apc.overload_lighting(70) + INVOKE_ASYNC(temp_apc, /obj/machinery/power/apc.proc/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) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 776a2bccd12..73dcd14c7c8 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -231,7 +231,7 @@ /atom/proc/on_reagent_change() return -/atom/proc/Bumped(AM as mob|obj) +/atom/proc/Bumped(atom/movable/AM) return /// Convenience proc to see if a container is open for chemistry handling @@ -257,7 +257,7 @@ /atom/proc/CheckExit() return TRUE -/atom/proc/HasProximity(atom/movable/AM as mob|obj) +/atom/proc/HasProximity(atom/movable/AM) return /atom/proc/emp_act(severity) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index ab5ff19462a..d463c85eaf6 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -61,7 +61,7 @@ /atom/movable/proc/get_cell() return -/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) +/atom/movable/proc/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) if(QDELETED(AM)) return FALSE if(!(AM.can_be_pulled(src, state, force))) @@ -87,7 +87,7 @@ if(ismob(AM)) var/mob/M = AM add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL) - if(!supress_message) + if(show_message) visible_message("[src] has grabbed [M] passively!") return TRUE @@ -120,12 +120,18 @@ if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move. pulledby.stop_pulling() -/atom/movable/proc/can_be_pulled(user, grab_state, force) +/atom/movable/proc/can_be_pulled(user, grab_state, force, show_message = FALSE) if(src == user || !isturf(loc)) return FALSE - if(anchored || throwing) + if(anchored || move_resist == INFINITY) + if(show_message) + to_chat(user, "[src] appears to be anchored to the ground!") + return FALSE + if(throwing) return FALSE if(force < (move_resist * MOVE_FORCE_PULL_RATIO)) + if(show_message) + to_chat(user, "[src] is too heavy to pull!") return FALSE return TRUE @@ -506,6 +512,7 @@ return //don't do an animation if attacking self var/pixel_x_diff = 0 var/pixel_y_diff = 0 + var/direction = get_dir(src, A) if(direction & NORTH) pixel_y_diff = 8 @@ -525,7 +532,8 @@ if(visual_effect_icon) I = image('icons/effects/effects.dmi', A, visual_effect_icon, A.layer + 0.1) else if(used_item) - I = image(used_item.icon, A, used_item.icon_state, A.layer + 0.1) + I = image(icon = used_item, loc = A, layer = A.layer + 0.1) + I.plane = GAME_PLANE // Scale the icon. I.transform *= 0.75 @@ -567,3 +575,6 @@ /atom/movable/proc/portal_destroyed(obj/effect/portal/P) return + +/atom/movable/proc/decompile_act(obj/item/matter_decompiler/C, mob/user) // For drones to decompile mobs and objs. See drone for an example. + return FALSE diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index fb0ccff32cb..3158cf90303 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -7,6 +7,11 @@ #define NEGATE_MUTATION_THRESHOLD 30 // Occupants with over ## percent radiation threshold will not gain mutations +#define PAGE_UI "ui" +#define PAGE_SE "se" +#define PAGE_BUFFER "buffer" +#define PAGE_REJUVENATORS "rejuvenators" + //list("data" = null, "owner" = null, "label" = null, "type" = null, "ue" = 0), /datum/dna2/record var/datum/dna/dna = null @@ -161,6 +166,7 @@ occupant = usr icon_state = "scanner_occupied" add_fingerprint(usr) + SStgui.update_uis(src) /obj/machinery/dna_scannernew/MouseDrop_T(atom/movable/O, mob/user) if(!istype(O)) @@ -214,6 +220,7 @@ return beaker = I + SStgui.update_uis(src) I.forceMove(src) user.visible_message("[user] adds \a [I] to \the [src]!", "You add \a [I] to \the [src]!") return @@ -260,6 +267,7 @@ M.forceMove(src) occupant = M icon_state = "scanner_occupied" + SStgui.update_uis(src) // search for ghosts, if the corpse is empty and the scanner is connected to a cloner if(locate(/obj/machinery/computer/cloning, get_step(src, NORTH)) \ @@ -281,6 +289,10 @@ occupant.forceMove(loc) occupant = null icon_state = "scanner_open" + SStgui.update_uis(src) + +/obj/machinery/dna_scannernew/force_eject_occupant() + go_out(null, TRUE) /obj/machinery/dna_scannernew/ex_act(severity) if(occupant) @@ -293,6 +305,7 @@ occupant = null updateUsrDialog() update_icon() + SStgui.update_uis(src) // Checks if occupants can be irradiated/mutated - prevents exploits where wearing full rad protection would still let you gain mutations /obj/machinery/dna_scannernew/proc/radiation_check() @@ -330,12 +343,11 @@ var/injector_ready = FALSE //Quick fix for issue 286 (screwdriver the screen twice to restore injector) -Pete var/obj/machinery/dna_scannernew/connected = null var/obj/item/disk/data/disk = null - var/selected_menu_key = null + var/selected_menu_key = PAGE_UI anchored = TRUE use_power = IDLE_POWER_USE idle_power_usage = 10 active_power_usage = 400 - var/waiting_for_user_input = 0 // Fix for #274 (Mash create block injector without answering dialog to make unlimited injectors) - N3X /obj/machinery/computer/scan_consolenew/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/disk/data)) //INSERT SOME diskS @@ -344,7 +356,7 @@ I.forceMove(src) disk = I to_chat(user, "You insert [I].") - SSnanoui.update_uis(src) // update all UIs attached to src() + SStgui.update_uis(src) return else return ..() @@ -396,35 +408,18 @@ if(stat & (NOPOWER|BROKEN)) return - ui_interact(user) + tgui_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 - * - * @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/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) +/obj/machinery/computer/scan_consolenew/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) if(user == connected.occupant) return - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, "dna_modifier.tmpl", "DNA Modifier Console", 660, 700) - // open the new ui window + ui = new(user, src, ui_key, "DNAModifier", name, 660, 700, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) -/obj/machinery/computer/scan_consolenew/ui_data(mob/user, datum/topic_state/state) +/obj/machinery/computer/scan_consolenew/tgui_data(mob/user) var/data[0] data["selectedMenuKey"] = selected_menu_key data["locked"] = connected.locked @@ -498,9 +493,12 @@ for(var/datum/reagent/R in connected.beaker.reagents.reagent_list) data["beakerVolume"] += R.volume + // Transfer modal information if there is one + data["modal"] = tgui_modal_data(src) + return data -/obj/machinery/computer/scan_consolenew/Topic(href, href_list) +/obj/machinery/computer/scan_consolenew/tgui_act(action, params) if(..()) return FALSE // don't update uis if(!istype(usr.loc, /turf)) @@ -509,405 +507,350 @@ return FALSE // don't update uis if(irradiating) // Make sure that it isn't already irradiating someone... return FALSE // don't update uis + if(stat & (NOPOWER|BROKEN)) + return add_fingerprint(usr) - if(href_list["selectMenuKey"]) - selected_menu_key = href_list["selectMenuKey"] - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["toggleLock"]) - if((connected && connected.occupant)) - connected.locked = !(connected.locked) - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["pulseRadiation"]) - irradiating = radiation_duration - var/lock_state = connected.locked - connected.locked = TRUE //lock it - SSnanoui.update_uis(src) // update all UIs attached to src - - sleep(10 * radiation_duration) // sleep for radiation_duration seconds - - irradiating = 0 - connected.locked = lock_state - - if(!connected.occupant) - return TRUE // return 1 forces an update to all Nano uis attached to src - - var/radiation = (((radiation_intensity * 3) + radiation_duration * 3) / connected.damage_coeff) - connected.occupant.apply_effect(radiation, IRRADIATE, 0) - if(connected.radiation_check()) - return TRUE - - if(prob(95)) - if(prob(75)) - randmutb(connected.occupant) - else - randmuti(connected.occupant) - else - if(prob(95)) - randmutg(connected.occupant) - else - randmuti(connected.occupant) - - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["radiationDuration"]) - if(text2num(href_list["radiationDuration"]) > 0) - if(radiation_duration < 20) - radiation_duration += 2 - else - if(radiation_duration > 2) - radiation_duration -= 2 - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["radiationIntensity"]) - if(text2num(href_list["radiationIntensity"]) > 0) - if(radiation_intensity < 10) - radiation_intensity++ - else - if(radiation_intensity > 1) - radiation_intensity-- - return TRUE // return 1 forces an update to all Nano uis attached to src - - //////////////////////////////////////////////////////// - - if(href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) > 0) - if(selected_ui_target < 15) - selected_ui_target++ - selected_ui_target_hex = selected_ui_target - switch(selected_ui_target) - if(10) - selected_ui_target_hex = "A" - if(11) - selected_ui_target_hex = "B" - if(12) - selected_ui_target_hex = "C" - if(13) - selected_ui_target_hex = "D" - if(14) - selected_ui_target_hex = "E" - if(15) - selected_ui_target_hex = "F" - else - selected_ui_target = 0 - selected_ui_target_hex = 0 - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) < 1) - if(selected_ui_target > 0) - selected_ui_target-- - selected_ui_target_hex = selected_ui_target - switch(selected_ui_target) - if(10) - selected_ui_target_hex = "A" - if(11) - selected_ui_target_hex = "B" - if(12) - selected_ui_target_hex = "C" - if(13) - selected_ui_target_hex = "D" - if(14) - selected_ui_target_hex = "E" - else - selected_ui_target = 15 - selected_ui_target_hex = "F" - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["selectUIBlock"] && href_list["selectUISubblock"]) // This chunk of code updates selected block / sub-block based on click - var/select_block = text2num(href_list["selectUIBlock"]) - var/select_subblock = text2num(href_list["selectUISubblock"]) - if((select_block <= DNA_UI_LENGTH) && (select_block >= 1)) - selected_ui_block = select_block - if((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1)) - selected_ui_subblock = select_subblock - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["pulseUIRadiation"]) - var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block, selected_ui_subblock) - - irradiating = radiation_duration - var/lock_state = connected.locked - connected.locked = TRUE //lock it - SSnanoui.update_uis(src) // update all UIs attached to src - - sleep(10 * radiation_duration) // sleep for radiation_duration seconds - - irradiating = 0 - connected.locked = lock_state - - if(!connected.occupant) - return TRUE - - if(prob((80 + (radiation_duration / 2)))) - var/radiation = (radiation_intensity + radiation_duration) - connected.occupant.apply_effect(radiation,IRRADIATE,0) - - if(connected.radiation_check()) - return TRUE - - block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration) - connected.occupant.dna.SetUISubBlock(selected_ui_block, selected_ui_subblock, block) - connected.occupant.UpdateAppearance() - else - var/radiation = ((radiation_intensity * 2) + radiation_duration) - connected.occupant.apply_effect(radiation, IRRADIATE, 0) - if(connected.radiation_check()) - return TRUE - - if(prob(20 + radiation_intensity)) - randmutb(connected.occupant) - domutcheck(connected.occupant, connected) - else - randmuti(connected.occupant) - connected.occupant.UpdateAppearance() - return TRUE // return 1 forces an update to all Nano uis attached to src - - //////////////////////////////////////////////////////// - - if(href_list["injectRejuvenators"]) - if(!connected.occupant) - return FALSE - var/inject_amount = round(text2num(href_list["injectRejuvenators"]), 5) // round to nearest 5 - if(inject_amount < 0) // Since the user can actually type the commands himself, some sanity checking - inject_amount = 0 - if(inject_amount > 50) - inject_amount = 50 - connected.beaker.reagents.trans_to(connected.occupant, inject_amount) - connected.beaker.reagents.reaction(connected.occupant) - return TRUE // return 1 forces an update to all Nano uis attached to src - - //////////////////////////////////////////////////////// - - if(href_list["selectSEBlock"] && href_list["selectSESubblock"]) // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes) - var/select_block = text2num(href_list["selectSEBlock"]) - var/select_subblock = text2num(href_list["selectSESubblock"]) - if((select_block <= DNA_SE_LENGTH) && (select_block >= 1)) - selected_se_block = select_block - if((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1)) - selected_se_subblock = select_subblock - //testing("User selected block [selected_se_block] (sent [select_block]), subblock [selected_se_subblock] (sent [select_block]).") - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["pulseSERadiation"]) - var/block = connected.occupant.dna.GetSESubBlock(selected_se_block, selected_se_subblock) - //var/original_block=block - //testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...") - - irradiating = radiation_duration - var/lock_state = connected.locked - connected.locked = TRUE //lock it - SSnanoui.update_uis(src) // update all UIs attached to src - - sleep(10 * radiation_duration) // sleep for radiation_duration seconds - - irradiating = 0 - connected.locked = lock_state - - if(connected.occupant) - if(prob((80 + ((radiation_duration / 2) + (connected.precision_coeff ** 3))))) - var/radiation = ((radiation_intensity + radiation_duration) / connected.damage_coeff) - connected.occupant.apply_effect(radiation, IRRADIATE, 0) - - if(connected.radiation_check()) - return 1 - - var/real_SE_block=selected_se_block - block = miniscramble(block, radiation_intensity, radiation_duration) - if(prob(20)) - if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2) - real_SE_block++ - else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH) - real_SE_block-- - - //testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!") - connected.occupant.dna.SetSESubBlock(real_SE_block, selected_se_subblock, block) - domutcheck(connected.occupant, connected) - else - var/radiation = (((radiation_intensity * 2) + radiation_duration) / connected.damage_coeff) - connected.occupant.apply_effect(radiation, IRRADIATE, 0) - - if(connected.radiation_check()) - return 1 - - if(prob(80 - radiation_duration)) - //testing("Random bad mut!") - randmutb(connected.occupant) - domutcheck(connected.occupant, connected) - else - randmuti(connected.occupant) - //testing("Random identity mut!") - connected.occupant.UpdateAppearance() - return TRUE // return 1 forces an update to all Nano uis attached to src - - if(href_list["ejectBeaker"]) - if(connected.beaker) - var/obj/item/reagent_containers/glass/B = connected.beaker - B.forceMove(connected.loc) - connected.beaker = null + if(tgui_act_modal(action, params)) return TRUE - if(href_list["ejectOccupant"]) - connected.eject_occupant(usr) - return TRUE - - // Transfer Buffer Management - if(href_list["bufferOption"]) - var/bufferOption = href_list["bufferOption"] - - // These bufferOptions do not require a bufferId - if(bufferOption == "wipeDisk") - if((isnull(disk)) || (disk.read_only)) - //temphtml = "Invalid disk. Please try again." - return FALSE - - disk.buf = null - //temphtml = "Data saved." - return TRUE - - if(bufferOption == "ejectDisk") - if(!disk) + . = TRUE + switch(action) + if("selectMenuKey") + var/key = params["key"] + if(!(key in list(PAGE_UI, PAGE_SE, PAGE_BUFFER, PAGE_REJUVENATORS))) return - disk.forceMove(get_turf(src)) - disk = null - return TRUE - - // All bufferOptions from here on require a bufferId - if(!href_list["bufferId"]) - return FALSE - - var/bufferId = text2num(href_list["bufferId"]) - - if(bufferId < 1 || bufferId > 3) - return FALSE // Not a valid buffer id - - if(bufferOption == "saveUI") - if(connected.occupant && connected.occupant.dna) - var/datum/dna2/record/databuf = new - databuf.types = DNA2_BUF_UI // DNA2_BUF_UE - databuf.dna = connected.occupant.dna.Clone() - if(ishuman(connected.occupant)) - databuf.dna.real_name=connected.occupant.name - databuf.name = "Unique Identifier" - buffers[bufferId] = databuf - return TRUE - - if(bufferOption == "saveUIAndUE") - if(connected.occupant && connected.occupant.dna) - var/datum/dna2/record/databuf = new - databuf.types = DNA2_BUF_UI|DNA2_BUF_UE - databuf.dna = connected.occupant.dna.Clone() - if(ishuman(connected.occupant)) - databuf.dna.real_name=connected.occupant.dna.real_name - databuf.name = "Unique Identifier + Unique Enzymes" - buffers[bufferId] = databuf - return TRUE - - if(bufferOption == "saveSE") - if(connected.occupant && connected.occupant.dna) - var/datum/dna2/record/databuf = new - databuf.types = DNA2_BUF_SE - databuf.dna = connected.occupant.dna.Clone() - if(ishuman(connected.occupant)) - databuf.dna.real_name = connected.occupant.dna.real_name - databuf.name = "Structural Enzymes" - buffers[bufferId] = databuf - return TRUE - - if(bufferOption == "clear") - buffers[bufferId] = new /datum/dna2/record() - return TRUE - - if(bufferOption == "changeLabel") - var/datum/dna2/record/buf = buffers[bufferId] - var/text = sanitize(input(usr, "New Label:", "Edit Label", buf.name) as text|null) - buf.name = text - buffers[bufferId] = buf - return TRUE - - if(bufferOption == "transfer") - if(!connected.occupant || (NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !connected.occupant.dna) - return TRUE - - irradiating = 2 + selected_menu_key = key + if("toggleLock") + if(connected && connected.occupant) + connected.locked = !(connected.locked) + if("pulseRadiation") + irradiating = radiation_duration var/lock_state = connected.locked connected.locked = TRUE //lock it - SSnanoui.update_uis(src) // update all UIs attached to src - sleep(2 SECONDS) + SStgui.update_uis(src) + sleep(10 * radiation_duration) // sleep for radiation_duration seconds irradiating = 0 connected.locked = lock_state - var/radiation = (rand(20,50) / connected.damage_coeff) + if(!connected.occupant) + return + + var/radiation = (((radiation_intensity * 3) + radiation_duration * 3) / connected.damage_coeff) connected.occupant.apply_effect(radiation, IRRADIATE, 0) - if(connected.radiation_check()) - return TRUE + return - var/datum/dna2/record/buf = buffers[bufferId] - - if((buf.types & DNA2_BUF_UI)) - if((buf.types & DNA2_BUF_UE)) - connected.occupant.real_name = buf.dna.real_name - connected.occupant.name = buf.dna.real_name - connected.occupant.UpdateAppearance(buf.dna.UI.Copy()) - else if(buf.types & DNA2_BUF_SE) - connected.occupant.dna.SE = buf.dna.SE.Copy() - connected.occupant.dna.UpdateSE() - domutcheck(connected.occupant, connected) - return TRUE - - if(bufferOption == "createInjector") - if(injector_ready && !waiting_for_user_input) - - var/success = 1 - var/obj/item/dnainjector/I = new /obj/item/dnainjector - var/datum/dna2/record/buf = buffers[bufferId] - buf = buf.copy() - if(href_list["createBlockInjector"]) - waiting_for_user_input=1 - var/list/selectedbuf - if(buf.types & DNA2_BUF_SE) - selectedbuf=buf.dna.SE - else - selectedbuf=buf.dna.UI - var/blk = input(usr,"Select Block","Block") as null|anything in all_dna_blocks(selectedbuf) - success = setInjectorBlock(I,blk,buf) + if(prob(95)) + if(prob(75)) + randmutb(connected.occupant) else - I.buf = buf - waiting_for_user_input = 0 - if(success) - I.forceMove(loc) - I.name += " ([buf.name])" - if(connected) - I.damage_coeff = connected.damage_coeff - injector_ready = FALSE - spawn(300) - injector_ready = TRUE - return TRUE + randmuti(connected.occupant) + else + if(prob(95)) + randmutg(connected.occupant) + else + randmuti(connected.occupant) + if("radiationDuration") + radiation_duration = clamp(text2num(params["value"]), 1, 20) + if("radiationIntensity") + radiation_intensity = clamp(text2num(params["value"]), 1, 10) + //////////////////////////////////////////////////////// + if("changeUITarget") + selected_ui_target = clamp(text2num(params["value"]), 1, 15) + selected_ui_target_hex = num2text(selected_ui_target, 1, 16) + if("selectUIBlock") // This chunk of code updates selected block / sub-block based on click + var/select_block = text2num(params["block"]) + var/select_subblock = text2num(params["subblock"]) + if(!select_block || !select_subblock) + return - if(bufferOption == "loadDisk") - if((isnull(disk)) || (!disk.buf)) - //temphtml = "Invalid disk. Please try again." - return FALSE + selected_ui_block = clamp(select_block, 1, DNA_UI_LENGTH) + selected_ui_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE) + if("pulseUIRadiation") + var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block, selected_ui_subblock) - buffers[bufferId] = disk.buf.copy() - //temphtml = "Data loaded." - return TRUE + irradiating = radiation_duration + var/lock_state = connected.locked + connected.locked = TRUE //lock it - if(bufferOption == "saveDisk") - if((isnull(disk)) || (disk.read_only)) - //temphtml = "Invalid disk. Please try again." - return FALSE + SStgui.update_uis(src) + sleep(10 * radiation_duration) // sleep for radiation_duration seconds - var/datum/dna2/record/buf = buffers[bufferId] + irradiating = 0 + connected.locked = lock_state - disk.buf = buf.copy() - disk.name = "data disk - '[buf.dna.real_name]'" - //temphtml = "Data saved." - return TRUE + if(!connected.occupant) + return + if(prob((80 + (radiation_duration / 2)))) + var/radiation = (radiation_intensity + radiation_duration) + connected.occupant.apply_effect(radiation,IRRADIATE,0) + + if(connected.radiation_check()) + return + + block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration) + connected.occupant.dna.SetUISubBlock(selected_ui_block, selected_ui_subblock, block) + connected.occupant.UpdateAppearance() + else + var/radiation = ((radiation_intensity * 2) + radiation_duration) + connected.occupant.apply_effect(radiation, IRRADIATE, 0) + if(connected.radiation_check()) + return + + if(prob(20 + radiation_intensity)) + randmutb(connected.occupant) + domutcheck(connected.occupant, connected) + else + randmuti(connected.occupant) + connected.occupant.UpdateAppearance() + //////////////////////////////////////////////////////// + if("injectRejuvenators") + if(!connected.occupant || !connected.beaker) + return + var/inject_amount = clamp(round(text2num(params["amount"]), 5), 0, 50) // round to nearest 5 and clamp to 0-50 + if(!inject_amount) + return + connected.beaker.reagents.trans_to(connected.occupant, inject_amount) + connected.beaker.reagents.reaction(connected.occupant) + //////////////////////////////////////////////////////// + if("selectSEBlock") // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes) + var/select_block = text2num(params["block"]) + var/select_subblock = text2num(params["subblock"]) + if(!select_block || !select_subblock) + return + + selected_se_block = clamp(select_block, 1, DNA_SE_LENGTH) + selected_se_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE) + if("pulseSERadiation") + var/block = connected.occupant.dna.GetSESubBlock(selected_se_block, selected_se_subblock) + //var/original_block=block + //testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...") + + irradiating = radiation_duration + var/lock_state = connected.locked + connected.locked = TRUE //lock it + + SStgui.update_uis(src) + sleep(10 * radiation_duration) // sleep for radiation_duration seconds + + irradiating = 0 + connected.locked = lock_state + + if(connected.occupant) + if(prob((80 + ((radiation_duration / 2) + (connected.precision_coeff ** 3))))) + var/radiation = ((radiation_intensity + radiation_duration) / connected.damage_coeff) + connected.occupant.apply_effect(radiation, IRRADIATE, 0) + + if(connected.radiation_check()) + return 1 + + var/real_SE_block=selected_se_block + block = miniscramble(block, radiation_intensity, radiation_duration) + if(prob(20)) + if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2) + real_SE_block++ + else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH) + real_SE_block-- + + //testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!") + connected.occupant.dna.SetSESubBlock(real_SE_block, selected_se_subblock, block) + domutcheck(connected.occupant, connected) + else + var/radiation = (((radiation_intensity * 2) + radiation_duration) / connected.damage_coeff) + connected.occupant.apply_effect(radiation, IRRADIATE, 0) + + if(connected.radiation_check()) + return + + if(prob(80 - radiation_duration)) + //testing("Random bad mut!") + randmutb(connected.occupant) + domutcheck(connected.occupant, connected) + else + randmuti(connected.occupant) + //testing("Random identity mut!") + connected.occupant.UpdateAppearance() + if("ejectBeaker") + if(connected.beaker) + var/obj/item/reagent_containers/glass/B = connected.beaker + B.forceMove(connected.loc) + connected.beaker = null + if("ejectOccupant") + connected.eject_occupant() + // Transfer Buffer Management + if("bufferOption") + var/bufferOption = params["option"] + var/bufferId = text2num(params["id"]) + if(bufferId < 1 || bufferId > 3) // Not a valid buffer id + return + + var/datum/dna2/record/buffer = buffers[bufferId] + switch(bufferOption) + if("saveUI") + if(connected.occupant && connected.occupant.dna) + var/datum/dna2/record/databuf = new + databuf.types = DNA2_BUF_UI // DNA2_BUF_UE + databuf.dna = connected.occupant.dna.Clone() + if(ishuman(connected.occupant)) + databuf.dna.real_name=connected.occupant.name + databuf.name = "Unique Identifier" + buffers[bufferId] = databuf + if("saveUIAndUE") + if(connected.occupant && connected.occupant.dna) + var/datum/dna2/record/databuf = new + databuf.types = DNA2_BUF_UI|DNA2_BUF_UE + databuf.dna = connected.occupant.dna.Clone() + if(ishuman(connected.occupant)) + databuf.dna.real_name=connected.occupant.dna.real_name + databuf.name = "Unique Identifier + Unique Enzymes" + buffers[bufferId] = databuf + if("saveSE") + if(connected.occupant && connected.occupant.dna) + var/datum/dna2/record/databuf = new + databuf.types = DNA2_BUF_SE + databuf.dna = connected.occupant.dna.Clone() + if(ishuman(connected.occupant)) + databuf.dna.real_name = connected.occupant.dna.real_name + databuf.name = "Structural Enzymes" + buffers[bufferId] = databuf + if("clear") + buffers[bufferId] = new /datum/dna2/record() + if("changeLabel") + tgui_modal_input(src, "changeBufferLabel", "Please enter the new buffer label:", null, list("id" = bufferId), buffer.name, TGUI_MODAL_INPUT_MAX_LENGTH_NAME) + if("transfer") + if(!connected.occupant || (NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !connected.occupant.dna) + return + + irradiating = 2 + var/lock_state = connected.locked + connected.locked = TRUE //lock it + + SStgui.update_uis(src) + sleep(2 SECONDS) + + irradiating = 0 + connected.locked = lock_state + + var/radiation = (rand(20,50) / connected.damage_coeff) + connected.occupant.apply_effect(radiation, IRRADIATE, 0) + + if(connected.radiation_check()) + return + + var/datum/dna2/record/buf = buffers[bufferId] + + if((buf.types & DNA2_BUF_UI)) + if((buf.types & DNA2_BUF_UE)) + connected.occupant.real_name = buf.dna.real_name + connected.occupant.name = buf.dna.real_name + connected.occupant.UpdateAppearance(buf.dna.UI.Copy()) + else if(buf.types & DNA2_BUF_SE) + connected.occupant.dna.SE = buf.dna.SE.Copy() + connected.occupant.dna.UpdateSE() + domutcheck(connected.occupant, connected) + if("createInjector") + if(!injector_ready) + return + if(text2num(params["block"]) > 0) + var/list/choices = all_dna_blocks((buffer.types & DNA2_BUF_SE) ? buffer.dna.SE : buffer.dna.UI) + tgui_modal_choice(src, "createInjectorBlock", "Please select the block to create an injector from:", null, list("id" = bufferId), null, choices) + else + create_injector(bufferId, TRUE) + if("loadDisk") + if(isnull(disk) || disk.read_only) + return + buffers[bufferId] = disk.buf.copy() + if("saveDisk") + if(isnull(disk) || disk.read_only) + return + var/datum/dna2/record/buf = buffers[bufferId] + disk.buf = buf.copy() + disk.name = "data disk - '[buf.dna.real_name]'" + if("wipeDisk") + if(isnull(disk) || disk.read_only) + return + disk.buf = null + if("ejectDisk") + if(!disk) + return + disk.forceMove(get_turf(src)) + disk = null + +/** + * Creates a blank injector with the name of the buffer at the given buffer_id + * + * Arguments: + * * buffer_id - The ID of the buffer + * * copy_buffer - Whether the injector should copy the buffer contents + */ +/obj/machinery/computer/scan_consolenew/proc/create_injector(buffer_id, copy_buffer = FALSE) + if(buffer_id < 1 || buffer_id > length(buffers)) + return + + // Cooldown + injector_ready = FALSE + addtimer(CALLBACK(src, .proc/injector_cooldown_finish), 30 SECONDS) + + // Create it + var/datum/dna2/record/buf = buffers[buffer_id] + var/obj/item/dnainjector/I = new() + I.forceMove(loc) + I.name += " ([buf.name])" + if(copy_buffer) + I.buf = buf.copy() + if(connected) + I.damage_coeff = connected.damage_coeff + return I + +/** + * Called when the injector creation cooldown finishes + */ +/obj/machinery/computer/scan_consolenew/proc/injector_cooldown_finish() + injector_ready = TRUE + +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/computer/scan_consolenew/proc/tgui_act_modal(action, params) + . = TRUE + var/id = params["id"] // The modal's ID + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("createInjectorBlock") + var/buffer_id = text2num(arguments["id"]) + if(buffer_id < 1 || buffer_id > length(buffers)) + return + var/datum/dna2/record/buf = buffers[buffer_id] + var/obj/item/dnainjector/I = create_injector(buffer_id) + setInjectorBlock(I, answer, buf.copy()) + if("changeBufferLabel") + var/buffer_id = text2num(arguments["id"]) + if(buffer_id < 1 || buffer_id > length(buffers)) + return + var/datum/dna2/record/buf = buffers[buffer_id] + buf.name = answer + buffers[buffer_id] = buf + else + return FALSE + else + return FALSE + + +#undef PAGE_UI +#undef PAGE_SE +#undef PAGE_BUFFER +#undef PAGE_REJUVENATORS /////////////////////////// DNA MACHINES diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm index 8b8a86009b9..a887959db48 100644 --- a/code/game/dna/genes/disabilities.dm +++ b/code/game/dna/genes/disabilities.dm @@ -249,14 +249,13 @@ block = GLOB.wingdingsblock /datum/dna/gene/disability/wingdings/OnSay(mob/M, message) - var/list/chars = string2charlist(message) var/garbled_message = "" - for(var/C in chars) - if(C in GLOB.alphabet_uppercase) + for(var/i in 1 to length(message)) + if(message[i] in GLOB.alphabet_uppercase) garbled_message += pick(GLOB.alphabet_uppercase) - else if(C in GLOB.alphabet) + else if(message[i] in GLOB.alphabet) garbled_message += pick(GLOB.alphabet) else - garbled_message += C + garbled_message += message[i] message = garbled_message return message diff --git a/code/game/dna/genes/goon_powers.dm b/code/game/dna/genes/goon_powers.dm index 87bbdf97c5c..77eb58e84b7 100644 --- a/code/game/dna/genes/goon_powers.dm +++ b/code/game/dna/genes/goon_powers.dm @@ -123,13 +123,13 @@ instability = GENE_INSTABILITY_MODERATE mutation = CRYO - spelltype = /obj/effect/proc_holder/spell/targeted/cryokinesis + spelltype = /obj/effect/proc_holder/spell/targeted/click/cryokinesis /datum/dna/gene/basic/grant_spell/cryo/New() ..() block = GLOB.cryoblock -/obj/effect/proc_holder/spell/targeted/cryokinesis +/obj/effect/proc_holder/spell/targeted/click/cryokinesis name = "Cryokinesis" desc = "Drops the bodytemperature of another person." panel = "Abilities" @@ -137,45 +137,44 @@ charge_type = "recharge" charge_max = 1200 - clothes_req = 0 - stat_allowed = 0 + clothes_req = FALSE + stat_allowed = FALSE + + click_radius = 0 + auto_target_single = FALSE // Give the clueless geneticists a way out and to have them not target themselves + selection_activated_message = "Your mind grow cold. Click on a target to cast the spell." + selection_deactivated_message = "Your mind returns to normal." + allowed_type = /mob/living/carbon invocation_type = "none" range = 7 selection_type = "range" - include_user = 1 + include_user = TRUE var/list/compatible_mobs = list(/mob/living/carbon/human) action_icon_state = "genetic_cryo" -/obj/effect/proc_holder/spell/targeted/cryokinesis/cast(list/targets, mob/user = usr) - if(!targets.len) - to_chat(user, "No target found in range.") - return +/obj/effect/proc_holder/spell/targeted/click/cryokinesis/cast(list/targets, mob/user = usr) var/mob/living/carbon/C = targets[1] - if(!iscarbon(C)) - to_chat(user, "This will only work on normal organic beings.") - return - if(COLDRES in C.mutations) C.visible_message("A cloud of fine ice crystals engulfs [C.name], but disappears almost instantly!") return - var/handle_suit = 0 + var/handle_suit = FALSE if(ishuman(C)) var/mob/living/carbon/human/H = C if(istype(H.head, /obj/item/clothing/head/helmet/space)) if(istype(H.wear_suit, /obj/item/clothing/suit/space)) - handle_suit = 1 + handle_suit = TRUE if(H.internal) H.visible_message("[user] sprays a cloud of fine ice crystals, engulfing [H]!", "[user] sprays a cloud of fine ice crystals over your [H.head]'s visor.") - add_attack_logs(user, C, "Cryokinesis") else H.visible_message("[user] sprays a cloud of fine ice crystals engulfing, [H]!", "[user] sprays a cloud of fine ice crystals cover your [H.head]'s visor and make it into your air vents!.") - add_attack_logs(user, C, "Cryokinesis") + H.bodytemperature = max(0, H.bodytemperature - 100) + add_attack_logs(user, C, "Cryokinesis") if(!handle_suit) C.bodytemperature = max(0, C.bodytemperature - 200) C.ExtinguishMob() @@ -454,7 +453,7 @@ name = "Polymorphism" desc = "Enables the subject to reconfigure their appearance to mimic that of others." - spelltype =/obj/effect/proc_holder/spell/targeted/polymorph + spelltype =/obj/effect/proc_holder/spell/targeted/click/polymorph //cooldown = 1800 activation_messages = list("You don't feel entirely like yourself somehow.") deactivation_messages = list("You feel secure in your identity.") @@ -465,34 +464,36 @@ ..() block = GLOB.polymorphblock -/obj/effect/proc_holder/spell/targeted/polymorph +/obj/effect/proc_holder/spell/targeted/click/polymorph name = "Polymorph" desc = "Mimic the appearance of others!" panel = "Abilities" charge_max = 1800 - clothes_req = 0 - human_req = 1 - stat_allowed = 0 + clothes_req = FALSE + stat_allowed = FALSE + + click_radius = -1 // Precision required + auto_target_single = FALSE // Safety to not turn into monkey (420) + selection_activated_message = "You body becomes unstable. Click on a target to cast transform into them." + selection_deactivated_message = "Your body calms down again." + allowed_type = /mob/living/carbon/human + invocation_type = "none" range = 1 selection_type = "range" action_icon_state = "genetic_poly" -/obj/effect/proc_holder/spell/targeted/polymorph/cast(list/targets, mob/user = usr) - var/mob/living/M = targets[1] - if(!ishuman(M)) - to_chat(usr, "You can only change your appearance to that of another human.") - return +/obj/effect/proc_holder/spell/targeted/click/polymorph/cast(list/targets, mob/user = usr) + var/mob/living/carbon/human/target = targets[1] user.visible_message("[user]'s body shifts and contorts.") spawn(10) - if(M && user) + if(target && user) playsound(user.loc, 'sound/goonstation/effects/gib.ogg', 50, 1) var/mob/living/carbon/human/H = user - var/mob/living/carbon/human/target = M H.UpdateAppearance(target.dna.UI) H.real_name = target.real_name H.name = target.name diff --git a/code/game/dna/genes/vg_powers.dm b/code/game/dna/genes/vg_powers.dm index 9092448e398..e08d35b681d 100644 --- a/code/game/dna/genes/vg_powers.dm +++ b/code/game/dna/genes/vg_powers.dm @@ -214,25 +214,21 @@ /obj/effect/proc_holder/spell/targeted/remotetalk/choose_targets(mob/user = usr) var/list/targets = new /list() - var/list/validtargets = new /list() - var/turf/T = get_turf(user) - for(var/mob/living/M in range(14, T)) - if(M && M.mind) - if(M == user) - continue - validtargets += M + var/list/validtargets = user.get_telepathic_targets() - if(!validtargets.len) + if(!length(validtargets)) to_chat(user, "There are no valid targets!") start_recharge() return - targets += input("Choose the target to talk to.", "Targeting") as null|mob in validtargets + var/target_name = input("Choose the target to talk to.", "Targeting") as null|anything in validtargets - if(!targets.len || !targets[1]) //doesn't waste the spell + var/mob/living/target + if(!target_name || !(target = validtargets[target_name])) revert_cast(user) return + targets += target perform(targets, user = user) /obj/effect/proc_holder/spell/targeted/remotetalk/cast(list/targets, mob/user = usr) @@ -249,7 +245,7 @@ target.show_message("You hear [user.real_name]'s voice: [say]") else target.show_message("You hear a voice that seems to echo around the room: [say]") - user.show_message("You project your mind into [target.name]: [say]") + user.show_message("You project your mind into [(target in user.get_visible_mobs()) ? target.name : "the unknown entity"]: [say]") for(var/mob/dead/observer/G in GLOB.player_list) G.show_message("Telepathic message from [user] ([ghost_follow_link(user, ghost=G)]) to [target] ([ghost_follow_link(target, ghost=G)]): [say]") @@ -266,26 +262,22 @@ var/list/available_targets = list() /obj/effect/proc_holder/spell/targeted/mindscan/choose_targets(mob/user = usr) - var/list/targets = new /list() - var/list/validtargets = new /list() - var/turf/T = get_turf(user) - for(var/mob/living/M in range(14, T)) - if(M && M.mind) - if(M == user) - continue - validtargets += M + var/list/targets = list() + var/list/validtargets = user.get_telepathic_targets() - if(!validtargets.len) + if(!length(validtargets)) to_chat(user, "There are no valid targets!") start_recharge() return - targets += input("Choose the target to listen to.", "Targeting") as null|mob in validtargets + var/target_name = input("Choose the target to listen to.", "Targeting") as null|anything in validtargets - if(!targets.len || !targets[1]) //doesn't waste the spell + var/mob/living/target + if(!target_name || !(target = validtargets[target_name])) revert_cast(user) return + targets += target perform(targets, user = user) /obj/effect/proc_holder/spell/targeted/mindscan/cast(list/targets, mob/user = usr) @@ -295,7 +287,7 @@ var/message = "You feel your mind expand briefly... (Click to send a message.)" if(REMOTE_TALK in target.mutations) message = "You feel [user.real_name] request a response from you... (Click here to project mind.)" - user.show_message("You offer your mind to [target.name].") + user.show_message("You offer your mind to [(target in user.get_visible_mobs()) ? target.name : "the unknown entity"].") target.show_message("[message]") available_targets += target addtimer(CALLBACK(src, .proc/removeAvailability, target), 100) diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm index 9c7fb99fe81..93b84dbb54d 100644 --- a/code/game/gamemodes/blob/blob_report.dm +++ b/code/game/gamemodes/blob/blob_report.dm @@ -40,7 +40,7 @@ aiPlayer.set_zeroth_law(law) to_chat(aiPlayer, "Laws Updated: [law]") - print_command_report(intercepttext, interceptname) + print_command_report(intercepttext, interceptname, FALSE) GLOB.event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg', from = "[command_name()] Update") /datum/station_state diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm index 46b1972e565..34c717f11a8 100644 --- a/code/game/gamemodes/blob/blobs/blob_mobs.dm +++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm @@ -7,6 +7,7 @@ /mob/living/simple_animal/hostile/blob icon = 'icons/mob/blob.dmi' pass_flags = PASSBLOB + status_flags = NONE //No throwing blobspores into deep space to despawn, or throwing blobbernaughts, which are bigger than you. faction = list(ROLE_BLOB) bubble_icon = "blob" atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) @@ -49,6 +50,7 @@ environment_smash = ENVIRONMENT_SMASH_STRUCTURES attacktext = "hits" attack_sound = 'sound/weapons/genhit1.ogg' + flying = TRUE speak_emote = list("pulses") var/obj/structure/blob/factory/factory = null var/list/human_overlays = list() diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm index be794b9092b..dc8d0f5aba2 100644 --- a/code/game/gamemodes/blob/blobs/core.dm +++ b/code/game/gamemodes/blob/blobs/core.dm @@ -17,12 +17,12 @@ START_PROCESSING(SSobj, src) GLOB.poi_list |= src adjustcolors(color) //so it atleast appears - if(!overmind) - create_overmind(new_overmind) - if(overmind) - adjustcolors(overmind.blob_reagent_datum.color) if(offspring) is_offspring = 1 + if(overmind) + adjustcolors(overmind.blob_reagent_datum.color) + if(!overmind) + create_overmind(new_overmind) point_rate = new_rate ..(loc, h) @@ -104,10 +104,11 @@ var/mob/C = null var/list/candidates = list() if(!new_overmind) + // sendit if(is_offspring) - candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1) + candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob offspring?", ROLE_BLOB, TRUE, source = src) else - candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1) + candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob?", ROLE_BLOB, TRUE, source = src) if(length(candidates)) C = pick(candidates) diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm index 6f2eed39499..350da7c7593 100644 --- a/code/game/gamemodes/blob/powers.dm +++ b/code/game/gamemodes/blob/powers.dm @@ -237,7 +237,7 @@ blobber.AIStatus = AI_OFF blobber.LoseTarget() spawn() - var/list/candidates = pollCandidates("Do you want to play as a blobbernaut?", ROLE_BLOB, 1, 100) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blobbernaut?", ROLE_BLOB, TRUE, 10 SECONDS, source = blobber) if(candidates.len) var/mob/C = pick(candidates) if(C) @@ -389,7 +389,7 @@ return split_used = TRUE - new /obj/structure/blob/core/ (get_turf(N), 200, null, blob_core.point_rate, "offspring") + new /obj/structure/blob/core/ (get_turf(N), 200, null, blob_core.point_rate, offspring = TRUE) qdel(N) if(SSticker && SSticker.mode.name == "blob") diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm index 9c33ffaaca5..b3b4000bbf1 100644 --- a/code/game/gamemodes/changeling/evolution_menu.dm +++ b/code/game/gamemodes/changeling/evolution_menu.dm @@ -370,16 +370,11 @@ GLOBAL_LIST_EMPTY(sting_paths) mind.changeling.purchasedpowers += path path.on_purchase(src) else //for respec - var/datum/action/changeling/hivemind_upload/S1 = new + var/datum/action/changeling/hivemind_pick/S1 = new if(!mind.changeling.has_sting(S1)) mind.changeling.purchasedpowers+=S1 S1.Grant(src) - var/datum/action/changeling/hivemind_download/S2 = new - if(!mind.changeling.has_sting(S2)) - mind.changeling.purchasedpowers+=S2 - S2.Grant(src) - var/mob/living/carbon/C = src //only carbons have dna now, so we have to typecaste mind.changeling.absorbed_dna |= C.dna.Clone() mind.changeling.trim_dna() diff --git a/code/game/gamemodes/changeling/powers/hivemind.dm b/code/game/gamemodes/changeling/powers/hivemind.dm index 98336608da4..18a5477fd3f 100644 --- a/code/game/gamemodes/changeling/powers/hivemind.dm +++ b/code/game/gamemodes/changeling/powers/hivemind.dm @@ -12,27 +12,36 @@ var/datum/changeling/changeling=user.mind.changeling changeling.changeling_speak = 1 to_chat(user, "Use say \":g message\" to communicate with the other changelings.") - var/datum/action/changeling/hivemind_upload/S1 = new + var/datum/action/changeling/hivemind_pick/S1 = new if(!changeling.has_sting(S1)) changeling.purchasedpowers+=S1 S1.Grant(user) - var/datum/action/changeling/hivemind_download/S2 = new - if(!changeling.has_sting(S2)) - S2.Grant(user) - changeling.purchasedpowers+=S2 return // HIVE MIND UPLOAD/DOWNLOAD DNA GLOBAL_LIST_EMPTY(hivemind_bank) -/datum/action/changeling/hivemind_upload +/datum/action/changeling/hivemind_pick name = "Hive Channel DNA" - desc = "Allows us to channel DNA in the airwaves to allow other changelings to absorb it. Costs 10 chemicals." - button_icon_state = "hivemind_channel" + desc = "Allows us to upload or absorb DNA in the airwaves. Does not count towards absorb objectives. Costs 10 chemicals." + button_icon_state = "hive_absorb" chemical_cost = 10 dna_cost = -1 -/datum/action/changeling/hivemind_upload/sting_action(var/mob/user) +/datum/action/changeling/hivemind_pick/sting_action(mob/user) + var/datum/changeling/changeling = user.mind.changeling + var/channel_pick = alert("Upload or Absorb DNA?", "Channel Select", "Upload", "Absorb") + + if(channel_pick == "Upload") + dna_upload(user) + if(channel_pick == "Absorb") + if(changeling.using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it. + to_chat(user, "We have reached our capacity to store genetic information! We must transform before absorbing more.") + return + else + dna_absorb(user) + +/datum/action/changeling/proc/dna_upload(mob/user) var/datum/changeling/changeling = user.mind.changeling var/list/names = list() for(var/datum/dna/DNA in (changeling.absorbed_dna+changeling.protected_dna)) @@ -56,23 +65,7 @@ GLOBAL_LIST_EMPTY(hivemind_bank) feedback_add_details("changeling_powers","HU") return 1 -/datum/action/changeling/hivemind_download - name = "Hive Absorb DNA" - desc = "Allows us to absorb DNA that has been channeled to the airwaves. Does not count towards absorb objectives. Costs 10 chemicals." - button_icon_state = "hive_absorb" - chemical_cost = 10 - dna_cost = -1 - -/datum/action/changeling/hivemind_download/can_sting(var/mob/living/carbon/user) - if(!..()) - return - var/datum/changeling/changeling = user.mind.changeling - if(changeling.using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it. - to_chat(user, "We have reached our capacity to store genetic information! We must transform before absorbing more.") - return - return 1 - -/datum/action/changeling/hivemind_download/sting_action(var/mob/user) +/datum/action/changeling/proc/dna_absorb(mob/user) var/datum/changeling/changeling = user.mind.changeling var/list/names = list() for(var/datum/dna/DNA in GLOB.hivemind_bank) diff --git a/code/game/gamemodes/changeling/powers/mutations.dm b/code/game/gamemodes/changeling/powers/mutations.dm index d12b970d884..be88e0bb6e0 100644 --- a/code/game/gamemodes/changeling/powers/mutations.dm +++ b/code/game/gamemodes/changeling/powers/mutations.dm @@ -221,6 +221,7 @@ projectile_type = /obj/item/projectile/tentacle caliber = "tentacle" icon_state = "tentacle_end" + muzzle_flash_effect = null var/obj/item/gun/magic/tentacle/gun //the item that shot it /obj/item/ammo_casing/magic/tentacle/New(obj/item/gun/magic/tentacle/tentacle_gun) diff --git a/code/game/gamemodes/changeling/powers/shriek.dm b/code/game/gamemodes/changeling/powers/shriek.dm index 867328c40b3..06332f61670 100644 --- a/code/game/gamemodes/changeling/powers/shriek.dm +++ b/code/game/gamemodes/changeling/powers/shriek.dm @@ -11,6 +11,10 @@ /datum/action/changeling/resonant_shriek/sting_action(var/mob/user) for(var/mob/living/M in get_mobs_in_view(4, user)) if(iscarbon(M)) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.check_ear_prot() >= HEARING_PROTECTION_TOTAL) + continue if(!M.mind || !M.mind.changeling) M.MinimumDeafTicks(30) M.AdjustConfused(20) diff --git a/code/game/gamemodes/changeling/powers/swap_form.dm b/code/game/gamemodes/changeling/powers/swap_form.dm index 0e02df12d88..97192c86a0f 100644 --- a/code/game/gamemodes/changeling/powers/swap_form.dm +++ b/code/game/gamemodes/changeling/powers/swap_form.dm @@ -18,12 +18,15 @@ if((NOCLONE || SKELETON || HUSK) in target.mutations) to_chat(user, "DNA of [target] is ruined beyond usability!") return - if(!istype(target) || issmall(target) || (NO_DNA in target.dna.species.species_traits)) + if(!istype(target) || !target.mind || issmall(target) || (NO_DNA in target.dna.species.species_traits)) to_chat(user, "[target] is not compatible with this ability.") return if(target.mind.changeling) to_chat(user, "We are unable to swap forms with another changeling!") return + if(target.has_brain_worms() || user.has_brain_worms()) + to_chat(user, "A foreign presence repels us from this body!") + return return 1 /datum/action/changeling/swap_form/sting_action(var/mob/living/carbon/user) @@ -63,6 +66,8 @@ target.add_language("Changeling") user.remove_language("Changeling") user.regenerate_icons() + if(target.stat == DEAD && target.suiciding) //If Target committed suicide, unset flag for User + target.suiciding = 0 for(var/power in lingpowers) var/datum/action/changeling/S = power diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm index fbfb75b8b6a..9bc9fc7b511 100644 --- a/code/game/gamemodes/changeling/powers/tiny_prick.dm +++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm @@ -112,7 +112,7 @@ feedback_add_details("changeling_powers","TS") return TRUE -datum/action/changeling/sting/extract_dna +/datum/action/changeling/sting/extract_dna name = "Extract DNA Sting" desc = "We stealthily sting a target and extract their DNA. Costs 25 chemicals." helptext = "Will give you the DNA of your target, allowing you to transform into them." @@ -132,7 +132,7 @@ datum/action/changeling/sting/extract_dna feedback_add_details("changeling_powers","ED") return 1 -datum/action/changeling/sting/mute +/datum/action/changeling/sting/mute name = "Mute Sting" desc = "We silently sting a human, completely silencing them for a short time. Costs 20 chemicals." helptext = "Does not provide a warning to the victim that they have been stung, until they try to speak and cannot." @@ -147,7 +147,7 @@ datum/action/changeling/sting/mute feedback_add_details("changeling_powers","MS") return 1 -datum/action/changeling/sting/blind +/datum/action/changeling/sting/blind name = "Blind Sting" desc = "We temporarily blind our victim. Costs 25 chemicals." helptext = "This sting completely blinds a target for a short time, and leaves them with blurred vision for a long time." @@ -165,7 +165,7 @@ datum/action/changeling/sting/blind feedback_add_details("changeling_powers","BS") return 1 -datum/action/changeling/sting/LSD +/datum/action/changeling/sting/LSD name = "Hallucination Sting" desc = "We cause mass terror to our victim. Costs 10 chemicals." helptext = "We evolve the ability to sting a target with a powerful hallucinogenic chemical. The target does not notice they have been stung, and the effect occurs after 30 to 60 seconds." @@ -182,7 +182,7 @@ datum/action/changeling/sting/LSD feedback_add_details("changeling_powers","HS") return 1 -datum/action/changeling/sting/cryo //Enable when mob cooling is fixed so that frostoil actually makes you cold, instead of mostly just hungry. +/datum/action/changeling/sting/cryo //Enable when mob cooling is fixed so that frostoil actually makes you cold, instead of mostly just hungry. name = "Cryogenic Sting" desc = "We silently sting our victim with a cocktail of chemicals that freezes them from the inside. Costs 15 chemicals." helptext = "Does not provide a warning to the victim, though they will likely realize they are suddenly freezing." diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index bf5fbb70731..433fc5b526a 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -20,8 +20,8 @@ GLOBAL_LIST_EMPTY(all_cults) var/mob/living/carbon/human/H = mind.current if(ismindshielded(H)) //mindshield protects against conversions unless removed return FALSE -// if(mind.offstation_role) cant convert offstation roles such as ghost spawns -// return FALSE Commented out until we can figure out why offstation_role is getting set to TRUE on normal crew + if(mind.offstation_role) + return FALSE if(issilicon(mind.current)) return FALSE //can't convert machines, that's ratvar's thing if(isguardian(mind.current)) @@ -231,6 +231,8 @@ GLOBAL_LIST_EMPTY(all_cults) /datum/game_mode/cult/proc/get_unconvertables() var/list/ucs = list() for(var/mob/living/carbon/human/player in GLOB.player_list) + if(player.mind && player.mind.offstation_role) + continue if(!is_convertable_to_cult(player.mind)) ucs += player.mind return ucs diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 388b16d71af..a68e4c1e9ca 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -140,6 +140,7 @@ body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS allowed = list(/obj/item/tome,/obj/item/melee/cultblade) var/current_charges = 3 + var/shield_state = "shield-cult" hoodtype = /obj/item/clothing/head/hooded/cult_hoodie /obj/item/clothing/head/hooded/cult_hoodie @@ -164,33 +165,49 @@ if(current_charges) owner.visible_message("\The [attack_text] is deflected in a burst of blood-red sparks!") current_charges-- + playsound(loc, "sparks", 100, 1) new /obj/effect/temp_visual/cult/sparks(get_turf(owner)) if(!current_charges) owner.visible_message("The runed shield around [owner] suddenly disappears!") + shield_state = "broken" owner.update_inv_wear_suit() return 1 return 0 -/obj/item/clothing/suit/hooded/cultrobes/berserker +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/special_overlays() + return mutable_appearance('icons/effects/cult_effects.dmi', shield_state, MOB_LAYER + 0.01) + +/obj/item/clothing/suit/hooded/cultrobes/flagellant_robe name = "flagellant's robes" desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." - icon_state = "hardsuit-berserker" - item_state = "hardsuit-berserker" + icon_state = "flagellantrobe" + item_state = "flagellantrobe" flags_inv = HIDEJUMPSUIT allowed = list(/obj/item/tome,/obj/item/melee/cultblade) body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS armor = list("melee" = -45, "bullet" = -45, "laser" = -45,"energy" = -45, "bomb" = -45, "bio" = -45, "rad" = -45, "fire" = 0, "acid" = 0) slowdown = -1 - hoodtype = /obj/item/clothing/head/hooded/berserkerhood + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Drask" = 'icons/mob/species/drask/suit.dmi', + "Grey" = 'icons/mob/species/grey/suit.dmi' + ) + hoodtype = /obj/item/clothing/head/hooded/flagellant_hood -/obj/item/clothing/head/hooded/berserkerhood +/obj/item/clothing/head/hooded/flagellant_hood name = "flagellant's robes" desc = "Blood-soaked garb infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." - icon_state = "culthood" + icon_state = "flagellanthood" + item_state = "flagellanthood" flags_inv = HIDEFACE flags_cover = HEADCOVERSEYES armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Drask" = 'icons/mob/species/drask/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) /obj/item/whetstone/cult name = "eldritch whetstone" diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index 0e96dd92062..17ae54591aa 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -120,7 +120,7 @@ selection_prompt = "You study the schematics etched on the forge..." selection_title = "Forge" creation_message = "You work the forge as dark knowledge guides your hands, creating %ITEM%!" - choosable_items = list("Shielded Robe" = /obj/item/clothing/suit/hooded/cultrobes/cult_shield, "Flagellant's Robe" = /obj/item/clothing/suit/hooded/cultrobes/berserker, \ + choosable_items = list("Shielded Robe" = /obj/item/clothing/suit/hooded/cultrobes/cult_shield, "Flagellant's Robe" = /obj/item/clothing/suit/hooded/cultrobes/flagellant_robe, \ "Cultist Hardsuit" = /obj/item/storage/box/cult) /obj/structure/cult/functional/forge/New() @@ -270,14 +270,10 @@ GLOBAL_LIST_INIT(blacklisted_pylon_turfs, typecacheof(list( /obj/effect/gateway/singularity_pull() return -/obj/effect/gateway/Bumped(mob/M as mob|obj) - spawn(0) - return +/obj/effect/gateway/Bumped(atom/movable/AM) return -/obj/effect/gateway/Crossed(AM as mob|obj, oldloc) - spawn(0) - return +/obj/effect/gateway/Crossed(atom/movable/AM, oldloc) return diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm index 60d2a7ff73b..af0dfb5e6e3 100644 --- a/code/game/gamemodes/cult/talisman.dm +++ b/code/game/gamemodes/cult/talisman.dm @@ -1,10 +1,10 @@ /obj/item/paper/talisman - icon = 'icons/obj/paper.dmi' + icon = 'icons/obj/bureaucracy.dmi' icon_state = "paper_talisman" var/cultist_name = "talisman" var/cultist_desc = "A basic talisman. It serves no purpose." var/invocation = "Naise meam!" - info = "


    " + info = "


    " var/uses = 1 var/health_cost = 0 //The amount of health taken from the user when invoking the talisman diff --git a/code/game/gamemodes/devil/contracts/friend.dm b/code/game/gamemodes/devil/contracts/friend.dm index 7760ee10ee7..8838dc8a371 100644 --- a/code/game/gamemodes/devil/contracts/friend.dm +++ b/code/game/gamemodes/devil/contracts/friend.dm @@ -17,7 +17,8 @@ /obj/effect/mob_spawn/human/demonic_friend/Initialize(mapload, datum/mind/owner_mind, obj/effect/proc_holder/spell/targeted/summon_friend/summoning_spell) . = ..() owner = owner_mind - flavour_text = "You have been given a reprieve from your eternity of torment, to be [owner.name]'s friend for [owner.p_their()] short mortal coil. Be aware that if you do not live up to [owner.name]'s expectations, [owner.p_they()] can send you back to hell with a single thought. [owner.name]'s death will also return you to hell." + description = "Be someone's loyal friend/slave. If they die, you die as well." //best I could think of in the moment, not sure how this role plays in practise, have never seen it. + flavour_text = "You have been given a reprieve from your eternity of torment, to be [owner.name]'s friend for [owner.p_their()] short mortal coil. Be aware that if you do not live up to [owner.name]'s expectations, [owner.p_they()] can send you back to hell with a single thought. [owner.name]'s death will also return you to hell." var/area/A = get_area(src) if(!mapload && A) notify_ghosts("\A friendship shell has been completed in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = TRUE) diff --git a/code/game/gamemodes/devil/devilinfo.dm b/code/game/gamemodes/devil/devilinfo.dm index ecbeb54f7f7..22a5f4a6cde 100644 --- a/code/game/gamemodes/devil/devilinfo.dm +++ b/code/game/gamemodes/devil/devilinfo.dm @@ -92,7 +92,7 @@ GLOBAL_LIST_INIT(lawlorify, list ( var/form = BASIC_DEVIL var/exists = 0 var/static/list/dont_remove_spells = list( - /obj/effect/proc_holder/spell/targeted/summon_contract, + /obj/effect/proc_holder/spell/targeted/click/summon_contract, /obj/effect/proc_holder/spell/targeted/conjure_item/violin, /obj/effect/proc_holder/spell/targeted/summon_dancefloor) var/ascendable = FALSE @@ -326,12 +326,12 @@ GLOBAL_LIST_INIT(lawlorify, list ( owner.RemoveSpell(S) /datum/devilinfo/proc/give_summon_contract() - owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_contract(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/summon_contract(null)) /datum/devilinfo/proc/give_base_spells(give_summon_contract = 0) remove_spells() - owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/fireball/hellish(null)) owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork(null)) if(give_summon_contract) give_summon_contract() @@ -343,13 +343,13 @@ GLOBAL_LIST_INIT(lawlorify, list ( /datum/devilinfo/proc/give_lizard_spells() remove_spells() owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/fireball/hellish(null)) owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null)) /datum/devilinfo/proc/give_true_spells() remove_spells() owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/greater(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/fireball/hellish(null)) owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/infernal_jaunt(null)) owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/sintouch(null)) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 2d71df8c979..8757fa00d4f 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -291,7 +291,8 @@ /////////////////////////////////// /datum/game_mode/proc/get_living_heads() . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/player = thing var/list/real_command_positions = GLOB.command_positions.Copy() - "Nanotrasen Representative" if(player.stat != DEAD && player.mind && (player.mind.assigned_role in real_command_positions)) . |= player.mind @@ -312,7 +313,8 @@ ////////////////////////////////////////////// /datum/game_mode/proc/get_living_sec() . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/player = thing if(player.stat != DEAD && player.mind && (player.mind.assigned_role in GLOB.security_positions)) . |= player.mind @@ -321,7 +323,8 @@ //////////////////////////////////////// /datum/game_mode/proc/get_all_sec() . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/player = thing if(player.mind && (player.mind.assigned_role in GLOB.security_positions)) . |= player.mind @@ -334,7 +337,7 @@ ////////////////////////// //Reports player logouts// ////////////////////////// -proc/display_roundstart_logout_report() +/proc/display_roundstart_logout_report() var/msg = "Roundstart logout report\n\n" for(var/mob/living/L in GLOB.mob_list) @@ -421,9 +424,9 @@ proc/display_roundstart_logout_report() return nukecode /datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type) - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [role_type]?", role_type, FALSE, 10 SECONDS) var/mob/dead/observer/theghost = null - if(candidates.len) + if(length(candidates)) theghost = pick(candidates) to_chat(M, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.") @@ -504,7 +507,7 @@ proc/display_roundstart_logout_report() message_text += G.get_report() message_text += "
    " - print_command_report(message_text, "[command_name()] Orders") + print_command_report(message_text, "[command_name()] Orders", FALSE) /datum/game_mode/proc/declare_station_goal_completion() for(var/V in station_goals) diff --git a/code/game/gamemodes/heist/heist.dm b/code/game/gamemodes/heist/heist.dm index 142ae33c078..f95702e7f28 100644 --- a/code/game/gamemodes/heist/heist.dm +++ b/code/game/gamemodes/heist/heist.dm @@ -250,7 +250,7 @@ GLOBAL_LIST_EMPTY(cortical_stacks) //Stacks for 'leave nobody behind' objective. ..() -datum/game_mode/proc/auto_declare_completion_heist() +/datum/game_mode/proc/auto_declare_completion_heist() if(raiders.len) var/check_return = 0 if(GAMEMODE_IS_HEIST) diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index a4b8c594b89..9ed2adf3332 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -83,6 +83,36 @@ else add_ranged_ability(user, enable_text) +/datum/action/innate/ai/choose_modules + name = "Choose Modules" + desc = "Spend your processing time to gain a variety of different abilities." + button_icon_state = "choose_module" + auto_use_uses = FALSE // This is an infinite ability. + +/datum/action/innate/ai/choose_modules/Grant(mob/living/L) + . = ..() + owner_AI.malf_picker = new /datum/module_picker + +/datum/action/innate/ai/choose_modules/Trigger() + . = ..() + owner_AI.malf_picker.use(owner_AI) + +/datum/action/innate/ai/return_to_core + name = "Return to Main Core" + desc = "Leave the APC you are shunted to, and return to your core." + icon_icon = 'icons/obj/power.dmi' + button_icon_state = "apcemag" + auto_use_uses = FALSE // Here just to prevent the "You have X uses remaining" from popping up. + +/datum/action/innate/ai/return_to_core/Trigger() + . = ..() + var/obj/machinery/power/apc/apc = owner_AI.loc + if(!istype(apc)) // This shouldn't happen but here for safety. + to_chat(src, "You are already in your Main Core.") + return + apc.malfvacate() + qdel(src) + //The datum and interface for the malf unlock menu, which lets them choose actions to unlock. /datum/module_picker var/temp @@ -96,13 +126,7 @@ if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade) possible_modules += AM -/datum/module_picker/proc/remove_malf_verbs(mob/living/silicon/ai/AI) //Removes all malfunction-related abilities from the target AI. - for(var/datum/AI_Module/AM in possible_modules) - for(var/datum/action/A in AI.actions) - if(istype(A, initial(AM.power_type))) - qdel(A) - -/datum/module_picker/proc/use(user as mob) +/datum/module_picker/proc/use(mob/user) var/dat dat += {"Select use of processing time: (currently #[processing_time] left.)

    @@ -592,7 +616,7 @@ active = FALSE return var/turf/T = get_turf(owner_AI.eyeobj) - new /obj/machinery/transformer/conveyor(T) + new /obj/machinery/transformer(T, owner_AI) playsound(T, 'sound/effects/phasein.ogg', 100, 1) owner_AI.can_shunt = FALSE to_chat(owner, "You are no longer able to shunt your core to APCs.") @@ -655,7 +679,7 @@ for(var/thing in GLOB.apcs) var/obj/machinery/power/apc/apc if(prob(30 * apc.overload)) - apc.overload_lighting() + INVOKE_ASYNC(apc, /obj/machinery/power/apc.proc/overload_lighting) else apc.overload++ to_chat(owner, "Overcurrent applied to the powernet.") @@ -757,3 +781,17 @@ if(AI.eyeobj) AI.eyeobj.relay_speech = TRUE +/datum/AI_Module/large/cameracrack + module_name = "Core Camera Cracker" + mod_pick_name = "cameracrack" + description = "By shortcirucuting the camera network chip, it overheats, preventing the camera console from using your internal camera." + cost = 10 + one_purchase = TRUE + upgrade = TRUE + unlock_text = "Network chip short circuited. Internal camera disconected from network. Minimal damage to other internal components." + unlock_sound = 'sound/items/wirecutter.ogg' + +/datum/AI_Module/large/cameracrack/upgrade(mob/living/silicon/ai/AI) + if(AI.builtInCamera) + QDEL_NULL(AI.builtInCamera) + diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm index f99a00567ae..750473b1dfa 100644 --- a/code/game/gamemodes/miniantags/borer/borer.dm +++ b/code/game/gamemodes/miniantags/borer/borer.dm @@ -394,7 +394,7 @@ to_chat(src, " You are feeling far too docile to do that.") return - var content = "" + var/content = "" content += "" diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm index 8da5b4468d7..7fe146d3e47 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm @@ -19,15 +19,13 @@ mob_name = "a swarmer" death = FALSE roundstart = FALSE - flavour_text = {" - You are a swarmer, a weapon of a long dead civilization. Until further orders from your original masters are received, you must continue to consume and replicate. - Clicking on any object will try to consume it, either deconstructing it into its components, destroying it, or integrating any materials it has into you if successful. - Ctrl-Clicking on a mob will attempt to remove it from the area and place it in a safe environment for storage. - Objectives: + important_info = "Follow your objectives, do not make the station inhospitable or try and kill crew." + flavour_text = "You are a swarmer, a weapon of a long dead civilization. Until further orders from your original masters are received, you must continue to consume and replicate." + description = {" Your goal is to create more of yourself by consuming the station. Clicking on any object will try to consume it, either deconstructing it into its components, destroying it, or integrating any materials it has into you if successful. Ctrl-Clicking on a mob will attempt to remove it from the area and place it in a safe environment for storage. + Objectives: 1. Consume resources and replicate until there are no more resources left. 2. Ensure that this location is fit for invasion at a later date; do not perform actions that would render it dangerous or inhospitable. - 3. Biological resources will be harvested at a later date; do not harm them. - "} + 3. Biological resources will be harvested at a later date; do not harm them."} /obj/effect/mob_spawn/swarmer/Initialize(mapload) . = ..() diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm index fbceda6bd94..5c4b83e965c 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm @@ -7,7 +7,7 @@ var/swarmer_report = "[command_name()] High-Priority Update" swarmer_report += "

    Our long-range sensors have detected an odd signal emanating from your station's gateway. We recommend immediate investigation of your gateway, as something may have come \ through." - print_command_report(swarmer_report, "Classified [command_name()] Update") + print_command_report(swarmer_report, "Classified [command_name()] Update", FALSE) GLOB.event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg') /datum/event/spawn_swarmer/start() diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm index f647ae5a6d1..ecfa7ea3f96 100644 --- a/code/game/gamemodes/miniantags/guardian/guardian.dm +++ b/code/game/gamemodes/miniantags/guardian/guardian.dm @@ -15,7 +15,7 @@ a_intent = INTENT_HARM can_change_intents = 0 stop_automated_movement = 1 - floating = 1 + flying = TRUE attack_sound = 'sound/weapons/punch1.ogg' minbodytemp = 0 maxbodytemp = INFINITY @@ -41,9 +41,15 @@ var/tech_fluff_string = "BOOT SEQUENCE COMPLETE. ERROR MODULE LOADED. THIS SHOULDN'T HAPPEN. Submit a bug report!" var/bio_fluff_string = "Your scarabs fail to mutate. This shouldn't happen! Submit a bug report!" var/admin_fluff_string = "URK URF!"//the wheels on the bus... - var/adminseal = FALSE var/name_color = "white"//only used with protector shields for the time being +/mob/living/simple_animal/hostile/guardian/Initialize(mapload, mob/living/host) + . = ..() + if(!host) + return + summoner = host + host.grant_guardian_actions(src) + /mob/living/simple_animal/hostile/guardian/med_hud_set_health() if(summoner) var/image/holder = hud_list[HEALTH_HUD] @@ -59,19 +65,20 @@ else holder.icon_state = "hudhealthy" -/mob/living/simple_animal/hostile/guardian/Life(seconds, times_fired) //Dies if the summoner dies +/mob/living/simple_animal/hostile/guardian/Life(seconds, times_fired) ..() if(summoner) if(summoner.stat == DEAD || (!summoner.check_death_method() && summoner.health <= HEALTH_THRESHOLD_DEAD)) + summoner.remove_guardian_actions() to_chat(src, "Your summoner has died!") visible_message("[src] dies along with its user!") ghostize() qdel(src) snapback() - if(summoned && !summoner && !adminseal) + if(summoned && !summoner && !admin_spawned) to_chat(src, "You somehow lack a summoner! As a result, you dispel!") ghostize() - qdel() + qdel(src) /mob/living/simple_animal/hostile/guardian/proc/snapback() // If the summoner dies instantly, the summoner's ghost may be drawn into null space as the protector is deleted. This check should prevent that. @@ -197,7 +204,7 @@ //override set to true if message should be passed through instead of going to host communication /mob/living/simple_animal/hostile/guardian/say(message, override = FALSE) - if(adminseal || override)//if it's an admin-spawned guardian without a host it can still talk normally + if(admin_spawned || override)//if it's an admin-spawned guardian without a host it can still talk normally return ..(message) Communicate(message) @@ -206,61 +213,6 @@ to_chat(src, "You dont have another mode!") -/mob/living/proc/guardian_comm() - set name = "Communicate" - set category = "Guardian" - set desc = "Communicate telepathically with your guardian." - var/input = stripped_input(src, "Please enter a message to tell your guardian.", "Message", "") - if(!input) - return - - // Find the guardian in our host's contents. - var/mob/living/simple_animal/hostile/guardian/G = locate() in contents - if(!G) - return - - // Show the message to our guardian and to host. - to_chat(G, "[src]: [input]") - to_chat(src, "[src]: [input]") - log_say("(GUARDIAN to [key_name(G)]) [input]", src) - create_log(SAY_LOG, "HOST to GUARDIAN: [input]", G) - - // Show the message to any ghosts/dead players. - for(var/mob/M in GLOB.dead_mob_list) - if(M && M.client && M.stat == DEAD && !isnewplayer(M)) - to_chat(M, "Guardian Communication from [src] ([ghost_follow_link(src, ghost=M)]): [input]") - -/mob/living/proc/guardian_recall() - set name = "Recall Guardian" - set category = "Guardian" - set desc = "Forcibly recall your guardian." - for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.mob_list) - if(G.summoner == src) - G.Recall() - -/mob/living/proc/guardian_reset() - set name = "Reset Guardian Player (One Use)" - set category = "Guardian" - set desc = "Re-rolls which ghost will control your Guardian. One use." - - src.verbs -= /mob/living/proc/guardian_reset - for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.mob_list) - if(G.summoner == src) - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [G.real_name]?", ROLE_GUARDIAN, 0, 100) - var/mob/dead/observer/new_stand = null - if(candidates.len) - new_stand = pick(candidates) - to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.") - to_chat(src, "Your guardian has been successfully reset.") - message_admins("[key_name_admin(new_stand)] has taken control of ([key_name_admin(G)])") - G.ghostize() - G.key = new_stand.key - else - to_chat(src, "There were no ghosts willing to take control. Looks like you're stuck with your Guardian for now.") - spawn(3000) - verbs += /mob/living/proc/guardian_reset - - /mob/living/simple_animal/hostile/guardian/proc/ToggleLight() if(!light_on) set_light(luminosity_on) @@ -312,7 +264,7 @@ used = FALSE return to_chat(user, "[use_message]") - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, 0, 100) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, FALSE, 10 SECONDS, source = src) var/mob/dead/observer/theghost = null if(candidates.len) @@ -363,8 +315,7 @@ if("Protector") pickedtype = /mob/living/simple_animal/hostile/guardian/protector - var/mob/living/simple_animal/hostile/guardian/G = new pickedtype(user) - G.summoner = user + var/mob/living/simple_animal/hostile/guardian/G = new pickedtype(user, user) G.summoned = TRUE G.key = key to_chat(G, "You are a [mob_name] bound to serve [user.real_name].") @@ -372,9 +323,6 @@ to_chat(G, "While personally invincible, you will die if [user.real_name] does, and any damage dealt to you will have a portion passed on to them as you feed upon them to sustain yourself.") to_chat(G, "[G.playstyle_string]") G.faction = user.faction - user.verbs += /mob/living/proc/guardian_comm - user.verbs += /mob/living/proc/guardian_recall - user.verbs += /mob/living/proc/guardian_reset var/color = pick(color_list) G.name_color = color_list[color] diff --git a/code/game/gamemodes/miniantags/guardian/host_actions.dm b/code/game/gamemodes/miniantags/guardian/host_actions.dm new file mode 100644 index 00000000000..c6e48221705 --- /dev/null +++ b/code/game/gamemodes/miniantags/guardian/host_actions.dm @@ -0,0 +1,130 @@ +/** + * # Base guardian host action + * + * These are used by guardian hosts to interact with their guardians. These are not buttons that guardians themselves use. + */ +/datum/action/guardian + name = "Generic guardian host action" + icon_icon = 'icons/mob/guardian.dmi' + button_icon_state = "base" + var/mob/living/simple_animal/hostile/guardian/guardian + +/datum/action/guardian/Grant(mob/M, mob/living/simple_animal/hostile/guardian/G) + if(!G || !istype(G)) + stack_trace("/datum/action/guardian created with no guardian to link to.") + qdel(src) + guardian = G + return ..() + +/** + * # Communicate action + * + * Allows the guardian host to communicate with their guardian. + */ +/datum/action/guardian/communicate + name = "Communicate" + desc = "Communicate telepathically with your guardian." + button_icon_state = "communicate" + +/datum/action/guardian/communicate/Trigger() + var/input = stripped_input(owner, "Enter a message to tell your guardian:", "Message", "") + if(!input) + return + + // Show the message to our guardian and to host. + to_chat(guardian, "[owner]: [input]") + to_chat(owner, "[owner]: [input]") + log_say("(GUARDIAN to [key_name(guardian)]) [input]", owner) + owner.create_log(SAY_LOG, "HOST to GUARDIAN: [input]", guardian) + + // Show the message to any ghosts/dead players. + for(var/mob/M in GLOB.dead_mob_list) + if(M && M.client && M.stat == DEAD && !isnewplayer(M)) + to_chat(M, "Guardian Communication from [owner] ([ghost_follow_link(owner, ghost=M)]): [input]") + +/** + * # Recall guardian action + * + * Allows the guardian host to recall their guardian. + */ +/datum/action/guardian/recall + name = "Recall Guardian" + desc = "Forcibly recall your guardian." + button_icon_state = "recall" + +/datum/action/guardian/recall/Trigger() + guardian.Recall() + +/** + * # Reset guardian action + * + * Allows the guardian host to exchange their guardian's player for another. + */ +/datum/action/guardian/reset_guardian + name = "Replace Guardian Player" + desc = "Replace your guardian's player with a ghost. This can only be done once." + button_icon_state = "reset" + var/cooldown_timer + +/datum/action/guardian/reset_guardian/IsAvailable() + if(cooldown_timer) + return FALSE + return TRUE + +/datum/action/guardian/reset_guardian/Trigger() + if(cooldown_timer) + to_chat(owner, "This ability is still recharging.") + return + + var/confirm = alert("Are you sure you want replace your guardian's player?", "Confirm", "Yes", "No") + if(confirm == "No") + return + + // Do this immediately, so the user can't spam a bunch of polls. + cooldown_timer = addtimer(CALLBACK(src, .proc/reset_cooldown), 5 MINUTES) + UpdateButtonIcon() + + to_chat(owner, "Searching for a replacement ghost...") + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as [guardian.real_name]?", ROLE_GUARDIAN, FALSE, 15 SECONDS, source = guardian) + + if(!length(candidates)) + to_chat(owner, "There were no ghosts willing to take control of your guardian. You can try again in 5 minutes.") + return + + var/mob/dead/observer/new_stand = pick(candidates) + to_chat(guardian, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.") + to_chat(owner, "Your guardian has been successfully reset.") + message_admins("[key_name_admin(new_stand)] has taken control of ([key_name_admin(guardian)])") + guardian.ghostize() + guardian.key = new_stand.key + qdel(src) + +/** + * Takes the action button off cooldown and makes it available again. + */ +/datum/action/guardian/reset_guardian/proc/reset_cooldown() + cooldown_timer = null + UpdateButtonIcon() + +/** + * Grants all existing `/datum/action/guardian` type actions to the src mob. + * + * Called whenever the host gains their gauardian. + */ +/mob/living/proc/grant_guardian_actions(mob/living/simple_animal/hostile/guardian/G) + if(!G || !istype(G)) + return + for(var/action in subtypesof(/datum/action/guardian)) + var/datum/action/guardian/A = new action + A.Grant(src, G) + +/** + * Removes all `/datum/action/guardian` type actions from the src mob. + * + * Called whenever the host loses their guardian. + */ +/mob/living/proc/remove_guardian_actions() + for(var/action in actions) + var/datum/action/A = action + if(istype(A, /datum/action/guardian)) + A.Remove(src) diff --git a/code/game/gamemodes/miniantags/guardian/types/healer.dm b/code/game/gamemodes/miniantags/guardian/types/healer.dm index c3f8046f0a9..72d1a03301e 100644 --- a/code/game/gamemodes/miniantags/guardian/types/healer.dm +++ b/code/game/gamemodes/miniantags/guardian/types/healer.dm @@ -26,7 +26,7 @@ melee_damage_lower = 0 melee_damage_upper = 0 melee_damage_type = STAMINA - adminseal = TRUE + admin_spawned = TRUE /mob/living/simple_animal/hostile/guardian/healer/New() ..() @@ -68,7 +68,7 @@ hud_used.action_intent.icon_state = a_intent speed = 0 damage_transfer = 0.7 - if(adminseal) + if(admin_spawned) damage_transfer = 0 melee_damage_lower = 15 melee_damage_upper = 15 @@ -79,7 +79,7 @@ hud_used.action_intent.icon_state = a_intent speed = 1 damage_transfer = 1 - if(adminseal) + if(admin_spawned) damage_transfer = 0 melee_damage_lower = 0 melee_damage_upper = 0 diff --git a/code/game/gamemodes/miniantags/guardian/types/lightning.dm b/code/game/gamemodes/miniantags/guardian/types/lightning.dm index bf84e199bab..e2ab6360ae5 100644 --- a/code/game/gamemodes/miniantags/guardian/types/lightning.dm +++ b/code/game/gamemodes/miniantags/guardian/types/lightning.dm @@ -3,12 +3,12 @@ layer = LYING_MOB_LAYER /mob/living/simple_animal/hostile/guardian/beam - melee_damage_lower = 7 - melee_damage_upper = 7 + melee_damage_lower = 12 + melee_damage_upper = 12 attacktext = "shocks" melee_damage_type = BURN attack_sound = 'sound/machines/defib_zap.ogg' - damage_transfer = 0.7 + damage_transfer = 0.6 range = 7 playstyle_string = "As a Lightning type, you will apply lightning chains to targets on attack and have a lightning chain to your summoner. Lightning chains will shock anyone near them." magic_fluff_string = "..And draw the Tesla, a shocking, lethal source of power." @@ -18,6 +18,16 @@ var/list/enemychains = list() var/successfulshocks = 0 +/mob/living/simple_animal/hostile/guardian/beam/Initialize(mapload, mob/living/host) + . = ..() + if(!summoner) + return + if(!(NO_SHOCK in summoner.mutations)) + summoner.mutations.Add(NO_SHOCK) + +/mob/living/simple_animal/hostile/guardian/beam/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) + return FALSE //You are lightning, you should not be hurt by such things. + /mob/living/simple_animal/hostile/guardian/beam/AttackingTarget() . = ..() if(. && isliving(target) && target != src && target != summoner) @@ -106,3 +116,8 @@ ) L.adjustFireLoss(1.2) //adds up very rapidly . = 1 + +/mob/living/simple_animal/hostile/guardian/beam/death(gibbed) + if(summoner && (NO_SHOCK in summoner.mutations)) + summoner.mutations.Remove(NO_SHOCK) + return ..() diff --git a/code/game/gamemodes/miniantags/guardian/types/protector.dm b/code/game/gamemodes/miniantags/guardian/types/protector.dm index 534b7e16934..fda005a58ed 100644 --- a/code/game/gamemodes/miniantags/guardian/types/protector.dm +++ b/code/game/gamemodes/miniantags/guardian/types/protector.dm @@ -25,6 +25,7 @@ overlays.Cut() melee_damage_lower = initial(melee_damage_lower) melee_damage_upper = initial(melee_damage_upper) + obj_damage = initial(obj_damage) speed = initial(speed) damage_transfer = 0.4 to_chat(src, "You switch to combat mode.") @@ -35,6 +36,7 @@ overlays.Add(shield_overlay) melee_damage_lower = 2 melee_damage_upper = 2 + obj_damage = 6 //40/7.5 rounded up, we don't want a protector guardian 2 shotting blob tiles while taking 5% damage, thats just silly. speed = 1 damage_transfer = 0.05 //damage? what's damage? to_chat(src, "You switch to protection mode.") diff --git a/code/game/gamemodes/miniantags/guardian/types/ranged.dm b/code/game/gamemodes/miniantags/guardian/types/ranged.dm index 496e31d1bd3..dab7dfb1eca 100644 --- a/code/game/gamemodes/miniantags/guardian/types/ranged.dm +++ b/code/game/gamemodes/miniantags/guardian/types/ranged.dm @@ -1,7 +1,7 @@ /obj/item/projectile/guardian name = "crystal spray" icon_state = "guardian" - damage = 5 + damage = 25 damage_type = BRUTE armour_penetration = 100 @@ -11,7 +11,7 @@ melee_damage_upper = 10 damage_transfer = 0.9 projectiletype = /obj/item/projectile/guardian - ranged_cooldown_time = 1 //fast! + ranged_cooldown_time = 5 //fast! projectilesound = 'sound/effects/hit_on_shattered_glass.ogg' ranged = 1 range = 13 diff --git a/code/game/gamemodes/miniantags/guardian/types/standard.dm b/code/game/gamemodes/miniantags/guardian/types/standard.dm index e6d95ffb92c..d458ce2f49d 100644 --- a/code/game/gamemodes/miniantags/guardian/types/standard.dm +++ b/code/game/gamemodes/miniantags/guardian/types/standard.dm @@ -45,4 +45,4 @@ playstyle_string = "As a standard type you have no special abilities, but have a high damage resistance and a powerful attack capable of smashing through walls." environment_smash = 2 battlecry = "URK" - adminseal = TRUE + admin_spawned = TRUE diff --git a/code/game/gamemodes/miniantags/morph/morph_event.dm b/code/game/gamemodes/miniantags/morph/morph_event.dm index e9465492a27..7fc9e134195 100644 --- a/code/game/gamemodes/miniantags/morph/morph_event.dm +++ b/code/game/gamemodes/miniantags/morph/morph_event.dm @@ -3,7 +3,7 @@ /datum/event/spawn_morph/proc/get_morph() spawn() - var/list/candidates = pollCandidates("Do you want to play as a morph?", ROLE_MORPH, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a morph?", ROLE_MORPH, TRUE, source = /mob/living/simple_animal/hostile/morph) if(!candidates.len) key_of_morph = null return kill() diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm index f86f018757e..cba5ea62a2a 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant.dm @@ -16,6 +16,7 @@ var/icon_stun = "revenant_stun" var/icon_drain = "revenant_draining" incorporeal_move = 3 + see_invisible = INVISIBILITY_REVENANT invisibility = INVISIBILITY_REVENANT health = INFINITY //Revenants don't use health, they use essence instead maxHealth = INFINITY @@ -33,7 +34,7 @@ status_flags = 0 wander = 0 density = 0 - flying = 1 + flying = TRUE move_resist = INFINITY mob_size = MOB_SIZE_TINY pass_flags = PASSTABLE | PASSGRILLE | PASSMOB @@ -140,7 +141,7 @@ giveObjectivesandGoals() giveSpells() else - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a revenant?", poll_time = 15 SECONDS) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", poll_time = 15 SECONDS, source = /mob/living/simple_animal/revenant) var/mob/dead/observer/theghost = null if(candidates.len) theghost = pick(candidates) @@ -396,7 +397,7 @@ spawn() if(!key_of_revenant) message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...") - var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant) if(!candidates.len) qdel(R) message_admins("No candidates were found for the new revenant. Oh well!") diff --git a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm index 91ed583efc7..47e5e759c77 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm @@ -153,7 +153,7 @@ else name = "[initial(name)] ([cast_amount]E)" -/obj/effect/proc_holder/spell/aoe_turf/revenant/can_cast(mob/living/simple_animal/revenant/user = usr) +/obj/effect/proc_holder/spell/aoe_turf/revenant/can_cast(mob/living/simple_animal/revenant/user = usr, charge_check = TRUE, show_message = FALSE) if(user.inhibited) return 0 if(charge_counter < charge_max) diff --git a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm index 5e5f0f87a79..aa76f3620ac 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm @@ -13,7 +13,7 @@ return spawn() - var/list/candidates = pollCandidates("Do you want to play as a revenant?", ROLE_REVENANT, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a revenant?", ROLE_REVENANT, TRUE, source = /mob/living/simple_animal/revenant) if(!candidates.len) key_of_revenant = null return kill() diff --git a/code/game/gamemodes/miniantags/slaughter/slaughter.dm b/code/game/gamemodes/miniantags/slaughter/slaughter.dm index d7e53bdae28..e58be201521 100644 --- a/code/game/gamemodes/miniantags/slaughter/slaughter.dm +++ b/code/game/gamemodes/miniantags/slaughter/slaughter.dm @@ -157,7 +157,7 @@ /mob/living/simple_animal/slaughter/cult/New() ..() spawn(5) - var/list/demon_candidates = pollCandidates("Do you want to play as a slaughter demon?", ROLE_DEMON, 1, 100) + var/list/demon_candidates = SSghost_spawns.poll_candidates("Do you want to play as a slaughter demon?", ROLE_DEMON, TRUE, 10 SECONDS, source = /mob/living/simple_animal/slaughter/cult) if(!demon_candidates.len) visible_message("[src] disappears in a flash of red light!") qdel(src) diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 5fed8ae00f4..7c87083dfe5 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -3,7 +3,7 @@ /datum/game_mode var/list/datum/mind/syndicates = list() -proc/issyndicate(mob/living/M as mob) +/proc/issyndicate(mob/living/M as mob) return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.syndicates) /datum/game_mode/nuclear @@ -53,7 +53,6 @@ proc/issyndicate(mob/living/M as mob) for(var/datum/mind/synd_mind in syndicates) synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs. synd_mind.special_role = SPECIAL_ROLE_NUKEOPS - synd_mind.offstation_role = TRUE return 1 @@ -104,7 +103,7 @@ proc/issyndicate(mob/living/M as mob) var/obj/effect/landmark/nuke_spawn = locate("landmark*Nuclear-Bomb") - var/nuke_code = "[rand(10000, 99999)]" + var/nuke_code = rand(10000, 99999) var/leader_selected = 0 var/agent_number = 1 var/spawnpos = 1 @@ -113,7 +112,7 @@ proc/issyndicate(mob/living/M as mob) if(spawnpos > synd_spawn.len) spawnpos = 2 synd_mind.current.loc = synd_spawn[spawnpos] - + synd_mind.offstation_role = TRUE forge_syndicate_objectives(synd_mind) create_syndicate(synd_mind) greet_syndicate(synd_mind) diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm index 8e38f106cc7..209f9689de1 100644 --- a/code/game/gamemodes/nuclear/nuclearbomb.dm +++ b/code/game/gamemodes/nuclear/nuclearbomb.dm @@ -14,59 +14,59 @@ GLOBAL_VAR(bomb_set) icon_state = "nuclearbomb0" density = 1 resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/deployable = 0 - var/extended = 0 - var/lighthack = 0 + var/extended = FALSE + var/lighthack = FALSE var/timeleft = 120 - var/timing = 0 + var/timing = FALSE + var/exploded = FALSE var/r_code = "ADMIN" - var/code = "" - var/yes_code = 0 - var/safety = 1 + var/code + var/yes_code = FALSE + var/safety = TRUE var/obj/item/disk/nuclear/auth = null var/removal_stage = NUKE_INTACT var/lastentered - var/is_syndicate = 0 + var/is_syndicate = FALSE use_power = NO_POWER_USE var/previous_level = "" var/datum/wires/nuclearbomb/wires = null /obj/machinery/nuclearbomb/syndicate - is_syndicate = 1 + is_syndicate = TRUE /obj/machinery/nuclearbomb/New() ..() - r_code = "[rand(10000, 99999.0)]"//Creates a random code upon object spawn. + r_code = rand(10000, 99999.0) // Creates a random code upon object spawn. wires = new/datum/wires/nuclearbomb(src) previous_level = get_security_level() GLOB.poi_list |= src /obj/machinery/nuclearbomb/Destroy() + SStgui.close_uis(wires) QDEL_NULL(wires) GLOB.poi_list.Remove(src) return ..() /obj/machinery/nuclearbomb/process() if(timing) - GLOB.bomb_set = 1 //So long as there is one nuke timing, it means one nuke is armed. + GLOB.bomb_set = TRUE // So long as there is one nuke timing, it means one nuke is armed. timeleft = max(timeleft - 2, 0) // 2 seconds per process() if(timeleft <= 0) INVOKE_ASYNC(src, .proc/explode) - SSnanoui.update_uis(src) return /obj/machinery/nuclearbomb/attackby(obj/item/O as obj, mob/user as mob, params) if(istype(O, /obj/item/disk/nuclear)) if(extended) if(!user.drop_item()) - to_chat(user, "\The [O] is stuck to your hand!") + to_chat(user, "[O] is stuck to your hand!") return O.forceMove(src) auth = O add_fingerprint(user) return attack_hand(user) else - to_chat(user, "You need to deploy \the [src] first. Right click on the sprite, select 'Make Deployable' then click on \the [src] with an empty hand.") + to_chat(user, "You need to deploy [src] first.") return return ..() @@ -170,181 +170,157 @@ GLOBAL_VAR(bomb_set) removal_stage = NUKE_SEALANT_OPEN /obj/machinery/nuclearbomb/attack_ghost(mob/user as mob) - if(extended) - attack_hand(user) + attack_hand(user) /obj/machinery/nuclearbomb/attack_hand(mob/user as mob) - if(extended) - if(panel_open) - wires.Interact(user) - else - ui_interact(user) - else if(deployable) - if(removal_stage != NUKE_MOBILE) - anchored = 1 - visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring!") - else - visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") - if(!lighthack) - flick("nuclearbombc", src) - icon_state = "nuclearbomb1" - extended = 1 - return - -/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "nuclear_bomb.tmpl", "Nuke Control Panel", 450, 550, state = GLOB.physical_state) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/nuclearbomb/ui_data(mob/user, datum/topic_state/state) - var/data[0] - data["is_syndicate"] = is_syndicate - data["hacking"] = 0 - data["auth"] = is_auth(user) - if(is_auth(user)) - if(yes_code) - data["authstatus"] = timing ? "Functional/Set" : "Functional" - else - data["authstatus"] = "Auth. S2" + if(panel_open) + wires.Interact(user) else - if(timing) - data["authstatus"] = "Set" - else - data["authstatus"] = "Auth. S1" + tgui_interact(user) + +/obj/machinery/nuclearbomb/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_physical_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "NuclearBomb", name, 450, 300, master_ui, state) + ui.open() + +/obj/machinery/nuclearbomb/tgui_data(mob/user) + var/list/data = list() + data["extended"] = extended + data["authdisk"] = is_auth(user) + data["diskname"] = auth ? auth.name : FALSE + data["authcode"] = yes_code + data["authfull"] = data["authdisk"] && data["authcode"] data["safe"] = safety ? "Safe" : "Engaged" data["time"] = timeleft data["timer"] = timing data["safety"] = safety data["anchored"] = anchored - data["yescode"] = yes_code - data["message"] = "AUTH" if(is_auth(user)) - data["message"] = code if(yes_code) - data["message"] = "*****" - - return data - -/obj/machinery/nuclearbomb/verb/make_deployable() - set category = "Object" - set name = "Make Deployable" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - - if(deployable) - to_chat(usr, "You close several panels to make [src] undeployable.") - deployable = 0 + data["codemsg"] = "CLEAR CODE" + else if(code) + data["codemsg"] = "RE-ENTER CODE" + else + data["codemsg"] = "ENTER CODE" else - to_chat(usr, "You adjust some panels to make [src] deployable.") - deployable = 1 - return + data["codemsg"] = "-----" + return data /obj/machinery/nuclearbomb/proc/is_auth(var/mob/user) if(auth) - return 1 + return TRUE else if(user.can_admin_interact()) - return 1 + return TRUE else - return 0 + return FALSE -/obj/machinery/nuclearbomb/Topic(href, href_list) +/obj/machinery/nuclearbomb/tgui_act(action, params) if(..()) - return 1 - - if(href_list["auth"]) - if(auth) - auth.loc = loc - yes_code = 0 - auth = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/disk/nuclear)) - usr.drop_item() - I.loc = src - auth = I - if(is_auth(usr)) - if(href_list["type"]) - if(href_list["type"] == "E") + return + . = TRUE + if(exploded) + return + switch(action) + if("deploy") + if(removal_stage != NUKE_MOBILE) + anchored = TRUE + visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring!") + else + visible_message("[src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") + if(!lighthack) + flick("nuclearbombc", src) + icon_state = "nuclearbomb1" + extended = TRUE + return + if("auth") + if(auth) + if(!usr.get_active_hand() && Adjacent(usr)) + usr.put_in_hands(auth) + else + auth.forceMove(get_turf(src)) + yes_code = FALSE + auth = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/disk/nuclear)) + usr.drop_item() + I.forceMove(src) + auth = I + return + if(!is_auth(usr)) // All requests below here require NAD inserted. + return FALSE + switch(action) + if("code") + if(yes_code) // Clear code + code = null + yes_code = FALSE + return + // If no code set, enter new one + var/tempcode = input(usr, "Code", "Input Code", null) as num|null + if(tempcode) + code = min(max(round(tempcode), 0), 999999) if(code == r_code) - yes_code = 1 + yes_code = TRUE code = null else code = "ERROR" + return + + if(!yes_code) // All requests below here require both NAD inserted AND code correct + return + + switch(action) + if("toggle_anchor") + if(removal_stage == NUKE_MOBILE) + anchored = FALSE + visible_message("[src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") + else if(isinspace()) + to_chat(usr, "There is nothing to anchor to!") + return FALSE else - if(href_list["type"] == "R") - yes_code = 0 - code = null - else - lastentered = text("[]", href_list["type"]) - if(text2num(lastentered) == null) - var/turf/LOC = get_turf(usr) - message_admins("[key_name_admin(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]! ([LOC ? "JMP" : "null"])", 0) - log_admin("EXPLOIT: [key_name(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]!") - else - code += lastentered - if(length(code) > 5) - code = "ERROR" - if(yes_code) - if(href_list["time"]) - var/time = text2num(href_list["time"]) - timeleft += time - timeleft = min(max(round(src.timeleft), 120), 600) - if(href_list["timer"]) - if(timing == -1.0) - SSnanoui.update_uis(src) - return - if(safety) - to_chat(usr, "The safety is still on.") - SSnanoui.update_uis(src) - return - timing = !(timing) - if(timing) - if(!lighthack) - icon_state = "nuclearbomb2" - if(!safety) - message_admins("[key_name_admin(usr)] engaged a nuclear bomb (JMP)") - if(!is_syndicate) - set_security_level("delta") - GLOB.bomb_set = 1 //There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke/N - else - GLOB.bomb_set = 0 + anchored = !(anchored) + if(anchored) + visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring.") else + visible_message("The anchoring bolts slide back into the depths of [src].") + return + if("set_time") + var/time = input(usr, "Detonation time (seconds, min 120, max 600)", "Input Time", 120) as num|null + if(time) + timeleft = min(max(round(time), 120), 600) + if("toggle_safety") + safety = !(safety) + if(safety) + if(!is_syndicate) + set_security_level(previous_level) + timing = FALSE + GLOB.bomb_set = FALSE + if("toggle_armed") + if(safety) + to_chat(usr, "The safety is still on.") + return + timing = !(timing) + if(timing) + if(!lighthack) + icon_state = "nuclearbomb2" + if(!safety) + message_admins("[key_name_admin(usr)] engaged a nuclear bomb [ADMIN_JMP(src)]") if(!is_syndicate) - set_security_level(previous_level) - GLOB.bomb_set = 0 - if(!lighthack) - icon_state = "nuclearbomb1" - if(href_list["safety"]) - safety = !(safety) - if(safety) - if(!is_syndicate) - set_security_level(previous_level) - timing = 0 - GLOB.bomb_set = 0 - if(href_list["anchor"]) - if(removal_stage == NUKE_MOBILE) - anchored = 0 - visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") - SSnanoui.update_uis(src) - return - - if(!isinspace()) - anchored = !(anchored) - if(anchored) - visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring.") - else - visible_message("The anchoring bolts slide back into the depths of [src].") + set_security_level("delta") + GLOB.bomb_set = TRUE // There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke else - to_chat(usr, "There is nothing to anchor to!") + GLOB.bomb_set = TRUE + else + if(!is_syndicate) + set_security_level(previous_level) + GLOB.bomb_set = FALSE + if(!lighthack) + icon_state = "nuclearbomb1" - SSnanoui.update_uis(src) /obj/machinery/nuclearbomb/blob_act(obj/structure/blob/B) - if(timing == -1.0) + if(exploded) return if(timing) //boom INVOKE_ASYNC(src, .proc/explode) @@ -359,11 +335,11 @@ GLOBAL_VAR(bomb_set) #define NUKERANGE 80 /obj/machinery/nuclearbomb/proc/explode() if(safety) - timing = 0 + timing = FALSE return - timing = -1.0 - yes_code = 0 - safety = 1 + exploded = TRUE + yes_code = FALSE + safety = TRUE if(!lighthack) icon_state = "nuclearbomb3" playsound(src,'sound/machines/alarm.ogg',100,0,5) @@ -407,6 +383,20 @@ GLOBAL_VAR(bomb_set) return return +/obj/machinery/nuclearbomb/proc/reset_lighthack_callback() + lighthack = !lighthack + +/obj/machinery/nuclearbomb/proc/reset_safety_callback() + safety = !safety + if(safety == 1) + if(!is_syndicate) + set_security_level(previous_level) + visible_message("The [src] quiets down.") + if(!lighthack) + if(icon_state == "nuclearbomb2") + icon_state = "nuclearbomb1" + else + visible_message("The [src] emits a quiet whirling noise!") //==========DAT FUKKEN DISK=============== /obj/item/disk/nuclear diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index e8f4c22e377..f6e454bd737 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -357,7 +357,8 @@ var/list/name_counts = list() var/list/names = list() - for(var/mob/living/carbon/human/H in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing if(!trackable(H)) continue diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index b09bb7a851f..213814d52e8 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -385,7 +385,8 @@ if(foecount == GLOB.score_arrested) GLOB.score_allarrested = 1 - for(var/mob/living/carbon/human/player in GLOB.mob_living_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/player = thing if(player.mind) var/role = player.mind.assigned_role if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) @@ -415,7 +416,8 @@ for(var/datum/mind/M in SSticker.mode:revolutionaries) if(M.current && M.current.stat != DEAD) revcount++ - for(var/mob/living/carbon/human/player in GLOB.mob_living_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/player = thing if(player.mind) var/role = player.mind.assigned_role if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) diff --git a/code/game/gamemodes/scoreboard.dm b/code/game/gamemodes/scoreboard.dm index d9e59e9ded1..2e370db5f7a 100644 --- a/code/game/gamemodes/scoreboard.dm +++ b/code/game/gamemodes/scoreboard.dm @@ -41,7 +41,8 @@ GLOB.score_deadaipenalty++ GLOB.score_deadcrew++ - for(var/mob/living/carbon/human/I in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/I = thing if(I.stat == DEAD && is_station_level(I.z)) GLOB.score_deadcrew++ @@ -60,7 +61,8 @@ var/dmg_score = 0 if(SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) - for(var/mob/living/carbon/human/E in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/E = thing cash_score = 0 dmg_score = 0 var/turf/location = get_turf(E.loc) diff --git a/code/game/gamemodes/shadowling/shadowling.dm b/code/game/gamemodes/shadowling/shadowling.dm index b46bc3c6c51..b51810e4967 100644 --- a/code/game/gamemodes/shadowling/shadowling.dm +++ b/code/game/gamemodes/shadowling/shadowling.dm @@ -74,7 +74,7 @@ Made by Xhuis required_enemies = 2 recommended_enemies = 2 restricted_jobs = list("AI", "Cyborg") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") /datum/game_mode/shadowling/announce() to_chat(world, "The current game mode is - Shadowling!") diff --git a/code/game/gamemodes/shadowling/shadowling_abilities.dm b/code/game/gamemodes/shadowling/shadowling_abilities.dm index 27267d2ec02..4be50a6cd59 100644 --- a/code/game/gamemodes/shadowling/shadowling_abilities.dm +++ b/code/game/gamemodes/shadowling/shadowling_abilities.dm @@ -18,80 +18,56 @@ return 0 -/obj/effect/proc_holder/spell/targeted/glare //Stuns and mutes a human target, depending on the distance relative to the shadowling +/obj/effect/proc_holder/spell/targeted/click/glare //Stuns and mutes a human target, depending on the distance relative to the shadowling name = "Glare" desc = "Stuns and mutes a target for a decent duration. Duration depends on the proximity to the target." panel = "Shadowling Abilities" charge_max = 300 - clothes_req = 0 + clothes_req = FALSE range = 10 //has no effect beyond this range, so setting this makes invalid/useless targets not show up in popup action_icon_state = "glare" - humans_only = 1 //useless since we override chose_targets, but might be used for other code later??? Might remove, idk -/obj/effect/proc_holder/spell/targeted/glare/choose_targets(mob/user) - var/list/possible_targets = list() - for(var/mob/living/carbon/human/target in view_or_range(range, user, "view")) - if(target.stat) - continue - if(is_shadow_or_thrall(target)) - continue - possible_targets += target - var/mob/living/carbon/human/M - var/list/targets = list() - if(possible_targets.len == 1)//no choice involved - targets = possible_targets - else - M = input("Choose the target for the spell.", "Targeting") as mob in possible_targets - if(M in view_or_range(range, user, "view")) - targets += M + selection_activated_message = "Your prepare to your eyes for a stunning glare! Left-click to cast at a target!" + selection_deactivated_message = "Your eyes relax... for now." + allowed_type = /mob/living/carbon/human +/obj/effect/proc_holder/spell/targeted/click/glare/cast_check(charge_check = TRUE, start_recharge = TRUE, mob/living/user = usr) + if(!shadowling_check(user)) + return FALSE + return ..() - if(!targets.len) //doesn't waste the spell - revert_cast(user) +/obj/effect/proc_holder/spell/targeted/click/glare/valid_target(mob/living/carbon/human/target, user) + if(!..()) + return FALSE + return !target.stat && !is_shadow_or_thrall(target) + +/obj/effect/proc_holder/spell/targeted/click/glare/cast(list/targets, mob/user = usr) + var/mob/living/carbon/human/H = targets[1] + + user.visible_message("[user]'s eyes flash a blinding red!") + var/distance = get_dist(H, user) + if (distance <= 1) //Melee glare + H.visible_message("[H] freezes in place, [H.p_their()] eyes glazing over...", \ + "Your gaze is forcibly drawn into [user]'s eyes, and you are mesmerized by [user.p_their()] heavenly beauty...") + H.Stun(10) + H.AdjustSilence(10) + else //Distant glare + var/loss = 10 - distance + var/duration = 10 - loss + if(loss <= 0) + to_chat(user, "Your glare had no effect over a such long distance!") + return + H.slowed = duration + H.AdjustSilence(10) + to_chat(H, "A red light flashes across your vision, and your mind tries to resist them.. you are exhausted.. you are not able to speak..") + addtimer(CALLBACK(src, .proc/do_stun, H, user, loss), duration SECONDS) + +/obj/effect/proc_holder/spell/targeted/click/glare/proc/do_stun(mob/living/carbon/human/target, user, stun_time) + if(!istype(target) || target.stat) return - - perform(targets, user = user) - return - - -/obj/effect/proc_holder/spell/targeted/glare/cast(list/targets, mob/user = usr) - for(var/mob/living/carbon/human/target in targets) - if(!ishuman(target)) - to_chat(user, "You may only glare at humans!") - charge_counter = charge_max - return - if(!shadowling_check(user)) - charge_counter = charge_max - return - if(target.stat) - to_chat(user, "[target] must be conscious!") - charge_counter = charge_max - return - if(is_shadow_or_thrall(target)) - to_chat(user, "You don't see why you would want to paralyze an ally.") - charge_counter = charge_max - return - var/mob/living/carbon/human/M = target - user.visible_message("[user]'s eyes flash a blinding red!") - var/distance = get_dist(target, user) - if (distance <= 1) //Melee glare - target.visible_message("[target] freezes in place, [target.p_their()] eyes glazing over...") - to_chat(target, "Your gaze is forcibly drawn into [user]'s eyes, and you are mesmerized by [user.p_their()] heavenly beauty...") - target.Stun(10) - M.AdjustSilence(10) - else //Distant glare - var/loss = 10 - distance - var/duration = 10 - loss - if(loss <= 0) - to_chat(user, "Your glare had no effect over a such long distance!") - return - target.slowed = duration - M.AdjustSilence(10) - to_chat(target, "A red light flashes across your vision, and your mind tries to resist them.. you are exhausted.. you are not able to speak..") - sleep(duration*10) - target.Stun(loss) - target.visible_message("[target] freezes in place, [target.p_their()] eyes glazing over...") - to_chat(target, "Red lights suddenly dance in your vision, and you are mesmerized by the heavenly lights...") + target.Stun(stun_time) + target.visible_message("[target] freezes in place, [target.p_their()] eyes glazing over...",\ + "Red lights suddenly dance in your vision, and you are mesmerized by the heavenly lights...") /obj/effect/proc_holder/spell/aoe_turf/veil name = "Veil" @@ -109,9 +85,10 @@ return to_chat(user, "You silently disable all nearby lights.") for(var/obj/structure/glowshroom/G in orange(2, user)) //Why the fuck was this in the loop below? - G.visible_message("\The [G] withers away!") + G.visible_message("[G] withers away!") qdel(G) for(var/turf/T in targets) + T.extinguish_light() for(var/atom/A in T.contents) A.extinguish_light() @@ -227,96 +204,80 @@ M.reagents.add_reagent("frostoil", 15) //Half of a cryosting -/obj/effect/proc_holder/spell/targeted/enthrall //Turns a target into the shadowling's slave. This overrides all previous loyalties +/obj/effect/proc_holder/spell/targeted/click/enthrall //Turns a target into the shadowling's slave. This overrides all previous loyalties name = "Enthrall" desc = "Allows you to enslave a conscious, non-braindead, non-catatonic human to your will. This takes some time to cast." panel = "Shadowling Abilities" charge_max = 0 - clothes_req = 0 + clothes_req = FALSE range = 1 //Adjacent to user - var/enthralling = 0 + var/enthralling = FALSE action_icon_state = "enthrall" - humans_only = 1 -/obj/effect/proc_holder/spell/targeted/enthrall/cast(list/targets, mob/user = usr) + click_radius = -1 // Precision baby + selection_activated_message = "Your prepare your mind to entrall a mortal. Left-click to cast at a target!" + selection_deactivated_message = "Your mind relaxes." + allowed_type = /mob/living/carbon/human + +/obj/effect/proc_holder/spell/targeted/click/enthrall/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE) + if(enthralling || !shadowling_check(user)) + return FALSE + return ..() + +/obj/effect/proc_holder/spell/targeted/click/enthrall/valid_target(mob/living/carbon/human/target, user) + if(!..()) + return FALSE + return target.key && target.mind && !target.stat && !is_shadow_or_thrall(target) && target.client + +/obj/effect/proc_holder/spell/targeted/click/enthrall/cast(list/targets, mob/user = usr) var/mob/living/carbon/human/ling = user listclearnulls(SSticker.mode.shadowling_thralls) if(!(ling.mind in SSticker.mode.shadows)) return - if(!isshadowling(ling)) - if(SSticker.mode.shadowling_thralls.len >= 5) - charge_counter = charge_max - return - for(var/mob/living/carbon/human/target in targets) - if(!in_range(user, target)) - to_chat(user, "You need to be closer to enthrall [target].") - charge_counter = charge_max - return - if(!target.key || !target.mind) - to_chat(user, "The target has no mind.") - charge_counter = charge_max - return - if(target.stat) - to_chat(user, "The target must be conscious.") - charge_counter = charge_max - return - if(is_shadow_or_thrall(target)) - to_chat(user, "You can not enthrall allies.") - charge_counter = charge_max - return - if(!ishuman(target)) - to_chat(user, "You can only enthrall humans.") - charge_counter = charge_max - return - if(enthralling) - to_chat(user, "You are already enthralling!") - charge_counter = charge_max - return - if(!target.client) - to_chat(user, "[target]'s mind is vacant of activity.") - enthralling = 1 - to_chat(user, "This target is valid. You begin the enthralling.") - to_chat(target, "[user] stares at you. You feel your head begin to pulse.") + var/mob/living/carbon/human/target = targets[1] + enthralling = TRUE + to_chat(user, "This target is valid. You begin the enthralling.") + to_chat(target, "[user] stares at you. You feel your head begin to pulse.") - for(var/progress = 0, progress <= 3, progress++) - switch(progress) - if(1) - to_chat(user, "You place your hands to [target]'s head...") - user.visible_message("[user] places [user.p_their()] hands onto the sides of [target]'s head!") - if(2) - to_chat(user, "You begin preparing [target]'s mind as a blank slate...") - user.visible_message("[user]'s palms flare a bright red against [target]'s temples!") - to_chat(target, "A terrible red light floods your mind. You collapse as conscious thought is wiped away.") - target.Weaken(12) - sleep(20) - if(ismindshielded(target)) - to_chat(user, "They have a mindshield implant. You begin to deactivate it - this will take some time.") - user.visible_message("[user] pauses, then dips [user.p_their()] head in concentration!") - to_chat(target, "Your mindshield implant becomes hot as it comes under attack!") - sleep(100) //10 seconds - not spawn() so the enthralling takes longer - to_chat(user, "The nanobots composing the mindshield implant have been rendered inert. Now to continue.") - user.visible_message("[user] relaxes again.") - for(var/obj/item/implant/mindshield/L in target) - if(L && L.implanted) - qdel(L) - to_chat(target, "Your mental protection implant unexpectedly falters, dims, dies.") - if(3) - to_chat(user, "You begin planting the tumor that will control the new thrall...") - user.visible_message("A strange energy passes from [user]'s hands into [target]'s head!") - to_chat(target, "You feel your memories twisting, morphing. A sense of horror dominates your mind.") - if(!do_mob(user, target, 70)) //around 21 seconds total for enthralling, 31 for someone with a mindshield implant - to_chat(user, "The enthralling has been interrupted - your target's mind returns to its previous state.") - to_chat(target, "You wrest yourself away from [user]'s hands and compose yourself") - enthralling = 0 - return + for(var/progress = 0, progress <= 3, progress++) + switch(progress) + if(1) + to_chat(user, "You place your hands to [target]'s head...") + user.visible_message("[user] places [user.p_their()] hands onto the sides of [target]'s head!") + if(2) + to_chat(user, "You begin preparing [target]'s mind as a blank slate...") + user.visible_message("[user]'s palms flare a bright red against [target]'s temples!") + to_chat(target, "A terrible red light floods your mind. You collapse as conscious thought is wiped away.") + target.Weaken(12) + sleep(20) + if(ismindshielded(target)) + to_chat(user, "They have a mindshield implant. You begin to deactivate it - this will take some time.") + user.visible_message("[user] pauses, then dips [user.p_their()] head in concentration!") + to_chat(target, "Your mindshield implant becomes hot as it comes under attack!") + sleep(100) //10 seconds - not spawn() so the enthralling takes longer + to_chat(user, "The nanobots composing the mindshield implant have been rendered inert. Now to continue.") + user.visible_message("[user] relaxes again.") + for(var/obj/item/implant/mindshield/L in target) + if(L && L.implanted) + qdel(L) + to_chat(target, "Your mental protection implant unexpectedly falters, dims, dies.") + if(3) + to_chat(user, "You begin planting the tumor that will control the new thrall...") + user.visible_message("A strange energy passes from [user]'s hands into [target]'s head!") + to_chat(target, "You feel your memories twisting, morphing. A sense of horror dominates your mind.") + if(!do_mob(user, target, 70)) //around 21 seconds total for enthralling, 31 for someone with a mindshield implant + to_chat(user, "The enthralling has been interrupted - your target's mind returns to its previous state.") + to_chat(target, "You wrest yourself away from [user]'s hands and compose yourself") + enthralling = FALSE + return - enthralling = 0 - to_chat(user, "You have enthralled [target]!") - target.visible_message("[target] looks to have experienced a revelation!", \ - "False faces all dark not real not real not--") - target.setOxyLoss(0) //In case the shadowling was choking them out - SSticker.mode.add_thrall(target.mind) - target.mind.special_role = SPECIAL_ROLE_SHADOWLING_THRALL + enthralling = FALSE + to_chat(user, "You have enthralled [target]!") + target.visible_message("[target] looks to have experienced a revelation!", \ + "False faces all dark not real not real not--") + target.setOxyLoss(0) //In case the shadowling was choking them out + SSticker.mode.add_thrall(target.mind) + target.mind.special_role = SPECIAL_ROLE_SHADOWLING_THRALL /obj/effect/proc_holder/spell/targeted/shadowling_regenarmor //Resets a shadowling's species to normal, removes genetic defects, and re-equips their armor name = "Rapid Re-Hatch" @@ -404,7 +365,7 @@ reviveThrallAcquired = 1 to_chat(target, "The power of your thralls has granted you the Black Recuperation ability. \ This will, after a short time, bring a dead thrall completely back to life with no bodily defects.") - target.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/reviveThrall(null)) + target.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/reviveThrall(null)) if(thralls < victory_threshold) to_chat(target, "You do not have the power to ascend. You require [victory_threshold] thralls, but only [thralls] living thralls are present.") @@ -570,169 +531,170 @@ -/obj/effect/proc_holder/spell/targeted/reviveThrall +/obj/effect/proc_holder/spell/targeted/click/reviveThrall name = "Black Recuperation" desc = "Revives or empowers a thrall." panel = "Shadowling Abilities" range = 1 charge_max = 600 - clothes_req = 0 - include_user = 0 + clothes_req = FALSE + include_user = FALSE action_icon_state = "revive_thrall" - humans_only = 1 + click_radius = -1 // Precision baby + selection_activated_message = "You start focusing your powers on mending wounds of allies. Left-click to cast at a target!" + selection_deactivated_message = "Your mind relaxes." + allowed_type = /mob/living/carbon/human -/obj/effect/proc_holder/spell/targeted/reviveThrall/cast(list/targets, mob/user = usr) +/obj/effect/proc_holder/spell/targeted/click/reviveThrall/can_cast(mob/user = usr) if(!shadowling_check(user)) - charge_counter = charge_max - return - for(var/mob/living/carbon/human/thrallToRevive in targets) - var/choice = alert(user,"Empower a living thrall or revive a dead one?",,"Empower","Revive","Cancel") - switch(choice) - if("Empower") - if(!is_thrall(thrallToRevive)) - to_chat(user, "[thrallToRevive] is not a thrall.") - charge_counter = charge_max - return - if(thrallToRevive.stat != CONSCIOUS) - to_chat(user, "[thrallToRevive] must be conscious to become empowered.") - charge_counter = charge_max - return - if(isshadowlinglesser(thrallToRevive)) - to_chat(user, "[thrallToRevive] is already empowered.") - charge_counter = charge_max - return - var/empowered_thralls = 0 - for(var/datum/mind/M in SSticker.mode.shadowling_thralls) - if(!ishuman(M.current)) - return - var/mob/living/carbon/human/H = M.current - if(isshadowlinglesser(H)) - empowered_thralls++ - if(empowered_thralls >= EMPOWERED_THRALL_LIMIT) - to_chat(user, "You cannot spare this much energy. There are too many empowered thralls.") - charge_counter = charge_max - return - user.visible_message("[user] places [user.p_their()] hands over [thrallToRevive]'s face, red light shining from beneath.", \ - "You place your hands on [thrallToRevive]'s face and begin gathering energy...") - to_chat(thrallToRevive, "[user] places [user.p_their()] hands over your face. You feel energy gathering. Stand still...") - if(!do_mob(user, thrallToRevive, 80)) - to_chat(user, "Your concentration snaps. The flow of energy ebbs.") - charge_counter = charge_max - return - to_chat(user, "You release a massive surge of power into [thrallToRevive]!") - user.visible_message("Red lightning surges into [thrallToRevive]'s face!") - playsound(thrallToRevive, 'sound/weapons/egloves.ogg', 50, 1) - playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1) - user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1) - thrallToRevive.Weaken(5) - thrallToRevive.visible_message("[thrallToRevive] collapses, [thrallToRevive.p_their()] skin and face distorting!", \ - "AAAAAAAAAAAAAAAAAAAGH-") - sleep(20) - thrallToRevive.visible_message("[thrallToRevive] slowly rises, no longer recognizable as human.", \ - "You feel new power flow into you. You have been gifted by your masters. You now closely resemble them. You are empowered in \ - darkness but wither slowly in light. In addition, you now have glare and true shadow walk.") - thrallToRevive.set_species(/datum/species/shadow/ling/lesser) - thrallToRevive.mind.RemoveSpell(/obj/effect/proc_holder/spell/targeted/lesser_shadow_walk) - thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/glare(null)) - thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadow_walk(null)) - if("Revive") - if(!is_thrall(thrallToRevive)) - to_chat(user, "[thrallToRevive] is not a thrall.") - charge_counter = charge_max - return - if(thrallToRevive.stat != DEAD) - to_chat(user, "[thrallToRevive] is not dead.") - charge_counter = charge_max - return - user.visible_message("[user] kneels over [thrallToRevive], placing [user.p_their()] hands on [thrallToRevive.p_their()] chest.", \ - "You crouch over the body of your thrall and begin gathering energy...") - thrallToRevive.notify_ghost_cloning("Your masters are resuscitating you! Re-enter your corpse if you wish to be brought to life.", source = thrallToRevive) - if(!do_mob(user, thrallToRevive, 30)) - to_chat(user, "Your concentration snaps. The flow of energy ebbs.") - charge_counter = charge_max - return - to_chat(user, "You release a massive surge of power into [thrallToRevive]!") - user.visible_message("Red lightning surges from [user]'s hands into [thrallToRevive]'s chest!") - playsound(thrallToRevive, 'sound/weapons/egloves.ogg', 50, 1) - playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1) - user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1) - sleep(10) - if(thrallToRevive.revive()) - thrallToRevive.visible_message("[thrallToRevive] heaves in breath, dim red light shining in [thrallToRevive.p_their()] eyes.", \ - "You have returned. One of your masters has brought you from the darkness beyond.") - thrallToRevive.Weaken(4) - thrallToRevive.emote("gasp") - playsound(thrallToRevive, "bodyfall", 50, 1) - else - charge_counter = charge_max - return + return FALSE + return ..() -/obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle +/obj/effect/proc_holder/spell/targeted/click/reviveThrall/valid_target(mob/living/carbon/human/target, user) + if(!..()) + return FALSE + + return is_thrall(target) + +/obj/effect/proc_holder/spell/targeted/click/reviveThrall/cast(list/targets, mob/user = usr) + var/mob/living/carbon/human/thrallToRevive = targets[1] + if(thrallToRevive.stat == CONSCIOUS) + if(isshadowlinglesser(thrallToRevive)) + to_chat(user, "[thrallToRevive] is already empowered.") + revert_cast(user) + return + var/empowered_thralls = 0 + for(var/datum/mind/M in SSticker.mode.shadowling_thralls) + if(!ishuman(M.current)) + return + var/mob/living/carbon/human/H = M.current + if(isshadowlinglesser(H)) + empowered_thralls++ + if(empowered_thralls >= EMPOWERED_THRALL_LIMIT) + to_chat(user, "You cannot spare this much energy. There are too many empowered thralls.") + revert_cast(user) + return + user.visible_message("[user] places [user.p_their()] hands over [thrallToRevive]'s face, red light shining from beneath.", \ + "You place your hands on [thrallToRevive]'s face and begin gathering energy...") + to_chat(thrallToRevive, "[user] places [user.p_their()] hands over your face. You feel energy gathering. Stand still...") + if(!do_mob(user, thrallToRevive, 80)) + to_chat(user, "Your concentration snaps. The flow of energy ebbs.") + revert_cast(user) + return + to_chat(user, "You release a massive surge of power into [thrallToRevive]!") + user.visible_message("Red lightning surges into [thrallToRevive]'s face!") + playsound(thrallToRevive, 'sound/weapons/egloves.ogg', 50, 1) + playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1) + user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1) + thrallToRevive.Weaken(5) + thrallToRevive.visible_message("[thrallToRevive] collapses, [thrallToRevive.p_their()] skin and face distorting!", \ + "AAAAAAAAAAAAAAAAAAAGH-") + sleep(20) + thrallToRevive.visible_message("[thrallToRevive] slowly rises, no longer recognizable as human.", \ + "You feel new power flow into you. You have been gifted by your masters. You now closely resemble them. You are empowered in \ + darkness but wither slowly in light. In addition, you now have glare and true shadow walk.") + thrallToRevive.set_species(/datum/species/shadow/ling/lesser) + thrallToRevive.mind.RemoveSpell(/obj/effect/proc_holder/spell/targeted/lesser_shadow_walk) + thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/glare(null)) + thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadow_walk(null)) + else if(thrallToRevive.stat == DEAD) + user.visible_message("[user] kneels over [thrallToRevive], placing [user.p_their()] hands on [thrallToRevive.p_their()] chest.", \ + "You crouch over the body of your thrall and begin gathering energy...") + thrallToRevive.notify_ghost_cloning("Your masters are resuscitating you! Re-enter your corpse if you wish to be brought to life.", source = thrallToRevive) + if(!do_mob(user, thrallToRevive, 30)) + to_chat(user, "Your concentration snaps. The flow of energy ebbs.") + revert_cast(user) + return + to_chat(user, "You release a massive surge of power into [thrallToRevive]!") + user.visible_message("Red lightning surges from [user]'s hands into [thrallToRevive]'s chest!") + playsound(thrallToRevive, 'sound/weapons/egloves.ogg', 50, 1) + playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1) + user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1) + sleep(10) + if(thrallToRevive.revive()) + thrallToRevive.visible_message("[thrallToRevive] heaves in breath, dim red light shining in [thrallToRevive.p_their()] eyes.", \ + "You have returned. One of your masters has brought you from the darkness beyond.") + thrallToRevive.Weaken(4) + thrallToRevive.emote("gasp") + playsound(thrallToRevive, "bodyfall", 50, 1) + else + to_chat(user, "The target must be awake to empower or dead to revive.") + revert_cast(user) + +/obj/effect/proc_holder/spell/targeted/click/shadowling_extend_shuttle name = "Destroy Engines" desc = "Extends the time of the emergency shuttle's arrival by ten minutes using a life force of our enemy. Shuttle will be unable to be recalled. This can only be used once." panel = "Shadowling Abilities" range = 1 - clothes_req = 0 + clothes_req = FALSE charge_max = 600 + click_radius = -1 // Precision baby + selection_activated_message = "You start gathering destructive powers to delay the shuttle. Left-click to cast at a target!" + selection_deactivated_message = "Your mind relaxes." + allowed_type = /mob/living/carbon/human action_icon_state = "extend_shuttle" var/global/extendlimit = 0 -/obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle/cast(list/targets, mob/user = usr) +/obj/effect/proc_holder/spell/targeted/click/shadowling_extend_shuttle/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE) if(!shadowling_check(user)) - charge_counter = charge_max - return + return FALSE if(extendlimit == 1) - to_chat(user, "Shuttle was already delayed.") - charge_counter = charge_max - return - for(var/mob/living/carbon/human/target in targets) - if(target.stat) - charge_counter = charge_max - return - if(is_shadow_or_thrall(target)) - to_chat(user, "[target] must not be an ally.") - charge_counter = charge_max - return - if(SSshuttle.emergency.mode != SHUTTLE_CALL) + if(show_message) + to_chat(user, "Shuttle was already delayed.") + return FALSE + if(SSshuttle.emergency.mode != SHUTTLE_CALL) + if(show_message) to_chat(user, "The shuttle must be inbound only to the station.") - charge_counter = charge_max - return - var/mob/living/carbon/human/M = target - user.visible_message("[user]'s eyes flash a bright red!", \ - "You begin to draw [M]'s life force.") - M.visible_message("[M]'s face falls slack, [M.p_their()] jaw slightly distending.", \ - "You are suddenly transported... far, far away...") - extendlimit = 1 - if(!do_after(user, 150, target = M)) - extendlimit = 0 - to_chat(M, "You are snapped back to reality, your haze dissipating!") - to_chat(user, "You have been interrupted. The draw has failed.") - return - to_chat(user, "You project [M]'s life force toward the approaching shuttle, extending its arrival duration!") - M.visible_message("[M]'s eyes suddenly flare red. They proceed to collapse on the floor, not breathing.", \ - "...speeding by... ...pretty blue glow... ...touch it... ...no glow now... ...no light... ...nothing at all...") - M.death() - if(SSshuttle.emergency.mode == SHUTTLE_CALL) - var/more_minutes = 6000 - var/timer = SSshuttle.emergency.timeLeft(1) + more_minutes - GLOB.event_announcement.Announce("Major system failure aboard the emergency shuttle. This will extend its arrival time by approximately 10 minutes and the shuttle is unable to be recalled.", "System Failure", 'sound/misc/notice1.ogg') - SSshuttle.emergency.setTimer(timer) - SSshuttle.emergency.canRecall = FALSE - user.mind.spell_list.Remove(src) //Can only be used once! - qdel(src) + return FALSE + return ..() + +/obj/effect/proc_holder/spell/targeted/click/shadowling_extend_shuttle/valid_target(mob/living/carbon/human/target, user) + if(!..()) + return FALSE + return !target.stat && !is_shadow_or_thrall(target) + + +/obj/effect/proc_holder/spell/targeted/click/shadowling_extend_shuttle/cast(list/targets, mob/user = usr) + var/mob/living/carbon/human/target = targets[1] + + user.visible_message("[user]'s eyes flash a bright red!", \ + "You begin to draw [target]'s life force.") + target.visible_message("[target]'s face falls slack, [target.p_their()] jaw slightly distending.", \ + "You are suddenly transported... far, far away...") + extendlimit = 1 + if(!do_after(user, 150, target = target)) + extendlimit = 0 + to_chat(target, "You are snapped back to reality, your haze dissipating!") + to_chat(user, "You have been interrupted. The draw has failed.") + return + to_chat(user, "You project [target]'s life force toward the approaching shuttle, extending its arrival duration!") + target.visible_message("[target]'s eyes suddenly flare red. They proceed to collapse on the floor, not breathing.", \ + "...speeding by... ...pretty blue glow... ...touch it... ...no glow now... ...no light... ...nothing at all...") + target.death() + if(SSshuttle.emergency.mode == SHUTTLE_CALL) + var/more_minutes = 6000 + var/timer = SSshuttle.emergency.timeLeft(1) + more_minutes + GLOB.event_announcement.Announce("Major system failure aboard the emergency shuttle. This will extend its arrival time by approximately 10 minutes and the shuttle is unable to be recalled.", "System Failure", 'sound/misc/notice1.ogg') + SSshuttle.emergency.setTimer(timer) + SSshuttle.emergency.canRecall = FALSE + user.mind.spell_list.Remove(src) //Can only be used once! + qdel(src) // ASCENDANT ABILITIES BEYOND THIS POINT // -/obj/effect/proc_holder/spell/targeted/annihilate +/obj/effect/proc_holder/spell/targeted/click/annihilate name = "Annihilate" desc = "Gibs someone instantly." panel = "Ascendant" range = 7 - charge_max = 0 - clothes_req = 0 + charge_max = FALSE + clothes_req = FALSE action_icon_state = "annihilate" + selection_activated_message = "You start thinking about gibs. Left-click to cast at a target!" + selection_deactivated_message = "Your mind relaxes." + allowed_type = /mob/living/carbon/human -/obj/effect/proc_holder/spell/targeted/annihilate/cast(list/targets, mob/user = usr) +/obj/effect/proc_holder/spell/targeted/click/annihilate/cast(list/targets, mob/user = usr) var/mob/living/simple_animal/ascendant_shadowling/SHA = user if(SHA.phasing) to_chat(user, "You are not in the same plane of existence. Unphase first.") @@ -755,45 +717,42 @@ -/obj/effect/proc_holder/spell/targeted/hypnosis +/obj/effect/proc_holder/spell/targeted/click/hypnosis name = "Hypnosis" desc = "Instantly enthralls a human." panel = "Ascendant" range = 7 - charge_max = 0 - clothes_req = 0 + charge_max = FALSE + clothes_req = FALSE action_icon_state = "enthrall" -/obj/effect/proc_holder/spell/targeted/hypnosis/cast(list/targets, mob/user = usr) - var/mob/living/simple_animal/ascendant_shadowling/SHA = user - if(SHA.phasing) - charge_counter = charge_max - to_chat(user, "You are not in the same plane of existence. Unphase first.") - return + click_radius = -1 + selection_activated_message = "You start preparing to mindwash over a mortal mind. Left-click to cast at a target!" + selection_deactivated_message = "Your mind relaxes." + allowed_type = /mob/living/carbon/human - for(var/mob/living/carbon/human/target in targets) - if(is_shadow_or_thrall(target)) - to_chat(user, "You cannot enthrall an ally.") - charge_counter = charge_max - return - if(!target.ckey || !target.mind) - to_chat(user, "The target has no mind.") - charge_counter = charge_max - return - if(target.stat) - to_chat(user, "The target must be conscious.") - charge_counter = charge_max - return - if(!ishuman(target)) - to_chat(user, "You can only enthrall humans.") - charge_counter = charge_max - return +/obj/effect/proc_holder/spell/targeted/click/hypnosis/can_cast(mob/living/simple_animal/ascendant_shadowling/user = usr, charge_check = TRUE, show_message = FALSE) + if(!istype(user)) + return FALSE + if(user.phasing) + if(show_message) + to_chat(user, "You are not in the same plane of existence. Unphase first.") + return FALSE + return ..() - to_chat(user, "You instantly rearrange [target]'s memories, hyptonitizing [target.p_them()] into a thrall.") - to_chat(target, "An agonizing spike of pain drives into your mind, and--") - SSticker.mode.add_thrall(target.mind) - target.mind.special_role = SPECIAL_ROLE_SHADOWLING_THRALL - target.add_language("Shadowling Hivemind") +/obj/effect/proc_holder/spell/targeted/click/hypnosis/valid_target(mob/living/carbon/human/target, user) + if(!..()) + return FALSE + return !is_shadow_or_thrall(target) && target.ckey && target.mind && !target.stat + +/obj/effect/proc_holder/spell/targeted/click/hypnosis/cast(list/targets, mob/user = usr) + var/mob/living/carbon/human/target = targets[1] + + to_chat(user, "You instantly rearrange [target]'s memories, hyptonitizing [target.p_them()] into a thrall.") + to_chat(target, "An agonizing spike of pain drives into your mind, and--") + SSticker.mode.add_thrall(target.mind) + target.mind.special_role = SPECIAL_ROLE_SHADOWLING_THRALL + target.add_language("Shadowling Hivemind") diff --git a/code/game/gamemodes/shadowling/special_shadowling_abilities.dm b/code/game/gamemodes/shadowling/special_shadowling_abilities.dm index 5fc5e1e78cc..28c934f7d38 100644 --- a/code/game/gamemodes/shadowling/special_shadowling_abilities.dm +++ b/code/game/gamemodes/shadowling/special_shadowling_abilities.dm @@ -99,14 +99,14 @@ GLOBAL_LIST_INIT(possibleShadowlingNames, list("U'ruan", "Y`shej", "Nex", "Hel-u to_chat(H, "Your powers are awoken. You may now live to your fullest extent. Remember your goal. Cooperate with your thralls and allies.") H.ExtinguishMob() H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadow_vision(null)) - H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/enthrall(null)) - H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/glare(null)) + H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/enthrall(null)) + H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/glare(null)) H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/veil(null)) H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadow_walk(null)) H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/flashfreeze(null)) H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/collective_mind(null)) H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadowling_regenarmor(null)) - H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle(null)) + H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/shadowling_extend_shuttle(null)) QDEL_NULL(H.hud_used) H.hud_used = new /datum/hud/human(H, ui_style2icon(H.client.prefs.UI_style), H.client.prefs.UI_style_color, H.client.prefs.UI_style_alpha) @@ -163,7 +163,7 @@ GLOBAL_LIST_INIT(possibleShadowlingNames, list("U'ruan", "Y`shej", "Nex", "Hel-u to_chat(M, "An immense pressure slams you onto the ground!") for(var/thing in GLOB.apcs) var/obj/machinery/power/apc/A = thing - A.overload_lighting() + INVOKE_ASYNC(A, /obj/machinery/power/apc.proc/overload_lighting) var/mob/living/simple_animal/ascendant_shadowling/A = new /mob/living/simple_animal/ascendant_shadowling(H.loc) A.announce("VYSHA NERADA YEKHEZET U'RUU!!", 5, 'sound/hallucinations/veryfar_noise.ogg') for(var/obj/effect/proc_holder/spell/S in H.mind.spell_list) @@ -172,8 +172,8 @@ GLOBAL_LIST_INIT(possibleShadowlingNames, list("U'ruan", "Y`shej", "Nex", "Hel-u H.mind.transfer_to(A) A.name = H.real_name A.languages = H.languages - A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/annihilate(null)) - A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/hypnosis(null)) + A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/annihilate(null)) + A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/hypnosis(null)) A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadowling_phase_shift(null)) A.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/ascendant_storm(null)) A.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadowlingAscendantTransmit(null)) diff --git a/code/game/gamemodes/steal_items.dm b/code/game/gamemodes/steal_items.dm index 4175883fc1a..c7ce0261bd0 100644 --- a/code/game/gamemodes/steal_items.dm +++ b/code/game/gamemodes/steal_items.dm @@ -43,7 +43,7 @@ protected_jobs = list("Captain") /datum/theft_objective/hoslaser - name = "the head of security's recreated antique laser gun" + name = "the head of security's X-01 multiphase energy gun" typepath = /obj/item/gun/energy/gun/hos protected_jobs = list("Head Of Security") @@ -57,7 +57,7 @@ typepath = /obj/item/aicard location_override = "AI Satellite. An intellicard for transportation can be found in Tech Storage, Science Department or manufactured" -datum/theft_objective/ai/check_special_completion(var/obj/item/aicard/C) +/datum/theft_objective/ai/check_special_completion(var/obj/item/aicard/C) if(..()) for(var/mob/living/silicon/ai/A in C) if(istype(A, /mob/living/silicon/ai) && A.stat != 2) //See if any AI's are alive inside that card. diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index c7ab4490f48..e7c5b4541a8 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -82,22 +82,10 @@ /datum/game_mode/proc/auto_declare_completion_traitor() if(traitors.len) - var/text = "The traitors were:" + var/text = "The traitors were:
    " for(var/datum/mind/traitor in traitors) var/traitorwin = 1 - - text += "
    [traitor.key] was [traitor.name] (" - if(traitor.current) - if(traitor.current.stat == DEAD) - text += "died" - else - text += "survived" - if(traitor.current.real_name != traitor.name) - text += " as [traitor.current.real_name]" - else - text += "body destroyed" - text += ")" - + text += printplayer(traitor) var/TC_uses = 0 var/uplink_true = 0 @@ -131,12 +119,20 @@ if(traitorwin) - text += "
    The [special_role_text] was successful!" + text += "
    The [special_role_text] was successful!
    " feedback_add_details("traitor_success","SUCCESS") else - text += "
    The [special_role_text] has failed!" + text += "
    The [special_role_text] has failed!
    " feedback_add_details("traitor_success","FAIL") + if(length(SSticker.mode.implanted)) + text += "

    The mindslaves were:
    " + for(var/datum/mind/mindslave in SSticker.mode.implanted) + text += printplayer(mindslave) + var/datum/mind/master_mind = SSticker.mode.implanted[mindslave] + var/mob/living/carbon/human/master = master_mind.current + text += " (slaved by: [master])
    " + var/phrases = jointext(GLOB.syndicate_code_phrase, ", ") var/responses = jointext(GLOB.syndicate_code_response, ", ") diff --git a/code/game/gamemodes/vampire/vampire.dm b/code/game/gamemodes/vampire/vampire.dm index 3caf860383d..75748448527 100644 --- a/code/game/gamemodes/vampire/vampire.dm +++ b/code/game/gamemodes/vampire/vampire.dm @@ -279,6 +279,7 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha var/blood = 0 var/old_bloodtotal = 0 //used to see if we increased our blood total var/old_bloodusable = 0 //used to see if we increased our blood usable + var/blood_volume_warning = 9999 //Blood volume threshold for warnings if(owner.is_muzzled()) to_chat(owner, "[owner.wear_mask] prevents you from biting [H]!") draining = null @@ -291,13 +292,10 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha H.LAssailant = owner while(do_mob(owner, H, 50)) if(!(owner.mind in SSticker.mode.vampires)) - to_chat(owner, "Your fangs have disappeared!") + to_chat(owner, "Your fangs have disappeared!") return old_bloodtotal = bloodtotal old_bloodusable = bloodusable - if(!H.blood_volume) - to_chat(owner, "They've got no blood left to give.") - break if(H.stat < DEAD) if(H.ckey || H.player_ghosted) //Requires ckey regardless if monkey or humanoid, or the body has been ghosted before it died blood = min(20, H.blood_volume) // if they have less than 20 blood, give them the remnant else they get 20 blood @@ -312,6 +310,17 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha to_chat(owner, "You have accumulated [bloodtotal] [bloodtotal > 1 ? "units" : "unit"] of blood[bloodusable != old_bloodusable ? ", and have [bloodusable] left to use" : ""].") check_vampire_upgrade() H.blood_volume = max(H.blood_volume - 25, 0) + //Blood level warnings (Code 'borrowed' from Fulp) + if(H.blood_volume) + if(H.blood_volume <= BLOOD_VOLUME_BAD && blood_volume_warning > BLOOD_VOLUME_BAD) + to_chat(owner, "Your victim's blood volume is dangerously low.") + else if(H.blood_volume <= BLOOD_VOLUME_OKAY && blood_volume_warning > BLOOD_VOLUME_OKAY) + to_chat(owner, "Your victim's blood is at an unsafe level.") + blood_volume_warning = H.blood_volume //Set to blood volume, so that you only get the message once + else + to_chat(owner, "You have bled your victim dry!") + break + if(ishuman(owner)) var/mob/living/carbon/human/V = owner if(!H.ckey && !H.player_ghosted)//Only runs if there is no ckey and the body has not being ghosted while alive diff --git a/code/game/gamemodes/vampire/vampire_powers.dm b/code/game/gamemodes/vampire/vampire_powers.dm index 7e0f00f8721..0ff7192a7e6 100644 --- a/code/game/gamemodes/vampire/vampire_powers.dm +++ b/code/game/gamemodes/vampire/vampire_powers.dm @@ -15,7 +15,7 @@ if(!gain_desc) gain_desc = "You have gained \the [src] ability." -/obj/effect/proc_holder/spell/vampire/cast_check(skipcharge = 0, mob/living/user = usr) +/obj/effect/proc_holder/spell/vampire/cast_check(charge_check = TRUE, start_recharge = TRUE, mob/living/user = usr) if(!user.mind) return 0 if(!ishuman(user)) @@ -45,7 +45,7 @@ return 0 return ..() -/obj/effect/proc_holder/spell/vampire/can_cast(mob/user = usr) +/obj/effect/proc_holder/spell/vampire/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE) if(!user.mind) return 0 if(!ishuman(user)) @@ -145,7 +145,7 @@ /obj/effect/proc_holder/spell/vampire/self/rejuvenate name = "Rejuvenate" - desc= "Flush your system with spare blood to remove any incapacitating effects." + desc= "Use reserve blood to enliven your body, removing any incapacitating effects." action_icon_state = "vampire_rejuvinate" charge_max = 200 stat_allowed = 1 @@ -158,7 +158,7 @@ user.SetParalysis(0) user.SetSleeping(0) U.adjustStaminaLoss(-75) - to_chat(user, "You flush your system with clean blood and remove any incapacitating effects.") + to_chat(user, "You instill your body with clean blood and remove any incapacitating effects.") spawn(1) if(usr.mind.vampire.get_ability(/datum/vampire_passive/regen)) for(var/i = 1 to 5) @@ -264,7 +264,7 @@ continue if(ishuman(C)) var/mob/living/carbon/human/H = C - if(istype(H.l_ear, /obj/item/clothing/ears/earmuffs) || istype(H.r_ear, /obj/item/clothing/ears/earmuffs)) + if(H.check_ear_prot() >= HEARING_PROTECTION_TOTAL) continue if(!affects(C)) continue @@ -354,10 +354,10 @@ var/datum/objective/protect/serve_objective = new serve_objective.owner = user.mind serve_objective.target = H.mind - serve_objective.explanation_text = "You have been Enthralled by [user]. Follow [user.p_their()] every command." + serve_objective.explanation_text = "You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command." H.mind.objectives += serve_objective - to_chat(H, "You have been Enthralled by [user]. Follow [user.p_their()] every command.") + to_chat(H, "You have been Enthralled by [user.real_name]. Follow [user.p_their()] every command.") to_chat(user, "You have successfully Enthralled [H]. If [H.p_they()] refuse[H.p_s()] to do as you say just adminhelp.") H.Stun(2) add_attack_logs(user, H, "Vampire-thralled") diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm index 09b228bdd0c..cb0c277b1e6 100644 --- a/code/game/gamemodes/wizard/artefact.dm +++ b/code/game/gamemodes/wizard/artefact.dm @@ -50,7 +50,8 @@ to_chat(H, "You already used this contract!") return used = 1 - var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, 1) + var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_wizard") + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [H.real_name]?", ROLE_WIZARD, TRUE, source = source) if(candidates.len) var/mob/C = pick(candidates) new /obj/effect/particle_effect/smoke(H.loc) @@ -60,7 +61,7 @@ switch(href_list["school"]) if("destruction") M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/projectile/magic_missile(null)) - M.mind.AddSpell(new /obj/effect/proc_holder/spell/fireball(null)) + M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/fireball(null)) to_chat(M, "Your service has not gone unrewarded, however. Studying under [H.real_name], you have learned powerful, destructive spells. You are able to cast magic missile and fireball.") if("bluespace") M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport(null)) @@ -73,7 +74,7 @@ to_chat(M, "Your service has not gone unrewarded, however. Studying under [H.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.") if("robeless") M.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null)) - M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null)) + M.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/mind_transfer(null)) to_chat(M, "Your service has not gone unrewarded, however. Studying under [H.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.") M.equip_to_slot_or_del(new /obj/item/radio/headset(M), slot_l_ear) @@ -307,7 +308,8 @@ GLOBAL_LIST_EMPTY(multiverse) if(M.assigned == assigned) M.cooldown = cooldown - var/list/candidates = pollCandidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, 1, 100) + var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_wizard") + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as the wizard apprentice of [user.real_name]?", ROLE_WIZARD, TRUE, 10 SECONDS, source = source) if(candidates.len) var/mob/C = pick(candidates) spawn_copy(C.client, get_turf(user.loc), user) @@ -834,6 +836,10 @@ GLOBAL_LIST_EMPTY(multiverse) var/wgw = sanitize(input(user, "What would you like the victim to say", "Voodoo", null) as text) target.say(wgw) log_game("[user][user.key] made [target][target.key] say [wgw] with a voodoo doll.") + log_say("Wicker doll say to [target][target.key]: [wgw]", user) + log_admin("[user][user.key] made [target][target.key] say [wgw] with a voodoo doll.") + user.create_log(SAY_LOG, "forced [target] to say [wgw] through [src].", target) + target.create_log(SAY_LOG, "was forced to say [wgw] through [src] by [user].", user) if("eyes") user.set_machine(src) user.reset_perspective(target) @@ -866,8 +872,9 @@ GLOBAL_LIST_EMPTY(multiverse) possible = list() if(!link) return - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) - if(md5(H.dna.uni_identity) in link.fingerprints) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing + if(H.stat != DEAD && (md5(H.dna.uni_identity) in link.fingerprints)) possible |= H /obj/item/voodoo/proc/GiveHint(mob/victim,force=0) diff --git a/code/game/gamemodes/wizard/raginmages.dm b/code/game/gamemodes/wizard/raginmages.dm index cc52b431249..d51b16d2cd3 100644 --- a/code/game/gamemodes/wizard/raginmages.dm +++ b/code/game/gamemodes/wizard/raginmages.dm @@ -118,7 +118,8 @@ return FALSE making_mage = TRUE - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS) + var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_wizard") + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a raging Space Wizard?", ROLE_WIZARD, TRUE, poll_time = 20 SECONDS, source = source) var/mob/dead/observer/harry = null message_admins("SWF is still pissed, sending another wizard - [max_mages - mages_made] left.") diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm index 4bda254d930..aab4318c774 100644 --- a/code/game/gamemodes/wizard/soulstone.dm +++ b/code/game/gamemodes/wizard/soulstone.dm @@ -363,7 +363,7 @@ break if(!chosen_ghost) //Failing that, we grab a ghost - var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 100) + var/list/consenting_candidates = SSghost_spawns.poll_candidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 10 SECONDS, source = /mob/living/simple_animal/shade) if(consenting_candidates.len) chosen_ghost = pick(consenting_candidates) if(!T) diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index d1695a86603..8efb04483cb 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -80,7 +80,7 @@ user.mind.spell_list.Remove(aspell) qdel(aspell) if(S) //If we created a temporary spell above, delete it now. - qdel(S) + QDEL_NULL(S) return cost * (spell_levels + 1) return -1 @@ -132,7 +132,7 @@ /datum/spellbook_entry/horseman name = "Curse of the Horseman" - spell_type = /obj/effect/proc_holder/spell/targeted/horsemask + spell_type = /obj/effect/proc_holder/spell/targeted/click/horsemask log_name = "HH" category = "Offensive" @@ -144,7 +144,7 @@ /datum/spellbook_entry/fireball name = "Fireball" - spell_type = /obj/effect/proc_holder/spell/fireball + spell_type = /obj/effect/proc_holder/spell/targeted/click/fireball log_name = "FB" category = "Offensive" @@ -257,7 +257,7 @@ /datum/spellbook_entry/mindswap name = "Mindswap" - spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer + spell_type = /obj/effect/proc_holder/spell/targeted/click/mind_transfer log_name = "MT" category = "Mobility" @@ -610,7 +610,7 @@ var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon - /datum/spellbook_entry/loadout for(var/T in entry_types) var/datum/spellbook_entry/E = new T - if(GAMEMODE_IS_WIZARD && E.is_ragin_restricted) + if(GAMEMODE_IS_RAGIN_MAGES && E.is_ragin_restricted) qdel(E) continue entries |= E @@ -877,7 +877,7 @@ return /obj/item/spellbook/oneuse/fireball - spell = /obj/effect/proc_holder/spell/fireball + spell = /obj/effect/proc_holder/spell/targeted/click/fireball spellname = "fireball" icon_state = "bookfireball" desc = "This book feels warm to the touch." @@ -910,7 +910,7 @@ user.EyeBlind(10) /obj/item/spellbook/oneuse/mindswap - spell = /obj/effect/proc_holder/spell/targeted/mind_transfer + spell = /obj/effect/proc_holder/spell/targeted/click/mind_transfer spellname = "mindswap" icon_state = "bookmindswap" desc = "This book's cover is pristine, though its pages look ragged and torn." @@ -934,8 +934,8 @@ to_chat(user, "You stare at the book some more, but there doesn't seem to be anything else to learn...") return - var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new - swapper.cast(user, stored_swap, 1) + var/obj/effect/proc_holder/spell/targeted/click/mind_transfer/swapper = new + swapper.cast(user, stored_swap) to_chat(stored_swap, "You're suddenly somewhere else... and someone else?!") to_chat(user, "Suddenly you're staring at [src] again... where are you, who are you?!") @@ -966,7 +966,7 @@ user.Weaken(20) /obj/item/spellbook/oneuse/horsemask - spell = /obj/effect/proc_holder/spell/targeted/horsemask + spell = /obj/effect/proc_holder/spell/targeted/click/horsemask spellname = "horses" icon_state = "bookhorses" desc = "This book is more horse than your mind has room for." diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index a0bb4c349af..77972b00880 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -45,9 +45,8 @@ for(var/datum/mind/wizard in wizards) log_game("[key_name(wizard)] has been selected as a Wizard") forge_wizard_objectives(wizard) - //learn_basic_spells(wizard.current) equip_wizard(wizard.current) - name_wizard(wizard.current) + INVOKE_ASYNC(src, .proc/name_wizard, wizard.current) greet_wizard(wizard) if(use_huds) update_wiz_icons_added(wizard) @@ -89,17 +88,15 @@ var/wizard_name_first = pick(GLOB.wizard_first) var/wizard_name_second = pick(GLOB.wizard_second) var/randomname = "[wizard_name_first] [wizard_name_second]" - spawn(0) - var/newname = sanitize(copytext(input(wizard_mob, "You are the Space Wizard. Would you like to change your name to something else?", "Name change", randomname) as null|text,1,MAX_NAME_LEN)) + var/newname = sanitize(copytext(input(wizard_mob, "You are the Space Wizard. Would you like to change your name to something else?", "Name change", randomname) as null|text,1,MAX_NAME_LEN)) - if(!newname) - newname = randomname + if(!newname) + newname = randomname - wizard_mob.real_name = newname - wizard_mob.name = newname - if(wizard_mob.mind) - wizard_mob.mind.name = newname - return + wizard_mob.real_name = newname + wizard_mob.name = newname + if(wizard_mob.mind) + wizard_mob.mind.name = newname /datum/game_mode/proc/greet_wizard(var/datum/mind/wizard, var/you_are=1) addtimer(CALLBACK(wizard.current, /mob/.proc/playsound_local, null, 'sound/ambience/antag/ragesmages.ogg', 100, 0), 30) diff --git a/code/game/gamemodes/wizard/wizloadouts.dm b/code/game/gamemodes/wizard/wizloadouts.dm index c050b27f273..518b9a68ca8 100644 --- a/code/game/gamemodes/wizard/wizloadouts.dm +++ b/code/game/gamemodes/wizard/wizloadouts.dm @@ -18,7 +18,7 @@ Care should be taken in hiding the item you choose as your phylactery after using Bind Soul, as you cannot revive if it destroyed or too far from your body!

    \ Provides Bind Soul, Ethereal Jaunt, Fireball, Rod Form, Disable Tech, and Greater Forcewall." log_name = "DL" - spells_path = list(/obj/effect/proc_holder/spell/targeted/lichdom, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt, /obj/effect/proc_holder/spell/fireball, \ + spells_path = list(/obj/effect/proc_holder/spell/targeted/lichdom, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt, /obj/effect/proc_holder/spell/targeted/click/fireball, \ /obj/effect/proc_holder/spell/targeted/rod_form, /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech, /obj/effect/proc_holder/spell/targeted/forcewall/greater) is_ragin_restricted = TRUE diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index d5a59a36872..ac533f0b7ef 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -426,6 +426,7 @@ //gets the alt title, failing that the actual job rank //this is unused +// THEN WHY IS IT STILL HERE?? -AA07, 2020-07-31 /obj/proc/sdsdsd() //GetJobDisplayName if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) return @@ -442,7 +443,7 @@ return "Unknown" -proc/GetIdCard(var/mob/living/carbon/human/H) +/proc/GetIdCard(var/mob/living/carbon/human/H) if(H.wear_id) var/id = H.wear_id.GetID() if(id) @@ -451,7 +452,7 @@ proc/GetIdCard(var/mob/living/carbon/human/H) var/obj/item/I = H.get_active_hand() return I.GetID() -proc/FindNameFromID(var/mob/living/carbon/human/H) +/proc/FindNameFromID(var/mob/living/carbon/human/H) ASSERT(istype(H)) var/obj/item/card/id/C = H.get_active_hand() if( istype(C) || istype(C, /obj/item/pda) ) @@ -480,7 +481,7 @@ proc/FindNameFromID(var/mob/living/carbon/human/H) if(ID) return ID.registered_name -proc/get_all_job_icons() //For all existing HUD icons +/proc/get_all_job_icons() //For all existing HUD icons return GLOB.joblist + list("Prisoner") /obj/proc/GetJobName() //Used in secHUD icon generation @@ -510,3 +511,26 @@ proc/get_all_job_icons() //For all existing HUD icons return rankName return "Unknown" //Return unknown if none of the above apply + +/proc/get_accesslist_static_data(num_min_region = REGION_GENERAL, num_max_region = REGION_COMMAND) + var/list/retval + for(var/i in num_min_region to num_max_region) + var/list/accesses = list() + var/list/available_accesses + if(i == REGION_CENTCOMM) // Override necessary, because get_region_accesses(REGION_CENTCOM) returns BOTH CC and crew accesses. + available_accesses = get_all_centcom_access() + else + available_accesses = get_region_accesses(i) + for(var/access in available_accesses) + var/access_desc = (i == REGION_CENTCOMM) ? get_centcom_access_desc(access) : get_access_desc(access) + if (access_desc) + accesses += list(list( + "desc" = replacetext(access_desc, " ", " "), + "ref" = access, + )) + retval += list(list( + "name" = get_region_accesses_name(i), + "regid" = i, + "accesses" = accesses + )) + return retval diff --git a/code/game/jobs/job/central.dm b/code/game/jobs/job/central.dm index f91460ba234..8684ca7d1b0 100644 --- a/code/game/jobs/job/central.dm +++ b/code/game/jobs/job/central.dm @@ -94,7 +94,7 @@ ) cybernetic_implants = list( /obj/item/organ/internal/cyberimp/eyes/xray, - /obj/item/organ/internal/cyberimp/brain/anti_stun, + /obj/item/organ/internal/cyberimp/brain/anti_stun/hardened, /obj/item/organ/internal/cyberimp/chest/nutriment/plus, /obj/item/organ/internal/cyberimp/arm/combat/centcom ) diff --git a/code/game/jobs/job/engineering.dm b/code/game/jobs/job/engineering.dm index 84f903dd2b1..a3333e1dc82 100644 --- a/code/game/jobs/job/engineering.dm +++ b/code/game/jobs/job/engineering.dm @@ -18,7 +18,7 @@ ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM) minimal_player_age = 21 - exp_requirements = 300 + exp_requirements = 1200 exp_type = EXP_TYPE_ENGINEERING outfit = /datum/outfit/job/chief_engineer diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm index 2d1b091a10d..b1b81e0b849 100644 --- a/code/game/jobs/job/medical.dm +++ b/code/game/jobs/job/medical.dm @@ -16,7 +16,7 @@ ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_MAINT_TUNNELS, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM) minimal_player_age = 21 - exp_requirements = 300 + exp_requirements = 1200 exp_type = EXP_TYPE_MEDICAL outfit = /datum/outfit/job/cmo diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index 684de933832..f6fdb02477b 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -18,7 +18,7 @@ ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM, ACCESS_NETWORK) minimal_player_age = 21 - exp_requirements = 300 + exp_requirements = 1200 exp_type = EXP_TYPE_SCIENCE // All science-y guys get bonuses for maxing out their tech. required_objectives = list( diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index a32656598df..6f4eb3759de 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -18,7 +18,7 @@ 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_PILOT, ACCESS_WEAPONS) minimal_player_age = 21 - exp_requirements = 300 + exp_requirements = 1200 exp_type = EXP_TYPE_SECURITY disabilities_allowed = 0 outfit = /datum/outfit/job/hos @@ -64,7 +64,7 @@ minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS) minimal_player_age = 21 exp_requirements = 600 - exp_type = EXP_TYPE_CREW + exp_type = EXP_TYPE_SECURITY outfit = /datum/outfit/job/warden /datum/outfit/job/warden diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index c510518716a..fef9c2302b3 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -32,7 +32,7 @@ minimal_player_age = 21 exp_requirements = 300 exp_type = EXP_TYPE_CREW - alt_titles = list("Android", "Robot") + alt_titles = list("Robot") /datum/job/cyborg/equip(mob/living/carbon/human/H) if(!H) diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm index 1d627f131b2..87ab2774384 100644 --- a/code/game/jobs/job/supervisor.dm +++ b/code/game/jobs/job/supervisor.dm @@ -13,7 +13,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca access = list() //See get_access() minimal_access = list() //See get_access() minimal_player_age = 30 - exp_requirements = 300 + exp_requirements = 1200 exp_type = EXP_TYPE_COMMAND disabilities_allowed = 0 outfit = /datum/outfit/job/captain @@ -33,7 +33,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca shoes = /obj/item/clothing/shoes/brown head = /obj/item/clothing/head/caphat l_ear = /obj/item/radio/headset/heads/captain/alt - glasses = /obj/item/clothing/glasses/sunglasses + glasses = /obj/item/clothing/glasses/hud/skills/sunglasses id = /obj/item/card/id/gold pda = /obj/item/pda/captain backpack_contents = list( @@ -67,7 +67,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca req_admin_notify = 1 is_command = 1 minimal_player_age = 21 - exp_requirements = 300 + exp_requirements = 1200 exp_type = EXP_TYPE_COMMAND access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, @@ -89,6 +89,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca uniform = /obj/item/clothing/under/rank/head_of_personnel shoes = /obj/item/clothing/shoes/brown head = /obj/item/clothing/head/hopcap + glasses = /obj/item/clothing/glasses/hud/skills/sunglasses l_ear = /obj/item/radio/headset/heads/hop id = /obj/item/card/id/silver pda = /obj/item/pda/heads/hop @@ -134,6 +135,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca uniform = /obj/item/clothing/under/rank/ntrep suit = /obj/item/clothing/suit/storage/ntrep shoes = /obj/item/clothing/shoes/centcom + glasses = /obj/item/clothing/glasses/hud/skills/sunglasses l_ear = /obj/item/radio/headset/heads/ntrep id = /obj/item/card/id/nanotrasen l_pocket = /obj/item/lighter/zippo/nt_rep diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm index 09328865b59..fda831242f9 100644 --- a/code/game/jobs/job/support.dm +++ b/code/game/jobs/job/support.dm @@ -458,6 +458,7 @@ total_positions = 0 spawn_positions = 0 supervisors = "the head of personnel" + department_head = list("Head of Personnel") selection_color = "#dddddd" access = list(ACCESS_MAINT_TUNNELS, ACCESS_GATEWAY, ACCESS_EVA, ACCESS_EXTERNAL_AIRLOCKS) minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_GATEWAY, ACCESS_EVA, ACCESS_EXTERNAL_AIRLOCKS) diff --git a/code/game/jobs/job/support_chaplain.dm b/code/game/jobs/job/support_chaplain.dm index b39eed344ae..bfd9c4520f5 100644 --- a/code/game/jobs/job/support_chaplain.dm +++ b/code/game/jobs/job/support_chaplain.dm @@ -26,7 +26,7 @@ /obj/item/camera/spooky = 1, /obj/item/nullrod = 1 ) - + /datum/outfit/job/chaplain/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) . = ..() @@ -77,7 +77,7 @@ new_deity = deity_name B.deity_name = new_deity - H.AddSpell(new /obj/effect/proc_holder/spell/targeted/chaplain_bless(null)) + H.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/chaplain_bless(null)) var/accepted = 0 var/outoftime = 0 diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm index f0e6f9bff42..aa9114c2963 100644 --- a/code/game/jobs/job_exp.dm +++ b/code/game/jobs/job_exp.dm @@ -224,19 +224,17 @@ GLOBAL_LIST_INIT(role_playtime_requirements, list( else return "none" -/proc/update_exp(var/mins, var/ann = 0) - if(!establish_db_connection()) - return -1 - spawn(0) - for(var/client/L in GLOB.clients) - if(L.inactivity >= (10 MINUTES)) - continue - spawn(0) - L.update_exp_client(mins, ann) - sleep(10) +/proc/update_exp(mins = 0, ann = 0) + if(!GLOB.dbcon.IsConnected()) + return + for(var/client/L in GLOB.clients) + if(L.inactivity >= (10 MINUTES)) + continue + L.update_exp_client(mins, ann) + CHECK_TICK -/client/proc/update_exp_client(var/minutes, var/announce_changes = 0) - if(!src ||!ckey) +/client/proc/update_exp_client(minutes = 0, announce_changes = 0) + if(!src || !ckey || !GLOB.dbcon.IsConnected()) return var/DBQuery/exp_read = GLOB.dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[ckey]'") if(!exp_read.Execute()) diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index 81cc54aa3c3..2f8e426e030 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -69,7 +69,7 @@ GLOBAL_LIST_INIT(supply_positions, list( "Shaft Miner" )) -GLOBAL_LIST_INIT(service_positions, (support_positions - supply_positions + list("Head of Personnel"))) +GLOBAL_LIST_INIT(service_positions, (list("Head of Personnel") + (support_positions - supply_positions))) GLOBAL_LIST_INIT(security_positions, list( diff --git a/code/game/machinery/Freezer.dm b/code/game/machinery/Freezer.dm index d9243005ead..a2a69fad357 100644 --- a/code/game/machinery/Freezer.dm +++ b/code/game/machinery/Freezer.dm @@ -104,59 +104,46 @@ to_chat(user, "Close the maintenance panel first.") return - src.ui_interact(user) + tgui_interact(user) -/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/unary/cold_sink/freezer/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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", 540, 300) - // open the new ui window + ui = new(user, src, ui_key, "GasFreezer", "Gas Cooling System", 540, 200) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) -/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["on"] = on ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) +/obj/machinery/atmospherics/unary/cold_sink/freezer/tgui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(air_contents.return_pressure()) + data["temperature"] = round(air_contents.temperature) + data["temperatureCelsius"] = round(air_contents.temperature - T0C, 1) if(air_contents.total_moles() == 0 && air_contents.temperature == 0) - data["gasTemperatureCelsius"] = 0 - data["minGasTemperature"] = round(min_temperature) - data["maxGasTemperature"] = round(T20C) - data["targetGasTemperature"] = round(current_temperature) - data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) - - 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 + data["temperatureCelsius"] = 0 + data["min"] = round(min_temperature) + data["max"] = round(T20C) + data["target"] = round(current_temperature) + data["targetCelsius"] = round(current_temperature - T0C, 1) return data -/obj/machinery/atmospherics/unary/cold_sink/freezer/Topic(href, href_list) +/obj/machinery/atmospherics/unary/cold_sink/freezer/tgui_act(action, params) if(..()) - return 1 - if(href_list["toggleStatus"]) - src.on = !src.on - update_icon() - else if(href_list["minimum"]) - current_temperature = min_temperature - else if(href_list["maximum"]) - current_temperature = T20C - else if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - src.current_temperature = min(T20C, src.current_temperature+amount) - else - src.current_temperature = max(min_temperature, src.current_temperature+amount) - src.add_fingerprint(usr) - return 1 + return + add_fingerprint(usr) + . = TRUE + + switch(action) + if("power") + on = !on + update_icon() + if("minimum") + current_temperature = min_temperature + if("maximum") + current_temperature = T20C + if("temp") + var/amount = params["temp"] + amount = text2num(amount) + current_temperature = clamp(amount, T20C, min_temperature) /obj/machinery/atmospherics/unary/cold_sink/freezer/power_change() ..() @@ -273,57 +260,46 @@ if(panel_open) to_chat(user, "Close the maintenance panel first.") return - src.ui_interact(user) + tgui_interact(user) -/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/unary/heat_reservoir/heater/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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", 540, 300) - // open the new ui window + ui = new(user, src, ui_key, "GasFreezer", "Gas Heating System", 540, 200) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) -/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["on"] = on ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) +/obj/machinery/atmospherics/unary/heat_reservoir/heater/tgui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(air_contents.return_pressure()) + data["temperature"] = round(air_contents.temperature) + data["temperatureCelsius"] = round(air_contents.temperature - T0C, 1) if(air_contents.total_moles() == 0 && air_contents.temperature == 0) - data["gasTemperatureCelsius"] = 0 - data["minGasTemperature"] = round(T20C) - data["maxGasTemperature"] = round(T20C+max_temperature) - data["targetGasTemperature"] = round(current_temperature) - data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) - - var/temp_class = "normal" - if(air_contents.temperature > (T20C+40)) - temp_class = "bad" - data["gasTemperatureClass"] = temp_class + data["temperatureCelsius"] = 0 + data["min"] = round(T20C) + data["max"] = round(T20C + max_temperature) + data["target"] = round(current_temperature) + data["targetCelsius"] = round(current_temperature - T0C, 1) return data -/obj/machinery/atmospherics/unary/heat_reservoir/heater/Topic(href, href_list) +/obj/machinery/atmospherics/unary/heat_reservoir/heater/tgui_act(action, params) if(..()) - return 1 - if(href_list["toggleStatus"]) - src.on = !src.on - update_icon() - else if(href_list["minimum"]) - current_temperature = T20C - else if(href_list["maximum"]) - current_temperature = max_temperature + T20C - else if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - src.current_temperature = min((T20C+max_temperature), src.current_temperature+amount) - else - src.current_temperature = max(T20C, src.current_temperature+amount) - src.add_fingerprint(usr) - return 1 + return + add_fingerprint(usr) + . = TRUE + + switch(action) + if("power") + on = !on + update_icon() + if("minimum") + current_temperature = T20C + if("maximum") + current_temperature = max_temperature + T20C + if("temp") + var/amount = params["temp"] + amount = text2num(amount) + current_temperature = clamp(amount, T20C, T20C + max_temperature) /obj/machinery/atmospherics/unary/heat_reservoir/heater/power_change() ..() diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index e4d0befa61e..d33518be857 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -125,7 +125,7 @@ return attack_hand(user) /obj/machinery/sleeper/attack_ghost(mob/user) - return attack_hand(user) + tgui_interact(user) /obj/machinery/sleeper/attack_hand(mob/user) if(stat & (NOPOWER|BROKEN)) @@ -135,17 +135,17 @@ to_chat(user, "Close the maintenance panel first.") return - ui_interact(user) + tgui_interact(user) -/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/sleeper/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper", 550, 770) + ui = new(user, src, ui_key, "Sleeper", "Sleeper", 550, 775) ui.open() - ui.set_auto_update(1) -/obj/machinery/sleeper/ui_data(mob/user, datum/topic_state/state) +/obj/machinery/sleeper/tgui_data(mob/user) var/data[0] + data["amounts"] = amounts data["hasOccupant"] = occupant ? 1 : 0 var/occupantData[0] var/crisis = 0 @@ -210,9 +210,13 @@ if(beaker) data["isBeakerLoaded"] = 1 if(beaker.reagents) + data["beakerMaxSpace"] = beaker.reagents.maximum_volume data["beakerFreeSpace"] = round(beaker.reagents.maximum_volume - beaker.reagents.total_volume) else + data["beakerMaxSpace"] = 0 data["beakerFreeSpace"] = 0 + else + data["isBeakerLoaded"] = FALSE var/chemicals[0] for(var/re in possible_chems) @@ -234,51 +238,52 @@ if(temp.id in occupant.reagents.overdose_list()) overdosing = 1 - // Because I don't know how to do this on the nano side pretty_amount = round(reagent_amount, 0.05) chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) data["chemicals"] = chemicals return data -/obj/machinery/sleeper/Topic(href, href_list) - if(!controls_inside && usr == occupant) - return 0 - +/obj/machinery/sleeper/tgui_act(action, params) if(..()) - return 1 - + return + if(!controls_inside && usr == occupant) + return if(panel_open) to_chat(usr, "Close the maintenance panel first.") - return 0 + return + if(stat & (NOPOWER|BROKEN)) + return - if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) - if(href_list["chemical"]) - if(occupant) - if(occupant.stat == DEAD) - to_chat(usr, "This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") - else if(occupant.health > min_health || (href_list["chemical"] in emergency_chems)) - inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"])) - else - to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") - - if(href_list["removebeaker"]) + . = TRUE + switch(action) + if("chemical") + if(!occupant) + return + if(occupant.stat == DEAD) + to_chat(usr, "This person has no life to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") + return + var/chemical = params["chemid"] + var/amount = text2num(params["amount"]) + if(!length(chemical) || amount <= 0) + return + if(occupant.health > min_health || (chemical in emergency_chems)) + inject_chemical(usr, chemical, amount) + else + to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") + if("removebeaker") remove_beaker() - - if(href_list["togglefilter"]) + if("togglefilter") toggle_filter() - - if(href_list["ejectify"]) + if("ejectify") eject() - - if(href_list["auto_eject_dead_on"]) + if("auto_eject_dead_on") auto_eject_dead = TRUE - - if(href_list["auto_eject_dead_off"]) + if("auto_eject_dead_off") auto_eject_dead = FALSE - - add_fingerprint(usr) - return 1 + else + return FALSE + add_fingerprint(usr) /obj/machinery/sleeper/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/reagent_containers/glass)) @@ -290,6 +295,7 @@ beaker = I I.forceMove(src) user.visible_message("[user] adds \a [I] to [src]!", "You add \a [I] to [src]!") + SStgui.update_uis(src) return else @@ -328,6 +334,7 @@ to_chat(M, "You feel cool air surround you. You go numb as your senses turn inward.") add_fingerprint(user) qdel(G) + SStgui.update_uis(src) return return ..() @@ -374,9 +381,11 @@ occupant = null updateUsrDialog() update_icon() + SStgui.update_uis(src) if(A == beaker) beaker = null updateUsrDialog() + SStgui.update_uis(src) /obj/machinery/sleeper/emp_act(severity) if(filtering) @@ -394,7 +403,7 @@ qdel(src) /obj/machinery/sleeper/proc/toggle_filter() - if(filtering) + if(filtering || !beaker) filtering = 0 else filtering = 1 @@ -410,26 +419,28 @@ // eject trash the occupant dropped for(var/atom/movable/A in contents - component_parts - list(beaker)) A.forceMove(loc) + SStgui.update_uis(src) -/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount) +/obj/machinery/sleeper/force_eject_occupant() + go_out() + +/obj/machinery/sleeper/proc/inject_chemical(mob/living/user, chemical, amount) if(!(chemical in possible_chems)) to_chat(user, "The sleeper does not offer that chemical!") return + if(!(amount in amounts)) + return if(occupant) if(occupant.reagents) if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) occupant.reagents.add_reagent(chemical, amount) - return else to_chat(user, "You can not inject any more of this chemical.") - return else to_chat(user, "The patient rejects the chemicals!") - return else to_chat(user, "There's no occupant in the sleeper!") - return /obj/machinery/sleeper/verb/eject() set name = "Eject Sleeper" @@ -456,6 +467,7 @@ filtering = 0 beaker.forceMove(usr.loc) beaker = null + SStgui.update_uis(src) add_fingerprint(usr) return @@ -508,6 +520,7 @@ add_fingerprint(user) if(user.pulling == L) user.stop_pulling() + SStgui.update_uis(src) return return @@ -544,6 +557,7 @@ for(var/obj/O in src) qdel(O) add_fingerprint(usr) + SStgui.update_uis(src) return return diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 1ee30b6e9d0..97ab80fc1d7 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -65,6 +65,7 @@ icon_state = "body_scanner_1" add_fingerprint(user) qdel(TYPECAST_YOUR_SHIT) + SStgui.update_uis(src) return return ..() @@ -127,12 +128,13 @@ occupant = H icon_state = "bodyscanner" add_fingerprint(user) + SStgui.update_uis(src) /obj/machinery/bodyscanner/attack_ai(user) return attack_hand(user) /obj/machinery/bodyscanner/attack_ghost(user) - return attack_hand(user) + tgui_interact(user) /obj/machinery/bodyscanner/attack_hand(user) if(stat & (NOPOWER|BROKEN)) @@ -145,7 +147,7 @@ to_chat(user, "Close the maintenance panel first.") return - ui_interact(user) + tgui_interact(user) /obj/machinery/bodyscanner/relaymove(mob/user) if(user.incapacitated()) @@ -171,6 +173,10 @@ // eject trash the occupant dropped for(var/atom/movable/A in contents - component_parts) A.forceMove(loc) + SStgui.update_uis(src) + +/obj/machinery/bodyscanner/force_eject_occupant() + go_out() /obj/machinery/bodyscanner/ex_act(severity) if(occupant) @@ -189,17 +195,16 @@ new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! qdel(src) -/obj/machinery/bodyscanner/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/bodyscanner/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "adv_med.tmpl", "Body Scanner", 690, 600) + ui = new(user, src, ui_key, "BodyScanner", "Body Scanner", 690, 600) ui.open() - ui.set_auto_update(1) -/obj/machinery/bodyscanner/ui_data(mob/user, datum/topic_state/state) +/obj/machinery/bodyscanner/tgui_data(mob/user) var/data[0] - data["occupied"] = occupant ? 1 : 0 + data["occupied"] = occupant ? TRUE : FALSE var/occupantData[0] if(occupant) @@ -233,9 +238,9 @@ occupantData["hasBorer"] = occupant.has_brain_worms() var/bloodData[0] - bloodData["hasBlood"] = 0 + bloodData["hasBlood"] = FALSE if(!(NO_BLOOD in occupant.dna.species.species_traits)) - bloodData["hasBlood"] = 1 + bloodData["hasBlood"] = TRUE bloodData["volume"] = occupant.blood_volume bloodData["percent"] = round(((occupant.blood_volume / BLOOD_VOLUME_NORMAL)*100)) bloodData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) @@ -279,19 +284,19 @@ if(E.status & ORGAN_BROKEN) organStatus["broken"] = E.broken_description if(E.is_robotic()) - organStatus["robotic"] = 1 + organStatus["robotic"] = TRUE if(E.status & ORGAN_SPLINTED) - organStatus["splinted"] = 1 + organStatus["splinted"] = TRUE if(E.status & ORGAN_DEAD) - organStatus["dead"] = 1 + organStatus["dead"] = TRUE organData["status"] = organStatus if(istype(E, /obj/item/organ/external/chest) && occupant.is_lung_ruptured()) - organData["lungRuptured"] = 1 + organData["lungRuptured"] = TRUE if(E.internal_bleeding) - organData["internalBleeding"] = 1 + organData["internalBleeding"] = TRUE extOrganData.Add(list(organData)) @@ -316,27 +321,33 @@ occupantData["blind"] = (BLINDNESS in occupant.mutations) occupantData["colourblind"] = (COLOURBLIND in occupant.mutations) - occupantData["nearsighted"] = (NEARSIGHTED in occupant.mutations) + occupantData["nearsighted"] = (NEARSIGHTED in occupant.mutations) data["occupant"] = occupantData return data -/obj/machinery/bodyscanner/Topic(href, href_list) +/obj/machinery/bodyscanner/tgui_act(action, params) if(..()) - return 1 + return + if(stat & (NOPOWER|BROKEN)) + return - if(href_list["ejectify"]) - eject() - - if(href_list["print_p"]) - visible_message("[src] rattles and prints out a sheet of paper.") - var/obj/item/paper/P = new /obj/item/paper(loc) - playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - P.info = "
    Body Scan - [href_list["name"]]

    " - P.info += "Time of scan: [station_time_timestamp()]

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

    Notes:
    " - P.name = "Body Scan - [href_list["name"]]" + . = TRUE + switch(action) + if("ejectify") + eject() + if("print_p") + visible_message("[src] rattles and prints out a sheet of paper.") + var/obj/item/paper/P = new /obj/item/paper(loc) + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + var/name = occupant ? occupant.name : "Unknown" + P.info = "
    Body Scan - [name]

    " + P.info += "Time of scan: [station_time_timestamp()]

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

    Notes:
    " + P.name = "Body Scan - [name]" + else + return FALSE /obj/machinery/bodyscanner/proc/generate_printing_text() var/dat = "" diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 9fda2cfe45c..951f2a1e9cf 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -222,6 +222,7 @@ first_run() /obj/machinery/alarm/Destroy() + SStgui.close_uis(wires) GLOB.air_alarms -= src if(SSradio) SSradio.remove_object(src, frequency) @@ -314,7 +315,7 @@ temperature_dangerlevel ) - if(old_danger_level!=danger_level) + if(old_danger_level != danger_level) apply_danger_level() if(mode == AALARM_MODE_REPLACEMENT && environment_pressure < ONE_ATMOSPHERE * 0.05) @@ -534,28 +535,35 @@ "checks"= 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) +/obj/machinery/alarm/proc/apply_danger_level() + var/new_area_danger_level = ATMOS_ALARM_NONE + for(var/obj/machinery/alarm/AA in alarm_area) + if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted) + new_area_danger_level = max(new_area_danger_level, AA.danger_level) + if(alarm_area.atmosalert(new_area_danger_level, src)) //if area was in normal state or if area was in alert state + post_alert(new_area_danger_level) update_icon() /obj/machinery/alarm/proc/post_alert(alert_level) + if(!report_danger_level) + return 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["zone"] = get_area_name(src, TRUE) alert_signal.data["type"] = "Atmospheric" - if(alert_level==2) + if(alert_level == ATMOS_ALARM_DANGER) alert_signal.data["alert"] = "severe" - else if(alert_level==1) + else if(alert_level == ATMOS_ALARM_WARNING) alert_signal.data["alert"] = "minor" - else if(alert_level==0) + else if(alert_level == ATMOS_ALARM_NONE) alert_signal.data["alert"] = "clear" frequency.post_signal(src, alert_signal) @@ -889,14 +897,14 @@ if(href_list["atmos_alarm"]) if(alarm_area.atmosalert(ATMOS_ALARM_DANGER, src)) - apply_danger_level(ATMOS_ALARM_DANGER) + post_alert(ATMOS_ALARM_DANGER) alarmActivated = 1 update_icon() return 1 if(href_list["atmos_reset"]) if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE)) - apply_danger_level(ATMOS_ALARM_NONE) + post_alert(ATMOS_ALARM_NONE) alarmActivated = 0 update_icon() return 1 @@ -951,7 +959,7 @@ to_chat(user, "It does nothing") return else - if(allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) + if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN)) locked = !locked to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.") updateUsrDialog() @@ -1030,7 +1038,7 @@ . = TRUE if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return - if(wires.wires_status == 31) // all wires cut + if(wires.is_all_cut()) // all wires cut var/obj/item/stack/cable_coil/new_coil = new /obj/item/stack/cable_coil(user.drop_location()) new_coil.amount = 5 buildstage = AIR_ALARM_BUILDING @@ -1076,6 +1084,15 @@ if(buildstage < 1) . += "The circuit is missing." +/obj/machinery/alarm/proc/unshort_callback() + if(shorted) + shorted = FALSE + update_icon() + +/obj/machinery/alarm/proc/enable_ai_control_callback() + if(aidisabled) + aidisabled = FALSE + /obj/machinery/alarm/all_access name = "all-access air alarm" desc = "This particular atmos control unit appears to have no access restrictions." diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index e927415c759..b950f3d51a3 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -11,6 +11,7 @@ list("name" = "\[SPECIAL\]", "icon" = "whiters") ) possibleseccolor = list( // no point in having the N2O and "whiters" ones in these lists + list("name" = "\[None\]", "icon" = "none"), list("name" = "\[N2\]", "icon" = "red-c"), list("name" = "\[O2\]", "icon" = "blue-c"), list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c"), @@ -19,6 +20,7 @@ list("name" = "\[CAUTION\]", "icon" = "yellow-c") ) possibletertcolor = list( + list("name" = "\[None\]", "icon" = "none"), list("name" = "\[N2\]", "icon" = "red-c-1"), list("name" = "\[O2\]", "icon" = "blue-c-1"), list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-1"), @@ -27,6 +29,7 @@ list("name" = "\[CAUTION\]", "icon" = "yellow-c-1") ) possiblequartcolor = list( + list("name" = "\[None\]", "icon" = "none"), list("name" = "\[N2\]", "icon" = "red-c-2"), list("name" = "\[O2\]", "icon" = "blue-c-2"), list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-2"), @@ -34,12 +37,6 @@ list("name" = "\[Air\]", "icon" = "grey-c-2"), list("name" = "\[CAUTION\]", "icon" = "yellow-c-2") ) - - possibledecals = list( //var that stores all possible decals, used by ui - list("name" = "Low temperature canister", "icon" = "cold"), - list("name" = "High temperature canister", "icon" = "hot"), - list("name" = "Plasma containing canister", "icon" = "plasma") - ) GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) /obj/machinery/portable_atmospherics/canister @@ -52,22 +49,17 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) max_integrity = 250 integrity_failure = 100 - var/menu = 0 - //used by nanoui: 0 = main menu, 1 = relabel - var/valve_open = 0 var/release_pressure = ONE_ATMOSPHERE var/list/canister_color //variable that stores colours - var/list/decals //list that stores the decals + var/list/color_index // list which stores tgui color indexes for the recoloring options, to enable previously-set colors to show up right //lists for check_change() var/list/oldcolor - var/list/olddecals //passed to the ui to render the color lists var/list/colorcontainer - var/list/possibledecals var/can_label = 1 var/filled = 0.5 @@ -82,18 +74,12 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) /obj/machinery/portable_atmospherics/canister/New() ..() canister_color = list( - "prim" = "yellow", - "sec" = "none", - "ter" = "none", - "quart" = "none") + "prim" = "yellow", + "sec" = "none", + "ter" = "none", + "quart" = "none" + ) oldcolor = new /list() - decals = list("cold" = 0, "hot" = 0, "plasma" = 0) - colorcontainer = list() - possibledecals = list() - update_icon() - -/obj/machinery/portable_atmospherics/canister/proc/init_data_vars() - //passed to the ui to render the color lists colorcontainer = list( "prim" = list( "options" = GLOB.canister_icon_container.possiblemaincolor, @@ -112,26 +98,8 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) "name" = "Quaternary color", ) ) - - //var/anycolor used by the nanoUI, 0: no color applied. 1: color applied - for(var/C in colorcontainer) - if(C == "prim") continue - var/list/L = colorcontainer[C] - if(!(canister_color[C]) || (canister_color[C] == "none")) - L.Add(list("anycolor" = 0)) - else - L.Add(list("anycolor" = 1)) - colorcontainer[C] = L - - possibledecals = list() - - var/i - var/list/L = GLOB.canister_icon_container.possibledecals - for(i=1;i<=L.len;i++) - var/list/LL = L[i] - LL = LL.Copy() //make sure we don't edit the datum list - LL.Add(list("active" = decals[LL["icon"]])) //"active" used by nanoUI - possibledecals.Add(LL) + color_index = list() + update_icon() /obj/machinery/portable_atmospherics/canister/proc/check_change() var/old_flag = update_flag @@ -155,10 +123,6 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) update_flag |= 64 oldcolor = canister_color.Copy() - if(list2params(olddecals) != list2params(decals)) - update_flag |= 128 - olddecals = decals.Copy() - if(update_flag == old_flag) return 1 else @@ -174,17 +138,16 @@ update_flag 16 = tank_pressure < 15*ONE_ATMOS 32 = tank_pressure go boom. 64 = colors -128 = decals -(note: colors and decals has to be applied every icon update) +(note: colors has to be applied every icon update) */ - if(src.destroyed) - src.overlays = 0 - src.icon_state = text("[]-1", src.canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever. + if(destroyed) + overlays = 0 + icon_state = text("[]-1", canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever. return - if(icon_state != src.canister_color["prim"]) - icon_state = src.canister_color["prim"] + if(icon_state != canister_color["prim"]) + icon_state = canister_color["prim"] if(check_change()) //Returns 1 if no change needed to icons. return @@ -192,14 +155,12 @@ update_flag overlays.Cut() for(var/C in canister_color) - if(C == "prim") continue - if(canister_color[C] == "none") continue + if(C == "prim") + continue + if(canister_color[C] == "none") + continue overlays.Add(canister_color[C]) - for(var/D in decals) - if(decals[D]) - overlays.Add("decal-" + D) - if(update_flag & 1) overlays += "can-open" if(update_flag & 2) @@ -213,35 +174,9 @@ update_flag else if(update_flag & 32) overlays += "can-o3" - update_flag &= ~196 //the flags 128 and 64 represent change, not states. As such, we have to reset them to be able to detect a change on the next go. + update_flag &= ~68 //the flag 64 represents change, not states. As such, we have to reset them to be able to detect a change on the next go. return -//template modification exploit prevention, used in Topic() -/obj/machinery/portable_atmospherics/canister/proc/is_a_color(var/inputVar, var/checkColor = "all") - if(checkColor == "prim" || checkColor == "all") - for(var/list/L in GLOB.canister_icon_container.possiblemaincolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "sec" || checkColor == "all") - for(var/list/L in GLOB.canister_icon_container.possibleseccolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "ter" || checkColor == "all") - for(var/list/L in GLOB.canister_icon_container.possibletertcolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "quart" || checkColor == "all") - for(var/list/L in GLOB.canister_icon_container.possiblequartcolor) - if(L["icon"] == inputVar) - return 1 - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/is_a_decal(var/inputVar) - for(var/list/L in GLOB.canister_icon_container.possibledecals) - if(L["icon"] == inputVar) - return 1 - return 0 - /obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) ..() if(exposed_temperature > temperature_resistance) @@ -271,7 +206,7 @@ update_flag stat |= BROKEN density = FALSE - playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) + playsound(loc, 'sound/effects/spray.ogg', 10, TRUE, -3) update_icon() if(holding) @@ -297,7 +232,7 @@ update_flag var/transfer_moles = 0 if((air_contents.temperature > 0) && (pressure_delta > 0)) - transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) + transfer_moles = pressure_delta * environment.volume / (air_contents.temperature * R_IDEAL_GAS_EQUATION) //Actually transfer the gas var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) @@ -307,7 +242,7 @@ update_flag else loc.assume_air(removed) air_update_turf() - src.update_icon() + update_icon() if(air_contents.return_pressure() < 1) @@ -315,20 +250,20 @@ update_flag else can_label = 0 - src.updateDialog() + updateDialog() return /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() + var/datum/gas_mixture/GM = 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() + var/datum/gas_mixture/GM = return_air() if(GM && GM.volume>0) return GM.return_pressure() return 0 @@ -343,141 +278,112 @@ update_flag else if(valve_open && holding) investigate_log("[key_name(user)] started a transfer into [holding].
    ", "atmos") -/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) +/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user) + add_hiddenprint(user) + return attack_hand(user) -/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user as mob) - return src.ui_interact(user) +/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user) + return tgui_interact(user) -/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) - return src.ui_interact(user) +/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user) + return tgui_interact(user) -/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state) - if(src.destroyed) - return - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/portable_atmospherics/canister/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_physical_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, state = state) - // open the new ui window + ui = new(user, src, ui_key, "Canister", name, 600, 350, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/portable_atmospherics/canister/ui_data(mob/user, datum/topic_state/state) - init_data_vars() //set up var/colorcontainer and var/possibledecals - - // this is the data which will be sent to the ui - var/data[0] - data["name"] = name - data["menu"] = menu ? 1 : 0 - data["canLabel"] = can_label ? 1 : 0 - data["canister_color"] = canister_color - data["colorContainer"] = colorcontainer.Copy() - colorcontainer.Cut() - data["possibleDecals"] = possibledecals.Copy() - possibledecals.Cut() +/obj/machinery/portable_atmospherics/canister/tgui_data() + var/data = list() 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["defaultReleasePressure"] = ONE_ATMOSPHERE + data["minReleasePressure"] = round(ONE_ATMOSPHERE / 10) + data["maxReleasePressure"] = round(ONE_ATMOSPHERE * 10) data["valveOpen"] = valve_open ? 1 : 0 - + data["name"] = name + data["canLabel"] = can_label ? 1 : 0 + data["colorContainer"] = colorcontainer.Copy() + data["color_index"] = color_index data["hasHoldingTank"] = holding ? 1 : 0 if(holding) data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure())) - return data -/obj/machinery/portable_atmospherics/canister/Topic(href, href_list) +/obj/machinery/portable_atmospherics/canister/tgui_act(action, params) if(..()) - return 1 - - if(href_list["choice"] == "menu") - menu = text2num(href_list["mode_target"]) - - if(href_list["toggle"]) - var/logmsg - if(valve_open) - if(holding) - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
    " - else - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air
    " - else - if(holding) - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding]
    " - else - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air
    " - if(air_contents.toxins > 0) - message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]") - if(air_contents.sleeping_agent > 0) - message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]") - investigate_log(logmsg, "atmos") - release_log += logmsg - valve_open = !valve_open - - if(href_list["remove_tank"]) - if(holding) - if(valve_open) - valve_open = 0 - release_log += "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
    " - holding.loc = 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["rename"]) - if(can_label) - var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null,1,MAX_NAME_LEN)) - if(can_label) //Exploit prevention - if(T) - name = T + return + var/can_min_release_pressure = round(ONE_ATMOSPHERE / 10) + var/can_max_release_pressure = round(ONE_ATMOSPHERE * 10) + . = TRUE + switch(action) + if("relabel") + if(can_label) + var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null, 1, MAX_NAME_LEN)) + if(can_label) //Exploit prevention + if(T) + name = T + else + name = "canister" else - name = "canister" + to_chat(usr, "As you attempted to rename it the pressure rose!") + . = FALSE + if("pressure") + var/pressure = params["pressure"] + if(pressure == "reset") + pressure = ONE_ATMOSPHERE + else if(pressure == "min") + pressure = can_min_release_pressure + else if(pressure == "max") + pressure = can_max_release_pressure + else if(pressure == "input") + pressure = input("New release pressure ([can_min_release_pressure]-[can_max_release_pressure] kPa):", name, release_pressure) as num|null + if(isnull(pressure)) + . = FALSE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + if(.) + release_pressure = clamp(round(pressure), can_min_release_pressure, can_max_release_pressure) + investigate_log("was set to [release_pressure] kPa by [key_name(usr)].", "atmos") + if("valve") + var/logmsg + valve_open = !valve_open + if(valve_open) + logmsg = "Valve was opened by [key_name(usr)], starting a transfer into the [holding || "air"].
    " + if(!holding) + logmsg = "Valve was opened by [key_name(usr)], starting a transfer into the air.
    " + if(air_contents.toxins > 0) + message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]") + if(air_contents.sleeping_agent > 0) + message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]") else - to_chat(usr, "As you attempted to rename it the pressure rose!") - - if(href_list["choice"] == "Primary color") - if(is_a_color(href_list["icon"],"prim")) - canister_color["prim"] = href_list["icon"] - if(href_list["choice"] == "Secondary color") - if(href_list["icon"] == "none") - canister_color["sec"] = "none" - else if(is_a_color(href_list["icon"],"sec")) - canister_color["sec"] = href_list["icon"] - if(href_list["choice"] == "Tertiary color") - if(href_list["icon"] == "none") - canister_color["ter"] = "none" - else if(is_a_color(href_list["icon"],"ter")) - canister_color["ter"] = href_list["icon"] - if(href_list["choice"] == "Quaternary color") - if(href_list["icon"] == "none") - canister_color["quart"] = "none" - else if(is_a_color(href_list["icon"],"quart")) - canister_color["quart"] = href_list["icon"] - - if(href_list["choice"] == "decals") - if(is_a_decal(href_list["icon"])) - decals[href_list["icon"]] = (decals[href_list["icon"]] == 0) - - src.add_fingerprint(usr) + logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding || "air"].
    " + investigate_log(logmsg, "atmos") + release_log += logmsg + if("eject") + if(holding) + if(valve_open) + valve_open = FALSE + release_log += "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
    " + replace_tank(usr, FALSE) + if("recolor") + if(can_label) + var/ctype = params["ctype"] + var/cnum = text2num(params["nc"]) + if(isnull(colorcontainer[ctype])) + message_admins("[key_name_admin(usr)] passed an invalid ctype var to a canister.") + return + var/newcolor = sanitize_integer(cnum, 0, length(colorcontainer[ctype]["options"])) + color_index[ctype] = newcolor + newcolor++ // javascript starts arrays at 0, byond (for some reason) starts them at 1, this converts JS values to byond values + canister_color[ctype] = colorcontainer[ctype]["options"][newcolor]["icon"] + add_fingerprint(usr) update_icon() - return 1 - /obj/machinery/portable_atmospherics/canister/toxins name = "Canister \[Toxin (Plasma)\]" @@ -513,19 +419,18 @@ update_flag ..() canister_color["prim"] = "orange" - decals["plasma"] = 1 - src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + air_contents.toxins = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 /obj/machinery/portable_atmospherics/canister/oxygen/New() ..() canister_color["prim"] = "blue" - src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + air_contents.oxygen = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 /obj/machinery/portable_atmospherics/canister/sleeping_agent/New() @@ -534,25 +439,25 @@ update_flag canister_color["prim"] = "redws" air_contents.sleeping_agent = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 /obj/machinery/portable_atmospherics/canister/nitrogen/New() ..() canister_color["prim"] = "red" - src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + air_contents.nitrogen = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 /obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() ..() canister_color["prim"] = "black" - src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + air_contents.carbon_dioxide = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 @@ -560,17 +465,17 @@ update_flag ..() canister_color["prim"] = "grey" - src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + air_contents.oxygen = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) + air_contents.nitrogen = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - src.update_icon() + update_icon() return 1 /obj/machinery/portable_atmospherics/canister/custom_mix/New() ..() canister_color["prim"] = "whiters" - src.update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD + update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD return 1 /obj/machinery/portable_atmospherics/canister/welder_act(mob/user, obj/item/I) diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 4d54b162466..8198976bc14 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -1,21 +1,26 @@ + +/// The maximum `target_pressure` you can set on the pump. Equates to about 1013.25 kPa. +#define MAX_TARGET_PRESSURE 10 * ONE_ATMOSPHERE +/// The pump will be siphoning gas. +#define DIRECTION_IN 0 +/// The pump will be pumping gas out. +#define DIRECTION_OUT 1 + /obj/machinery/portable_atmospherics/pump name = "Portable Air Pump" - icon = 'icons/obj/atmos.dmi' icon_state = "psiphon:0" - density = 1 - - var/on = 0 - var/direction_out = 0 //0 = siphoning, 1 = releasing - var/target_pressure = 100 - - var/pressuremin = 0 - var/pressuremax = 10 * ONE_ATMOSPHERE - + density = TRUE volume = 1000 + /// If the pump is turned on or off. + var/on = FALSE + /// The direction the pump is operating in. This should be either `DIRECTION_IN` or `DIRECTION_OUT`. + var/direction = DIRECTION_IN + /// The desired pressure the pump should be outputting, either into the atmosphere, or into a holding tank. + var/target_pressure = 101.325 /obj/machinery/portable_atmospherics/pump/update_icon() - src.overlays = 0 + overlays = 0 if(on) icon_state = "psiphon:1" @@ -39,7 +44,7 @@ on = !on if(prob(100/severity)) - direction_out = !direction_out + direction = !direction target_pressure = rand(0,1300) update_icon() @@ -54,7 +59,7 @@ environment = holding.air_contents else environment = loc.return_air() - if(direction_out) + if(direction == DIRECTION_OUT) var/pressure_delta = target_pressure - environment.return_pressure() //Can not have a pressure delta that would cause environment pressure > tank pressure @@ -87,9 +92,7 @@ air_update_turf() air_contents.merge(removed) - //src.update_icon() - src.updateDialog() return /obj/machinery/portable_atmospherics/pump/return_air() @@ -102,72 +105,78 @@ if(on) on = FALSE update_icon() - else if(on && holding && direction_out) + else if(on && holding && direction == DIRECTION_OUT) investigate_log("[key_name(user)] started a transfer into [holding].
    ", "atmos") -/obj/machinery/portable_atmospherics/pump/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) +/obj/machinery/portable_atmospherics/pump/attack_ai(mob/user) + add_hiddenprint(user) + return attack_hand(user) -/obj/machinery/portable_atmospherics/pump/attack_ghost(var/mob/user as mob) - return src.attack_hand(user) +/obj/machinery/portable_atmospherics/pump/attack_ghost(mob/user) + return attack_hand(user) -/obj/machinery/portable_atmospherics/pump/attack_hand(var/mob/user as mob) - ui_interact(user) +/obj/machinery/portable_atmospherics/pump/attack_hand(mob/user) + tgui_interact(user) -/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/portable_atmospherics/pump/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, "portpump.tmpl", "Portable Pump", 480, 400, state = state) - // open the new ui window + ui = new(user, src, ui_key, "PortablePump", "Portable Pump", 434, 377, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) + ui.set_autoupdate(TRUE) -/obj/machinery/portable_atmospherics/pump/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state) - var/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["on"] = on ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 +/obj/machinery/portable_atmospherics/pump/tgui_data(mob/user) + var/list/data = list( + "on" = on, + "direction" = direction, + "port_connected" = connected_port ? TRUE : FALSE, + "max_target_pressure" = MAX_TARGET_PRESSURE, + "target_pressure" = round(target_pressure, 0.001), + "tank_pressure" = air_contents.return_pressure() > 0 ? round(air_contents.return_pressure(), 0.001) : 0 + ) if(holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) + data["has_holding_tank"] = TRUE + data["holding_tank"] = list("name" = holding.name, "tank_pressure" = holding.air_contents.return_pressure() > 0 ? round(holding.air_contents.return_pressure(), 0.001) : 0) + else + data["has_holding_tank"] = FALSE return data -/obj/machinery/portable_atmospherics/pump/Topic(href, href_list) +/obj/machinery/portable_atmospherics/pump/tgui_act(action, list/params) if(..()) - return 1 + return - if(href_list["power"]) - on = !on - if(on && direction_out) - investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") - update_icon() + switch(action) + if("power") + on = !on + if(on && direction == DIRECTION_OUT) + investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") + update_icon() + return TRUE - if(href_list["direction"]) - direction_out = !direction_out - if(on && holding) - investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") + if("set_direction") + if(text2num(params["direction"]) == DIRECTION_IN) + direction = DIRECTION_IN + else + direction = DIRECTION_OUT + if(on && holding) + investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") + return TRUE - if(href_list["remove_tank"]) - if(holding) - on = FALSE - holding.loc = loc - holding = null - update_icon() + if("remove_tank") + if(holding) + on = FALSE + holding.forceMove(get_turf(src)) + holding = null + update_icon() + return TRUE - if(href_list["pressure_adj"]) - var/diff = text2num(href_list["pressure_adj"]) - target_pressure = clamp(target_pressure+diff, pressuremin, pressuremax) - update_icon() + if("set_pressure") + target_pressure = clamp(text2num(params["pressure"]), 0, MAX_TARGET_PRESSURE) + return TRUE - src.add_fingerprint(usr) + add_fingerprint(usr) + +#undef MAX_TARGET_PRESSURE +#undef DIRECTION_IN +#undef DIRECTION_OUT diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 7af7751ff2c..6bfb5e07274 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -1,18 +1,18 @@ + +#define MAX_RATE 10 * ONE_ATMOSPHERE + /obj/machinery/portable_atmospherics/scrubber name = "Portable Air Scrubber" - icon = 'icons/obj/atmos.dmi' icon_state = "pscrubber:0" - density = 1 - - var/on = 0 - var/volume_rate = 800 - var/widenet = 0 //is this scrubber acting on the 3x3 area around it. - + density = TRUE volume = 750 - - var/minrate = 0//probably useless, but whatever - var/maxrate = 10 * ONE_ATMOSPHERE + /// Whether the scrubber is switched on or off. + var/on = FALSE + /// The volume of gas that can be scrubbed every time `process_atmos()` is called (0.5 seconds). + var/volume_rate = 101.325 + /// Is this scrubber acting on the 3x3 area around it. + var/widenet = FALSE /obj/machinery/portable_atmospherics/scrubber/emp_act(severity) if(stat & (BROKEN|NOPOWER)) @@ -26,7 +26,7 @@ ..(severity) /obj/machinery/portable_atmospherics/scrubber/update_icon() - src.overlays = 0 + overlays = 0 if(on) icon_state = "pscrubber:1" @@ -53,7 +53,7 @@ for(var/turf/simulated/tile in T.GetAtmosAdjacentTurfs(alldir=1)) scrub(tile) -/obj/machinery/portable_atmospherics/scrubber/proc/scrub(var/turf/simulated/tile) +/obj/machinery/portable_atmospherics/scrubber/proc/scrub(turf/simulated/tile) var/datum/gas_mixture/environment if(holding) environment = holding.air_contents @@ -99,62 +99,62 @@ /obj/machinery/portable_atmospherics/scrubber/return_air() return air_contents -/obj/machinery/portable_atmospherics/scrubber/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) +/obj/machinery/portable_atmospherics/scrubber/attack_ai(mob/user) + add_hiddenprint(user) + return attack_hand(user) -/obj/machinery/portable_atmospherics/scrubber/attack_ghost(var/mob/user as mob) - return src.attack_hand(user) +/obj/machinery/portable_atmospherics/scrubber/attack_ghost(mob/user) + return attack_hand(user) -/obj/machinery/portable_atmospherics/scrubber/attack_hand(var/mob/user as mob) - ui_interact(user) +/obj/machinery/portable_atmospherics/scrubber/attack_hand(mob/user) + tgui_interact(user) return -/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/portable_atmospherics/scrubber/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, "portscrubber.tmpl", "Portable Scrubber", 480, 400, state = state) - // open the new ui window + ui = new(user, src, ui_key, "PortableScrubber", "Portable Scrubber", 433, 346, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) + ui.set_autoupdate(TRUE) -/obj/machinery/portable_atmospherics/scrubber/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state) - var/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["on"] = on ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 +/obj/machinery/portable_atmospherics/scrubber/tgui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state) + var/list/data = list( + "on" = on, + "port_connected" = connected_port ? TRUE : FALSE, + "max_rate" = MAX_RATE, + "rate" = round(volume_rate, 0.001), + "tank_pressure" = air_contents.return_pressure() > 0 ? round(air_contents.return_pressure(), 0.001) : 0 + ) if(holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) + data["has_holding_tank"] = TRUE + data["holding_tank"] = list("name" = holding.name, "tank_pressure" = holding.air_contents.return_pressure() > 0 ? round(holding.air_contents.return_pressure(), 0.001) : 0) + else + data["has_holding_tank"] = FALSE return data -/obj/machinery/portable_atmospherics/scrubber/Topic(href, href_list) +/obj/machinery/portable_atmospherics/scrubber/tgui_act(action, list/params) if(..()) - return 1 + return - if(href_list["power"]) - on = !on - update_icon() + switch(action) + if("power") + on = !on + update_icon() + return TRUE - if(href_list["remove_tank"]) - if(holding) - holding.loc = loc - holding = null + if("remove_tank") + if(holding) + holding.forceMove(get_turf(src)) + holding = null + update_icon() + return TRUE - if(href_list["volume_adj"]) - var/diff = text2num(href_list["volume_adj"]) - volume_rate = clamp(volume_rate+diff, minrate, maxrate) + if("set_rate") + volume_rate = clamp(text2num(params["rate"]), 0, MAX_RATE) + return TRUE - src.add_fingerprint(usr) + add_fingerprint(usr) /obj/machinery/portable_atmospherics/scrubber/huge name = "Huge Air Scrubber" @@ -175,36 +175,38 @@ name = "[name] (ID [id])" -/obj/machinery/portable_atmospherics/scrubber/huge/attack_hand(var/mob/user as mob) +/obj/machinery/portable_atmospherics/scrubber/huge/attack_hand(mob/user) to_chat(usr, "You can't directly interact with this machine. Use the area atmos computer.") /obj/machinery/portable_atmospherics/scrubber/huge/update_icon() - src.overlays = 0 + overlays = 0 if(on) icon_state = "scrubber:1" else icon_state = "scrubber:0" -/obj/machinery/portable_atmospherics/scrubber/huge/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - if(istype(W, /obj/item/wrench)) - if(stationary) - to_chat(user, "The bolts are too tight for you to unscrew!") - return - if(on) - to_chat(user, "Turn it off first!") - return - - anchored = !anchored - playsound(loc, W.usesound, 50, 1) - to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") - return - +/obj/machinery/portable_atmospherics/scrubber/huge/attackby(obj/item/W, mob/user, params) if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) atmosanalyzer_scan(air_contents, user) return return ..() +/obj/machinery/portable_atmospherics/scrubber/huge/wrench_act(mob/user, obj/item/I) + . = TRUE + if(stationary) + to_chat(user, "The bolts are too tight for you to unscrew!") + return + if(on) + to_chat(user, "Turn it off first!") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + anchored = !anchored + to_chat(user, "You [anchored ? "wrench" : "unwrench"] [src].") + /obj/machinery/portable_atmospherics/scrubber/huge/stationary name = "Stationary Air Scrubber" stationary = 1 + +#undef MAX_RATE diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index c22922e4d14..3fffc598334 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -24,7 +24,7 @@ use_power = IDLE_POWER_USE idle_power_usage = 10 active_power_usage = 100 - var/busy = 0 + var/busy = FALSE var/prod_coeff var/datum/wires/autolathe/wires = null @@ -33,7 +33,7 @@ var/list/datum/design/matching_designs var/temp_search var/selected_category - var/screen = 1 + var/list/recipiecache = list() var/list/categories = list("Tools", "Electronics", "Construction", "Communication", "Security", "Machinery", "Medical", "Miscellaneous", "Dinnerware", "Imported") @@ -65,6 +65,7 @@ RefreshParts() /obj/machinery/autolathe/Destroy() + SStgui.close_uis(wires) QDEL_NULL(wires) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) materials.retrieve_all() @@ -78,63 +79,121 @@ if(panel_open) wires.Interact(user) else - ui_interact(user) + tgui_interact(user) -/obj/machinery/autolathe/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/autolathe/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "autolathe.tmpl", name, 800, 550) + ui = new(user, src, ui_key, "Autolathe", name, 750, 700, master_ui, state) ui.open() -/obj/machinery/autolathe/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + +/obj/machinery/autolathe/tgui_static_data(mob/user) + var/list/data = list() + data["categories"] = categories + if(!recipiecache.len) + var/list/recipes = list() + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + var/list/cost_list = design_cost_data(D) + var/list/matreq = list() + for(var/list/x in cost_list) + if(!x["amount"]) + continue + if(x["name"] == "metal") // Do not use MAT_METAL or MAT_GLASS here. + matreq["metal"] = x["amount"] + if(x["name"] == "glass") + matreq["glass"] = x["amount"] + var/obj/item/I = D.build_path + var/maxmult = 1 + if(ispath(D.build_path, /obj/item/stack)) + maxmult = D.maxstack + recipes.Add(list(list( + "name" = D.name, + "category" = D.category, + "uid" = D.UID(), + "requirements" = matreq, + "hacked" = ("hacked" in D.category) ? TRUE : FALSE, + "max_multiplier" = maxmult, + "image" = "[icon2base64(icon(initial(I.icon), initial(I.icon_state), SOUTH, 1))]" + ))) + recipiecache = recipes + data["recipes"] = recipiecache + return data + +/obj/machinery/autolathe/tgui_data(mob/user) + var/list/data = list() //..() var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - var/data[0] - data["screen"] = screen data["total_amount"] = materials.total_amount data["max_amount"] = materials.max_amount + data["fill_percent"] = round((materials.total_amount / materials.max_amount) * 100) data["metal_amount"] = materials.amount(MAT_METAL) data["glass_amount"] = materials.amount(MAT_GLASS) - switch(screen) - if(AUTOLATHE_MAIN_MENU) - data["uid"] = UID() - data["categories"] = categories - if(AUTOLATHE_CATEGORY_MENU) - data["selected_category"] = selected_category - var/list/designs = list() - data["designs"] = designs - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(!(selected_category in D.category)) - continue - var/list/design = list() - designs[++designs.len] = design - design["name"] = D.name - design["id"] = D.id - design["disabled"] = disabled || !can_build(D) ? "disabled" : null - if(ispath(D.build_path, /obj/item/stack)) - design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) - else - design["max_multiplier"] = null - design["materials"] = design_cost_data(D) - if(AUTOLATHE_SEARCH_MENU) - data["search"] = temp_search - var/list/designs = list() - data["designs"] = designs - for(var/datum/design/D in matching_designs) - var/list/design = list() - designs[++designs.len] = design - design["name"] = D.name - design["id"] = D.id - design["disabled"] = disabled || !can_build(D) ? "disabled" : null - if(ispath(D.build_path, /obj/item/stack)) - design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) - else - design["max_multiplier"] = null - design["materials"] = design_cost_data(D) - - data = queue_data(data) + data["busyname"] = FALSE + data["busyamt"] = 1 + if(length(being_built) > 0) + var/datum/design/D = being_built[1] + data["busyname"] = istype(D) && D.name ? D.name : FALSE + data["busyamt"] = length(being_built) > 1 ? being_built[2] : 1 + data["showhacked"] = hacked ? TRUE : FALSE + data["buildQueue"] = queue + data["buildQueueLen"] = queue.len return data +/obj/machinery/autolathe/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return FALSE + + add_fingerprint(usr) + + . = TRUE + switch(action) + if("clear_queue") + queue = list() + if("remove_from_queue") + var/index = text2num(params["remove_from_queue"]) + if(isnum(index) && ISINRANGE(index, 1, queue.len)) + remove_from_queue(index) + to_chat(usr, "Removed item from queue.") + if("make") + BuildTurf = loc + var/datum/design/design_last_ordered + design_last_ordered = locateUID(params["make"]) + if(!istype(design_last_ordered)) + to_chat(usr, "Invalid design") + return + if(!(design_last_ordered.build_type & AUTOLATHE)) + to_chat(usr, "Invalid design (not buildable in autolathe, report this error.)") + return + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + if(design_last_ordered.materials["$metal"] > materials.amount(MAT_METAL)) + to_chat(usr, "Invalid design (not enough metal)") + return + if(design_last_ordered.materials["$glass"] > materials.amount(MAT_GLASS)) + to_chat(usr, "Invalid design (not enough glass)") + return + if(!hacked && ("hacked" in design_last_ordered.category)) + to_chat(usr, "Invalid design (lathe requires hacking)") + return + //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier + var/multiplier = text2num(params["multiplier"]) + var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/design_last_ordered.materials[MAT_METAL]):INFINITY,design_last_ordered.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/design_last_ordered.materials[MAT_GLASS]):INFINITY) + var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack) + + if(!is_stack && (multiplier > 1)) + return + if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc + message_admins("Player [key_name_admin(usr)] attempted to pass invalid multiplier [multiplier] to an autolathe in tgui_act. Possible href exploit.") + return + if((queue.len + 1) < queue_max_len) + add_to_queue(design_last_ordered, multiplier) + else + to_chat(usr, "The autolathe queue is full!") + if(!busy) + busy = TRUE + process_queue() + busy = FALSE + /obj/machinery/autolathe/proc/design_cost_data(datum/design/D) var/list/data = list() var/coeff = get_coeff(D) @@ -184,14 +243,21 @@ if(istype(O, /obj/item/disk/design_disk)) var/obj/item/disk/design_disk/D = O if(D.blueprint) + if(!(D.blueprint.build_type & AUTOLATHE)) // otherwise, would silently fail in AddDesign2Known + to_chat(user, "This design is not compatible with the autolathe.") + return 1 user.visible_message("[user] begins to load \the [O] in \the [src]...", "You begin to load a design from \the [O]...", "You hear the chatter of a floppy drive.") playsound(get_turf(src), 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - busy = 1 + busy = TRUE if(do_after(user, 14.4, target = src)) + if(!("Imported" in D.blueprint.category)) // R&D should always ensure this is set on design disks, but it doesn't. + D.blueprint.category += "Imported" // now it will actually show up in the list. files.AddDesign2Known(D.blueprint) - busy = 0 + recipiecache = list() + SStgui.close_uis(src) // forces all connected users to re-open the TGUI. Imported entries won't show otherwise due to static_data + busy = FALSE else to_chat(user, "That disk does not have a design on it!") return 1 @@ -221,7 +287,6 @@ to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") return if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", I)) - SSnanoui.update_uis(src) I.play_tool_sound(user, I.tool_volume) /obj/machinery/autolathe/wirecutter_act(mob/user, obj/item/I) @@ -253,7 +318,7 @@ if(MAT_GLASS) flick("autolathe_r", src)//plays glass insertion animation use_power(min(1000, amount_inserted / 100)) - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/machinery/autolathe/attack_ghost(mob/user) interact(user) @@ -263,79 +328,6 @@ return interact(user) -/obj/machinery/autolathe/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["menu"]) - screen = text2num(href_list["menu"]) - - if(href_list["category"]) - selected_category = href_list["category"] - screen = AUTOLATHE_CATEGORY_MENU - - if(href_list["make"]) - BuildTurf = loc - - ///////////////// - //href protection - var/datum/design/design_last_ordered - design_last_ordered = files.FindDesignByID(href_list["make"]) //check if it's a valid design - if(!design_last_ordered) - return - if(!(design_last_ordered.build_type & AUTOLATHE)) - return - - //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier - var/multiplier = text2num(href_list["multiplier"]) - var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/design_last_ordered.materials[MAT_METAL]):INFINITY,design_last_ordered.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/design_last_ordered.materials[MAT_GLASS]):INFINITY) - var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack) - - if(!is_stack && (multiplier > 1)) - return - if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc - return - ///////////////// - - if((queue.len + 1) < queue_max_len) - add_to_queue(design_last_ordered,multiplier) - else - to_chat(usr, "The autolathe queue is full!") - if(!busy) - busy = 1 - process_queue() - busy = 0 - - if(href_list["remove_from_queue"]) - var/index = text2num(href_list["remove_from_queue"]) - if(isnum(index) && ISINRANGE(index, 1, queue.len)) - remove_from_queue(index) - if(href_list["queue_move"] && href_list["index"]) - var/index = text2num(href_list["index"]) - var/new_index = index + text2num(href_list["queue_move"]) - if(isnum(index) && isnum(new_index)) - if(ISINRANGE(new_index, 1, queue.len)) - queue.Swap(index,new_index) - if(href_list["clear_queue"]) - queue = list() - if(href_list["search"]) - if(href_list["to_search"]) - temp_search = href_list["to_search"] - if(!temp_search) - return - matching_designs.Cut() - - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(findtext(D.name, temp_search)) - matching_designs.Add(D) - - screen = AUTOLATHE_SEARCH_MENU - - SSnanoui.update_uis(src) - return 1 - /obj/machinery/autolathe/RefreshParts() var/tot_rating = 0 prod_coeff = 0 @@ -370,7 +362,7 @@ else var/list/materials_used = list(MAT_METAL=metal_cost/coeff, MAT_GLASS=glass_cost/coeff) materials.use_amount(materials_used) - SSnanoui.update_uis(src) + SStgui.update_uis(src) sleep(32/coeff) if(is_stack) var/obj/item/stack/S = new D.build_path(BuildTurf) @@ -379,7 +371,7 @@ var/obj/item/new_item = new D.build_path(BuildTurf) new_item.materials[MAT_METAL] /= coeff new_item.materials[MAT_GLASS] /= coeff - SSnanoui.update_uis(src) + SStgui.update_uis(src) desc = initial(desc) /obj/machinery/autolathe/proc/can_build(datum/design/D, multiplier = 1, custom_metal, custom_glass) @@ -454,7 +446,6 @@ D = listgetindex(listgetindex(queue, 1),1) multiplier = listgetindex(listgetindex(queue,1),2) being_built = new /list() - //visible_message("[bicon(src)] \The [src] beeps, \"Queue processing finished successfully.\"") /obj/machinery/autolathe/proc/adjust_hacked(hack) hacked = hack @@ -467,3 +458,17 @@ for(var/datum/design/D in files.known_designs) if("hacked" in D.category) files.known_designs -= D.id + SStgui.close_uis(src) // forces all connected users to re-open the TGUI, thus adding/removing hacked entries from lists + recipiecache = list() + +/obj/machinery/autolathe/proc/check_hacked_callback() + if(!wires.is_cut(WIRE_AUTOLATHE_HACK)) + adjust_hacked(FALSE) + +/obj/machinery/autolathe/proc/check_electrified_callback() + if(!wires.is_cut(WIRE_ELECTRIFY)) + shocked = FALSE + +/obj/machinery/autolathe/proc/check_disabled_callback() + if(!wires.is_cut(WIRE_AUTOLATHE_DISABLE)) + disabled = FALSE diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index b66cad8d328..60beb105dce 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -20,7 +20,6 @@ anchored = TRUE var/start_active = FALSE //If it ignores the random chance to start broken on round start var/invuln = null - var/obj/item/camera_bug/bug = null var/obj/item/camera_assembly/assembly = null //OTHER @@ -28,13 +27,14 @@ var/view_range = 7 var/short_range = 2 + var/alarm_on = FALSE var/busy = FALSE var/emped = FALSE //Number of consecutive EMP's on this camera var/in_use_lights = 0 // TO BE IMPLEMENTED var/toggle_sound = 'sound/items/wirecutter.ogg' -/obj/machinery/camera/Initialize() +/obj/machinery/camera/Initialize(mapload) . = ..() wires = new(src) assembly = new(src) @@ -44,25 +44,30 @@ GLOB.cameranet.cameras += src GLOB.cameranet.addCamera(src) + if(isturf(loc)) + LAZYADD(myArea.cameras, UID()) if(is_station_level(z) && prob(3) && !start_active) toggle_cam(null, FALSE) - wires.CutAll() + wires.cut_all() + +/obj/machinery/camera/proc/set_area_motion(area/A) + area_motion = A /obj/machinery/camera/Destroy() + SStgui.close_uis(wires) toggle_cam(null, FALSE) //kick anyone viewing out QDEL_NULL(assembly) - if(istype(bug)) - bug.bugged_cameras -= c_tag - if(bug.current == src) - bug.current = null - bug = null QDEL_NULL(wires) GLOB.cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that GLOB.cameranet.cameras -= src + if(isarea(myArea)) + LAZYREMOVE(myArea.cameras, UID()) var/area/ai_monitored/A = get_area(src) if(istype(A)) - A.motioncamera = null + A.motioncameras -= src area_motion = null + cancelCameraAlarm() + cancelAlarm() return ..() /obj/machinery/camera/emp_act(severity) @@ -158,6 +163,10 @@ // OTHER else if((istype(I, /obj/item/paper) || istype(I, /obj/item/pda)) && isliving(user)) + if (!can_use()) + to_chat(user, "You can't show something to a disabled camera!") + return + var/mob/living/U = user var/obj/item/paper/X = null var/obj/item/pda/PDA = null @@ -173,7 +182,7 @@ var/datum/data/pda/app/notekeeper/N = PDA.find_program(/datum/data/pda/app/notekeeper) if(N) itemname = PDA.name - info = N.notehtml + info = N.note to_chat(U, "You hold \the [itemname] up to the camera ...") U.changeNext_move(CLICK_CD_MELEE) for(var/mob/O in GLOB.player_list) @@ -190,19 +199,6 @@ to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...") O << browse(text("[][]", itemname, info), text("window=[]", itemname)) - else if(istype(I, /obj/item/camera_bug)) - if(!can_use()) - to_chat(user, "Camera non-functional.") - return - if(istype(bug)) - to_chat(user, "Camera bug removed.") - bug.bugged_cameras -= c_tag - bug = null - else - to_chat(user, "Camera bugged.") - bug = I - bug.bugged_cameras[c_tag] = src - else if(istype(I, /obj/item/laser_pointer)) var/obj/item/laser_pointer/L = I L.laser_act(src, user) @@ -252,7 +248,7 @@ if(status && !(flags & NODECONSTRUCT)) triggerCameraAlarm() toggle_cam(null, FALSE) - wires.CutAll() + wires.cut_all() /obj/machinery/camera/deconstruct(disassembled = TRUE) if(!(flags & NODECONSTRUCT)) @@ -282,9 +278,16 @@ status = !status if(can_use()) GLOB.cameranet.addCamera(src) + if(isturf(loc)) + myArea = get_area(src) + LAZYADD(myArea.cameras, UID()) + else + myArea = null else set_light(0) GLOB.cameranet.removeCamera(src) + if(isarea(myArea)) + LAZYREMOVE(myArea.cameras, UID()) GLOB.cameranet.updateChunk(x, y, z) var/change_msg = "deactivates" if(status) @@ -313,12 +316,12 @@ to_chat(O, "The screen bursts into static.") /obj/machinery/camera/proc/triggerCameraAlarm() - if(is_station_contact(z)) - SSalarms.camera_alarm.triggerAlarm(loc, src) + alarm_on = TRUE + SSalarm.triggerAlarm("Camera", get_area(src), list(UID()), src) /obj/machinery/camera/proc/cancelCameraAlarm() - if(is_station_contact(z)) - SSalarms.camera_alarm.clearAlarm(loc, src) + alarm_on = FALSE + SSalarm.cancelAlarm("Camera", get_area(src), src) /obj/machinery/camera/proc/can_use() if(!status) @@ -414,8 +417,8 @@ /obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets. var/turf/prev_turf -/obj/machinery/camera/portable/New() - ..() +/obj/machinery/camera/portable/Initialize(mapload) + . = ..() assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed. assembly.anchored = 0 assembly.update_icon() diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index 16fb6eca204..8a32c6d6374 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/list/localMotionTargets = list() var/detectTime = 0 var/area/ai_monitored/area_motion = null - var/alarm_delay = 100 - + var/alarm_delay = 30 // Don't forget, there's another 3 seconds in queueAlarm() /obj/machinery/camera/process() // motion camera event loop - if(stat & (EMPED|NOPOWER)) - return if(!isMotion()) . = PROCESS_KILL return + if(stat & (EMPED|NOPOWER)) + 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) + for(var/thing in getTargetList()) + var/mob/target = locateUID(thing) + if(QDELETED(target) || target.stat == DEAD || (!area_motion && !in_range(src, target))) + //If not part of a monitored area and the camera is not in range or the target is dead + lostTargetRef(thing) -/obj/machinery/camera/proc/newTarget(var/mob/target) - if(istype(target, /mob/living/silicon/ai)) return 0 +/obj/machinery/camera/proc/getTargetList() + if(area_motion) + return area_motion.motionTargets + return localMotionTargets + +/obj/machinery/camera/proc/newTarget(mob/target) + if(isAI(target)) + return FALSE if(detectTime == 0) detectTime = world.time // start the clock - if(!(target in motionTargets)) - motionTargets += target - return 1 + var/list/targets = getTargetList() + targets |= target.UID() + return TRUE -/obj/machinery/camera/proc/lostTarget(var/mob/target) - if(target in motionTargets) - motionTargets -= target - if(motionTargets.len == 0) +/obj/machinery/camera/proc/lostTargetRef(uid) + var/list/targets = getTargetList() + targets -= uid + if(length(targets)) cancelAlarm() /obj/machinery/camera/proc/cancelAlarm() - if(!status || (stat & NOPOWER)) - return FALSE - if(detectTime == -1 && is_station_contact(z)) - SSalarms.motion_alarm.clearAlarm(loc, src) + if(detectTime == -1) + if(status) + SSalarm.cancelAlarm("Motion", get_area(src), src) detectTime = 0 return TRUE /obj/machinery/camera/proc/triggerAlarm() - if(!status || (stat & NOPOWER)) + if(!detectTime) return FALSE - if(!detectTime || !is_station_contact(z)) - return FALSE - SSalarms.motion_alarm.triggerAlarm(loc, src) + if(status) + SSalarm.triggerAlarm("Motion", get_area(src), list(UID()), src) + visible_message("A red light flashes on the [src]!") detectTime = -1 return TRUE -/obj/machinery/camera/HasProximity(atom/movable/AM as mob|obj) +/obj/machinery/camera/HasProximity(atom/movable/AM) // Motion cameras outside of an "ai monitored" area will use this to detect stuff. if(!area_motion) if(isliving(AM)) diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index b7ae75cd13f..33c970639e5 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -2,7 +2,7 @@ // EMP -/obj/machinery/camera/emp_proof/Initialize() +/obj/machinery/camera/emp_proof/Initialize(mapload) . = ..() upgradeEmpProof() @@ -11,19 +11,23 @@ /obj/machinery/camera/xray icon_state = "xraycam" // Thanks to Krutchen for the icons. -/obj/machinery/camera/xray/Initialize() +/obj/machinery/camera/xray/Initialize(mapload) . = ..() upgradeXRay() // MOTION +/obj/machinery/camera/motion + name = "motion-sensitive security camera" -/obj/machinery/camera/motion/Initialize() +/obj/machinery/camera/motion/Initialize(mapload) . = ..() upgradeMotion() // ALL UPGRADES +/obj/machinery/camera/all + icon_state = "xraycamera" //mapping icon. -/obj/machinery/camera/all/Initialize() +/obj/machinery/camera/all/Initialize(mapload) . = ..() upgradeEmpProof() upgradeXRay() @@ -78,6 +82,10 @@ // If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list. /obj/machinery/camera/proc/upgradeMotion() + if(isMotion()) + return + if(name == initial(name)) + name = "motion-sensitive security camera" assembly.upgrades.Add(new /obj/item/assembly/prox_sensor(assembly)) setPowerUsage() // Add it to machines that process diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index 21fddce64b7..1b083bd9c87 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -40,8 +40,7 @@ add_fingerprint(user) if(stat & (BROKEN|NOPOWER)) return - ui_interact(user) - + tgui_interact(user) /obj/machinery/computer/operating/attack_hand(mob/user) if(..(user)) @@ -52,16 +51,15 @@ add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/operating/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "op_computer.tmpl", "Patient Monitor", 650, 455) + ui = new(user, src, ui_key, "OperatingComputer", "Patient Monitor", 650, 455, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/computer/operating/tgui_data(mob/user) var/data[0] var/mob/living/carbon/human/occupant if(table) @@ -136,38 +134,43 @@ return data -/obj/machinery/computer/operating/Topic(href, href_list) +/obj/machinery/computer/operating/tgui_act(action, params) if(..()) - return 1 + return + if(stat & (NOPOWER|BROKEN)) + 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["verboseOn"]) - verbose=1 - if(href_list["verboseOff"]) - verbose=0 - if(href_list["healthOn"]) - healthAnnounce=1 - if(href_list["healthOff"]) - healthAnnounce=0 - if(href_list["critOn"]) - crit=1 - if(href_list["critOff"]) - crit=0 - if(href_list["oxyOn"]) - oxy=1 - if(href_list["oxyOff"]) - oxy=0 - if(href_list["oxy_adj"]!=0) - oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"]) - if(href_list["choiceOn"]) - choice=1 - if(href_list["choiceOff"]) - choice=0 - if(href_list["health_adj"]!=0) - healthAlarm=healthAlarm+text2num(href_list["health_adj"]) - return - + . = TRUE + switch(action) + if("verboseOn") + verbose = TRUE + if("verboseOff") + verbose = FALSE + if("healthOn") + healthAnnounce = TRUE + if("healthOff") + healthAnnounce = FALSE + if("critOn") + crit = TRUE + if("critOff") + crit = FALSE + if("oxyOn") + oxy = TRUE + if("oxyOff") + oxy = FALSE + if("oxy_adj") + oxyAlarm = clamp(text2num(params["new"]), -100, 100) + if("choiceOn") + choice = TRUE + if("choiceOff") + choice = FALSE + if("health_adj") + healthAlarm = clamp(text2num(params["new"]), -100, 100) + else + return FALSE /obj/machinery/computer/operating/process() @@ -178,6 +181,7 @@ atom_say("New patient detected, loading stats") victim = table.victim atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") + SStgui.update_uis(src) if(nextTick < world.time) nextTick=world.time + OP_COMPUTER_COOLDOWN if(crit && victim.health <= -50 ) diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm index 44b55ed1d2c..1fe5c3eaea8 100644 --- a/code/game/machinery/computer/ai_core.dm +++ b/code/game/machinery/computer/ai_core.dm @@ -108,14 +108,10 @@ update_icon() return - if(AI_READY_CORE) - if(istype(P, /obj/item/aicard)) - P.transfer_ai("INACTIVE", "AICARD", src, user) - return return ..() /obj/structure/AIcore/crowbar_act(mob/living/user, obj/item/I) - if(state !=CIRCUIT_CORE || state != GLASS_CORE || !(state == CABLED_CORE && brain)) + if(state !=CIRCUIT_CORE && state != GLASS_CORE && !(state == CABLED_CORE && brain)) return . = TRUE if(!I.use_tool(src, user, 0, volume = I.tool_volume)) @@ -229,7 +225,7 @@ qdel(src) /obj/structure/AIcore/welder_act(mob/user, obj/item/I) - if(!state) + if(state) return . = TRUE if(!I.tool_use_check(user, 0)) @@ -288,7 +284,7 @@ That prevents a few funky behaviors. //The type of interaction, the player performing the operation, the AI itself, and the card object, if any. -atom/proc/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) +/atom/proc/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) if(istype(card)) if(card.flush) to_chat(user, "ERROR: AI flush is in progress, cannot execute transfer protocol.") diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index 0d715fe5759..6b198deba49 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -22,23 +22,22 @@ return ..() /obj/machinery/computer/aifixer/attack_ai(var/mob/user as mob) - ui_interact(user) + tgui_interact(user) /obj/machinery/computer/aifixer/attack_hand(var/mob/user as mob) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/aifixer/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "ai_fixer.tmpl", "AI System Integrity Restorer", 550, 500) + ui = new(user, src, ui_key, "AIFixer", name, 550, 500, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/computer/aifixer/ui_data(mob/user, datum/topic_state/state) +/obj/machinery/computer/aifixer/tgui_data(mob/user, datum/topic_state/state) var/data[0] + data["occupant"] = (occupant ? occupant.name : null) // a null occupant isn't passed on if this is below the if. if(occupant) - data["occupant"] = occupant.name data["reference"] = "\ref[occupant]" data["integrity"] = (occupant.health+100)/2 data["stat"] = occupant.stat @@ -48,45 +47,49 @@ var/laws[0] for(var/datum/ai_law/law in occupant.laws.all_laws()) - laws.Add(list(list("law" = law.law, "number" = law.get_index()))) - + if(law in occupant.laws.ion_laws) // If we're an ion law, give it an ion index code + laws.Add(ionnum() + ". " + law.law) + else + laws.Add(num2text(law.get_index()) + ". " + law.law) data["laws"] = laws + data["has_laws"] = length(laws) return data -/obj/machinery/computer/aifixer/Topic(href, href_list) +/obj/machinery/computer/aifixer/tgui_act(action, params) if(..()) - return 1 + return + switch(action) + if("fix") + if(active) // Prevent from starting a fix while fixing. + to_chat(usr, "You are already fixing this AI!") + return + active = TRUE + INVOKE_ASYNC(src, .proc/fix_ai) + add_fingerprint(usr) - if(href_list["fix"]) - active = 1 - while(occupant.health < 100) - occupant.adjustOxyLoss(-1, FALSE) - occupant.adjustFireLoss(-1, FALSE) - occupant.adjustToxLoss(-1, FALSE) - occupant.adjustBruteLoss(-1, FALSE) - occupant.updatehealth() - if(occupant.health >= 0 && occupant.stat == DEAD) - occupant.update_revive() - occupant.lying = 0 - update_icon() - sleep(10) - active = 0 - add_fingerprint(usr) + if("wireless") + occupant.control_disabled = !occupant.control_disabled - if(href_list["wireless"]) - var/wireless = text2num(href_list["wireless"]) - if(wireless == 0 || wireless == 1) - occupant.control_disabled = wireless + if("radio") + occupant.aiRadio.disabledAi = !occupant.aiRadio.disabledAi - if(href_list["radio"]) - var/radio = text2num(href_list["radio"]) - if(radio == 0 || radio == 1) - occupant.aiRadio.disabledAi = radio - - SSnanoui.update_uis(src) update_icon() - return + return TRUE + +/obj/machinery/computer/aifixer/proc/fix_ai() // Can we fix it? Probrably. + while(occupant.health < 100) + occupant.adjustOxyLoss(-1, FALSE) + occupant.adjustFireLoss(-1, FALSE) + occupant.adjustToxLoss(-1, FALSE) + occupant.adjustBruteLoss(-1, FALSE) + occupant.updatehealth() + if(occupant.health >= 0 && occupant.stat == DEAD) + occupant.update_revive() + occupant.lying = FALSE + update_icon() + sleep(10) + active = FALSE /obj/machinery/computer/aifixer/update_icon() ..() @@ -113,7 +116,7 @@ if(stat & (NOPOWER|BROKEN)) to_chat(user, "[src] is offline and cannot take an AI at this time!") return - AI.loc = src + AI.forceMove(src) occupant = AI AI.control_disabled = 1 AI.aiRadio.disabledAi = 1 @@ -125,7 +128,7 @@ if(occupant && !active) to_chat(occupant, "You have been downloaded to a mobile storage device. Still no remote access.") to_chat(user, "Transfer successful: [occupant.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory.") - occupant.loc = card + occupant.forceMove(card) occupant = null update_icon() else if(active) diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index 20ff79041a9..adbed5f15eb 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -571,20 +571,22 @@ event = null else if(href_list["killcrew"]) //shoot a crewmember + if(length(settlers) <= 0 || alive <= 0) + return var/sheriff = remove_crewmember() //I shot the sheriff - playsound(loc,'sound/weapons/gunshots/gunshot.ogg', 100, 1) + playsound(loc, 'sound/weapons/gunshots/gunshot.ogg', 100, 1) - if(settlers.len == 0 || alive == 0) + if(length(settlers) == 0 || alive == 0) atom_say("The last crewmember [sheriff], shot themselves, GAME OVER!") if(emagged) - usr.death(0) - emagged = 0 - gameover = 1 + usr.death(FALSE) + emagged = FALSE + gameover = TRUE event = null else if(emagged) if(usr.name == sheriff) atom_say("The crew of the ship chose to kill [usr.name]!") - usr.death(0) + usr.death(FALSE) if(event == ORION_TRAIL_LING) //only ends the ORION_TRAIL_LING event, since you can do this action in multiple places event = null diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index 123cee3ae4e..0be4aa0449b 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -1,84 +1,90 @@ -GLOBAL_LIST_EMPTY(priority_air_alarms) -GLOBAL_LIST_EMPTY(minor_air_alarms) - - /obj/machinery/computer/atmos_alert name = "atmospheric alert computer" desc = "Used to access the station's atmospheric sensors." circuit = /obj/item/circuitboard/atmos_alert + var/ui_x = 350 + var/ui_y = 300 icon_keyboard = "atmos_key" icon_screen = "alert:0" light_color = LIGHT_COLOR_CYAN + var/list/priority_alarms = list() + var/list/minor_alarms = list() + var/receive_frequency = ATMOS_FIRE_FREQ + var/datum/radio_frequency/radio_connection -/obj/machinery/computer/atmos_alert/New() - ..() - SSalarms.atmosphere_alarm.register(src, /obj/machinery/computer/station_alert/.proc/update_icon) +/obj/machinery/computer/atmos_alert/Initialize(mapload) + . = ..() + set_frequency(receive_frequency) /obj/machinery/computer/atmos_alert/Destroy() - SSalarms.atmosphere_alarm.unregister(src) - return ..() + SSradio.remove_object(src, receive_frequency) + return ..() /obj/machinery/computer/atmos_alert/attack_hand(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/atmos_alert/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/atmos_alert/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "atmos_alert.tmpl", src.name, 500, 500) + ui = new(user, src, ui_key, "AtmosAlertConsole", name, ui_x, ui_y, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/computer/atmos_alert/ui_data(mob/user, datum/topic_state/state) - var/data[0] - var/major_alarms[0] - var/minor_alarms[0] +/obj/machinery/computer/atmos_alert/tgui_data(mob/user) + var/list/data = list() - for(var/datum/alarm/alarm in SSalarms.atmosphere_alarm.major_alarms()) - major_alarms[++major_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "\ref[alarm]") - - for(var/datum/alarm/alarm in SSalarms.atmosphere_alarm.minor_alarms()) - minor_alarms[++minor_alarms.len] = list("name" = sanitize(alarm.alarm_name()), "ref" = "\ref[alarm]") - - data["priority_alarms"] = major_alarms - data["minor_alarms"] = minor_alarms + data["priority"] = list() + for(var/zone in priority_alarms) + data["priority"] |= zone + data["minor"] = list() + for(var/zone in minor_alarms) + data["minor"] |= zone return data -/obj/machinery/computer/atmos_alert/update_icon() - var/list/alarms = SSalarms.atmosphere_alarm.major_alarms() - if(alarms.len) - icon_screen = "alert:2" - else - alarms = SSalarms.atmosphere_alarm.minor_alarms() - if(alarms.len) - icon_screen = "alert:1" - else - icon_screen = "alert:0" - ..() - -/obj/machinery/computer/atmos_alert/Topic(href, href_list) +/obj/machinery/computer/atmos_alert/tgui_act(action, params) if(..()) - return 1 + return + switch(action) + if("clear") + var/zone = params["zone"] + if(zone in priority_alarms) + to_chat(usr, "Priority alarm for [zone] cleared.") + priority_alarms -= zone + . = TRUE + if(zone in minor_alarms) + to_chat(usr, "Minor alarm for [zone] cleared.") + minor_alarms -= zone + . = TRUE + update_icon() - if(href_list["clear_alarm"]) - var/datum/alarm/alarm = locate(href_list["clear_alarm"]) in SSalarms.atmosphere_alarm.alarms - if(alarm) - for(var/datum/alarm_source/alarm_source in alarm.sources) - var/obj/machinery/alarm/air_alarm = alarm_source.source - if(istype(air_alarm)) - var/list/new_ref = list("atmos_reset" = 1) - air_alarm.Topic(href, new_ref, state = GLOB.air_alarm_topic) - update_icon() - return 1 +/obj/machinery/computer/atmos_alert/proc/set_frequency(new_frequency) + SSradio.remove_object(src, receive_frequency) + receive_frequency = new_frequency + radio_connection = SSradio.add_object(src, receive_frequency, RADIO_ATMOSIA) -GLOBAL_DATUM_INIT(air_alarm_topic, /datum/topic_state/air_alarm_topic, new) +/obj/machinery/computer/atmos_alert/receive_signal(datum/signal/signal) + if(!signal) + return -/datum/topic_state/air_alarm_topic/href_list(var/mob/user) - var/list/extra_href = list() - extra_href["remote_connection"] = 1 - extra_href["remote_access"] = 1 + var/zone = signal.data["zone"] + var/severity = signal.data["alert"] - return extra_href + if(!zone || !severity) + return -/datum/topic_state/air_alarm_topic/can_use_topic(var/src_object, var/mob/user) - return STATUS_INTERACTIVE + minor_alarms -= zone + priority_alarms -= zone + if(severity == "severe") + priority_alarms += zone + else if(severity == "minor") + minor_alarms += zone + update_icon() + +/obj/machinery/computer/atmos_alert/update_icon() + if(length(priority_alarms)) + icon_screen = "alert:2" + else if(length(minor_alarms)) + icon_screen = "alert:1" + else + icon_screen = "alert:0" + ..() diff --git a/code/game/machinery/computer/brigcells.dm b/code/game/machinery/computer/brigcells.dm index a391bfa4021..b220ded29c5 100644 --- a/code/game/machinery/computer/brigcells.dm +++ b/code/game/machinery/computer/brigcells.dm @@ -12,7 +12,7 @@ /obj/machinery/computer/brigcells/attack_ai(mob/user) attack_hand(user) - ui_interact(user) + tgui_interact(user) /obj/machinery/computer/brigcells/attack_hand(mob/user) add_fingerprint(user) @@ -21,38 +21,46 @@ if(!allowed(user)) to_chat(user, "Access denied.") return - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/brigcells/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "brig_cells.tmpl", "Brig Cell Management", 1000, 400) - ui.open() - ui.set_auto_update(1) +/obj/machinery/computer/brigcells/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "BrigCells", "Brig Cell Management", 1000, 400, master_ui, state) + ui.open() + ui.set_autoupdate(TRUE) -/obj/machinery/computer/brigcells/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - var/list/timers = list() - for(var/obj/machinery/door_timer/T in GLOB.celltimers_list) - var/timer = list() - timer["cell_id"] = T.name - timer["occupant"] = T.occupant - timer["crimes"] = T.crimes - timer["brigged_by"] = T.officer - if(T.time == 0) - timer["background"] = "'background-color:#007f47'" - else - timer["background"] = "'background-color:#890E26'" - timer["time_set"] = seconds_to_clock(T.time / 10) - timer["time_left"] = seconds_to_clock(T.timeleft()) - timer["ref"] = "\ref[T]" - timers[++timers.len] += timer - timers = sortByKey(timers, "cell_id") - data["cells"] = timers - return data +/obj/machinery/computer/brigcells/tgui_data(mob/user) + var/list/data = list() + var/list/timers = list() + for(var/obj/machinery/door_timer/T in GLOB.celltimers_list) + var/timer = list() + timer["cell_id"] = T.name + timer["occupant"] = T.occupant + timer["crimes"] = T.crimes + timer["brigged_by"] = T.officer + timer["time_set_seconds"] = round(T.timetoset / 10, 1) + timer["time_left_seconds"] = round(T.timeleft(), 1) + timer["ref"] = "\ref[T]" + timers[++timers.len] += timer + timers = sortByKey(timers, "cell_id") + data["cells"] = timers + return data -/obj/machinery/computer/brigcells/Topic(href, href_list) - if(href_list["release"]) - var/obj/machinery/door_timer/T = locate(href_list["release"]) - T.timer_end() - T.Radio.autosay("Timer stopped manually from a cell management console.", T.name, "Security", list(z)) +/obj/machinery/computer/brigcells/tgui_act(action, params) + if (..()) + return FALSE + + if(!allowed(usr)) + to_chat(usr, "Access denied.") + return FALSE + + if (action == "release") + var/ref = params["ref"] + var/obj/machinery/door_timer/T = locate(ref) + if (T) + T.timer_end() + T.Radio.autosay("Timer stopped manually from a cell management console.", T.name, "Security", list(z)) + return TRUE + + return FALSE diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index b502d6e5441..a02c46524c5 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -159,18 +159,12 @@ name = "Circuit board (Security Records)" build_path = /obj/machinery/computer/secure_data origin_tech = "programming=2;combat=2" -/obj/item/circuitboard/skills - name = "Circuit board (Employment Records)" - build_path = /obj/machinery/computer/skills /obj/item/circuitboard/stationalert_engineering name = "Circuit Board (Station Alert Console (Engineering))" build_path = /obj/machinery/computer/station_alert -/obj/item/circuitboard/stationalert_security - name = "Circuit Board (Station Alert Console (Security))" +/obj/item/circuitboard/stationalert + name = "Circuit Board (Station Alert Console)" build_path = /obj/machinery/computer/station_alert -/obj/item/circuitboard/stationalert_all - name = "Circuit Board (Station Alert Console (All))" - build_path = /obj/machinery/computer/station_alert/all /obj/item/circuitboard/atmos_alert name = "Circuit Board (Atmospheric Alert Computer)" build_path = /obj/machinery/computer/atmos_alert @@ -236,7 +230,10 @@ /obj/item/circuitboard/brigcells name = "Circuit board (Brig Cell Control)" build_path = /obj/machinery/computer/brigcells - +/obj/item/circuitboard/sm_monitor + name = "Circuit board (Supermatter Monitoring Console)" + build_path = /obj/machinery/computer/sm_monitor + origin_tech = "programming=2;powerstorage=2" // RD console circuits, so that {de,re}constructing one of the special consoles doesn't ruin everything forever /obj/item/circuitboard/rdconsole @@ -283,7 +280,7 @@ origin_tech = "programming=3;powerstorage=3" /obj/item/circuitboard/ordercomp name = "Circuit board (Supply Ordering Console)" - build_path = /obj/machinery/computer/ordercomp + build_path = /obj/machinery/computer/supplycomp/public origin_tech = "programming=3" /obj/item/circuitboard/supplycomp name = "Circuit board (Supply Shuttle Console)" diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 3f8fc080bf8..cb7e1b2d20b 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -10,75 +10,167 @@ var/mapping = 0 // For the overview file (overview.dm), not used on this page var/list/network = list() - var/list/available_networks = list() - var/list/watchers = list() //who's using the console, associated with the camera they're on. + var/obj/machinery/camera/active_camera + var/list/watchers = list() -/obj/machinery/computer/security/New() // Lists existing networks and their required access. Format: available_networks[] = list() - generate_network_access() - ..() + // Stuff needed to render the map + var/map_name + var/const/default_map_size = 15 + var/obj/screen/map_view/cam_screen + /// All the plane masters that need to be applied. + var/list/cam_plane_masters + var/obj/screen/background/cam_background -/obj/machinery/computer/security/proc/generate_network_access() - available_networks["SS13"] = list(ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Telecomms"] = list(ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Research Outpost"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Mining Outpost"] = list(ACCESS_QM,ACCESS_HOP,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Research"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Prison"] = list(ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Labor Camp"] = list(ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Interrogation"] = list(ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Atmosphere Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Fire Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Power Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Supermatter"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["MiniSat"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Singularity"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Anomaly Isolation"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Toxins"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Telepad"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["TestChamber"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["ERT"] = list(ACCESS_CENT_SPECOPS_COMMANDER,ACCESS_CENT_COMMANDER) - available_networks["CentComm"] = list(ACCESS_CENT_SECURITY,ACCESS_CENT_COMMANDER) - available_networks["Thunderdome"] = list(ACCESS_CENT_THUNDER,ACCESS_CENT_COMMANDER) + // Parent object this camera is assigned to. Used for camera bugs + var/atom/movable/parent + +/obj/machinery/computer/security/tgui_host() + return parent ? parent : src + +/obj/machinery/computer/security/Initialize() + . = ..() + // Initialize map objects + map_name = "camera_console_[UID()]_map" + cam_screen = new + cam_screen.name = "screen" + cam_screen.assigned_map = map_name + cam_screen.del_on_map_removal = FALSE + cam_screen.screen_loc = "[map_name]:1,1" + cam_plane_masters = list() + for(var/plane in subtypesof(/obj/screen/plane_master)) + var/obj/screen/instance = new plane() + instance.assigned_map = map_name + instance.del_on_map_removal = FALSE + instance.screen_loc = "[map_name]:CENTER" + cam_plane_masters += instance + cam_background = new + cam_background.assigned_map = map_name + cam_background.del_on_map_removal = FALSE /obj/machinery/computer/security/Destroy() - if(watchers.len) - for(var/mob/M in watchers) - M.unset_machine() //to properly reset the view of the users if the console is deleted. + qdel(cam_screen) + QDEL_LIST(cam_plane_masters) + qdel(cam_background) return ..() -/obj/machinery/computer/security/proc/isCameraFarAway(obj/machinery/camera/C) - var/turf/consoleturf = get_turf(src) - var/turf/cameraturf = get_turf(C) - if((is_away_level(cameraturf.z) || is_away_level(consoleturf.z)) && !atoms_share_level(cameraturf, consoleturf)) //can only recieve away mission cameras on away missions +/obj/machinery/computer/security/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + // Update UI + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + // Show static if can't use the camera + if(!active_camera?.can_use()) + show_camera_static() + if(!ui) + var/user_uid = user.UID() + var/is_living = isliving(user) + // Ghosts shouldn't count towards concurrent users, which produces + // an audible terminal_on click. + if(is_living) + watchers += user_uid + // Turn on the console + if(length(watchers) == 1 && is_living) + playsound(src, 'sound/machines/terminal_on.ogg', 25, FALSE) + use_power(active_power_usage) + // Register map objects + user.client.register_map_obj(cam_screen) + for(var/plane in cam_plane_masters) + user.client.register_map_obj(plane) + user.client.register_map_obj(cam_background) + // Open UI + ui = new(user, src, ui_key, "CameraConsole", name, 870, 708, master_ui, state) + ui.open() + +/obj/machinery/computer/security/tgui_data() + var/list/data = list() + data["network"] = network + data["activeCamera"] = null + if(active_camera) + data["activeCamera"] = list( + name = active_camera.c_tag, + status = active_camera.status, + ) + return data + +/obj/machinery/computer/security/tgui_static_data() + var/list/data = list() + data["mapRef"] = map_name + var/list/cameras = get_available_cameras() + data["cameras"] = list() + for(var/i in cameras) + var/obj/machinery/camera/C = cameras[i] + data["cameras"] += list(list( + name = C.c_tag, + )) + return data + + +/obj/machinery/computer/security/tgui_act(action, params) + if(..()) + return + + if(action == "switch_camera") + var/c_tag = params["name"] + var/list/cameras = get_available_cameras() + var/obj/machinery/camera/C = cameras[c_tag] + active_camera = C + playsound(src, get_sfx("terminal_type"), 25, FALSE) + + // Show static if can't use the camera + if(!active_camera?.can_use()) + show_camera_static() + return TRUE + + var/list/visible_turfs = list() + for(var/turf/T in (C.isXRay() \ + ? range(C.view_range, C) \ + : view(C.view_range, C))) + visible_turfs += T + + var/list/bbox = get_bbox_of_atoms(visible_turfs) + var/size_x = bbox[3] - bbox[1] + 1 + var/size_y = bbox[4] - bbox[2] + 1 + + cam_screen.vis_contents = visible_turfs + cam_background.icon_state = "clear" + cam_background.fill_rect(1, 1, size_x, size_y) + return TRUE -/obj/machinery/computer/security/check_eye(mob/user) - if((stat & (NOPOWER|BROKEN)) || user.incapacitated() || !user.has_vision()) - user.unset_machine() - return - if(!(user in watchers)) - user.unset_machine() - return - if(!watchers[user]) - user.unset_machine() - return - var/obj/machinery/camera/C = watchers[user] - if(isCameraFarAway(C)) - user.unset_machine() - return - if(!can_access_camera(C, user)) - user.unset_machine() - -/obj/machinery/computer/security/on_unset_machine(mob/user) - watchers.Remove(user) - user.reset_perspective(null) +// Returns the list of cameras accessible from this computer +/obj/machinery/computer/security/proc/get_available_cameras() + var/list/L = list() + for (var/obj/machinery/camera/C in GLOB.cameranet.cameras) + if((is_away_level(z) || is_away_level(C.z)) && (C.z != z))//if on away mission, can only receive feed from same z_level cameras + continue + L.Add(C) + var/list/D = list() + for(var/obj/machinery/camera/C in L) + if(!C.network) + stack_trace("Camera in a cameranet has no camera network") + continue + if(!(islist(C.network))) + stack_trace("Camera in a cameranet has a non-list camera network") + continue + var/list/tempnetwork = C.network & network + if(tempnetwork.len) + D["[C.c_tag]"] = C + return D /obj/machinery/computer/security/attack_hand(mob/user) if(stat || ..()) user.unset_machine() return - ui_interact(user) + tgui_interact(user) + +/obj/machinery/computer/security/attack_ai(mob/user) + to_chat(user, "You realise its kind of stupid to access a camera console when you have the entire camera network at your metaphorical fingertips") + return + + +/obj/machinery/computer/security/proc/show_camera_static() + cam_screen.vis_contents.Cut() + cam_background.icon_state = "scanline2" + cam_background.fill_rect(1, 1, default_map_size, default_map_size) /obj/machinery/computer/security/telescreen/multitool_act(mob/user, obj/item/I) . = TRUE @@ -99,212 +191,6 @@ if("West") pixel_x = -32 -/obj/machinery/computer/security/emag_act(user as mob) - if(!emagged) - emagged = 1 - to_chat(user, "You have authorized full network access!") - attack_hand(user) - else - attack_hand(user) - -/obj/machinery/computer/security/proc/get_user_access(mob/user) - var/list/access = list() - - if(emagged) - access = get_all_accesses() // Assume captain level access when emagged - else if(ishuman(user)) - access = user.get_access() - else if((isAI(user) || isrobot(user)) && CanUseTopic(user, GLOB.default_state) == STATUS_INTERACTIVE) - access = get_all_accesses() // Assume captain level access when AI - else if(user.can_admin_interact()) - access = get_all_accesses() - return access - -/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800) - - // adding a template with the key "mapContent" enables the map ui functionality - ui.add_template("mapContent", "sec_camera_map_content.tmpl") - // adding a template with the key "mapHeader" replaces the map header content - ui.add_template("mapHeader", "sec_camera_map_header.tmpl") - - ui.open() - -/obj/machinery/computer/security/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - var/list/cameras = list() - for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) - if(isCameraFarAway(C)) - continue - if(!can_access_camera(C, user)) - continue - - cameras[++cameras.len] = C.nano_structure() - - for(var/i = cameras.len, i > 0, i--) //based off /proc/camera_sort, sorts cameras alphabetically for the UI - for(var/j = 1 to i - 1) - var/a = cameras[j] - var/b = cameras[j + 1] - if(sorttext(a["name"], b["name"]) < 0) - cameras.Swap(j, j + 1) - - data["cameras"] = cameras - - var/list/access = get_user_access(user) - if(emagged) - data["emagged"] = 1 - - var/list/networks_list = list() - // Loop through the ID's permission, and check which networks the ID has access to. - for(var/net in available_networks) // Loop through networks. - for(var/req in available_networks[net]) // Loop through access levels of the networks. - if(req in access) - if(net in network) // Checks if the network is currently active. - networks_list.Add(list(list("name" = net, "active" = 1))) - else - networks_list.Add(list(list("name" = net, "active" = 0))) - break - - if(networks_list.len) - data["networks"] = networks_list - - data["current"] = null - if(watchers[user]) - var/obj/machinery/camera/watched = watchers[user] - data["current"] = watched.nano_structure() - - return data - -/obj/machinery/computer/security/Topic(href, href_list) - if(..()) - usr.unset_machine() - return 1 - - if(href_list["switchTo"]) - var/obj/machinery/camera/C = locate(href_list["switchTo"]) in GLOB.cameranet.cameras - if(!C) - return 1 - - switch_to_camera(usr, C) - - else if(href_list["reset"]) - usr.unset_machine() - - else if(href_list["activate"]) // Activate: enable or disable networks - var/net = href_list["activate"] // Network to be enabled or disabled. - var/active = href_list["active"] // Is the network currently active. - var/list/access = get_user_access(usr) - for(var/a in available_networks[net]) - if(a in access) // Re-check for authorization. - if(text2num(active) == 1) - network -= net - break - else - network += net - break - - SSnanoui.update_uis(src) - -// Check if camera is accessible when jumping -/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C, var/mob/M) - if(CanUseTopic(M, GLOB.default_state) != STATUS_INTERACTIVE || M.incapacitated() || !M.has_vision()) - return 0 - - if(isrobot(M)) - var/list/viewing = viewers(src) - if(!viewing.Find(M)) - return 0 - - if(isAI(M)) - var/mob/living/silicon/ai/A = M - if(!A.is_in_chassis()) - return 0 - - if(!issilicon(M) && !Adjacent(M)) - return 0 - - var/list/shared_networks = network & C.network - if(!shared_networks.len || !C.can_use()) - return 0 - - return 1 - -// Switching to cameras -/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C) - if(!can_access_camera(C, user)) - user.unset_machine() - return 1 - - if(isAI(user)) - var/mob/living/silicon/ai/A = user - A.eyeobj.setLoc(get_turf(C)) - A.client.eye = A.eyeobj - else - user.reset_perspective(C) - watchers[user] = C - use_power(50) - -//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)) - var/mob/living/carbon/human/H = A - jump_to = locate() in H.head - else if(isrobot(A)) - var/mob/living/silicon/robot/R = A - jump_to = R.camera - - else if(isobj(A)) - var/obj/O = A - jump_to = locate() in O - - else if(isturf(A)) - var/best_dist = INFINITY - for(var/obj/machinery/camera/camera in get_area(A)) - if(!camera.can_use()) - continue - if(!can_access_camera(camera, user)) - 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, user)) - switch_to_camera(user, jump_to) - -// 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/Move(n, direct) - if(istype(machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = machine - var/turf/T = get_turf(console.watchers[src]) - for(var/i; i < 10; i++) - T = get_step(T, direct) - console.jump_on_click(src, T) - return - return ..() - // Other computer monitors. /obj/machinery/computer/security/telescreen name = "telescreen" diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 8d6c545e0a8..7211970fd14 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -2,6 +2,12 @@ //increase the slots of many jobs. GLOBAL_VAR_INIT(time_last_changed_position, 0) +#define IDCOMPUTER_SCREEN_TRANSFER 0 +#define IDCOMPUTER_SCREEN_SLOTS 1 +#define IDCOMPUTER_SCREEN_ACCESS 2 +#define IDCOMPUTER_SCREEN_RECORDS 3 +#define IDCOMPUTER_SCREEN_DEPT 4 + /obj/machinery/computer/card name = "identification computer" desc = "Terminal for programming Nanotrasen employee ID cards to access parts of the station." @@ -12,9 +18,9 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) light_color = LIGHT_COLOR_LIGHTBLUE var/obj/item/card/id/scan = null var/obj/item/card/id/modify = null - var/mode = 0.0 - var/printing = null + var/mode = 0 var/target_dept = 0 //Which department this computer has access to. 0=all departments + var/obj/item/radio/Radio //Cooldown for closing positions in seconds //if set to -1: No cooldown... probably a bad idea @@ -54,15 +60,27 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) //Assoc array: "JobName" = (int) var/list/opened_positions = list() + +/obj/machinery/computer/card/Initialize() + ..() + Radio = new /obj/item/radio(src) + Radio.listening = 0 + Radio.config(list("Command" = 0)) + Radio.follow_target = src + +/obj/machinery/computer/card/Destroy() + QDEL_NULL(Radio) + return ..() + /obj/machinery/computer/card/proc/is_centcom() - return istype(src, /obj/machinery/computer/card/centcom) + return FALSE /obj/machinery/computer/card/proc/is_authenticated(var/mob/user) if(user.can_admin_interact()) - return 1 + return TRUE if(scan) return check_access(scan) - return 0 + return FALSE /obj/machinery/computer/card/proc/get_target_rank() return modify && modify.assignment ? modify.assignment : "Unassigned" @@ -134,18 +152,18 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if(!istype(id_card)) return ..() - if(!scan && (ACCESS_CHANGE_IDS in id_card.access)) + if(!scan && check_access(id_card)) user.drop_item() - id_card.loc = src + id_card.forceMove(src) scan = id_card playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) else if(!modify) user.drop_item() - id_card.loc = src + id_card.forceMove(src) modify = id_card playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) - SSnanoui.update_uis(src) + SStgui.update_uis(src) attack_hand(user) //Check if you can't touch a job in any way whatsoever @@ -156,68 +174,123 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) /obj/machinery/computer/card/proc/job_blacklisted_partial(datum/job/job) return (job.type in blacklisted_partial) -//Logic check for Topic() if you can open the job +// Logic check for if you can open the job /obj/machinery/computer/card/proc/can_open_job(datum/job/job) if(job) - if(!job_blacklisted_full(job) && !job_blacklisted_partial(job) && job_in_department(job, FALSE)) - if((job.total_positions <= GLOB.player_list.len * (max_relative_positions / 100))) - var/delta = (world.time / 10) - GLOB.time_last_changed_position - if((change_position_cooldown < delta) || (opened_positions[job.title] < 0)) - return 1 - return -2 - return -1 - return 0 + if(job_blacklisted_full(job)) + return FALSE + if(job_blacklisted_partial(job)) + return FALSE + if(!job_in_department(job, FALSE)) + return FALSE + if((job.total_positions > GLOB.player_list.len * (max_relative_positions / 100))) + return FALSE + if(opened_positions[job.title] < 0) + return TRUE + var/delta = (world.time / 10) - GLOB.time_last_changed_position + if(change_position_cooldown < delta) + return TRUE + return FALSE -//Logic check for Topic() if you can close the job +// Logic check for if you can close the job /obj/machinery/computer/card/proc/can_close_job(datum/job/job) if(job) - if(!job_blacklisted_full(job) && !job_blacklisted_partial(job) && job_in_department(job, FALSE)) - if(job.total_positions > job.current_positions && !(job in SSjobs.prioritized_jobs)) - var/delta = (world.time / 10) - GLOB.time_last_changed_position - if((change_position_cooldown < delta) || (opened_positions[job.title] > 0)) - return 1 - return -2 - return -1 - return 0 + if(job_blacklisted_full(job)) + return FALSE + if(job_blacklisted_partial(job)) + return FALSE + if(!job_in_department(job, FALSE)) + return FALSE + if(job in SSjobs.prioritized_jobs) // different to above + return FALSE + if(job.total_positions <= job.current_positions) // different to above + return FALSE + if(opened_positions[job.title] > 0) // different to above + return TRUE + var/delta = (world.time / 10) - GLOB.time_last_changed_position + if(change_position_cooldown < delta) + return TRUE + return FALSE /obj/machinery/computer/card/proc/can_prioritize_job(datum/job/job) if(job) - if(!job_blacklisted_full(job) && job_in_department(job, FALSE)) - if(job in SSjobs.prioritized_jobs) - return 2 - else - if(SSjobs.prioritized_jobs.len >= 3) - return 0 - if(job.total_positions <= job.current_positions) - return 0 - return 1 - return -1 + if(job_blacklisted_full(job)) + return FALSE + if(!job_in_department(job, FALSE)) + return FALSE + if(job in SSjobs.prioritized_jobs) + return TRUE // because this also lets us un-prioritize the job + if(SSjobs.prioritized_jobs.len >= 3) + return FALSE + if(job.total_positions <= job.current_positions) + return FALSE + return TRUE + return FALSE +/obj/machinery/computer/card/proc/has_idchange_access() + return scan && scan.access && (ACCESS_CHANGE_IDS in scan.access) ? TRUE : FALSE -/obj/machinery/computer/card/proc/job_in_department(datum/job/targetjob, includecivs = 1) +/obj/machinery/computer/card/proc/job_in_department(datum/job/targetjob, includecivs = TRUE) if(!scan || !scan.access) - return 0 + return FALSE if(!target_dept) - return 1 + return TRUE if(!scan.assignment) - return 0 - if(ACCESS_CAPTAIN in scan.access) - return 1 + return FALSE + if(has_idchange_access()) + return TRUE if(!targetjob || !targetjob.title) - return 0 + return FALSE if(targetjob.title in get_subordinates(scan.assignment, includecivs)) - return 1 - return 0 + return TRUE + return FALSE /obj/machinery/computer/card/proc/get_subordinates(rank, addcivs) var/list/jobs_returned = list() for(var/datum/job/thisjob in SSjobs.occupations) + if(thisjob.title in GLOB.nonhuman_positions) // hides AI from list when Captain ID is inserted into dept console + continue if(rank in thisjob.department_head) jobs_returned += thisjob.title if(addcivs) jobs_returned += "Civilian" return jobs_returned +/obj/machinery/computer/card/proc/get_employees(list/selectedranks) + var/list/names_returned = list() + if(isnull(GLOB.data_core.general) || isnull(GLOB.data_core.security)) + return names_returned + for(var/datum/data/record/R in GLOB.data_core.general) + if(!R.fields || !R.fields["name"] || !R.fields["real_rank"]) + continue + if(!(R.fields["real_rank"] in selectedranks)) + continue + for(var/datum/data/record/E in GLOB.data_core.security) + if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) + var/buttontext + var/isdemotable = FALSE + if(status_valid_for_demotion(E.fields["criminal"])) + buttontext = "Demote" + isdemotable = TRUE + else if(E.fields["criminal"] == SEC_RECORD_STATUS_DEMOTE) + buttontext = "Pending Demotion" + else + buttontext = "Ineligible" + names_returned.Add(list(list( + "name" = E.fields["name"], + "crimstat" = E.fields["criminal"], + "title" = R.fields["real_rank"], + "buttontext" = buttontext, + "demotable" = isdemotable + ))) + break + return names_returned + +/obj/machinery/computer/card/proc/status_valid_for_demotion(crimstat) + if(crimstat in list(SEC_RECORD_STATUS_NONE, SEC_RECORD_STATUS_MONITOR, SEC_RECORD_STATUS_RELEASED, SEC_RECORD_STATUS_SEARCH)) + return TRUE + return FALSE + /obj/machinery/computer/card/attack_ai(var/mob/user as mob) return attack_hand(user) @@ -227,103 +300,119 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) if(stat & (NOPOWER|BROKEN)) return - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/card/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/card/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "identification_computer.tmpl", src.name, 775, 700) + ui = new(user, src, ui_key, "CardComputer", name, 800, 800, master_ui, state) ui.open() -/obj/machinery/computer/card/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["src"] = UID() - data["station_name"] = station_name() +/obj/machinery/computer/card/tgui_data(mob/user) + var/list/data = list() data["mode"] = mode - data["printing"] = printing - data["manifest"] = GLOB.data_core ? GLOB.data_core.get_manifest(0) : null - data["target_name"] = modify ? modify.name : "-----" - data["target_owner"] = modify && modify.registered_name ? modify.registered_name : "-----" - data["target_rank"] = get_target_rank() - data["scan_name"] = scan ? scan.name : "-----" - data["scan_owner"] = scan && scan.registered_name ? scan.registered_name : null - data["authenticated"] = is_authenticated(user) - data["has_modify"] = !!modify - data["account_number"] = modify ? modify.associated_account_number : null - data["centcom_access"] = is_centcom() - data["all_centcom_access"] = null - data["regions"] = null + data["modify_name"] = modify ? modify.name : FALSE + data["modify_owner"] = modify && modify.registered_name ? modify.registered_name : "-----" + data["modify_rank"] = get_target_rank() + data["modify_lastlog"] = modify && modify.lastlog ? modify.lastlog : FALSE + data["scan_name"] = scan ? scan.name : FALSE + data["scan_rank"] = scan ? scan.rank : FALSE + + data["authenticated"] = is_authenticated(user) ? TRUE : FALSE data["target_dept"] = target_dept - data["card_is_owned"] = modify && modify.owner_ckey + data["iscentcom"] = is_centcom() ? TRUE : FALSE - var/list/job_formats = SSjobs.format_jobs_for_id_computer(modify) - - data["top_jobs"] = format_jobs(list("Captain", "Custom"), data["target_rank"], job_formats) - data["engineering_jobs"] = format_jobs(GLOB.engineering_positions, data["target_rank"], job_formats) - data["medical_jobs"] = format_jobs(GLOB.medical_positions, data["target_rank"], job_formats) - data["science_jobs"] = format_jobs(GLOB.science_positions, data["target_rank"], job_formats) - data["security_jobs"] = format_jobs(GLOB.security_positions, data["target_rank"], job_formats) - data["support_jobs"] = format_jobs(GLOB.support_positions, data["target_rank"], job_formats) - data["civilian_jobs"] = format_jobs(GLOB.civilian_positions, data["target_rank"], job_formats) - data["special_jobs"] = format_jobs(GLOB.whitelisted_positions, data["target_rank"], job_formats) - data["centcom_jobs"] = format_jobs(get_all_centcom_jobs(), data["target_rank"], job_formats) - data["card_skins"] = format_card_skins(get_station_card_skins()) - - data["job_slots"] = format_job_slots() - - var/time_to_wait = round(change_position_cooldown - ((world.time / 10) - GLOB.time_last_changed_position), 1) - var/mins = round(time_to_wait / 60) - var/seconds = time_to_wait - (60*mins) - data["cooldown_mins"] = mins - data["cooldown_secs"] = (seconds < 10) ? "0[seconds]" : seconds - - if(mode == 3 && is_authenticated(user)) - data["id_change_html"] = SSjobs.fetch_transfer_record_html(is_centcom()) - - if(modify) - data["current_skin"] = modify.icon_state - - if(modify && is_centcom()) - var/list/all_centcom_access = list() - for(var/access in get_all_centcom_access()) - all_centcom_access.Add(list(list( - "desc" = replacetext(get_centcom_access_desc(access), " ", " "), - "ref" = access, - "allowed" = (access in modify.access) ? 1 : 0))) - - data["all_centcom_access"] = all_centcom_access - data["all_centcom_skins"] = format_card_skins(get_centcom_card_skins()) - - else if(modify) - var/list/regions = list() - for(var/i = 1; i <= 7; i++) - var/list/accesses = list() - for(var/access in get_region_accesses(i)) - if(get_access_desc(access)) - accesses.Add(list(list( - "desc" = replacetext(get_access_desc(access), " ", " "), - "ref" = access, - "allowed" = (access in modify.access) ? 1 : 0))) - - regions.Add(list(list( - "name" = get_region_accesses_name(i), - "accesses" = accesses))) - - data["regions"] = regions + switch(mode) + if(IDCOMPUTER_SCREEN_TRANSFER) // JOB TRANSFER + if(modify) + if(!scan) + return data + else if(target_dept) + data["jobs_dept"] = get_subordinates(scan.assignment, FALSE) + data["canterminate"] = has_idchange_access() + else + data["account_number"] = modify ? modify.associated_account_number : null + data["jobs_top"] = list("Captain", "Custom") + data["jobs_engineering"] = GLOB.engineering_positions + data["jobs_medical"] = GLOB.medical_positions + data["jobs_science"] = GLOB.science_positions + data["jobs_security"] = GLOB.security_positions + data["jobs_service"] = GLOB.service_positions + data["jobs_supply"] = GLOB.supply_positions - "Head of Personnel" + data["jobs_civilian"] = GLOB.civilian_positions + data["jobs_karma"] = GLOB.whitelisted_positions + data["jobs_centcom"] = get_all_centcom_jobs() + data["jobFormats"] = SSjobs.format_jobs_for_id_computer(modify) + data["current_skin"] = modify.icon_state + data["card_skins"] = format_card_skins(get_station_card_skins()) + data["all_centcom_skins"] = is_centcom() ? format_card_skins(get_centcom_card_skins()) : FALSE + if(IDCOMPUTER_SCREEN_SLOTS) // JOB SLOTS + data["job_slots"] = format_job_slots() + data["priority_jobs"] = list() + for(var/datum/job/a in SSjobs.prioritized_jobs) + data["priority_jobs"] += a.title + var/time_to_wait = round(change_position_cooldown - ((world.time / 10) - GLOB.time_last_changed_position), 1) + if(time_to_wait > 0) + var/mins = round(time_to_wait / 60) + var/seconds = time_to_wait - (60*mins) + seconds = (seconds < 10) ? "0[seconds]" : seconds + data["cooldown_time"] = "[mins]:[seconds]" + else + data["cooldown_time"] = FALSE + if(IDCOMPUTER_SCREEN_ACCESS) // ACCESS CHANGES + if(modify) + data["selectedAccess"] = modify.access + data["regions"] = get_accesslist_static_data(REGION_GENERAL, is_centcom() ? REGION_CENTCOMM : REGION_COMMAND) + if(IDCOMPUTER_SCREEN_RECORDS) // RECORDS + if(is_authenticated(user)) + data["records"] = SSjobs.format_job_change_records(data["iscentcom"]) + if(IDCOMPUTER_SCREEN_DEPT) // DEPARTMENT EMPLOYEE LIST + if(is_authenticated(user) && scan) // .requires both (aghosts don't count) + data["jobs_dept"] = get_subordinates(scan.assignment, FALSE) + data["people_dept"] = get_employees(data["jobs_dept"]) return data -/obj/machinery/computer/card/Topic(href, href_list) - if(..()) - return 1 +/obj/machinery/computer/card/proc/regenerate_id_name() + if(modify) + modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") - switch(href_list["choice"]) - if("modify") +/obj/machinery/computer/card/tgui_act(action, params) + if(..()) + return + . = TRUE + + // 1st, handle the functions that require no authorization at all + + switch(action) + if("scan") // inserting or removing your authorizing ID + if(scan) + if(ishuman(usr)) + scan.forceMove(get_turf(src)) + if(!usr.get_active_hand() && Adjacent(usr)) + usr.put_in_hands(scan) + scan = null + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) + else + scan.forceMove(get_turf(src)) + scan = null + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) + else if(Adjacent(usr)) + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/card/id)) + if(!check_access(I)) + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) + to_chat(usr, "This card does not have access.") + return FALSE + usr.drop_item() + I.forceMove(src) + scan = I + playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) + return + if("modify") // inserting or removing the ID you plan to modify if(modify) GLOB.data_core.manifest_modify(modify.registered_name, modify.assignment) - modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") + regenerate_id_name() if(ishuman(usr)) modify.forceMove(get_turf(src)) if(!usr.get_active_hand() && Adjacent(usr)) @@ -341,260 +430,299 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) I.forceMove(src) modify = I playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) + return + if("mode") // changing mode in the menu + mode = text2num(params["mode"]) + return - if("scan") - if(scan) - if(ishuman(usr)) - scan.forceMove(get_turf(src)) - if(!usr.get_active_hand() && Adjacent(usr)) - usr.put_in_hands(scan) - scan = null - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) - else - scan.forceMove(get_turf(src)) - scan = null - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) - else if(Adjacent(usr)) - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) + // Everything below HERE requires auth + if(!is_authenticated(usr)) + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) + to_chat(usr, "This function is not available unless you are logged in.") + return FALSE - if("access") - if(href_list["allowed"] && !target_dept) - if(is_authenticated(usr)) - var/access_type = text2num(href_list["access_target"]) - var/access_allowed = text2num(href_list["allowed"]) - if(access_type in (is_centcom() ? get_all_centcom_access() : get_all_accesses())) - modify.access -= access_type - if(!access_allowed) - modify.access += access_type - - if("skin") - if(!target_dept) - var/skin = href_list["skin_target"] - if(is_authenticated(usr) && modify && ((skin in get_station_card_skins()) || ((skin in get_centcom_card_skins()) && is_centcom()))) - modify.icon_state = href_list["skin_target"] - - if("assign") - if(is_authenticated(usr) && modify) - var/t1 = href_list["assign_target"] - if(target_dept && modify.assignment == "Demoted") - visible_message("[src]: Demoted individuals must see the HoP for a new job.") - return 0 + // 2nd, handle the functions that are available to head-level consoles (department consoles) + switch(action) + if("assign") // transfer to a new job + if(!modify) + return + var/t1 = params["assign_target"] + if(target_dept) + if(modify.assignment == "Demoted") + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) + visible_message("[src]: Reassigning a demoted individual requires a full ID computer.") + return FALSE if(!job_in_department(SSjobs.GetJob(modify.rank), FALSE)) - visible_message("[src]: Cross-department job transfers must be done by the HoP.") - return 0 + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) + visible_message("[src]: Reassigning someone outside your department requires a full ID computer.") + return FALSE if(!job_in_department(SSjobs.GetJob(t1))) - return 0 - if(t1 == "Custom") - var/temp_t = sanitize(copytext(input("Enter a custom job assignment.","Assignment"),1,MAX_MESSAGE_LEN)) - //let custom jobs function as an impromptu alt title, mainly for sechuds - if(temp_t && modify) - SSjobs.log_job_transfer(modify.registered_name, modify.getRankAndAssignment(), temp_t, scan.registered_name) - modify.assignment = temp_t - log_game("[key_name(usr)] has given \"[modify.registered_name]\" the custom job title \"[temp_t]\".") + return FALSE + if(t1 == "Custom") + var/temp_t = sanitize(reject_bad_name(copytext(input("Enter a custom job assignment.", "Assignment"), 1, MAX_MESSAGE_LEN), TRUE)) + //let custom jobs function as an impromptu alt title, mainly for sechuds + if(temp_t && scan && modify) + var/oldrank = modify.getRankAndAssignment() + SSjobs.log_job_transfer(modify.registered_name, oldrank, temp_t, scan.registered_name, null) + modify.lastlog = "[station_time_timestamp()]: Reassigned by \"[scan.registered_name]\" from \"[oldrank]\" to \"[temp_t]\"." + modify.assignment = temp_t + log_game("[key_name(usr)] ([scan.assignment]) has reassigned \"[modify.registered_name]\" from \"[oldrank]\" to \"[temp_t]\".") + SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] has transferred \"[modify.registered_name]\" the \"[oldrank]\" to \"[temp_t]\".") + else + var/list/access = list() + if(is_centcom() && islist(get_centcom_access(t1))) + access = get_centcom_access(t1) else - var/list/access = list() - if(is_centcom() && islist(get_centcom_access(t1))) - access = get_centcom_access(t1) - else - var/datum/job/jobdatum - for(var/jobtype in typesof(/datum/job)) - var/datum/job/J = new jobtype - if(ckey(J.title) == ckey(t1)) - jobdatum = J - break - if(!jobdatum) - to_chat(usr, "No log exists for this job: [t1]") - return + var/datum/job/jobdatum + for(var/jobtype in typesof(/datum/job)) + var/datum/job/J = new jobtype + if(ckey(J.title) == ckey(t1)) + jobdatum = J + break + if(!jobdatum) + to_chat(usr, "No log exists for this job: [t1]") + return - access = jobdatum.get_access() + access = jobdatum.get_access() - var/jobnamedata = modify.getRankAndAssignment() - log_game("[key_name(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".") - if(t1 == "Civilian") - message_admins("[key_name_admin(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".") + var/jobnamedata = modify.getRankAndAssignment() + log_game("[key_name(usr)] ([scan.assignment]) has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".") + if(t1 == "Civilian") + message_admins("[key_name_admin(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".") - SSjobs.log_job_transfer(modify.registered_name, jobnamedata, t1, scan.registered_name) + SSjobs.log_job_transfer(modify.registered_name, jobnamedata, t1, scan.registered_name, null) + modify.lastlog = "[station_time_timestamp()]: Reassigned by \"[scan.registered_name]\" from \"[jobnamedata]\" to \"[t1]\"." + SSjobs.notify_dept_head(t1, "[scan.registered_name] has transferred \"[modify.registered_name]\" the \"[jobnamedata]\" to \"[t1]\".") + if(modify.owner_uid) SSjobs.slot_job_transfer(modify.rank, t1) - var/mob/living/carbon/human/H = modify.getPlayer() - if(istype(H)) - if(jobban_isbanned(H, t1)) - message_admins("[ADMIN_FULLMONTY(H)] has been assigned the job [t1], in possible violation of their job ban.") - if(H.mind) - H.mind.playtime_role = t1 + var/mob/living/carbon/human/H = modify.getPlayer() + if(istype(H)) + if(jobban_isbanned(H, t1)) + message_admins("[ADMIN_FULLMONTY(H)] has been assigned the job [t1], in possible violation of their job ban.") + if(H.mind) + H.mind.playtime_role = t1 - modify.access = access - modify.rank = t1 - modify.assignment = t1 + modify.access = access + modify.rank = t1 + modify.assignment = t1 + regenerate_id_name() + return + if("demote") + if(modify.assignment == "Demoted") + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) + visible_message("[src]: Demoted crew cannot be demoted any further. If further action is warranted, ask the Captain about Termination.") + return FALSE + if(!job_in_department(SSjobs.GetJob(modify.rank), FALSE)) + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) + visible_message("[src]: Heads may only demote members of their own department.") + return FALSE + var/reason = sanitize(copytext(input("Enter legal reason for demotion. Enter nothing to cancel.","Legal Demotion"), 1, MAX_MESSAGE_LEN)) + if(!reason || !is_authenticated(usr) || !modify) + return FALSE + var/list/access = list() + var/datum/job/jobdatum = new /datum/job/civilian + access = jobdatum.get_access() + var/jobnamedata = modify.getRankAndAssignment() + var/m_ckey = modify.getPlayerCkey() + var/m_ckey_text = m_ckey ? "([m_ckey])" : "(no ckey)" + log_game("[key_name(usr)] ([scan.assignment]) has demoted \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".") + message_admins("[key_name_admin(usr)] has demoted \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".") + usr.create_log(MISC_LOG, "demoted \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\"") + SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Demoted", scan.registered_name, reason) + modify.lastlog = "[station_time_timestamp()]: DEMOTED by \"[scan.registered_name]\" ([scan.assignment]) from \"[jobnamedata]\" for: \"[reason]\"." + SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] ([scan.assignment]) has demoted \"[modify.registered_name]\" ([jobnamedata]) for \"[reason]\".") + modify.access = access + modify.rank = "Civilian" + modify.assignment = "Demoted" + modify.icon_state = "id" + regenerate_id_name() + return + if("terminate") + if(!has_idchange_access()) // because captain/HOP can use this even on dept consoles + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) + visible_message("[src]: Only the Captain or HOP may completely terminate the employment of a crew member.") + return FALSE + var/jobnamedata = modify.getRankAndAssignment() + var/reason = sanitize(copytext(input("Enter legal reason for termination. Enter nothing to cancel.", "Employment Termination"), 1, MAX_MESSAGE_LEN)) + if(!reason || !has_idchange_access() || !modify) + return FALSE + var/m_ckey = modify.getPlayerCkey() + var/m_ckey_text = m_ckey ? "([m_ckey])" : "(no ckey)" + log_game("[key_name(usr)] ([scan.assignment]) has terminated \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".") + message_admins("[key_name_admin(usr)] has terminated \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\" for: \"[reason]\".") + usr.create_log(MISC_LOG, "terminated the employment of \"[modify.registered_name]\" [m_ckey_text] the \"[jobnamedata]\"") + SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Terminated", scan.registered_name, reason) + modify.lastlog = "[station_time_timestamp()]: TERMINATED by \"[scan.registered_name]\" ([scan.assignment]) from \"[jobnamedata]\" for: \"[reason]\"." + SSjobs.notify_dept_head(modify.rank, "[scan.registered_name] ([scan.assignment]) has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\" for \"[reason]\".") + modify.assignment = "Terminated" + modify.access = list() + regenerate_id_name() + return + if("make_job_available") // MAKE ANOTHER JOB POSITION AVAILABLE FOR LATE JOINERS + var/edit_job_target = params["job"] + var/datum/job/j = SSjobs.GetJob(edit_job_target) + if(!job_in_department(j, FALSE)) + return FALSE + if(!j) + return FALSE + if(!can_open_job(j)) + return FALSE + if(opened_positions[edit_job_target] >= 0) + GLOB.time_last_changed_position = world.time / 10 + j.total_positions++ + opened_positions[edit_job_target]++ + log_game("[key_name(usr)] ([scan.assignment]) has opened a job slot for job \"[j.title]\".") + message_admins("[key_name_admin(usr)] has opened a job slot for job \"[j.title]\".") + return + if("make_job_unavailable") // MAKE JOB POSITION UNAVAILABLE FOR LATE JOINERS + var/edit_job_target = params["job"] + var/datum/job/j = SSjobs.GetJob(edit_job_target) + if(!job_in_department(j, FALSE)) + return FALSE + if(!j) + return FALSE + if(!can_close_job(j)) + return FALSE + //Allow instant closing without cooldown if a position has been opened before + if(opened_positions[edit_job_target] <= 0) + GLOB.time_last_changed_position = world.time / 10 + j.total_positions-- + opened_positions[edit_job_target]-- + log_game("[key_name(usr)] ([scan.assignment]) has closed a job slot for job \"[j.title]\".") + message_admins("[key_name_admin(usr)] has closed a job slot for job \"[j.title]\".") + return + if("remote_demote") + var/reason = sanitize(copytext(input("Enter legal reason for demotion. Enter nothing to cancel.","Legal Demotion"), 1, MAX_MESSAGE_LEN)) + if(!reason || !is_authenticated(usr) || !scan) + return FALSE + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == params["remote_demote"]) + var/datum/job/j = SSjobs.GetJob(E.fields["real_rank"]) + var/tempname = params["remote_demote"] + var/temprank = E.fields["real_rank"] + if(!j) + visible_message("[src]: This employee has either no job, or a customized job ([temprank]).") + return FALSE + if(!job_in_department(j, FALSE)) + visible_message("[src]: Only the head of this employee may demote them.") + return FALSE + for(var/datum/data/record/R in GLOB.data_core.security) + if(R.fields["id"] == E.fields["id"]) + if(status_valid_for_demotion(R.fields["criminal"])) + set_criminal_status(usr, R, SEC_RECORD_STATUS_DEMOTE, reason, scan.assignment) + Radio.autosay("[scan.registered_name] ([scan.assignment]) has set [tempname] ([temprank]) to demote for: [reason]", name, "Command", list(z)) + message_admins("[key_name_admin(usr)] ([scan.assignment]) has set [tempname] ([temprank]) to demote for: \"[reason]\"") + log_game("[key_name(usr)] ([scan.assignment]) has set \"[tempname]\" ([temprank]) to demote for: \"[reason]\".") + SSjobs.notify_by_name(tempname, "[scan.registered_name] ([scan.assignment]) has ordered your demotion. Report to their office, or the HOP. Reason given: \"[reason]\"") + else + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) + to_chat(usr, "[src]: Cannot demote, due to their current security status.") + return FALSE + return + return - if("reg") - if(is_authenticated(usr) && !target_dept) - var/t2 = modify - if((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) - var/temp_name = reject_bad_name(href_list["reg"]) - if(temp_name) - modify.registered_name = temp_name - else - visible_message("[src] buzzes rudely.") - SSnanoui.update_uis(src) + // Everything below here requires a full ID computer (dept consoles do not qualify) + if(target_dept) + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) + to_chat(usr, "This function is not available on department-level consoles.") + return - if("account") - if(is_authenticated(usr) && !target_dept) - var/t2 = modify - if((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) - var/account_num = text2num(href_list["account"]) - modify.associated_account_number = account_num - SSnanoui.update_uis(src) + // 3rd, handle the functions that require a full ID computer + switch(action) + // Changing basic card info + if("reg") // registered name on card + var/temp_name = reject_bad_name(input(usr, "Who is this ID for?", "ID Card Renaming", modify.registered_name), TRUE) + if(!modify || !temp_name) + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 0) + visible_message("[src] buzzes rudely.") + return FALSE + modify.registered_name = temp_name + regenerate_id_name() + return + if("account") // card account number + var/account_num = input(usr, "Account Number", "Input Number", null) as num|null + if(!scan || !modify) + return FALSE + modify.associated_account_number = clamp(round(account_num), 0, 999999) + return + if("skin") + if(!modify) + return FALSE + var/skin = params["skin_target"] + var/skin_list = is_centcom() ? get_centcom_card_skins() : get_station_card_skins() + if(skin in skin_list) + modify.icon_state = skin + return + // Changing card access + if("set") // add/remove a single access number + var/access = text2num(params["access"]) + var/list/changable = is_centcom() ? get_all_centcom_access() + get_all_accesses() : get_all_accesses() + if(access in changable) + if(access in modify.access) + modify.access -= access + else + modify.access += access + return + if("grant_region") + var/region = text2num(params["region"]) + if(isnull(region) || region < REGION_GENERAL || region > (is_centcom() ? REGION_CENTCOMM : REGION_COMMAND)) + return + modify.access |= get_region_accesses(region) + return + if("deny_region") + var/region = text2num(params["region"]) + if(isnull(region) || region < REGION_GENERAL || region > (is_centcom() ? REGION_CENTCOMM : REGION_COMMAND)) + return + modify.access -= get_region_accesses(region) + return + if("clear_all") + modify.access = list() + return + if("grant_all") + modify.access = get_all_accesses() + return - if("mode") - mode = text2num(href_list["mode_target"]) + // JOB SLOT MANAGEMENT functions - if("wipe_my_logs") - if(is_authenticated(usr) && is_centcom()) - var/delcount = SSjobs.delete_log_records(scan.registered_name, FALSE) - if(delcount) - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - SSnanoui.update_uis(src) + if("prioritize_job") // TOGGLE WHETHER JOB APPEARS AS PRIORITIZED IN THE LOBBY + var/priority_target = params["job"] + var/datum/job/j = SSjobs.GetJob(priority_target) + if(!j) + return FALSE + if(!job_in_department(j)) + return FALSE + var/priority = TRUE + if(j in SSjobs.prioritized_jobs) + SSjobs.prioritized_jobs -= j + priority = FALSE + else if(SSjobs.prioritized_jobs.len < 3) + SSjobs.prioritized_jobs += j + else + return FALSE + log_game("[key_name(usr)] ([scan.assignment]) [priority ? "prioritized" : "unprioritized"] the job \"[j.title]\".") + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) + return - if("wipe_all_logs") + if("wipe_all_logs") // Delete all records from 'records' section if(is_authenticated(usr) && !target_dept) var/delcount = SSjobs.delete_log_records(scan.registered_name, TRUE) if(delcount) + message_admins("[key_name_admin(usr)] has wiped all ID computer logs.") + usr.create_log(MISC_LOG, "wiped all ID computer logs.") playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - SSnanoui.update_uis(src) + return - if("print") - if(!printing && !target_dept) - printing = 1 - playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - spawn(50) - printing = null - SSnanoui.update_uis(src) + // Everything below here is exclusive to the CC card computer. + if(!is_centcom()) + return - var/obj/item/paper/P = new(loc) - if(mode == 2) - P.name = "crew manifest ([station_time_timestamp()])" - P.info = {"

    Crew Manifest

    -
    - [GLOB.data_core ? GLOB.data_core.get_manifest(0) : ""] - "} - else if(modify && !mode) - P.name = "access report" - P.info = {"

    Access Report

    - Prepared By: [scan && scan.registered_name ? scan.registered_name : "Unknown"]
    - For: [modify.registered_name ? modify.registered_name : "Unregistered"]
    -
    - Assignment: [modify.assignment]
    - Account Number: #[modify.associated_account_number]
    - Blood Type: [modify.blood_type]

    - Access:
    - "} - - var/first = 1 - for(var/A in modify.access) - P.info += "[first ? "" : ", "][get_access_desc(A)]" - first = 0 - P.info += "
    " - - if("terminate") - if(is_authenticated(usr) && !target_dept) - var/jobnamedata = modify.getRankAndAssignment() - log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".") - message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".") - SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Terminated", scan.registered_name) - modify.assignment = "Terminated" - modify.access = list() - - if("demote") - if(is_authenticated(usr)) - if(modify.assignment == "Demoted") - visible_message("[src]: Demoted crew cannot be demoted any further. If further action is warranted, ask the Captain about Termination.") - return 0 - if(!job_in_department(SSjobs.GetJob(modify.rank), FALSE)) - visible_message("[src]: Heads may only demote members of their own department.") - return 0 - - var/list/access = list() - var/datum/job/jobdatum = new /datum/job/civilian - access = jobdatum.get_access() - - var/jobnamedata = modify.getRankAndAssignment() - log_game("[key_name(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Demoted)\".") - message_admins("[key_name_admin(usr)] has demoted \"[modify.registered_name]\" the \"[jobnamedata]\" to \"Civilian (Demoted)\".") - SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Demoted", scan.registered_name) - - modify.access = access - modify.rank = "Civilian" - modify.assignment = "Demoted" - modify.icon_state = "id" - - if("make_job_available") - // MAKE ANOTHER JOB POSITION AVAILABLE FOR LATE JOINERS - if(is_authenticated(usr)) - var/edit_job_target = href_list["job"] - var/datum/job/j = SSjobs.GetJob(edit_job_target) - if(!job_in_department(j, FALSE)) - return 0 - if(!j) - return 0 - if(can_open_job(j) != 1) - return 0 - if(opened_positions[edit_job_target] >= 0) - GLOB.time_last_changed_position = world.time / 10 - j.total_positions++ - opened_positions[edit_job_target]++ - log_game("[key_name(usr)] has opened a job slot for job \"[j]\".") - message_admins("[key_name_admin(usr)] has opened a job slot for job \"[j.title]\".") - SSnanoui.update_uis(src) - - if("make_job_unavailable") - // MAKE JOB POSITION UNAVAILABLE FOR LATE JOINERS - if(is_authenticated(usr)) - var/edit_job_target = href_list["job"] - var/datum/job/j = SSjobs.GetJob(edit_job_target) - if(!job_in_department(j, FALSE)) - return 0 - if(!j) - return 0 - if(can_close_job(j) != 1) - return 0 - //Allow instant closing without cooldown if a position has been opened before - if(opened_positions[edit_job_target] <= 0) - GLOB.time_last_changed_position = world.time / 10 - j.total_positions-- - opened_positions[edit_job_target]-- - log_game("[key_name(usr)] has closed a job slot for job \"[j]\".") - message_admins("[key_name_admin(usr)] has closed a job slot for job \"[j.title]\".") - SSnanoui.update_uis(src) - - if("prioritize_job") - // TOGGLE WHETHER JOB APPEARS AS PRIORITIZED IN THE LOBBY - if(is_authenticated(usr) && !target_dept) - var/priority_target = href_list["job"] - var/datum/job/j = SSjobs.GetJob(priority_target) - if(!j) - return 0 - if(!job_in_department(j)) - return 0 - var/priority = TRUE - if(j in SSjobs.prioritized_jobs) - SSjobs.prioritized_jobs -= j - priority = FALSE - else if(SSjobs.prioritized_jobs.len < 3) - SSjobs.prioritized_jobs += j - else - return 0 - log_game("[key_name(usr)] [priority ? "prioritized" : "unprioritized"] the job \"[j.title]\".") + switch(action) + if("wipe_my_logs") + var/delcount = SSjobs.delete_log_records(scan.registered_name, FALSE) + if(delcount) playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - if(modify) - modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") - return 1 /obj/machinery/computer/card/centcom name = "\improper CentComm identification computer" @@ -604,6 +732,9 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) blacklisted_full = list() blacklisted_partial = list() +/obj/machinery/computer/card/centcom/is_centcom() + return TRUE + /obj/machinery/computer/card/minor name = "department management console" target_dept = TARGET_DEPT_GENERIC diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 3091caa82a2..04a4f7e0db4 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -1,3 +1,6 @@ +#define MENU_MAIN 1 +#define MENU_RECORDS 2 + /obj/machinery/computer/cloning name = "cloning console" icon = 'icons/obj/computer.dmi' @@ -6,11 +9,11 @@ circuit = /obj/item/circuitboard/cloning req_access = list(ACCESS_HEADS) //Only used for record deletion right now. var/obj/machinery/dna_scannernew/scanner = null //Linked scanner. For scanning. - var/list/pods = list() //Linked cloning pods. - var/temp = "" - var/scantemp = "Scanner ready." - var/menu = 1 //Which menu screen to display - var/list/records = list() + var/list/pods = null //Linked cloning pods. + var/list/temp = null + var/list/scantemp = null + var/menu = MENU_MAIN //Which menu screen to display + var/list/records = null var/datum/dna2/record/active_record = null var/obj/item/disk/data/diskette = null //Mostly so the geneticist can steal everything. var/loading = 0 // Nice loading text @@ -24,6 +27,9 @@ /obj/machinery/computer/cloning/Initialize() ..() + pods = list() + records = list() + set_scan_temp("Scanner ready.", "good") updatemodules() /obj/machinery/computer/cloning/Destroy() @@ -91,7 +97,7 @@ W.loc = src src.diskette = W to_chat(user, "You insert [W].") - SSnanoui.update_uis(src) + SStgui.update_uis(src) return else if(istype(W, /obj/item/multitool)) var/obj/item/multitool/M = W @@ -117,19 +123,21 @@ return updatemodules() - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/cloning/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/computer/cloning/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) if(stat & (NOPOWER|BROKEN)) return - // Set up the Nano UI - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + var/datum/asset/cloning/assets = get_asset_datum(/datum/asset/cloning) + assets.send(user) + + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "cloning_console.tmpl", "Cloning Console UI", 640, 520) + ui = new(user, src, ui_key, "CloningConsole", "Cloning Console", 640, 520) ui.open() -/obj/machinery/computer/cloning/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/computer/cloning/tgui_data(mob/user) var/data[0] data["menu"] = src.menu data["scanner"] = sanitize("[src.scanner]") @@ -143,7 +151,18 @@ if(pod.efficiency > 5) canpodautoprocess = 1 - tempods.Add(list(list("pod" = "\ref[pod]", "name" = sanitize(capitalize(pod.name)), "biomass" = pod.biomass))) + var/status = "idle" + if(pod.mess) + status = "mess" + else if(pod.occupant && !(pod.stat & NOPOWER)) + status = "cloning" + tempods.Add(list(list( + "pod" = "\ref[pod]", + "name" = sanitize(capitalize(pod.name)), + "biomass" = pod.biomass, + "status" = status, + "progress" = (pod.occupant && pod.occupant.stat != DEAD) ? pod.get_completion() : 0 + ))) data["pods"] = tempods data["loading"] = loading @@ -164,192 +183,194 @@ data["selected_pod"] = "\ref[selected_pod]" var/list/temprecords[0] for(var/datum/dna2/record/R in records) - var tempRealName = R.dna.real_name + var/tempRealName = R.dna.real_name temprecords.Add(list(list("record" = "\ref[R]", "realname" = sanitize(tempRealName)))) data["records"] = temprecords - if(src.menu == 3) - if(src.active_record) - data["activerecord"] = "\ref[src.active_record]" - var/obj/item/implant/health/H = null - if(src.active_record.implant) - H = locate(src.active_record.implant) + if(selected_pod && (selected_pod in pods) && selected_pod.biomass >= CLONE_BIOMASS) + data["podready"] = 1 + else + data["podready"] = 0 - if((H) && (istype(H))) - data["health"] = H.sensehealth() - data["realname"] = sanitize(src.active_record.dna.real_name) - data["unidentity"] = src.active_record.dna.uni_identity - data["strucenzymes"] = src.active_record.dna.struc_enzymes - if(selected_pod && (selected_pod in pods) && selected_pod.biomass >= CLONE_BIOMASS) - data["podready"] = 1 - else - data["podready"] = 0 + data["modal"] = tgui_modal_data(src) return data -/obj/machinery/computer/cloning/Topic(href, href_list) +/obj/machinery/computer/cloning/tgui_act(action, params) if(..()) - return 1 - - if(loading) + return + if(stat & (NOPOWER|BROKEN)) return - if(href_list["scan"] && scanner && scanner.occupant) - scantemp = "Scanner ready." - - loading = 1 - - spawn(20) - if(can_brainscan() && scan_mode) - scan_mob(scanner.occupant, scan_brain = 1) - else - scan_mob(scanner.occupant) - - loading = 0 - SSnanoui.update_uis(src) - - if(href_list["task"]) - switch(href_list["task"]) - if("autoprocess") - autoprocess = 1 - SSnanoui.update_uis(src) - if("stopautoprocess") - autoprocess = 0 - SSnanoui.update_uis(src) - - //No locking an open scanner. - else if((href_list["lock"]) && (!isnull(src.scanner))) - if((!src.scanner.locked) && (src.scanner.occupant)) - src.scanner.locked = 1 - else - src.scanner.locked = 0 - - else if(href_list["view_rec"]) - src.active_record = locate(href_list["view_rec"]) - if(istype(src.active_record,/datum/dna2/record)) - if((isnull(src.active_record.ckey))) - qdel(src.active_record) - src.temp = "Error: Record corrupt." - else - src.menu = 3 - else - src.active_record = null - src.temp = "Error: Record missing." - - else if(href_list["del_rec"]) - if((!src.active_record) || (src.menu < 3)) - return - if(src.menu == 3) //If we are viewing a record, confirm deletion - src.temp = "Please confirm that you want to delete the record?" - src.menu = 4 - - else if(src.menu == 4) - var/obj/item/card/id/C = usr.get_active_hand() - if(istype(C)||istype(C, /obj/item/pda)) - if(src.check_access(C)) - src.records.Remove(src.active_record) - qdel(src.active_record) - src.temp = "Record deleted." - src.menu = 2 + . = TRUE + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_ANSWER) + if(params["id"] == "del_rec" && active_record) + var/obj/item/card/id/C = usr.get_active_hand() + if(!istype(C) && !istype(C, /obj/item/pda)) + set_temp("ID not in hand.", "danger") + return + if(check_access(C)) + records.Remove(active_record) + qdel(active_record) + set_temp("Record deleted.", "success") + menu = MENU_RECORDS else - src.temp = "Error: Access denied." - - else if(href_list["disk"]) //Load or eject. - switch(href_list["disk"]) - if("load") - if((isnull(src.diskette)) || isnull(src.diskette.buf)) - src.temp = "Error: The disk's data could not be read." - SSnanoui.update_uis(src) - return - if(isnull(src.active_record)) - src.temp = "Error: No active record was found." - src.menu = 1 - SSnanoui.update_uis(src) - return - - src.active_record = src.diskette.buf.copy() - - src.temp = "Load successful." - - if("eject") - if(!isnull(src.diskette)) - src.diskette.loc = src.loc - src.diskette = null - - else if(href_list["save_disk"]) //Save to disk! - if((isnull(src.diskette)) || (src.diskette.read_only) || (isnull(src.active_record))) - src.temp = "Error: The data could not be saved." - SSnanoui.update_uis(src) + set_temp("Access denied.", "danger") return - // DNA2 makes things a little simpler. - src.diskette.buf=src.active_record.copy() - src.diskette.buf.types=0 - switch(href_list["save_disk"]) //Save as Ui/Ui+Ue/Se - if("ui") - src.diskette.buf.types=DNA2_BUF_UI - if("ue") - src.diskette.buf.types=DNA2_BUF_UI|DNA2_BUF_UE - if("se") - src.diskette.buf.types=DNA2_BUF_SE - src.diskette.name = "data disk - '[src.active_record.dna.real_name]'" - src.temp = "Save \[[href_list["save_disk"]]\] successful." + switch(action) + if("scan") + if(!scanner || !scanner.occupant || loading) + return + set_scan_temp("Scanner ready.", "good") + loading = TRUE - else if(href_list["refresh"]) - SSnanoui.update_uis(src) - - else if(href_list["selectpod"]) - var/obj/machinery/clonepod/selected = locate(href_list["selectpod"]) - if(istype(selected) && (selected in pods)) - selected_pod = selected - - else if(href_list["clone"]) - var/datum/dna2/record/C = locate(href_list["clone"]) - //Look for that player! They better be dead! - if(istype(C)) - //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs. - if(!pods.len) - temp = "Error: No cloning pod detected." - else - var/obj/machinery/clonepod/pod = selected_pod - var/cloneresult - if(!selected_pod) - temp = "Error: No cloning pod selected." - else if(pod.occupant) - temp = "Error: The cloning pod is currently occupied." - else if(pod.biomass < CLONE_BIOMASS) - temp = "Error: Not enough biomass." - else if(pod.mess) - temp = "Error: The cloning pod is malfunctioning." - else if(!config.revival_cloning) - temp = "Error: Unable to initiate cloning cycle." + spawn(20) + if(can_brainscan() && scan_mode) + scan_mob(scanner.occupant, scan_brain = TRUE) else - cloneresult = pod.growclone(C) - if(cloneresult) - if(cloneresult > 0) - temp = "Initiating cloning cycle..." - records.Remove(C) - qdel(C) - menu = 1 + scan_mob(scanner.occupant) + loading = FALSE + SStgui.update_uis(src) + if("autoprocess") + autoprocess = text2num(params["on"]) > 0 + if("lock") + if(isnull(scanner) || !scanner.occupant) //No locking an open scanner. + return + scanner.locked = !scanner.locked + if("view_rec") + var/ref = params["ref"] + if(!length(ref)) + return + active_record = locate(ref) + if(istype(active_record)) + if(isnull(active_record.ckey)) + qdel(active_record) + set_temp("Error: Record corrupt.", "danger") + else + var/obj/item/implant/health/H = null + if(active_record.implant) + H = locate(active_record.implant) + var/list/payload = list( + activerecord = "\ref[active_record]", + health = (H && istype(H)) ? H.sensehealth() : "", + realname = sanitize(active_record.dna.real_name), + unidentity = active_record.dna.uni_identity, + strucenzymes = active_record.dna.struc_enzymes, + ) + tgui_modal_message(src, action, "", null, payload) + else + active_record = null + set_temp("Error: Record missing.", "danger") + if("del_rec") + if(!active_record) + return + tgui_modal_boolean(src, action, "Please confirm that you want to delete the record by holding your ID and pressing Delete:", yes_text = "Delete", no_text = "Cancel") + if("disk") // Disk management. + if(!length(params["option"])) + return + switch(params["option"]) + if("load") + if(isnull(diskette) || isnull(diskette.buf)) + set_temp("Error: The disk's data could not be read.", "danger") + return + else if(isnull(active_record)) + set_temp("Error: No active record was found.", "danger") + menu = MENU_MAIN + return + + active_record = diskette.buf.copy() + set_temp("Successfully loaded from disk.", "success") + if("save") + if(isnull(diskette) || diskette.read_only || isnull(active_record)) + set_temp("Error: The data could not be saved.", "danger") + return + + // DNA2 makes things a little simpler. + var/types + switch(params["savetype"]) // Save as Ui/Ui+Ue/Se + if("ui") + types = DNA2_BUF_UI + if("ue") + types = DNA2_BUF_UI|DNA2_BUF_UE + if("se") + types = DNA2_BUF_SE + else + set_temp("Error: Invalid save format.", "danger") + return + diskette.buf = active_record.copy() + diskette.buf.types = types + diskette.name = "data disk - '[active_record.dna.real_name]'" + set_temp("Successfully saved to disk.", "success") + if("eject") + if(!isnull(diskette)) + diskette.loc = loc + diskette = null + if("refresh") + SStgui.update_uis(src) + if("selectpod") + var/ref = params["ref"] + if(!length(ref)) + return + var/obj/machinery/clonepod/selected = locate(ref) + if(istype(selected) && (selected in pods)) + selected_pod = selected + if("clone") + var/ref = params["ref"] + if(!length(ref)) + return + var/datum/dna2/record/C = locate(ref) + //Look for that player! They better be dead! + if(istype(C)) + tgui_modal_clear(src) + //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs. + if(!length(pods)) + set_temp("Error: No cloning pod detected.", "danger") + else + var/obj/machinery/clonepod/pod = selected_pod + var/cloneresult + if(!selected_pod) + set_temp("Error: No cloning pod selected.", "danger") + else if(pod.occupant) + set_temp("Error: The cloning pod is currently occupied.", "danger") + else if(pod.biomass < CLONE_BIOMASS) + set_temp("Error: Not enough biomass.", "danger") + else if(pod.mess) + set_temp("Error: The cloning pod is malfunctioning.", "danger") + else if(!config.revival_cloning) + set_temp("Error: Unable to initiate cloning cycle.", "danger") else - temp = "[C.name] => Initialisation failure." - + cloneresult = pod.growclone(C) + if(cloneresult) + set_temp("Initiating cloning cycle...", "success") + records.Remove(C) + qdel(C) + menu = MENU_MAIN + else + set_temp("Error: Initialisation failure.", "danger") + else + set_temp("Error: Data corruption.", "danger") + if("menu") + menu = clamp(text2num(params["num"]), MENU_MAIN, MENU_RECORDS) + if("toggle_mode") + if(loading) + return + if(can_brainscan()) + scan_mode = !scan_mode + else + scan_mode = FALSE + if("eject") + if(usr.incapacitated() || !scanner || loading) + return + scanner.eject_occupant(usr) + scanner.add_fingerprint(usr) + if("cleartemp") + temp = null else - temp = "Error: Data corruption." - - else if(href_list["menu"]) - src.menu = text2num(href_list["menu"]) - temp = "" - scantemp = "Scanner ready." - else if(href_list["toggle_mode"]) - if(can_brainscan()) - scan_mode = !scan_mode - else - scan_mode = 0 + return FALSE src.add_fingerprint(usr) - SSnanoui.update_uis(src) - return /obj/machinery/computer/cloning/proc/scan_mob(mob/living/carbon/human/subject as mob, var/scan_brain = 0) if(stat & NOPOWER) @@ -360,46 +381,46 @@ return if(isnull(subject) || (!(ishuman(subject))) || (!subject.dna)) if(isalien(subject)) - scantemp = "Error: Xenomorphs are not scannable." - SSnanoui.update_uis(src) + set_scan_temp("Xenomorphs are not scannable.", "bad") + SStgui.update_uis(src) return // can add more conditions for specific non-human messages here else - scantemp = "Error: Subject species is not scannable." - SSnanoui.update_uis(src) + set_scan_temp("Subject species is not scannable.", "bad") + SStgui.update_uis(src) return if(subject.get_int_organ(/obj/item/organ/internal/brain)) var/obj/item/organ/internal/brain/Brn = subject.get_int_organ(/obj/item/organ/internal/brain) if(istype(Brn)) if(NO_SCAN in Brn.dna.species.species_traits) - scantemp = "Error: [Brn.dna.species.name_plural] are not scannable." - SSnanoui.update_uis(src) + set_scan_temp("[Brn.dna.species.name_plural] are not scannable.", "bad") + SStgui.update_uis(src) return if(!subject.get_int_organ(/obj/item/organ/internal/brain)) - scantemp = "Error: No brain detected in subject." - SSnanoui.update_uis(src) + set_scan_temp("No brain detected in subject.", "bad") + SStgui.update_uis(src) return if(subject.suiciding) - scantemp = "Error: Subject has committed suicide and is not scannable." - SSnanoui.update_uis(src) + set_scan_temp("Subject has committed suicide and is not scannable.", "bad") + SStgui.update_uis(src) return if((!subject.ckey) || (!subject.client)) - scantemp = "Error: Subject's brain is not responding. Further attempts after a short delay may succeed." - SSnanoui.update_uis(src) + set_scan_temp("Subject's brain is not responding. Further attempts after a short delay may succeed.", "bad") + SStgui.update_uis(src) return if((NOCLONE in subject.mutations) && src.scanner.scan_level < 2) - scantemp = "Error: Subject has incompatible genetic mutations." - SSnanoui.update_uis(src) + set_scan_temp("Subject has incompatible genetic mutations.", "bad") + SStgui.update_uis(src) return if(!isnull(find_record(subject.ckey))) - scantemp = "Subject already in database." - SSnanoui.update_uis(src) + set_scan_temp("Subject already in database.") + SStgui.update_uis(src) return for(var/obj/machinery/clonepod/pod in pods) if(pod.occupant && pod.clonemind == subject.mind) - scantemp = "Subject already getting cloned." - SSnanoui.update_uis(src) + set_scan_temp("Subject already getting cloned.") + SStgui.update_uis(src) return subject.dna.check_integrity() @@ -434,8 +455,8 @@ R.mind = "\ref[subject.mind]" src.records += R - scantemp = "Subject successfully scanned. " + extra_info - SSnanoui.update_uis(src) + set_scan_temp("Subject successfully scanned. [extra_info]", "good") + SStgui.update_uis(src) //Find a specific record by key. /obj/machinery/computer/cloning/proc/find_record(var/find_key) @@ -451,3 +472,30 @@ /obj/machinery/computer/cloning/proc/can_brainscan() return (scanner && scanner.scan_level > 3) + +/** + * Sets a temporary message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * style - The style of the message: (color name), info, success, warning, danger + */ +/obj/machinery/computer/cloning/proc/set_temp(text = "", style = "info", update_now = FALSE) + temp = list(text = text, style = style) + if(update_now) + SStgui.update_uis(src) + +/** + * Sets a temporary scan message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * color - The color of the message: (color name) + */ +/obj/machinery/computer/cloning/proc/set_scan_temp(text = "", color = "", update_now = FALSE) + scantemp = list(text = text, color = color) + if(update_now) + SStgui.update_uis(src) + +#undef MENU_MAIN +#undef MENU_RECORDS diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index e161891cdbc..60563e692b3 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -1,35 +1,39 @@ #define COMM_SCREEN_MAIN 1 #define COMM_SCREEN_STAT 2 #define COMM_SCREEN_MESSAGES 3 -#define COMM_SCREEN_SECLEVEL 4 #define COMM_AUTHENTICATION_NONE 0 #define COMM_AUTHENTICATION_MIN 1 #define COMM_AUTHENTICATION_MAX 2 +#define COMM_MSGLEN_MINIMUM 6 +#define COMM_CCMSGLEN_MINIMUM 20 + // The communications computer /obj/machinery/computer/communications name = "communications console" - desc = "This can be used for various important functions. Still under developement." + desc = "This allows the Captain to contact Central Command, or change the alert level. It also allows the command staff to call the Escape Shuttle." icon_keyboard = "tech_key" icon_screen = "comm" req_access = list(ACCESS_HEADS) circuit = /obj/item/circuitboard/communications - var/prints_intercept = 1 - var/authenticated = COMM_AUTHENTICATION_NONE var/list/messagetitle = list() var/list/messagetext = list() - var/currmsg = 0 - var/aicurrmsg = 0 + var/currmsg + + var/authenticated = COMM_AUTHENTICATION_NONE var/menu_state = COMM_SCREEN_MAIN var/ai_menu_state = COMM_SCREEN_MAIN - var/message_cooldown = 0 - var/centcomm_message_cooldown = 0 + var/aicurrmsg + + var/message_cooldown + var/centcomm_message_cooldown var/tmp_alertlevel = 0 var/stat_msg1 var/stat_msg2 - var/display_type="blank" + var/display_type = "blank" + var/display_icon var/datum/announcement/priority/crew_announcement = new @@ -70,123 +74,113 @@ feedback_inc("alert_comms_blue",1) tmp_alertlevel = 0 -/obj/machinery/computer/communications/Topic(href, href_list) - if(..(href, href_list)) - return 1 - - if(!is_secure_level(src.z)) +/obj/machinery/computer/communications/tgui_act(action, params) + if(..()) + return + if(!is_secure_level(z)) to_chat(usr, "Unable to establish a connection: You're too far away from the station!") - return 1 + return - if(href_list["login"]) + . = TRUE + + if(action == "auth") if(!ishuman(usr)) to_chat(usr, "Access denied.") + return FALSE + // Logout function. + if(authenticated != COMM_AUTHENTICATION_NONE) + authenticated = COMM_AUTHENTICATION_NONE + crew_announcement.announcer = null + setMenuState(usr, COMM_SCREEN_MAIN) return - + // Login function. var/list/access = usr.get_access() if(allowed(usr)) authenticated = COMM_AUTHENTICATION_MIN - if(ACCESS_CAPTAIN in access) authenticated = COMM_AUTHENTICATION_MAX var/mob/living/carbon/human/H = usr var/obj/item/card/id = H.get_idcard(TRUE) if(istype(id)) crew_announcement.announcer = GetNameAndAssignmentFromId(id) - - SSnanoui.update_uis(src) - return - - if(href_list["logout"]) - authenticated = COMM_AUTHENTICATION_NONE - crew_announcement.announcer = "" - setMenuState(usr,COMM_SCREEN_MAIN) - SSnanoui.update_uis(src) + if(authenticated == COMM_AUTHENTICATION_NONE) + to_chat(usr, "You need to wear your ID.") return + // All functions below this point require authentication. if(!is_authenticated(usr)) - return 1 + return FALSE - switch(href_list["operation"]) + switch(action) if("main") - setMenuState(usr,COMM_SCREEN_MAIN) - - if("changeseclevel") - setMenuState(usr,COMM_SCREEN_SECLEVEL) + setMenuState(usr, COMM_SCREEN_MAIN) if("newalertlevel") if(isAI(usr) || isrobot(usr)) to_chat(usr, "Firewalls prevent you from changing the alert level.") - return 1 + return else if(usr.can_admin_interact()) - change_security_level(text2num(href_list["level"])) - return 1 + change_security_level(text2num(params["level"])) + return else if(!ishuman(usr)) to_chat(usr, "Security measures prevent you from changing the alert level.") - return 1 + return - var/mob/living/carbon/human/L = usr - var/obj/item/card = L.get_active_hand() - var/obj/item/card/id/I = (card && card.GetID()) || L.wear_id || L.wear_pda - if(istype(I, /obj/item/pda)) - var/obj/item/pda/pda = I - I = pda.id - if(I && istype(I)) + var/mob/living/carbon/human/H = usr + var/obj/item/card/id/I = H.get_idcard(TRUE) + if(istype(I)) if(ACCESS_CAPTAIN in I.access) - change_security_level(text2num(href_list["level"])) + change_security_level(text2num(params["level"])) else to_chat(usr, "You are not authorized to do this.") - setMenuState(usr,COMM_SCREEN_MAIN) + setMenuState(usr, COMM_SCREEN_MAIN) else - to_chat(usr, "You need to swipe your ID.") + to_chat(usr, "You need to wear your ID.") if("announce") if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(message_cooldown) + if(message_cooldown > world.time) to_chat(usr, "Please allow at least one minute to pass between announcements.") - SSnanoui.update_uis(src) return var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") - if(!input || message_cooldown || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) + if(!input || message_cooldown > world.time || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + return + if(length(input) < COMM_MSGLEN_MINIMUM) + to_chat(usr, "Message '[input]' is too short. [COMM_MSGLEN_MINIMUM] character minimum.") return crew_announcement.Announce(input) - message_cooldown = 1 - spawn(600)//One minute cooldown - message_cooldown = 0 + message_cooldown = world.time + 600 //One minute if("callshuttle") var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") if(!input || ..() || !is_authenticated(usr)) - SSnanoui.update_uis(src) return - call_shuttle_proc(usr, input) if(SSshuttle.emergency.timer) post_status("shuttle") - setMenuState(usr,COMM_SCREEN_MAIN) + setMenuState(usr, COMM_SCREEN_MAIN) if("cancelshuttle") if(isAI(usr) || isrobot(usr)) to_chat(usr, "Firewalls prevent you from recalling the shuttle.") - SSnanoui.update_uis(src) - return 1 + return var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No") if(response == "Yes") cancel_call_proc(usr) if(SSshuttle.emergency.timer) post_status("shuttle") - setMenuState(usr,COMM_SCREEN_MAIN) + setMenuState(usr, COMM_SCREEN_MAIN) if("messagelist") - currmsg = 0 - if(href_list["msgid"]) - setCurrentMessage(usr, text2num(href_list["msgid"])) - setMenuState(usr,COMM_SCREEN_MESSAGES) + currmsg = null + aicurrmsg = null + if(params["msgid"]) + setCurrentMessage(usr, text2num(params["msgid"])) + setMenuState(usr, COMM_SCREEN_MESSAGES) if("delmessage") - if(href_list["msgid"]) - currmsg = text2num(href_list["msgid"]) + if(params["msgid"]) + currmsg = text2num(params["msgid"]) var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No") if(response == "Yes") if(currmsg) @@ -196,95 +190,95 @@ messagetitle.Remove(title) messagetext.Remove(text) if(currmsg == id) - currmsg = 0 + currmsg = null if(aicurrmsg == id) - aicurrmsg = 0 - setMenuState(usr,COMM_SCREEN_MESSAGES) + aicurrmsg = null + setMenuState(usr, COMM_SCREEN_MESSAGES) if("status") - setMenuState(usr,COMM_SCREEN_STAT) + setMenuState(usr, COMM_SCREEN_STAT) // Status display stuff if("setstat") - display_type=href_list["statdisp"] + display_type = params["statdisp"] switch(display_type) if("message") + display_icon = null post_status("message", stat_msg1, stat_msg2, usr) if("alert") - post_status("alert", href_list["alert"], user = usr) + display_icon = params["alert"] + post_status("alert", params["alert"], user = usr) else - post_status(href_list["statdisp"], user = usr) - setMenuState(usr,COMM_SCREEN_STAT) + display_icon = null + post_status(params["statdisp"], user = usr) + setMenuState(usr, COMM_SCREEN_STAT) if("setmsg1") stat_msg1 = clean_input("Line 1", "Enter Message Text", stat_msg1) - setMenuState(usr,COMM_SCREEN_STAT) + setMenuState(usr, COMM_SCREEN_STAT) if("setmsg2") stat_msg2 = clean_input("Line 2", "Enter Message Text", stat_msg2) - setMenuState(usr,COMM_SCREEN_STAT) + setMenuState(usr, COMM_SCREEN_STAT) if("nukerequest") if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(centcomm_message_cooldown) + if(centcomm_message_cooldown > world.time) to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) return var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","") if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) + return + if(length(input) < COMM_CCMSGLEN_MINIMUM) + to_chat(usr, "Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.") return Nuke_request(input, usr) to_chat(usr, "Request sent.") log_game("[key_name(usr)] has requested the nuclear codes from Centcomm") GLOB.priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg') - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) + centcomm_message_cooldown = world.time + 6000 // 10 minutes + setMenuState(usr, COMM_SCREEN_MAIN) if("MessageCentcomm") if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(centcomm_message_cooldown) + if(centcomm_message_cooldown > world.time) to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) return var/input = stripped_input(usr, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) + return + if(length(input) < COMM_CCMSGLEN_MINIMUM) + to_chat(usr, "Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.") return Centcomm_announce(input, usr) print_centcom_report(input, station_time_timestamp() + " Captain's Message") to_chat(usr, "Message transmitted.") log_game("[key_name(usr)] has made a Centcomm announcement: [input]") - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) + centcomm_message_cooldown = world.time + 6000 // 10 minutes + setMenuState(usr, COMM_SCREEN_MAIN) // OMG SYNDICATE ...LETTERHEAD if("MessageSyndicate") if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (src.emagged)) - if(centcomm_message_cooldown) + if(centcomm_message_cooldown > world.time) to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) return var/input = stripped_input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) + return + if(length(input) < COMM_CCMSGLEN_MINIMUM) + to_chat(usr, "Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.") return Syndicate_announce(input, usr) to_chat(usr, "Message transmitted.") log_game("[key_name(usr)] has made a Syndicate announcement: [input]") - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) + centcomm_message_cooldown = world.time + 6000 // 10 minutes + setMenuState(usr, COMM_SCREEN_MAIN) if("RestoreBackup") to_chat(usr, "Backup routing data restored!") src.emagged = 0 - setMenuState(usr,COMM_SCREEN_MAIN) + setMenuState(usr, COMM_SCREEN_MAIN) if("RestartNanoMob") if(SSmob_hunt) @@ -298,18 +292,13 @@ else to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") - if("ToggleATC") - GLOB.atc.squelched = !GLOB.atc.squelched - to_chat(usr, "ATC traffic is now: [GLOB.atc.squelched ? "Disabled" : "Enabled"].") - SSnanoui.update_uis(src) - return 1 /obj/machinery/computer/communications/emag_act(user as mob) if(!emagged) src.emagged = 1 to_chat(user, "You scramble the communication routing circuits!") - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/machinery/computer/communications/attack_ai(var/mob/user as mob) return src.attack_hand(user) @@ -325,28 +314,25 @@ to_chat(user, "Unable to establish a connection: You're too far away from the station!") return - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/communications/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui) +/obj/machinery/computer/communications/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, "comm_console.tmpl", "Communications Console", 400, 500) - // open the new ui window + ui = new(user, src, ui_key, "CommunicationsComputer", name, 500, 600, master_ui, state) ui.open() -/obj/machinery/computer/communications/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/computer/communications/tgui_data(mob/user) + var/list/data = list() data["is_ai"] = isAI(user) || isrobot(user) data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state data["emagged"] = emagged data["authenticated"] = is_authenticated(user, 0) - data["screen"] = getMenuState(usr) + data["authmax"] = data["authenticated"] == COMM_AUTHENTICATION_MAX ? TRUE : FALSE data["stat_display"] = list( "type" = display_type, + "icon" = display_icon, "line_1" = (stat_msg1 ? stat_msg1 : "-----"), "line_2" = (stat_msg2 ? stat_msg2 : "-----"), @@ -364,12 +350,20 @@ ) ) - data["security_level"] = GLOB.security_level + data["security_level"] = GLOB.security_level + switch(GLOB.security_level) + if(SEC_LEVEL_GREEN) + data["security_level_color"] = "green"; + if(SEC_LEVEL_BLUE) + data["security_level_color"] = "blue"; + if(SEC_LEVEL_RED) + data["security_level_color"] = "red"; + else + data["security_level_color"] = "purple"; data["str_security_level"] = capitalize(get_security_level()) data["levels"] = list( - list("id" = SEC_LEVEL_GREEN, "name" = "Green"), - list("id" = SEC_LEVEL_BLUE, "name" = "Blue"), - //SEC_LEVEL_RED = list("name"="Red"), + list("id" = SEC_LEVEL_GREEN, "name" = "Green", "icon" = "dove"), + list("id" = SEC_LEVEL_BLUE, "name" = "Blue", "icon" = "eye"), ) var/list/msg_data = list() @@ -377,29 +371,30 @@ msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i))) data["messages"] = msg_data + + data["current_message"] = null + data["current_message_title"] = null if((data["is_ai"] && aicurrmsg) || (!data["is_ai"] && currmsg)) data["current_message"] = data["is_ai"] ? messagetext[aicurrmsg] : messagetext[currmsg] data["current_message_title"] = data["is_ai"] ? messagetitle[aicurrmsg] : messagetitle[currmsg] data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null + data["msg_cooldown"] = message_cooldown ? (round((message_cooldown - world.time) / 10)) : 0 + data["cc_cooldown"] = centcomm_message_cooldown ? (round((centcomm_message_cooldown - world.time) / 10)) : 0 - var/shuttle[0] - switch(SSshuttle.emergency.mode) - if(SHUTTLE_IDLE, SHUTTLE_RECALL) - shuttle["callStatus"] = 2 //#define - else - shuttle["callStatus"] = 1 - if(SSshuttle.emergency.mode == SHUTTLE_CALL) + var/secondsToRefuel = SSshuttle.secondsToRefuel() + data["esc_callable"] = SSshuttle.emergency.mode == SHUTTLE_IDLE && !secondsToRefuel ? TRUE : FALSE + data["esc_recallable"] = SSshuttle.emergency.mode == SHUTTLE_CALL ? TRUE : FALSE + data["esc_status"] = FALSE + if(SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL) var/timeleft = SSshuttle.emergency.timeLeft() - shuttle["eta"] = "[timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" - - data["shuttle"] = shuttle - - data["atcSquelched"] = GLOB.atc.squelched - + data["esc_status"] = SSshuttle.emergency.mode == SHUTTLE_CALL ? "ETA:" : "RECALLING:" + data["esc_status"] += " [timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" + else if(secondsToRefuel) + data["esc_status"] = "Refueling: [secondsToRefuel / 60 % 60]:[add_zero(num2text(secondsToRefuel % 60), 2)]" + data["esc_section"] = data["esc_status"] || data["esc_callable"] || data["esc_recallable"] || data["lastCallLoc"] return data - /obj/machinery/computer/communications/proc/setCurrentMessage(var/mob/user,var/value) if(isAI(user) || isrobot(user)) aicurrmsg = value @@ -418,12 +413,6 @@ else menu_state=value -/obj/machinery/computer/communications/proc/getMenuState(var/mob/user) - if(isAI(user) || isrobot(user)) - return ai_menu_state - else - return menu_state - /proc/call_shuttle_proc(var/mob/user, var/reason) if(GLOB.sent_strike_team == 1) to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") @@ -527,24 +516,16 @@ SSshuttle.autoEvac() return ..() -/proc/print_command_report(text = "", title = "Central Command Update") +/proc/print_command_report(text = "", title = "Central Command Update", add_to_records = TRUE) for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) if(!(C.stat & (BROKEN|NOPOWER)) && is_station_contact(C.z)) var/obj/item/paper/P = new /obj/item/paper(C.loc) P.name = "paper- '[title]'" P.info = text P.update_icon() - C.messagetitle.Add("[title]") - C.messagetext.Add(text) - for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) - var/turf/T = get_turf(P.computer) - if(T && P.program_state != PROGRAM_STATE_KILLED && is_station_contact(T.z)) - if(P.computer) - var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] - if(printer) - printer.print_text(text, "paper- '[title]'") - P.messagetitle.Add("[title]") - P.messagetext.Add(text) + if(add_to_records) + C.messagetitle.Add("[title]") + C.messagetext.Add(text) /proc/print_centcom_report(text = "", title = "Incoming Message") for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) @@ -555,12 +536,5 @@ P.update_icon() C.messagetitle.Add("[title]") C.messagetext.Add(text) - for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) - var/turf/T = get_turf(P.computer) - if(T && P.program_state != PROGRAM_STATE_KILLED && is_admin_level(T.z)) - if(P.computer) - var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] - if(printer) - printer.print_text(text, "paper- '[title]'") - P.messagetitle.Add("[title]") - P.messagetext.Add(text) + + diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 131956ca3c1..d6a1ccdd33c 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -27,7 +27,7 @@ return tgui_interact(user) -/obj/machinery/computer/crew/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) +/obj/machinery/computer/crew/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) crew_monitor.tgui_interact(user, ui_key, ui, force_open) /obj/machinery/computer/crew/interact(mob/user) diff --git a/code/game/machinery/computer/depot.dm b/code/game/machinery/computer/depot.dm index 62c82d9bd95..f2e89923eb4 100644 --- a/code/game/machinery/computer/depot.dm +++ b/code/game/machinery/computer/depot.dm @@ -165,7 +165,7 @@ alerts_when_broken = TRUE /obj/machinery/computer/syndicate_depot/selfdestruct/get_menu(mob/user) - var menutext = {"Syndicate Depot Fusion Reactor Control
    + var/menutext = {"Syndicate Depot Fusion Reactor Control


    Disable Containment Field
    "} return menutext @@ -193,10 +193,6 @@ /obj/machinery/computer/syndicate_depot/shieldcontrol/New() . = ..() perimeterarea = locate(/area/syndicate_depot/perimeter) - if(istype(perimeterarea) && (GAMEMODE_IS_NUCLEAR || prob(20))) - spawn(200) - perimeterarea.perimeter_shields_up() - depotarea.perimeter_shield_status = TRUE /obj/machinery/computer/syndicate_depot/shieldcontrol/Destroy() if(istype(perimeterarea) && perimeterarea.shield_list.len) @@ -204,7 +200,7 @@ return ..() /obj/machinery/computer/syndicate_depot/shieldcontrol/get_menu(mob/user) - var menutext = {"Syndicate Depot Shield Grid Control
    + var/menutext = {"Syndicate Depot Shield Grid Control

    "} menutext += {"(SYNDI-LEADER) Whole-base Shield: [perimeterarea.shield_list.len ? "ON" : "OFF"] ([perimeterarea.shield_list.len ? "Disable" : "Enable"])
    "} menutext += {"(SYNDI-LEADER) Armory Shield: [depotarea.shield_list.len ? "ON" : "OFF"] ([depotarea.shield_list.len ? "Disable" : "Enable"])
    "} diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index 74cc417e7ce..d7987477a65 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -11,55 +11,57 @@ light_range_on = 2 - verb/AccessInternals() - set category = "Object" - set name = "Access Computer's Internals" - set src in oview(1) - if(get_dist(src, usr) > 1 || usr.restrained() || usr.lying || usr.stat || istype(usr, /mob/living/silicon)) - return - - opened = !opened - if(opened) - to_chat(usr, "The access panel is now open.") - else - to_chat(usr, "The access panel is now closed.") +// What the fuck even is this +/obj/machinery/computer/aiupload/verb/AccessInternals() + set category = "Object" + set name = "Access Computer's Internals" + set src in oview(1) + if(get_dist(src, usr) > 1 || usr.restrained() || usr.lying || usr.stat || istype(usr, /mob/living/silicon)) return + opened = !opened + if(opened) + to_chat(usr, "The access panel is now open.") + else + to_chat(usr, "The access panel is now closed.") + return - attackby(obj/item/O as obj, mob/user as mob, params) - if(istype(O, /obj/item/aiModule)) - if(!current)//no AI selected - to_chat(user, "No AI selected. Please chose a target before proceeding with upload.") - return - var/turf/T = get_turf(current) - if(!atoms_share_level(T, src)) - to_chat(user, "Unable to establish a connection: You're too far away from the target silicon!") - return - var/obj/item/aiModule/M = O - M.install(src) + +/obj/machinery/computer/aiupload/attackby(obj/item/O as obj, mob/user as mob, params) + if(istype(O, /obj/item/aiModule)) + if(!current)//no AI selected + to_chat(user, "No AI selected. Please chose a target before proceeding with upload.") return - return ..() - - - attack_hand(var/mob/user as mob) - if(src.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") - return - if(src.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") + var/turf/T = get_turf(current) + if(!atoms_share_level(T, src)) + to_chat(user, "Unable to establish a connection: You're too far away from the target silicon!") return + var/obj/item/aiModule/M = O + M.install(src) + return + return ..() - src.current = select_active_ai(user) - if(!src.current) - to_chat(usr, "No active AIs detected.") - else - to_chat(usr, "[src.current.name] selected for law changes.") +/obj/machinery/computer/aiupload/attack_hand(var/mob/user as mob) + if(src.stat & NOPOWER) + to_chat(usr, "The upload computer has no power!") + return + if(src.stat & BROKEN) + to_chat(usr, "The upload computer is broken!") return - attack_ghost(user as mob) - return 1 + src.current = select_active_ai(user) + if(!src.current) + to_chat(usr, "No active AIs detected.") + else + to_chat(usr, "[src.current.name] selected for law changes.") + return + +/obj/machinery/computer/aiupload/attack_ghost(user as mob) + return 1 + +// Why is this not a subtype /obj/machinery/computer/borgupload name = "cyborg upload console" desc = "Used to upload laws to Cyborgs." @@ -69,35 +71,35 @@ var/mob/living/silicon/robot/current = null - attackby(obj/item/aiModule/module as obj, mob/user as mob, params) - if(istype(module, /obj/item/aiModule)) - if(!current)//no borg selected - to_chat(user, "No borg selected. Please chose a target before proceeding with upload.") - return - var/turf/T = get_turf(current) - if(!atoms_share_level(T, src)) - to_chat(user, "Unable to establish a connection: You're too far away from the target silicon!") - return - module.install(src) +/obj/machinery/computer/borgupload/attackby(obj/item/aiModule/module as obj, mob/user as mob, params) + if(istype(module, /obj/item/aiModule)) + if(!current)//no borg selected + to_chat(user, "No borg selected. Please chose a target before proceeding with upload.") return - return ..() - - - attack_hand(var/mob/user as mob) - if(src.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") - return - if(src.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") + var/turf/T = get_turf(current) + if(!atoms_share_level(T, src)) + to_chat(user, "Unable to establish a connection: You're too far away from the target silicon!") return + module.install(src) + return + return ..() - src.current = freeborg() - if(!src.current) - to_chat(usr, "No free cyborgs detected.") - else - to_chat(usr, "[src.current.name] selected for law changes.") +/obj/machinery/computer/borgupload/attack_hand(var/mob/user as mob) + if(src.stat & NOPOWER) + to_chat(usr, "The upload computer has no power!") + return + if(src.stat & BROKEN) + to_chat(usr, "The upload computer is broken!") return - attack_ghost(user as mob) + src.current = freeborg() + + if(!src.current) + to_chat(usr, "No free cyborgs detected.") + else + to_chat(usr, "[src.current.name] selected for law changes.") + return + +/obj/machinery/computer/borgupload/attack_ghost(user as mob) return 1 diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 3674358f07f..48e946a999b 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -1,10 +1,12 @@ -#define MED_DATA_MAIN 1 // Main menu #define MED_DATA_R_LIST 2 // Record list #define MED_DATA_MAINT 3 // Records maintenance #define MED_DATA_RECORD 4 // Record #define MED_DATA_V_DATA 5 // Virus database #define MED_DATA_MEDBOT 6 // Medbot monitor +#define FIELD(N, V, E) list(field = N, value = V, edit = E) +#define MED_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB) + /obj/machinery/computer/med_data //TODO:SANITY name = "medical records console" desc = "This can be used to check medical records." @@ -12,28 +14,55 @@ icon_screen = "medcomp" req_one_access = list(ACCESS_MEDICAL, ACCESS_FORENSICS_LOCKERS) circuit = /obj/item/circuitboard/med_data - var/obj/item/card/id/scan = null - var/authenticated = null - var/rank = null var/screen = null var/datum/data/record/active1 = null var/datum/data/record/active2 = null - var/temp = null + var/list/temp = null var/printing = null + // The below are used to make modal generation more convenient + var/static/list/field_edit_questions + var/static/list/field_edit_choices light_color = LIGHT_COLOR_DARKBLUE +/obj/machinery/computer/med_data/Initialize() + ..() + field_edit_questions = list( + // General + "sex" = "Please select new sex:", + "age" = "Please input new age:", + "fingerprint" = "Please input new fingerprint hash:", + "p_stat" = "Please select new physical status:", + "m_stat" = "Please select new mental status:", + // Medical + "blood_type" = "Please select new blood type:", + "b_dna" = "Please input new DNA:", + "mi_dis" = "Please input new minor disabilities:", + "mi_dis_d" = "Please summarize minor disabilities:", + "ma_dis" = "Please input new major disabilities:", + "ma_dis_d" = "Please summarize major disabilities:", + "alg" = "Please input new allergies:", + "alg_d" = "Please summarize allergies:", + "cdi" = "Please input new current diseases:", + "cdi_d" = "Please summarize current diseases:", + "notes" = "Please input new important notes:", + ) + field_edit_choices = list( + // General + "sex" = list("Male", "Female"), + "p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"), + "m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"), + // Medical + "blood_type" = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"), + ) + /obj/machinery/computer/med_data/Destroy() active1 = null active2 = null return ..() /obj/machinery/computer/med_data/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/card/id) && !scan) - usr.drop_item() - O.forceMove(src) - scan = O - ui_interact(user) + if(tgui_login_attackby(O, user)) return return ..() @@ -44,21 +73,23 @@ to_chat(user, "Unable to establish a connection: You're too far away from the station!") return add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/med_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/med_data/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "med_data.tmpl", name, 800, 380) + ui = new(user, src, ui_key, "MedicalRecords", "Medical Records", 800, 380, master_ui, state) ui.open() + ui.set_autoupdate(FALSE) -/obj/machinery/computer/med_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/computer/med_data/tgui_data(mob/user) var/data[0] data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated data["screen"] = screen - if(authenticated) + data["printing"] = printing + // This proc appends login state to data. + tgui_login_data(data, user) + if(data["loginState"]["logged_in"]) switch(screen) if(MED_DATA_R_LIST) if(!isnull(GLOB.data_core.general)) @@ -72,17 +103,17 @@ if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) var/list/fields = list() general["fields"] = fields - fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = null) - fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = null) - fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") - fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") - fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") - fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = "p_stat") - fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = "m_stat") + fields[++fields.len] = FIELD("Name", active1.fields["name"], null) + fields[++fields.len] = FIELD("ID", active1.fields["id"], null) + fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex") + fields[++fields.len] = FIELD("Age", active1.fields["age"], "age") + fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint") + fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], "p_stat") + fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], "m_stat") var/list/photos = list() general["photos"] = photos - photos[++photos.len] = list("photo" = active1.fields["photo-south"]) - photos[++photos.len] = list("photo" = active1.fields["photo-west"]) + photos[++photos.len] = active1.fields["photo-south"] + photos[++photos.len] = active1.fields["photo-west"] general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) general["empty"] = 0 else @@ -93,17 +124,17 @@ if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) var/list/fields = list() medical["fields"] = fields - fields[++fields.len] = list("field" = "Blood Type:", "value" = active2.fields["blood_type"], "edit" = "blood_type", "line_break" = 0) - fields[++fields.len] = list("field" = "DNA:", "value" = active2.fields["b_dna"], "edit" = "b_dna", "line_break" = 1) - fields[++fields.len] = list("field" = "Minor Disabilities:", "value" = active2.fields["mi_dis"], "edit" = "mi_dis", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_dis_d"], "edit" = "mi_dis_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Major Disabilities:", "value" = active2.fields["ma_dis"], "edit" = "ma_dis", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_dis_d"], "edit" = "ma_dis_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Allergies:", "value" = active2.fields["alg"], "edit" = "alg", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["alg_d"], "edit" = "alg_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Current Diseases:", "value" = active2.fields["cdi"], "edit" = "cdi", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["cdi_d"], "edit" = "cdi_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) + fields[++fields.len] = MED_FIELD("Blood Type", active2.fields["blood_type"], "blood_type", FALSE) + fields[++fields.len] = MED_FIELD("DNA", active2.fields["b_dna"], "b_dna", TRUE) + fields[++fields.len] = MED_FIELD("Minor Disabilities", active2.fields["mi_dis"], "mi_dis", FALSE) + fields[++fields.len] = MED_FIELD("Details", active2.fields["mi_dis_d"], "mi_dis_d", TRUE) + fields[++fields.len] = MED_FIELD("Major Disabilities", active2.fields["ma_dis"], "ma_dis", FALSE) + fields[++fields.len] = MED_FIELD("Details", active2.fields["ma_dis_d"], "ma_dis_d", TRUE) + fields[++fields.len] = MED_FIELD("Allergies", active2.fields["alg"], "alg", FALSE) + fields[++fields.len] = MED_FIELD("Details", active2.fields["alg_d"], "alg_d", TRUE) + fields[++fields.len] = MED_FIELD("Current Diseases", active2.fields["cdi"], "cdi", FALSE) + fields[++fields.len] = MED_FIELD("Details", active2.fields["cdi_d"], "cdi_d", TRUE) + fields[++fields.len] = MED_FIELD("Important Notes", active2.fields["notes"], "notes", TRUE) if(!active2.fields["comments"] || !islist(active2.fields["comments"])) active2.fields["comments"] = list() medical["comments"] = active2.fields["comments"] @@ -127,7 +158,9 @@ var/turf/T = get_turf(M) if(T) var/medbot = list() + var/area/A = get_area(T) medbot["name"] = M.name + medbot["area"] = A.name medbot["x"] = T.x medbot["y"] = T.y medbot["on"] = M.on @@ -138,395 +171,254 @@ else medbot["use_beaker"] = 0 data["medbots"] += list(medbot) + + data["modal"] = tgui_modal_data(src) return data -/obj/machinery/computer/med_data/Topic(href, href_list) +/obj/machinery/computer/med_data/tgui_act(action, params) if(..()) - return 1 + return + if(stat & (NOPOWER|BROKEN)) + return if(!GLOB.data_core.general.Find(active1)) active1 = null if(!GLOB.data_core.medical.Find(active2)) active2 = null - if(href_list["temp"]) - temp = null + . = TRUE + if(tgui_act_modal(action, params)) + return + if(tgui_login_act(action, params)) + return - if(href_list["temp_action"]) - if(href_list["temp_action"]) - var/temp_href = splittext(href_list["temp_action"], "=") - switch(temp_href[1]) - if("del_all2") - for(var/datum/data/record/R in GLOB.data_core.medical) - qdel(R) - setTemp("

    All records deleted.

    ") - if("p_stat") - if(active1) - switch(temp_href[2]) - if("deceased") - active1.fields["p_stat"] = "*Deceased*" - if("ssd") - active1.fields["p_stat"] = "*SSD*" - if("active") - active1.fields["p_stat"] = "Active" - if("unfit") - active1.fields["p_stat"] = "Physically Unfit" - if("disabled") - active1.fields["p_stat"] = "Disabled" - if("m_stat") - if(active1) - switch(temp_href[2]) - if("insane") - active1.fields["m_stat"] = "*Insane*" - if("unstable") - active1.fields["m_stat"] = "*Unstable*" - if("watch") - active1.fields["m_stat"] = "*Watch*" - if("stable") - active1.fields["m_stat"] = "Stable" - if("blood_type") - if(active2) - switch(temp_href[2]) - if("an") - active2.fields["blood_type"] = "A-" - if("bn") - active2.fields["blood_type"] = "B-" - if("abn") - active2.fields["blood_type"] = "AB-" - if("on") - active2.fields["blood_type"] = "O-" - if("ap") - active2.fields["blood_type"] = "A+" - if("bp") - active2.fields["blood_type"] = "B+" - if("abp") - active2.fields["blood_type"] = "AB+" - if("op") - active2.fields["blood_type"] = "O+" - if("del_r2") - QDEL_NULL(active2) - - if(href_list["scan"]) - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null + switch(action) + if("cleartemp") + temp = null else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I + . = FALSE - if(href_list["login"]) - if(isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - else if(istype(scan, /obj/item/card/id)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment + if(.) + return - if(authenticated) - active1 = null - active2 = null - screen = MED_DATA_MAIN + if(tgui_login_get().logged_in) + . = TRUE + switch(action) + if("screen") + screen = clamp(text2num(params["screen"]) || 0, MED_DATA_R_LIST, MED_DATA_MEDBOT) + active1 = null + active2 = null + if("vir") + var/type = text2path(params["vir"] || "") + if(!ispath(type, /datum/disease)) + return - if(authenticated) - if(href_list["logout"]) - authenticated = null - screen = null - active1 = null - active2 = null + var/datum/disease/D = new type(0) + var/list/payload = list( + name = D.name, + max_stages = D.max_stages, + spread_text = D.spread_text, + cure = D.cure_text || "None", + desc = D.desc, + severity = D.severity + ); + tgui_modal_message(src, "virus", "", null, payload) + qdel(D) + if("del_all") + for(var/datum/data/record/R in GLOB.data_core.medical) + qdel(R) + set_temp("All medical records deleted.") + if("del_r") + if(active2) + set_temp("Medical record deleted.") + qdel(active2) + if("d_rec") + var/datum/data/record/general_record = locate(params["d_rec"] || "") + if(!GLOB.data_core.general.Find(general_record)) + set_temp("Record not found.", "danger") + return - if(href_list["screen"]) - screen = text2num(href_list["screen"]) - if(screen < 1) - screen = MED_DATA_MAIN + var/datum/data/record/medical_record + for(var/datum/data/record/M in GLOB.data_core.medical) + if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"]) + medical_record = M + break - active1 = null - active2 = null - - if(href_list["vir"]) - var/type = href_list["vir"] - var/datum/disease/D = new type(0) - var/afs = "" - for(var/mob/M in D.viable_mobtypes) - afs += "[initial(M.name)];" - var/severity = D.severity - switch(severity) - if("Harmful", "Minor") - severity = "[severity]" - if("Medium") - severity = "[severity]" - if("Dangerous!") - severity = "[severity]" - if("BIOHAZARD THREAT!") - severity = "

    [severity]

    " - setTemp({"Name: [D.name] -
    Number of stages: [D.max_stages] -
    Spread: [D.spread_text] Transmission -
    Possible Cure: [(D.cure_text||"none")] -
    Affected Lifeforms:[afs]
    -
    Notes: [D.desc]
    -
    Severity: [severity]"}) - qdel(D) - - if(href_list["del_all"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1") - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null) - setTemp("

    Are you sure you wish to delete all records?

    ", buttons) - - if(href_list["field"]) - if(..()) - return 1 - var/a1 = active1 - var/a2 = active2 - switch(href_list["field"]) - if("fingerprint") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Med. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["fingerprint"] = t1 - if("sex") - if(istype(active1, /datum/data/record)) - if(active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" - else - active1.fields["sex"] = "Male" - if("age") - if(istype(active1, /datum/data/record)) - var/t1 = input("Please input age:", "Med. records", active1.fields["age"], null) as num - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["age"] = t1 - if("mi_dis") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input minor disabilities list:", "Med. records", active2.fields["mi_dis"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_dis"] = t1 - if("mi_dis_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize minor dis.:", "Med. records", active2.fields["mi_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_dis_d"] = t1 - if("ma_dis") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input major diabilities list:", "Med. records", active2.fields["ma_dis"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_dis"] = t1 - if("ma_dis_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize major dis.:", "Med. records", active2.fields["ma_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_dis_d"] = t1 - if("alg") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please state allergies:", "Med. records", active2.fields["alg"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["alg"] = t1 - if("alg_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize allergies:", "Med. records", active2.fields["alg_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["alg_d"] = t1 - if("cdi") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please state diseases:", "Med. records", active2.fields["cdi"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["cdi"] = t1 - if("cdi_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize diseases:", "Med. records", active2.fields["cdi_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["cdi_d"] = t1 - if("notes") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Med. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["notes"] = t1 - if("p_stat") - if(istype(active1, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "*Deceased*", "icon" = "stethoscope", "href" = "p_stat=deceased", "status" = (active1.fields["p_stat"] == "*Deceased*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*SSD*", "icon" = "stethoscope", "href" = "p_stat=ssd", "status" = (active1.fields["p_stat"] == "*SSD*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Active", "icon" = "stethoscope", "href" = "p_stat=active", "status" = (active1.fields["p_stat"] == "Active" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Physically Unfit", "icon" = "stethoscope", "href" = "p_stat=unfit", "status" = (active1.fields["p_stat"] == "Physically Unfit" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Disabled", "icon" = "stethoscope", "href" = "p_stat=disabled", "status" = (active1.fields["p_stat"] == "Disabled" ? "selected" : null)) - setTemp("

    Physical Condition

    ", buttons) - if("m_stat") - if(istype(active1, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "*Insane*", "icon" = "stethoscope", "href" = "m_stat=insane", "status" = (active1.fields["m_stat"] == "*Insane*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Unstable*", "icon" = "stethoscope", "href" = "m_stat=unstable", "status" = (active1.fields["m_stat"] == "*Unstable*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Watch*", "icon" = "stethoscope", "href" = "m_stat=watch", "status" = (active1.fields["m_stat"] == "*Watch*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Stable", "icon" = "stethoscope", "href" = "m_stat=stable", "status" = (active1.fields["m_stat"] == "Stable" ? "selected" : null)) - setTemp("

    Mental Condition

    ", buttons) - if("blood_type") - if(istype(active2, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "A-", "icon" = "tint", "href" = "blood_type=an", "status" = (active2.fields["blood_type"] == "A-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "A+", "icon" = "tint", "href" = "blood_type=ap", "status" = (active2.fields["blood_type"] == "A+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "B-", "icon" = "tint", "href" = "blood_type=bn", "status" = (active2.fields["blood_type"] == "B-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "B+", "icon" = "tint", "href" = "blood_type=bp", "status" = (active2.fields["blood_type"] == "B+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "AB-", "icon" = "tint", "href" = "blood_type=abn", "status" = (active2.fields["blood_type"] == "AB-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "AB+", "icon" = "tint", "href" = "blood_type=abp", "status" = (active2.fields["blood_type"] == "AB+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "O-", "icon" = "tint", "href" = "blood_type=on", "status" = (active2.fields["blood_type"] == "O-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "O+", "icon" = "tint", "href" = "blood_type=op", "status" = (active2.fields["blood_type"] == "O+" ? "selected" : null)) - setTemp("

    Blood Type

    ", buttons) - if("b_dna") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input DNA hash:", "Med. records", active2.fields["b_dna"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["b_dna"] = t1 - if("vir_name") - var/datum/data/record/v = locate(href_list["edit_vir"]) - if(v) - var/t1 = copytext(trim(sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - v.fields["name"] = t1 - if("vir_desc") - var/datum/data/record/v = locate(href_list["edit_vir"]) - if(v) - var/t1 = copytext(trim(sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - v.fields["description"] = t1 - - if(href_list["del_r"]) - if(active2) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete the record (Medical Portion Only)?

    ", buttons) - - if(href_list["d_rec"]) - var/datum/data/record/R = locate(href_list["d_rec"]) - var/datum/data/record/M = locate(href_list["d_rec"]) - if(!GLOB.data_core.general.Find(R)) - setTemp("

    Record not found!

    ") - return 1 - for(var/datum/data/record/E in GLOB.data_core.medical) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - M = E - active1 = R - active2 = M - screen = MED_DATA_RECORD - - if(href_list["new"]) - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Medical Record #[R.fields["id"]]" - R.fields["blood_type"] = "Unknown" - R.fields["b_dna"] = "Unknown" - R.fields["mi_dis"] = "None" - R.fields["mi_dis_d"] = "No minor disabilities have been declared." - R.fields["ma_dis"] = "None" - R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - R.fields["alg"] = "None" - R.fields["alg_d"] = "No allergies have been detected in this patient." - R.fields["cdi"] = "None" - R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - R.fields["notes"] = "No notes." - GLOB.data_core.medical += R - active2 = R + active1 = general_record + active2 = medical_record screen = MED_DATA_RECORD - - if(href_list["add_c"]) - if(!istype(active2, /datum/data/record)) - return 1 - var/a2 = active2 - var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" - - if(href_list["del_c"]) - var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) - if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) - active2.fields["comments"] -= active2.fields["comments"][index] - - if(href_list["search"]) - var/t1 = clean_input("Search String: (Name, DNA, or ID)", "Med. records", null, null) - if(!t1 || ..()) - return 1 - active1 = null - active2 = null - t1 = lowertext(t1) - for(var/datum/data/record/R in GLOB.data_core.medical) - if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) + if("new") + if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) + var/datum/data/record/R = new /datum/data/record() + R.fields["name"] = active1.fields["name"] + R.fields["id"] = active1.fields["id"] + R.name = "Medical Record #[R.fields["id"]]" + R.fields["blood_type"] = "Unknown" + R.fields["b_dna"] = "Unknown" + R.fields["mi_dis"] = "None" + R.fields["mi_dis_d"] = "No minor disabilities have been declared." + R.fields["ma_dis"] = "None" + R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." + R.fields["alg"] = "None" + R.fields["alg_d"] = "No allergies have been detected in this patient." + R.fields["cdi"] = "None" + R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." + R.fields["notes"] = "No notes." + GLOB.data_core.medical += R active2 = R - if(!active2) - setTemp("

    Could not locate record [t1].

    ") - else + screen = MED_DATA_RECORD + set_temp("Medical record created.", "success") + if("del_c") + var/index = text2num(params["del_c"] || "") + if(!index || !istype(active2, /datum/data/record)) + return + + var/list/comments = active2.fields["comments"] + index = clamp(index, 1, length(comments)) + if(comments[index]) + comments.Cut(index, index + 1) + if("search") + active1 = null + active2 = null + var/t1 = lowertext(params["t1"] || "") + if(!length(t1)) + return + + for(var/datum/data/record/R in GLOB.data_core.medical) + if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) + active2 = R + break + if(!active2) + set_temp("Medical record not found. You must enter the person's exact name, ID or DNA.", "danger") + return for(var/datum/data/record/E in GLOB.data_core.general) if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"]) active1 = E + break screen = MED_DATA_RECORD + if("print_p") + if(!printing) + printing = TRUE + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + SStgui.update_uis(src) + addtimer(CALLBACK(src, .proc/print_finish), 5 SECONDS) + else + return FALSE - if(href_list["print_p"]) - if(!printing) - printing = 1 - playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - sleep(50) - var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
    Medical Record

    " - if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
    \nSex: [active1.fields["sex"]] -
    \nAge: [active1.fields["age"]] -
    \nFingerprint: [active1.fields["fingerprint"]] -
    \nPhysical Status: [active1.fields["p_stat"]] -
    \nMental Status: [active1.fields["m_stat"]]
    "} +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/computer/med_data/proc/tgui_act_modal(action, params) + . = TRUE + var/id = params["id"] // The modal's ID + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_OPEN) + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/question = field_edit_questions[field] + var/choices = field_edit_choices[field] + if(length(choices)) + tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) + else + tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) + if("add_c") + tgui_modal_input(src, id, "Please enter your message:") else - P.info += "General Record Lost!
    " - if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) - P.info += {"
    \n
    Medical Data
    -
    \nBlood Type: [active2.fields["blood_type"]] -
    \nDNA: [active2.fields["b_dna"]]
    \n -
    \nMinor Disabilities: [active2.fields["mi_dis"]] -
    \nDetails: [active2.fields["mi_dis_d"]]
    \n -
    \nMajor Disabilities: [active2.fields["ma_dis"]] -
    \nDetails: [active2.fields["ma_dis_d"]]
    \n -
    \nAllergies: [active2.fields["alg"]] -
    \nDetails: [active2.fields["alg_d"]]
    \n -
    \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) -
    \nDetails: [active2.fields["cdi_d"]]
    \n -
    \nImportant Notes: -
    \n\t[active2.fields["notes"]]
    \n -
    \n -
    Comments/Log

    "} - for(var/c in active2.fields["comments"]) - P.info += "[c]
    " - else - P.info += "Medical Record Lost!
    " - P.info += "" - P.name = "paper- 'Medical Record: [active1.fields["name"]]'" - printing = 0 - return 1 + return FALSE + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/list/choices = field_edit_choices[field] + if(length(choices) && !(answer in choices)) + return -/obj/machinery/computer/med_data/proc/setTemp(text, list/buttons = list()) - temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) + if(field == "age") + var/new_age = text2num(answer) + if(new_age < AGE_MIN || new_age > AGE_MAX) + set_temp("Invalid age. It must be between [AGE_MIN] and [AGE_MAX].", "danger") + return + answer = new_age + + if(istype(active2) && (field in active2.fields)) + active2.fields[field] = answer + else if(istype(active1) && (field in active1.fields)) + active1.fields[field] = answer + if("add_c") + var/datum/tgui_login/state = tgui_login_get() + if(!length(answer) || !istype(active2) || !length(state.name)) + return + active2.fields["comments"] += list(list( + header = "Made by [state.name] ([state.name]) on [GLOB.current_date_string] [station_time_timestamp()]", + text = answer + )) + else + return FALSE + else + return FALSE + +/** + * Called when the print timer finishes + */ +/obj/machinery/computer/med_data/proc/print_finish() + var/obj/item/paper/P = new /obj/item/paper(loc) + P.info = "
    Medical Record

    " + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) + P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] +
    \nSex: [active1.fields["sex"]] +
    \nAge: [active1.fields["age"]] +
    \nFingerprint: [active1.fields["fingerprint"]] +
    \nPhysical Status: [active1.fields["p_stat"]] +
    \nMental Status: [active1.fields["m_stat"]]
    "} + else + P.info += "
    General Record Lost!

    " + if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) + P.info += {"
    \n
    Medical Data
    +
    \nBlood Type: [active2.fields["blood_type"]] +
    \nDNA: [active2.fields["b_dna"]]
    \n +
    \nMinor Disabilities: [active2.fields["mi_dis"]] +
    \nDetails: [active2.fields["mi_dis_d"]]
    \n +
    \nMajor Disabilities: [active2.fields["ma_dis"]] +
    \nDetails: [active2.fields["ma_dis_d"]]
    \n +
    \nAllergies: [active2.fields["alg"]] +
    \nDetails: [active2.fields["alg_d"]]
    \n +
    \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) +
    \nDetails: [active2.fields["cdi_d"]]
    \n +
    \nImportant Notes: +
    \n\t[active2.fields["notes"]]
    \n +
    \n +
    Comments/Log

    "} + for(var/c in active2.fields["comments"]) + P.info += "[c]
    " + else + P.info += "
    Medical Record Lost!

    " + P.info += "" + P.name = "paper - 'Medical Record: [active1.fields["name"]]'" + printing = FALSE + SStgui.update_uis(src) + +/** + * Sets a temporary message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * style - The style of the message: (color name), info, success, warning, danger, virus + */ +/obj/machinery/computer/med_data/proc/set_temp(text = "", style = "info", update_now = FALSE) + temp = list(text = text, style = style) + if(update_now) + SStgui.update_uis(src) /obj/machinery/computer/med_data/emp_act(severity) if(stat & (BROKEN|NOPOWER)) @@ -555,6 +447,10 @@ ..(severity) +/obj/machinery/computer/med_data/tgui_login_on_login(datum/tgui_login/state) + active1 = null + active2 = null + screen = MED_DATA_R_LIST /obj/machinery/computer/med_data/laptop name = "medical laptop" @@ -564,9 +460,10 @@ icon_screen = "medlaptop" density = 0 -#undef MED_DATA_MAIN #undef MED_DATA_R_LIST #undef MED_DATA_MAINT #undef MED_DATA_RECORD #undef MED_DATA_V_DATA #undef MED_DATA_MEDBOT +#undef FIELD +#undef MED_FIELD diff --git a/code/game/machinery/computer/power.dm b/code/game/machinery/computer/power.dm index bb38e59c800..a0bbedf2f73 100644 --- a/code/game/machinery/computer/power.dm +++ b/code/game/machinery/computer/power.dm @@ -9,8 +9,17 @@ light_color = LIGHT_COLOR_ORANGE circuit = /obj/item/circuitboard/powermonitor var/datum/powernet/powernet = null - var/datum/nano_module/power_monitor/power_monitor + var/datum/tgui_module/power_monitor/power_monitor + /// Will this monitor be hidden from viewers? var/is_secret_monitor = FALSE + /// How many records to keep of supply and demand + var/record_size = 60 + /// Interval between power snapshots + var/record_interval = 5 SECONDS + /// Time to next record power + var/next_record = 0 + /// The history list itself of the power + var/list/history = list() /obj/machinery/computer/monitor/secret //Hides the power monitor (such as ones on ruins & CentCom) from PDA's to prevent metagaming. name = "outdated power monitoring console" @@ -28,6 +37,8 @@ ..() GLOB.powermonitor_repository.update_cache() powernet = find_powernet() + history["supply"] = list() + history["demand"] = list() /obj/machinery/computer/monitor/Destroy() GLOB.power_monitors -= src @@ -56,10 +67,35 @@ return // Update the powernet powernet = find_powernet() - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - power_monitor.ui_interact(user, ui_key, ui, force_open) +/obj/machinery/computer/monitor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + power_monitor.tgui_interact(user, ui_key, ui, force_open) /obj/machinery/computer/monitor/interact(mob/user) - power_monitor.ui_interact(user) + power_monitor.tgui_interact(user) + +/obj/machinery/computer/monitor/process() + record() + +/** + * Power snapshot recording proc + * + * This proc handles recording powernet history for the graph on the TGUI + * It is called every process(), but only logs every 5 seconds + */ +/obj/machinery/computer/monitor/proc/record() + if(world.time >= next_record) + next_record = world.time + record_interval + if(!powernet) + return + + var/list/supply = history["supply"] + supply += powernet.viewavail + if(length(supply) > record_size) + supply.Cut(1, 2) + + var/list/demand = history["demand"] + demand += powernet.viewload + if(length(demand) > record_size) + demand.Cut(1, 2) diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 6134860733a..f676d861f96 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -20,221 +20,210 @@ return if(stat & (NOPOWER|BROKEN)) return - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/robotics/proc/is_authenticated(var/mob/user as mob) +/obj/machinery/computer/robotics/proc/is_authenticated(mob/user) + if(!istype(user)) + return FALSE if(user.can_admin_interact()) - return 1 - else if(allowed(user)) - return 1 - return 0 + return TRUE + if(allowed(user)) + return TRUE + return FALSE -/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/** + * Does this borg show up in the console + * + * Returns TRUE if a robot will show up in the console + * Returns FALSE if a robot will not show up in the console + * Arguments: + * * R - The [mob/living/silicon/robot] to be checked + */ +/obj/machinery/computer/robotics/proc/console_shows(mob/living/silicon/robot/R) + if(!istype(R)) + return FALSE + if(istype(R, /mob/living/silicon/robot/drone)) + return FALSE + if(R.scrambledcodes) + return FALSE + if(!atoms_share_level(get_turf(src), get_turf(R))) + return FALSE + return TRUE + +/** + * Check if a user can send a lockdown/detonate command to a specific borg + * + * Returns TRUE if a user can send the command (does not guarantee it will work) + * Returns FALSE if a user cannot + * Arguments: + * * user - The [mob/user] to be checked + * * R - The [mob/living/silicon/robot] to be checked + * * telluserwhy - Bool of whether the user should be sent a to_chat message if they don't have access + */ +/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R, telluserwhy = FALSE) + if(!istype(user)) + return FALSE + if(!console_shows(R)) + return FALSE + if(isAI(user)) + if(R.connected_ai != user) + if(telluserwhy) + to_chat(user, "AIs can only control cyborgs which are linked to them.") + return FALSE + if(isrobot(user)) + if(R != user) + if(telluserwhy) + to_chat(user, "Cyborgs cannot control other cyborgs.") + return FALSE + return TRUE + +/** + * Check if the user is the right kind of entity to be able to hack borgs + * + * Returns TRUE if a user is a traitor AI, or aghost + * Returns FALSE otherwise + * Arguments: + * * user - The [mob/user] to be checked + */ +/obj/machinery/computer/robotics/proc/can_hack_any(mob/user) + if(!istype(user)) + return FALSE + if(user.can_admin_interact()) + return TRUE + if(!isAI(user)) + return FALSE + return (user.mind.special_role && user.mind.original == user) + +/** + * Check if the user is allowed to hack a specific borg + * + * Returns TRUE if a user can hack the specific cyborg + * Returns FALSE if a user cannot + * Arguments: + * * user - The [mob/user] to be checked + * * R - The [mob/living/silicon/robot] to be checked + */ +/obj/machinery/computer/robotics/proc/can_hack(mob/user, mob/living/silicon/robot/R) + if(!can_hack_any(user)) + return FALSE + if(!istype(R)) + return FALSE + if(R.emagged) + return FALSE + if(R.connected_ai != user) + return FALSE + return TRUE + +/obj/machinery/computer/robotics/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "robot_control.tmpl", "Robotic Control Console", 400, 500) + ui = new(user, src, ui_key, "RoboticsControlConsole", name, 500, 460, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/computer/robotics/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - var/list/robots = get_cyborgs(user) - if(robots.len) - data["robots"] = robots +/obj/machinery/computer/robotics/tgui_data(mob/user) + var/list/data = list() + data["auth"] = is_authenticated(user) + data["can_hack"] = can_hack_any(user) + data["cyborgs"] = list() data["safety"] = safety - // Also applies for cyborgs. Hides the manual self-destruct button. - data["is_ai"] = issilicon(user) - data["allowed"] = is_authenticated(user) + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + if(!console_shows(R)) + continue + var/area/A = get_area(R) + var/turf/T = get_turf(R) + var/list/cyborg_data = list( + name = R.name, + uid = R.UID(), + locked_down = R.lockcharge, + locstring = "[A.name] ([T.x], [T.y])", + status = R.stat, + health = round(R.health * 100 / R.maxHealth, 0.1), + charge = R.cell ? round(R.cell.percent()) : null, + cell_capacity = R.cell ? R.cell.maxcharge : null, + module = R.module ? R.module.name : "No Module Detected", + synchronization = R.connected_ai, + is_hacked = R.connected_ai && R.emagged, + hackable = can_hack(user, R), + ) + data["cyborgs"] += list(cyborg_data) + data["show_detonate_all"] = (data["auth"] && length(data["cyborgs"]) > 0 && ishuman(user)) return data -/obj/machinery/computer/robotics/Topic(href, href_list) +/obj/machinery/computer/robotics/tgui_act(action, params) if(..()) - return 1 - - var/mob/user = usr - if(!is_authenticated(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) || !is_authenticated(user)) - 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() - else - message_admins("[key_name_admin(usr)] detonated [key_name_admin(target)] (JMP)!") - log_game("\[key_name(usr)] detonated [key_name(target)]!") - to_chat(target, "Self-destruct command received.") - if(target.connected_ai) - to_chat(target.connected_ai, "

    ALERT - Cyborg detonation detected: [target.name]
    ") - 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 - - var/choice = input("Really [target.lockcharge ? "unlock" : "lockdown"] [target.name] ?") in list ("Yes", "No") - if(choice != "Yes") - return - - if(!target || !istype(target)) - return - - message_admins("[key_name_admin(usr)] [target.canmove ? "locked down" : "released"] [key_name_admin(target)]!") - log_game("[key_name(usr)] [target.canmove ? "locked down" : "released"] [key_name(target)]!") - target.SetLockdown(!target.lockcharge) - to_chat(target, "[!target.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") - if(target.connected_ai) - to_chat(target.connected_ai, "[!target.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [target.name]

    ") - - // 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.connected_ai != user) - to_chat(user, "Access Denied. This robot is not linked to you.") - 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 [key_name_admin(target)] using robotic console!") - log_game("[key_name(usr)] emagged [key_name(target)] using robotic console!") - 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!") - - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(istype(R, /mob/living/silicon/robot/drone)) - continue - // Ignore antagonistic cyborgs - if(R.scrambledcodes) - continue + . = FALSE + if(!is_authenticated(usr)) + to_chat(usr, "Access denied.") + return + switch(action) + if("arm") // Arms the emergency self-destruct system + if(issilicon(usr)) + to_chat(usr, "Access Denied (silicon detected)") + return + safety = !safety + to_chat(usr, "You [safety ? "disarm" : "arm"] the emergency self destruct.") + . = TRUE + if("nuke") // Destroys all accessible cyborgs if safety is disabled + if(issilicon(usr)) + to_chat(usr, "Access Denied (silicon detected)") + return + if(safety) + to_chat(usr, "Self-destruct aborted - safety active") + return + message_admins("[key_name_admin(usr)] detonated all cyborgs!") + log_game("\[key_name(usr)] detonated all cyborgs!") + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + if(istype(R, /mob/living/silicon/robot/drone)) + continue + // Ignore antagonistic cyborgs + if(R.scrambledcodes) + continue + to_chat(R, "Self-destruct command received.") + if(R.connected_ai) + to_chat(R.connected_ai, "

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

    ALERT - Cyborg detonation detected: [R.name]
    ") - 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 GLOB.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.canmove) - 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 - - var/turf/pos = get_turf(R) - var/area/bot_area = get_area(R) - robot["xpos"] = pos.x - robot["ypos"] = pos.y - robot["zpos"] = pos.z - robot["area"] = format_text(bot_area.name) - - robot["health"] = round(R.health * 100 / R.maxHealth,0.1) - - 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 GLOB.mob_list) - if(R.name == name) - return R + R.self_destruct() + . = TRUE + if("stopbot") // lock or unlock the borg + if(isrobot(usr)) + to_chat(usr, "Access Denied.") + return + var/mob/living/silicon/robot/R = locateUID(params["uid"]) + if(!can_control(usr, R, TRUE)) + return + message_admins("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!") + log_game("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!") + R.SetLockdown(!R.lockcharge) + to_chat(R, "[!R.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") + if(R.connected_ai) + to_chat(R.connected_ai, "[!R.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [R.name]
    ") + . = TRUE + if("hackbot") // AIs hacking/emagging a borg + var/mob/living/silicon/robot/R = locateUID(params["uid"]) + if(!can_hack(usr, R)) + return + var/choice = input("Really hack [R.name]? This cannot be undone.") in list("Yes", "No") + if(choice != "Yes") + return + log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!") + message_admins("[key_name_admin(usr)] emagged [key_name_admin(R)] using robotic console!") + R.emagged = TRUE + to_chat(R, "Failsafe protocols overriden. New tools available.") + . = TRUE diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 8c815198d31..d091b07d4cb 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -2,43 +2,67 @@ #define SEC_DATA_MAINT 2 // Records maintenance #define SEC_DATA_RECORD 3 // Record -/obj/machinery/computer/secure_data//TODO:SANITY +#define SEC_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB) + +/obj/machinery/computer/secure_data name = "security records" desc = "Used to view and edit personnel's security records." icon_keyboard = "security_key" icon_screen = "security" - req_one_access = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS) circuit = /obj/item/circuitboard/secure_data - var/obj/item/card/id/scan = null - var/authenticated = null - var/rank = null - var/list/authcard_access = list() - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/temp = null - var/printing = null - //Sorting Variables - var/sortBy = "name" - var/order = 1 // -1 = Descending - 1 = Ascending + /// The current page being viewed. + var/current_page = SEC_DATA_R_LIST + /// The current general record being viewed. + var/datum/data/record/record_general = null + /// The current security record being viewed. + var/datum/data/record/record_security = null + /// Whether the computer is currently printing a paper or not. + var/is_printing = FALSE + /// The editable fields and their associated question to display to the user. + var/static/list/field_edit_questions + /// The editable fields and their associated choices to display to the user. + var/static/list/field_edit_choices + /// The current temporary notice. + var/temp_notice light_color = LIGHT_COLOR_RED +/obj/machinery/computer/secure_data/Initialize(mapload) + . = ..() + req_one_access = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS) + if(!field_edit_questions) + field_edit_questions = list( + // General + "name" = "Please input new name:", + "id" = "Please input new ID:", + "sex" = "Please select new sex:", + "age" = "Please input new age:", + "fingerprint" = "Please input new fingerprint hash:", + // Security + "criminal" = "Please select new criminal status:", + "mi_crim" = "Please input new minor crimes:", + "mi_crim_d" = "Please summarize minor crimes:", + "ma_crim" = "Please input new major crimes:", + "ma_crim_d" = "Please summarize major crimes:", + "notes" = "Please input new important notes:", + ) + field_edit_choices = list( + // General + "sex" = list("Male", "Female"), + // Security + "criminal" = list(SEC_RECORD_STATUS_NONE, SEC_RECORD_STATUS_ARREST, SEC_RECORD_STATUS_EXECUTE, SEC_RECORD_STATUS_INCARCERATED, SEC_RECORD_STATUS_RELEASED, SEC_RECORD_STATUS_PAROLLED, SEC_RECORD_STATUS_DEMOTE, SEC_RECORD_STATUS_SEARCH, SEC_RECORD_STATUS_MONITOR), + ) + /obj/machinery/computer/secure_data/Destroy() - active1 = null - active2 = null + record_general = null + record_security = null return ..() /obj/machinery/computer/secure_data/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/card/id) && !scan) - user.drop_item() - O.forceMove(src) - scan = O - ui_interact(user) + if(tgui_login_attackby(O, user)) return return ..() -//Someone needs to break down the dat += into chunks instead of long ass lines. /obj/machinery/computer/secure_data/attack_hand(mob/user) if(..()) return @@ -46,267 +70,124 @@ to_chat(user, "Unable to establish a connection: You're too far away from the station!") return add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/secure_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/secure_data/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "secure_data.tmpl", name, 800, 800) + ui = new(user, src, ui_key, "SecurityRecords", name, 800, 800) ui.open() + ui.set_autoupdate(FALSE) -/obj/machinery/computer/secure_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["screen"] = screen - if(authenticated) - switch(screen) +/obj/machinery/computer/secure_data/tgui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/list/data = list() + data["currentPage"] = current_page + data["isPrinting"] = is_printing + tgui_login_data(data, user) + data["modal"] = tgui_modal_data() + data["temp"] = temp_notice + if(data["loginState"]["logged_in"]) + switch(current_page) if(SEC_DATA_R_LIST) - if(!isnull(GLOB.data_core.general)) - for(var/datum/data/record/R in sortRecord(GLOB.data_core.general, sortBy, order)) - var/crimstat = "null" - for(var/datum/data/record/E in GLOB.data_core.security) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - crimstat = E.fields["criminal"] - break - var/background = "''" - switch(crimstat) - if(SEC_RECORD_STATUS_EXECUTE) - background = "'background-color:#5E0A1A'" - if(SEC_RECORD_STATUS_ARREST) - background = "'background-color:#890E26'" - if(SEC_RECORD_STATUS_SEARCH) - background = "'background-color:#999900'" - if(SEC_RECORD_STATUS_MONITOR) - background = "'background-color:#004C99'" - if(SEC_RECORD_STATUS_DEMOTE) - background = "'background-color:#C2A111'" - if(SEC_RECORD_STATUS_INCARCERATED) - background = "'background-color:#743B03'" - if(SEC_RECORD_STATUS_PAROLLED) - background = "'background-color:#743B03'" - if(SEC_RECORD_STATUS_RELEASED) - background = "'background-color:#216489'" - if(SEC_RECORD_STATUS_NONE) - background = "'background-color:#007f47'" - if("null") - crimstat = "No record." - data["records"] += list(list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"], "rank" = R.fields["rank"], "fingerprint" = R.fields["fingerprint"], "background" = background, "crimstat" = crimstat)) + // Prepare the list of security records to associate with the general ones. + // This is not ideal but datacore code sucks and needs to be rewritten. + var/list/sec_records_assoc = list() + for(var/datum/data/record/S in GLOB.data_core.security) + sec_records_assoc["[S.fields["name"]]|[S.fields["id"]]"] = S + // List the general records + var/list/records = list() + data["records"] = records + for(var/datum/data/record/G in GLOB.data_core.general) + var/datum/data/record/S = sec_records_assoc["[G.fields["name"]]|[G.fields["id"]]"] + var/list/record_line = list("uid_gen" = G.UID(), "id" = G.fields["id"], "name" = G.fields["name"], "rank" = G.fields["rank"], "fingerprint" = G.fields["fingerprint"]) + record_line["status"] = S?.fields["criminal"] || "No record" + record_line["uid_sec"] = S?.UID() // So we don't have to perform the search through a for loop again later + records[++records.len] = record_line if(SEC_DATA_RECORD) var/list/general = list() data["general"] = general - if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = "name") - fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = "id") - fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") - fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") - fields[++fields.len] = list("field" = "Rank:", "value" = active1.fields["rank"], "edit" = "rank") - fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") - fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = null) - fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = null) - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = list("photo" = active1.fields["photo-south"]) - photos[++photos.len] = list("photo" = active1.fields["photo-west"]) - general["has_photos"] += (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 + if(record_general && GLOB.data_core.general.Find(record_general)) + var/list/gen_fields = record_general.fields + general["fields"] = list( + SEC_FIELD("Name", gen_fields["name"], "name", FALSE), + SEC_FIELD("ID", gen_fields["id"], "id", TRUE), + SEC_FIELD("Sex", gen_fields["sex"], "sex", FALSE), + SEC_FIELD("Age", gen_fields["age"], "age", TRUE), + SEC_FIELD("Assignment", gen_fields["rank"], null, FALSE), + SEC_FIELD("Fingerprint", gen_fields["fingerprint"], "fingerprint", TRUE), + SEC_FIELD("Physical Status", gen_fields["p_stat"], null, FALSE), + SEC_FIELD("Mental Status", gen_fields["m_stat"], null, TRUE), + SEC_FIELD("Important Notes", gen_fields["notes"], null, FALSE), + ) + general["photos"] = list( + gen_fields["photo-south"], + gen_fields["photo-west"], + ) + general["has_photos"] = (gen_fields["photo-south"] || gen_fields["photo-west"]) ? TRUE : FALSE + general["empty"] = FALSE else - general["empty"] = 1 + general["empty"] = TRUE var/list/security = list() data["security"] = security - if(istype(active2, /datum/data/record) && GLOB.data_core.security.Find(active2)) - var/list/fields = list() - security["fields"] = fields - fields[++fields.len] = list("field" = "Criminal Status:", "value" = active2.fields["criminal"], "edit" = "criminal", "line_break" = 1) - fields[++fields.len] = list("field" = "Minor Crimes:", "value" = active2.fields["mi_crim"], "edit" = "mi_crim", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_crim_d"], "edit" = "mi_crim_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Major Crimes:", "value" = active2.fields["ma_crim"], "edit" = "ma_crim", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_crim_d"], "edit" = "ma_crim_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) - if(!active2.fields["comments"] || !islist(active2.fields["comments"])) - active2.fields["comments"] = list() - security["comments"] = active2.fields["comments"] - security["empty"] = 0 + if(record_security && GLOB.data_core.security.Find(record_security)) + var/list/sec_fields = record_security.fields + security["fields"] = list( + SEC_FIELD("Criminal Status", sec_fields["criminal"], "criminal", TRUE), + SEC_FIELD("Minor Crimes", sec_fields["mi_crim"], "mi_crim", FALSE), + SEC_FIELD("Details", sec_fields["mi_crim_d"], "mi_crim_d", TRUE), + SEC_FIELD("Major Crimes", sec_fields["ma_crim"], "ma_crim", FALSE), + SEC_FIELD("Details", sec_fields["ma_crim_d"], "ma_crim_d", TRUE), + SEC_FIELD("Important Notes", sec_fields["notes"], null, FALSE), + ) + if(!islist(sec_fields["comments"])) + sec_fields["comments"] = list() + security["comments"] = sec_fields["comments"] + security["empty"] = FALSE else - security["empty"] = 1 + security["empty"] = TRUE + return data -/obj/machinery/computer/secure_data/Topic(href, href_list) +/obj/machinery/computer/secure_data/tgui_act(action, list/params) if(..()) - return 1 + return - if(!GLOB.data_core.general.Find(active1)) - active1 = null - if(!GLOB.data_core.security.Find(active2)) - active2 = null + . = TRUE + if(tgui_act_modal(action, params)) + return + if(tgui_login_act(action, params)) + return - if(href_list["temp"]) - temp = null - - if(href_list["temp_action"]) - var/temp_href = splittext(href_list["temp_action"], "=") - switch(temp_href[1]) - if("del_all2") - for(var/datum/data/record/R in GLOB.data_core.security) - qdel(R) - update_all_mob_security_hud() - setTemp("

    All records deleted.

    ") - if("del_alllogs2") - if(GLOB.cell_logs.len) - setTemp("

    All cell logs deleted.

    ") - GLOB.cell_logs.Cut() - else - to_chat(usr, "Error; No cell logs to delete.") - if("del_r2") - if(active2) - qdel(active2) - update_all_mob_security_hud() - if("del_rg2") - if(active1) - for(var/datum/data/record/R in GLOB.data_core.medical) - if(R.fields["name"] == active1.fields["name"] && R.fields["id"] == active1.fields["id"]) - qdel(R) - QDEL_NULL(active1) - QDEL_NULL(active2) - update_all_mob_security_hud() - screen = SEC_DATA_R_LIST - if("criminal") - if(active2) - var/t1 - if(temp_href[2] == "execute") - t1 = copytext(trim(sanitize(input("Explain why they are being executed. Include a list of their crimes, and victims.", "EXECUTION ORDER", null, null) as text)), 1, MAX_MESSAGE_LEN) - else - t1 = copytext(trim(sanitize(input("Enter Reason:", "Secure. records", null, null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1) - t1 = "(none)" - if(!set_criminal_status(usr, active2, temp_href[2], t1, rank, authcard_access)) - setTemp("

    Error: permission denied.

    ") - return 1 - if("rank") - if(active1) - active1.fields["rank"] = temp_href[2] - if(temp_href[2] in GLOB.joblist) - active1.fields["real_rank"] = temp_href[2] - - if(href_list["scan"]) - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - - if(href_list["login"]) - if(isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - else if(istype(scan, /obj/item/card/id)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - authcard_access = scan.access - - if(authenticated) - active1 = null - active2 = null - screen = SEC_DATA_R_LIST - - if(authenticated) - if(href_list["logout"]) - authenticated = null - screen = null - active1 = null - active2 = null - authcard_access = list() - - else if(href_list["sort"]) - // Reverse the order if clicked twice - if(sortBy == href_list["sort"]) - if(order == 1) - order = -1 - else - order = 1 - else - sortBy = href_list["sort"] - order = initial(order) - - else if(href_list["screen"]) - screen = text2num(href_list["screen"]) - if(screen < 1) - screen = SEC_DATA_R_LIST - - active1 = null - active2 = null - - else if(href_list["d_rec"]) - var/datum/data/record/R = locate(href_list["d_rec"]) - var/datum/data/record/M = locate(href_list["d_rec"]) - if(!GLOB.data_core.general.Find(R)) - setTemp("

    Record not found!

    ") - return 1 - for(var/datum/data/record/E in GLOB.data_core.security) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - M = E - active1 = R - active2 = M - screen = SEC_DATA_RECORD - - else if(href_list["del_all"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete all records?

    ", buttons) - - else if(href_list["del_alllogs"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_alllogs2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete all cell logs?

    ", buttons) - - else if(href_list["del_rg"]) - if(active1) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_rg2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete the record (ALL)?

    ", buttons) - - else if(href_list["del_r"]) - if(active1) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete the record (Security Portion Only)?

    ", buttons) - - else if(href_list["new_s"]) - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Security Record #[R.fields["id"]]" - R.fields["criminal"] = "None" - R.fields["mi_crim"] = "None" - R.fields["mi_crim_d"] = "No minor crime convictions." - R.fields["ma_crim"] = "None" - R.fields["ma_crim_d"] = "No major crime convictions." - R.fields["notes"] = "No notes." - GLOB.data_core.security += R - active2 = R - screen = SEC_DATA_RECORD - - else if(href_list["new_g"]) + var/logged_in = tgui_login_get().logged_in + switch(action) + if("cleartemp") + temp_notice = null + if("page") // Select Page + if(!logged_in) + return + var/page_num = clamp(text2num(params["page"]), SEC_DATA_R_LIST, SEC_DATA_MAINT) // SEC_DATA_RECORD cannot be accessed through this act + current_page = page_num + record_general = null + record_security = null + if("view") // View Record + if(!logged_in) + return + var/datum/data/record/G = locateUID(params["uid_gen"]) + var/datum/data/record/S = locateUID(params["uid_sec"]) + if(!istype(G)) // No general record! + set_temp("General record not found!", "danger") + return + if(istype(S) && !(G.fields["name"] == S.fields["name"] && G.fields["id"] == S.fields["id"])) // General and security records don't match! + S = null + record_general = G + record_security = S + current_page = SEC_DATA_RECORD + if("new_general") // New General Record + if(!logged_in) + return + if(record_general) + return var/datum/data/record/G = new /datum/data/record() G.fields["name"] = "New Record" G.fields["id"] = "[add_zero(num2hex(rand(1, 1.6777215E7)), 6)]" @@ -318,229 +199,251 @@ G.fields["p_stat"] = "Active" G.fields["m_stat"] = "Stable" G.fields["species"] = "Human" + G.fields["notes"] = "No notes." GLOB.data_core.general += G - active1 = G - active2 = null + record_general = G + record_security = null + current_page = SEC_DATA_RECORD + if("new_security") // New Security Record + if(!logged_in) + return + if(!record_general || record_security) + return + var/datum/data/record/S = new /datum/data/record() + S.fields["name"] = record_general.fields["name"] + S.fields["id"] = record_general.fields["id"] + S.name = "Security Record #[S.fields["id"]]" + S.fields["criminal"] = SEC_RECORD_STATUS_NONE + S.fields["mi_crim"] = "None" + S.fields["mi_crim_d"] = "No minor crime convictions." + S.fields["ma_crim"] = "None" + S.fields["ma_crim_d"] = "No major crime convictions." + S.fields["notes"] = "No notes." + GLOB.data_core.security += S + record_security = S + update_all_mob_security_hud() + if("delete_general") // Delete General, Security and Medical Records + if(!logged_in) + return + if(!record_general) + return + message_admins("[key_name_admin(usr)] has deleted [record_general.fields["name"]]'s general, security and medical records at [ADMIN_COORDJMP(usr)]") + usr.create_log(MISC_LOG, "deleted [record_general.fields["name"]]'s general, security and medical records") + for(var/datum/data/record/M in GLOB.data_core.medical) + if(M.fields["name"] == record_general.fields["name"] && M.fields["id"] == record_general.fields["id"]) + qdel(M) + QDEL_NULL(record_general) + QDEL_NULL(record_security) + update_all_mob_security_hud() + current_page = SEC_DATA_R_LIST + set_temp("General, Security and Medical records deleted.") + if("delete_security") // Delete Security Record + if(!logged_in) + return + if(!record_security) + return + message_admins("[key_name_admin(usr)] has deleted [record_security.fields["name"]]'s security record at [ADMIN_COORDJMP(usr)]") + usr.create_log(MISC_LOG, "deleted [record_security.fields["name"]]'s security record") + QDEL_NULL(record_security) + update_all_mob_security_hud() + set_temp("Security record deleted.") + if("delete_security_all") // Delete All Security Records + if(!logged_in) + return + for(var/datum/data/record/S in GLOB.data_core.security) + qdel(S) + message_admins("[key_name_admin(usr)] has deleted all security records at [ADMIN_COORDJMP(usr)]") + usr.create_log(MISC_LOG, "deleted all security records") + update_all_mob_security_hud() + set_temp("All security records deleted.") + if("delete_cell_logs") // Delete All Cell Logs + if(!logged_in) + return + if(!length(GLOB.cell_logs)) + set_temp("There are no cell logs to delete.") + return + message_admins("[key_name_admin(usr)] has deleted all cell logs at [ADMIN_COORDJMP(usr)]") + usr.create_log(MISC_LOG, "deleted all cell logs") + GLOB.cell_logs.Cut() + set_temp("All cell logs deleted.") + if("comment_delete") // Delete Comment + if(!logged_in) + return + var/index = text2num(params["id"]) + if(!index || !record_security) + return - else if(href_list["print_r"]) - if(!printing) - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - sleep(50) - var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
    Security Record

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

    "} - for(var/c in active2.fields["comments"]) - P.info += "[c]
    " - else - P.info += "Security Record Lost!
    " - P.info += "" - P.name = "paper - 'Security Record: [active1.fields["name"]]'" - printing = 0 + var/list/comments = record_security.fields["comments"] + if(!length(comments)) + return + index = clamp(index, 1, length(comments)) + comments.Cut(index, index + 1) + if("print_record") + if(!logged_in) + return + if(is_printing) + return + is_printing = TRUE + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + addtimer(CALLBACK(src, .proc/print_record_finish), 5 SECONDS) + else + return FALSE -/* Removed due to BYOND issue - else if(href_list["print_p"]) - if(!printing) - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - sleep(50) - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - create_record_photo(active1) - printing = 0 -*/ + add_fingerprint(usr) - else if(href_list["printlogs"]) - if(GLOB.cell_logs.len && !printing) - var/obj/item/paper/P = input(usr, "Select log to print", "Available Cell Logs") as null|anything in GLOB.cell_logs - if(!P) - return 0 - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - to_chat(usr, "Printing file [P.name].") - sleep(50) - var/obj/item/paper/log = new /obj/item/paper(loc) - log.name = P.name - log.info = P.info - printing = 0 - return 1 - else - to_chat(usr, "[src] has no logs stored or is already printing.") - - - else if(href_list["add_c"]) - if(istype(active2, /datum/data/record)) - var/a2 = active2 - var/t1 = copytext(trim(sanitize(input("Add Comment:", "Secure. records", null, null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" - - else if(href_list["del_c"]) - var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) - if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) - active2.fields["comments"] -= active2.fields["comments"][index] - - if(href_list["field"]) - if(..()) - return 1 - var/a1 = active1 - var/a2 = active2 - switch(href_list["field"]) - if("name") - if(istype(active1, /datum/data/record)) - var/t1 = reject_bad_name(clean_input("Please input name:", "Secure. records", active1.fields["name"], null)) - if(!t1 || !length(trim(t1)) || ..() || active1 != a1) - return 1 - active1.fields["name"] = t1 - if(istype(active2, /datum/data/record)) - active2.fields["name"] = t1 - if("id") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["id"] = t1 - if(istype(active2, /datum/data/record)) - active2.fields["id"] = t1 - if("fingerprint") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["fingerprint"] = t1 - if("sex") - if(istype(active1, /datum/data/record)) - if(active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" - else - active1.fields["sex"] = "Male" - if("age") - if(istype(active1, /datum/data/record)) - var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["age"] = t1 - if("mi_crim") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input minor crimes list:", "Secure. records", active2.fields["mi_crim"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_crim"] = t1 - if("mi_crim_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize minor crimes:", "Secure. records", active2.fields["mi_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_crim_d"] = t1 - if("ma_crim") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input major crimes list:", "Secure. records", active2.fields["ma_crim"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_crim"] = t1 - if("ma_crim_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize major crimes:", "Secure. records", active2.fields["ma_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_crim_d"] = t1 - if("notes") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Secure. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["notes"] = t1 - if("criminal") - if(istype(active2, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "None", "icon" = "unlock", "href" = "criminal=none", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_NONE ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Arrest*", "icon" = "lock", "href" = "criminal=arrest", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_ARREST ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Search", "icon" = "lock", "href" = "criminal=search", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_SEARCH ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Monitor", "icon" = "unlock", "href" = "criminal=monitor", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_MONITOR ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Demote", "icon" = "lock", "href" = "criminal=demote", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_DEMOTE ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Incarcerated", "icon" = "lock", "href" = "criminal=incarcerated", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_INCARCERATED ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Execute*", "icon" = "lock", "href" = "criminal=execute", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_EXECUTE ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Parolled", "icon" = "unlock-alt", "href" = "criminal=parolled", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_PAROLLED ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Released", "icon" = "unlock", "href" = "criminal=released", "status" = (active2.fields["criminal"] == SEC_RECORD_STATUS_RELEASED ? "selected" : null)) - setTemp("

    Criminal Status

    ", buttons) - if("rank") - var/list/L = list("Head of Personnel", "Captain", "AI") - //This was so silly before the change. Now it actually works without beating your head against the keyboard. /N - if(istype(active1, /datum/data/record) && L.Find(rank)) - var/list/buttons = list() - for(var/rank in GLOB.joblist) - buttons[++buttons.len] = list("name" = rank, "icon" = null, "href" = "rank=[rank]", "status" = (active1.fields["rank"] == rank ? "selected" : null)) - setTemp("

    Rank

    ", buttons) +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/computer/secure_data/proc/tgui_act_modal(action, list/params) + if(!tgui_login_get().logged_in) + return + . = TRUE + var/id = params["id"] + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_OPEN) + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/question = field_edit_questions[field] + var/choices = field_edit_choices[field] + if(length(choices)) + tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices) else - setTemp("

    You do not have the required rank to do this!

    ") - if("species") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["species"] = t1 - return 1 + tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"]) + if("comment_add") + tgui_modal_input(src, id, "Please enter your message:") + if("print_cell_log") + if(is_printing) + return + if(!length(GLOB.cell_logs)) + set_temp("There are no cell logs available to print.") + return + var/list/choices = list() + var/list/already_in = list() + for(var/p in GLOB.cell_logs) + var/obj/item/paper/P = p + if(already_in[P.name]) + continue + choices += P.name + already_in[P.name] = TRUE + tgui_modal_choice(src, id, "Please select the cell log you would like printed:", choices = choices) + else + return FALSE + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("edit") + var/field = arguments["field"] + if(!length(field) || !field_edit_questions[field]) + return + var/list/choices = field_edit_choices[field] + if(length(choices) && !(answer in choices)) + return -/obj/machinery/computer/secure_data/proc/setTemp(text, list/buttons = list()) - temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) + if(field == "age") + var/new_age = text2num(answer) + if(new_age < AGE_MIN || new_age > AGE_MAX) + set_temp("Invalid age. It must be between [AGE_MIN] and [AGE_MAX].", "danger") + return + answer = new_age + else if(field == "criminal") + var/text = "Please enter a reason for the status change to [answer]:" + if(answer == SEC_RECORD_STATUS_EXECUTE) + text = "Please explain why they are being executed. Include a list of their crimes, and victims." + else if(answer == SEC_RECORD_STATUS_DEMOTE) + text = "Please explain why they are being demoted. Include a list of their offenses." + tgui_modal_input(src, "criminal_reason", text, arguments = list("status" = answer)) + return -/* Proc disabled due to BYOND Issue + if(record_security && (field in record_security.fields)) + record_security.fields[field] = answer + else if(record_general && (field in record_general.fields)) + record_general.fields[field] = answer + if("criminal_reason") + var/status = arguments["status"] + if(!record_security || !(status in field_edit_choices["criminal"])) + return + if((status in list(SEC_RECORD_STATUS_EXECUTE, SEC_RECORD_STATUS_DEMOTE)) && !length(answer)) + set_temp("A valid reason must be provided for this status.", "danger") + return + var/datum/tgui_login/state = tgui_login_get() + if(!set_criminal_status(usr, record_security, status, answer, state.rank, state.access, state.name)) + set_temp("Required permissions to set this criminal status not found!", "danger") + if("comment_add") + var/datum/tgui_login/state = tgui_login_get() + if(!length(answer) || !record_security || !length(state.name)) + return + record_security.fields["comments"] += list(list( + header = "Made by [state.name] ([state.rank]) on [GLOB.current_date_string] [station_time_timestamp()]", + text = answer + )) + if("print_cell_log") + if(is_printing) + return + var/obj/item/paper/T + for(var/obj/item/paper/P in GLOB.cell_logs) + if(P.name == answer) + T = P + break + if(!T) + set_temp("Cell log not found!", "danger") + return + is_printing = TRUE + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + addtimer(CALLBACK(src, .proc/print_cell_log_finish, T.name, T.info), 5 SECONDS) + else + return FALSE + else + return FALSE -/obj/machinery/computer/secure_data/proc/create_record_photo(datum/data/record/R) - // basically copy-pasted from the camera code but different enough that it has to be redone - var/icon/photoimage = get_record_photo(R) - var/icon/small_img = icon(photoimage) - var/icon/tiny_img = icon(photoimage) - var/icon/ic = icon('icons/obj/items.dmi',"photo") - var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") - small_img.Scale(8, 8) - tiny_img.Scale(4, 4) - ic.Blend(small_img, ICON_OVERLAY, 10, 13) - pc.Blend(tiny_img, ICON_OVERLAY, 12, 19) +/** + * Called when the print record timer finishes + */ +/obj/machinery/computer/secure_data/proc/print_record_finish() + var/obj/item/paper/P = new(loc) + P.info = "
    Security Record

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

    "} + for(var/c in record_security.fields["comments"]) + P.info += "[c]
    " + else + P.info += "Security Record Lost!
    " + is_printing = FALSE + SStgui.update_uis(src) - var/datum/picture/P = new() - P.fields["name"] = "File Photo - [R.fields["name"]]" - P.fields["author"] = "Central Command" - P.fields["icon"] = ic - P.fields["tiny"] = pc - P.fields["img"] = photoimage - P.fields["desc"] = "You can see [R.fields["name"]] on the photo." - P.fields["pixel_x"] = rand(-10, 10) - P.fields["pixel_y"] = rand(-10, 10) - P.fields["size"] = 2 - - var/obj/item/photo/PH = new/obj/item/photo(loc) - PH.construct(P) - -*/ - -/obj/machinery/computer/secure_data/proc/get_record_photo(datum/data/record/R) - // similar to the code to make a photo, but of course the actual rendering is completely different - var/icon/res = icon('icons/effects/96x96.dmi', "") - // will be 2x2 to fit the 2 directions - res.Scale(2 * 32, 2 * 32) - // transparent background (it's a plastic transparency, you see) with the front and side icons - res.Blend(icon(R.fields["photo"], dir = SOUTH), ICON_OVERLAY, 1, 17) - res.Blend(icon(R.fields["photo"], dir = WEST), ICON_OVERLAY, 33, 17) - - return res +/** + * Called when the print cell log timer finishes + */ +/obj/machinery/computer/secure_data/proc/print_cell_log_finish(name, info) + var/obj/item/paper/P = new(loc) + P.name = name + P.info = info + is_printing = FALSE + SStgui.update_uis(src) /obj/machinery/computer/secure_data/emp_act(severity) if(stat & (BROKEN|NOPOWER)) @@ -548,8 +451,8 @@ return for(var/datum/data/record/R in GLOB.data_core.security) - if(prob(10/severity)) - switch(rand(1,6)) + if(prob(10 / severity)) + switch(rand(1, 6)) if(1) R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" if(2) @@ -570,9 +473,17 @@ ..(severity) -/obj/machinery/computer/secure_data/detective_computer - icon = 'icons/obj/computer.dmi' - icon_state = "messyfiles" +/** + * Sets a temporary message to display to the user + * + * Arguments: + * * text - Text to display, null/empty to clear the message from the UI + * * style - The style of the message: (color name), info, success, warning, danger + */ +/obj/machinery/computer/secure_data/proc/set_temp(text = "", style = "info", update_now = FALSE) + temp_notice = list(text = text, style = style) + if(update_now) + SStgui.update_uis(src) /obj/machinery/computer/secure_data/laptop name = "security laptop" @@ -580,8 +491,9 @@ icon_state = "laptop" icon_keyboard = "seclaptop_key" icon_screen = "seclaptop" - density = 0 + density = FALSE #undef SEC_DATA_R_LIST #undef SEC_DATA_MAINT #undef SEC_DATA_RECORD +#undef SEC_FIELD diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm deleted file mode 100644 index b7361db01a3..00000000000 --- a/code/game/machinery/computer/skills.dm +++ /dev/null @@ -1,328 +0,0 @@ -#define SKILL_DATA_R_LIST 1 // Record list -#define SKILL_DATA_MAINT 2 // Records maintenance -#define SKILL_DATA_RECORD 3 // Record - -/obj/machinery/computer/skills//TODO:SANITY - name = "employment records console" - desc = "Used to view personnel's employment records" - icon_state = "laptop" - icon_keyboard = "laptop_key" - icon_screen = "medlaptop" - density = 0 - light_color = LIGHT_COLOR_GREEN - req_one_access = list(ACCESS_HEADS) - circuit = /obj/item/circuitboard/skills - var/obj/item/card/id/scan = null - var/authenticated = null - var/rank = null - var/screen = null - var/datum/data/record/active1 = null - var/temp = null - var/printing = null - //Sorting Variables - var/sortBy = "name" - var/order = 1 // -1 = Descending - 1 = Ascending - -/obj/machinery/computer/skills/Destroy() - active1 = null - return ..() - -/obj/machinery/computer/skills/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/card/id) && !scan) - user.drop_item() - O.forceMove(src) - scan = O - ui_interact(user) - return - return ..() - -//Someone needs to break down the dat += into chunks instead of long ass lines. -/obj/machinery/computer/skills/attack_hand(mob/user) - if(..()) - return - if(is_away_level(z)) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/computer/skills/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "skills_data.tmpl", name, 800, 380) - ui.open() - -/obj/machinery/computer/skills/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["screen"] = screen - if(authenticated) - switch(screen) - if(SKILL_DATA_R_LIST) - if(!isnull(GLOB.data_core.general)) - for(var/datum/data/record/R in sortRecord(GLOB.data_core.general, sortBy, order)) - data["records"] += list(list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"], "rank" = R.fields["rank"], "fingerprint" = R.fields["fingerprint"])) - if(SKILL_DATA_RECORD) - var/list/general = list() - data["general"] = general - if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "name" = "name") - fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "name" = "id") - fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "name" = "sex") - fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "name" = "age") - fields[++fields.len] = list("field" = "Rank:", "value" = active1.fields["rank"], "name" = "rank") - fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "name" = "fingerprint") - fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"]) - fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"]) - general["notes"] = active1.fields["notes"] - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = list("photo" = active1.fields["photo-south"]) - photos[++photos.len] = list("photo" = active1.fields["photo-west"]) - general["has_photos"] += (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 - else - general["empty"] = 1 - return data - -/obj/machinery/computer/skills/Topic(href, href_list) - if(..()) - return 1 - - if(!GLOB.data_core.general.Find(active1)) - active1 = null - - if(href_list["temp"]) - temp = null - - if(href_list["temp_action"]) - var/temp_list = splittext(href_list["temp_action"], "=") - switch(temp_list[1]) - if("del_all2") - if(GLOB.PDA_Manifest && GLOB.PDA_Manifest.len) - GLOB.PDA_Manifest.Cut() - for(var/datum/data/record/R in GLOB.data_core.security) - qdel(R) - setTemp("

    All employment records deleted.

    ") - if("del_rg2") - if(active1) - if(GLOB.PDA_Manifest && GLOB.PDA_Manifest.len) - GLOB.PDA_Manifest.Cut() - for(var/datum/data/record/R in GLOB.data_core.medical) - if(R.fields["name"] == active1.fields["name"] && R.fields["id"] == active1.fields["id"]) - qdel(R) - QDEL_NULL(active1) - screen = SKILL_DATA_R_LIST - if("rank") - if(active1) - if(GLOB.PDA_Manifest && GLOB.PDA_Manifest.len) - GLOB.PDA_Manifest.Cut() - active1.fields["rank"] = temp_list[2] - if(temp_list[2] in GLOB.joblist) - active1.fields["real_rank"] = temp_list[2] - - if(href_list["scan"]) - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - - if(href_list["login"]) - if(isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - else if(istype(scan, /obj/item/card/id)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - - if(authenticated) - active1 = null - screen = SKILL_DATA_R_LIST - - if(authenticated) - var/incapable = (usr.stat || usr.restrained() || (!in_range(src, usr) && !issilicon(usr))) - if(href_list["logout"]) - authenticated = null - screen = null - active1 = null - - else if(href_list["sort"]) - // Reverse the order if clicked twice - if(sortBy == href_list["sort"]) - if(order == 1) - order = -1 - else - order = 1 - else - sortBy = href_list["sort"] - order = initial(order) - - else if(href_list["screen"]) - screen = text2num(href_list["screen"]) - if(screen < 1) - screen = SKILL_DATA_R_LIST - - active1 = null - - else if(href_list["d_rec"]) - var/datum/data/record/R = locate(href_list["d_rec"]) - if(!GLOB.data_core.general.Find(R)) - setTemp("

    Record not found!

    ") - return 1 - active1 = R - screen = SKILL_DATA_RECORD - - else if(href_list["del_all"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "val" = "del_all2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "val" = null, "status" = null) - setTemp("

    Are you sure you wish to delete all employment records?

    ", buttons) - - else if(href_list["del_rg"]) - if(active1) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "val" = "del_rg2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "val" = null, "status" = null) - setTemp("

    Are you sure you wish to delete the record (ALL)?

    ", buttons) - - else if(href_list["new_g"]) - if(GLOB.PDA_Manifest.len) - GLOB.PDA_Manifest.Cut() - var/datum/data/record/G = new /datum/data/record() - G.fields["name"] = "New Record" - G.fields["id"] = "[add_zero(num2hex(rand(1, 1.6777215E7)), 6)]" - G.fields["rank"] = "Unassigned" - G.fields["real_rank"] = "Unassigned" - G.fields["sex"] = "Male" - G.fields["age"] = "Unknown" - G.fields["fingerprint"] = "Unknown" - G.fields["p_stat"] = "Active" - G.fields["m_stat"] = "Stable" - G.fields["species"] = "Human" - GLOB.data_core.general += G - active1 = G - - else if(href_list["print_r"]) - if(!printing) - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - sleep(50) - var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
    Employment Record

    " - if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
    \nSex: [active1.fields["sex"]] -
    \nAge: [active1.fields["age"]] -
    \nFingerprint: [active1.fields["fingerprint"]] -
    \nPhysical Status: [active1.fields["p_stat"]] -
    \nMental Status: [active1.fields["m_stat"]] -
    \nEmployment/Skills Summary:[active1.fields["notes"]]
    "} - else - P.info += "General Record Lost!
    " - P.info += "" - P.name = "paper - 'Employment Record: [active1.fields["name"]]'" - printing = 0 - - if(href_list["field"]) - if(incapable) - return 1 - var/a1 = active1 - switch(href_list["field"]) - if("name") - if(istype(active1, /datum/data/record)) - var/t1 = reject_bad_name(clean_input("Please input name:", "Secure. records", active1.fields["name"], null)) - if(!t1 || !length(trim(t1)) || incapable || active1 != a1) - return 1 - active1.fields["name"] = t1 - if("id") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || incapable || active1 != a1) - return 1 - active1.fields["id"] = t1 - if("fingerprint") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || incapable || active1 != a1) - return 1 - active1.fields["fingerprint"] = t1 - if("sex") - if(istype(active1, /datum/data/record)) - if(active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" - else - active1.fields["sex"] = "Male" - if("age") - if(istype(active1, /datum/data/record)) - var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num - if(!t1 || incapable || active1 != a1) - return 1 - active1.fields["age"] = t1 - if("rank") - var/list/L = list("Head of Personnel", "Captain", "AI") - //This was so silly before the change. Now it actually works without beating your head against the keyboard. /N - if(istype(active1, /datum/data/record) && L.Find(rank)) - var/list/buttons = list() - for(var/rank in GLOB.joblist) - buttons[++buttons.len] = list("name" = rank, "icon" = null, "val" = "rank=[rank]", "status" = (active1.fields["rank"] == rank ? "selected" : null)) - setTemp("

    Rank

    ", buttons) - else - setTemp("You do not have the required rank to do this!") - if("species") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || incapable || active1 != a1) - return 1 - active1.fields["species"] = t1 - return 1 - -/obj/machinery/computer/skills/proc/setTemp(text, list/buttons = list()) - temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) - -/obj/machinery/computer/skills/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - for(var/datum/data/record/R in GLOB.data_core.security) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["criminal"] = pick(SEC_RECORD_STATUS_NONE, SEC_RECORD_STATUS_ARREST, SEC_RECORD_STATUS_INCARCERATED, SEC_RECORD_STATUS_PAROLLED, SEC_RECORD_STATUS_RELEASED) - if(5) - R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - -#undef SKILL_DATA_R_LIST -#undef SKILL_DATA_MAINT -#undef SKILL_DATA_RECORD diff --git a/code/game/machinery/computer/sm_monitor.dm b/code/game/machinery/computer/sm_monitor.dm new file mode 100644 index 00000000000..f2e01e6c4fa --- /dev/null +++ b/code/game/machinery/computer/sm_monitor.dm @@ -0,0 +1,147 @@ +/obj/machinery/computer/sm_monitor + name = "supermatter monitoring console" + desc = "Used to monitor supermatter shards." + icon_keyboard = "power_key" + icon_screen = "smmon_0" + circuit = /obj/item/circuitboard/sm_monitor + light_color = LIGHT_COLOR_YELLOW + /// Cache-list of all supermatter shards + var/list/supermatters + /// Last status of the active supermatter for caching purposes + var/last_status + /// Reference to the active shard + var/obj/machinery/power/supermatter_shard/active + +/obj/machinery/computer/sm_monitor/Destroy() + active = null + return ..() + +/obj/machinery/computer/sm_monitor/attack_ai(mob/user) + attack_hand(user) + +/obj/machinery/computer/sm_monitor/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + tgui_interact(user) + +/obj/machinery/computer/sm_monitor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "SupermatterMonitor", name, 600, 325, master_ui, state) + ui.open() + +/obj/machinery/computer/sm_monitor/tgui_data(mob/user) + var/list/data = list() + + if(istype(active)) + var/turf/T = get_turf(active) + // If we somehow delam during this proc, handle it somewhat + if(!T) + active = null + refresh() + return + var/datum/gas_mixture/air = T.return_air() + if(!air) + active = null + return + + data["active"] = TRUE + data["SM_integrity"] = active.get_integrity() + data["SM_power"] = active.power + data["SM_ambienttemp"] = air.temperature + data["SM_ambientpressure"] = air.return_pressure() + //data["SM_EPR"] = round((air.total_moles / air.group_multiplier) / 23.1, 0.01) + var/other_moles = air.total_trace_moles() + var/TM = air.total_moles() + if(TM) + data["SM_gas_O2"] = round(100*air.oxygen/TM, 0.01) + data["SM_gas_CO2"] = round(100*air.carbon_dioxide/TM, 0.01) + data["SM_gas_N2"] = round(100*air.nitrogen/TM, 0.01) + data["SM_gas_PL"] = round(100*air.toxins/TM, 0.01) + if(other_moles) + data["SM_gas_OTHER"] = round(100 * other_moles / TM, 0.01) + else + data["SM_gas_OTHER"] = 0 + else + data["SM_gas_O2"] = 0 + data["SM_gas_CO2"] = 0 + data["SM_gas_N2"] = 0 + data["SM_gas_PH"] = 0 + data["SM_gas_OTHER"] = 0 + else + var/list/SMS = list() + for(var/I in supermatters) + var/obj/machinery/power/supermatter_shard/S = I + var/area/A = get_area(S) + if(!A) + continue + + SMS.Add(list(list( + "area_name" = A.name, + "integrity" = S.get_integrity(), + "uid" = S.UID() + ))) + + data["active"] = FALSE + data["supermatters"] = SMS + + return data + +/** + * Supermatter List Refresher + * + * This proc loops through the list of supermatters in the atmos SS and adds them to this console's cache list + */ +/obj/machinery/computer/sm_monitor/proc/refresh() + supermatters = list() + var/turf/T = get_turf(tgui_host()) // Get the TGUI host incase this ever turned into a supermatter monitoring module for AIs to use or something + if(!T) + return + for(var/obj/machinery/power/supermatter_shard/S in SSair.atmos_machinery) + // Delaminating, not within coverage, not on a tile. + if(!(is_station_level(S.z) || is_mining_level(S.z) || atoms_share_level(S, T) || !istype(S.loc, /turf/simulated/))) + continue + supermatters.Add(S) + + if(!(active in supermatters)) + active = null + +/obj/machinery/computer/sm_monitor/process() + if(stat & (NOPOWER|BROKEN)) + return FALSE + + if(active) + var/new_status = active.get_status() + if(last_status != new_status) + last_status = new_status + if(last_status == SUPERMATTER_ERROR) + last_status = SUPERMATTER_INACTIVE + icon_screen = "smmon_[last_status]" + update_icon() + + return TRUE + +/obj/machinery/computer/sm_monitor/tgui_act(action, params) + if(..()) + return + + if(stat & (BROKEN|NOPOWER)) + return + + . = TRUE + + switch(action) + if("refresh") + refresh() + + if("view") + var/newuid = params["view"] + for(var/obj/machinery/power/supermatter_shard/S in supermatters) + if(S.UID() == newuid) + active = S + break + + if("back") + active = null + diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm index 607918442ea..3b4631b171a 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -6,48 +6,91 @@ icon_screen = "alert:0" light_color = LIGHT_COLOR_CYAN circuit = /obj/item/circuitboard/stationalert_engineering - var/datum/nano_module/alarm_monitor/alarm_monitor - var/monitor_type = /datum/nano_module/alarm_monitor/engineering + var/ui_x = 325 + var/ui_y = 500 + var/list/alarms_listend_for = list("Fire", "Atmosphere", "Power") -/obj/machinery/computer/station_alert/security - monitor_type = /datum/nano_module/alarm_monitor/security - circuit = /obj/item/circuitboard/stationalert_security - -/obj/machinery/computer/station_alert/all - monitor_type = /datum/nano_module/alarm_monitor/all - circuit = /obj/item/circuitboard/stationalert_all - -/obj/machinery/computer/station_alert/New() - ..() - alarm_monitor = new monitor_type(src) - alarm_monitor.register(src, /obj/machinery/computer/station_alert/.proc/update_icon) +/obj/machinery/computer/station_alert/Initialize(mapload) + . = ..() + GLOB.alert_consoles += src + RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered) + RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled) /obj/machinery/computer/station_alert/Destroy() - alarm_monitor.unregister(src) - QDEL_NULL(alarm_monitor) + GLOB.alert_consoles -= src return ..() /obj/machinery/computer/station_alert/attack_ai(mob/user) add_fingerprint(user) if(stat & (BROKEN|NOPOWER)) return - interact(user) + tgui_interact(user) /obj/machinery/computer/station_alert/attack_hand(mob/user) add_fingerprint(user) if(stat & (BROKEN|NOPOWER)) return - interact(user) + tgui_interact(user) -/obj/machinery/computer/station_alert/interact(mob/user) - alarm_monitor.ui_interact(user) +/obj/machinery/computer/station_alert/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "StationAlertConsole", name, ui_x, ui_y, master_ui, state) + ui.open() + +/obj/machinery/computer/station_alert/tgui_data(mob/user) + var/list/data = list() + + data["alarms"] = list() + for(var/class in SSalarm.alarms) + if(!(class in alarms_listend_for)) + continue + data["alarms"][class] = list() + for(var/area in SSalarm.alarms[class]) + for(var/thing in SSalarm.alarms[class][area][3]) + var/atom/A = locateUID(thing) + if(atoms_share_level(A, src)) + data["alarms"][class] += area + + return data + +/obj/machinery/computer/station_alert/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource) + if(!(class in alarms_listend_for)) + return + if(alarmsource.z != z) + return + if(stat & (BROKEN)) + return + update_icon() + +/obj/machinery/computer/station_alert/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared) + if(!(class in alarms_listend_for)) + return + if(origin.z != z) + return + if(stat & (BROKEN)) + return + update_icon() /obj/machinery/computer/station_alert/update_icon() - if(alarm_monitor) - var/list/alarms = alarm_monitor.major_alarms() - if(alarms.len) - icon_screen = "alert:2" - else - icon_screen = "alert:0" + var/active_alarms = FALSE + var/list/list/temp_alarm_list = SSalarm.alarms.Copy() + for(var/cat in temp_alarm_list) + if(!(cat in alarms_listend_for)) + continue + var/list/list/L = temp_alarm_list[cat].Copy() + for(var/alarm in L) + var/list/list/alm = L[alarm].Copy() + var/list/list/sources = alm[3].Copy() + for(var/thing in sources) + var/atom/A = locateUID(thing) + if(A && A.z != z) + L -= alarm + if(length(L)) + active_alarms = TRUE + if(active_alarms) + icon_screen = "alert:2" + else + icon_screen = "alert:0" ..() diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index ee29504fe99..8ee774d1ae0 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -253,7 +253,6 @@ to destroy them and players will be able to make replacements. /obj/machinery/vending/engineering = "Robco Tool Maker", /obj/machinery/vending/sovietsoda = "BODA", /obj/machinery/vending/security = "SecTech", - /obj/machinery/vending/modularpc = "Deluxe Silicate Selections", /obj/machinery/vending/crittercare = "CritterCare") /obj/item/circuitboard/vendor/screwdriver_act(mob/user, obj/item/I) diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index 4213ff517bf..70798c0b92e 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -27,12 +27,13 @@ var/running_bob_animation = 0 // This is used to prevent threads from building up if update_icons is called multiple times light_color = LIGHT_COLOR_WHITE - power_change() - ..() - if(!(stat & (BROKEN|NOPOWER))) - set_light(2) - else - set_light(0) + +/obj/machinery/atmospherics/unary/cryo_cell/power_change() + ..() + if(!(stat & (BROKEN|NOPOWER))) + set_light(2) + else + set_light(0) /obj/machinery/atmospherics/unary/cryo_cell/New() ..() @@ -103,7 +104,7 @@ beaker.forceMove(drop_location()) beaker = null -/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) +/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O, mob/living/user) if(O.loc == user) //no you can't pull things out of your ass return if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other @@ -140,6 +141,7 @@ add_attack_logs(user, L, "put into a cryo cell at [COORD(src)].", ATKLOG_ALL) if(user.pulling == L) user.stop_pulling() + SStgui.update_uis(src) /obj/machinery/atmospherics/unary/cryo_cell/process() ..() @@ -177,14 +179,14 @@ return FALSE -/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob) +/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user) if(user.stat) return go_out() return /obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user) - return attack_hand(user) + tgui_interact(user) /obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) if(user == occupant) @@ -194,36 +196,18 @@ to_chat(usr, "Close the maintenance panel first.") return - ui_interact(user) + tgui_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) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/atmospherics/unary/cryo_cell/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, 480) - // open the new ui window + ui = new(user, src, ui_key, "Cryo", "Cryo Cell", 520, 490) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) -/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/atmospherics/unary/cryo_cell/tgui_data(mob/user) var/data[0] data["isOperating"] = on - data["hasOccupant"] = occupant ? 1 : 0 + data["hasOccupant"] = occupant ? TRUE : FALSE var/occupantData[0] if(occupant) @@ -246,7 +230,7 @@ else if(air_contents.temperature > TCRYO) data["cellTemperatureStatus"] = "average" - data["isBeakerLoaded"] = beaker ? 1 : 0 + data["isBeakerLoaded"] = beaker ? TRUE : FALSE data["beakerLabel"] = null data["beakerVolume"] = 0 if(beaker) @@ -259,48 +243,44 @@ data["auto_eject_dead"] = (auto_eject_prefs & AUTO_EJECT_DEAD) ? TRUE : FALSE return data -/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list) - if(usr == occupant) - return 0 // don't update UIs attached to this object +/obj/machinery/atmospherics/unary/cryo_cell/tgui_act(action, params) + if(..() || usr == occupant) + return + if(stat & (NOPOWER|BROKEN)) + return - if(..()) - return 0 // don't update UIs attached to this object - - if(href_list["switchOn"]) - on = TRUE - update_icon() - - if(href_list["switchOff"]) - on = FALSE - update_icon() - - if(href_list["auto_eject_healthy_on"]) - auto_eject_prefs |= AUTO_EJECT_HEALTHY - - if(href_list["auto_eject_healthy_off"]) - auto_eject_prefs &= ~AUTO_EJECT_HEALTHY - - if(href_list["auto_eject_dead_on"]) - auto_eject_prefs |= AUTO_EJECT_DEAD - - if(href_list["auto_eject_dead_off"]) - auto_eject_prefs &= ~AUTO_EJECT_DEAD - - if(href_list["ejectBeaker"]) - if(beaker) + . = TRUE + switch(action) + if("switchOn") + on = TRUE + update_icon() + if("switchOff") + on = FALSE + update_icon() + if("auto_eject_healthy_on") + auto_eject_prefs |= AUTO_EJECT_HEALTHY + if("auto_eject_healthy_off") + auto_eject_prefs &= ~AUTO_EJECT_HEALTHY + if("auto_eject_dead_on") + auto_eject_prefs |= AUTO_EJECT_DEAD + if("auto_eject_dead_off") + auto_eject_prefs &= ~AUTO_EJECT_DEAD + if("ejectBeaker") + if(!beaker) + return 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 - add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL) - go_out() + if("ejectOccupant") + if(!occupant || isslime(usr) || ispAI(usr)) + return + add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL) + go_out() + else + return FALSE 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, params) +/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G, var/mob/user, params) if(istype(G, /obj/item/reagent_containers/glass)) var/obj/item/reagent_containers/B = G if(beaker) @@ -313,6 +293,7 @@ beaker = B add_attack_logs(user, null, "Added [B] containing [B.reagents.log_list()] to a cryo cell at [COORD(src)]") user.visible_message("[user] adds \a [B] to [src]!", "You add \a [B] to [src]!") + SStgui.update_uis(src) return if(exchange_parts(user, G)) @@ -357,7 +338,7 @@ return if(occupant) - var/image/pickle = image(occupant.icon, occupant.icon_state) + var/mutable_appearance/pickle = mutable_appearance(occupant.icon, occupant.icon_state) pickle.overlays = occupant.overlays pickle.pixel_y = 22 @@ -443,6 +424,9 @@ for(var/atom/movable/A in contents - component_parts - list(beaker)) A.forceMove(get_step(loc, SOUTH)) +/obj/machinery/atmospherics/unary/cryo_cell/force_eject_occupant() + go_out() + /// Called when either the occupant is dead and the AUTO_EJECT_DEAD flag is present, OR the occupant is alive, has no external damage, and the AUTO_EJECT_HEALTHY flag is present. /obj/machinery/atmospherics/unary/cryo_cell/proc/auto_eject(eject_flag) on = FALSE @@ -452,8 +436,9 @@ playsound(loc, 'sound/machines/ding.ogg', 50, 1) if(AUTO_EJECT_DEAD) playsound(loc, 'sound/machines/buzz-sigh.ogg', 40) + SStgui.update_uis(src) -/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) +/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M) if(!istype(M)) to_chat(usr, "The cryo cell cannot handle such a lifeform!") return @@ -527,7 +512,7 @@ /datum/data/function/proc/reset() return -/datum/data/function/proc/r_input(href, href_list, mob/user as mob) +/datum/data/function/proc/r_input(href, href_list, mob/user) return /datum/data/function/proc/display() diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 36196ea4eca..a13f87845ff 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -302,6 +302,7 @@ // Eject dead people if(occupant.stat == DEAD) go_out() + return // Allow a gap between entering the pod and actually despawning. if(world.time - time_entered < time_till_despawn) @@ -768,9 +769,13 @@ return ..() + /proc/cryo_ssd(var/mob/living/carbon/person_to_cryo) if(istype(person_to_cryo.loc, /obj/machinery/cryopod)) return 0 + if(isobj(person_to_cryo.loc)) + var/obj/O = person_to_cryo.loc + O.force_eject_occupant() var/list/free_cryopods = list() for(var/obj/machinery/cryopod/P in GLOB.machines) if(!P.occupant && istype(get_area(P), /area/crew_quarters/sleep)) @@ -779,6 +784,9 @@ if(free_cryopods.len) target_cryopod = safepick(free_cryopods) if(target_cryopod.check_occupant_allowed(person_to_cryo)) + var/turf/T = get_turf(person_to_cryo) + var/obj/effect/portal/SP = new /obj/effect/portal(T, null, null, 40) + SP.name = "NT SSD Teleportation Portal" target_cryopod.take_occupant(person_to_cryo, 1) return 1 return 0 diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 42ff08ff00f..a732348ac3c 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -38,6 +38,11 @@ #define AIRLOCK_DAMAGE_DEFLECTION_N 21 // Normal airlock damage deflection #define AIRLOCK_DAMAGE_DEFLECTION_R 30 // Reinforced airlock damage deflection +#define TGUI_GREEN 2 +#define TGUI_ORANGE 1 +#define TGUI_RED 0 + + GLOBAL_LIST_EMPTY(airlock_overlays) /obj/machinery/door/airlock @@ -51,10 +56,9 @@ GLOBAL_LIST_EMPTY(airlock_overlays) autoclose = TRUE explosion_block = 1 assemblytype = /obj/structure/door_assembly - normalspeed = 1 siemens_strength = 1 var/security_level = 0 //How much are wires secured - var/aiControlDisabled = FALSE //If TRUE, 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/aiControlDisabled = AICONTROLDISABLED_OFF var/hackProof = FALSE // if TRUE, 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. @@ -65,7 +69,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays) var/spawnPowerRestoreRunning = 0 var/lights = TRUE // bolt lights show by default var/datum/wires/airlock/wires - var/aiDisabledIdScanner = 0 + var/aiDisabledIdScanner = FALSE var/aiHacking = 0 var/obj/machinery/door/airlock/closeOther var/closeOtherId @@ -96,7 +100,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays) var/doorDeni = 'sound/machines/deniedbeep.ogg' // i'm thinkin' Deni's var/boltUp = 'sound/machines/boltsup.ogg' var/boltDown = 'sound/machines/boltsdown.ogg' - var/is_special = 0 + var/is_special = FALSE /obj/machinery/door/airlock/welded welded = TRUE @@ -142,6 +146,7 @@ About the new airlock wires panel: break /obj/machinery/door/airlock/Destroy() + SStgui.close_uis(wires) QDEL_NULL(electronics) QDEL_NULL(wires) QDEL_NULL(note) @@ -173,7 +178,7 @@ About the new airlock wires panel: spawn (10) justzap = 0 return - else /*if(justzap)*/ + else return else if(user.hallucination > 50 && prob(10) && !operating) if(user.electrocute_act(50, src, 1, illusion = TRUE)) // We'll just go with a flat 50 damage, instead of doing powernet checks @@ -188,15 +193,11 @@ About the new airlock wires panel: return 1 return 0 -/obj/machinery/door/airlock/proc/isWireCut(wireIndex) - // You can find the wires in the datum folder. - return wires.IsIndexCut(wireIndex) - /obj/machinery/door/airlock/proc/canAIControl() - return ((aiControlDisabled!=1) && (!isAllPowerLoss())) + return ((aiControlDisabled != AICONTROLDISABLED_ON) && (!isAllPowerLoss())) /obj/machinery/door/airlock/proc/canAIHack() - return ((aiControlDisabled==1) && (!hackProof) && (!isAllPowerLoss())) + return ((aiControlDisabled == AICONTROLDISABLED_ON) && (!hackProof) && (!isAllPowerLoss())) /obj/machinery/door/airlock/proc/arePowerSystemsOn() if(stat & (NOPOWER|BROKEN)) @@ -204,27 +205,21 @@ About the new airlock wires panel: return (main_power_lost_until==0 || backup_power_lost_until==0) /obj/machinery/door/airlock/requiresID() - return !(isWireCut(AIRLOCK_WIRE_IDSCAN) || aiDisabledIdScanner) + return !(wires.is_cut(WIRE_IDSCAN) || aiDisabledIdScanner) /obj/machinery/door/airlock/proc/isAllPowerLoss() if(stat & (NOPOWER|BROKEN)) return 1 - if(mainPowerCablesCut() && backupPowerCablesCut()) + if(wires.is_cut(WIRE_MAIN_POWER1) && wires.is_cut(WIRE_BACKUP_POWER1)) return 1 return 0 -/obj/machinery/door/airlock/proc/mainPowerCablesCut() - return isWireCut(AIRLOCK_WIRE_MAIN_POWER1) - -/obj/machinery/door/airlock/proc/backupPowerCablesCut() - return isWireCut(AIRLOCK_WIRE_BACKUP_POWER1) - /obj/machinery/door/airlock/proc/loseMainPower() - main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + 60 SECONDS + main_power_lost_until = wires.is_cut(WIRE_MAIN_POWER1) ? -1 : world.time + 60 SECONDS if(main_power_lost_until > 0) main_power_timer = addtimer(CALLBACK(src, .proc/regainMainPower), 60 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE) // 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()) + if(backup_power_lost_until == -1 && !wires.is_cut(WIRE_BACKUP_POWER1)) backup_power_lost_until = world.time + 10 SECONDS backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), 10 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE) // Disable electricity if required @@ -232,7 +227,7 @@ About the new airlock wires panel: electrify(0) /obj/machinery/door/airlock/proc/loseBackupPower() - backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + 60 SECONDS + backup_power_lost_until = wires.is_cut(WIRE_BACKUP_POWER1) ? -1 : world.time + 60 SECONDS if(backup_power_lost_until > 0) backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), 60 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE) @@ -243,7 +238,7 @@ About the new airlock wires panel: /obj/machinery/door/airlock/proc/regainMainPower() main_power_timer = null - if(!mainPowerCablesCut()) + if(!wires.is_cut(WIRE_MAIN_POWER1)) 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) @@ -253,18 +248,18 @@ About the new airlock wires panel: /obj/machinery/door/airlock/proc/regainBackupPower() backup_power_timer = null - if(!backupPowerCablesCut()) + if(!wires.is_cut(WIRE_BACKUP_POWER1)) // Restore backup power only if main power is offline, otherwise permanently disable backup_power_lost_until = main_power_lost_until == 0 ? -1 : 0 update_icon() -/obj/machinery/door/airlock/proc/electrify(duration, feedback = 0) +/obj/machinery/door/airlock/proc/electrify(duration, mob/user = usr, feedback = FALSE) if(electrified_timer) deltimer(electrified_timer) electrified_timer = null var/message = "" - if(isWireCut(AIRLOCK_WIRE_ELECTRIFY) && arePowerSystemsOn()) + if(wires.is_cut(WIRE_ELECTRIFY) && arePowerSystemsOn()) message = text("The electrification wire is cut - Door permanently electrified.") electrified_until = -1 else if(duration && !arePowerSystemsOn()) @@ -274,10 +269,10 @@ About the new airlock wires panel: message = "The door is now un-electrified." electrified_until = 0 else if(duration) //electrify door for the given duration seconds - if(usr) - shockedby += text("\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") - usr.create_attack_log("Electrified the [name] at [x] [y] [z]") - add_attack_logs(usr, src, "Electrified", ATKLOG_ALL) + if(user) + shockedby += text("\[[time_stamp()]\] - [user](ckey:[user.ckey])") + user.create_attack_log("Electrified the [name] at [x] [y] [z]") + add_attack_logs(user, src, "Electrified", ATKLOG_ALL) else shockedby += text("\[[time_stamp()]\] - EMP)") message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." @@ -285,7 +280,7 @@ About the new airlock wires panel: if(duration != -1) electrified_timer = addtimer(CALLBACK(src, .proc/electrify, 0), duration SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE) if(feedback && message) - to_chat(usr, message) + to_chat(user, message) // shock user with probability prb (if all connections & power are working) // returns 1 if shocked, 0 otherwise @@ -520,6 +515,10 @@ About the new airlock wires panel: var/obj/machinery/door/airlock/airlock = painter.available_paint_jobs["[painter.paint_setting]"] // get the airlock type path associated with the airlock name the user just chose var/obj/structure/door_assembly/assembly = initial(airlock.assemblytype) + if(assemblytype == assembly) + to_chat(user, "This airlock is already painted [painter.paint_setting]!") + return + if(airlock_material == "glass" && initial(assembly.noglass)) // prevents painting glass airlocks with a paint job that doesn't have a glass version, such as the freezer to_chat(user, "This paint job can only be applied to non-glass airlocks.") return @@ -570,38 +569,58 @@ About the new airlock wires panel: /obj/machinery/door/airlock/attack_ghost(mob/user) if(panel_open) wires.Interact(user) - ui_interact(user) + tgui_interact(user) /obj/machinery/door/airlock/attack_ai(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/door/airlock/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/door/airlock/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "door_control.tmpl", "Door Controls - [src]", 600, 375) + ui = new(user, src, ui_key, "AiAirlock", name, 600, 400, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/door/airlock/ui_data(mob/user, datum/topic_state/state = GLOB.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 +/obj/machinery/door/airlock/tgui_data(mob/user) + var/list/data = list() - 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) - commands[++commands.len] = list("name" = "Emergency Access","command"= "emergency", "active" = !emergency, "enabled" = "Disabled", "disabled" = "Enabled", "danger" = 0, "act" = 0) + var/list/power = list() + power["main"] = main_power_lost_until ? TGUI_RED : TGUI_GREEN + power["main_timeleft"] = max(main_power_lost_until - world.time, 0) / 10 + power["backup"] = backup_power_lost_until ? TGUI_RED : TGUI_GREEN + power["backup_timeleft"] = max(backup_power_lost_until - world.time, 0) / 10 + data["power"] = power + if(electrified_until == -1) + data["shock"] = TGUI_RED + else if(electrified_until > 0) + data["shock"] = TGUI_ORANGE + else + data["shock"] = TGUI_GREEN - data["commands"] = commands + data["shock_timeleft"] = max(electrified_until - world.time, 0) / 10 + data["id_scanner"] = !aiDisabledIdScanner + data["emergency"] = emergency // access + data["locked"] = locked // bolted + data["lights"] = lights // bolt lights + data["safe"] = safe // safeties + data["speed"] = normalspeed // safe speed + data["welded"] = welded // welded + data["opened"] = !density // opened + + var/list/wire = list() + wire["main_power"] = !wires.is_cut(WIRE_MAIN_POWER1) + wire["backup_power"] = !wires.is_cut(WIRE_BACKUP_POWER1) + wire["shock"] = !wires.is_cut(WIRE_ELECTRIFY) + wire["id_scanner"] = !wires.is_cut(WIRE_IDSCAN) + wire["bolts"] = !wires.is_cut(WIRE_DOOR_BOLTS) + wire["lights"] = !wires.is_cut(WIRE_BOLT_LIGHT) + wire["safe"] = !wires.is_cut(WIRE_SAFETY) + wire["timing"] = !wires.is_cut(WIRE_SPEED) + + data["wires"] = wire return data + /obj/machinery/door/airlock/proc/hack(mob/user) set waitfor = 0 if(!aiHacking) @@ -641,7 +660,7 @@ About the new airlock wires panel: to_chat(user, "Transfer complete. Forcing airlock to execute program.") sleep(50) //disable blocked control - aiControlDisabled = 2 + aiControlDisabled = AICONTROLDISABLED_BYPASS to_chat(user, "Receiving control information from airlock.") sleep(10) //bring up airlock dialog @@ -737,125 +756,136 @@ About the new airlock wires panel: else try_to_activate_door(user) -/obj/machinery/door/airlock/CanUseTopic(mob/user) - if(!issilicon(user) && !isobserver(user)) - return STATUS_CLOSE - +/obj/machinery/door/airlock/proc/ai_control_check(mob/user) + if(!issilicon(user)) + return TRUE if(emagged) to_chat(user, "Unable to interface: Internal error.") - return STATUS_CLOSE - if(!canAIControl() && !isobserver(user)) + return FALSE + if(!canAIControl()) if(canAIHack(user)) hack(user) else - if(isAllPowerLoss()) //don't really like how this gets checked a second time, but not sure how else to do it. + if(isAllPowerLoss()) to_chat(user, "Unable to interface: Connection timed out.") else to_chat(user, "Unable to interface: Connection refused.") - return STATUS_CLOSE + return FALSE + return TRUE - return ..() - -/obj/machinery/door/airlock/Topic(href, href_list, nowindow = 0) +/obj/machinery/door/airlock/tgui_act(action, params) if(..()) - return 1 - - var/activate = text2num(href_list["activate"]) - switch(href_list["command"]) - if("idscan") - if(isWireCut(AIRLOCK_WIRE_IDSCAN)) - to_chat(usr, "The IdScan wire has been cut - IdScan feature permanently disabled.") - else if(activate && aiDisabledIdScanner) - aiDisabledIdScanner = 0 - to_chat(usr, "IdScan feature has been enabled.") - else if(!activate && !aiDisabledIdScanner) - aiDisabledIdScanner = 1 - to_chat(usr, "IdScan feature has been disabled.") - if("main_power") + return + if(!issilicon(usr) && !usr.can_admin_interact()) + to_chat(usr, "Access denied. Only silicons may use this interface.") + return + if(!ai_control_check(usr)) + return + . = TRUE + switch(action) + if("disrupt-main") if(!main_power_lost_until) loseMainPower() update_icon() - if("backup_power") + else + to_chat(usr, "Main power is already offline.") + . = FALSE + if("disrupt-backup") if(!backup_power_lost_until) loseBackupPower() update_icon() - if("bolts") - if(isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) - to_chat(usr, "The door bolt control wire has been cut - Door bolts permanently dropped.") - else if(activate && lock()) - to_chat(usr, "The door bolts have been dropped.") - else if(!activate && unlock()) - to_chat(usr, "The door bolts have been raised.") - if("electrify_temporary") - if(activate && isWireCut(AIRLOCK_WIRE_ELECTRIFY)) - to_chat(usr, text("The electrification wire is cut - Door permanently electrified.")) - else if(!activate && electrified_until != 0) - to_chat(usr, "The door is now un-electrified.") - electrify(0) - else if(activate) //electrify door for 30 seconds - shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") - usr.create_attack_log("Electrified the [name] at [x] [y] [z]") - add_attack_logs(usr, src, "Electrified", ATKLOG_ALL) - to_chat(usr, "The door is now electrified for thirty seconds.") - electrify(30) - if("electrify_permanently") - if(isWireCut(AIRLOCK_WIRE_ELECTRIFY)) - to_chat(usr, text("The electrification wire is cut - Cannot electrify the door.")) - else if(!activate && electrified_until != 0) - to_chat(usr, "The door is now un-electrified.") - electrify(0) - else if(activate) - shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") - usr.create_attack_log("Electrified the [name] at [x] [y] [z]") - add_attack_logs(usr, src, "Electrified", ATKLOG_ALL) - to_chat(usr, "The door is now electrified.") - electrify(-1) - if("open") - if(welded) - to_chat(usr, text("The airlock has been welded shut!")) - else if(locked) - to_chat(usr, text("The door bolts are down!")) - else if(activate && density) - open() - else if(!activate && !density) - close() - if("safeties") - // Safeties! We don't need no stinking safeties! - if(isWireCut(AIRLOCK_WIRE_SAFETY)) - to_chat(usr, text("The safety wire is cut - Cannot secure the door.")) - else if(activate && safe) - safe = 0 - else if(!activate && !safe) - safe = 1 - if("timing") - // Door speed control - if(isWireCut(AIRLOCK_WIRE_SPEED)) - to_chat(usr, text("The timing wire is cut - Cannot alter timing.")) - else if(activate && normalspeed) - normalspeed = 0 - else if(!activate && !normalspeed) - normalspeed = 1 - if("lights") - // Bolt lights - if(isWireCut(AIRLOCK_WIRE_LIGHT)) - to_chat(usr, "The bolt lights wire has been cut - The door bolt lights are permanently disabled.") - else if(!activate && lights) - lights = 0 - to_chat(usr, "The door bolt lights have been disabled.") - else if(activate && !lights) - lights = 1 - to_chat(usr, "The door bolt lights have been enabled.") - update_icon() - if("emergency") - // Emergency access - if(emergency) - emergency = 0 - to_chat(usr, "Emergency access has been disabled.") else - emergency = 1 - to_chat(usr, "Emergency access has been enabled.") - update_icon() - return 1 + to_chat(usr, "Backup power is already offline.") + if("shock-restore") + electrify(0, usr, TRUE) + if("shock-temp") + if(wires.is_cut(WIRE_ELECTRIFY)) + to_chat(usr, "The electrification wire is cut - Door permanently electrified.") + . = FALSE + else + //electrify door for 30 seconds + electrify(30, usr, TRUE) + if("shock-perm") + if(wires.is_cut(WIRE_ELECTRIFY)) + to_chat(usr, "The electrification wire is cut - Cannot electrify the door.") + . = FALSE + else + electrify(-1, usr, TRUE) + if("idscan-toggle") + if(wires.is_cut(WIRE_IDSCAN)) + to_chat(usr, "The IdScan wire has been cut - IdScan feature permanently disabled.") + . = FALSE + else if(aiDisabledIdScanner) + aiDisabledIdScanner = FALSE + to_chat(usr, "IdScan feature has been enabled.") + else + aiDisabledIdScanner = TRUE + to_chat(usr, "IdScan feature has been disabled.") + if("emergency-toggle") + toggle_emergency_status(usr) + if("bolt-toggle") + toggle_bolt(usr) + if("light-toggle") + toggle_light(usr) + if("safe-toggle") + if(wires.is_cut(WIRE_SAFETY)) + to_chat(usr, "The safety wire is cut - Cannot secure the door.") + else if(safe) + safe = 0 + to_chat(usr, "The door safeties have been disabled.") + else + safe = 1 + to_chat(usr, "The door safeties have been enabled.") + if("speed-toggle") + if(wires.is_cut(WIRE_SPEED)) + to_chat(usr, "The timing wire is cut - Cannot alter timing.") + else if(normalspeed) + normalspeed = 0 + else + normalspeed = 1 + if("open-close") + open_close(usr) + else + . = FALSE + +/obj/machinery/door/airlock/proc/open_close(mob/user) + if(welded) + to_chat(user, "The airlock has been welded shut!") + return FALSE + else if(locked) + to_chat(user, "The door bolts are down!") + return FALSE + else if(density) + return open() + else + return close() + +/obj/machinery/door/airlock/proc/toggle_light(mob/user) + if(wires.is_cut(WIRE_BOLT_LIGHT)) + to_chat(user, "The bolt lights wire has been cut - The door bolt lights are permanently disabled.") + else if(lights) + lights = FALSE + to_chat(user, "The door bolt lights have been disabled.") + else if(!lights) + lights = TRUE + to_chat(user, "The door bolt lights have been enabled.") + update_icon() + +/obj/machinery/door/airlock/proc/toggle_bolt(mob/user) + if(wires.is_cut(WIRE_DOOR_BOLTS)) + to_chat(user, "The door bolt control wire has been cut - Door bolts permanently dropped.") + else if(lock()) + to_chat(user, "The door bolts have been dropped.") + else if(unlock()) + to_chat(user, "The door bolts have been raised.") + +/obj/machinery/door/airlock/proc/toggle_emergency_status(mob/user) + emergency = !emergency + if(emergency) + to_chat(user, "Emergency access has been enabled.") + else + to_chat(user, "Emergency access has been disabled.") + update_icon() /obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params) add_fingerprint(user) @@ -1064,7 +1094,7 @@ About the new airlock wires panel: return return TRUE -/obj/machinery/door/airlock/try_to_crowbar(mob/living/user, obj/item/I) //*scream +/obj/machinery/door/airlock/try_to_crowbar(mob/living/user, obj/item/I) if(operating) return if(istype(I, /obj/item/twohanded/fireaxe)) //let's make this more specific //FUCK YOU @@ -1126,7 +1156,7 @@ About the new airlock wires panel: if(operating || welded || locked || emagged) return 0 if(!forced) - if(!arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR)) + if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR)) return 0 use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people if(forced) @@ -1163,7 +1193,7 @@ About the new airlock wires panel: if(!forced) //despite the name, this wire is for general door control. //Bolts are already covered by the check for locked, above - if(!arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_OPEN_DOOR)) + if(!arePowerSystemsOn() || wires.is_cut(WIRE_OPEN_DOOR)) return if(safe) for(var/turf/turf in locs) @@ -1219,7 +1249,7 @@ About the new airlock wires panel: return if(!forced) - if(operating || !arePowerSystemsOn() || isWireCut(AIRLOCK_WIRE_DOOR_BOLTS)) + if(operating || !arePowerSystemsOn() || wires.is_cut(WIRE_DOOR_BOLTS)) return locked = 0 @@ -1245,8 +1275,10 @@ About the new airlock wires panel: return 1 /obj/machinery/door/airlock/emp_act(severity) - ..() - if(prob(40/severity)) + . = ..() + if(prob(20 / severity)) + open() + if(prob(40 / severity)) var/duration = world.time + (30 / severity) SECONDS if(duration > electrified_until) electrify(duration) @@ -1314,7 +1346,7 @@ About the new airlock wires panel: stat |= BROKEN if(!panel_open) panel_open = TRUE - wires.CutAll() + wires.cut_all() update_icon() /obj/machinery/door/airlock/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) @@ -1407,6 +1439,12 @@ About the new airlock wires panel: A.name = name qdel(src) +/obj/machinery/door/airlock/proc/ai_control_callback() + if(aiControlDisabled == AICONTROLDISABLED_ON) + aiControlDisabled = AICONTROLDISABLED_OFF + else if(aiControlDisabled == AICONTROLDISABLED_BYPASS) + aiControlDisabled = AICONTROLDISABLED_PERMA + #undef AIRLOCK_CLOSED #undef AIRLOCK_CLOSING #undef AIRLOCK_OPEN @@ -1426,3 +1464,7 @@ About the new airlock wires panel: #undef AIRLOCK_INTEGRITY_MULTIPLIER #undef AIRLOCK_DAMAGE_DEFLECTION_N #undef AIRLOCK_DAMAGE_DEFLECTION_R + +#undef TGUI_GREEN +#undef TGUI_ORANGE +#undef TGUI_RED diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm index dd9581a5af3..1d194507357 100644 --- a/code/game/machinery/doors/airlock_types.dm +++ b/code/game/machinery/doors/airlock_types.dm @@ -365,14 +365,14 @@ assemblytype = /obj/structure/door_assembly/door_assembly_vault security_level = 6 hackProof = TRUE - aiControlDisabled = TRUE + aiControlDisabled = AICONTROLDISABLED_ON /obj/machinery/door/airlock/hatch/gamma name = "gamma level hatch" - hackProof = 1 - aiControlDisabled = 1 + hackProof = TRUE + aiControlDisabled = AICONTROLDISABLED_ON resistance_flags = FIRE_PROOF | ACID_PROOF - is_special = 1 + is_special = TRUE /obj/machinery/door/airlock/hatch/gamma/attackby(obj/C, mob/user, params) if(!issilicon(user)) @@ -432,8 +432,8 @@ /obj/machinery/door/airlock/highsecurity/red name = "secure armory airlock" - hackProof = 1 - aiControlDisabled = 1 + hackProof = TRUE + aiControlDisabled = AICONTROLDISABLED_ON /obj/machinery/door/airlock/highsecurity/red/attackby(obj/C, mob/user, params) if(!issilicon(user)) @@ -487,7 +487,7 @@ damage_deflection = 30 explosion_block = 3 hackProof = TRUE - aiControlDisabled = 1 + aiControlDisabled = AICONTROLDISABLED_ON normal_integrity = 700 security_level = 1 paintable = FALSE @@ -504,7 +504,7 @@ assemblytype = /obj/structure/door_assembly/door_assembly_cult damage_deflection = 10 hackProof = TRUE - aiControlDisabled = TRUE + aiControlDisabled = AICONTROLDISABLED_ON paintable = FALSE var/openingoverlaytype = /obj/effect/temp_visual/cult/door var/friendly = FALSE diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index 9970974e4ff..b2f061e1d67 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -36,9 +36,10 @@ var/crimes = CELL_NONE var/time = 0 var/officer = CELL_NONE - var/prisoner_name = "" - var/prisoner_charge = "" - var/prisoner_time = "" + var/prisoner_name + var/prisoner_charge + var/prisoner_time + var/prisoner_hasrecord = FALSE /obj/machinery/door_timer/New() GLOB.celltimers_list += src @@ -82,12 +83,17 @@ var/datum/data/record/R = find_security_record("name", occupant) - var/announcetext = "Detainee [occupant] ([prisoner_drank]) has been incarcerated for [seconds_to_time(timetoset / 10)] for the charges of: '[crimes]'. \ + var/timetext = seconds_to_time(timetoset / 10) + var/announcetext = "Detainee [occupant] ([prisoner_drank]) has been incarcerated for [timetext] for the crime of: '[crimes]'. \ Arresting Officer: [usr.name].[R ? "" : " Detainee record not found, manual record update required."]" Radio.autosay(announcetext, name, "Security", list(z)) - if(prisoner_trank != "unknown") - notify_dept_head(prisoner_trank, announcetext) + // Notify the actual criminal being brigged. This is a QOL thing to ensure they always know the charges against them. + // Announcing it on radio isn't enough, as they're unlikely to have sec radio. + notify_prisoner("You have been incarcerated for [timetext] for the crime of: '[crimes]'.") + + if(prisoner_trank != "unknown" && prisoner_trank != "Civilian") + SSjobs.notify_dept_head(prisoner_trank, announcetext) if(R) prisoner = R @@ -104,30 +110,12 @@ update_all_mob_security_hud() return 1 - -/obj/machinery/door_timer/proc/notify_dept_head(jobtitle, antext) - if(!jobtitle || !antext) - return - if(jobtitle == "Civilian") - // Don't notify the HoP about greytiding civilians - return - var/datum/job/brigged_job = SSjobs.GetJob(jobtitle) - if(!brigged_job) - return - if(!brigged_job.department_head[1]) - return - var/boss_title = brigged_job.department_head[1] - - var/obj/item/pda/target_pda - for(var/obj/item/pda/check_pda in GLOB.PDAs) - if(check_pda.ownrank == boss_title) - target_pda = check_pda - if(!target_pda) - return - var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger) - if(PM && PM.can_receive()) - PM.notify("Message from Brig Timer (Automated), \"[antext]\" (Unable to Reply)") - +/obj/machinery/door_timer/proc/notify_prisoner(notifytext) + for(var/mob/living/carbon/human/H in range(4, get_turf(src))) + if(occupant == H.name) + to_chat(H, "[src] beeps, \"[notifytext]\"") + return + atom_say("[src] beeps, \"[occupant]: [notifytext]\"") /obj/machinery/door_timer/Initialize() ..() @@ -238,6 +226,7 @@ occupant = CELL_NONE crimes = CELL_NONE time = 0 + timetoset = 0 officer = CELL_NONE releasetime = 0 printed = 0 @@ -249,8 +238,7 @@ for(var/obj/machinery/door/window/brigdoor/door in targets) if(!door.density) continue - spawn(0) - door.open() + INVOKE_ASYNC(door, /obj/machinery/door/window/brigdoor.proc/open) for(var/obj/structure/closet/secure_closet/brig/C in targets) if(C.broken) @@ -286,13 +274,11 @@ return -//Allows AIs to use door_timer, see human attack_hand function below /obj/machinery/door_timer/attack_ai(mob/user) - attack_hand(user) - ui_interact(user) + tgui_interact(user) /obj/machinery/door_timer/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) //Allows humans to use door_timer //Opens dialog window when someone clicks on door timer @@ -301,71 +287,109 @@ /obj/machinery/door_timer/attack_hand(mob/user) if(..()) return - ui_interact(user) + tgui_interact(user) -/obj/machinery/door_timer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/door_timer/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "brig_timer.tmpl", "Brig Timer", 500, 400) + ui = new(user, src, ui_key, "BrigTimer", name, 500, 450, master_ui, state) ui.open() - ui.set_auto_update(TRUE) -/obj/machinery/door_timer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/door_timer/tgui_static_data(mob/user) + var/list/data = list() + data["spns"] = list() + for(var/mob/living/carbon/human/H in range(4, get_turf(src))) + if(H.handcuffed) + data["spns"] += H.name + return data + +/obj/machinery/door_timer/tgui_data(mob/user) + var/list/data = list() data["cell_id"] = name data["occupant"] = occupant data["crimes"] = crimes data["brigged_by"] = officer - data["time_set"] = seconds_to_clock(time / 10) + data["time_set"] = seconds_to_clock(timetoset / 10) data["time_left"] = seconds_to_clock(timeleft()) data["timing"] = timing data["isAllowed"] = allowed(user) data["prisoner_name"] = prisoner_name data["prisoner_charge"] = prisoner_charge data["prisoner_time"] = prisoner_time - + data["prisoner_hasrec"] = prisoner_hasrecord return data -/obj/machinery/door_timer/Topic(href, href_list) - if(!allowed(usr) && !usr.can_admin_interact()) - return 1 +/obj/machinery/door_timer/allowed(mob/user) + if(user.can_admin_interact()) + return TRUE + return ..() - if(href_list["flash"]) - for(var/obj/machinery/flasher/F in targets) - if(F.last_flash && (F.last_flash + 150) > world.time) - to_chat(usr, "Flash still charging.") +/obj/machinery/door_timer/tgui_act(action, params) + if(..()) + return + if(!allowed(usr)) + to_chat(usr, "Access denied.") + return + . = TRUE + switch(action) + if("prisoner_name") + if(params["prisoner_name"]) + prisoner_name = params["prisoner_name"] else - F.flash() - - if(href_list["release"]) - if(timing) - timer_end() - Radio.autosay("Timer stopped manually from cell control.", name, "Security", list(z)) - ui_interact(usr) - - if(href_list["prisoner_name"]) - prisoner_name = input("Prisoner Name:", name, prisoner_name) as text|null - - if(href_list["prisoner_charge"]) - prisoner_charge = input("Prisoner Charge:", name, prisoner_charge) as text|null - - if(href_list["prisoner_time"]) - prisoner_time = input("Prisoner Time (in minutes):", name, prisoner_time) as num|null - prisoner_time = min(max(round(prisoner_time), 0), 60) - - if(href_list["set_timer"]) - if(!prisoner_name || !prisoner_charge || !prisoner_time) - return - timeset(prisoner_time * 60) - occupant = prisoner_name - crimes = prisoner_charge - prisoner_name = "" - prisoner_charge = "" - prisoner_time = "" - timing = TRUE - timer_start() - ui_interact(usr) - update_icon() + prisoner_name = input("Prisoner Name:", name, prisoner_name) as text|null + if(prisoner_name) + var/datum/data/record/R = find_security_record("name", prisoner_name) + if(istype(R)) + prisoner_hasrecord = TRUE + else + prisoner_hasrecord = FALSE + if("prisoner_charge") + prisoner_charge = input("Prisoner Charge:", name, prisoner_charge) as text|null + if("prisoner_time") + prisoner_time = input("Prisoner Time (in minutes):", name, prisoner_time) as num|null + prisoner_time = min(max(round(prisoner_time), 0), 60) + if("start") + if(!prisoner_name || !prisoner_charge || !prisoner_time) + return FALSE + timeset(prisoner_time * 60) + occupant = prisoner_name + crimes = prisoner_charge + prisoner_name = null + prisoner_charge = null + prisoner_time = null + timing = TRUE + timer_start() + update_icon() + if("restart_timer") + if(timing) + var/reset_reason = sanitize(copytext(input(usr, "Reason for resetting timer:", name, "") as text|null, 1, MAX_MESSAGE_LEN)) + if(!reset_reason) + to_chat(usr, "Cancelled reset: reason field is required.") + return FALSE + releasetime = world.timeofday + timetoset + var/resettext = isobserver(usr) ? "for: [reset_reason]." : "by [usr.name] for: [reset_reason]." + Radio.autosay("Prisoner [occupant] had their timer reset [resettext]", name, "Security", list(z)) + notify_prisoner("Your brig timer has been reset for: '[reset_reason]'.") + var/datum/data/record/R = find_security_record("name", occupant) + if(istype(R)) + R.fields["comments"] += "Autogenerated by [name] on [GLOB.current_date_string] [station_time_timestamp()]
    Timer reset [resettext]" + else + . = FALSE + if("stop") + if(timing) + timer_end() + var/stoptext = isobserver(usr) ? "from cell control." : "by [usr.name]." + Radio.autosay("Timer stopped manually [stoptext]", name, "Security", list(z)) + else + . = FALSE + if("flash") + for(var/obj/machinery/flasher/F in targets) + if(F.last_flash && (F.last_flash + 150) > world.time) + to_chat(usr, "Flash still charging.") + else + F.flash() + else + . = FALSE //icon update function diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 2d6fac3bb81..a86ea7d4a6b 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -20,7 +20,7 @@ var/locked = FALSE //whether the door is bolted or not. var/glass = FALSE var/welded = FALSE - var/normalspeed = 1 + var/normalspeed = TRUE var/auto_close_time = 150 var/auto_close_time_dangerous = 15 var/assemblytype //the type of door frame to drop during deconstruction @@ -234,12 +234,6 @@ emagged = 1 return 1 -/obj/machinery/door/emp_act(severity) - if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) ) - spawn(0) - open() - ..() - /obj/machinery/door/update_icon() if(density) icon_state = "door1" diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 37dbdb6c9c0..c509675868b 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -28,6 +28,11 @@ var/nextstate = null var/boltslocked = TRUE var/active_alarm = FALSE + var/list/affecting_areas + +/obj/machinery/door/firedoor/Initialize(mapload) + . = ..() + CalculateAffectingAreas() /obj/machinery/door/firedoor/examine(mob/user) . = ..() @@ -40,11 +45,31 @@ else . += "The bolt locks have been unscrewed, but the bolts themselves are still wrenched to the floor." +/obj/machinery/door/firedoor/proc/CalculateAffectingAreas() + remove_from_areas() + affecting_areas = get_adjacent_open_areas(src) | get_area(src) + for(var/I in affecting_areas) + var/area/A = I + LAZYADD(A.firedoors, src) + /obj/machinery/door/firedoor/closed icon_state = "door_closed" opacity = TRUE density = TRUE +//see also turf/AfterChange for adjacency shennanigans + +/obj/machinery/door/firedoor/proc/remove_from_areas() + if(affecting_areas) + for(var/I in affecting_areas) + var/area/A = I + LAZYREMOVE(A.firedoors, src) + +/obj/machinery/door/firedoor/Destroy() + remove_from_areas() + affecting_areas.Cut() + return ..() + /obj/machinery/door/firedoor/Bumped(atom/AM) if(panel_open || operating) return diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index e423f92683c..f27d4bf34a8 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -57,6 +57,11 @@ if(emagged) . += "Its access panel is smoking slightly." +/obj/machinery/door/window/emp_act(severity) + . = ..() + if(prob(20 / severity)) + open() + /obj/machinery/door/window/proc/open_and_close() open() if(check_access(null)) diff --git a/code/game/machinery/embedded_controller/airlock_program.dm b/code/game/machinery/embedded_controller/airlock_program.dm index 8ad85a608f0..d9500e30808 100644 --- a/code/game/machinery/embedded_controller/airlock_program.dm +++ b/code/game/machinery/embedded_controller/airlock_program.dm @@ -305,7 +305,7 @@ signalDoor(tag_exterior_door, command) signalDoor(tag_interior_door, command) -datum/computer/file/embedded_program/airlock/proc/signal_mech_sensor(var/command, var/sensor) +/datum/computer/file/embedded_program/airlock/proc/signal_mech_sensor(var/command, var/sensor) var/datum/signal/signal = new signal.data["tag"] = sensor signal.data["command"] = command diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index f12361f1664..137dcb50110 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -25,7 +25,11 @@ FIRE ALARM active_power_usage = 6 power_channel = ENVIRON resistance_flags = FIRE_PROOF - var/last_process = 0 + + light_power = 0 + light_range = 7 + light_color = "#ff3232" + var/wiresexposed = 0 var/buildstage = 2 // 2 = complete, 1 = no wires, 0 = circuit gone @@ -79,7 +83,8 @@ FIRE ALARM return attack_hand(user) /obj/machinery/firealarm/attack_ghost(mob/user) - ui_interact(user) + if(user.can_admin_interact()) + toggle_alarm(user) /obj/machinery/firealarm/emp_act(severity) if(prob(50/severity)) @@ -191,6 +196,7 @@ FIRE ALARM /obj/machinery/firealarm/obj_break(damage_flag) if(!(stat & BROKEN) && !(flags & NODECONSTRUCT) && buildstage != 0) //can't break the electronics if there isn't any inside. stat |= BROKEN + LAZYREMOVE(myArea.firealarms, src) update_icon() /obj/machinery/firealarm/deconstruct(disassembled = TRUE) @@ -203,20 +209,13 @@ FIRE ALARM new /obj/item/stack/cable_coil(loc, 3) qdel(src) -/obj/machinery/firealarm/process()//Note: this processing was mostly phased out due to other code, and only runs when needed - if(stat & (NOPOWER|BROKEN)) - return - - if(timing) - if(time > 0) - time = time - ((world.timeofday - last_process)/10) - else - alarm() - time = 0 - timing = 0 - STOP_PROCESSING(SSobj, src) - updateDialog() - last_process = world.timeofday +/obj/machinery/firealarm/proc/update_fire_light(fire) + if(fire == !!light_power) + return // do nothing if we're already active + if(fire) + set_light(l_power = 0.8) + else + set_light(l_power = 0) /obj/machinery/firealarm/power_change() if(powered(ENVIRON)) @@ -234,78 +233,33 @@ FIRE ALARM if(user.incapacitated()) return 1 - ui_interact(user) + toggle_alarm(user) -/obj/machinery/firealarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "firealarm.tmpl", name, 400, 400, state = state) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/firealarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/firealarm/proc/toggle_alarm(mob/user) var/area/A = get_area(src) - data["fire"] = A.fire - data["timing"] = timing + if(istype(A)) + add_fingerprint(user) + if(A.fire) + reset() + else + alarm() - data["sec_level"] = get_security_level() - - var/second = round(time % 60) - var/minute = round(time / 60) - - data["time_left"] = "[minute ? "[minute]:" : ""][add_zero(num2text(second), 2)]" - return data - -/obj/machinery/firealarm/Topic(href, href_list) - if(..()) - return 1 - - if(buildstage != 2) - return 1 - - add_fingerprint(usr) - - if(href_list["reset"]) - reset() - else if(href_list["alarm"]) - alarm() - else if(href_list["time"]) - var/oldTiming = timing - timing = text2num(href_list["time"]) - last_process = world.timeofday - if(oldTiming != timing) - if(timing) - START_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) - else if(href_list["tp"]) - var/tp = text2num(href_list["tp"]) - time += tp - time = min(max(round(time), 0), 120) +/obj/machinery/firealarm/examine(mob/user) + . = ..() + . += "It shows the alert level as: [capitalize(get_security_level())]." /obj/machinery/firealarm/proc/reset() - if(!working) + if(!working || !report_fire_alarms) return var/area/A = get_area(src) - A.fire_reset() + A.firereset(src) - for(var/obj/machinery/firealarm/FA in A) - if(is_station_contact(z) && FA.report_fire_alarms) - SSalarms.fire_alarm.clearAlarm(loc, FA) - -/obj/machinery/firealarm/proc/alarm(var/duration = 0) - if(!working) +/obj/machinery/firealarm/proc/alarm() + if(!working || !report_fire_alarms) return - var/area/A = get_area(src) - for(var/obj/machinery/firealarm/FA in A) - if(is_station_contact(z) && FA.report_fire_alarms) - SSalarms.fire_alarm.triggerAlarm(loc, FA, duration) - else - A.fire_alert() // Manually trigger alarms if the alarm isn't reported - + A.firealert(src) // Manually trigger alarms if the alarm isn't reported update_icon() /obj/machinery/firealarm/New(location, direction, building) @@ -323,8 +277,14 @@ FIRE ALARM else overlays += image('icons/obj/monitors.dmi', "overlay_green") + myArea = get_area(src) + LAZYADD(myArea.firealarms, src) update_icon() +/obj/machinery/firealarm/Destroy() + LAZYREMOVE(myArea.firealarms, src) + return ..() + /* FIRE ALARM CIRCUIT Just a object used in constructing fire alarms diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 37766db58fb..369d43e5d94 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -25,20 +25,17 @@ base_state = "pflash" density = 1 -/* -/obj/machinery/flasher/New() - sleep(4) //<--- What the fuck are you doing? D= - sd_set_light(2) -*/ +/obj/machinery/flasher/portable/ComponentInitialize() + . = ..() + AddComponent(/datum/component/proximity_monitor) + /obj/machinery/flasher/power_change() if( powered() ) stat &= ~NOPOWER icon_state = "[base_state]1" -// sd_set_light(2) else stat |= ~NOPOWER icon_state = "[base_state]1-p" -// sd_set_light(0) //Let the AI trigger them directly. /obj/machinery/flasher/attack_ai(mob/user) @@ -81,7 +78,7 @@ flash() ..(severity) -/obj/machinery/flasher/portable/HasProximity(atom/movable/AM as mob|obj) +/obj/machinery/flasher/portable/HasProximity(atom/movable/AM) if((disable) || (last_flash && world.time < last_flash + 150)) return @@ -109,7 +106,7 @@ if(anchored) WRENCH_ANCHOR_MESSAGE overlays.Cut() - else if(anchored) + else WRENCH_UNANCHOR_MESSAGE overlays += "[base_state]-s" diff --git a/code/game/machinery/floodlight.dm b/code/game/machinery/floodlight.dm index 0cc6dda7098..b4babfbabfd 100644 --- a/code/game/machinery/floodlight.dm +++ b/code/game/machinery/floodlight.dm @@ -146,3 +146,4 @@ /obj/machinery/floodlight/extinguish_light() on = 0 set_light(0) + update_icon() diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index f02e037e8c5..ad164fe720a 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -310,6 +310,12 @@ Class Procs: return ..() +/obj/machinery/tgui_status(mob/user, datum/tgui_state/state) + if(!interact_offline && (stat & (NOPOWER|BROKEN))) + return STATUS_CLOSE + + return ..() + /obj/machinery/CouldUseTopic(var/mob/user) ..() user.set_machine(src) diff --git a/code/game/machinery/overview.dm b/code/game/machinery/overview.dm index 7165cceb1ff..7d8910d6ef5 100644 --- a/code/game/machinery/overview.dm +++ b/code/game/machinery/overview.dm @@ -342,13 +342,13 @@ return -proc/getr(col) +/proc/getr(col) return hex2num( copytext(col, 2,4)) -proc/getg(col) +/proc/getg(col) return hex2num( copytext(col, 4,6)) -proc/getb(col) +/proc/getb(col) return hex2num( copytext(col, 6)) diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index 42452a9f8e2..a8c9710ff6c 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -106,10 +106,6 @@ else if(istype(make_from, /obj/machinery/atmospherics/unary/passive_vent)) src.pipe_type = PIPE_PASV_VENT - else if(istype(make_from, /obj/machinery/atmospherics/omni/mixer)) - src.pipe_type = PIPE_OMNI_MIXER - else if(istype(make_from, /obj/machinery/atmospherics/omni/filter)) - src.pipe_type = PIPE_OMNI_FILTER else if(istype(make_from, /obj/machinery/atmospherics/binary/circulator)) src.pipe_type = PIPE_CIRCULATOR @@ -259,7 +255,7 @@ return dir //dir|acw if(PIPE_CONNECTOR, PIPE_UVENT, PIPE_PASV_VENT, PIPE_SCRUBBER, PIPE_HEAT_EXCHANGE, PIPE_INJECTOR) return dir|flip - if(PIPE_MANIFOLD4W, PIPE_SUPPLY_MANIFOLD4W, PIPE_SCRUBBERS_MANIFOLD4W, PIPE_OMNI_MIXER, PIPE_OMNI_FILTER) + if(PIPE_MANIFOLD4W, PIPE_SUPPLY_MANIFOLD4W, PIPE_SCRUBBERS_MANIFOLD4W) return dir|flip|cw|acw if(PIPE_MANIFOLD, PIPE_SUPPLY_MANIFOLD, PIPE_SCRUBBERS_MANIFOLD) return flip|cw|acw @@ -317,7 +313,7 @@ dir = 1 else if(dir==8) dir = 4 - else if(pipe_type in list(PIPE_MANIFOLD4W, PIPE_SUPPLY_MANIFOLD4W, PIPE_SCRUBBERS_MANIFOLD4W, PIPE_OMNI_MIXER, PIPE_OMNI_FILTER)) + else if(pipe_type in list(PIPE_MANIFOLD4W, PIPE_SUPPLY_MANIFOLD4W, PIPE_SCRUBBERS_MANIFOLD4W)) dir = 2 /obj/item/pipe/attack_self(mob/user as mob) @@ -499,14 +495,6 @@ P.name = pipename P.on_construction(dir, pipe_dir, color) - if(PIPE_OMNI_MIXER) - var/obj/machinery/atmospherics/omni/mixer/P = new(loc) - P.on_construction(dir, pipe_dir, color) - - if(PIPE_OMNI_FILTER) - var/obj/machinery/atmospherics/omni/filter/P = new(loc) - P.on_construction(dir, pipe_dir, color) - user.visible_message( \ "[user] fastens the [src].", \ "You have fastened the [src].", \ diff --git a/code/game/machinery/poolcontroller.dm b/code/game/machinery/poolcontroller.dm index b910b1272ff..777aa155a62 100644 --- a/code/game/machinery/poolcontroller.dm +++ b/code/game/machinery/poolcontroller.dm @@ -15,7 +15,6 @@ var/decalinpool = list() // List containing all of the cleanable decals in pool var/linked_area = null var/temperature = NORMAL //The temperature of the pool, starts off on normal, which has no effects. - var/temperaturecolor = "" //used for nanoUI fancyness var/srange = 5 //The range of the search for pool turfs, change this for bigger or smaller pools. var/list/linkedmist = list() //Used to keep track of created mist var/deep_water = FALSE //set to 1 to drown even standing people @@ -67,8 +66,8 @@ else to_chat(user, "Nothing happens.")//If not emagged, don't do anything, and don't tell the user that it can be emagged. -/obj/machinery/poolcontroller/attack_hand(mob/user as mob) - ui_interact(user) +/obj/machinery/poolcontroller/attack_hand(mob/user) + tgui_interact(user) /obj/machinery/poolcontroller/process() processMob() //Call the mob affecting proc @@ -151,64 +150,74 @@ linkedmist.Cut() -/obj/machinery/poolcontroller/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/poolcontroller/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "poolcontroller.tmpl", "Pool Controller Interface", 520, 410) + ui = new(user, src, ui_key, "PoolController", "Pool Controller Interface", 520, 410) ui.open() -/obj/machinery/poolcontroller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - var/currenttemp - switch(temperature) //So we can output nice things like "Cool" to nanoUI +/obj/machinery/poolcontroller/proc/temp_to_str(temp) + switch(temp) //So we can output nice things like "Cool" to tgUI if(FRIGID) - currenttemp = "frigid" + return "frigid" if(COOL) - currenttemp = "cool" + return "cool" if(NORMAL) - currenttemp = "normal" + return "normal" if(WARM) - currenttemp = "warm" + return "warm" if(SCALDING) - currenttemp = "scalding" - data["currentTemp"] = currenttemp + return "scalding" + +/obj/machinery/poolcontroller/proc/set_temp(val) + if (val != WARM && val != NORMAL && val != COOL && !(emagged && (val == SCALDING || val == FRIGID))) + return + + if(val == SCALDING) + miston() + else + mistoff() + + temperature = val + + +/obj/machinery/poolcontroller/proc/str_to_temp(str) + switch(str) + if("frigid") + return FRIGID + if("cool") + return COOL + if("normal") + return NORMAL + if("warm") + return WARM + if("scalding") + return SCALDING + +/obj/machinery/poolcontroller/proc/set_temp_str(target) + var/temp = str_to_temp(target) + if(temp) + set_temp(temp) + + +/obj/machinery/poolcontroller/tgui_data(mob/user) + var/list/data = list() + data["currentTemp"] = temp_to_str(temperature) data["emagged"] = emagged - data["TempColor"] = temperaturecolor return data -/obj/machinery/poolcontroller/Topic(href, href_list) +/obj/machinery/poolcontroller/tgui_act(action, list/params) if(..()) - return 1 + return - switch(href_list["temp"]) - if("Scalding") - if(!emagged) - return 0 - temperature = SCALDING - temperaturecolor = "#FF0000" - miston() - if("Frigid") - if(!emagged) - return 0 - temperature = FRIGID - temperaturecolor = "#00CCCC" - mistoff() - if("Warm") - temperature = WARM - temperaturecolor = "#990000" - mistoff() - if("Cool") - temperature = COOL - temperaturecolor = "#009999" - mistoff() - if("Normal") - temperature = NORMAL - temperaturecolor = "" - mistoff() + switch(action) + if("setTemp") + set_temp_str(params["temp"]) + return TRUE - return 1 + return FALSE #undef FRIGID #undef COOL diff --git a/code/game/machinery/portable_tag_turret.dm b/code/game/machinery/portable_tag_turret.dm index 12bae1bbf5a..4b22315f387 100644 --- a/code/game/machinery/portable_tag_turret.dm +++ b/code/game/machinery/portable_tag_turret.dm @@ -6,6 +6,14 @@ // Reasonable defaults, in case someone manually spawns us var/lasercolor = "r" //Something to do with lasertag turrets, blame Sieve for not adding a comment. installation = /obj/item/gun/energy/laser/tag/red + targetting_is_configurable = FALSE + lethal_is_configurable = FALSE + shot_delay = 30 + iconholder = 1 + has_cover = FALSE + always_up = TRUE + raised = TRUE + req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE) /obj/machinery/porta_turret/tag/red @@ -18,43 +26,15 @@ icon_state = "[lasercolor]grey_target_prism" /obj/machinery/porta_turret/tag/weapon_setup(var/obj/item/gun/energy/E) - switch(E.type) - if(/obj/item/gun/energy/laser/tag/blue) - eprojectile = /obj/item/gun/energy/laser/tag/blue - lasercolor = "b" - req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE) - check_arrest = 0 - check_records = 0 - check_weapons = 1 - check_access = 0 - check_anomalies = 0 - shot_delay = 30 + return - if(/obj/item/gun/energy/laser/tag/red) - eprojectile = /obj/item/gun/energy/laser/tag/red - lasercolor = "r" - req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE) - check_arrest = 0 - check_records = 0 - check_weapons = 1 - check_access = 0 - check_anomalies = 0 - shot_delay = 30 - iconholder = 1 - -/obj/machinery/porta_turret/tag/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 300) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/porta_turret/tag/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["access"] = !isLocked(user) - data["locked"] = locked - data["enabled"] = enabled - data["is_lethal"] = 0 +/obj/machinery/porta_turret/tag/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, // is turret turned on? + "lethal" = FALSE, + "lethal_is_configurable" = lethal_is_configurable + ) return data /obj/machinery/porta_turret/tag/update_icon() diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index 79f1eb83e23..346008fce3f 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -7,18 +7,18 @@ name = "turret" icon = 'icons/obj/turrets.dmi' icon_state = "turretCover" - anchored = 1 - density = 0 + anchored = TRUE + density = FALSE use_power = IDLE_POWER_USE //this turret uses and requires power idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power power_channel = EQUIP //drains power from the EQUIPMENT channel armor = list(melee = 50, bullet = 30, laser = 30, energy = 30, bomb = 30, bio = 0, rad = 0, fire = 90, acid = 90) - var/raised = 0 //if the turret cover is "open" and the turret is raised - var/raising= 0 //if the turret is currently opening or closing its cover + var/raised = FALSE //if the turret cover is "open" and the turret is raised + var/raising= FALSE //if the turret is currently opening or closing its cover var/health = 80 //the turret's health - var/locked = 1 //if the turret's behaviour control access is locked - var/controllock = 0 //if the turret responds to control panels + var/locked = TRUE //if the turret's behaviour control access is locked + var/controllock = FALSE //if the turret responds to control panels. TRUE = does NOT respond var/installation = /obj/item/gun/energy/gun/turret //the type of weapon installed var/gun_charge = 0 //the charge of the gun inserted @@ -31,68 +31,49 @@ var/last_fired = 0 //1: if the turret is cooling down from a shot, 0: turret is ready to fire var/shot_delay = 15 //1.5 seconds between each shot - var/check_arrest = 1 //checks if the perp is set to arrest - var/check_records = 1 //checks if a security record exists at all - var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have - var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements - var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos) - var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg - var/ailock = 0 // AI cannot use this + var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI + var/check_arrest = TRUE //checks if the perp is set to arrest + var/check_records = TRUE //checks if a security record exists at all + var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have + var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements + var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos) + var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg + var/check_borgs = FALSE //if TRUE, target all cyborgs. + var/ailock = FALSE // if TRUE, AI cannot use this - var/attacked = 0 //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!) + var/attacked = FALSE //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!) - var/enabled = 1 //determines if the turret is on - var/lethal = 0 //whether in lethal or stun mode - var/disabled = 0 + var/enabled = TRUE //determines if the turret is on + var/lethal = FALSE //whether in lethal or stun mode + var/lethal_is_configurable = TRUE // if false, its lethal setting cannot be changed + var/disabled = FALSE var/shot_sound //what sound should play when the turret fires var/eshot_sound //what sound should play when the emagged turret fires var/datum/effect_system/spark_spread/spark_system //the spark system, used for generating... sparks? - var/wrenching = 0 + var/wrenching = FALSE var/last_target //last target fired at, prevents turrets from erratically firing at all valid targets in range - var/screen = 0 // Screen 0: main control, screen 1: access levels - var/one_access = 0 // Determines if access control is set to req_one_access or req_access + var/one_access = FALSE // Determines if access control is set to req_one_access or req_access + var/region_min = REGION_GENERAL + var/region_max = REGION_COMMAND - var/syndicate = 0 //is the turret a syndicate turret? + var/syndicate = FALSE //is the turret a syndicate turret? var/faction = "" - var/emp_vulnerable = 1 // Can be empd + var/emp_vulnerable = TRUE // Can be empd var/scan_range = 7 - var/always_up = 0 //Will stay active - var/has_cover = 1 //Hides the cover + var/always_up = FALSE //Will stay active + var/has_cover = TRUE //Hides the cover -/obj/machinery/porta_turret/centcom - name = "Centcom Turret" - enabled = 0 - ailock = 1 - check_synth = 0 - check_access = 1 - check_arrest = 1 - check_records = 1 - check_weapons = 1 - check_anomalies = 1 - -/obj/machinery/porta_turret/centcom/pulse - name = "Pulse Turret" - health = 200 - enabled = 1 - lethal = 1 - req_access = list(ACCESS_CENT_COMMANDER) - installation = /obj/item/gun/energy/pulse/turret - -/obj/machinery/porta_turret/stationary - ailock = 1 - lethal = 1 - installation = /obj/item/gun/energy/laser /obj/machinery/porta_turret/Initialize(mapload) . = ..() if(req_access && req_access.len) req_access.Cut() req_one_access = list(ACCESS_SECURITY, ACCESS_HEADS) - one_access = 1 + one_access = TRUE //Sets up a spark system spark_system = new /datum/effect_system/spark_spread @@ -110,7 +91,7 @@ if(req_one_access && req_one_access.len) req_one_access.Cut() req_access = list(ACCESS_CENT_SPECOPS) - one_access = 0 + one_access = FALSE /obj/machinery/porta_turret/proc/setup() var/obj/item/gun/energy/E = new installation //All energy-based weapons are applicable @@ -185,152 +166,151 @@ GLOBAL_LIST_EMPTY(turret_icons) else icon_state = "turretCover" -/obj/machinery/porta_turret/proc/isLocked(mob/user) - if(ailock && (isrobot(user) || isAI(user))) - to_chat(user, "There seems to be a firewall preventing you from accessing this device.") - return 1 - - if(locked && !(isrobot(user) || isAI(user) || isobserver(user))) - to_chat(user, "Access denied.") - return 1 - - return 0 - -/obj/machinery/porta_turret/attack_ai(mob/user) - if(isLocked(user)) - return - - ui_interact(user) - -/obj/machinery/porta_turret/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/porta_turret/attack_hand(mob/user) - if(isLocked(user)) - return - - ui_interact(user) - -/obj/machinery/porta_turret/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 320) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/porta_turret/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["access"] = !isLocked(user) - data["screen"] = screen - data["locked"] = locked - data["enabled"] = enabled - data["lethal_control"] = !syndicate ? 1 : 0 - data["lethal"] = lethal - - if(data["access"] && !syndicate) - var/settings[0] - settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth) - settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons) - settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records) - settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest) - settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access) - settings[++settings.len] = list("category" = "Check Misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies) - data["settings"] = settings - - if(!syndicate) - data["one_access"] = one_access - var/accesses[0] - var/list/access_list = get_all_accesses() - for(var/access in access_list) - var/name = get_access_desc(access) - var/active - if(one_access) - active = (access in req_one_access) - else - active = (access in req_access) - accesses[++accesses.len] = list("name" = name, "active" = active, "number" = access) - data["accesses"] = accesses - return data - /obj/machinery/porta_turret/proc/HasController() var/area/A = get_area(src) return A && A.turret_controls.len > 0 -/obj/machinery/porta_turret/CanUseTopic(var/mob/user) +/obj/machinery/porta_turret/proc/access_is_configurable() + return targetting_is_configurable && !HasController() + +/obj/machinery/porta_turret/proc/isLocked(mob/user) if(HasController()) - to_chat(user, "Turrets can only be controlled using the assigned turret controller.") - return STATUS_CLOSE + return TRUE + if(isrobot(user) || isAI(user)) + if(ailock) + to_chat(user, "There seems to be a firewall preventing you from accessing this device.") + return TRUE + else + return FALSE + if(isobserver(user)) + if(user.can_admin_interact()) + return FALSE + else + return TRUE + if(locked) + return TRUE + return FALSE - if(isLocked(user)) - return STATUS_CLOSE +/obj/machinery/porta_turret/attack_ai(mob/user) + tgui_interact(user) - if(!anchored) - to_chat(usr, "\The [src] has to be secured first!") - return STATUS_CLOSE +/obj/machinery/porta_turret/attack_ghost(mob/user) + tgui_interact(user) - return ..() +/obj/machinery/porta_turret/attack_hand(mob/user) + tgui_interact(user) -/obj/machinery/porta_turret/Topic(href, href_list, var/nowindow = 0) - if(..()) - return 1 - - if(href_list["command"] && href_list["value"]) - var/value = text2num(href_list["value"]) - if(href_list["command"] == "enable") - enabled = value - else if(syndicate) - return 1 - else if(href_list["command"] == "screen") - screen = value - else if(href_list["command"] == "lethal") - lethal = value - else if(href_list["command"] == "check_synth") - check_synth = value - else if(href_list["command"] == "check_weapons") - check_weapons = value - else if(href_list["command"] == "check_records") - check_records = value - else if(href_list["command"] == "check_arrest") - check_arrest = value - else if(href_list["command"] == "check_access") - check_access = value - else if(href_list["command"] == "check_anomalies") - check_anomalies = value - - if(!syndicate) - if(href_list["one_access"]) - toggle_one_access(href_list["one_access"]) - - if(href_list["access"]) - toggle_access(href_list["access"]) - - return 1 - -/obj/machinery/porta_turret/proc/toggle_one_access(var/access) - one_access = text2num(access) - - if(one_access == 1) - req_one_access = req_access.Copy() - req_access.Cut() - else if(one_access == 0) - req_access = req_one_access.Copy() - req_one_access.Cut() - -/obj/machinery/porta_turret/proc/toggle_access(var/access) - var/required = text2num(access) - if(!(required in get_all_accesses())) +/obj/machinery/porta_turret/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + if(HasController()) + to_chat(user, "[src] can only be controlled using the assigned turret controller.") return + if(!anchored) + to_chat(user, "[src] has to be secured first!") + return + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "PortableTurret", name, 500, access_is_configurable() ? 800 : 400) + ui.open() - if(one_access) - if((required in req_one_access)) - req_one_access -= required - else - req_one_access += required - else - if((required in req_access)) - req_access -= required - else - req_access += required +/obj/machinery/porta_turret/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, + "targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up + "lethal" = lethal, + "lethal_is_configurable" = lethal_is_configurable, + "check_weapons" = check_weapons, + "neutralize_noaccess" = check_access, + "one_access" = one_access, + "selectedAccess" = one_access ? req_one_access : req_access, + "access_is_configurable" = access_is_configurable(), + "neutralize_norecord" = check_records, + "neutralize_criminals" = check_arrest, + "neutralize_all" = check_synth, + "neutralize_unidentified" = check_anomalies, + "neutralize_cyborgs" = check_borgs + ) + return data + +/obj/machinery/porta_turret/tgui_static_data(mob/user) + var/list/data = list() + data["regions"] = get_accesslist_static_data(region_min, region_max) + return data + +/obj/machinery/porta_turret/tgui_act(action, params) + if (..()) + return + if(isLocked(usr)) + return + . = TRUE + switch(action) + if("power") + enabled = !enabled + if("lethal") + if(lethal_is_configurable) + lethal = !lethal + if(targetting_is_configurable) + switch(action) + if("authweapon") + check_weapons = !check_weapons + if("authaccess") + check_access = !check_access + if("authnorecord") + check_records = !check_records + if("autharrest") + check_arrest = !check_arrest + if("authxeno") + check_anomalies = !check_anomalies + if("authsynth") + check_synth = !check_synth + if("authborgs") + check_borgs = !check_borgs + if("set") + var/access = text2num(params["access"]) + if(one_access) + if(!(access in req_one_access)) + req_one_access += access + else + req_one_access -= access + else + if(!(access in req_access)) + req_access += access + else + req_access -= access + if(access_is_configurable()) + switch(action) + if("grant_region") + var/region = text2num(params["region"]) + if(isnull(region)) + return + if(one_access) + req_one_access |= get_region_accesses(region) + else + req_access |= get_region_accesses(region) + if("deny_region") + var/region = text2num(params["region"]) + if(isnull(region)) + return + if(one_access) + req_one_access -= get_region_accesses(region) + else + req_access -= get_region_accesses(region) + if("clear_all") + if(one_access) + req_one_access = list() + else + req_access = list() + if("grant_all") + if(one_access) + req_one_access = get_all_accesses() + else + req_access = get_all_accesses() + if("one_access") + if(one_access) + req_one_access = list() + else + req_access = list() + one_access = !one_access /obj/machinery/porta_turret/power_change() if(powered() || !use_power) @@ -379,23 +359,25 @@ GLOBAL_LIST_EMPTY(turret_icons) "You begin [anchored ? "un" : ""]securing the turret." \ ) - wrenching = 1 + wrenching = TRUE if(do_after(user, 50 * I.toolspeed, target = src)) //This code handles moving the turret around. After all, it's a portable turret! if(!anchored) playsound(loc, I.usesound, 100, 1) - anchored = 1 + anchored = TRUE update_icon() to_chat(user, "You secure the exterior bolts on the turret.") else if(anchored) playsound(loc, I.usesound, 100, 1) - anchored = 0 + anchored = FALSE to_chat(user, "You unsecure the exterior bolts on the turret.") update_icon() - wrenching = 0 + wrenching = FALSE else if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda)) - if(allowed(user)) + if(HasController()) + to_chat(user, "Turrets regulated by a nearby turret controller are not unlockable.") + else if(allowed(user)) locked = !locked to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].") updateUsrDialog() @@ -409,9 +391,9 @@ GLOBAL_LIST_EMPTY(turret_icons) playsound(src.loc, 'sound/weapons/smash.ogg', 60, 1) if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off if(!attacked && !emagged) - attacked = 1 + attacked = TRUE spawn(60) - attacked = 0 + attacked = FALSE ..() @@ -445,12 +427,12 @@ GLOBAL_LIST_EMPTY(turret_icons) if(user) to_chat(user, "You short out [src]'s threat assessment circuits.") visible_message("[src] hums oddly...") - emagged = 1 + emagged = TRUE iconholder = 1 - controllock = 1 - enabled = 0 //turns off the turret temporarily + controllock = TRUE + enabled = FALSE //turns off the turret temporarily sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit - enabled = 1 //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here + enabled = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here /obj/machinery/porta_turret/take_damage(force) if(!raised && !raising) @@ -470,9 +452,9 @@ GLOBAL_LIST_EMPTY(turret_icons) if(enabled) if(!attacked && !emagged) - attacked = 1 + attacked = TRUE spawn(60) - attacked = 0 + attacked = FALSE ..() @@ -489,12 +471,12 @@ GLOBAL_LIST_EMPTY(turret_icons) check_access = prob(20) // check_access is a pretty big deal, so it's least likely to get turned on check_anomalies = prob(50) if(prob(5)) - emagged = 1 + emagged = TRUE enabled=0 - spawn(rand(60,600)) + spawn(rand(60, 600)) if(!enabled) - enabled=1 + enabled = TRUE ..() @@ -585,8 +567,8 @@ GLOBAL_LIST_EMPTY(turret_icons) if(get_turf(L) == get_turf(src)) return TURRET_NOT_TARGET - if(!emagged && !syndicate && (issilicon(L) || isbot(L))) // Don't target silica - return TURRET_NOT_TARGET + if(!emagged && !syndicate && (issilicon(L) || isbot(L))) + return (check_borgs && isrobot(L)) ? TURRET_PRIORITY_TARGET : TURRET_NOT_TARGET if(L.stat && !emagged) //if the perp is dead/dying, no need to bother really return TURRET_NOT_TARGET //move onto next potential victim! @@ -643,7 +625,7 @@ GLOBAL_LIST_EMPTY(turret_icons) return if(stat & BROKEN) return - set_raised_raising(raised, 1) + set_raised_raising(raised, TRUE) playsound(get_turf(src), 'sound/effects/turret/open.wav', 60, 1) update_icon() @@ -653,7 +635,7 @@ GLOBAL_LIST_EMPTY(turret_icons) sleep(10) qdel(flick_holder) - set_raised_raising(1, 0) + set_raised_raising(TRUE, FALSE) update_icon() /obj/machinery/porta_turret/proc/popDown() //pops the turret down @@ -664,7 +646,7 @@ GLOBAL_LIST_EMPTY(turret_icons) return if(stat & BROKEN) return - set_raised_raising(raised, 1) + set_raised_raising(raised, TRUE) playsound(get_turf(src), 'sound/effects/turret/open.wav', 60, 1) update_icon() @@ -674,7 +656,7 @@ GLOBAL_LIST_EMPTY(turret_icons) sleep(10) qdel(flick_holder) - set_raised_raising(0, 0) + set_raised_raising(FALSE, FALSE) update_icon() /obj/machinery/porta_turret/on_assess_perp(mob/living/carbon/human/perp) @@ -739,6 +721,27 @@ GLOBAL_LIST_EMPTY(turret_icons) A.throw_at(target, scan_range, 1) return A +/obj/machinery/porta_turret/centcom + name = "Centcom Turret" + enabled = FALSE + ailock = TRUE + check_synth = FALSE + check_access = TRUE + check_arrest = TRUE + check_records = TRUE + check_weapons = TRUE + check_anomalies = TRUE + region_max = REGION_CENTCOMM // Non-turretcontrolled turrets at CC can have their access customized to check for CC accesses. + +/obj/machinery/porta_turret/centcom/pulse + name = "Pulse Turret" + health = 200 + enabled = TRUE + lethal = TRUE + lethal_is_configurable = FALSE + req_access = list(ACCESS_CENT_COMMANDER) + installation = /obj/item/gun/energy/pulse/turret + /datum/turret_checks var/enabled var/lethal @@ -748,6 +751,7 @@ GLOBAL_LIST_EMPTY(turret_icons) var/check_arrest var/check_weapons var/check_anomalies + var/check_borgs var/ailock /obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC) @@ -763,6 +767,7 @@ GLOBAL_LIST_EMPTY(turret_icons) check_arrest = TC.check_arrest check_weapons = TC.check_weapons check_anomalies = TC.check_anomalies + check_borgs = TC.check_borgs ailock = TC.ailock power_change() @@ -791,7 +796,7 @@ GLOBAL_LIST_EMPTY(turret_icons) if(istype(I, /obj/item/wrench) && !anchored) playsound(loc, I.usesound, 100, 1) to_chat(user, "You secure the external bolts.") - anchored = 1 + anchored = TRUE build_step = 1 return @@ -816,7 +821,7 @@ GLOBAL_LIST_EMPTY(turret_icons) else if(istype(I, /obj/item/wrench)) playsound(loc, I.usesound, 75, 1) to_chat(user, "You unfasten the external bolts.") - anchored = 0 + anchored = FALSE build_step = 0 return @@ -841,8 +846,10 @@ GLOBAL_LIST_EMPTY(turret_icons) gun_charge = E.cell.charge //the gun's charge is stored in gun_charge to_chat(user, "You add [I] to the turret.") - if(istype(installation, /obj/item/gun/energy/laser/tag/blue) || istype(installation, /obj/item/gun/energy/laser/tag/red)) - target_type = /obj/machinery/porta_turret/tag + if(istype(E, /obj/item/gun/energy/laser/tag/blue)) + target_type = /obj/machinery/porta_turret/tag/blue + else if(istype(E, /obj/item/gun/energy/laser/tag/red)) + target_type = /obj/machinery/porta_turret/tag/red else target_type = /obj/machinery/porta_turret @@ -934,7 +941,7 @@ GLOBAL_LIST_EMPTY(turret_icons) Turret.name = finish_name Turret.installation = installation Turret.gun_charge = gun_charge - Turret.enabled = 0 + Turret.enabled = FALSE Turret.setup() qdel(src) @@ -981,25 +988,27 @@ GLOBAL_LIST_EMPTY(turret_icons) var/icon_state_active = "syndieturret1" var/icon_state_destroyed = "syndieturret2" - syndicate = 1 + syndicate = TRUE installation = null - always_up = 1 + always_up = TRUE use_power = NO_POWER_USE - has_cover = 0 - raised = 1 + has_cover = FALSE + raised = TRUE scan_range = 9 faction = "syndicate" - emp_vulnerable = 0 + emp_vulnerable = FALSE - lethal = 1 - check_arrest = 0 - check_records = 0 - check_weapons = 0 - check_access = 0 - check_anomalies = 1 - check_synth = 1 - ailock = 1 + lethal = TRUE + lethal_is_configurable = FALSE + targetting_is_configurable = FALSE + check_arrest = FALSE + check_records = FALSE + check_weapons = FALSE + check_access = FALSE + check_anomalies = TRUE + check_synth = TRUE + ailock = TRUE var/area/syndicate_depot/core/depotarea /obj/machinery/porta_turret/syndicate/die() @@ -1018,7 +1027,7 @@ GLOBAL_LIST_EMPTY(turret_icons) if(req_one_access && req_one_access.len) req_one_access.Cut() req_access = list(ACCESS_SYNDICATE) - one_access = 0 + one_access = FALSE /obj/machinery/porta_turret/syndicate/update_icon() if(stat & BROKEN) diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index 82b473b50f0..85fe8fcf843 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -1,22 +1,27 @@ +#define RECHARGER_POWER_USAGE_GUN 250 +#define RECHARGER_POWER_USAGE_MISC 200 + /obj/machinery/recharger name = "recharger" icon = 'icons/obj/stationobjs.dmi' icon_state = "recharger0" desc = "A charging dock for energy based weaponry." - anchored = 1 + anchored = TRUE use_power = IDLE_POWER_USE idle_power_usage = 4 active_power_usage = 200 pass_flags = PASSTABLE - var/obj/item/charging = null - var/using_power = FALSE - var/list/allowed_devices = list(/obj/item/gun/energy, /obj/item/melee/baton, /obj/item/modular_computer, /obj/item/rcs, /obj/item/bodyanalyzer) + + var/list/allowed_devices = list(/obj/item/gun/energy, /obj/item/melee/baton, /obj/item/rcs, /obj/item/bodyanalyzer) var/icon_state_off = "rechargeroff" var/icon_state_charged = "recharger2" var/icon_state_charging = "recharger1" var/icon_state_idle = "recharger0" var/recharge_coeff = 1 + var/obj/item/charging = null // The item that is being charged + var/using_power = FALSE // Whether the recharger is actually transferring power or not, used for icon + /obj/machinery/recharger/New() ..() component_parts = list() @@ -34,32 +39,32 @@ if(allowed) if(anchored) if(charging) - return 1 + return TRUE //Checks to make sure he's not in space doing it, and that the area got proper power. var/area/a = get_area(src) - if(!isarea(a) || a.power_equip == 0) + if(!isarea(a) || !a.power_equip) to_chat(user, "[src] blinks red as you try to insert [G].") - return 1 + return TRUE if(istype(G, /obj/item/gun/energy)) var/obj/item/gun/energy/E = G if(!E.can_charge) to_chat(user, "Your gun has no external power connector.") - return 1 + return TRUE if(!user.drop_item()) - return 1 + return TRUE G.forceMove(src) charging = G use_power = ACTIVE_POWER_USE + using_power = check_cell_needs_recharging(get_cell_from(G)) update_icon() else to_chat(user, "[src] isn't connected to anything!") - return 1 + return TRUE return ..() - /obj/machinery/recharger/crowbar_act(mob/user, obj/item/I) if(panel_open && !charging && default_deconstruction_crowbar(user, I)) return TRUE @@ -106,57 +111,15 @@ if(stat & (NOPOWER|BROKEN) || !anchored) return - using_power = FALSE - if(charging) - if(istype(charging, /obj/item/gun/energy)) - var/obj/item/gun/energy/E = charging - if(E.cell.charge < E.cell.maxcharge) - E.cell.give(E.cell.chargerate * recharge_coeff) - E.on_recharge() - use_power(250) - using_power = TRUE - - - if(istype(charging, /obj/item/melee/baton)) - var/obj/item/melee/baton/B = charging - if(B.cell) - if(B.cell.give(B.cell.chargerate)) - use_power(200) - using_power = TRUE - - if(istype(charging, /obj/item/modular_computer)) - var/obj/item/modular_computer/C = charging - var/obj/item/computer_hardware/battery/battery_module = C.all_components[MC_CELL] - if(battery_module) - var/obj/item/computer_hardware/battery/B = battery_module - if(B.battery) - if(B.battery.charge < B.battery.maxcharge) - B.battery.give(B.battery.chargerate) - use_power(200) - using_power = TRUE - - if(istype(charging, /obj/item/rcs)) - var/obj/item/rcs/R = charging - if(R.rcell) - if(R.rcell.give(R.rcell.chargerate)) - use_power(200) - using_power = TRUE - - if(istype(charging, /obj/item/bodyanalyzer)) - var/obj/item/bodyanalyzer/B = charging - if(B.cell) - if(B.cell.give(B.cell.chargerate)) - use_power(200) - using_power = TRUE - - update_icon(using_power) + using_power = try_recharging_if_possible() + update_icon() /obj/machinery/recharger/emp_act(severity) if(stat & (NOPOWER|BROKEN) || !anchored) ..(severity) return - if(istype(charging, /obj/item/gun/energy)) + if(istype(charging, /obj/item/gun/energy)) var/obj/item/gun/energy/E = charging if(E.cell) E.cell.emp_act(severity) @@ -167,7 +130,11 @@ B.cell.charge = 0 ..(severity) -/obj/machinery/recharger/update_icon(using_power = FALSE) //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier. +/obj/machinery/recharger/power_change() + ..() + update_icon() + +/obj/machinery/recharger/update_icon() //we have an update_icon() in addition to the stuff in process to make it feel a tiny bit snappier. if(stat & (NOPOWER|BROKEN) || !anchored) icon_state = icon_state_off return @@ -179,6 +146,49 @@ return icon_state = icon_state_idle +/obj/machinery/recharger/proc/get_cell_from(obj/item/I) + if(istype(I, /obj/item/gun/energy)) + var/obj/item/gun/energy/E = I + return E.cell + + if(istype(I, /obj/item/melee/baton)) + var/obj/item/melee/baton/B = I + return B.cell + + if(istype(I, /obj/item/rcs)) + var/obj/item/rcs/R = I + return R.rcell + + if(istype(I, /obj/item/bodyanalyzer)) + var/obj/item/bodyanalyzer/B = I + return B.cell + + return null + +/obj/machinery/recharger/proc/check_cell_needs_recharging(obj/item/stock_parts/cell/C) + if(!C || C.charge >= C.maxcharge) + return FALSE + return TRUE + +/obj/machinery/recharger/proc/recharge_cell(obj/item/stock_parts/cell/C, power_usage) + C.give(C.chargerate * recharge_coeff) + use_power(power_usage) + +/obj/machinery/recharger/proc/try_recharging_if_possible() + var/obj/item/stock_parts/cell/C = get_cell_from(charging) + if(!check_cell_needs_recharging(C)) + return FALSE + + if(istype(charging, /obj/item/gun/energy)) + recharge_cell(C, RECHARGER_POWER_USAGE_GUN) + + var/obj/item/gun/energy/E = charging + E.on_recharge() + else + recharge_cell(C, RECHARGER_POWER_USAGE_MISC) + + return TRUE + /obj/machinery/recharger/examine(mob/user) . = ..() if(charging && (!in_range(user, src) && !issilicon(user) && !isobserver(user))) @@ -204,3 +214,6 @@ icon_state_idle = "wrecharger0" icon_state_charging = "wrecharger1" icon_state_charged = "wrecharger2" + +#undef RECHARGER_POWER_USAGE_GUN +#undef RECHARGER_POWER_USAGE_MISC diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index e4e37ae1bcb..5d26f0e946e 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -8,8 +8,8 @@ layer = MOB_LAYER+1 // Overhead anchored = 1 density = 1 - damage_deflection = 10 - var/safety_mode = 0 // Temporarily stops machine if it detects a mob + damage_deflection = 15 + var/emergency_mode = FALSE // Temporarily stops machine if it detects a mob var/icon_name = "grinder-o" var/blood = 0 var/eat_dir = WEST @@ -42,9 +42,9 @@ /obj/machinery/recycler/examine(mob/user) . = ..() - . += "The power light is [(stat & NOPOWER) ? "off" : "on"]." - . += "The safety-mode light is [safety_mode ? "on" : "off"]." - . += "The safety-sensors status light is [emagged ? "off" : "on"]." + . += "The power light is [(stat & NOPOWER) ? "off" : "on"]." + . += "The operation light is [emergency_mode ? "off. [src] has detected a forbidden object with its sensors, and has shut down temporarily." : "on. [src] is active."]" + . += "The safety sensor light is [emagged ? "off!" : "on."]" /obj/machinery/recycler/power_change() ..() @@ -73,8 +73,8 @@ /obj/machinery/recycler/emag_act(mob/user) if(!emagged) emagged = 1 - if(safety_mode) - safety_mode = 0 + if(emergency_mode) + emergency_mode = FALSE update_icon() playsound(loc, "sparks", 75, 1, -1) to_chat(user, "You use the cryptographic sequencer on the [name].") @@ -82,7 +82,7 @@ /obj/machinery/recycler/update_icon() ..() var/is_powered = !(stat & (BROKEN|NOPOWER)) - if(safety_mode) + if(emergency_mode) is_powered = 0 icon_state = icon_name + "[is_powered]" + "[(blood ? "bld" : "")]" // add the blood tag at the end @@ -92,14 +92,13 @@ if(AM) Bumped(AM) - /obj/machinery/recycler/Bumped(atom/movable/AM) if(stat & (BROKEN|NOPOWER)) return if(!anchored) return - if(safety_mode) + if(emergency_mode) return var/move_dir = get_dir(loc, AM.loc) @@ -146,14 +145,14 @@ /obj/machinery/recycler/proc/emergency_stop(mob/living/L) playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) - safety_mode = 1 + emergency_mode = TRUE update_icon() L.loc = loc addtimer(CALLBACK(src, .proc/reboot), SAFETY_COOLDOWN) /obj/machinery/recycler/proc/reboot() playsound(loc, 'sound/machines/ping.ogg', 50, 0) - safety_mode = 0 + emergency_mode = FALSE update_icon() /obj/machinery/recycler/proc/crush_living(mob/living/L) diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm index 5952108b6a9..1624ef4bb92 100644 --- a/code/game/machinery/slotmachine.dm +++ b/code/game/machinery/slotmachine.dm @@ -12,29 +12,26 @@ var/resultlvl = null /obj/machinery/slot_machine/attack_hand(mob/user as mob) + tgui_interact(user) + +/obj/machinery/slot_machine/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "SlotMachine", name, 350, 200, master_ui, state) + ui.open() + +/obj/machinery/slot_machine/tgui_data(mob/user) + var/list/data = list() + // Get account account = user.get_worn_id_account() if(!account) if(istype(user.get_active_hand(), /obj/item/card/id)) account = get_card_account(user.get_active_hand()) else account = null - ui_interact(user) -/obj/machinery/slot_machine/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - default_unfasten_wrench(user, I) -/obj/machinery/slot_machine/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "slotmachine.tmpl", name, 350, 200) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/slot_machine/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] + // Send data data["working"] = working data["money"] = account ? account.money : null data["plays"] = plays @@ -42,22 +39,23 @@ data["resultlvl"] = resultlvl return data -/obj/machinery/slot_machine/Topic(href, href_list) +/obj/machinery/slot_machine/tgui_act(action, params) + if(..()) + return add_fingerprint(usr) - if(href_list["ops"]) - if(text2num(href_list["ops"])) // Play - if(working) - return - if(!account || account.money < 10) - return - if(!account.charge(10, null, "Bet", "Slot Machine", "Slot Machine")) - return - plays++ - working = 1 - icon_state = "slots-on" - playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) - addtimer(CALLBACK(src, .proc/spin_slots, usr.name), 25) + if(action == "spin") + if(working) + return + if(!account || account.money < 10) + return + if(!account.charge(10, null, "Bet", "Slot Machine", "Slot Machine")) + return + plays++ + working = TRUE + icon_state = "slots-on" + playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) + addtimer(CALLBACK(src, .proc/spin_slots, usr.name), 25) /obj/machinery/slot_machine/proc/spin_slots(userName) switch(rand(1,4050)) @@ -65,44 +63,45 @@ atom_say("JACKPOT! [userName] has won a MILLION CREDITS!") GLOB.event_announcement.Announce("Congratulations to [userName] on winning the Jackpot of ONE MILLION CREDITS!", "Jackpot Winner") result = "JACKPOT! You win one million credits!" - resultlvl = "highlight" + resultlvl = "teal" win_money(1000000, 'sound/goonstation/misc/airraid_loop.ogg') if(2 to 5) // .07% atom_say("Big Winner! [userName] has won a hundred thousand credits!") GLOB.event_announcement.Announce("Congratulations to [userName] on winning a hundred thousand credits!", "Big Winner") result = "Big Winner! You win a hundred thousand credits!" - resultlvl = "good" + resultlvl = "green" win_money(100000, 'sound/goonstation/misc/klaxon.ogg') if(6 to 50) // 1.08% atom_say("Big Winner! [userName] has won ten thousand credits!") result = "You win ten thousand credits!" - resultlvl = "good" + resultlvl = "green" win_money(10000, 'sound/goonstation/misc/klaxon.ogg') if(51 to 100) // 1.21% atom_say("Winner! [userName] has won a thousand credits!") result = "You win a thousand credits!" - resultlvl = "good" + resultlvl = "green" win_money(1000, 'sound/goonstation/misc/bell.ogg') if(101 to 200) // 2.44% atom_say("Winner! [userName] has won a hundred credits!") result = "You win a hundred credits!" - resultlvl = "good" + resultlvl = "green" win_money(100, 'sound/goonstation/misc/bell.ogg') if(201 to 300) // 2.44% atom_say("Winner! [userName] has won fifty credits!") result = "You win fifty credits!" - resultlvl = "good" + resultlvl = "green" win_money(50) if(301 to 1000) // 17.26% atom_say("Winner! [userName] has won ten credits!") result = "You win ten credits!" - resultlvl = "good" + resultlvl = "green" win_money(10) else // 75.31% - result = "No luck!" - resultlvl = "average" - working = 0 + result = "No luck!" + resultlvl = "orange" + working = FALSE icon_state = "slots-off" + SStgui.update_uis(src) // Push a UI update /obj/machinery/slot_machine/proc/win_money(amt, sound='sound/machines/ping.ogg') if(sound) @@ -110,3 +109,9 @@ if(!account) return account.credit(amt, "Slot Winnings", "Slot Machine", account.owner_name) + +/obj/machinery/slot_machine/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_unfasten_wrench(user, I) diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index da4df4f8382..59865dee52a 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -96,7 +96,7 @@ name = "atmospherics suit storage unit" suit_type = /obj/item/clothing/suit/space/hardsuit/engine/atmos mask_type = /obj/item/clothing/mask/gas - magboots_type = /obj/item/clothing/shoes/magboots + magboots_type = /obj/item/clothing/shoes/magboots/atmos req_access = list(ACCESS_ATMOSPHERICS) /obj/machinery/suit_storage_unit/atmos/secure @@ -255,6 +255,7 @@ occupant_typecache = typecacheof(occupant_typecache) /obj/machinery/suit_storage_unit/Destroy() + SStgui.close_uis(wires) QDEL_NULL(suit) QDEL_NULL(helmet) QDEL_NULL(mask) @@ -740,10 +741,11 @@ if(!occupant) return - if(user != occupant) - to_chat(occupant, "The machine kicks you out!") - if(user.loc != loc) - to_chat(occupant, "You leave the not-so-cozy confines of the SSU.") + if(user) + if(user != occupant) + to_chat(occupant, "The machine kicks you out!") + if(user.loc != loc) + to_chat(occupant, "You leave the not-so-cozy confines of [src].") occupant.forceMove(loc) occupant = null if(!state_open) @@ -751,6 +753,8 @@ update_icon() return +/obj/machinery/suit_storage_unit/force_eject_occupant() + eject_occupant() /obj/machinery/suit_storage_unit/verb/get_out() set name = "Eject Suit Storage Unit" @@ -799,3 +803,7 @@ /obj/machinery/suit_storage_unit/attack_ai(mob/user as mob) return attack_hand(user) + +/obj/machinery/suit_storage_unit/proc/check_electrified_callback() + if(!wires.is_cut(WIRE_ELECTRIFY)) + shocked = FALSE diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm index 181d5e494da..37dd2c6d439 100644 --- a/code/game/machinery/syndicatebomb.dm +++ b/code/game/machinery/syndicatebomb.dm @@ -91,6 +91,7 @@ ..() /obj/machinery/syndicatebomb/Destroy() + SStgui.close_uis(wires) QDEL_NULL(wires) QDEL_NULL(countdown) STOP_PROCESSING(SSfastprocess, src) @@ -174,7 +175,7 @@ . = TRUE if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return - if(open_panel && wires.IsAllCut()) + if(open_panel && wires.is_all_cut()) if(payload) to_chat(user, "You carefully pry out [payload].") payload.loc = user.loc @@ -188,7 +189,7 @@ /obj/machinery/syndicatebomb/welder_act(mob/user, obj/item/I) . = TRUE - if(payload || !wires.IsAllCut() || !open_panel) + if(payload || !wires.is_all_cut() || !open_panel) return if(!I.tool_use_check(user, 0)) return @@ -259,14 +260,11 @@ var/turf/bombturf = get_turf(src) var/area/A = get_area(bombturf) if(payload && !istype(payload, /obj/item/bombcore/training)) - msg_admin_attack("[key_name_admin(user)] has primed a [name] ([payload]) for detonation at [A.name] (JMP).", ATKLOG_FEW) log_game("[key_name(user)] has primed a [name] ([payload]) for detonation at [A.name] [COORD(bombturf)]") investigate_log("[key_name(user)] has has primed a [name] ([payload]) for detonation at [A.name] [COORD(bombturf)]", INVESTIGATE_BOMB) + add_attack_logs(user, src, "has primed a [name] ([payload]) for detonation", ATKLOG_FEW) payload.adminlog = "\The [src] that [key_name(user)] had primed detonated!" -/obj/machinery/syndicatebomb/proc/isWireCut(var/index) - return wires.IsIndexCut(index) - ///Bomb Subtypes/// /obj/machinery/syndicatebomb/training @@ -297,7 +295,7 @@ /obj/machinery/syndicatebomb/empty/New() ..() - wires.CutAll() + wires.cut_all() /obj/machinery/syndicatebomb/self_destruct name = "self destruct device" @@ -368,7 +366,7 @@ var/obj/machinery/syndicatebomb/holder = loc if(istype(holder)) if(holder.wires) - holder.wires.Shuffle() + holder.wires.shuffle_wires() holder.defused = 0 holder.open_panel = 0 holder.delayedbig = FALSE @@ -654,8 +652,8 @@ var/turf/T = get_turf(src) var/area/A = get_area(T) detonated-- - message_admins("[key_name_admin(user)] has remotely detonated [detonated ? "syndicate bombs" : "a syndicate bomb"] using a [name] at [A.name] (JMP).") investigate_log("[key_name(user)] has remotely detonated [detonated ? "syndicate bombs" : "a syndicate bomb"] using a [name] at [A.name] ([T.x],[T.y],[T.z])", INVESTIGATE_BOMB) + add_attack_logs(user, src, "has remotely detonated [detonated ? "syndicate bombs" : "a syndicate bomb"] using", ATKLOG_FEW) log_game("[key_name(user)] has remotely detonated [detonated ? "syndicate bombs" : "a syndicate bomb"] using a [name] at [A.name] ([T.x],[T.y],[T.z])") detonated = 0 existant = 0 diff --git a/code/game/machinery/tcomms/_base.dm b/code/game/machinery/tcomms/_base.dm index dc933099faa..892392a8c54 100644 --- a/code/game/machinery/tcomms/_base.dm +++ b/code/game/machinery/tcomms/_base.dm @@ -40,7 +40,7 @@ GLOBAL_LIST_EMPTY(tcomms_machines) var/network_id = "None" /// Is the machine active var/active = TRUE - /// Has the machine been hit by an ionspheric anomalie + /// Has the machine been hit by an ionospheric anomaly var/ion = FALSE /** @@ -52,6 +52,19 @@ GLOBAL_LIST_EMPTY(tcomms_machines) . = ..() GLOB.tcomms_machines += src update_icon() + if((!mapload) && (usr)) + // To the person who asks "Hey affected, why are you using this massive operator when you can use AREACOORD?" Well, ill tell you + // get_area_name is fucking broken and uses a for(x in world) search + // It doesnt even work, is expensive, and returns 0 + // Im not refactoring one thing which could risk breaking all admin location logs + // Fight me + log_action(usr, "constructed a new [src] at [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)]", adminmsg = TRUE) + // Add in component parts for the sake of deconstruction + component_parts = list() + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) /** * Base Destructor @@ -60,6 +73,8 @@ GLOBAL_LIST_EMPTY(tcomms_machines) */ /obj/machinery/tcomms/Destroy() GLOB.tcomms_machines -= src + if(usr) + log_action(usr, "destroyed a [src] at [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)]", adminmsg = TRUE) return ..() /** @@ -79,35 +94,49 @@ GLOBAL_LIST_EMPTY(tcomms_machines) // Attack overrides. These are needed so the UIs can be opened up // /obj/machinery/tcomms/attack_ai(mob/user) add_hiddenprint(user) - ui_interact(user) + tgui_interact(user) /obj/machinery/tcomms/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) /obj/machinery/tcomms/attack_hand(mob/user) if(..(user)) return - ui_interact(user) + tgui_interact(user) /** - * Start of Ion Anomalie Event + * Start of Ion Anomaly Event * - * Proc to easily start an Ion Anomalie's effects, and update the icon + * Proc to easily start an Ion Anomaly's effects, and update the icon */ /obj/machinery/tcomms/proc/start_ion() ion = TRUE update_icon() /** - * End of Ion Anomalie Event + * End of Ion Anomaly Event * - * Proc to easily stop an Ion Anomalie's effects, and update the icon + * Proc to easily stop an Ion Anomaly's effects, and update the icon */ /obj/machinery/tcomms/proc/end_ion() ion = FALSE update_icon() +/** + * Z-Level transit change helper + * + * Proc to make sure you cant have two of these active on a Z-level at once. It also makes sure to update the linkage + */ +/obj/machinery/tcomms/onTransitZ(old_z, new_z) + . = ..() + if(active) + active = FALSE + // This needs a timer because otherwise its on the shuttle Z and the message is missed + addtimer(CALLBACK(src, /atom.proc/visible_message, "Radio equipment on [src] has been overloaded by heavy bluespace interference. Please restart the machine."), 5) + update_icon() + + /** * Logging helper * @@ -462,3 +491,20 @@ GLOBAL_LIST_EMPTY(tcomms_machines) break return ..() +/** + * Screwdriver Act Handler + * + * Handles the screwdriver action for all tcomms machines, so they can be open and closed to be deconstructed + */ +/obj/machinery/tcomms/screwdriver_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_screwdriver(user, icon_state, icon_state, I) + +/** + * Crowbar Act Handler + * + * Handles the crowbar action for all tcomms machines, so they can be deconstructed + */ +/obj/machinery/tcomms/crowbar_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_crowbar(user, I) diff --git a/code/game/machinery/tcomms/core.dm b/code/game/machinery/tcomms/core.dm index 88846a5de33..9f84f3af963 100644 --- a/code/game/machinery/tcomms/core.dm +++ b/code/game/machinery/tcomms/core.dm @@ -14,6 +14,8 @@ name = "Telecommunications Core" desc = "A large rack full of communications equipment. Looks important." icon_state = "core" + // This starts as off so you cant make cores as hot spares + active = FALSE /// The NTTC config for this device var/datum/nttc_configuration/nttc = new() /// List of all reachable devices @@ -34,6 +36,12 @@ . = ..() link_password = GenerateKey() reachable_zlevels |= loc.z + component_parts += new /obj/item/circuitboard/tcomms/core(null) + if(check_power_on()) + active = TRUE + else + visible_message("Error: Another core is already active in this sector. Power-up cancelled due to radio interference.") + update_icon() /** * Destructor for the core. @@ -120,102 +128,126 @@ reachable_zlevels |= R.loc.z +/** + * Z-Level transit change helper + * + * Handles parent call of disabling the machine if it changes Z-level, but also rebuilds the list of reachable levels + */ +/obj/machinery/tcomms/core/onTransitZ(old_z, new_z) + . = ..() + refresh_zlevels() + +/** + * Power-on checker + * + * Checks the z-level to see if an existing core is already powered on, and deny this one turning on if there is one. Returns TRUE if it can power on, or FALSE if it cannot + */ +/obj/machinery/tcomms/core/proc/check_power_on() + // Cancel if we are already on + if(active) + return TRUE + + for(var/obj/machinery/tcomms/core/C in GLOB.tcomms_machines) + // Make sure we dont check ourselves + if(C == src) + continue + // We dont care about ones on other zlevels + if(!atoms_share_level(C, src)) + continue + // If another core is active, return FALSE + if(C.active) + return FALSE + // If we got here there isnt an active core on this Z-level. So return true + return TRUE + ////////////// // UI STUFF // ////////////// -/obj/machinery/tcomms/core/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // this is silly but it has to be done because NTTC inits before languages do - if(nttc.valid_languages.len == 1) +/obj/machinery/tcomms/core/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + // This needs to happen here because of how late the language datum initializes. I dont like it + if(length(nttc.valid_languages) == 1) nttc.update_languages() - // Now the actual UI stuff - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "tcomms_core.tmpl", "Telecommunications Core", 900, 600) + ui = new(user, src, ui_key, "TcommsCore", name, 900, 600, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/tcomms/core/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - // What tab are we on - data["tab"] = ui_tab +/obj/machinery/tcomms/core/tgui_data(mob/user) + var/list/data = list() data["ion"] = ion - // Only send NTTC settings if were on the right tab. This saves on sending overhead. - if(ui_tab == UI_TAB_CONFIG) - // Z-level list. Note that this will also show sectors with hidden relay links, but you cant see the relays themselves - // This allows the crew to realise that sectors have hidden relays - data["sectors_available"] = "Count: [length(reachable_zlevels)] | List: [jointext(reachable_zlevels, " ")]" - // Toggles - data["active"] = active - data["nttc_toggle_jobs"] = nttc.toggle_jobs - data["nttc_toggle_job_color"] = nttc.toggle_job_color - data["nttc_toggle_name_color"] = nttc.toggle_name_color - data["nttc_toggle_command_bold"] = nttc.toggle_command_bold - // Strings - data["nttc_setting_language"] = nttc.setting_language - data["nttc_job_indicator_type"] = nttc.job_indicator_type - // Network ID - data["network_id"] = network_id + // Z-level list. Note that this will also show sectors with hidden relay links, but you cant see the relays themselves + // This allows the crew to realise that sectors have hidden relays + data["sectors_available"] = "Count: [length(reachable_zlevels)] | List: [jointext(reachable_zlevels, " ")]" + // Toggles + data["active"] = active + data["nttc_toggle_jobs"] = nttc.toggle_jobs + data["nttc_toggle_job_color"] = nttc.toggle_job_color + data["nttc_toggle_name_color"] = nttc.toggle_name_color + data["nttc_toggle_command_bold"] = nttc.toggle_command_bold + // Strings + data["nttc_setting_language"] = nttc.setting_language + data["nttc_job_indicator_type"] = nttc.job_indicator_type + // Network ID + data["network_id"] = network_id - if(ui_tab == UI_TAB_LINKS) - data["link_password"] = link_password - // You ready to see some shit? - for(var/obj/machinery/tcomms/relay/R in linked_relays) - // Dont show relays with a hidden link - if(R.hidden_link) - continue - // Assume false - var/status = FALSE - var/status_color = "'background-color: #eb4034'" // Red - if(R.active && !(R.stat & NOPOWER)) - status = TRUE - status_color = "'background-color: #32a852'" // Green + data["link_password"] = link_password + // You ready to see some awful shit? + var/list/relays = list() + for(var/obj/machinery/tcomms/relay/R in linked_relays) + // Dont show relays with a hidden link + if(R.hidden_link) + continue + // Assume false + var/status = FALSE + if(R.active && !(R.stat & NOPOWER)) + status = TRUE + relays += list(list("addr" = "\ref[R]", "net_id" = R.network_id, "sector" = R.loc.z, "status" = status)) - data["entries"] += list(list("addr" = "\ref[R]", "net_id" = R.network_id, "sector" = R.loc.z, "status" = status, "status_color" = status_color)) - // End the shit + data["relay_entries"] = relays + // End the shit - if(ui_tab == UI_TAB_FILTER) - data["filtered_users"] = nttc.filtering + data["filtered_users"] = nttc.filtering return data -/obj/machinery/tcomms/core/Topic(href, href_list) +/obj/machinery/tcomms/core/tgui_act(action, list/params) // Check against href exploits if(..()) return - if(href_list["tab"]) - // Make sure its a valid tab - if(href_list["tab"] in list(UI_TAB_CONFIG, UI_TAB_LINKS, UI_TAB_FILTER)) - ui_tab = href_list["tab"] + . = TRUE - // Check if they did a href, but only for that current tab - if(ui_tab == UI_TAB_CONFIG) + switch(action) // All the toggle on/offs go here - if(href_list["toggle_active"]) - active = !active - update_icon() + if("toggle_active") + if(check_power_on()) + active = !active + update_icon() + else + to_chat(usr, "Error: Another core is already active in this sector. Power-up cancelled due to radio interference.") + // NTTC Toggles - if(href_list["nttc_toggle_jobs"]) + if("nttc_toggle_jobs") nttc.toggle_jobs = !nttc.toggle_jobs log_action(usr, "toggled job tags (Now [nttc.toggle_jobs])") - if(href_list["nttc_toggle_job_color"]) + if("nttc_toggle_job_color") nttc.toggle_job_color = !nttc.toggle_job_color log_action(usr, "toggled job colors (Now [nttc.toggle_job_color])") - if(href_list["nttc_toggle_name_color"]) + if("nttc_toggle_name_color") nttc.toggle_name_color = !nttc.toggle_name_color log_action(usr, "toggled name colors (Now [nttc.toggle_name_color])") - if(href_list["nttc_toggle_command_bold"]) + if("nttc_toggle_command_bold") nttc.toggle_command_bold = !nttc.toggle_command_bold log_action(usr, "toggled command bold (Now [nttc.toggle_command_bold])") // We need to be a little more fancy for the others // Job Format - if(href_list["nttc_job_indicator_type"]) + if("nttc_job_indicator_type") var/card_style = input(usr, "Pick a job card format.", "Job Card Format") as null|anything in nttc.job_card_styles if(!card_style) return @@ -224,7 +256,7 @@ log_action(usr, "has set NTTC job card format to [card_style]") // Language Settings - if(href_list["nttc_setting_language"]) + if("nttc_setting_language") var/new_language = input(usr, "Pick a language to convert messages to.", "Language Conversion") as null|anything in nttc.valid_languages if(!new_language) return @@ -238,24 +270,23 @@ log_action(usr, new_language == "--DISABLE--" ? "disabled NTTC language conversion" : "set NTTC language conversion to [new_language]", TRUE) // Imports and exports - if(href_list["import"]) + if("import") var/json = input(usr, "Provide configuration JSON below.", "Load Config", nttc.nttc_serialize()) as message if(nttc.nttc_deserialize(json, usr.ckey)) log_action(usr, "has uploaded a NTTC JSON configuration: [ADMIN_SHOWDETAILS("Show", json)]", TRUE) - if(href_list["export"]) + if("export") usr << browse(nttc.nttc_serialize(), "window=save_nttc") // Set network ID - if(href_list["network_id"]) + if("network_id") var/new_id = input(usr, "Please enter a new network ID", "Network ID", network_id) log_action(usr, "renamed core with ID [network_id] to [new_id]") to_chat(usr, "Device ID changed from [network_id] to [new_id].") network_id = new_id - if(ui_tab == UI_TAB_LINKS) - if(href_list["unlink"]) - var/obj/machinery/tcomms/relay/R = locate(href_list["unlink"]) + if("unlink") + var/obj/machinery/tcomms/relay/R = locate(params["addr"]) if(istype(R, /obj/machinery/tcomms/relay)) var/confirm = alert("Are you sure you want to unlink this relay?\nID: [R.network_id]\nADDR: \ref[R]", "Relay Unlink", "Yes", "No") if(confirm == "Yes") @@ -264,14 +295,13 @@ else to_chat(usr, "ERROR: Relay not found. Please file an issue report.") - if(href_list["change_password"]) + if("change_password") var/new_password = input(usr, "Please enter a new password","New Password", link_password) log_action(usr, "has changed the password on core with ID [network_id] from [link_password] to [new_password]") to_chat(usr, "Successfully changed password from [link_password] to [new_password].") link_password = new_password - if(ui_tab == UI_TAB_FILTER) - if(href_list["add_filter"]) + if("add_filter") // This is a stripped input because I did NOT come this far for this system to be abused by HTML injection var/name_to_add = stripped_input(usr, "Enter a name to add to the filtering list", "Name Entry") if(name_to_add == "") @@ -283,9 +313,8 @@ log_action(usr, "has added [name_to_add] to the NTTC filter list on core with ID [network_id]", TRUE) to_chat(usr, "Successfully added [name_to_add] to the NTTC filtering list.") - - if(href_list["remove_filter"]) - var/name_to_remove = href_list["remove_filter"] + if("remove_filter") + var/name_to_remove = params["user"] if(!(name_to_remove in nttc.filtering)) to_chat(usr, "ERROR: Name does not exist in filter list. Please file an issue report.") else @@ -296,9 +325,6 @@ to_chat(usr, "Successfully removed [name_to_remove] from the NTTC filtering list.") - // Hack to speed update the nanoUI - SSnanoui.update_uis(src) - #undef UI_TAB_CONFIG #undef UI_TAB_LINKS #undef UI_TAB_FILTER diff --git a/code/game/machinery/tcomms/relay.dm b/code/game/machinery/tcomms/relay.dm index 2cd2a44b1af..27d4e750bc5 100644 --- a/code/game/machinery/tcomms/relay.dm +++ b/code/game/machinery/tcomms/relay.dm @@ -9,6 +9,8 @@ name = "Telecommunications Relay" desc = "A large device with several radio antennas on it." icon_state = "relay" + // This starts as off so you cant make cores as hot spares + active = FALSE /// The host core for this relay var/obj/machinery/tcomms/core/linked_core /// ID of the hub to auto link to @@ -25,6 +27,12 @@ */ /obj/machinery/tcomms/relay/Initialize(mapload) . = ..() + component_parts += new /obj/item/circuitboard/tcomms/relay(null) + if(check_power_on()) + active = TRUE + else + visible_message("Error: Another relay is already active in this sector. Power-up cancelled due to radio interference.") + update_icon() if(mapload && autolink_id) return INITIALIZE_HINT_LATELOAD @@ -50,6 +58,40 @@ // Only ONE of these with one ID should exist per world break +/** + * Z-Level transit change helper + * + * Handles parent call of disabling the machine if it changes Z-level, but also rebuilds the list of reachable levels on the linked core + */ +/obj/machinery/tcomms/relay/onTransitZ(old_z, new_z) + . = ..() + if(linked_core) + linked_core.refresh_zlevels() + + +/** + * Power-on checker + * + * Checks the z-level to see if an existing relay is already powered on, and deny this one turning on if there is one. Returns TRUE if it can power on, or FALSE if it cannot + */ +/obj/machinery/tcomms/relay/proc/check_power_on() + // Cancel if we are already on + if(active) + return TRUE + + for(var/obj/machinery/tcomms/relay/R in GLOB.tcomms_machines) + // Make sure we dont check ourselves + if(R == src) + continue + // We dont care about ones on other zlevels + if(!atoms_share_level(R, src)) + continue + // If another relay is active, return FALSE + if(R.active) + return FALSE + // If we got here there isnt an active relay on this Z-level. So return TRUE + return TRUE + /** * Proc to link the relay to the core. * @@ -82,23 +124,22 @@ * Proc which ensures the host core has its zlevels updated (icons are updated by parent call) */ /obj/machinery/tcomms/relay/power_change() - ..() - if(linked_core) - linked_core.refresh_zlevels() + ..() + if(linked_core) + linked_core.refresh_zlevels() ////////////// // UI STUFF // ////////////// -/obj/machinery/tcomms/relay/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/tcomms/relay/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "tcomms_relay.tmpl", "Telecommunications Relay", 600, 400) + ui = new(user, src, ui_key, "TcommsRelay", name, 600, 400, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/tcomms/relay/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/tcomms/relay/tgui_data(mob/user) + var/list/data = list() // Are we on or not data["active"] = active // What is our network ID @@ -112,47 +153,58 @@ if(linked) data["linked_core_id"] = linked_core.network_id data["linked_core_addr"] = "\ref[linked_core]" - else + var/list/cores = list() for(var/obj/machinery/tcomms/core/C in GLOB.tcomms_machines) - data["entries"] += list(list("addr" = "\ref[C]", "net_id" = C.network_id, "sector" = C.loc.z)) + cores += list(list("addr" = "\ref[C]", "net_id" = C.network_id, "sector" = C.loc.z)) + data["cores"] = cores return data -/obj/machinery/tcomms/relay/Topic(href, href_list) +/obj/machinery/tcomms/relay/tgui_act(action, list/params) // Check against href exploits if(..()) return - // All the toggle on/offs go here - if(href_list["toggle_active"]) - active = !active - update_icon() - if(linked_core) - linked_core.refresh_zlevels() + . = TRUE - // Set network ID - if(href_list["network_id"]) - var/new_id = input(usr, "Please enter a new network ID", "Network ID", network_id) - log_action(usr, "renamed core with ID [network_id] to [new_id]") - to_chat(usr, "Device ID changed from [network_id] to [new_id].") - network_id = new_id + switch(action) + if("toggle_active") + if(check_power_on()) + active = !active + update_icon() + if(linked_core) + linked_core.refresh_zlevels() + else + to_chat(usr, "Error: Another relay is already active in this sector. Power-up cancelled due to radio interference.") + + // Set network ID + if("network_id") + var/new_id = input(usr, "Please enter a new network ID", "Network ID", network_id) + log_action(usr, "renamed core with ID [network_id] to [new_id]") + to_chat(usr, "Device ID changed from [network_id] to [new_id].") + network_id = new_id - if(linked) // Only do these hrefs if we are linked to prevent bugs/exploits - if(href_list["toggle_hidden_link"]) + if("toggle_hidden_link") + if(!linked) + return hidden_link = !hidden_link log_action(usr, "Modified hidden link for [network_id] (Now [hidden_link])") - if(href_list["unlink"]) + if("unlink") + if(!linked) + return var/choice = alert(usr, "Are you SURE you want to unlink this relay?\nYou wont be able to re-link without the core password", "Unlink","Yes","No") if(choice == "Yes") log_action(usr, "Unlinked [network_id] from [linked_core.network_id]") Reset() - else + // You should only be able to link if its not linked, to prevent weirdness - if(href_list["link"]) - var/obj/machinery/tcomms/core/C = locate(href_list["link"]) + if("link") + if(linked) + return + var/obj/machinery/tcomms/core/C = locate(params["addr"]) if(istype(C, /obj/machinery/tcomms/core)) var/user_pass = input(usr, "Please enter core password","Password Entry") // Check the password @@ -165,5 +217,3 @@ to_chat(usr, "ERROR: Core not found. Please file an issue report.") - // Hack to speed update the nanoUI - SSnanoui.update_uis(src) diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index b92e3c78bb8..005e6720b8b 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -1,16 +1,24 @@ +#define REGIME_TELEPORT 0 +#define REGIME_GATE 1 +#define REGIME_GPS 2 + /obj/machinery/computer/teleporter name = "teleporter control console" desc = "Used to control a linked teleportation Hub and Station." icon_screen = "teleport" icon_keyboard = "teleport_key" circuit = /obj/item/circuitboard/teleporter - var/obj/item/gps/locked = null - var/regime_set = "Teleporter" + var/obj/item/gps/locked = null /// A GPS with a locked destination + var/regime = REGIME_TELEPORT /// Switches mode between teleporter, gate and gps var/id = null - var/obj/machinery/teleport/station/power_station - var/calibrating - var/turf/target //Used for one-time-use teleport cards (such as clown planet coordinates.) - //Setting this to 1 will set src.locked to null after a player enters the portal and will not allow hand-teles to open portals to that location. + var/obj/machinery/teleport/station/power_station /// The power station that's connected to the console + var/calibrating = FALSE /// Whether calibration is in progress or not. Calibration prevents changes. + var/turf/target ///The target turf of the teleporter + var/target_list ///lists of suitable teleport targets, dependent on regime. Used in the UI + + /* var/area_bypass is for one-time-use teleport cards (such as clown planet coordinates.) + Setting this to TRUE will set var/obj/item/gps/locked to null after a player enters the portal and will not allow hand-teles to open portals to that location. + */ var/area_bypass = FALSE var/cc_beacon = FALSE @@ -24,6 +32,7 @@ ..() link_power_station() update_icon() + target_list = targets_teleport() /obj/machinery/computer/teleporter/Destroy() if(power_station) @@ -55,202 +64,238 @@ /obj/machinery/computer/teleporter/emag_act(mob/user) if(!emagged) - emagged = 1 + emagged = TRUE to_chat(user, "The teleporter can now lock on to Syndicate beacons!") else - ui_interact(user) + tgui_interact(user) /obj/machinery/computer/teleporter/attack_ai(mob/user) - src.attack_hand(user) + attack_hand(user) /obj/machinery/computer/teleporter/attack_hand(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + +/obj/machinery/computer/teleporter/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) if(stat & (NOPOWER|BROKEN)) return - - // Set up the Nano UI - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "teleporter_console.tmpl", "Teleporter Console UI", 400, 400) + ui = new(user, src, ui_key, "Teleporter", "Teleporter Console", 380, 260) ui.open() -/obj/machinery/computer/teleporter/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/computer/teleporter/tgui_data(mob/user) + var/list/data = list() data["powerstation"] = power_station - if(power_station) + if(power_station?.teleporter_hub) data["teleporterhub"] = power_station.teleporter_hub data["calibrated"] = power_station.teleporter_hub.calibrated - data["accurate"] = power_station.teleporter_hub.accurate else data["teleporterhub"] = null data["calibrated"] = null - data["accurate"] = null - data["regime"] = regime_set + data["regime"] = regime var/area/targetarea = get_area(target) data["target"] = (!target || !targetarea) ? "None" : sanitize(targetarea.name) data["calibrating"] = calibrating - data["locked"] = locked + data["locked"] = locked ? TRUE : FALSE + data["targetsTeleport"] = target_list return data -/obj/machinery/computer/teleporter/Topic(href, href_list) +/obj/machinery/computer/teleporter/tgui_act(action, params) if(..()) - return 1 - - if(href_list["eject"]) - eject() - SSnanoui.update_uis(src) return if(!check_hub_connection()) - to_chat(usr, "Error: Unable to detect hub.") - SSnanoui.update_uis(src) + atom_say("Error: Unable to detect hub.") return if(calibrating) - to_chat(usr, "Error: Calibration in progress. Stand by.") - SSnanoui.update_uis(src) + atom_say("Error: Calibration in progress. Stand by.") return - if(href_list["regimeset"]) - power_station.engaged = 0 - power_station.teleporter_hub.update_icon() - power_station.teleporter_hub.calibrated = 0 - reset_regime() - SSnanoui.update_uis(src) - if(href_list["settarget"]) - power_station.engaged = 0 - power_station.teleporter_hub.update_icon() - power_station.teleporter_hub.calibrated = 0 - set_target(usr) - SSnanoui.update_uis(src) - if(href_list["lock"]) - power_station.engaged = 0 - power_station.teleporter_hub.update_icon() - power_station.teleporter_hub.calibrated = 0 - target = get_turf(locked.locked_location) - SSnanoui.update_uis(src) - if(href_list["calibrate"]) - if(!target) - to_chat(usr, "Error: No target set to calibrate to.") - SSnanoui.update_uis(src) - return - if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accurate >= 3) - to_chat(usr, "Hub is already calibrated.") - SSnanoui.update_uis(src) - return - src.visible_message("Processing hub calibration to target...") + . = TRUE - calibrating = 1 - SSnanoui.update_uis(src) - spawn(50 * (3 - power_station.teleporter_hub.accurate)) //Better parts mean faster calibration - calibrating = 0 - if(check_hub_connection()) - power_station.teleporter_hub.calibrated = 1 - src.visible_message("Calibration complete.") - else - src.visible_message("Error: Unable to detect hub.") - SSnanoui.update_uis(src) + switch(action) + if("eject") //eject gps device + eject() + if("load") //load gps coordinates + target = locate(locked.locked_location.x,locked.locked_location.y,locked.locked_location.z) + if("setregime") + regime = text2num(params["regime"]) + if(regime == REGIME_TELEPORT) + target_list = targets_teleport() + if(regime == REGIME_GATE) + target_list = targets_gate() + if(regime == REGIME_GPS) + target_list = null //clears existing entries, target is added by load action + resetPowerstation() + target = null + if("settarget") + resetPowerstation() + var/turf/tmpTarget = locate(text2num(params["x"]),text2num(params["y"]),text2num(params["z"])) + if(!istype(tmpTarget, /turf)) + atom_say("No valid targets available.") + return + target = tmpTarget + if(regime == REGIME_TELEPORT) + teleport_helper() + if(regime == REGIME_GATE) + gate_helper() + if("calibrate") + if(!target) + atom_say("Error: No target set to calibrate to.") + return + if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accurate >= 3) + atom_say("Hub is already calibrated.") + return - SSnanoui.update_uis(src) + atom_say("Processing hub calibration to target...") + calibrating = TRUE + addtimer(CALLBACK(src, .proc/calibrateCallback), 50 * (3 - power_station.teleporter_hub.accurate)) //Better parts mean faster calibration + +/** +* Resets the connected powerstation to initial values. Helper function of tgui_act +*/ +/obj/machinery/computer/teleporter/proc/resetPowerstation() + power_station.engaged = FALSE + power_station.teleporter_hub.calibrated = FALSE + power_station.teleporter_hub.update_icon() + +/** +* Calibrates the hub. Helper function of tgui_act +*/ +/obj/machinery/computer/teleporter/proc/calibrateCallback() + calibrating = FALSE + if(check_hub_connection()) + power_station.teleporter_hub.calibrated = TRUE + atom_say("Calibration complete.") + else + atom_say("Error: Unable to detect hub.") /obj/machinery/computer/teleporter/proc/check_hub_connection() if(!power_station) return if(!power_station.teleporter_hub) return - return 1 - -/obj/machinery/computer/teleporter/proc/reset_regime() - target = null - if(regime_set == "Teleporter") - regime_set = "Gate" - else - regime_set = "Teleporter" + return TRUE +/** +* Helper function of tgui_act +* +* Triggered when ejecting a gps device. Sets the gps to the ground and resets the console +*/ /obj/machinery/computer/teleporter/proc/eject() if(locked) locked.loc = loc locked = null + regime = REGIME_TELEPORT + target_list = targets_teleport() -/obj/machinery/computer/teleporter/proc/set_target(mob/user) - area_bypass = FALSE - if(regime_set == "Teleporter") - var/list/L = list() - var/list/areaindex = list() +/** +* Creates a list of viable targets for the teleport. Helper function of tgui_data +*/ +/obj/machinery/computer/teleporter/proc/targets_teleport() + var/list/L = list() + var/list/areaindex = list() - for(var/obj/item/radio/beacon/R in GLOB.beacons) - var/turf/T = get_turf(R) - if(!T) - continue - if(!is_teleport_allowed(T.z) && !R.cc_beacon) - continue - if(R.syndicate == 1 && emagged == 0) - continue - var/tmpname = T.loc.name + for(var/obj/item/radio/beacon/R in GLOB.beacons) + var/turf/T = get_turf(R) + if(!T) + continue + if(!is_teleport_allowed(T.z) && !R.cc_beacon) + continue + if(R.syndicate && !emagged) + continue + var/tmpname = T.loc.name + if(areaindex[tmpname]) + tmpname = "[tmpname] ([++areaindex[tmpname]])" + else + areaindex[tmpname] = 1 + L[tmpname] = list( + "name" = tmpname, + "x" = T.x, + "y" = T.y, + "z" = T.z) + + for(var/obj/item/implant/tracking/I in GLOB.tracked_implants) + if(!I.implanted || !ismob(I.loc)) + continue + else + var/mob/M = I.loc + if(M.stat == DEAD) + if(M.timeofdeath + 6000 < world.time) + continue + var/turf/T = get_turf(M) + if(!T) continue + if(!is_teleport_allowed(T.z)) continue + var/tmpname = M.real_name if(areaindex[tmpname]) tmpname = "[tmpname] ([++areaindex[tmpname]])" else areaindex[tmpname] = 1 - L[tmpname] = R + L[tmpname] = list( + "name" = tmpname, + "x" = T.x, + "y" = T.y, + "z" = T.z) + return L - for(var/obj/item/implant/tracking/I in GLOB.tracked_implants) - if(!I.implanted || !ismob(I.loc)) - continue - else - var/mob/M = I.loc - if(M.stat == DEAD) - if(M.timeofdeath + 6000 < world.time) - continue - var/turf/T = get_turf(M) - if(!T) continue - if(!is_teleport_allowed(T.z)) continue - var/tmpname = M.real_name - if(areaindex[tmpname]) - tmpname = "[tmpname] ([++areaindex[tmpname]])" - else - areaindex[tmpname] = 1 - L[tmpname] = I +/** +* Creates a list of viable targets for the gate. Helper function of tgui_data +*/ +/obj/machinery/computer/teleporter/proc/targets_gate(mob/users) + var/list/L = list() + var/list/areaindex = list() + var/list/S = power_station.linked_stations + if(!S.len) + return L + for(var/obj/machinery/teleport/station/R in S) + var/turf/T = get_turf(R) + if(!T || !R.teleporter_hub || !R.teleporter_console) + continue + if(!is_teleport_allowed(T.z)) + continue + var/tmpname = T.loc.name + if(areaindex[tmpname]) + tmpname = "[tmpname] ([++areaindex[tmpname]])" + else + areaindex[tmpname] = 1 + L[tmpname] = list( + "name" = tmpname, + "x" = T.x, + "y" = T.y, + "z" = T.z) + return L - var/desc = input("Please select a location to lock in.", "Locking Computer") in L - target = L[desc] - if(istype(target, /obj/item/radio/beacon)) - var/obj/item/radio/beacon/B = target +/** +* Helper function of tgui_act. +* +* Called after selecting a target for the gate in the UI. Sets area_bypass and cc_beacon. +*/ +/obj/machinery/computer/teleporter/proc/teleport_helper() + area_bypass = FALSE + for(var/item in target.contents) + if(istype(item, /obj/item/radio/beacon)) + var/obj/item/radio/beacon/B = item if(B.area_bypass) area_bypass = TRUE cc_beacon = B.cc_beacon - else - var/list/L = list() - var/list/areaindex = list() - var/list/S = power_station.linked_stations - if(!S.len) - to_chat(user, "No connected stations located.") - return - for(var/obj/machinery/teleport/station/R in S) - var/turf/T = get_turf(R) - if(!T || !R.teleporter_hub || !R.teleporter_console) - continue - if(!is_teleport_allowed(T.z)) - continue - var/tmpname = T.loc.name - if(areaindex[tmpname]) - tmpname = "[tmpname] ([++areaindex[tmpname]])" - else - areaindex[tmpname] = 1 - L[tmpname] = R - var/desc = input("Please select a station to lock in.", "Locking Computer") in L - target = L[desc] - if(target) - var/obj/machinery/teleport/station/trg = target - trg.linked_stations |= power_station - trg.stat &= ~NOPOWER - if(trg.teleporter_hub) - trg.teleporter_hub.stat &= ~NOPOWER - trg.teleporter_hub.update_icon() - if(trg.teleporter_console) - trg.teleporter_console.stat &= ~NOPOWER - trg.teleporter_console.update_icon() - return + +/** +* Helper function of tgui_act. +* +* Called after selecting a target for the teleporter in the UI. +*/ +/obj/machinery/computer/teleporter/proc/gate_helper() + area_bypass = FALSE + var/obj/machinery/teleport/station/trg = target + trg.linked_stations |= power_station + trg.stat &= ~NOPOWER + if(trg.teleporter_hub) + trg.teleporter_hub.stat &= ~NOPOWER + trg.teleporter_hub.update_icon() + if(trg.teleporter_console) + trg.teleporter_console.stat &= ~NOPOWER + trg.teleporter_console.update_icon() /proc/find_loc(obj/R as obj) if(!R) return null @@ -263,20 +308,20 @@ /obj/machinery/teleport name = "teleport" icon = 'icons/obj/stationobjs.dmi' - density = 1 - anchored = 1.0 + density = TRUE + anchored = TRUE /obj/machinery/teleport/hub name = "teleporter hub" desc = "It's the hub of a teleporting machine." icon_state = "tele0" - var/accurate = 0 + var/accurate = FALSE use_power = IDLE_POWER_USE idle_power_usage = 10 active_power_usage = 2000 var/obj/machinery/teleport/station/power_station var/calibrated //Calibration prevents mutation - var/admin_usage = 0 // if 1, works on z2. If 0, doesn't. Used for admin room teleport. + var/admin_usage = FALSE // if 1, works on z2. If 0, doesn't. Used for admin room teleport. /obj/machinery/teleport/hub/New() ..() @@ -317,6 +362,7 @@ for(dir in list(NORTH,EAST,SOUTH,WEST)) power_station = locate(/obj/machinery/teleport/station, get_step(src, dir)) if(power_station) + power_station.link_console_and_hub() break return power_station @@ -324,27 +370,12 @@ if(!is_teleport_allowed(z) && !admin_usage) to_chat(M, "You can't use this here.") return - if(power_station && power_station.engaged && !panel_open) - //--FalseIncarnate - //Prevents AI cores from using the teleporter, prints out failure messages for clarity - if(istype(M, /mob/living/silicon/ai) || istype(M, /obj/structure/AIcore)) - visible_message("The teleporter rejects the AI unit.") - if(istype(M, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/T = M - var/list/TPError = list("Firmware instructions dictate you must remain on your assigned station!", - "You cannot interface with this technology and get rejected!", - "External firewalls prevent you from utilizing this machine!", - "Your AI core's anti-bluespace failsafes trigger and prevent teleportation!") - to_chat(T, "[pick(TPError)]") - return - else - if(!teleport(M) && isliving(M)) // the isliving(M) is needed to avoid triggering errors if a spark bumps the telehub - visible_message("[src] emits a loud buzz, as its teleport portal flickers and fails!") - playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) - power_station.toggle() // turn off the portal. - - use_power(5000) - //--FalseIncarnate + if(power_station && power_station.engaged && !panel_open && !blockAI(M) && !istype(M, /obj/spacepod)) + if(!teleport(M) && isliving(M)) // the isliving(M) is needed to avoid triggering errors if a spark bumps the telehub + visible_message("[src] emits a loud buzz, as its teleport portal flickers and fails!") + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, FALSE) + power_station.toggle() // turn off the portal. + use_power(5000) return /obj/machinery/teleport/hub/attackby(obj/item/I, mob/user, params) @@ -375,7 +406,7 @@ . = do_teleport(M, locate(rand((2*TRANSITIONEDGE), world.maxx - (2*TRANSITIONEDGE)), rand((2*TRANSITIONEDGE), world.maxy - (2*TRANSITIONEDGE)), 3), 2, bypass_area_flag = com.area_bypass) else . = do_teleport(M, com.target, bypass_area_flag = com.area_bypass) - calibrated = 0 + calibrated = FALSE /obj/machinery/teleport/hub/update_icon() if(panel_open) @@ -389,7 +420,7 @@ name = "permanent teleporter" desc = "A teleporter with the target pre-set on the circuit board." icon_state = "tele0" - var/recalibrating = 0 + var/recalibrating = FALSE use_power = IDLE_POWER_USE idle_power_usage = 10 active_power_usage = 2000 @@ -406,37 +437,42 @@ tele_delay = max(A, 0) update_icon() -/obj/machinery/teleport/perma/Bumped(M as mob|obj) +/** + Internal helper function + + Prevents AI from using the teleporter, prints out failure messages for clarity +*/ +/obj/machinery/teleport/proc/blockAI(atom/A) + if(istype(A, /mob/living/silicon/ai) || istype(A, /obj/structure/AIcore)) + visible_message("The teleporter rejects the AI unit.") + if(istype(A, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/T = A + var/list/TPError = list("Firmware instructions dictate you must remain on your assigned station!", + "You cannot interface with this technology and get rejected!", + "External firewalls prevent you from utilizing this machine!", + "Your AI core's anti-bluespace failsafes trigger and prevent teleportation!") + to_chat(T, "[pick(TPError)]") + return TRUE + return FALSE + +/obj/machinery/teleport/perma/Bumped(atom/A) if(stat & (BROKEN|NOPOWER)) return if(!is_teleport_allowed(z)) - to_chat(M, "You can't use this here.") + to_chat(A, "You can't use this here.") return - if(target && !recalibrating && !panel_open) - //--FalseIncarnate - //Prevents AI cores from using the teleporter, prints out failure messages for clarity - if(istype(M, /mob/living/silicon/ai) || istype(M, /obj/structure/AIcore)) - visible_message("The teleporter rejects the AI unit.") - if(istype(M, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/T = M - var/list/TPError = list("Firmware instructions dictate you must remain on your assigned station!", - "You cannot interface with this technology and get rejected!", - "External firewalls prevent you from utilizing this machine!", - "Your AI core's anti-bluespace failsafes trigger and prevent teleportation!") - to_chat(T, "[pick(TPError)]") - return - else - do_teleport(M, target) - use_power(5000) - if(tele_delay) - recalibrating = 1 - update_icon() - spawn(tele_delay) - recalibrating = 0 - update_icon() - //--FalseIncarnate - return + if(target && !recalibrating && !panel_open && !blockAI(A)) + do_teleport(A, target) + use_power(5000) + if(tele_delay) + recalibrating = TRUE + update_icon() + addtimer(CALLBACK(src, .proc/BumpedCallback), tele_delay) + +/obj/machinery/teleport/perma/proc/BumpedCallback() + recalibrating = FALSE + update_icon() /obj/machinery/teleport/perma/power_change() ..() @@ -467,7 +503,7 @@ name = "station" desc = "The power control station for a bluespace teleporter." icon_state = "controller" - var/engaged = 0 + var/engaged = FALSE use_power = IDLE_POWER_USE idle_power_usage = 10 active_power_usage = 2000 @@ -568,7 +604,7 @@ /obj/machinery/teleport/station/attack_ai() - src.attack_hand() + attack_hand() /obj/machinery/teleport/station/attack_hand(mob/user) if(!panel_open) @@ -583,12 +619,12 @@ to_chat(user, "Close the hub's maintenance panel first.") return if(teleporter_console.target) - src.engaged = !src.engaged + engaged = !engaged use_power(5000) visible_message("Teleporter [engaged ? "" : "dis"]engaged!") else visible_message("No target detected.") - src.engaged = 0 + engaged = FALSE teleporter_hub.update_icon() if(istype(user)) add_fingerprint(user) diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm index cc9fc3e4666..18abeba7400 100644 --- a/code/game/machinery/transformer.dm +++ b/code/game/machinery/transformer.dm @@ -6,17 +6,45 @@ layer = MOB_LAYER+1 // Overhead anchored = 1 density = 1 - var/transform_dead = 0 - var/transform_standing = 0 - var/cooldown_duration = 600 // 1 minute - var/cooldown = 0 - var/robot_cell_charge = 5000 + /// TRUE if the factory can transform dead mobs. + var/transform_dead = TRUE + /// TRUE if the mob can be standing and still be transformed. + var/transform_standing = TRUE + /// Cooldown between each transformation, in deciseconds. + var/cooldown_duration = 1 MINUTES + /// If the factory is currently on cooldown from its last transformation. + var/is_on_cooldown = FALSE + /// The type of cell that newly created borgs get. + var/robot_cell_type = /obj/item/stock_parts/cell/high/plus + /// The direction that mobs must moving in to get transformed. var/acceptdir = EAST + /// The AI who placed this factory. + var/mob/living/silicon/ai/masterAI -/obj/machinery/transformer/New() - // On us - ..() - new /obj/machinery/conveyor/auto(loc, WEST) +/obj/machinery/transformer/Initialize(mapload, mob/living/silicon/ai/_ai = null) + . = ..() + if(_ai) + masterAI = _ai + initialize_belts() + +/// Used to create all of the belts the transformer will be using. All belts should be pushing `WEST`. +/obj/machinery/transformer/proc/initialize_belts() + var/turf/T = get_turf(src) + if(!T) + return + + // Belt under the factory. + new /obj/machinery/conveyor/auto(T, WEST) + + // Get the turf 1 tile to the EAST. + var/turf/east = locate(T.x + 1, T.y, T.z) + if(istype(east, /turf/simulated/floor)) + new /obj/machinery/conveyor/auto(east, WEST) + + // Get the turf 1 tile to the WEST. + var/turf/west = locate(T.x - 1, T.y, T.z) + if(istype(west, /turf/simulated/floor)) + new /obj/machinery/conveyor/auto(west, WEST) /obj/machinery/transformer/power_change() ..() @@ -24,7 +52,7 @@ /obj/machinery/transformer/update_icon() ..() - if(stat & (BROKEN|NOPOWER) || cooldown == 1) + if(is_on_cooldown || stat & (BROKEN|NOPOWER)) icon_state = "separator-AO0" else icon_state = initial(icon_state) @@ -35,120 +63,76 @@ C.setDir(newdir) acceptdir = turn(newdir, 180) -/obj/machinery/transformer/Bumped(var/atom/movable/AM) +/// Resets `is_on_cooldown` to `FALSE` and updates our icon. Used in a callback after the transformer does a transformation. +/obj/machinery/transformer/proc/reset_cooldown() + is_on_cooldown = FALSE + update_icon() - if(cooldown == 1) +/obj/machinery/transformer/Bumped(atom/movable/AM) + // They have to be human to be transformed. + if(is_on_cooldown || !ishuman(AM)) return - // Crossed didn't like people lying down. - if(ishuman(AM)) - // Only humans can enter from the west side, while lying down. - var/move_dir = get_dir(loc, AM.loc) - var/mob/living/carbon/human/H = AM - if((transform_standing || H.lying) && move_dir == acceptdir)// || move_dir == WEST) - AM.loc = src.loc - do_transform(AM) + var/mob/living/carbon/human/H = AM + var/move_dir = get_dir(loc, H.loc) -/obj/machinery/transformer/proc/do_transform(var/mob/living/carbon/human/H) - if(stat & (BROKEN|NOPOWER)) - return - if(cooldown == 1) + if((transform_standing || H.lying) && move_dir == acceptdir) + H.forceMove(drop_location()) + do_transform(H) + +/// Transforms a human mob into a cyborg, connects them to the malf AI which placed the factory. +/obj/machinery/transformer/proc/do_transform(mob/living/carbon/human/H) + if(is_on_cooldown || stat & (BROKEN|NOPOWER)) return if(!transform_dead && H.stat == DEAD) - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) return - playsound(src.loc, 'sound/items/welder.ogg', 50, 1) - H.emote("scream") // It is painful - H.adjustBruteLoss(max(0, 80 - H.getBruteLoss())) // Hurt the human, don't try to kill them though. - - // Sleep for a couple of ticks to allow the human to see the pain - sleep(5) - + playsound(loc, 'sound/items/welder.ogg', 50, 1) use_power(5000) // Use a lot of power. - var/mob/living/silicon/robot/R = H.Robotize(1) // Delete the items or they'll all pile up in a single tile and lag - - R.cell.maxcharge = robot_cell_charge - R.cell.charge = robot_cell_charge - - // So he can't jump out the gate right away. - R.lockcharge = !R.lockcharge - spawn(50) - playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) - sleep(30) - if(R) - R.lockcharge = !R.lockcharge - R.notify_ai(1) // Activate the cooldown - cooldown = 1 + is_on_cooldown = TRUE update_icon() - spawn(cooldown_duration) - cooldown = 0 - update_icon() - -/obj/machinery/transformer/conveyor/New() - ..() - var/turf/T = loc - if(T) - // Spawn Conveyour Belts - - //East - var/turf/east = locate(T.x + 1, T.y, T.z) - if(istype(east, /turf/simulated/floor)) - new /obj/machinery/conveyor/auto(east, WEST) - - // West - var/turf/west = locate(T.x - 1, T.y, T.z) - if(istype(west, /turf/simulated/floor)) - new /obj/machinery/conveyor/auto(west, WEST) + addtimer(CALLBACK(src, .proc/reset_cooldown), cooldown_duration) + addtimer(CALLBACK(null, .proc/playsound, loc, 'sound/machines/ping.ogg', 50, 0), 3 SECONDS) + H.emote("scream") + if(!masterAI) // If the factory was placed via admin spawning or other means, it wont have an owner_AI. + H.Robotize(robot_cell_type) + return + var/mob/living/silicon/robot/R = H.Robotize(robot_cell_type, FALSE, masterAI) + if(R.mind && !R.client && !R.grab_ghost()) // Make sure this is an actual player first and not just a humanized monkey or something. + message_admins("[key_name_admin(R)] was just transformed by a borg factory, but they were SSD. Polling ghosts for a replacement.") + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a malfunctioning cyborg?", ROLE_TRAITOR, poll_time = 15 SECONDS) + if(!length(candidates)) + return + var/mob/dead/observer/O = pick(candidates) + R.key= O.key /obj/machinery/transformer/mime name = "Mimetech Greyscaler" desc = "Turns anything placed inside black and white." - -/obj/machinery/transformer/mime/conveyor/New() - ..() - var/turf/T = loc - if(T) - // Spawn Conveyour Belts - - //East - var/turf/east = locate(T.x + 1, T.y, T.z) - if(istype(east, /turf/simulated/floor)) - new /obj/machinery/conveyor/auto(east, WEST) - - // West - var/turf/west = locate(T.x - 1, T.y, T.z) - if(istype(west, /turf/simulated/floor)) - new /obj/machinery/conveyor/auto(west, WEST) - -/obj/machinery/transformer/mime/Bumped(var/atom/movable/AM) - - if(cooldown == 1) +/obj/machinery/transformer/mime/Bumped(atom/movable/AM) + if(is_on_cooldown) return // Crossed didn't like people lying down. - if(isatom(AM)) - AM.loc = src.loc + if(istype(AM)) + AM.forceMove(drop_location()) do_transform_mime(AM) else to_chat(AM, "Only items can be greyscaled.") return -/obj/machinery/transformer/proc/do_transform_mime(var/obj/item/I) - if(stat & (BROKEN|NOPOWER)) - return - if(cooldown == 1) +/obj/machinery/transformer/proc/do_transform_mime(obj/item/I) + if(is_on_cooldown || stat & (BROKEN|NOPOWER)) return - playsound(src.loc, 'sound/items/welder.ogg', 50, 1) - // Sleep for a couple of ticks to allow the human to see the pain - sleep(5) + playsound(loc, 'sound/items/welder.ogg', 50, 1) use_power(5000) // Use a lot of power. var/icon/newicon = new(I.icon, I.icon_state) @@ -156,42 +140,27 @@ I.icon = newicon // Activate the cooldown - cooldown = 1 + is_on_cooldown = TRUE update_icon() - spawn(cooldown_duration) - cooldown = 0 - update_icon() + addtimer(CALLBACK(src, .proc/reset_cooldown), cooldown_duration) /obj/machinery/transformer/xray name = "Automatic X-Ray 5000" desc = "A large metalic machine with an entrance and an exit. A sign on the side reads, 'backpack go in, backpack come out', 'human go in, irradiated human come out'." + acceptdir = WEST -/obj/machinery/transformer/xray/Initialize(mapload) - . = ..() - // On us - new /obj/machinery/conveyor/auto(loc, EAST) - -/obj/machinery/transformer/xray/conveyor/New() - ..() - var/turf/T = loc +/obj/machinery/transformer/xray/initialize_belts() + var/turf/T = get_turf(src) if(T) - // Spawn Conveyour Belts + // This handles the belt under the transformer and 1 tile to the left and right. + . = ..() - //East - var/turf/east = locate(T.x + 1, T.y, T.z) - if(istype(east, /turf/simulated/floor)) - new /obj/machinery/conveyor/auto(east, EAST) - //East2 + // Get the turf 2 tiles to the EAST. var/turf/east2 = locate(T.x + 2, T.y, T.z) if(istype(east2, /turf/simulated/floor)) new /obj/machinery/conveyor/auto(east2, EAST) - // West - var/turf/west = locate(T.x - 1, T.y, T.z) - if(istype(west, /turf/simulated/floor)) - new /obj/machinery/conveyor/auto(west, EAST) - - // West2 + // Get the turf 2 tiles to the WEST. var/turf/west2 = locate(T.x - 2, T.y, T.z) if(istype(west2, /turf/simulated/floor)) new /obj/machinery/conveyor/auto(west2, EAST) @@ -207,30 +176,30 @@ else icon_state = initial(icon_state) -/obj/machinery/transformer/xray/Bumped(var/atom/movable/AM) - - if(cooldown == 1) +/obj/machinery/transformer/xray/Bumped(atom/movable/AM) + if(is_on_cooldown) return // Crossed didn't like people lying down. if(ishuman(AM)) // Only humans can enter from the west side, while lying down. - var/move_dir = get_dir(loc, AM.loc) var/mob/living/carbon/human/H = AM - if(H.lying && move_dir == WEST)// || move_dir == WEST) - AM.loc = src.loc - irradiate(AM) + var/move_dir = get_dir(loc, H.loc) - else if(isatom(AM)) - AM.loc = src.loc + if(H.lying && move_dir == acceptdir) + H.forceMove(drop_location()) + irradiate(H) + + else if(istype(AM)) + AM.forceMove(drop_location()) scan(AM) -/obj/machinery/transformer/xray/proc/irradiate(var/mob/living/carbon/human/H) +/obj/machinery/transformer/xray/proc/irradiate(mob/living/carbon/human/H) if(stat & (BROKEN|NOPOWER)) return flick("separator-AO0",src) - playsound(src.loc, 'sound/effects/alert.ogg', 50, 0) + playsound(loc, 'sound/effects/alert.ogg', 50, 0) sleep(5) H.apply_effect((rand(150,200)),IRRADIATE,0) if(prob(5)) @@ -242,15 +211,15 @@ domutcheck(H,null,1) -/obj/machinery/transformer/xray/proc/scan(var/obj/item/I) +/obj/machinery/transformer/xray/proc/scan(obj/item/I) if(scan_rec(I)) - playsound(src.loc, 'sound/effects/alert.ogg', 50, 0) + playsound(loc, 'sound/effects/alert.ogg', 50, 0) flick("separator-AO0",src) else - playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) + playsound(loc, 'sound/machines/ping.ogg', 50, 0) sleep(30) -/obj/machinery/transformer/xray/proc/scan_rec(var/obj/item/I) +/obj/machinery/transformer/xray/proc/scan_rec(obj/item/I) if(istype(I, /obj/item/gun)) return TRUE if(istype(I, /obj/item/transfer_valve)) diff --git a/code/game/machinery/turntable.dm b/code/game/machinery/turntable.dm deleted file mode 100644 index 70d7adff2be..00000000000 --- a/code/game/machinery/turntable.dm +++ /dev/null @@ -1,280 +0,0 @@ -/sound/turntable/test - file = 'sound/turntable/testloop1.ogg' - falloff = 2 - repeat = 1 - -/mob/var/music = 0 - -/obj/machinery/party/turntable - name = "turntable" - desc = "A turntable used for parties and shit." - icon = 'icons/effects/lasers2.dmi' - icon_state = "turntable" - var/playing = 0 - anchored = 1 - -/obj/machinery/party/mixer - name = "mixer" - desc = "A mixing board for mixing music" - icon = 'icons/effects/lasers2.dmi' - icon_state = "mixer" - anchored = 1 - - -/obj/machinery/party/turntable/New() - ..() - sleep(2) - new /sound/turntable/test(src) - return - -/obj/machinery/party/turntable/attack_hand(mob/user as mob) - - var/t = "Turntable Interface

    " - //t += "On
    " - t += "Off

    " - t += "One
    " - t += "TestLoop2
    " - t += "TestLoop3
    " - - user << browse(t, "window=turntable;size=420x700") - - -/obj/machinery/party/turntable/Topic(href, href_list) - ..() - if( href_list["on1"] ) - if(src.playing == 0) -// to_chat(world, "Should be working...") - var/sound/S = sound('sound/turntable/testloop1.ogg') - S.repeat = 1 - S.channel = 10 - S.falloff = 2 - S.wait = 1 - S.environment = 0 - //for(var/mob/M in world) - // if(M.loc.loc == src.loc.loc && M.music == 0) -// to_chat(world, "Found the song...") -// M << S - // M.music = 1 - var/area/A = src.loc.loc - - for(var/obj/machinery/party/lasermachine/L in A) - L.turnon() - playing = 1 - while(playing == 1) - for(var/mob/M in world) - if((M.loc.loc in A) && M.music == 0) -// to_chat(world, "Found the song...") - M << S - M.music = 1 - else if(!(M.loc.loc in A) && M.music == 1) - var/sound/Soff = sound(null) - Soff.channel = 10 - M << Soff - M.music = 0 - sleep(10) - return - if( href_list["on2"] ) - if(src.playing == 0) -// to_chat(world, "Should be working...") - var/sound/S = sound('sound/turntable/testloop2.ogg') - S.repeat = 1 - S.channel = 10 - S.falloff = 2 - S.wait = 1 - S.environment = 0 - //for(var/mob/M in world) - // if(M.loc.loc == src.loc.loc && M.music == 0) -// to_chat(world, "Found the song...") -// M << S - // M.music = 1 - var/area/A = src.loc.loc - for(var/obj/machinery/party/lasermachine/L in A) - L.turnon() - playing = 1 - while(playing == 1) - for(var/mob/M in world) - if(M.loc.loc == src.loc.loc && M.music == 0) -// to_chat(world, "Found the song...") - M << S - M.music = 1 - else if(M.loc.loc != src.loc.loc && M.music == 1) - var/sound/Soff = sound(null) - Soff.channel = 10 - M << Soff - M.music = 0 - sleep(10) - return - if( href_list["on3"] ) - if(src.playing == 0) -// to_chat(world, "Should be working...") - var/sound/S = sound('sound/turntable/testloop3.ogg') - S.repeat = 1 - S.channel = 10 - S.falloff = 2 - S.wait = 1 - S.environment = 0 - //for(var/mob/M in world) - // if(M.loc.loc == src.loc.loc && M.music == 0) -// to_chat(world, "Found the song...") -// M << S - // M.music = 1 - var/area/A = src.loc.loc - for(var/obj/machinery/party/lasermachine/L in A) - L.turnon() - playing = 1 - while(playing == 1) - for(var/mob/M in world) - if(M.loc.loc == src.loc.loc && M.music == 0) -// to_chat(world, "Found the song...") - M << S - M.music = 1 - else if(M.loc.loc != src.loc.loc && M.music == 1) - var/sound/Soff = sound(null) - Soff.channel = 10 - M << Soff - M.music = 0 - sleep(10) - return - - - if( href_list["off"] ) - if(src.playing == 1) - var/sound/S = sound(null) - S.channel = 10 - S.wait = 1 - for(var/mob/M in world) - M << S - M.music = 0 - playing = 0 - var/area/A = src.loc.loc - for(var/obj/machinery/party/lasermachine/L in A) - L.turnoff() - - - -/obj/machinery/party/lasermachine - name = "laser machine" - desc = "A laser machine that shoots lasers." - icon = 'icons/effects/lasers2.dmi' - icon_state = "lasermachine" - anchored = 1 - var/mirrored = 0 - -/obj/effect/turntable_laser - name = "laser" - desc = "A laser..." - icon = 'icons/effects/lasers2.dmi' - icon_state = "laserred1" - anchored = 1 - layer = 4 - -/obj/item/lasermachine/New() - ..() - -/obj/machinery/party/lasermachine/proc/turnon() - var/wall = 0 - var/cycle = 1 - var/area/A = get_area(src) - var/X = 1 - var/Y = 0 - if(mirrored == 0) - while(wall == 0) - if(cycle == 1) - var/obj/effect/turntable_laser/F = new(src) - F.x = src.x+X - F.y = src.y+Y - F.z = src.z - F.icon_state = "laserred1" - var/area/AA = get_area(F) - var/turf/T = get_turf(F) - if(T.density == 1 || AA.name != A.name) - qdel(F) - return - cycle++ - if(cycle > 3) - cycle = 1 - X++ - if(cycle == 2) - var/obj/effect/turntable_laser/F = new(src) - F.x = src.x+X - F.y = src.y+Y - F.z = src.z - F.icon_state = "laserred2" - var/area/AA = get_area(F) - var/turf/T = get_turf(F) - if(T.density == 1 || AA.name != A.name) - qdel(F) - return - cycle++ - if(cycle > 3) - cycle = 1 - Y++ - if(cycle == 3) - var/obj/effect/turntable_laser/F = new(src) - F.x = src.x+X - F.y = src.y+Y - F.z = src.z - F.icon_state = "laserred3" - var/area/AA = get_area(F) - var/turf/T = get_turf(F) - if(T.density == 1 || AA.name != A.name) - qdel(F) - return - cycle++ - if(cycle > 3) - cycle = 1 - X++ - if(mirrored == 1) - while(wall == 0) - if(cycle == 1) - var/obj/effect/turntable_laser/F = new(src) - F.x = src.x+X - F.y = src.y-Y - F.z = src.z - F.icon_state = "laserred1m" - var/area/AA = get_area(F) - var/turf/T = get_turf(F) - if(T.density == 1 || AA.name != A.name) - qdel(F) - return - cycle++ - if(cycle > 3) - cycle = 1 - Y++ - if(cycle == 2) - var/obj/effect/turntable_laser/F = new(src) - F.x = src.x+X - F.y = src.y-Y - F.z = src.z - F.icon_state = "laserred2m" - var/area/AA = get_area(F) - var/turf/T = get_turf(F) - if(T.density == 1 || AA.name != A.name) - qdel(F) - return - cycle++ - if(cycle > 3) - cycle = 1 - X++ - if(cycle == 3) - var/obj/effect/turntable_laser/F = new(src) - F.x = src.x+X - F.y = src.y-Y - F.z = src.z - F.icon_state = "laserred3m" - var/area/AA = get_area(F) - var/turf/T = get_turf(F) - if(T.density == 1 || AA.name != A.name) - qdel(F) - return - cycle++ - if(cycle > 3) - cycle = 1 - X++ - - - -/obj/machinery/party/lasermachine/proc/turnoff() - var/area/A = src.loc.loc - for(var/obj/effect/turntable_laser/F in A) - qdel(F) diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm index 9dda3b193bb..71cb58c6478 100644 --- a/code/game/machinery/turret_control.dm +++ b/code/game/machinery/turret_control.dm @@ -11,51 +11,56 @@ desc = "Used to control a room's automated defenses." icon = 'icons/obj/machines/turret_control.dmi' icon_state = "control_standby" - anchored = 1 - density = 0 + anchored = TRUE + density = FALSE resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/enabled = 0 - var/lethal = 0 - var/locked = 1 + var/enabled = FALSE + var/lethal = FALSE + var/lethal_is_configurable = TRUE + var/locked = TRUE var/area/control_area //can be area name, path or nothing. - var/check_arrest = 1 //checks if the perp is set to arrest - var/check_records = 1 //checks if a security record exists at all - var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have - var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements - var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos) - var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg - var/ailock = 0 //Silicons cannot use this + var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI + var/check_arrest = TRUE //checks if the perp is set to arrest + var/check_records = TRUE //checks if a security record exists at all + var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have + var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements + var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos) + var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg + var/check_borgs = FALSE //if TRUE, target all cyborgs. + var/ailock = FALSE //Silicons cannot use this - var/syndicate = 0 + var/syndicate = FALSE var/faction = "" // Turret controls can only access turrets that are in the same faction req_access = list(ACCESS_AI_UPLOAD) /obj/machinery/turretid/stun - enabled = 1 + enabled = TRUE icon_state = "control_stun" /obj/machinery/turretid/lethal - enabled = 1 - lethal = 1 + enabled = TRUE + lethal = TRUE icon_state = "control_kill" /obj/machinery/turretid/syndicate - enabled = 1 - lethal = 1 + enabled = TRUE + lethal = TRUE + lethal_is_configurable = FALSE + targetting_is_configurable = FALSE icon_state = "control_kill" - lethal = 1 - check_arrest = 0 - check_records = 0 - check_weapons = 0 - check_access = 0 - check_anomalies = 1 - check_synth = 1 - ailock = 1 + check_arrest = FALSE + check_records = FALSE + check_weapons = FALSE + check_access = FALSE + check_anomalies = TRUE + check_synth = TRUE + check_borgs = FALSE + ailock = TRUE - syndicate = 1 + syndicate = TRUE faction = "syndicate" req_access = list(ACCESS_SYNDICATE_LEADER) @@ -87,21 +92,23 @@ return /obj/machinery/turretid/proc/isLocked(mob/user) - if(ailock && (isrobot(user) || isAI(user))) - to_chat(user, "There seems to be a firewall preventing you from accessing this device.") - return 1 + if(isrobot(user) || isAI(user)) + if(ailock) + to_chat(user, "There seems to be a firewall preventing you from accessing this device.") + return TRUE + else + return FALSE - if(locked && !(isrobot(user) || isAI(user) || isobserver(user))) - to_chat(user, "Access denied.") - return 1 + if(isobserver(user)) + if(user.can_admin_interact()) + return FALSE + else + return TRUE - return 0 + if(locked) + return TRUE -/obj/machinery/turretid/CanUseTopic(mob/user) - if(isLocked(user)) - return STATUS_CLOSE - - return ..() + return FALSE /obj/machinery/turretid/attackby(obj/item/W, mob/user) if(stat & BROKEN) @@ -120,89 +127,82 @@ /obj/machinery/turretid/emag_act(user as mob) if(!emagged) to_chat(user, "You short out the turret controls' access analysis module.") - emagged = 1 - locked = 0 - ailock = 0 + emagged = TRUE + locked = FALSE + ailock = FALSE return /obj/machinery/turretid/attack_ai(mob/user as mob) - if(isLocked(user)) - return - - ui_interact(user) + tgui_interact(user) /obj/machinery/turretid/attack_ghost(mob/user as mob) - ui_interact(user) + tgui_interact(user) /obj/machinery/turretid/attack_hand(mob/user as mob) - if(isLocked(user)) - return + tgui_interact(user) - ui_interact(user) - -/obj/machinery/turretid/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/turretid/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 350) + ui = new(user, src, ui_key, "PortableTurret", name, 500, 400) ui.open() - ui.set_auto_update(1) - -/obj/machinery/turretid/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["access"] = !isLocked(user) - data["locked"] = locked - data["enabled"] = enabled - data["lethal_control"] = !syndicate ? 1 : 0 - data["lethal"] = lethal - - if(data["access"] && !syndicate) - var/settings[0] - settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth) - settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons) - settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records) - settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest) - settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access) - settings[++settings.len] = list("category" = "Check Misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies) - data["settings"] = settings +/obj/machinery/turretid/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, + "targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up + "lethal" = lethal, + "lethal_is_configurable" = lethal_is_configurable, + "check_weapons" = check_weapons, + "neutralize_noaccess" = check_access, + "one_access" = FALSE, + "selectedAccess" = list(), + "access_is_configurable" = FALSE, + "neutralize_norecord" = check_records, + "neutralize_criminals" = check_arrest, + "neutralize_all" = check_synth, + "neutralize_unidentified" = check_anomalies, + "neutralize_cyborgs" = check_borgs + ) return data -/obj/machinery/turretid/Topic(href, href_list, var/nowindow = 0) - if(..()) - return 1 - +/obj/machinery/turretid/tgui_act(action, params) + if (..()) + return if(isLocked(usr)) - return 1 - - if(href_list["command"] && href_list["value"]) - var/value = text2num(href_list["value"]) - if(href_list["command"] == "enable") - enabled = value - else if(syndicate) - return 1 - else if(href_list["command"] == "lethal") - lethal = value - else if(href_list["command"] == "check_synth") - check_synth = value - else if(href_list["command"] == "check_weapons") - check_weapons = value - else if(href_list["command"] == "check_records") - check_records = value - else if(href_list["command"] == "check_arrest") - check_arrest = value - else if(href_list["command"] == "check_access") - check_access = value - else if(href_list["command"] == "check_anomalies") - check_anomalies = value - - updateTurrets() - return 1 + return + . = TRUE + switch(action) + if("power") + enabled = !enabled + if("lethal") + if(lethal_is_configurable) + lethal = !lethal + if(targetting_is_configurable) + switch(action) + if("authweapon") + check_weapons = !check_weapons + if("authaccess") + check_access = !check_access + if("authnorecord") + check_records = !check_records + if("autharrest") + check_arrest = !check_arrest + if("authxeno") + check_anomalies = !check_anomalies + if("authsynth") + check_synth = !check_synth + if("authborgs") + check_borgs = !check_borgs + updateTurrets() /obj/machinery/turretid/proc/updateTurrets() var/datum/turret_checks/TC = new TC.enabled = enabled TC.lethal = lethal TC.check_synth = check_synth + TC.check_borgs = check_borgs TC.check_access = check_access TC.check_records = check_records TC.check_arrest = check_arrest diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 7aa5661febe..20fd93547eb 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -11,9 +11,6 @@ var/max_amount = 0 var/price = 0 // Price to buy one -/** - * A vending machine - */ /obj/machinery/vending name = "\improper Vendomat" desc = "A generic vending machine." @@ -25,8 +22,10 @@ max_integrity = 300 integrity_failure = 100 armor = list(melee = 20, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) - var/icon_vend //Icon_state when vending - var/icon_deny //Icon_state when denying access + /// Icon_state when vending + var/icon_vend + /// Icon_state when denying access + var/icon_deny // Power use_power = IDLE_POWER_USE @@ -34,12 +33,14 @@ var/vend_power_usage = 150 // Vending-related - var/active = 1 //No sales pitches if off! - var/vend_ready = 1 //Are we ready to vend?? Is it time?? - var/vend_delay = 10 //How long does it take to vend? - var/datum/data/vending_product/currently_vending = null // What we're requesting payment for right now - var/status_message = "" // Status screen messages like "insufficient funds", displayed in NanoUI - var/status_error = 0 // Set to 1 if status_message is an error + /// No sales pitches if off + var/active = 1 + /// If off, vendor is busy and unusable until current action finishes + var/vend_ready = TRUE + /// How long vendor takes to vend one item. + var/vend_delay = 10 + /// Item currently being bought + var/datum/data/vending_product/currently_vending = null // To be filled out at compile time var/list/products = list() // For each, use the following pattern: @@ -51,16 +52,19 @@ var/list/product_records = list() var/list/hidden_records = list() var/list/coin_records = list() + var/list/imagelist = list() - - var/list/ads_list = list() //Small ad messages in the vending screen - random chance, TODO: implementation + /// Unimplemented list of ads that are meant to show up somewhere, but don't. + var/list/ads_list = list() // Stuff relating vocalizations - var/list/slogan_list = list() //List of slogans the vendor will say, optional + /// List of slogans the vendor will say, optional + var/list/slogan_list = list() var/vend_reply //Thank you for shopping! - var/shut_up = 0 //Stop spouting those godawful pitches! + /// If true, prevent saying sales pitches + var/shut_up = FALSE ///can we access the hidden inventory? - var/extended_inventory = 0 + var/extended_inventory = FALSE var/last_reply = 0 var/last_slogan = 0 //When did we last pitch? var/slogan_delay = 6000 //How long until we can pitch again? @@ -69,17 +73,26 @@ var/obj/item/vending_refill/refill_canister = null // Things that can go wrong - emagged = 0 //Ignores if somebody doesn't have card access to that machine. - var/seconds_electrified = 0 //Shock customers like an airlock. - var/shoot_inventory = 0 //Fire items at customers! We're broken! - var/shoot_speed = 3 //How hard are we firing the items? - var/shoot_chance = 2 //How often are we firing the items? + /// Allows people to access a vendor that's normally access restricted. + emagged = 0 + /// Shocks people like an airlock + var/seconds_electrified = 0 + /// Fire items at customers! We're broken! + var/shoot_inventory = FALSE + /// How hard are we firing the items? + var/shoot_speed = 3 + /// How often are we firing the items? (prob(...)) + var/shoot_chance = 2 - var/scan_id = 1 + /// If true, enforce access checks on customers. Disabled by messing with wires. + var/scan_id = TRUE + /// Holder for a coin inserted into the vendor var/obj/item/coin/coin var/datum/wires/vending/wires = null + /// boolean, whether this vending machine can accept people inserting items into it, used for coffee vendors var/item_slot = FALSE + /// the actual item inserted var/obj/item/inserted_item = null /obj/machinery/vending/Initialize(mapload) @@ -92,6 +105,10 @@ build_inventory(products, product_records) build_inventory(contraband, hidden_records) build_inventory(premium, coin_records) + for (var/datum/data/vending_product/R in (product_records + coin_records + hidden_records)) + var/obj/item/I = R.product_path + var/pp = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-") + imagelist[pp] = "[icon2base64(icon(initial(I.icon), initial(I.icon_state)))]" if(LAZYLEN(slogan_list)) // So not all machines speak at the exact same time. // The first time this machine says something will be at slogantime + this random value, @@ -101,6 +118,7 @@ power_change() /obj/machinery/vending/Destroy() + SStgui.close_uis(wires) QDEL_NULL(wires) QDEL_NULL(coin) QDEL_NULL(inserted_item) @@ -213,37 +231,16 @@ ..() /obj/machinery/vending/attackby(obj/item/I, mob/user, params) - if(currently_vending && GLOB.vendor_account && !GLOB.vendor_account.suspended) - var/paid = 0 - var/handled = 0 - if(istype(I, /obj/item/card/id)) - var/obj/item/card/id/C = I - paid = pay_with_card(C) - handled = 1 - if(istype(I, /obj/item/pda)) - var/obj/item/pda/PDA = I - if(PDA.id) - paid = pay_with_card(PDA.id) - handled = 1 - else if(istype(I, /obj/item/stack/spacecash)) - var/obj/item/stack/spacecash/C = I - paid = pay_with_cash(C, user) - handled = 1 - - if(paid) - vend(currently_vending, usr) + if(istype(I, /obj/item/coin)) + if(!premium.len) + to_chat(user, "[src] does not accept coins.") return - else if(handled) - SSnanoui.update_uis(src) - return // don't smack that machine with your 2 thalers - - if(istype(I, /obj/item/coin) && premium.len) if(!user.drop_item()) return I.forceMove(src) coin = I to_chat(user, "You insert the [I] into the [src]") - SSnanoui.update_uis(src) + SStgui.update_uis(src) return if(refill_canister && istype(I, refill_canister)) if(!panel_open) @@ -294,7 +291,7 @@ else SCREWDRIVER_CLOSE_PANEL_MESSAGE overlays.Cut() - SSnanoui.update_uis(src) // Speaker switch is on the main UI, not wires UI + SStgui.update_uis(src) /obj/machinery/vending/wirecutter_act(mob/user, obj/item/I) . = TRUE @@ -358,12 +355,10 @@ if(!user.drop_item()) to_chat(user, "[I] is stuck to your hand, you can't seem to put it down!") return - inserted_item = I I.forceMove(src) - to_chat(user, "You insert [I] into [src].") - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/machinery/vending/proc/eject_item(mob/user) if(!item_slot || !inserted_item) @@ -376,23 +371,19 @@ var/turf/T = get_turf(src) inserted_item.forceMove(T) inserted_item = null - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/machinery/vending/emag_act(user as mob) emagged = TRUE to_chat(user, "You short out the product lock on [src]") -/** - * Receive payment with cashmoney. - * - * usr is the mob who gets the change. - */ + /obj/machinery/vending/proc/pay_with_cash(obj/item/stack/spacecash/cashmoney, mob/user) if(currently_vending.price > cashmoney.amount) // This is not a status display message, since it's something the character // themselves is meant to see BEFORE putting the money in to_chat(usr, "[bicon(cashmoney)] That is not enough money.") - return 0 + return FALSE // Bills (banknotes) cannot really have worth different than face value, // so we have to eat the bill and spit out change in a bundle @@ -403,66 +394,38 @@ cashmoney.use(currently_vending.price) // Vending machines have no idea who paid with cash - credit_purchase("(cash)") - return 1 + GLOB.vendor_account.credit(currently_vending.price, "Sale of [currently_vending.name]", name, "(cash)") + return TRUE -/** - * Scan a card and attempt to transfer payment from associated account. - * - * Takes payment for whatever is the currently_vending item. Returns 1 if - * successful, 0 if failed - */ -/obj/machinery/vending/proc/pay_with_card(var/obj/item/card/id/I) - visible_message("[usr] swipes a card through [src].") - return pay_with_account(get_card_account(I)) -/obj/machinery/vending/proc/pay_with_account(var/datum/money_account/customer_account) +/obj/machinery/vending/proc/pay_with_card(obj/item/card/id/I, mob/M) + visible_message("[M] swipes a card through [src].") + return pay_with_account(get_card_account(I), M) + +/obj/machinery/vending/proc/pay_with_account(datum/money_account/customer_account, mob/M) if(!customer_account) - src.status_message = "Error: Unable to access account. Please contact technical support if problem persists." - src.status_error = 1 - return 0 - + to_chat(M, "Error: Unable to access account. Please contact technical support if problem persists.") + return FALSE if(customer_account.suspended) - src.status_message = "Unable to access account: account suspended." - src.status_error = 1 - return 0 - - // Have the customer punch in the PIN before checking if there's enough money. Prevents people from figuring out acct is - // empty at high security levels - if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) + to_chat(M, "Unable to access account: account suspended.") + return FALSE + // Have the customer punch in the PIN before checking if there's enough money. + // Prevents people from figuring out acct is empty at high security levels + if(customer_account.security_level != 0) + // If card requires pin authentication (ie seclevel 1 or 2) var/attempt_pin = input("Enter pin code", "Vendor transaction") as num - if(!attempt_account_access(customer_account.account_number, attempt_pin, 2)) - src.status_message = "Unable to access account: incorrect credentials." - src.status_error = 1 - return 0 - + to_chat(M, "Unable to access account: incorrect credentials.") + return FALSE if(currently_vending.price > customer_account.money) - src.status_message = "Insufficient funds in account." - src.status_error = 1 - return 0 - else - // Okay to move the money at this point - var/paid = customer_account.charge(currently_vending.price, GLOB.vendor_account, - "Purchase of [currently_vending.name]", name, GLOB.vendor_account.owner_name, - "Sale of [currently_vending.name]", customer_account.owner_name) + to_chat(M, "Your bank account has insufficient money to purchase this.") + return FALSE + // Okay to move the money at this point + customer_account.charge(currently_vending.price, GLOB.vendor_account, + "Purchase of [currently_vending.name]", name, GLOB.vendor_account.owner_name, + "Sale of [currently_vending.name]", customer_account.owner_name) + return TRUE - if(paid) - // Give the vendor the money. We use the account owner name, which means - // that purchases made with stolen/borrowed card will look like the card - // owner made them - credit_purchase(customer_account.owner_name) - return paid - -/** - * Add money for current purchase to the vendor account. - * - * Called after the money has already been taken from the customer. - */ -/obj/machinery/vending/proc/credit_purchase(var/target as text) - GLOB.vendor_account.money += currently_vending.price - GLOB.vendor_account.credit(currently_vending.price, "Sale of [currently_vending.name]", - name, target) /obj/machinery/vending/attack_ai(mob/user) return attack_hand(user) @@ -478,174 +441,238 @@ if(src.shock(user, 100)) return - ui_interact(user) + tgui_interact(user) wires.Interact(user) -/** - * Display the NanoUI window for the vending machine. - * - * See NanoUI documentation for details. - */ -/obj/machinery/vending/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/vending/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "vending_machine.tmpl", src.name, 440, 600) + var/estimated_height = 100 + min(length(product_records) * 34, 500) + if(length(prices) > 0) + estimated_height += 100 // to account for the "current user" interface + ui = new(user, src, ui_key, "Vending", name, 470, estimated_height, master_ui, state) ui.open() -/obj/machinery/vending/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/vending/tgui_data(mob/user) var/list/data = list() - if(currently_vending) - data["mode"] = 1 - data["product"] = sanitize(currently_vending.name) - data["price"] = currently_vending.price - data["message_err"] = 0 - data["message"] = src.status_message - data["message_err"] = src.status_error - else - data["mode"] = 0 - var/list/listed_products = list() - - var/list/display_records = product_records + coin_records - if(extended_inventory) - display_records = product_records + coin_records + hidden_records - - for(var/key = 1 to display_records.len) - var/datum/data/vending_product/I = display_records[key] - - if(coin_records.Find(I) && !coin) - continue - - if(hidden_records.Find(I) && !extended_inventory) - continue - - listed_products.Add(list(list( - "key" = key, - "name" = sanitize(I.name), - "price" = I.price, - "amount" = I.amount))) - - data["products"] = listed_products - - if(coin) - data["coin"] = coin.name - - if(item_slot) - data["item_slot"] = 1 - if(inserted_item) - data["inserted_item"] = inserted_item - else - data["inserted_item"] = null - else - data["item_slot"] = 0 - - if(panel_open) - data["panel"] = 1 - data["speaker"] = shut_up ? 0 : 1 - else - data["panel"] = 0 + var/mob/living/carbon/human/H + var/obj/item/card/id/C + data["guestNotice"] = "No valid ID card detected. Wear your ID, or present cash."; + data["userMoney"] = 0 + data["user"] = null + if(ishuman(user)) + H = user + C = H.get_idcard(TRUE) + var/obj/item/stack/spacecash/S = H.get_active_hand() + if(istype(S)) + data["userMoney"] = S.amount + data["guestNotice"] = "Accepting Cash. You have: [S.amount] credits." + else if(istype(C)) + var/datum/money_account/A = get_card_account(C) + if(istype(A)) + data["user"] = list() + data["user"]["name"] = A.owner_name + data["userMoney"] = A.money + data["user"]["job"] = (istype(C) && C.rank) ? C.rank : "No Job" + else + data["guestNotice"] = "Unlinked ID detected. Present cash to pay."; + data["stock"] = list() + for (var/datum/data/vending_product/R in product_records + coin_records + hidden_records) + data["stock"][R.name] = R.amount + data["extended_inventory"] = extended_inventory + data["vend_ready"] = vend_ready + data["coin_name"] = coin ? coin.name : FALSE + data["panel_open"] = panel_open ? TRUE : FALSE + data["speaker"] = shut_up ? FALSE : TRUE + data["item_slot"] = item_slot // boolean + data["inserted_item_name"] = inserted_item ? inserted_item.name : FALSE return data -/obj/machinery/vending/Topic(href, href_list) - if(..()) - return 1 - if(href_list["remove_coin"] && !istype(usr,/mob/living/silicon)) - if(!coin) - to_chat(usr, "There is no coin in this machine.") - return +/obj/machinery/vending/tgui_static_data(mob/user) + var/list/data = list() + data["chargesMoney"] = length(prices) > 0 ? TRUE : FALSE + data["product_records"] = list() + var/i = 1 + for (var/datum/data/vending_product/R in product_records) + var/list/data_pr = list( + path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), + name = R.name, + price = (R.product_path in prices) ? prices[R.product_path] : 0, + max_amount = R.max_amount, + req_coin = FALSE, + is_hidden = FALSE, + inum = i + ) + data["product_records"] += list(data_pr) + i++ + data["coin_records"] = list() + for (var/datum/data/vending_product/R in coin_records) + var/list/data_cr = list( + path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), + name = R.name, + price = (R.product_path in prices) ? prices[R.product_path] : 0, + max_amount = R.max_amount, + req_coin = TRUE, + is_hidden = FALSE, + inum = i, + premium = TRUE + ) + data["coin_records"] += list(data_cr) + i++ + data["hidden_records"] = list() + for (var/datum/data/vending_product/R in hidden_records) + var/list/data_hr = list( + path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), + name = R.name, + price = (R.product_path in prices) ? prices[R.product_path] : 0, + max_amount = R.max_amount, + req_coin = FALSE, + is_hidden = TRUE, + inum = i, + premium = TRUE + ) + data["hidden_records"] += list(data_hr) + i++ + data["imagelist"] = imagelist + return data - usr.put_in_hands(coin) - coin = null - to_chat(usr, "You remove [coin] from [src].") +/obj/machinery/vending/tgui_act(action, params) + . = ..() + if(.) + return + if(issilicon(usr) && !isrobot(usr)) + to_chat(usr, "The vending machine refuses to interface with you, as you are not in its target demographic!") + return + switch(action) + if("toggle_voice") + if(panel_open) + shut_up = !shut_up + . = TRUE + if("eject_item") + eject_item(usr) + . = TRUE + if("remove_coin") + if(!coin) + to_chat(usr, "There is no coin in this machine.") + return + if(istype(usr, /mob/living/silicon)) + to_chat(usr, "You lack hands.") + return + to_chat(usr, "You remove [coin] from [src].") + usr.put_in_hands(coin) + coin = null + . = TRUE + if("vend") + if(!vend_ready) + to_chat(usr, "The vending machine is busy!") + return + if(panel_open) + to_chat(usr, "The vending machine cannot dispense products while its service panel is open!") + return + var/key = text2num(params["inum"]) + var/list/display_records = product_records + coin_records + if(extended_inventory) + display_records = product_records + coin_records + hidden_records + if(key < 1 || key > length(display_records)) + to_chat(usr, "ERROR: invalid inum passed to vendor. Report this bug.") + return + var/datum/data/vending_product/R = display_records[key] + if(!istype(R)) + to_chat(usr, "ERROR: unknown vending_product record. Report this bug.") + return + var/list/record_to_check = product_records + coin_records + if(extended_inventory) + record_to_check = product_records + coin_records + hidden_records + if(!R || !istype(R) || !R.product_path) + to_chat(usr, "ERROR: unknown product record. Report this bug.") + return + if(R in hidden_records) + if(!extended_inventory) + // Exploit prevention, stop the user purchasing hidden stuff if they haven't hacked the machine. + to_chat(usr, "ERROR: machine does not allow extended_inventory in current state. Report this bug.") + return + else if (!(R in record_to_check)) + // Exploit prevention, stop the user + message_admins("Vending machine exploit attempted by [ADMIN_LOOKUPFLW(usr)]!") + return + if (R.amount <= 0) + to_chat(usr, "Sold out of [R.name].") + flick(icon_deny, src) + return - if(href_list["remove_item"]) - eject_item(usr) + vend_ready = FALSE // From this point onwards, vendor is locked to performing this transaction only, until it is resolved. - if(href_list["pay"]) - if(currently_vending && GLOB.vendor_account && !GLOB.vendor_account.suspended) - var/paid = 0 - var/handled = 0 - var/datum/money_account/A = usr.get_worn_id_account() - if(A) - paid = pay_with_account(A) - handled = 1 - else if(istype(usr.get_active_hand(), /obj/item/card)) - paid = pay_with_card(usr.get_active_hand()) - handled = 1 - else if(usr.can_admin_interact()) - paid = 1 - handled = 1 + if(!ishuman(usr) || R.price <= 0) + // Either the purchaser is not human, or the item is free. + // Skip all payment logic. + vend(R, usr) + add_fingerprint(usr) + vend_ready = TRUE + . = TRUE + return + + // --- THE REST OF THIS PROC IS JUST PAYMENT LOGIC --- + + var/mob/living/carbon/human/H = usr + var/obj/item/card/id/C = H.get_idcard(TRUE) + + if(!GLOB.vendor_account || GLOB.vendor_account.suspended) + to_chat(usr, "Vendor account offline. Unable to process transaction.") + flick(icon_deny, src) + vend_ready = TRUE + return + + currently_vending = R + var/paid = FALSE + + if(istype(usr.get_active_hand(), /obj/item/stack/spacecash)) + var/obj/item/stack/spacecash/S = usr.get_active_hand() + paid = pay_with_cash(S) + else if(istype(C, /obj/item/card)) + // Because this uses H.get_idcard(TRUE), it will attempt to use: + // active hand, inactive hand, pda.id, and then wear_id ID in that order + // this is important because it lets people buy stuff with someone else's ID by holding it while using the vendor + paid = pay_with_card(C, usr) + else if(usr.can_advanced_admin_interact()) + to_chat(usr, "Vending object due to admin interaction.") + paid = TRUE + else + to_chat(usr, "Payment failure: you have no ID or other method of payment.") + vend_ready = TRUE + flick(icon_deny, src) + . = TRUE // we set this because they shouldn't even be able to get this far, and we want the UI to update. + return if(paid) vend(currently_vending, usr) - return - else if(handled) - SSnanoui.update_uis(src) - return // don't smack that machine with your 2 credits - - if((href_list["vend"]) && vend_ready && !currently_vending) - - if(issilicon(usr) && !isrobot(usr)) - to_chat(usr, "The vending machine refuses to interface with you, as you are not in its target demographic!") - return - - if(!allowed(usr) && !usr.can_admin_interact() && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH - to_chat(usr, "Access denied.") //Unless emagged of course - flick(icon_deny,src) - return - - var/key = text2num(href_list["vend"]) - var/list/display_records = product_records + coin_records - if(extended_inventory) - display_records = product_records + coin_records + hidden_records - var/datum/data/vending_product/R = display_records[key] - - // This should not happen unless the request from NanoUI was bad - if(coin_records.Find(R) && !coin) - return - - if(hidden_records.Find(R) && !extended_inventory) - return - - if(R.price <= 0) - vend(R, usr) - else - currently_vending = R - if(!GLOB.vendor_account || GLOB.vendor_account.suspended) - status_message = "This machine is currently unable to process payments due to problems with the associated account." - status_error = 1 + . = TRUE else - status_message = "Please swipe a card or insert cash to pay for the item." - status_error = 0 + to_chat(usr, "Payment failure: unable to process payment.") + vend_ready = TRUE + if(.) + add_fingerprint(usr) - else if(href_list["cancelpurchase"]) - currently_vending = null - else if(href_list["togglevoice"] && panel_open) - shut_up = !src.shut_up - add_fingerprint(usr) - SSnanoui.update_uis(src) /obj/machinery/vending/proc/vend(datum/data/vending_product/R, mob/user) - if(!allowed(usr) && !usr.can_admin_interact() && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH - to_chat(usr, "Access denied.")//Unless emagged of course - flick(icon_deny,src) + if(!allowed(user) && !user.can_admin_interact() && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH + to_chat(user, "Access denied.")//Unless emagged of course + flick(icon_deny, src) + vend_ready = TRUE return if(!R.amount) to_chat(user, "The vending machine has ran out of that product.") + vend_ready = TRUE return - vend_ready = 0 //One thing at a time!! - status_message = "Vending..." - status_error = 0 - SSnanoui.update_uis(src) + vend_ready = FALSE //One thing at a time!! if(coin_records.Find(R)) if(!coin) to_chat(user, "You need to insert a coin to get this item.") + vend_ready = TRUE return if(coin.string_attached) if(prob(50)) @@ -665,15 +692,13 @@ use_power(vend_power_usage) //actuators and stuff if(icon_vend) //Show the vending animation if needed flick(icon_vend, src) + playsound(get_turf(src), 'sound/machines/machine_vend.ogg', 50, TRUE) addtimer(CALLBACK(src, .proc/delayed_vend, R, user), vend_delay) /obj/machinery/vending/proc/delayed_vend(datum/data/vending_product/R, mob/user) do_vend(R, user) - status_message = "" - status_error = 0 - vend_ready = 1 + vend_ready = TRUE currently_vending = null - SSnanoui.update_uis(src) //override this proc to add handling for what to do with the vended product when you have a inserted item and remember to include a parent call for this generic handling /obj/machinery/vending/proc/do_vend(datum/data/vending_product/R, mob/user) @@ -809,17 +834,6 @@ */ -/* -/obj/machinery/vending/atmospherics //Commenting this out until someone ponies up some actual working, broken, and unpowered sprites - Quarxink - name = "\improper Tank Vendor" - desc = "A vendor with a wide variety of masks and gas tanks." - icon = 'icons/obj/objects.dmi' - icon_state = "dispenser" - product_paths = "/obj/item/tank/oxygen;/obj/item/tank/plasma;/obj/item/tank/emergency_oxygen;/obj/item/tank/emergency_oxygen/engi;/obj/item/clothing/mask/breath" - product_amounts = "10;10;10;5;25" - vend_delay = 0 -*/ - /obj/machinery/vending/assist products = list( /obj/item/assembly/prox_sensor = 5,/obj/item/assembly/igniter = 3,/obj/item/assembly/signaler = 4, /obj/item/wirecutters = 1, /obj/item/cartridge/signal = 4) @@ -1258,7 +1272,7 @@ ads_list = list("We like plants!","Don't you want some?","The greenest thumbs ever.","We like big plants.","Soft soil...") icon_state = "nutri" icon_deny = "nutri-deny" - products = list(/obj/item/reagent_containers/glass/bottle/nutrient/ez = 30,/obj/item/reagent_containers/glass/bottle/nutrient/l4z = 20,/obj/item/reagent_containers/glass/bottle/nutrient/rh = 10,/obj/item/reagent_containers/spray/pestspray = 20, + products = list(/obj/item/reagent_containers/glass/bottle/nutrient/ez = 20,/obj/item/reagent_containers/glass/bottle/nutrient/l4z = 13,/obj/item/reagent_containers/glass/bottle/nutrient/rh = 6,/obj/item/reagent_containers/spray/pestspray = 20, /obj/item/reagent_containers/syringe = 5,/obj/item/storage/bag/plants = 5,/obj/item/cultivator = 3,/obj/item/shovel/spade = 3,/obj/item/plant_analyzer = 4) contraband = list(/obj/item/reagent_containers/glass/bottle/ammonia = 10,/obj/item/reagent_containers/glass/bottle/diethylamine = 5) refill_canister = /obj/item/vending_refill/hydronutrients @@ -1385,7 +1399,6 @@ /obj/item/clothing/glasses/gglasses = 1, /obj/item/clothing/shoes/jackboots = 1, /obj/item/clothing/under/schoolgirl = 1, - /obj/item/clothing/head/kitty = 1, /obj/item/clothing/under/blackskirt = 1, /obj/item/clothing/suit/toggle/owlwings = 1, /obj/item/clothing/under/owl = 1, @@ -1480,6 +1493,7 @@ /obj/item/clothing/under/victsuit/redblk = 1, /obj/item/clothing/under/victsuit/red = 1, /obj/item/clothing/suit/tailcoat = 1, + /obj/item/clothing/under/tourist_suit = 1, /obj/item/clothing/suit/draculacoat = 1, /obj/item/clothing/head/zepelli = 1, /obj/item/clothing/under/redhawaiianshirt = 1, @@ -1566,7 +1580,6 @@ desc = "Tools for tools." icon_state = "tool" icon_deny = "tool-deny" - //req_access_txt = "12" //Maintenance access products = list(/obj/item/stack/cable_coil/random = 10,/obj/item/crowbar = 5,/obj/item/weldingtool = 3,/obj/item/wirecutters = 5, /obj/item/wrench = 5,/obj/item/analyzer = 5,/obj/item/t_scanner = 5,/obj/item/screwdriver = 5) contraband = list(/obj/item/weldingtool/hugetank = 2,/obj/item/clothing/gloves/color/fyellow = 2) @@ -1894,46 +1907,3 @@ component_parts += new /obj/item/vending_refill/crittercare(null) RefreshParts() return ..() - -/obj/machinery/vending/modularpc - name = "\improper Deluxe Silicate Selections" - desc = "All the parts you need to build your own custom pc." - icon_state = "modularpc" - icon_deny = "modularpc-deny" - ads_list = list("Get your gamer gear!","The best GPUs for all of your space-crypto needs!","The most robust cooling!","The finest RGB in space!") - vend_reply = "Game on!" - products = list(/obj/item/modular_computer/laptop = 4, - /obj/item/modular_computer/tablet = 4, - /obj/item/computer_hardware/hard_drive = 4, - /obj/item/computer_hardware/hard_drive/small = 4, - /obj/item/computer_hardware/network_card = 8, - /obj/item/computer_hardware/hard_drive/portable = 8, - /obj/item/computer_hardware/battery = 8, - /obj/item/stock_parts/cell/computer = 8, - /obj/item/computer_hardware/processor_unit = 4, - /obj/item/computer_hardware/processor_unit/small = 4) - premium = list(/obj/item/computer_hardware/card_slot = 2, - /obj/item/computer_hardware/ai_slot = 2, - /obj/item/computer_hardware/printer/mini = 2, - /obj/item/computer_hardware/recharger/APC = 2, - /obj/item/paicard = 2) - prices = list(/obj/item/modular_computer/laptop = 300, - /obj/item/modular_computer/tablet = 300, - /obj/item/computer_hardware/hard_drive = 100, - /obj/item/computer_hardware/hard_drive/small = 50, - /obj/item/computer_hardware/network_card = 100, - /obj/item/computer_hardware/hard_drive/portable = 100, - /obj/item/computer_hardware/battery = 100, - /obj/item/stock_parts/cell/computer = 100, - /obj/item/computer_hardware/processor_unit = 100, - /obj/item/computer_hardware/processor_unit/small = 100) - refill_canister = /obj/item/vending_refill/modularpc - -/obj/machinery/vending/modularpc/Initialize(mapload) - component_parts = list() - var/obj/item/circuitboard/vendor/V = new(null) - V.set_type(type) - component_parts += V - component_parts += new /obj/item/vending_refill/modularpc(null) - RefreshParts() - return ..() diff --git a/code/game/mecha/combat/honker.dm b/code/game/mecha/combat/honker.dm index 1bb73856f1b..eb7bfad8942 100644 --- a/code/game/mecha/combat/honker.dm +++ b/code/game/mecha/combat/honker.dm @@ -137,7 +137,7 @@ squeak = 0 return result -obj/mecha/combat/honker/Topic(href, href_list) +/obj/mecha/combat/honker/Topic(href, href_list) ..() if(href_list["play_sound"]) switch(href_list["play_sound"]) diff --git a/code/game/mecha/equipment/tools/mining_tools.dm b/code/game/mecha/equipment/tools/mining_tools.dm index 5916d0f96c7..2248c37f518 100644 --- a/code/game/mecha/equipment/tools/mining_tools.dm +++ b/code/game/mecha/equipment/tools/mining_tools.dm @@ -94,7 +94,7 @@ /obj/item/mecha_parts/mecha_equipment/drill/proc/drill_mob(mob/living/target, mob/user) target.visible_message("[chassis] is drilling [target] with [src]!", "[chassis] is drilling you with [src]!") - add_attack_logs(user, target, "DRILLED with [src] (INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])") + add_attack_logs(user, target, "DRILLED with [src] ([uppertext(user.a_intent)]) ([uppertext(damtype)])") if(target.stat == DEAD && target.getBruteLoss() >= 200) add_attack_logs(user, target, "gibbed") if(LAZYLEN(target.butcher_results)) diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm index 61ba683ad18..8fdbf0880df 100644 --- a/code/game/mecha/equipment/tools/other_tools.dm +++ b/code/game/mecha/equipment/tools/other_tools.dm @@ -87,11 +87,15 @@ energy_drain = 100 range = MECHA_MELEE | MECHA_RANGED var/atom/movable/locked + var/cooldown_timer = 0 var/mode = 1 //1 - gravsling 2 - gravpush /obj/item/mecha_parts/mecha_equipment/gravcatapult/action(atom/movable/target) if(!action_checks(target)) return + if(cooldown_timer > world.time) + occupant_message("[src] is still recharging.") + return switch(mode) if(1) if(!locked) @@ -106,6 +110,7 @@ locked.throw_at(target, 14, 1.5) locked = null send_byjax(chassis.occupant,"exosuit.browser","\ref[src]",get_equip_info()) + cooldown_timer = world.time + 3 SECONDS return 1 else locked = null @@ -125,6 +130,7 @@ step_away(A,target) sleep(2) var/turf/T = get_turf(target) + cooldown_timer = world.time + 3 SECONDS log_game("[key_name(chassis.occupant)] used a Gravitational Catapult in ([T.x],[T.y],[T.z])") return 1 diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm index 63eeb21e165..5962a1eccbb 100644 --- a/code/game/mecha/equipment/tools/work_tools.dm +++ b/code/game/mecha/equipment/tools/work_tools.dm @@ -60,7 +60,7 @@ target.visible_message("[chassis] squeezes [target].", \ "[chassis] squeezes [target].",\ "You hear something crack.") - add_attack_logs(chassis.occupant, M, "Squeezed with [src] (INTENT: [uppertext(chassis.occupant.a_intent)]) (DAMTYE: [uppertext(damtype)])") + add_attack_logs(chassis.occupant, M, "Squeezed with [src] ([uppertext(chassis.occupant.a_intent)]) ([uppertext(damtype)])") start_cooldown() else step_away(M,chassis) diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 9acdcba48de..e8db08bf37c 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -59,9 +59,7 @@ sleep(max(0, projectile_delay)) set_ready_state(0) log_message("Fired from [name], targeting [target].") - var/turf/T = get_turf(src) - msg_admin_attack("[key_name_admin(chassis.occupant)] fired a [src] in ([T.x], [T.y], [T.z] - [ADMIN_JMP(T)])") - log_game("[key_name(chassis.occupant)] fired a [src] in [T.x], [T.y], [T.z]") + add_attack_logs(chassis.occupant, target, "fired a [src]") do_after_cooldown() return @@ -209,7 +207,7 @@ for(var/mob/living/carbon/M in ohearers(6, chassis)) if(istype(M, /mob/living/carbon/human)) var/mob/living/carbon/human/H = M - if(istype(H.l_ear, /obj/item/clothing/ears/earmuffs) || istype(H.r_ear, /obj/item/clothing/ears/earmuffs)) + if(H.check_ear_prot() >= HEARING_PROTECTION_TOTAL) continue to_chat(M, "HONK") M.SetSleeping(0) @@ -238,7 +236,7 @@ chassis.use_power(energy_drain) log_message("Honked from [name]. HONK!") var/turf/T = get_turf(src) - msg_admin_attack("[key_name_admin(chassis.occupant)] used a Mecha Honker in ([T.x], [T.y], [T.z] - [ADMIN_JMP(T)])") + add_attack_logs(chassis.occupant, target, "used a Mecha Honker", ATKLOG_MOST) log_game("[key_name(chassis.occupant)] used a Mecha Honker in [T.x], [T.y], [T.z]") do_after_cooldown() return @@ -357,7 +355,7 @@ projectiles-- log_message("Fired from [name], targeting [target].") var/turf/T = get_turf(src) - msg_admin_attack("[key_name_admin(chassis.occupant)] fired a [src] in ([T.x], [T.y], [T.z] - [ADMIN_JMP(T)])") + add_attack_logs(chassis.occupant, target, "fired a [src]", ATKLOG_FEW) log_game("[key_name(chassis.occupant)] fired a [src] in [T.x], [T.y], [T.z]") do_after_cooldown() return @@ -385,7 +383,7 @@ return /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang - name = "SGL-6 Grenade Launcher" + name = "SGL-6 Flashbang Launcher" icon_state = "mecha_grenadelnchr" origin_tech = "combat=4;engineering=4" projectile = /obj/item/grenade/flashbang @@ -411,7 +409,7 @@ return /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang/clusterbang//Because I am a heartless bastard -Sieve - name = "SOB-3 Grenade Launcher" + name = "SOB-3 Clusterbang Launcher" desc = "A weapon for combat exosuits. Launches primed clusterbangs. You monster." origin_tech = "combat=4;materials=4" projectiles = 3 diff --git a/code/game/mecha/mech_bay.dm b/code/game/mecha/mech_bay.dm index 896ce886887..34e8648c59e 100644 --- a/code/game/mecha/mech_bay.dm +++ b/code/game/mecha/mech_bay.dm @@ -158,36 +158,36 @@ /obj/machinery/computer/mech_bay_power_console/attack_hand(mob/user as mob) if(..()) return - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/mech_bay_power_console/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/mech_bay_power_console/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, "mech_bay_console.tmpl", "Mech Bay Control Console", 500, 325) - // open the new ui window + ui = new(user, src, ui_key, "MechBayConsole", name, 400, 150, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) -/obj/machinery/computer/mech_bay_power_console/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/computer/mech_bay_power_console/tgui_act(action, params) + if(..()) + return + switch(action) + if("reconnect") + reconnect() + . = TRUE + update_icon() + +/obj/machinery/computer/mech_bay_power_console/tgui_data(mob/user) + var/data = list() if(!recharge_port) reconnect() if(recharge_port && !QDELETED(recharge_port)) data["recharge_port"] = list("mech" = null) if(recharge_port.recharging_mecha && !QDELETED(recharge_port.recharging_mecha)) - data["recharge_port"]["mech"] = list("health" = recharge_port.recharging_mecha.obj_integrity, "maxhealth" = initial(recharge_port.recharging_mecha.max_integrity), "cell" = null) + data["recharge_port"]["mech"] = list("health" = recharge_port.recharging_mecha.obj_integrity, "maxhealth" = recharge_port.recharging_mecha.max_integrity, "cell" = null, "name" = recharge_port.recharging_mecha.name) if(recharge_port.recharging_mecha.cell && !QDELETED(recharge_port.recharging_mecha.cell)) - data["has_mech"] = 1 - data["mecha_name"] = recharge_port.recharging_mecha || "None" - data["mecha_charge"] = isnull(recharge_port.recharging_mecha) ? 0 : recharge_port.recharging_mecha.cell.charge - data["mecha_maxcharge"] = isnull(recharge_port.recharging_mecha) ? 0 : recharge_port.recharging_mecha.cell.maxcharge - data["mecha_charge_percentage"] = isnull(recharge_port.recharging_mecha) ? 0 : round(recharge_port.recharging_mecha.cell.percent()) - else - data["has_mech"] = 0 - + data["recharge_port"]["mech"]["cell"] = list( + "charge" = recharge_port.recharging_mecha.cell.charge, + "maxcharge" = recharge_port.recharging_mecha.cell.maxcharge + ) return data /obj/machinery/computer/mech_bay_power_console/Initialize() diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 52973c9e285..fe032e0f927 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -1,3 +1,5 @@ +#define OCCUPANT_LOGGING occupant ? occupant : "empty mech" + /obj/mecha name = "Mecha" desc = "Exosuit" @@ -157,7 +159,6 @@ radio.name = "[src] radio" radio.icon = icon radio.icon_state = icon_state - radio.subspace_transmission = 1 /obj/mecha/examine(mob/user) . = ..() @@ -329,6 +330,8 @@ else occupant.clear_alert("mechaport") if(leg_overload_mode) + log_message("Leg Overload damage.") + take_damage(1, BRUTE, FALSE, FALSE) if(obj_integrity < max_integrity - max_integrity / 3) leg_overload_mode = FALSE step_in = initial(step_in) @@ -421,12 +424,7 @@ return if(isobj(obstacle)) var/obj/O = obstacle - if(istype(O, /obj/effect/portal)) //derpfix - anchored = 0 - O.Bumped(src) - spawn(0) //countering portal teleport spawn(0), hurr - anchored = 1 - else if(!O.anchored) + if(!O.anchored) step(obstacle, dir) else if(ismob(obstacle)) step(obstacle, dir) @@ -500,7 +498,7 @@ check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) else check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT)) - if(. >= 5 || prob(33)) + if((. >= 5 || prob(33)) && !(. == 1 && leg_overload_mode)) //If it takes 1 damage and leg_overload_mode is true, do not say TAKING DAMAGE! to the user several times a second. occupant_message("Taking damage!") log_message("Took [damage_amount] points of damage. Damage type: [damage_type]") @@ -538,12 +536,13 @@ user.changeNext_move(CLICK_CD_MELEE) user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) playsound(loc, 'sound/weapons/tap.ogg', 40, 1, -1) - user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.") + user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.") log_message("Attack by hand/paw. Attacker - [user].") /obj/mecha/attack_alien(mob/living/user) log_message("Attack by alien. Attacker - [user].", TRUE) + add_attack_logs(user, OCCUPANT_LOGGING, "Alien attacked mech [src]") playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE) attack_generic(user, 15, BRUTE, "melee", 0) @@ -561,8 +560,8 @@ if(user.obj_damage) animal_damage = user.obj_damage animal_damage = min(animal_damage, 20*user.environment_smash) - user.create_attack_log("attacked [name]") - add_attack_logs(user, src, "Attacked") + if(animal_damage) + add_attack_logs(user, OCCUPANT_LOGGING, "Animal attacked mech [src]") attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect) return TRUE @@ -573,7 +572,7 @@ . = ..() if(.) log_message("Attack by hulk. Attacker - [user].", 1) - add_attack_logs(user, src, "Punched with hulk powers") + add_attack_logs(user, OCCUPANT_LOGGING, "Hulk punched mech [src]") /obj/mecha/blob_act(obj/structure/blob/B) log_message("Attack by blob. Attacker - [B].") @@ -584,10 +583,14 @@ /obj/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper log_message("Hit by [AM].") + if(isitem(AM)) + var/obj/item/I = AM + add_attack_logs(I.thrownby, OCCUPANT_LOGGING, "threw [AM] at mech [src]") . = ..() /obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).") + add_attack_logs(Proj.firer, OCCUPANT_LOGGING, "shot [Proj.name]([Proj.flag]) at mech [src]") ..() /obj/mecha/ex_act(severity, target) @@ -774,6 +777,8 @@ to_chat(user, "You stop installing [M].") else + if(W.force) + add_attack_logs(user, OCCUPANT_LOGGING, "attacked mech '[src]' using [W]") return ..() @@ -861,7 +866,7 @@ return 0 use_power(melee_energy_drain) if(M.damtype == BRUTE || M.damtype == BURN) - add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") + add_attack_logs(M.occupant, src, "Mecha-attacked with [M] ([uppertext(M.occupant.a_intent)]) ([uppertext(M.damtype)])") . = ..() /obj/mecha/emag_act(mob/user) @@ -1264,6 +1269,13 @@ L.client.RemoveViewMod("mecha") zoom_mode = FALSE + if(ishuman(L)) + var/mob/living/carbon/human/H = L + H.regenerate_icons() // workaround for 14457 + +/obj/mecha/force_eject_occupant() + go_out() + ///////////////////////// ////// Access stuff ///// ///////////////////////// @@ -1534,3 +1546,5 @@ if(L.incapacitated()) return FALSE return TRUE + +#undef OCCUPANT_LOGGING diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm index f72770a8ea3..72c8985a126 100644 --- a/code/game/mecha/mecha_control_console.dm +++ b/code/game/mecha/mecha_control_console.dm @@ -8,68 +8,65 @@ circuit = /obj/item/circuitboard/mecha_control var/list/located = list() var/screen = 0 - var/stored_data + var/stored_data = list() /obj/machinery/computer/mecha/attack_ai(mob/user) return attack_hand(user) /obj/machinery/computer/mecha/attack_hand(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/mecha/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/mecha/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "exosuit_control.tmpl", "Exosuit Control Console", 420, 500) + ui = new(user, src, ui_key, "MechaControlConsole", name, 420, 500, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/computer/mecha/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["screen"] = screen - if(screen == 0) - var/list/mechas[0] - var/list/trackerlist = list() - for(var/stompy in GLOB.mechas_list) - var/obj/mecha/MC = stompy - trackerlist += MC.trackers - for(var/thing in trackerlist) - var/obj/item/mecha_parts/mecha_tracking/TR = thing - var/answer = TR.get_mecha_info() - if(answer) - mechas[++mechas.len] = answer - data["mechas"] = mechas - if(screen == 1) - data["log"] = stored_data +/obj/machinery/computer/mecha/tgui_data(mob/user) + var/list/data = list() + data["beacons"] = list() + var/list/trackerlist = list() + for(var/stompy in GLOB.mechas_list) + var/obj/mecha/MC = stompy + trackerlist += MC.trackers + for(var/thing in trackerlist) + var/obj/item/mecha_parts/mecha_tracking/TR = thing + var/list/tr_data = TR.retrieve_data() + if(tr_data) + data["beacons"] += list(tr_data) + + data["stored_data"] = stored_data + return data -/obj/machinery/computer/mecha/Topic(href, href_list) + +/obj/machinery/computer/mecha/tgui_act(action, params) if(..()) - return 1 - - var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) - if(href_list["send_message"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("send_message") - var/message = strip_html_simple(input(usr,"Input message","Transmit message") as text) - if(!trim(message) || ..()) - return 1 - var/obj/mecha/M = MT.in_mecha() - if(M) - M.occupant_message(message) - - if(href_list["shock"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("shock") - MT.shock() - - if(href_list["get_log"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("get_log") - stored_data = MT.get_mecha_log() - screen = 1 - - if(href_list["return"]) - screen = 0 - - SSnanoui.update_uis(src) - return + return + switch(action) + if("send_message") + var/obj/item/mecha_parts/mecha_tracking/MT = locateUID(params["mt"]) + if(istype(MT)) + var/message = strip_html_simple(input(usr, "Input message", "Transmit message") as text) + if(!message || !trim(message) || ..()) + return FALSE + var/obj/mecha/M = MT.in_mecha() + if(M) + M.occupant_message(message) + return TRUE + if("shock") + var/obj/item/mecha_parts/mecha_tracking/MT = locateUID(params["mt"]) + if(istype(MT)) + MT.shock() + return TRUE + if("get_log") + var/obj/item/mecha_parts/mecha_tracking/MT = locateUID(params["mt"]) + if(istype(MT)) + stored_data = MT.get_mecha_log() + return TRUE + if("clear_log") + stored_data = list() + return TRUE /obj/item/mecha_parts/mecha_tracking name = "Exosuit tracking beacon" @@ -103,7 +100,7 @@ if(istype(M, /obj/mecha/working/ripley)) var/obj/mecha/working/ripley/RM = M answer["hascargo"] = 1 - answer["cargo"] = RM.cargo.len/RM.cargo_capacity*100 + answer["cargo"] = length(RM.cargo) / RM.cargo_capacity * 100 return answer @@ -122,10 +119,34 @@ Active equipment: [M.selected||"None"]
    "} if(istype(M, /obj/mecha/working/ripley)) var/obj/mecha/working/ripley/RM = M - answer += "Used cargo space: [RM.cargo.len/RM.cargo_capacity*100]%
    " + answer += "Used cargo space: [length(RM.cargo) / RM.cargo_capacity * 100]%
    " return answer +/obj/item/mecha_parts/mecha_tracking/proc/retrieve_data() + var/list/data = list() + if(!in_mecha()) + return FALSE + var/obj/mecha/M = loc + data["uid"] = UID() + data["charge"] = M.get_charge() + data["name"] = M.name + data["health"] = M.obj_integrity + data["maxHealth"] = M.max_integrity + data["cell"] = M.cell + if(M.cell) + data["cellCharge"] = M.cell.charge + data["cellMaxCharge"] = M.cell.charge + data["airtank"] = M.return_pressure() + data["pilot"] = M.occupant + data["location"] = get_area(M) + data["active"] = M.selected + if(istype(M, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/RM = M + data["cargoUsed"] = length(RM.cargo) + data["cargoMax"] = RM.cargo_capacity + return data + /obj/item/mecha_parts/mecha_tracking/emp_act() qdel(src) @@ -142,9 +163,9 @@ /obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() if(!in_mecha()) - return 0 + return list() var/obj/mecha/M = loc - return M.get_log_html() + return M.get_log_tgui() /obj/item/mecha_parts/mecha_tracking/ai_control name = "exosuit AI control beacon" diff --git a/code/game/mecha/mecha_topic.dm b/code/game/mecha/mecha_topic.dm index fc8f38a971c..8c9a74c0f8d 100644 --- a/code/game/mecha/mecha_topic.dm +++ b/code/game/mecha/mecha_topic.dm @@ -158,6 +158,14 @@ output += "" return output +/obj/mecha/proc/get_log_tgui() + var/list/data = list() + for(var/list/entry in log) + data.Add(list(list( + "time" = time2text(entry["time"], "hh:mm:ss"), + "message" = entry["message"], + ))) + return data /obj/mecha/proc/output_access_dialog(obj/item/card/id/id_card, mob/user) if(!id_card || !user) return diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index 8b9e5e92319..41b3e705f36 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -26,7 +26,7 @@ /obj/effect/anomaly/proc/anomalyEffect() if(prob(50)) - step(src,pick(GLOB.alldirs)) + step(src, pick(GLOB.alldirs)) /obj/effect/anomaly/proc/anomalyNeutralize() @@ -183,3 +183,30 @@ if( T && istype(T,/turf/simulated) && prob(turf_removal_chance) ) T.ex_act(ex_act_force) return + +///////////////////// + +/obj/effect/anomaly/atmos + name = "transformative gas anomaly" + icon_state = "electricity2" + var/gas_type + +/obj/effect/anomaly/atmos/New() + ..() + gas_type = pick(GAS_N2O, GAS_CO2, GAS_N2) + aSignal.origin_tech = "materials=7" //turning gas into another gas has some interesting implications for material science + //might also work as biotech maybe? Since there's no anomaly for that. + +/obj/effect/anomaly/atmos/anomalyEffect() + ..() + var/turf/simulated/T = get_turf(src) + if(istype(T)) + var/flag + switch(gas_type) + if(GAS_CO2) + flag = LINDA_SPAWN_CO2 + if(GAS_N2O) + flag = LINDA_SPAWN_N2O + else + flag = LINDA_SPAWN_NITROGEN + T.atmos_spawn_air(LINDA_SPAWN_20C | flag, 10) diff --git a/code/game/objects/effects/decals/remains.dm b/code/game/objects/effects/decals/remains.dm index 9a879a02da3..e750c15e761 100644 --- a/code/game/objects/effects/decals/remains.dm +++ b/code/game/objects/effects/decals/remains.dm @@ -28,6 +28,12 @@ icon_state = "remainsrobot" anchored = TRUE +/obj/effect/decal/remains/robot/decompile_act(obj/item/matter_decompiler/C, mob/user) + C.stored_comms["glass"] += 2 + C.stored_comms["metal"] += 3 + qdel(src) + return TRUE + /obj/effect/decal/remains/slime name = "You shouldn't see this" desc = "Noooooooooooooooooooooo" diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index a55c2a731eb..3f8eb81c6b9 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -481,7 +481,7 @@ would spawn and follow the beaker, even if it is carried or thrown. var/more = "" if(M) more = " " - msg_admin_attack("A chemical smoke reaction has taken place in ([whereLink])[contained]. Last associated key is [carry.my_atom.fingerprintslast][more].", 0, 1) + add_attack_logs(M, location, "Caused a chemical smoke reaction containing [contained]. Last associated key is [carry.my_atom.fingerprintslast][more]", ATKLOG_FEW) log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last associated key is [carry.my_atom.fingerprintslast].") else msg_admin_attack("A chemical smoke reaction has taken place in ([whereLink]). No associated key.", 0, 1) diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm index 5ae6b0c6860..9ae89309eed 100644 --- a/code/game/objects/effects/effect_system/effect_system.dm +++ b/code/game/objects/effects/effect_system/effect_system.dm @@ -49,6 +49,8 @@ would spawn and follow the beaker, even if it is carried or thrown. holder = atom /datum/effect_system/proc/start() + if(QDELETED(src)) + return for(var/i in 1 to number) if(total_effects > 20) return @@ -68,7 +70,8 @@ would spawn and follow the beaker, even if it is carried or thrown. for(var/j in 1 to steps_amt) sleep(5) step(E,direction) - addtimer(CALLBACK(src, .proc/decrement_total_effect), 20) + if(!QDELETED(src)) + addtimer(CALLBACK(src, .proc/decrement_total_effect), 20) /datum/effect_system/proc/decrement_total_effect() total_effects-- diff --git a/code/game/objects/effects/effect_system/effects_chem_smoke.dm b/code/game/objects/effects/effect_system/effects_chem_smoke.dm index 8c28c7f11c5..2ebad516beb 100644 --- a/code/game/objects/effects/effect_system/effects_chem_smoke.dm +++ b/code/game/objects/effects/effect_system/effects_chem_smoke.dm @@ -83,13 +83,13 @@ var/more = "" if(M) more = " " - msg_admin_attack("A chemical smoke reaction has taken place in ([whereLink])[contained]. Last associated key is [carry.my_atom.fingerprintslast][more].", ATKLOG_FEW) + add_attack_logs(M, location, "Caused a chemical smoke reaction containing [contained]. Last associated key is [carry.my_atom.fingerprintslast][more]", ATKLOG_FEW) log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last associated key is [carry.my_atom.fingerprintslast].") else - msg_admin_attack("A chemical smoke reaction has taken place in ([whereLink]). No associated key.", ATKLOG_FEW) + msg_admin_attack("A chemical smoke reaction has taken place in ([whereLink])[contained]. No associated key.", ATKLOG_FEW) log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.") else - msg_admin_attack("A chemical smoke reaction has taken place in ([whereLink]). No associated key. CODERS: carry.my_atom may be null.", ATKLOG_FEW) + msg_admin_attack("A chemical smoke reaction has taken place in ([whereLink])[contained]. No associated key. CODERS: carry.my_atom may be null.", ATKLOG_FEW) log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key. CODERS: carry.my_atom may be null.") diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm index 7497b148788..625d2ad8fd9 100644 --- a/code/game/objects/effects/effects.dm +++ b/code/game/objects/effects/effects.dm @@ -45,6 +45,42 @@ if(prob(25)) qdel(src) +/** + * # The abstract object + * + * This is an object that is intended to able to be placed, but that is completely invisible. + * The object should be immune to all forms of damage, or things that can delete it, such as the singularity, or explosions. + */ +/obj/effect/abstract + name = "Abstract object" + invisibility = INVISIBILITY_ABSTRACT + layer = TURF_LAYER + density = FALSE + icon = null + icon_state = null + +// Most of these overrides procs below are overkill, but better safe than sorry. +/obj/effect/abstract/swarmer_act() + return + +/obj/effect/abstract/bullet_act(obj/item/projectile/P) + return + +/obj/effect/abstract/decompile_act(obj/item/matter_decompiler/C, mob/user) + return + +/obj/effect/abstract/tesla_act(power) + return + +/obj/effect/abstract/singularity_act() + return + +/obj/effect/abstract/narsie_act() + return + +/obj/effect/abstract/ex_act(severity) + return + /obj/effect/decal plane = FLOOR_PLANE resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF diff --git a/code/game/objects/effects/manifest.dm b/code/game/objects/effects/manifest.dm index f267494496e..6ada54e75f7 100644 --- a/code/game/objects/effects/manifest.dm +++ b/code/game/objects/effects/manifest.dm @@ -10,7 +10,8 @@ /obj/effect/manifest/proc/manifest() var/dat = "Crew Manifest:
    " - for(var/mob/living/carbon/human/M in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/M = thing dat += text(" [] - []
    ", M.name, M.get_assignment()) var/obj/item/paper/P = new /obj/item/paper( src.loc ) P.info = dat diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index aa756631768..a2b084463a4 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -88,9 +88,6 @@ if(!M.simulated || iseffect(M)) . = FALSE - if(M.anchored && ismecha(M)) - . = FALSE - /obj/effect/portal/proc/teleport(atom/movable/M) if(!can_teleport(M)) return FALSE diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm index 2e81ecb235d..9b62faa5252 100644 --- a/code/game/objects/effects/spawners/bombspawner.dm +++ b/code/game/objects/effects/spawners/bombspawner.dm @@ -6,18 +6,18 @@ var/btemp1 = 1500 var/btemp2 = 1000 // tank temperatures - timer - btype = 2 +/obj/effect/spawner/newbomb/timer + btype = 2 - syndicate - btemp1 = 150 - btemp2 = 20 +/obj/effect/spawner/newbomb/timer/syndicate + btemp1 = 150 + btemp2 = 20 - proximity - btype = 1 +/obj/effect/spawner/newbomb/proximity + btype = 1 - radio - btype = 0 +/obj/effect/spawner/newbomb/radio + btype = 0 /obj/effect/spawner/newbomb/New() diff --git a/code/game/objects/effects/spawners/random_spawners.dm b/code/game/objects/effects/spawners/random_spawners.dm index 9eb8e9de1a2..acd1e54eef7 100644 --- a/code/game/objects/effects/spawners/random_spawners.dm +++ b/code/game/objects/effects/spawners/random_spawners.dm @@ -285,7 +285,7 @@ /obj/item/clothing/gloves/color/yellow/power = 1, /obj/item/twohanded/chainsaw = 1, /obj/item/bee_briefcase = 1, - /obj/item/twohanded/energizedfireaxe = 1, + /obj/item/twohanded/fireaxe/energized = 1, /obj/item/clothing/glasses/thermal = 1, /obj/item/chameleon = 1, /obj/item/reagent_containers/hypospray/autoinjector/stimulants = 1, diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 7af5d7a998f..a1538b5fe92 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -42,7 +42,7 @@ /obj/structure/spider/stickyweb/CanPass(atom/movable/mover, turf/target, height=0) if(height == 0) return TRUE - if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider)) + if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider) || isterrorspider(mover)) return TRUE else if(istype(mover, /mob/living)) if(prob(50)) @@ -58,7 +58,7 @@ icon_state = "eggs" var/amount_grown = 0 var/player_spiders = 0 - var/list/faction = list() + var/list/faction = list("spiders") /obj/structure/spider/eggcluster/New() ..() @@ -90,7 +90,7 @@ var/obj/machinery/atmospherics/unary/vent_pump/entry_vent var/travelling_in_vent = 0 var/player_spiders = 0 - var/list/faction = list() + var/list/faction = list("spiders") var/selecting_player = 0 /obj/structure/spider/spiderling/New() @@ -180,7 +180,7 @@ if(player_spiders && !selecting_player) selecting_player = 1 spawn() - var/list/candidates = pollCandidates("Do you want to play as a spider?", ROLE_GSPIDER, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a giant spider?", ROLE_GSPIDER, TRUE, source = S) if(candidates.len) var/mob/C = pick(candidates) @@ -190,6 +190,16 @@ to_chat(S, "You are a spider who is loyal to [S.master_commander], obey [S.master_commander]'s every order and assist [S.master_commander.p_them()] in completing [S.master_commander.p_their()] goals at any cost.") qdel(src) +/obj/structure/spider/spiderling/decompile_act(obj/item/matter_decompiler/C, mob/user) + if(!istype(user, /mob/living/silicon/robot/drone)) + user.visible_message("[user] sucks [src] into its decompiler. There's a horrible crunching noise.", \ + "It's a bit of a struggle, but you manage to suck [user] into your decompiler. It makes a series of visceral crunching noises.") + C.stored_comms["wood"] += 2 + C.stored_comms["glass"] += 2 + qdel(src) + return TRUE + return ..() + /obj/effect/decal/cleanable/spiderling_remains name = "spiderling remains" desc = "Green squishy mess." diff --git a/code/game/objects/effects/temporary_visuals/muzzle_flashes.dm b/code/game/objects/effects/temporary_visuals/muzzle_flashes.dm new file mode 100644 index 00000000000..ab7bb7a056d --- /dev/null +++ b/code/game/objects/effects/temporary_visuals/muzzle_flashes.dm @@ -0,0 +1,19 @@ +/obj/effect/temp_visual/target_angled/muzzle_flash + icon = 'icons/effects/projectile.dmi' + icon_state = "firing_effect" + duration = 0.2 + +/obj/effect/temp_visual/target_angled/muzzle_flash/Initialize(mapload, atom/target, duration_override = null) + if(duration_override) + duration = duration_override + . = ..() + if(get_dir(src, target) & NORTH) + layer = BELOW_MOB_LAYER + + +/obj/effect/temp_visual/target_angled/muzzle_flash/energy + icon_state = "firing_effect_energy" + +/obj/effect/temp_visual/target_angled/muzzle_flash/magic + icon = 'icons/effects/effects.dmi' + icon_state = "shieldsparkles" diff --git a/code/game/objects/effects/temporary_visuals/temporary_visual.dm b/code/game/objects/effects/temporary_visuals/temporary_visual.dm index d09acce7255..0bbbb554ace 100644 --- a/code/game/objects/effects/temporary_visuals/temporary_visual.dm +++ b/code/game/objects/effects/temporary_visuals/temporary_visual.dm @@ -35,3 +35,13 @@ if(set_dir) setDir(set_dir) . = ..() + +/obj/effect/temp_visual/target_angled + randomdir = FALSE + +/obj/effect/temp_visual/target_angled/Initialize(mapload, atom/target) + . = ..() + if(target) + var/matrix/M = new + M.Turn(get_angle(src, target)) + transform = M diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm index 1b31891e537..f35b877bc17 100644 --- a/code/game/objects/explosion.dm +++ b/code/game/objects/explosion.dm @@ -137,19 +137,7 @@ T.ex_act(3) CHECK_TICK - //--- THROW ITEMS AROUND --- -/* - if(throw_dist > 0) - var/throw_dir = get_dir(epicenter,T) - for(var/obj/item/I in T) - spawn(0) //Simultaneously not one at a time - if(I && !I.anchored) - var/throw_mult = 0.5 + (0.5 * rand()) // Between 0.5 and 1.0 - var/throw_range = round((throw_dist + 1) * throw_mult) // Roughly 50% to 100% of throw_dist - if(throw_range > 0) - var/turf/throw_at = get_ranged_target_turf(I, throw_dir, throw_range) - I.throw_at(throw_at, throw_range, 2, no_spin = 1) //Throw it at 2 speed, this is purely visual anyway; don't spin the thrown items, it's very costly. -*/ + var/took = stop_watch(watch) //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare if(GLOB.debug2) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 76361776b0a..edcc6620531 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -2,6 +2,8 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect /obj/item name = "item" icon = 'icons/obj/items.dmi' + + move_resist = null // Set in the Initialise depending on the item size. Unless it's overriden by a specific item var/discrete = 0 // used in item_attack.dm to make an item not show an attack message to viewers var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite var/blood_overlay_color = null @@ -113,6 +115,23 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect hitsound = 'sound/items/welder.ogg' if(damtype == "brute") hitsound = "swing_hit" + if(!move_resist) + determine_move_resist() + +/obj/item/proc/determine_move_resist() + switch(w_class) + if(WEIGHT_CLASS_TINY) + move_resist = MOVE_FORCE_EXTREMELY_WEAK + if(WEIGHT_CLASS_SMALL) + move_resist = MOVE_FORCE_VERY_WEAK + if(WEIGHT_CLASS_NORMAL) + move_resist = MOVE_FORCE_WEAK + if(WEIGHT_CLASS_BULKY) + move_resist = MOVE_FORCE_NORMAL + if(WEIGHT_CLASS_HUGE) + move_resist = MOVE_FORCE_NORMAL + if(WEIGHT_CLASS_GIGANTIC) + move_resist = MOVE_FORCE_NORMAL /obj/item/Destroy() flags &= ~DROPDEL //prevent reqdels @@ -506,7 +525,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect "You stab yourself in the eyes with [src]!" \ ) - add_attack_logs(user, M, "Eye-stabbed with [src] (INTENT: [uppertext(user.a_intent)])") + add_attack_logs(user, M, "Eye-stabbed with [src] ([uppertext(user.a_intent)])") if(istype(H)) var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes) diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index 5f5c62abd21..97e46e3abb5 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -221,6 +221,12 @@ A.contents += thing thing.change_area(old_area, A) + var/area/oldA = get_area(get_turf(usr)) + var/list/firedoors = oldA.firedoors + for(var/door in firedoors) + var/obj/machinery/door/firedoor/FD = door + FD.CalculateAffectingAreas() + interact() area_created = TRUE return area_created @@ -236,6 +242,10 @@ return set_area_machinery_title(A,str,prevname) A.name = str + if(A.firedoors) + for(var/D in A.firedoors) + var/obj/machinery/door/firedoor/FD = D + FD.CalculateAffectingAreas() to_chat(usr, "You rename the '[prevname]' to '[str]'.") interact() return 1 diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm index 5c63dbc709c..58a4ab63d34 100644 --- a/code/game/objects/items/bodybag.dm +++ b/code/game/objects/items/bodybag.dm @@ -7,10 +7,10 @@ icon_state = "bodybag_folded" w_class = WEIGHT_CLASS_SMALL - attack_self(mob/user) - var/obj/structure/closet/body_bag/R = new /obj/structure/closet/body_bag(user.loc) - R.add_fingerprint(user) - qdel(src) +/obj/item/bodybag/attack_self(mob/user) + var/obj/structure/closet/body_bag/R = new /obj/structure/closet/body_bag(user.loc) + R.add_fingerprint(user) + qdel(src) /obj/structure/closet/body_bag name = "body bag" @@ -56,16 +56,13 @@ /obj/structure/closet/body_bag/MouseDrop(over_object, src_location, over_location) - ..() + . = ..() if((over_object == usr && (in_range(src, usr) || usr.contents.Find(src)))) - if(!ishuman(usr)) return - if(opened) return 0 - if(contents.len) return 0 - visible_message("[usr] folds up the [src.name]") + if(!ishuman(usr) || opened || length(contents)) + return FALSE + visible_message("[usr] folds up the [name]") new item_path(get_turf(src)) - spawn(0) - qdel(src) - return + qdel(src) /obj/structure/closet/body_bag/relaymove(mob/user as mob) if(user.stat) diff --git a/code/game/objects/items/changestone.dm b/code/game/objects/items/changestone.dm index 62d56614a07..ec7ad232857 100644 --- a/code/game/objects/items/changestone.dm +++ b/code/game/objects/items/changestone.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/artifacts.dmi' icon_state = "changerock" -obj/item/changestone/attack_hand(var/mob/user as mob) +/obj/item/changestone/attack_hand(var/mob/user as mob) if(istype(user,/mob/living/carbon/human)) var/mob/living/carbon/human/H = user if(!H.gloves) diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/control_wand.dm index 01033e6c123..185a00305de 100644 --- a/code/game/objects/items/control_wand.dm +++ b/code/game/objects/items/control_wand.dm @@ -1,6 +1,7 @@ #define WAND_OPEN "Open Door" #define WAND_BOLT "Toggle Bolts" #define WAND_EMERGENCY "Toggle Emergency Access" +#define WAND_SPEED "Change Closing Speed" /obj/item/door_remote icon_state = "gangtool-white" @@ -21,7 +22,7 @@ for(var/region in region_access) ID.access += get_region_accesses(region) ID.access += additional_access - ID.access = uniquelist(ID.access) //remove duplicates + ID.access = uniquelist(ID.access) /obj/item/door_remote/Destroy() QDEL_NULL(ID) @@ -34,8 +35,11 @@ if(WAND_BOLT) mode = WAND_EMERGENCY if(WAND_EMERGENCY) + mode = WAND_SPEED + if(WAND_SPEED) mode = WAND_OPEN - to_chat(user, "Now in mode: [mode].") + + to_chat(user, "Now in mode: [mode].") /obj/item/door_remote/afterattack(obj/machinery/door/airlock/D, mob/user) if(!istype(D)) @@ -64,10 +68,13 @@ D.lock() if(WAND_EMERGENCY) if(D.emergency) - D.emergency = 0 + D.emergency = FALSE else - D.emergency = 1 + D.emergency = TRUE D.update_icon() + if(WAND_SPEED) + D.normalspeed = !D.normalspeed + to_chat(user, "[D] is now in [D.normalspeed ? "normal" : "fast"] mode.") else to_chat(user, "[src] does not have access to this door.") @@ -144,3 +151,4 @@ #undef WAND_OPEN #undef WAND_BOLT #undef WAND_EMERGENCY +#undef WAND_SPEED diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index eeb484b9f50..ce52e260bca 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -11,7 +11,7 @@ slot_flags = SLOT_BELT | SLOT_EARS attack_verb = list("attacked", "coloured") toolspeed = 1 - var/colour = "#FF0000" //RGB + var/colour = COLOR_RED var/drawtype = "rune" var/list/graffiti = list("body","amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","up","down","left","right","heart","borgsrogue","voxpox","shitcurity","catbeast","hieroglyphs1","hieroglyphs2","hieroglyphs3","security","syndicate1","syndicate2","nanotrasen","lie","valid","arrowleft","arrowright","arrowup","arrowdown","chicken","hailcrab","brokenheart","peace","scribble","scribble2","scribble3","skrek","squish","tunnelsnake","yip","youaredead") var/list/letters = list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z") @@ -123,54 +123,54 @@ /obj/item/toy/crayon/red icon_state = "crayonred" - colour = "#DA0000" + colour = COLOR_RED colourName = "red" /obj/item/toy/crayon/orange icon_state = "crayonorange" - colour = "#FF9300" + colour = COLOR_ORANGE colourName = "orange" /obj/item/toy/crayon/yellow icon_state = "crayonyellow" - colour = "#FFF200" + colour = COLOR_YELLOW colourName = "yellow" /obj/item/toy/crayon/green icon_state = "crayongreen" - colour = "#A8E61D" + colour = COLOR_GREEN colourName = "green" /obj/item/toy/crayon/blue icon_state = "crayonblue" - colour = "#00B7EF" + colour = COLOR_BLUE colourName = "blue" /obj/item/toy/crayon/purple icon_state = "crayonpurple" - colour = "#DA00FF" + colour = COLOR_PURPLE colourName = "purple" /obj/item/toy/crayon/random/New() icon_state = pick(list("crayonred", "crayonorange", "crayonyellow", "crayongreen", "crayonblue", "crayonpurple")) switch(icon_state) if("crayonred") - colour = "#DA0000" + colour = COLOR_RED colourName = "red" if("crayonorange") - colour = "#FF9300" + colour = COLOR_ORANGE colourName = "orange" if("crayonyellow") - colour = "#FFF200" + colour = COLOR_YELLOW colourName = "yellow" if("crayongreen") - colour = "#A8E61D" + colour =COLOR_GREEN colourName = "green" if("crayonblue") - colour = "#00B7EF" + colour = COLOR_BLUE colourName = "blue" if("crayonpurple") - colour = "#DA00FF" + colour = COLOR_PURPLE colourName = "purple" ..() @@ -197,10 +197,10 @@ if(!Adjacent(usr) || usr.incapacitated()) return if(href_list["color"]) - if(colour != "#FFFFFF") - colour = "#FFFFFF" + if(colour != COLOR_WHITE) + colour = COLOR_WHITE else - colour = "#000000" + colour = COLOR_BLACK update_window(usr) else ..() diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index 093f28692ec..7eb1dc9fd56 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -37,71 +37,83 @@ overlays.Cut() /obj/item/aicard/attack_self(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/item/aicard/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/aicard/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "aicard.tmpl", "[name]", 600, 400, state = state) + ui = new(user, src, ui_key, "AICard", "[name]", 600, 394, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/item/aicard/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) +/obj/item/aicard/tgui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) var/data[0] var/mob/living/silicon/ai/AI = locate() in src if(istype(AI)) - data["has_ai"] = 1 + data["has_ai"] = TRUE data["name"] = AI.name - data["hardware_integrity"] = ((AI.health + 100) / 2) + data["integrity"] = ((AI.health + 100) / 2) data["radio"] = !AI.aiRadio.disabledAi data["wireless"] = !AI.control_disabled data["operational"] = AI.stat != DEAD data["flushing"] = flush var/laws[0] - for(var/datum/ai_law/AL in AI.laws.all_laws()) - laws[++laws.len] = list("index" = AL.get_index(), "law" = sanitize(AL.law)) + for(var/datum/ai_law/law in AI.laws.all_laws()) + if(law in AI.laws.ion_laws) // If we're an ion law, give it an ion index code + laws.Add(ionnum() + ". " + law.law) + else + laws.Add(num2text(law.get_index()) + ". " + law.law) data["laws"] = laws - data["has_laws"] = laws.len + data["has_laws"] = length(AI.laws.all_laws()) + + else + data["has_ai"] = FALSE // If this isn't passed to tgui, it won't show there isn't a AI in the card. return data -/obj/item/aicard/Topic(href, href_list, nowindow, state) +/obj/item/aicard/tgui_act(action, params) if(..()) - return 1 + return var/mob/living/silicon/ai/AI = locate() in src if(!istype(AI)) - return 1 + return var/user = usr + switch(action) + if("wipe") + if(flush) // Don't doublewipe. + to_chat(user, "You are already wiping this AI!") + return + var/confirm = alert("Are you sure you want to wipe this card's memory? This cannot be undone once started.", "Confirm Wipe", "Yes", "No") + if(confirm == "Yes" && (tgui_status(user, GLOB.tgui_inventory_state) == STATUS_INTERACTIVE)) // And make doubly sure they want to wipe (three total clicks) + msg_admin_attack("[key_name_admin(user)] wiped [key_name_admin(AI)] with \the [src].", ATKLOG_FEW) + add_attack_logs(user, AI, "Wiped with [src].") + INVOKE_ASYNC(src, .proc/wipe_ai) - if(href_list["wipe"]) - var/confirm = alert("Are you sure you want to wipe this card's memory? This cannot be undone once started.", "Confirm Wipe", "Yes", "No") - if(confirm == "Yes" && (CanUseTopic(user, state) == STATUS_INTERACTIVE)) - msg_admin_attack("[key_name_admin(user)] wiped [key_name_admin(AI)] with \the [src].", ATKLOG_FEW) - add_attack_logs(user, AI, "Wiped with [src].") - flush = 1 - AI.suiciding = 1 - to_chat(AI, "Your core files are being wiped!") - while(AI && AI.stat != DEAD) - AI.adjustOxyLoss(2) - sleep(10) - flush = 0 + if("radio") + AI.aiRadio.disabledAi = !AI.aiRadio.disabledAi + to_chat(AI, "Your Subspace Transceiver has been [AI.aiRadio.disabledAi ? "disabled" : "enabled"]!") + to_chat(user, "You [AI.aiRadio.disabledAi ? "disable" : "enable"] the AI's Subspace Transceiver.") - if(href_list["radio"]) - AI.aiRadio.disabledAi = text2num(href_list["radio"]) - to_chat(AI, "Your Subspace Transceiver has been [AI.aiRadio.disabledAi ? "disabled" : "enabled"]!") - to_chat(user, "You [AI.aiRadio.disabledAi ? "disable" : "enable"] the AI's Subspace Transceiver.") + if("wireless") + AI.control_disabled = !AI.control_disabled + to_chat(AI, "Your wireless interface has been [AI.control_disabled ? "disabled" : "enabled"]!") + to_chat(user, "You [AI.control_disabled ? "disable" : "enable"] the AI's wireless interface.") + update_icon() - if(href_list["wireless"]) - AI.control_disabled = text2num(href_list["wireless"]) - to_chat(AI, "Your wireless interface has been [AI.control_disabled ? "disabled" : "enabled"]!") - to_chat(user, "You [AI.control_disabled ? "disable" : "enable"] the AI's wireless interface.") - update_icon() + return TRUE - return 1 +/obj/item/aicard/proc/wipe_ai() + var/mob/living/silicon/ai/AI = locate() in src + flush = TRUE + AI.suiciding = TRUE + to_chat(AI, "Your core files are being wiped!") + while(AI && AI.stat != DEAD) + AI.adjustOxyLoss(2) + sleep(10) + flush = FALSE diff --git a/code/game/objects/items/devices/camera_bug.dm b/code/game/objects/items/devices/camera_bug.dm index 7724d499e7e..f2ccc0c1aa1 100644 --- a/code/game/objects/items/devices/camera_bug.dm +++ b/code/game/objects/items/devices/camera_bug.dm @@ -1,9 +1,4 @@ -#define BUGMODE_LIST 0 -#define BUGMODE_MONITOR 1 -#define BUGMODE_TRACK 2 - - - +// This item just has an integrated camera console, which the data is "proxied" to /obj/item/camera_bug name = "camera bug" desc = "For illicit snooping through the camera network." @@ -14,270 +9,26 @@ throw_speed = 4 throw_range = 20 origin_tech = "syndicate=1;engineering=3" + /// Integrated camera console to serve UI data + var/obj/machinery/computer/security/camera_bug/integrated_console - var/obj/machinery/camera/current = null +/obj/machinery/computer/security/camera_bug + name = "invasive camera utility" + desc = "How did this get here?! Please report this as a bug to github" + use_power = NO_POWER_USE - var/last_net_update = 0 - var/list/bugged_cameras = list() - - var/track_mode = BUGMODE_LIST - var/last_tracked = 0 - var/refresh_interval = 50 - - var/tracked_name = null - var/atom/tracking = null - - var/last_found = null - var/last_seen = null - -/obj/item/camera_bug/New() - ..() - START_PROCESSING(SSobj, src) +/obj/item/camera_bug/Initialize(mapload) + . = ..() + integrated_console = new(src) + integrated_console.parent = src + integrated_console.network = list("SS13") /obj/item/camera_bug/Destroy() - get_cameras() - for(var/cam_tag in bugged_cameras) - var/obj/machinery/camera/camera = bugged_cameras[cam_tag] - if(camera.bug == src) - camera.bug = null - bugged_cameras = list() - if(tracking) - tracking = null + QDEL_NULL(integrated_console) return ..() -/obj/item/camera_bug/interact(var/mob/user = usr) - var/datum/browser/popup = new(user, "camerabug","Camera Bug",nref=src) - popup.set_content(menu(get_cameras())) - popup.open() - /obj/item/camera_bug/attack_self(mob/user as mob) - user.set_machine(src) - interact(user) + tgui_interact(user) -/obj/item/camera_bug/check_eye(mob/user) - if(loc != user || user.incapacitated() || !user.has_vision() || !current) - user.unset_machine() - return FALSE - var/turf/T_user = get_turf(user.loc) - var/turf/T_current = get_turf(current) - if(!atoms_share_level(T_user, T_current) || !current.can_use()) - to_chat(user, "[src] has lost the signal.") - current = null - user.unset_machine() - return FALSE - return TRUE - -/obj/item/camera_bug/on_unset_machine(mob/user) - user.reset_perspective(null) - -/obj/item/camera_bug/proc/get_cameras() - if(world.time > (last_net_update + 100)) - bugged_cameras = list() - for(var/obj/machinery/camera/camera in GLOB.cameranet.cameras) - if(camera.stat || !camera.can_use()) - continue - if(length(list("SS13","MINE")&camera.network)) - bugged_cameras[camera.c_tag] = camera - sortTim(bugged_cameras, /proc/cmp_text_asc) - return bugged_cameras - -/obj/item/camera_bug/proc/menu(var/list/cameras) - if(!cameras || !cameras.len) - return "No bugged cameras found." - - var/html - switch(track_mode) - if(BUGMODE_LIST) - html = "

    Select a camera:

    \[Cancel camera view\]
    " - for(var/entry in cameras) - var/obj/machinery/camera/C = cameras[entry] - var/functions = "" - if(C.bug == src) - functions = " - \[Monitor\] \[Disable\]" - else - functions = " - \[Monitor\]" - html += "" - - if(BUGMODE_MONITOR) - if(current) - html = "Analyzing Camera '[current.c_tag]' \[Select Camera\]
    " - html += camera_report() - else - track_mode = BUGMODE_LIST - return .(cameras) - if(BUGMODE_TRACK) - if(tracking) - html = "Tracking '[tracked_name]' \[Cancel Tracking\] \[Cancel camera view\]
    " - if(last_found) - var/time_diff = round((world.time - last_seen) / 150) - var/obj/machinery/camera/C = bugged_cameras[last_found] - var/outstring - if(C) - outstring = "[last_found]" - else - outstring = last_found - if(!time_diff) - html += "Last seen near [outstring] (now)
    " - else - // 15 second intervals ~ 1/4 minute - var/m = round(time_diff/4) - var/s = (time_diff - 4*m) * 15 - if(!s) s = "00" - html += "Last seen near [outstring] ([m]:[s] minute\s ago)
    " - if( C && (C.bug == src)) //Checks to see if the camera has a bug - html += "\[Disable\]" - - else - html += "Not yet seen." - else - track_mode = BUGMODE_LIST - return .(cameras) - return html - -/obj/item/camera_bug/proc/camera_report() - // this should only be called if current exists - var/dat = "" - if(current && current.can_use()) - var/list/seen = current.can_see() - var/list/names = list() - for(var/obj/singularity/S in seen) // god help you if you see more than one - if(S.name in names) - names[S.name]++ - dat += "[S.name] ([names[S.name]])" - else - names[S.name] = 1 - dat += "[S.name]" - var/stage = round(S.current_size / 2)+1 - dat += " (Stage [stage])" - dat += " \[Track\]
    " - - for(var/obj/mecha/M in seen) - if(M.name in names) - names[M.name]++ - dat += "[M.name] ([names[M.name]])" - else - names[M.name] = 1 - dat += "[M.name]" - dat += " \[Track\]
    " - - - for(var/mob/living/M in seen) - if(M.name in names) - names[M.name]++ - dat += "[M.name] ([names[M.name]])" - else - names[M.name] = 1 - dat += "[M.name]" - if(M.buckled && !M.lying) - dat += " (Sitting)" - if(M.lying) - dat += " (Laying down)" - dat += " \[Track\]
    " - if(length(dat) == 0) - dat += "No motion detected." - return dat - else - return "Camera Offline
    " - -/obj/item/camera_bug/Topic(var/href,var/list/href_list) - if(usr != loc) - usr.unset_machine() - usr << browse(null, "window=camerabug") - return - usr.set_machine(src) - if("mode" in href_list) - track_mode = text2num(href_list["mode"]) - if("monitor" in href_list) - var/obj/machinery/camera/C = locate(href_list["monitor"]) - if(C) - track_mode = BUGMODE_MONITOR - current = C - usr.reset_perspective(null) - interact() - if("track" in href_list) - var/atom/A = locate(href_list["track"]) - if(A) - tracking = A - tracked_name = A.name - last_found = current.c_tag - last_seen = world.time - track_mode = BUGMODE_TRACK - if("emp" in href_list) - var/obj/machinery/camera/C = locate(href_list["emp"]) - if(istype(C) && C.bug == src) - C.emp_act(1) - C.bug = null - bugged_cameras -= C.c_tag - interact() - return - if("close" in href_list) - usr.reset_perspective(null) - usr.unset_machine() - current = null - return // I do not <- I do not remember what I was going to write in this comment -Sayu, sometime later - if("view" in href_list) - var/obj/machinery/camera/C = locate(href_list["view"]) - if(istype(C)) - if(!C.can_use()) - to_chat(usr, "Something's wrong with that camera. You can't get a feed.") - return - var/turf/T = get_turf(loc) - if(!T || C.z != T.z) - to_chat(usr, "You can't get a signal.") - return - current = C - spawn(6) - if(src.check_eye(usr)) - usr.reset_perspective(C) - interact() - else - usr.unset_machine() - usr << browse(null, "window=camerabug") - return - else - usr.unset_machine() - usr.reset_perspective(null) - - interact() - -/obj/item/camera_bug/process() - if(track_mode == BUGMODE_LIST || (world.time < (last_tracked + refresh_interval))) - return - last_tracked = world.time - if(track_mode == BUGMODE_TRACK ) // search for user - // Note that it will be tricked if your name appears to change. - // This is not optimal but it is better than tracking you relentlessly despite everything. - if(!tracking) - src.updateSelfDialog() - return - - if(tracking.name != tracked_name) // Hiding their identity, tricksy - var/mob/M = tracking - if(istype(M)) - if(!(tracked_name == "Unknown" && findtext(tracking.name,"Unknown"))) // we saw then disguised before - if(!(tracked_name == M.real_name && findtext(tracking.name,M.real_name))) // or they're still ID'd - src.updateSelfDialog()//But if it's neither of those cases - return // you won't find em on the cameras - else - src.updateSelfDialog() - return - - var/list/tracking_cams = list() - var/list/b_cams = get_cameras() - for(var/entry in b_cams) - tracking_cams += b_cams[entry] - var/list/target_region = view(tracking) - - for(var/obj/machinery/camera/C in (target_region & tracking_cams)) - if(!can_see(C,tracking)) // target may have xray, that doesn't make them visible to cameras - continue - if(C.can_use()) - last_found = C.c_tag - last_seen = world.time - break - src.updateSelfDialog() - - -#undef BUGMODE_LIST -#undef BUGMODE_MONITOR -#undef BUGMODE_TRACK +/obj/item/camera_bug/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state) + integrated_console.tgui_interact(user, ui_key, ui, force_open, master_ui, state) diff --git a/code/game/objects/items/devices/floor_painter.dm b/code/game/objects/items/devices/floor_painter.dm index 889138c9c91..ae5a75d5622 100644 --- a/code/game/objects/items/devices/floor_painter.dm +++ b/code/game/objects/items/devices/floor_painter.dm @@ -4,7 +4,8 @@ name = "floor painter" icon = 'icons/obj/device.dmi' icon_state = "floor_painter" - item_state = "electronic" + item_state = "floor_painter" + usesound = 'sound/effects/spray2.ogg' var/floor_icon var/floor_state = "floor" @@ -31,10 +32,16 @@ return var/turf/simulated/floor/plasteel/F = A + + if(F.icon_state == floor_state && F.dir == floor_dir) + to_chat(user, "This is already painted [floor_state] [dir2text(floor_dir)]!") + return + if(!istype(F)) to_chat(user, "\The [src] can only be used on station flooring.") return + playsound(loc, usesound, 30, TRUE) F.icon_state = floor_state F.icon_regular_floor = floor_state F.dir = floor_dir diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 7a6b41966a8..e96fceb8b6c 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -191,7 +191,7 @@ if(CanUse(U)) if(!Use(U)) return - to_chat(U, "You replace [target.fitting] with [src].") + to_chat(U, "You replace the light [target.fitting] with [src].") if(target.status != LIGHT_EMPTY) AddShards(1, U) diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index b3695229790..42c134736d7 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -18,13 +18,6 @@ name = "syndicate personal AI device" faction = list("syndicate") -/obj/item/paicard/relaymove(var/mob/user, var/direction) - if(user.stat || user.stunned) - return - var/obj/item/rig/rig = get_rig() - if(istype(rig)) - rig.forced_move(direction, user) - /obj/item/paicard/New() ..() overlays += "pai-off" diff --git a/code/game/objects/items/devices/pipe_painter.dm b/code/game/objects/items/devices/pipe_painter.dm index 842a96c2af2..c386e64a19d 100644 --- a/code/game/objects/items/devices/pipe_painter.dm +++ b/code/game/objects/items/devices/pipe_painter.dm @@ -1,8 +1,9 @@ /obj/item/pipe_painter name = "pipe painter" - icon = 'icons/obj/bureaucracy.dmi' - icon_state = "labeler1" - item_state = "flight" + icon = 'icons/obj/device.dmi' + icon_state = "pipe_painter" + item_state = "pipe_painter" + usesound = 'sound/effects/spray2.ogg' var/list/modes var/mode @@ -18,13 +19,19 @@ return var/obj/machinery/atmospherics/pipe/P = A + if(P.pipe_color == "[GLOB.pipe_colors[mode]]") + to_chat(user, "This pipe is aready painted [mode]!") + return + var/turf/T = P.loc if(P.level < 2 && T.level==1 && isturf(T) && T.intact) to_chat(user, "You must remove the plating first.") return + playsound(loc, usesound, 30, TRUE) P.change_color(GLOB.pipe_colors[mode]) + /obj/item/pipe_painter/attack_self(mob/user as mob) mode = input("Which colour do you want to use?", "Pipe Painter", mode) in modes diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index 284ce8d5598..c9d95dd8a99 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -10,8 +10,6 @@ materials = list(MAT_METAL=10000, MAT_GLASS=2500) var/code = 2 - is_special = 1 - /obj/item/radio/electropack/attack_hand(mob/user as mob) if(src == user.back) to_chat(user, "You need help taking this off!") @@ -54,23 +52,6 @@ if(src.flags & NODROP) A.flags |= NODROP -/obj/item/radio/electropack/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["freq"]) - var/new_frequency = sanitize_frequency(frequency + text2num(href_list["freq"])) - set_frequency(new_frequency) - - else if(href_list["code"]) - code += text2num(href_list["code"]) - code = round(code) - code = clamp(code, 1, 100) - - else if(href_list["power"]) - on = !on - - add_fingerprint(usr) /obj/item/radio/electropack/receive_signal(datum/signal/signal) if(!signal || signal.encryption != code) @@ -96,18 +77,48 @@ return -/obj/item/radio/electropack/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/radio/electropack/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "radio_electro.tmpl", "[name]", 400, 500) + ui = new(user, src, ui_key, "Electropack", name, 360, 150, master_ui, state) ui.open() - ui.set_auto_update(1) - -/obj/item/radio/electropack/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/item/radio/electropack/tgui_data(mob/user) + var/list/data = list() data["power"] = on - data["freq"] = format_frequency(frequency) + data["frequency"] = frequency data["code"] = code - + data["minFrequency"] = PUBLIC_LOW_FREQ + data["maxFrequency"] = PUBLIC_HIGH_FREQ return data + +/obj/item/radio/electropack/tgui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("power") + on = !on + if("freq") + var/value = params["freq"] + if(value) + frequency = sanitize_frequency(value) + set_frequency(frequency) + else + . = FALSE + if("code") + var/value = text2num(params["code"]) + if(value) + value = round(value) + code = clamp(value, 1, 100) + else + . = FALSE + if("reset") + if(params["reset"] == "freq") + frequency = initial(frequency) + else if(params["reset"] == "code") + code = initial(code) + else + . = FALSE + if(.) + add_fingerprint(usr) diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 691c12e1347..2142dc9e50b 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -9,7 +9,6 @@ "Vox Armalis" = 'icons/mob/species/armalis/ears.dmi' ) //We read you loud and skree-er. materials = list(MAT_METAL=75) - subspace_transmission = TRUE canhear_range = 0 // can't hear headsets from very far away slot_flags = SLOT_EARS @@ -46,9 +45,6 @@ QDEL_NULL(keyslot2) return ..() -/obj/item/radio/headset/list_channels(var/mob/user) - return list_secure_channels() - /obj/item/radio/headset/examine(mob/user) . = ..() if(in_range(src, user) && radio_desc) @@ -91,6 +87,7 @@ ks1type = /obj/item/encryptionkey/syndicate/nukeops requires_tcomms = FALSE instant = TRUE // Work instantly if there are no comms + freqlock = TRUE /obj/item/radio/headset/syndicate/alt //undisguised bowman with flash protection name = "syndicate headset" @@ -289,6 +286,7 @@ icon_state = "com_headset" item_state = "headset" ks2type = /obj/item/encryptionkey/ert + freqlock = TRUE /obj/item/radio/headset/ert/alt name = "\proper emergency response team's bowman headset" @@ -367,7 +365,7 @@ else to_chat(user, "This headset doesn't have any encryption keys! How useless...") -/obj/item/radio/headset/proc/recalculateChannels(var/setDescription = FALSE) +/obj/item/radio/headset/recalculateChannels(setDescription = FALSE) channels = list() translate_binary = FALSE translate_hive = FALSE diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 59d2c2a99bd..d1d4e900543 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -6,9 +6,7 @@ w_class = WEIGHT_CLASS_BULKY canhear_range = 2 flags = CONDUCT - var/number = 0 var/circuitry_installed = 1 - var/last_tick //used to delay the powercheck var/buildstage = 0 dog_fashion = null @@ -50,7 +48,7 @@ . = ..() buildstage = building if(buildstage) - START_PROCESSING(SSobj, src) + update_operating_status() else if(direction) setDir(direction) @@ -75,7 +73,6 @@ name = "illicit intercom" desc = "Talk through this. Evilly" frequency = SYND_FREQ - subspace_transmission = TRUE syndiekey = new /obj/item/encryptionkey/syndicate/nukeops /obj/item/radio/intercom/syndicate/New() @@ -85,7 +82,6 @@ /obj/item/radio/intercom/pirate name = "pirate radio intercom" desc = "You wouldn't steal a space shuttle. Piracy. It's a crime!" - subspace_transmission = 1 /obj/item/radio/intercom/pirate/New() ..() @@ -105,20 +101,17 @@ ) /obj/item/radio/intercom/Destroy() - STOP_PROCESSING(SSobj, src) GLOB.global_intercoms.Remove(src) return ..() -/obj/item/radio/intercom/attack_ai(mob/user as mob) +/obj/item/radio/intercom/attack_ai(mob/user) add_hiddenprint(user) add_fingerprint(user) - spawn(0) - attack_self(user) + attack_self(user) -/obj/item/radio/intercom/attack_hand(mob/user as mob) +/obj/item/radio/intercom/attack_hand(mob/user) add_fingerprint(user) - spawn(0) - attack_self(user) + attack_self(user) /obj/item/radio/intercom/receive_range(freq, level) if(!is_listening()) @@ -134,7 +127,7 @@ return canhear_range -/obj/item/radio/intercom/attackby(obj/item/W as obj, mob/user as mob) +/obj/item/radio/intercom/attackby(obj/item/W, mob/user) if(istype(W, /obj/item/stack/tape_roll)) //eww return else if(iscoil(W) && buildstage == 1) @@ -184,12 +177,12 @@ buildstage = 3 to_chat(user, "You secure the electronics!") update_icon() - START_PROCESSING(SSobj, src) + update_operating_status() for(var/i, i<= 5, i++) - wires.UpdateCut(i,1) + wires.on_cut(i, 1) /obj/item/radio/intercom/wirecutter_act(mob/user, obj/item/I) - if(!(buildstage == 3 && b_stat && wires.IsAllCut())) + if(!(buildstage == 3 && b_stat && wires.is_all_cut())) return . = TRUE if(!I.use_tool(src, user, 0, volume = I.tool_volume)) @@ -200,7 +193,7 @@ b_stat = 1 buildstage = 1 update_icon() - STOP_PROCESSING(SSobj, src) + update_operating_status(FALSE) /obj/item/radio/intercom/welder_act(mob/user, obj/item/I) if(buildstage != 0) @@ -220,20 +213,30 @@ return icon_state = "intercom[!on?"-p":""][b_stat ? "-open":""]" -/obj/item/radio/intercom/process() - if(((world.timeofday - last_tick) > 30) || ((world.timeofday - last_tick) < 0)) - last_tick = world.timeofday +/obj/item/radio/intercom/proc/update_operating_status(on = TRUE) + var/area/current_area = get_area(src) + if(!current_area) + return + if(on) + RegisterSignal(current_area, COMSIG_AREA_POWER_CHANGE, .proc/AreaPowerCheck) + else + UnregisterSignal(current_area, COMSIG_AREA_POWER_CHANGE) - - if(!src.loc) - on = 0 - else - var/area/A = get_area(src) - if(!A) - on = 0 - else - on = A.powered(EQUIP) // set "on" to the power status - update_icon() +/** + * Proc called whenever the intercom's area loses or gains power. Responsible for setting the `on` variable and calling `update_icon()`. + * + * Normally called after the intercom's area recieves the `COMSIG_AREA_POWER_CHANGE` signal, but it can also be called directly. + * Arguments: + * + * source - the area that just had a power change. + */ +/obj/item/radio/intercom/proc/AreaPowerCheck(datum/source) + var/area/current_area = get_area(src) + if(!current_area) + on = FALSE + else + on = current_area.powered(EQUIP) // set "on" to the equipment power status of our area. + update_icon() /obj/item/intercom_electronics name = "intercom electronics" @@ -247,14 +250,7 @@ usesound = 'sound/items/deconstruct.ogg' /obj/item/radio/intercom/locked - var/locked_frequency - -/obj/item/radio/intercom/locked/set_frequency(var/frequency) - if(frequency == locked_frequency) - ..(locked_frequency) - -/obj/item/radio/intercom/locked/list_channels() - return "" + freqlock = TRUE /obj/item/radio/intercom/locked/ai_private name = "\improper AI intercom" @@ -270,4 +266,4 @@ /obj/item/radio/intercom/locked/prison/New() ..() - wires.CutWireIndex(RADIO_WIRE_TRANSMIT) + wires.cut(WIRE_RADIO_TRANSMIT) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 009ec01ef43..b372727fef7 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -26,21 +26,37 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( suffix = "\[3\]" icon_state = "walkietalkie" item_state = "walkietalkie" - var/on = 1 // 0 for off + /// boolean for radio enabled or not + var/on = TRUE var/last_transmission - var/frequency = PUB_FREQ //common chat - var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies - var/canhear_range = 3 // the range which mobs can hear this radio from + var/frequency = PUB_FREQ + /// tune to frequency to unlock traitor supplies + var/traitor_frequency = 0 + /// the range which mobs can hear this radio from + var/canhear_range = 3 var/datum/wires/radio/wires = null var/b_stat = 0 - var/broadcasting = 0 - var/listening = 1 - var/list/channels = list() //see communications.dm for full list. First channes is a "default" for :h - var/subspace_transmission = 0 - var/obj/item/encryptionkey/syndicate/syndiekey = null //Holder for the syndicate encryption key if present - var/disable_timer = 0 //How many times this is disabled by EMPs - var/is_special = 0 //For electropacks mostly, skips Topic() checks + /// Whether the radio will broadcast stuff it hears, out over the radio + var/broadcasting = FALSE + /// Whether the radio is currently receiving + var/listening = TRUE + /// Whether the radio can be re-tuned to restricted channels it has no key for + var/freerange = FALSE + /// Whether the radio is able to have its primary frequency changed. Used for radios with weird primary frequencies, like DS, syndi, etc + var/freqlock = FALSE + + /// Whether the radio broadcasts to everyone within a few tiles, or not + var/loudspeaker = FALSE + /// Whether loudspeaker can be toggled by the user + var/has_loudspeaker = FALSE + + /// see communications.dm for full list. First channes is a "default" for :h + var/list/channels = list() + /// Holder for the syndicate encryption key if present + var/obj/item/encryptionkey/syndicate/syndiekey = null + /// How many times this is disabled by EMPs + var/disable_timer = 0 flags = CONDUCT slot_flags = SLOT_BELT @@ -76,6 +92,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( GLOB.global_radios |= src /obj/item/radio/Destroy() + SStgui.close_uis(wires) QDEL_NULL(wires) if(SSradio) SSradio.remove_object(src, frequency) @@ -99,76 +116,122 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( /obj/item/radio/attack_ghost(mob/user) return interact(user) -/obj/item/radio/attack_self(mob/user as mob) - user.set_machine(src) - interact(user) +/obj/item/radio/attack_self(mob/user) + tgui_interact(user) /obj/item/radio/interact(mob/user) if(!user) return 0 - if(b_stat) wires.Interact(user) + tgui_interact(user) - return ui_interact(user) - -/obj/item/radio/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/radio/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "radio_basic.tmpl", "[name]", 400, 550) + var/list/schannels = list_secure_channels(user) + var/list/ichannels = list_internal_channels(user) + var/calc_height = 150 + (schannels.len * 20) + (ichannels.len * 10) + ui = new(user, src, ui_key, "Radio", name, 400, calc_height, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/item/radio/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/item/radio/tgui_data(mob/user) + var/list/data = list() - data["mic_status"] = broadcasting - data["speaker"] = listening - data["freq"] = format_frequency(frequency) - data["rawfreq"] = num2text(frequency) - - data["mic_cut"] = (wires.IsIndexCut(RADIO_WIRE_TRANSMIT) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) - data["spk_cut"] = (wires.IsIndexCut(RADIO_WIRE_RECEIVE) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) - - var/list/chanlist = list_channels(user) - if(islist(chanlist) && chanlist.len) - data["chan_list"] = chanlist - data["chan_list_len"] = chanlist.len - - if(syndiekey) - data["useSyndMode"] = 1 + data["broadcasting"] = broadcasting + data["listening"] = listening + data["frequency"] = frequency + data["minFrequency"] = freerange ? RADIO_LOW_FREQ : PUBLIC_LOW_FREQ + data["maxFrequency"] = freerange ? RADIO_HIGH_FREQ : PUBLIC_HIGH_FREQ + data["canReset"] = frequency == initial(frequency) ? FALSE : TRUE + data["freqlock"] = freqlock + data["schannels"] = list_secure_channels(user) + data["ichannels"] = list_internal_channels(user) + data["has_loudspeaker"] = has_loudspeaker + data["loudspeaker"] = loudspeaker return data +/obj/item/radio/tgui_act(action, params, datum/tgui/ui) + if(..()) + return + . = TRUE + switch(action) + if("frequency") + if(freqlock) + return + var/tune = params["tune"] + var/adjust = text2num(params["adjust"]) + if(tune == "reset") + tune = initial(frequency) + else if(adjust) + tune = frequency + adjust * 10 + else if(text2num(tune) != null) + tune = tune * 10 + else + . = FALSE + if(hidden_uplink) + if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency)) + usr << browse(null, "window=radio") + if(.) + set_frequency(sanitize_frequency(tune, freerange)) + if("ichannel") // change primary frequency to an internal channel authorized by access + if(freqlock) + return + var/freq = params["ichannel"] + if(has_channel_access(usr, freq)) + set_frequency(text2num(freq)) + if("listen") + listening = !listening + if("broadcast") + broadcasting = !broadcasting + if("channel") + var/channel = params["channel"] + if(!(channel in channels)) + return + if(channels[channel] & FREQ_LISTENING) + channels[channel] &= ~FREQ_LISTENING + else + channels[channel] |= FREQ_LISTENING + if("loudspeaker") + // Toggle loudspeaker mode, AKA everyone around you hearing your radio. + if(has_loudspeaker) + loudspeaker = !loudspeaker + if(loudspeaker) + canhear_range = 3 + else + canhear_range = 0 + else + . = FALSE + if(.) + add_fingerprint(usr) -/obj/item/radio/proc/list_channels(var/mob/user) - return list_internal_channels(user) - -/obj/item/radio/proc/list_secure_channels(var/mob/user) - var/dat[0] - - for(var/ch_name in channels) - var/chan_stat = channels[ch_name] - var/listening = !!(chan_stat & FREQ_LISTENING) != 0 - - dat.Add(list(list("chan" = ch_name, "display_name" = ch_name, "secure_channel" = 1, "sec_channel_listen" = !listening, "chan_span" = SSradio.frequency_span_class(SSradio.radiochannels[ch_name])))) - +/obj/item/radio/proc/list_secure_channels(mob/user) + var/list/dat = list() + for(var/channel in channels) + dat[channel] = channels[channel] & FREQ_LISTENING return dat -/obj/item/radio/proc/list_internal_channels(var/mob/user) - var/dat[0] +/obj/item/radio/proc/list_internal_channels(mob/user) + var/list/dat = list() + if(freqlock) + return dat for(var/internal_chan in internal_channels) + var/freqnum = text2num(internal_chan) + var/freqname = get_frequency_name(freqnum) if(has_channel_access(user, internal_chan)) - dat.Add(list(list("chan" = internal_chan, "display_name" = get_frequency_name(text2num(internal_chan)), "chan_span" = SSradio.frequency_span_class(text2num(internal_chan))))) - + dat[freqname] = freqnum // unlike secure_channels, this is set to the freq number so Radio.js can use it as an arg return dat -/obj/item/radio/proc/has_channel_access(var/mob/user, var/freq) +/obj/item/radio/proc/has_channel_access(mob/user, freq) if(!user) - return 0 + return FALSE if(!(freq in internal_channels)) - return 0 + return FALSE + + if(isrobot(user)) + return FALSE // cyborgs and drones are not allowed to remotely re-tune intercomms, etc return user.has_internal_radio_channel_access(user, internal_channels[freq]) @@ -184,57 +247,10 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( return can_admin_interact() /obj/item/radio/proc/ToggleBroadcast() - broadcasting = !broadcasting && !(wires.IsIndexCut(RADIO_WIRE_TRANSMIT) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) + broadcasting = !broadcasting && !(wires.is_cut(WIRE_RADIO_TRANSMIT) || wires.is_cut(WIRE_RADIO_SIGNAL)) /obj/item/radio/proc/ToggleReception() - listening = !listening && !(wires.IsIndexCut(RADIO_WIRE_RECEIVE) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) - -/obj/item/radio/Topic(href, href_list) - if(..()) - return 1 - - if(is_special) - return 0 - - if(href_list["track"]) - var/mob/target = locate(href_list["track"]) - var/mob/living/silicon/ai/A = locate(href_list["track2"]) - if(A && target) - A.ai_actual_track(target) - . = 1 - - else if(href_list["freq"]) - var/new_frequency = (frequency + text2num(href_list["freq"])) - if((new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ)) - new_frequency = sanitize_frequency(new_frequency) - set_frequency(new_frequency) - if(hidden_uplink) - if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency)) - usr << browse(null, "window=radio") - . = 1 - else if(href_list["talk"]) - ToggleBroadcast() - . = 1 - else if(href_list["listen"]) - var/chan_name = href_list["ch_name"] - if(!chan_name) - ToggleReception() - else - if(channels[chan_name] & FREQ_LISTENING) - channels[chan_name] &= ~FREQ_LISTENING - else - channels[chan_name] |= FREQ_LISTENING - . = 1 - else if(href_list["spec_freq"]) - var freq = href_list["spec_freq"] - if(has_channel_access(usr, freq)) - set_frequency(text2num(freq)) - . = 1 - - if(href_list["nowindow"]) // here for pAIs, maybe others will want it, idk - return 1 - - add_fingerprint(usr) + listening = !listening && !(wires.is_cut(WIRE_RADIO_RECEIVER) || wires.is_cut(WIRE_RADIO_SIGNAL)) /obj/item/radio/proc/autosay(message, from, channel, role = "Unknown") //BS12 EDIT var/datum/radio_frequency/connection = null @@ -272,7 +288,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( tcm.sender_job = "Automated Announcement" tcm.vname = "synthesized voice" tcm.data = SIGNALTYPE_AINOTRACK - // Datum radios dont have a location (obviously + // Datum radios dont have a location (obviously) if(loc && loc.z) tcm.source_level = loc.z // For anyone that reads this: This used to pull from a LIST from the CONFIG DATUM. WHYYYYYYYYY!!!!!!!! -aa else @@ -325,7 +341,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( // If we were to send to a channel we don't have, drop it. return RADIO_CONNECTION_FAIL -/obj/item/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says") +/obj/item/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, verbage = "says") if(!on) return 0 // the device has to be on // Fix for permacell radios, but kinda eh about actually fixing them. @@ -334,7 +350,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( // Uncommenting this. To the above comment: // The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom - if(wires.IsIndexCut(RADIO_WIRE_TRANSMIT)) // The device has to have all its wires and shit intact + if(wires.is_cut(WIRE_RADIO_TRANSMIT)) // The device has to have all its wires and shit intact return 0 if(!M.IsVocal()) @@ -411,11 +427,16 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( jobname = "Unknown" voicemask = TRUE + // Copy the message pieces so we can safely edit comms line without affecting the actual line + var/list/message_pieces_copy = list() + for(var/datum/multilingual_say_piece/S in message_pieces) + message_pieces_copy += new /datum/multilingual_say_piece(S.speaking, S.message) + // Make us a message datum! var/datum/tcomms_message/tcm = new tcm.sender_name = displayname tcm.sender_job = jobname - tcm.message_pieces = message_pieces + tcm.message_pieces = message_pieces_copy tcm.source_level = position.z tcm.freq = connection.frequency tcm.vmask = voicemask @@ -423,6 +444,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( tcm.connection = connection tcm.vname = M.voice_name tcm.sender = M + tcm.verbage = verbage // Now put that through the stuff var/handled = FALSE if(connection) @@ -509,7 +531,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( var/is_listening = TRUE if(!on) is_listening = FALSE - if(!wires || wires.IsIndexCut(RADIO_WIRE_RECEIVE)) + if(!wires || wires.is_cut(WIRE_RADIO_RECEIVER)) is_listening = FALSE if(!listening) is_listening = FALSE @@ -575,25 +597,31 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( if(!disable_timer) on = 1 +/obj/item/radio/proc/recalculateChannels() + /// Exists so that borg radios and headsets can override it. + stack_trace("recalculateChannels() called on a radio which does not implement the proc.") + /////////////////////////////// //////////Borg Radios////////// /////////////////////////////// //Giving borgs their own radio to have some more room to work with -Sieve /obj/item/radio/borg + name = "Cyborg Radio" var/mob/living/silicon/robot/myborg = null // Cyborg which owns this radio. Used for power checks - var/obj/item/encryptionkey/keyslot = null//Borg radios can handle a single encryption key - var/shut_up = 1 + var/obj/item/encryptionkey/keyslot // Borg radios can handle a single encryption key icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. icon_state = "radio" + has_loudspeaker = TRUE + loudspeaker = FALSE canhear_range = 0 - subspace_transmission = 1 dog_fashion = null + freqlock = TRUE // don't let cyborgs change the default channel of their internal radio away from common /obj/item/radio/borg/syndicate keyslot = new /obj/item/encryptionkey/syndicate/nukeops -/obj/item/radio/borg/syndicate/CanUseTopic(mob/user, datum/topic_state/state) +/obj/item/radio/borg/syndicate/tgui_status(mob/user, datum/tgui_state/state) . = ..() if(. == STATUS_UPDATE && istype(user, /mob/living/silicon/robot/syndicate)) . = STATUS_INTERACTIVE @@ -602,19 +630,18 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( myborg = null return ..() -/obj/item/radio/borg/list_channels(var/mob/user) - return list_secure_channels(user) - /obj/item/radio/borg/syndicate/New() ..() syndiekey = keyslot set_frequency(SYND_FREQ) + freqlock = TRUE /obj/item/radio/borg/deathsquad /obj/item/radio/borg/deathsquad/New() ..() set_frequency(DTH_FREQ) + freqlock = TRUE /obj/item/radio/borg/ert keyslot = new /obj/item/encryptionkey/ert @@ -622,6 +649,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( /obj/item/radio/borg/ert/New() ..() set_frequency(ERT_FREQ) + freqlock = TRUE /obj/item/radio/borg/ert/specops keyslot = new /obj/item/encryptionkey/centcom @@ -666,7 +694,7 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( else to_chat(user, "This radio doesn't have any encryption keys!") -/obj/item/radio/borg/proc/recalculateChannels() +/obj/item/radio/borg/recalculateChannels() channels = list() syndiekey = null @@ -699,72 +727,12 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( return -/obj/item/radio/borg/Topic(href, href_list) - if(..()) - return 1 - if(href_list["mode"]) - var/enable_subspace_transmission = text2num(href_list["mode"]) - if(enable_subspace_transmission != subspace_transmission) - subspace_transmission = !subspace_transmission - if(subspace_transmission) - to_chat(usr, "Subspace Transmission is enabled.") - else - to_chat(usr, "Subspace Transmission is disabled.") - if(subspace_transmission == 0)//Simple as fuck, clears the channel list to prevent talking/listening over them if subspace transmission is disabled - channels = list() - else - recalculateChannels() - . = 1 - if(href_list["shutup"]) // Toggle loudspeaker mode, AKA everyone around you hearing your radio. - var/do_shut_up = text2num(href_list["shutup"]) - if(do_shut_up != shut_up) - shut_up = !shut_up - if(shut_up) - canhear_range = 0 - to_chat(usr, "Loudspeaker disabled.") - else - canhear_range = 3 - to_chat(usr, "Loudspeaker enabled.") - . = 1 - - -/obj/item/radio/borg/interact(mob/user as mob) +/obj/item/radio/borg/interact(mob/user) if(!on) return - . = ..() -/obj/item/radio/borg/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "radio_basic.tmpl", "[name]", 430, 500) - ui.open() - ui.set_auto_update(1) - -/obj/item/radio/borg/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - data["mic_status"] = broadcasting - data["speaker"] = listening - data["freq"] = format_frequency(frequency) - data["rawfreq"] = num2text(frequency) - - var/list/chanlist = list_channels(user) - if(islist(chanlist) && chanlist.len) - data["chan_list"] = chanlist - data["chan_list_len"] = chanlist.len - - if(syndiekey) - data["useSyndMode"] = 1 - - data["has_loudspeaker"] = 1 - data["loudspeaker"] = !shut_up - data["has_subspace"] = 1 - data["subspace"] = subspace_transmission - - return data - /obj/item/radio/proc/config(op) if(SSradio) for(var/ch_name in channels) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index f20a2cc5875..43697ac4776 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -72,7 +72,7 @@ REAGENT SCANNER var/turf/U = O.loc if(U && U.intact) O.invisibility = 101 - O.alpha = 255 + O.alpha = 255 for(var/mob/living/M in T.contents) var/oldalpha = M.alpha if(M.alpha < 255 && istype(M)) diff --git a/code/game/objects/items/devices/sensor_device.dm b/code/game/objects/items/devices/sensor_device.dm index b83128cf4a5..9133bf37b9c 100644 --- a/code/game/objects/items/devices/sensor_device.dm +++ b/code/game/objects/items/devices/sensor_device.dm @@ -19,5 +19,5 @@ /obj/item/sensor_device/attack_self(mob/user as mob) tgui_interact(user) -/obj/item/sensor_device/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) +/obj/item/sensor_device/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) crew_monitor.tgui_interact(user, ui_key, ui, force_open) diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index acba0087caf..9035207018a 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -46,7 +46,7 @@ w_class = I.w_class update_icon() - SSnanoui.update_uis(src) // update all UIs attached to src + SStgui.update_uis(src) // update all UIs attached to src //TODO: Have this take an assemblyholder else if(isassembly(I)) var/obj/item/assembly/A = I @@ -62,12 +62,14 @@ to_chat(user, "You attach the [A] to the valve controls and secure it.") A.holder = src A.toggle_secure() //this calls update_icon(), which calls update_icon() on the holder (i.e. the bomb). + if(istype(attached_device, /obj/item/assembly/prox_sensor)) + AddComponent(/datum/component/proximity_monitor) investigate_log("[key_name(user)] attached a [A] to a transfer valve.", INVESTIGATE_BOMB) - msg_admin_attack("[key_name_admin(user)]attached [A] to a transfer valve.", ATKLOG_FEW) + add_attack_logs(user, src, "attached [A] to a transfer valve", ATKLOG_FEW) log_game("[key_name_admin(user)] attached [A] to a transfer valve.") attacher = user - SSnanoui.update_uis(src) // update all UIs attached to src + SStgui.update_uis(src) // update all UIs attached to src /obj/item/transfer_valve/HasProximity(atom/movable/AM) @@ -86,64 +88,65 @@ O.hear_message(M, msg) /obj/item/transfer_valve/attack_self(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/item/transfer_valve/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/transfer_valve/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, "transfer_valve.tmpl", "Tank Transfer Valve", 460, 280) - // open the new ui window + ui = new(user, src, ui_key, "TransferValve", name, 460, 320, master_ui, state) ui.open() - // auto update every Master Controller tick - //ui.set_auto_update(1) - -/obj/item/transfer_valve/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - data["attachmentOne"] = tank_one ? tank_one.name : null - data["attachmentTwo"] = tank_two ? tank_two.name : null - data["valveAttachment"] = attached_device ? attached_device.name : null - data["valveOpen"] = valve_open ? 1 : 0 +/obj/item/transfer_valve/tgui_data(mob/user) + var/list/data = list() + data["tank_one"] = tank_one ? tank_one.name : null + data["tank_two"] = tank_two ? tank_two.name : null + data["attached_device"] = attached_device ? attached_device.name : null + data["valve"] = valve_open return data -/obj/item/transfer_valve/Topic(href, href_list) - ..() - if(usr.incapacitated()) - return 0 - if(loc != usr) - return 0 - if(tank_one && href_list["tankone"]) - split_gases() - valve_open = 0 - tank_one.forceMove(get_turf(src)) - tank_one = null + + +/obj/item/transfer_valve/tgui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("tankone") + if(tank_one) + split_gases() + valve_open = FALSE + tank_one.forceMove(get_turf(src)) + tank_one = null + update_icon() + if((!tank_two || tank_two.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) + w_class = WEIGHT_CLASS_NORMAL + if("tanktwo") + if(tank_two) + split_gases() + valve_open = FALSE + tank_two.forceMove(get_turf(src)) + tank_two = null + update_icon() + if((!tank_one || tank_one.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) + w_class = WEIGHT_CLASS_NORMAL + if("toggle") + toggle_valve(usr) + if("device") + if(attached_device) + attached_device.attack_self(usr) + if("remove_device") + if(attached_device) + attached_device.forceMove(get_turf(src)) + attached_device.holder = null + attached_device = null + qdel(GetComponent(/datum/component/proximity_monitor)) + update_icon() + else + . = FALSE + if(.) update_icon() - if((!tank_two || tank_two.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) - w_class = WEIGHT_CLASS_NORMAL - else if(tank_two && href_list["tanktwo"]) - split_gases() - valve_open = 0 - tank_two.forceMove(get_turf(src)) - tank_two = null - update_icon() - if((!tank_one || tank_one.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) - w_class = WEIGHT_CLASS_NORMAL - else if(href_list["open"]) - toggle_valve() - else if(attached_device) - if(href_list["rem_device"]) - attached_device.forceMove(get_turf(src)) - attached_device.holder = null - attached_device = null - update_icon() - if(href_list["device"]) - attached_device.attack_self(usr) - add_fingerprint(usr) - return 1 // Returning 1 sends an update to attached UIs + add_fingerprint(usr) + /obj/item/transfer_valve/proc/process_activation(obj/item/D) if(toggle) @@ -190,7 +193,7 @@ it explodes properly when it gets a signal (and it does). */ -/obj/item/transfer_valve/proc/toggle_valve() +/obj/item/transfer_valve/proc/toggle_valve(mob/user) if(!valve_open && tank_one && tank_two) valve_open = 1 var/turf/bombturf = get_turf(src) @@ -207,6 +210,8 @@ investigate_log("Bomb valve opened at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with [attached_device ? attached_device : "no device"], attached by [attacher_name]. Last touched by: [key_name(mob)]", INVESTIGATE_BOMB) message_admins("Bomb valve opened at [A.name] (JMP) with [attached_device ? attached_device : "no device"], attached by [attacher_name]. Last touched by: [key_name_admin(mob)]") log_game("Bomb valve opened at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with [attached_device ? attached_device : "no device"], attached by [attacher_name]. Last touched by: [key_name(mob)]") + if(user) + add_attack_logs(user, src, "Bomb valve opened with [attached_device ? attached_device : "no device"], attached by [attacher_name]. Last touched by: [key_name(mob)]", ATKLOG_FEW) merge_gases() spawn(20) // In case one tank bursts for(var/i in 1 to 5) diff --git a/code/game/objects/items/devices/uplinks.dm b/code/game/objects/items/devices/uplinks.dm index 77e9eacc6a1..06f728ad748 100644 --- a/code/game/objects/items/devices/uplinks.dm +++ b/code/game/objects/items/devices/uplinks.dm @@ -9,14 +9,12 @@ A list of items and costs is stored under the datum of every game mode, alongsid GLOBAL_LIST_EMPTY(world_uplinks) /obj/item/uplink - var/welcome // Welcoming menu message var/uses // Numbers of crystals var/hidden_crystals = 0 - var/list/uplink_items // List of categories with lists of items - var/list/ItemsReference // List of references with an associated item - var/list/nanoui_items // List of items for NanoUI use - var/nanoui_menu = 0 // The current menu we are in - var/list/nanoui_data = new // Additional data for NanoUI use + /// List of categories with items inside + var/list/uplink_cats + /// List of all items in total (For buying random) + var/list/uplink_items var/purchase_log = "" var/uplink_owner = null//text-only @@ -26,12 +24,11 @@ GLOBAL_LIST_EMPTY(world_uplinks) var/temp_category var/uplink_type = "traitor" -/obj/item/uplink/nano_host() +/obj/item/uplink/tgui_host() return loc /obj/item/uplink/New() ..() - welcome = SSticker.mode.uplink_welcome uses = SSticker.mode.uplink_uses uplink_items = get_uplink_items() @@ -41,97 +38,45 @@ GLOBAL_LIST_EMPTY(world_uplinks) GLOB.world_uplinks -= src return ..() -/obj/item/uplink/proc/generate_items(mob/user as mob) - var/datum/nano_item_lists/IL = generate_item_lists(user) - nanoui_items = IL.items_nano - ItemsReference = IL.items_reference - -// BS12 no longer use this menu but there are forks that do, hency why we keep it -/obj/item/uplink/proc/generate_menu(mob/user as mob) +/** + * Build the item lists for use with the UI + * + * Generates a list of items for use in the UI, based on job and other parameters + * + * Arguments: + * * user - User to check + */ +/obj/item/uplink/proc/generate_item_lists(mob/user) if(!job) job = user.mind.assigned_role - var/dat = "[src.welcome]
    " - dat += "Telecrystals left: [src.uses]
    " - dat += "
    " - dat += "Request item:
    " - dat += "Each item costs a number of telecrystals as indicated by the number following its name.
    " - - var/category_items = 1 - for(var/category in uplink_items) - if(category_items < 1) - dat += "We apologize, as you could not afford anything from this category.
    " - dat += "
    " - dat += "[category]
    " - category_items = 0 - - for(var/datum/uplink_item/I in uplink_items[category]) - if(I.cost > uses) - continue - if(I.job && I.job.len) - if(!(I.job.Find(job))) - continue - dat += "[I.name] ([I.cost])" - if(I.hijack_only) - dat += " (HIJACK ONLY)" - dat += "
    " - category_items++ - - dat += "Random Item (??)
    " - dat += "
    " - return dat - -/* - Built the item lists for use with NanoUI -*/ -/obj/item/uplink/proc/generate_item_lists(mob/user as mob) - if(!job) - job = user.mind.assigned_role - - var/list/nano = new - var/list/reference = new + var/list/cats = list() for(var/category in uplink_items) - nano[++nano.len] = list("Category" = category, "items" = list()) + cats[++cats.len] = list("cat" = category, "items" = list()) for(var/datum/uplink_item/I in uplink_items[category]) if(I.job && I.job.len) if(!(I.job.Find(job))) continue - nano[nano.len]["items"] += list(list("Name" = sanitize(I.name), "Description" = sanitize(I.description()),"Cost" = I.cost, "hijack_only" = I.hijack_only, "obj_path" = I.reference)) - reference[I.reference] = I + cats[cats.len]["items"] += list(list("name" = sanitize(I.name), "desc" = sanitize(I.description()),"cost" = I.cost, "hijack_only" = I.hijack_only, "obj_path" = I.reference, "refundable" = I.refundable)) + uplink_items[I.reference] = I - var/datum/nano_item_lists/result = new - result.items_nano = nano - result.items_reference = reference - return result + uplink_cats = cats //If 'random' was selected /obj/item/uplink/proc/chooseRandomItem() if(uses <= 0) return - var/list/random_items = new - for(var/IR in ItemsReference) - var/datum/uplink_item/UI = ItemsReference[IR] + var/list/random_items = list() + + for(var/IR in uplink_items) + var/datum/uplink_item/UI = uplink_items[IR] if(UI.cost <= uses && UI.limited_stock != 0) random_items += UI + return pick(random_items) -/obj/item/uplink/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["refund"]) - refund(usr) - - if(href_list["buy_item"] == "random") - var/datum/uplink_item/UI = chooseRandomItem() - href_list["buy_item"] = UI.reference - return buy(UI, "RN") - else - var/datum/uplink_item/UI = ItemsReference[href_list["buy_item"]] - return buy(UI, UI ? UI.reference : "") - /obj/item/uplink/proc/buy(var/datum/uplink_item/UI, var/reference) if(!UI) return @@ -141,17 +86,9 @@ GLOBAL_LIST_EMPTY(world_uplinks) UI.buy(src,usr) if(UI.limited_stock > 0) // only decrement it if it's actually limited UI.limited_stock-- - SSnanoui.update_uis(src) + SStgui.update_uis(src) - /* var/list/L = UI.spawn_item(get_turf(usr),src) - if(ishuman(usr)) - var/mob/living/carbon/human/A = usr - for(var/obj/I in L) - A.put_in_any_hand_if_possible(I) - - purchase_log[UI] = purchase_log[UI] + 1 */ - - return 1 + return TRUE /obj/item/uplink/proc/refund(mob/user as mob) var/obj/item/I = user.get_active_hand() @@ -167,6 +104,8 @@ GLOBAL_LIST_EMPTY(world_uplinks) to_chat(user, "[I] refunded.") qdel(I) return + // If we are here, we didnt refund + to_chat(user, "[I] is not refundable.") // HIDDEN UPLINK - Can be stored in anything but the host item has to have a trigger for it. /* How to create an uplink in 3 easy steps! @@ -208,92 +147,76 @@ GLOBAL_LIST_EMPTY(world_uplinks) /obj/item/uplink/hidden/proc/check_trigger(mob/user as mob, var/value, var/target) if(value == target) trigger(user) - return 1 - return 0 + return TRUE + return FALSE -/* - NANO UI FOR UPLINK WOOP WOOP -*/ -/obj/item/uplink/hidden/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state) - var/title = "Remote Uplink" - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/uplink/hidden/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, "uplink.tmpl", title, 700, 600, state = state) - // open the new ui window + ui = new(user, src, ui_key, "Uplink", name, 900, 600, master_ui, state) ui.open() -/obj/item/uplink/hidden/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) - var/data[0] +/obj/item/uplink/hidden/tgui_data(mob/user) + var/list/data = list() - data["welcome"] = welcome data["crystals"] = uses - data["menu"] = nanoui_menu - if(!nanoui_items) - generate_items(user) - data["nano_items"] = nanoui_items - data["category_choice"] = temp_category - data += nanoui_data return data +/obj/item/uplink/hidden/tgui_static_data(mob/user) + var/list/data = list() + + // Actual items + if(!uplink_cats || !uplink_items) + generate_item_lists(user) + data["cats"] = uplink_cats + + // Exploitable info + var/list/exploitable = list() + for(var/datum/data/record/L in GLOB.data_core.general) + exploitable += list(list( + "name" = html_encode(L.fields["name"]), + "sex" = html_encode(L.fields["sex"]), + "age" = html_encode(L.fields["age"]), + "species" = html_encode(L.fields["species"]), + "rank" = html_encode(L.fields["rank"]), + "fingerprint" = html_encode(L.fields["fingerprint"]) + )) + + data["exploitable"] = exploitable + + return data + + // Interaction code. Gathers a list of items purchasable from the paren't uplink and displays it. It also adds a lock button. /obj/item/uplink/hidden/interact(mob/user) - ui_interact(user) + tgui_interact(user) // The purchasing code. -/obj/item/uplink/hidden/Topic(href, href_list) - if(usr.stat || usr.restrained()) - return 1 +/obj/item/uplink/hidden/tgui_act(action, list/params) + if(..()) + return - if(!( istype(usr, /mob/living/carbon/human))) - return 1 - var/mob/user = usr - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") - if((usr.contents.Find(src.loc) || (in_range(src.loc, usr) && istype(src.loc.loc, /turf)))) - usr.set_machine(src) - if(..(href, href_list)) - return 1 - else if(href_list["lock"]) + . = TRUE + + switch(action) + if("lock") toggle() uses += hidden_crystals hidden_crystals = 0 - ui.close() - return 1 - if(href_list["menu"]) - nanoui_menu = text2num(href_list["menu"]) - update_nano_data(href_list["id"]) - if(href_list["category"]) - temp_category = href_list["category"] - update_nano_data() + SStgui.close_uis(src) - SSnanoui.update_uis(src) - return 1 + if("refund") + refund(usr) -/obj/item/uplink/hidden/proc/update_nano_data(var/id) - if(nanoui_menu == 1) - var/permanentData[0] - for(var/datum/data/record/L in sortRecord(GLOB.data_core.general)) - permanentData[++permanentData.len] = list(Name = sanitize(L.fields["name"]),"id" = L.fields["id"]) - nanoui_data["exploit_records"] = permanentData + if("buyRandom") + var/datum/uplink_item/UI = chooseRandomItem() + return buy(UI, "RN") - if(nanoui_menu == 11) - nanoui_data["exploit_exists"] = 0 + if("buyItem") + var/datum/uplink_item/UI = uplink_items[params["item"]] + return buy(UI, UI ? UI.reference : "") - for(var/datum/data/record/L in GLOB.data_core.general) - if(L.fields["id"] == id) - nanoui_data["exploit"] = list() // Setting this to equal L.fields passes it's variables that are lists as reference instead of value. - nanoui_data["exploit"]["name"] = html_encode(L.fields["name"]) - nanoui_data["exploit"]["sex"] = html_encode(L.fields["sex"]) - nanoui_data["exploit"]["age"] = html_encode(L.fields["age"]) - nanoui_data["exploit"]["species"] = html_encode(L.fields["species"]) - nanoui_data["exploit"]["rank"] = html_encode(L.fields["rank"]) - nanoui_data["exploit"]["fingerprint"] = html_encode(L.fields["fingerprint"]) - - nanoui_data["exploit_exists"] = 1 - break // I placed this here because of how relevant it is. // You place this in your uplinkable item to check if an uplink is active or not. diff --git a/code/game/objects/items/flag.dm b/code/game/objects/items/flag.dm index 14bb468e049..0dda06d3e5c 100644 --- a/code/game/objects/items/flag.dm +++ b/code/game/objects/items/flag.dm @@ -242,15 +242,16 @@ to_chat(user, "You hide [I] in the [src]. It will detonate some time after the flag is lit on fire.") var/turf/bombturf = get_turf(src) var/area/A = get_area(bombturf) - message_admins("[key_name_admin(user)] has hidden [I] in the [src] ready for detonation at [A.name] (JMP).") log_game("[key_name(user)] has hidden [I] in the [src] ready for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).") investigate_log("[key_name(user)] has hidden [I] in the [src] ready for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).", INVESTIGATE_BOMB) + add_attack_logs(user, src, "has hidden [I] ready for detonation in", ATKLOG_MOST) else if(is_hot(I) && !(resistance_flags & ON_FIRE) && boobytrap && trapper) var/turf/bombturf = get_turf(src) var/area/A = get_area(bombturf) - message_admins("[key_name_admin(user)] has lit the [src] trapped with [boobytrap] by [key_name_admin(trapper)] at [A.name] (JMP).") log_game("[key_name_admin(user)] has lit the [src] trapped with [boobytrap] by [key_name_admin(trapper)] at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).") investigate_log("[key_name_admin(user)] has lit the [src] trapped with [boobytrap] by [key_name_admin(trapper)] at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).", INVESTIGATE_BOMB) + add_attack_logs(user, src, "has lit (booby trapped with [boobytrap]", ATKLOG_FEW) + burn() else return ..() @@ -267,8 +268,16 @@ /obj/item/flag/chameleon/burn() if(boobytrap) - boobytrap.prime() - ..() + fire_act() + addtimer(CALLBACK(src, .proc/prime_boobytrap), boobytrap.det_time) + else + ..() + +/obj/item/flag/chameleon/proc/prime_boobytrap() + boobytrap.forceMove(get_turf(loc)) + boobytrap.prime() + boobytrap = null + burn() /obj/item/flag/chameleon/updateFlagIcon() icon_state = updated_icon_state diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index eae59151a38..f1487a09986 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -29,7 +29,7 @@ "[user] has prodded you with [src]!") playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) - add_attack_logs(user, M, "Stunned with [src] (INTENT: [uppertext(user.a_intent)])") + add_attack_logs(user, M, "Stunned with [src] ([uppertext(user.a_intent)])") /obj/item/borg/overdrive name = "Overdrive" diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 2d2b110fa06..fe15d146aa4 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -183,22 +183,96 @@ return TRUE +/obj/item/borg/upgrade/abductor_engi + name = "engineering cyborg abductor upgrade" + desc = "An experimental upgrade that replaces an engineering cyborgs tools with the abductor version." + icon_state = "abductor_mod" + origin_tech = "engineering=6;materials=6;abductor=3" + require_module = TRUE + module_type = /obj/item/robot_module/engineering + +/obj/item/borg/upgrade/abductor_engi/action(mob/living/silicon/robot/R) + if(..()) + return + + for(var/obj/item/weldingtool/largetank/cyborg/W in R.module.modules) + qdel(W) + for(var/obj/item/screwdriver/cyborg/S in R.module.modules) + qdel(S) + for(var/obj/item/wrench/cyborg/E in R.module.modules) + qdel(E) + for(var/obj/item/crowbar/cyborg/C in R.module.modules) + qdel(C) + for(var/obj/item/wirecutters/cyborg/I in R.module.modules) + qdel(I) + for(var/obj/item/multitool/cyborg/M in R.module.modules) + qdel(M) + + R.module.modules += new /obj/item/weldingtool/abductor(R.module) + R.module.modules += new /obj/item/wrench/abductor(R.module) + R.module.modules += new /obj/item/screwdriver/abductor(R.module) + R.module.modules += new /obj/item/crowbar/abductor(R.module) + R.module.modules += new /obj/item/wirecutters/abductor(R.module) + R.module.modules += new /obj/item/multitool/abductor(R.module) + R.module.rebuild() + + return TRUE + +/obj/item/borg/upgrade/abductor_medi + name = "medical cyborg abductor upgrade" + desc = "An experimental upgrade that replaces a medical cyborgs tools with the abductor version." + icon_state = "abductor_mod" + origin_tech = "biotech=6;materials=6;abductor=3" + require_module = TRUE + module_type = /obj/item/robot_module/medical + +/obj/item/borg/upgrade/abductor_medi/action(mob/living/silicon/robot/R) + if(..()) + return + + for(var/obj/item/scalpel/laser/laser1/L in R.module.modules) + qdel(L) + for(var/obj/item/hemostat/H in R.module.modules) + qdel(H) + for(var/obj/item/retractor/E in R.module.modules) + qdel(E) + for(var/obj/item/bonegel/B in R.module.modules) + qdel(B) + for(var/obj/item/FixOVein/F in R.module.modules) + qdel(F) + for(var/obj/item/bonesetter/S in R.module.modules) + qdel(S) + for(var/obj/item/circular_saw/C in R.module.modules) + qdel(C) + for(var/obj/item/surgicaldrill/D in R.module.modules) + qdel(D) + + R.module.modules += new /obj/item/scalpel/laser/laser3(R.module) //no abductor laser scalpel, so next best thing. + R.module.modules += new /obj/item/hemostat/alien(R.module) + R.module.modules += new /obj/item/retractor/alien(R.module) + R.module.modules += new /obj/item/bonegel/alien(R.module) + R.module.modules += new /obj/item/FixOVein/alien(R.module) + R.module.modules += new /obj/item/bonesetter/alien(R.module) + R.module.modules += new /obj/item/circular_saw/alien(R.module) + R.module.modules += new /obj/item/surgicaldrill/alien(R.module) + R.module.rebuild() + + return TRUE + /obj/item/borg/upgrade/syndicate name = "safety override module" - desc = "Unlocks the hidden, deadlier functions of a cyborg. Also prevents emag subversion." + desc = "Unlocks the hidden, deadlier functions of a cyborg." icon_state = "cyborg_upgrade3" - origin_tech = "combat=4;syndicate=1" + origin_tech = "combat=6;materials=6" require_module = TRUE /obj/item/borg/upgrade/syndicate/action(mob/living/silicon/robot/R) if(..()) return - if(R.emagged) - return if(R.weapons_unlock) - to_chat(R, "Internal diagnostic error: incompatible upgrade module detected.") + to_chat(R, "Warning: Safety Overide Protocols have be disabled.") return - R.emagged = 1 + R.weapons_unlock = 1 return TRUE /obj/item/borg/upgrade/lavaproof diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 4d3e3181726..000ad2d225f 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -17,9 +17,9 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \ new/datum/stack_recipe/window("directional window", /obj/structure/window/basic, time = 0, on_floor = TRUE, window_checks = TRUE), \ new/datum/stack_recipe/window("fulltile window", /obj/structure/window/full/basic, 2, time = 0, on_floor = TRUE, window_checks = TRUE), \ - new/datum/stack_recipe("fishbowl", /obj/machinery/fishtank/bowl, 1, time = 0), \ - new/datum/stack_recipe("fish tank", /obj/machinery/fishtank/tank, 3, time = 0, on_floor = TRUE), \ - new/datum/stack_recipe("wall aquariam", /obj/machinery/fishtank/wall, 4, time = 0, on_floor = TRUE) \ + new/datum/stack_recipe("fishbowl", /obj/machinery/fishtank/bowl, 1, time = 10), \ + new/datum/stack_recipe("fish tank", /obj/machinery/fishtank/tank, 3, time = 20, on_floor = TRUE), \ + new/datum/stack_recipe("wall aquariam", /obj/machinery/fishtank/wall, 4, time = 40, on_floor = TRUE) \ )) /obj/item/stack/sheet/glass diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm index 6bdfdd49234..8b302cd338a 100644 --- a/code/game/objects/items/stacks/sheets/leather.dm +++ b/code/game/objects/items/stacks/sheets/leather.dm @@ -124,7 +124,7 @@ GLOBAL_LIST_INIT(leather_recipes, list ( new/datum/stack_recipe("leather satchel", /obj/item/storage/backpack/satchel, 5), new/datum/stack_recipe("bandolier", /obj/item/storage/belt/bandolier, 5), new/datum/stack_recipe("leather jacket", /obj/item/clothing/suit/jacket/leather, 7), - new/datum/stack_recipe("leather shoes", /obj/item/clothing/shoes/laceup, 2), + new/datum/stack_recipe("leather shoes", /obj/item/clothing/shoes/leather, 2), new/datum/stack_recipe("leather overcoat", /obj/item/clothing/suit/jacket/leather/overcoat, 10), new/datum/stack_recipe("hide mantle", /obj/item/clothing/suit/unathi/mantle, 4))) @@ -242,7 +242,7 @@ GLOBAL_LIST_INIT(sinew_recipes, list ( \ HS.amount++ src.use(1) wetness = initial(wetness) - break + return //If it gets to here it means it did not find a suitable stack on the tile. var/obj/item/stack/sheet/leather/HS = new(src.loc) HS.amount = 1 diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index 54d42176f52..9dabea08319 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -218,10 +218,22 @@ GLOBAL_LIST_INIT(sandbag_recipes, list ( \ /obj/item/stack/sheet/mineral/plasma/welder_act(mob/user, obj/item/I) if(I.use_tool(src, user, volume = I.tool_volume)) - message_admins("Plasma sheets ignited by [key_name_admin(user)]([ADMIN_QUE(user,"?")]) ([ADMIN_FLW(user,"FLW")]) in ([x],[y],[z] - JMP)",0,1) - log_game("Plasma sheets ignited by [key_name(user)] in ([x],[y],[z])") - investigate_log("was ignited by [key_name(user)]","atmos") - fire_act() + log_and_set_aflame(user, I) + return TRUE + +/obj/item/stack/sheet/mineral/plasma/attackby(obj/item/I, mob/living/user, params) + if(is_hot(I)) + log_and_set_aflame(user, I) + else + return ..() + +/obj/item/stack/sheet/mineral/plasma/proc/log_and_set_aflame(mob/user, obj/item/I) + var/turf/T = get_turf(src) + message_admins("Plasma sheets ignited by [key_name_admin(user)]([ADMIN_QUE(user, "?")]) ([ADMIN_FLW(user, "FLW")]) in ([COORD(T)] - [ADMIN_JMP(T)]") + log_game("Plasma sheets ignited by [key_name(user)] in [COORD(T)]") + investigate_log("was ignited by [key_name(user)]", "atmos") + user.create_log(MISC_LOG, "Plasma sheets ignited using [I]", src) + fire_act() /obj/item/stack/sheet/mineral/plasma/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) ..() diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index ac635eebef7..6b70ac015ca 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -15,6 +15,7 @@ */ GLOBAL_LIST_INIT(metal_recipes, list( new /datum/stack_recipe("stool", /obj/structure/chair/stool, one_per_turf = 1, on_floor = 1), + new /datum/stack_recipe("barstool", /obj/structure/chair/stool/bar, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("chair", /obj/structure/chair, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("shuttle seat", /obj/structure/chair/comfy/shuttle, 2, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("sofa (middle)", /obj/structure/chair/sofa, one_per_turf = 1, on_floor = 1), @@ -53,7 +54,6 @@ GLOBAL_LIST_INIT(metal_recipes, list( new /datum/stack_recipe/rods("metal rod", /obj/item/stack/rods, 1, 2, 50), null, new /datum/stack_recipe("computer frame", /obj/structure/computerframe, 5, time = 25, one_per_turf = 1, on_floor = 1), - new /datum/stack_recipe("modular console", /obj/machinery/modular_computer/console/buildable/, 10, time = 25, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("wall girders", /obj/structure/girder, 2, time = 50, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("machine frame", /obj/machinery/constructable_frame/machine_frame, 5, time = 25, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("turret frame", /obj/machinery/porta_turret_construct, 5, time = 25, one_per_turf = 1, on_floor = 1), @@ -471,7 +471,8 @@ GLOBAL_LIST_INIT(plastic_recipes, list( new /datum/stack_recipe("cane mould", /obj/item/kitchen/mould/cane, 1, on_floor = 1), \ new /datum/stack_recipe("cash mould", /obj/item/kitchen/mould/cash, 1, on_floor = 1), \ new /datum/stack_recipe("coin mould", /obj/item/kitchen/mould/coin, 1, on_floor = 1), \ - new /datum/stack_recipe("sucker mould", /obj/item/kitchen/mould/loli, 1, on_floor = 1))) + new /datum/stack_recipe("sucker mould", /obj/item/kitchen/mould/loli, 1, on_floor = 1), \ + new /datum/stack_recipe("warning cone", /obj/item/clothing/head/cone, 5, on_floor = 1))) /obj/item/stack/sheet/plastic name = "plastic" diff --git a/code/game/objects/items/tools/multitool.dm b/code/game/objects/items/tools/multitool.dm index c31f0666f50..db7194c00db 100644 --- a/code/game/objects/items/tools/multitool.dm +++ b/code/game/objects/items/tools/multitool.dm @@ -25,7 +25,7 @@ var/shows_wire_information = FALSE // shows what a wire does if set to TRUE var/obj/machinery/buffer // simple machine buffer for device linkage -/obj/item/multitool/proc/IsBufferA(var/typepath) +/obj/item/multitool/proc/IsBufferA(typepath) if(!buffer) return 0 return istype(buffer,typepath) @@ -41,12 +41,11 @@ to_chat(user, "You load [M] into [src]'s internal buffer.") return TRUE -// Syndicate device disguised as a multitool; it will turn red when an AI camera is nearby. - /obj/item/multitool/Destroy() buffer = null return ..() +// Syndicate device disguised as a multitool; it will turn red when an AI camera is nearby. /obj/item/multitool/ai_detect var/track_cooldown = 0 var/track_delay = 10 //How often it checks for proximity diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 66f35a169f9..5eddfae9bbb 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -35,7 +35,7 @@ desc = "A translucent balloon. There's nothing in it." icon = 'icons/obj/toy.dmi' icon_state = "waterballoon-e" - item_state = "balloon-empty" + item_state = "waterballoon-e" /obj/item/toy/balloon/New() ..() @@ -99,10 +99,10 @@ /obj/item/toy/balloon/update_icon() if(src.reagents.total_volume >= 1) icon_state = "waterballoon" - item_state = "balloon" + item_state = "waterballoon" else icon_state = "waterballoon-e" - item_state = "balloon-empty" + item_state = "waterballoon-e" /obj/item/toy/syndicateballoon name = "syndicate balloon" @@ -214,8 +214,9 @@ /obj/item/twohanded/dualsaber/toy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) return 0 -/obj/item/twohanded/dualsaber/toy/IsReflect()//Stops Toy Dualsabers from reflecting energy projectiles - return 0 +/obj/item/twohanded/dualsaber/toy/IsReflect() + if(wielded) + return 2 /obj/item/toy/katana name = "replica katana" @@ -344,56 +345,56 @@ /obj/item/toy/prize/ripley name = "toy ripley" - desc = "Mini-Mecha action figure! Collect them all! 1/11." + desc = "Mini-Mecha action figure! Collect them all! 1/11. This one is a ripley, a mining and engineering mecha." /obj/item/toy/prize/fireripley name = "toy firefighting ripley" - desc = "Mini-Mecha action figure! Collect them all! 2/11." + desc = "Mini-Mecha action figure! Collect them all! 2/11. This one is a firefighter ripley, a fireproof mining and engineering mecha." icon_state = "fireripleytoy" /obj/item/toy/prize/deathripley name = "toy deathsquad ripley" - desc = "Mini-Mecha action figure! Collect them all! 3/11." + desc = "Mini-Mecha action figure! Collect them all! 3/11. This one is the black ripley used by the hero of DeathSquad, that TV drama about loose-cannon ERT officers!" icon_state = "deathripleytoy" /obj/item/toy/prize/gygax name = "toy gygax" - desc = "Mini-Mecha action figure! Collect them all! 4/11." + desc = "Mini-Mecha action figure! Collect them all! 4/11. This one is the speedy gygax combat mecha. Zoom zoom, pew pew!" icon_state = "gygaxtoy" /obj/item/toy/prize/durand name = "toy durand" - desc = "Mini-Mecha action figure! Collect them all! 5/11." + desc = "Mini-Mecha action figure! Collect them all! 5/11. This one is the heavy durand combat mecha. Stomp stomp!" icon_state = "durandprize" /obj/item/toy/prize/honk name = "toy H.O.N.K." - desc = "Mini-Mecha action figure! Collect them all! 6/11." + desc = "Mini-Mecha action figure! Collect them all! 6/11. This one is the infamous H.O.N.K mech!" icon_state = "honkprize" /obj/item/toy/prize/marauder name = "toy marauder" - desc = "Mini-Mecha action figure! Collect them all! 7/11." + desc = "Mini-Mecha action figure! Collect them all! 7/11. This one is the powerful marauder combat mecha! Run for cover!" icon_state = "marauderprize" /obj/item/toy/prize/seraph name = "toy seraph" - desc = "Mini-Mecha action figure! Collect them all! 8/11." + desc = "Mini-Mecha action figure! Collect them all! 8/11. This one is the powerful seraph combat mecha! Someone's in trouble!" icon_state = "seraphprize" /obj/item/toy/prize/mauler name = "toy mauler" - desc = "Mini-Mecha action figure! Collect them all! 9/11." + desc = "Mini-Mecha action figure! Collect them all! 9/11. This one is the deadly mauler combat mecha! Look out!" icon_state = "maulerprize" /obj/item/toy/prize/odysseus name = "toy odysseus" - desc = "Mini-Mecha action figure! Collect them all! 10/11." + desc = "Mini-Mecha action figure! Collect them all! 10/11. This one is the spindly, syringe-firing odysseus medical mecha." icon_state = "odysseusprize" /obj/item/toy/prize/phazon name = "toy phazon" - desc = "Mini-Mecha action figure! Collect them all! 11/11." + desc = "Mini-Mecha action figure! Collect them all! 11/11. This one is the mysterious Phazon combat mecha! Nobody's safe!" icon_state = "phazonprize" @@ -404,7 +405,7 @@ -obj/item/toy/cards +/obj/item/toy/cards resistance_flags = FLAMMABLE max_integrity = 50 var/parentdeck = null @@ -416,14 +417,14 @@ obj/item/toy/cards var/card_throw_range = 20 var/list/card_attack_verb = list("attacked") -obj/item/toy/cards/New() +/obj/item/toy/cards/New() ..() -obj/item/toy/cards/proc/apply_card_vars(obj/item/toy/cards/newobj, obj/item/toy/cards/sourceobj) // Applies variables for supporting multiple types of card deck +/obj/item/toy/cards/proc/apply_card_vars(obj/item/toy/cards/newobj, obj/item/toy/cards/sourceobj) // Applies variables for supporting multiple types of card deck if(!istype(sourceobj)) return -obj/item/toy/cards/deck +/obj/item/toy/cards/deck name = "deck of cards" desc = "A deck of space-grade playing cards." icon = 'icons/obj/toy.dmi' @@ -433,7 +434,7 @@ obj/item/toy/cards/deck var/cooldown = 0 var/list/cards = list() -obj/item/toy/cards/deck/New() +/obj/item/toy/cards/deck/New() ..() icon_state = "deck_[deckstyle]_full" for(var/i in 2 to 10) @@ -458,7 +459,7 @@ obj/item/toy/cards/deck/New() cards += "Ace of Clubs" cards += "Ace of Diamonds" -obj/item/toy/cards/deck/attack_hand(mob/user as mob) +/obj/item/toy/cards/deck/attack_hand(mob/user as mob) var/choice = null if(cards.len == 0) icon_state = "deck_[deckstyle]_empty" @@ -476,14 +477,14 @@ obj/item/toy/cards/deck/attack_hand(mob/user as mob) visible_message("[user] draws a card from the deck.", "You draw a card from the deck.") update_icon() -obj/item/toy/cards/deck/attack_self(mob/user as mob) +/obj/item/toy/cards/deck/attack_self(mob/user as mob) if(cooldown < world.time - 50) cards = shuffle(cards) playsound(user, 'sound/items/cardshuffle.ogg', 50, 1) user.visible_message("[user] shuffles the deck.", "You shuffle the deck.") cooldown = world.time -obj/item/toy/cards/deck/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) +/obj/item/toy/cards/deck/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) ..() if(istype(C)) if(C.parentdeck == src) @@ -498,7 +499,7 @@ obj/item/toy/cards/deck/attackby(obj/item/toy/cards/singlecard/C, mob/living/use update_icon() -obj/item/toy/cards/deck/attackby(obj/item/toy/cards/cardhand/C, mob/living/user, params) +/obj/item/toy/cards/deck/attackby(obj/item/toy/cards/cardhand/C, mob/living/user, params) ..() if(istype(C)) if(C.parentdeck == src) @@ -512,7 +513,7 @@ obj/item/toy/cards/deck/attackby(obj/item/toy/cards/cardhand/C, mob/living/user, to_chat(user, "You can't mix cards from other decks.") update_icon() -obj/item/toy/cards/deck/MouseDrop(atom/over_object) +/obj/item/toy/cards/deck/MouseDrop(atom/over_object) var/mob/M = usr if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) return @@ -536,7 +537,7 @@ obj/item/toy/cards/deck/MouseDrop(atom/over_object) else to_chat(usr, "You can't reach it from here.") -obj/item/toy/cards/deck/update_icon() +/obj/item/toy/cards/deck/update_icon() switch(cards.len) if(0) icon_state = "deck_[deckstyle]_empty" @@ -547,7 +548,7 @@ obj/item/toy/cards/deck/update_icon() else icon_state = "deck_[deckstyle]_full" -obj/item/toy/cards/cardhand +/obj/item/toy/cards/cardhand name = "hand of cards" desc = "A number of cards not in a deck, customarily held in ones hand." icon = 'icons/obj/toy.dmi' @@ -557,11 +558,11 @@ obj/item/toy/cards/cardhand var/choice = null -obj/item/toy/cards/cardhand/attack_self(mob/user as mob) +/obj/item/toy/cards/cardhand/attack_self(mob/user as mob) user.set_machine(src) interact(user) -obj/item/toy/cards/cardhand/interact(mob/user) +/obj/item/toy/cards/cardhand/interact(mob/user) var/dat = "You have:
    " for(var/t in currenthand) dat += "A [t].
    " @@ -572,7 +573,7 @@ obj/item/toy/cards/cardhand/interact(mob/user) popup.open() -obj/item/toy/cards/cardhand/Topic(href, href_list) +/obj/item/toy/cards/cardhand/Topic(href, href_list) if(..()) return if(usr.stat || !ishuman(usr) || !usr.canmove) @@ -606,7 +607,7 @@ obj/item/toy/cards/cardhand/Topic(href, href_list) qdel(src) return -obj/item/toy/cards/cardhand/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) +/obj/item/toy/cards/cardhand/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) if(istype(C)) if(C.parentdeck == parentdeck) currenthand += C.cardname @@ -618,7 +619,7 @@ obj/item/toy/cards/cardhand/attackby(obj/item/toy/cards/singlecard/C, mob/living else to_chat(user, "You can't mix cards from other decks.") -obj/item/toy/cards/cardhand/apply_card_vars(obj/item/toy/cards/newobj,obj/item/toy/cards/sourceobj) +/obj/item/toy/cards/cardhand/apply_card_vars(obj/item/toy/cards/newobj,obj/item/toy/cards/sourceobj) ..() newobj.deckstyle = sourceobj.deckstyle newobj.icon_state = "[deckstyle]_hand2" // Another dumb hack, without this the hand is invisible (or has the default deckstyle) until another card is added. @@ -631,7 +632,7 @@ obj/item/toy/cards/cardhand/apply_card_vars(obj/item/toy/cards/newobj,obj/item/t newobj.resistance_flags = sourceobj.resistance_flags -obj/item/toy/cards/singlecard +/obj/item/toy/cards/singlecard name = "card" desc = "a card" icon = 'icons/obj/toy.dmi' @@ -642,7 +643,7 @@ obj/item/toy/cards/singlecard pixel_x = -5 -obj/item/toy/cards/singlecard/examine(mob/user) +/obj/item/toy/cards/singlecard/examine(mob/user) . = ..() if(get_dist(user, src) <= 0) if(ishuman(user)) @@ -653,7 +654,7 @@ obj/item/toy/cards/singlecard/examine(mob/user) . += "You need to have the card in your hand to check it." -obj/item/toy/cards/singlecard/verb/Flip() +/obj/item/toy/cards/singlecard/verb/Flip() set name = "Flip Card" set category = "Object" set src in range(1) @@ -674,7 +675,7 @@ obj/item/toy/cards/singlecard/verb/Flip() name = "card" pixel_x = -5 -obj/item/toy/cards/singlecard/attackby(obj/item/I, mob/living/user, params) +/obj/item/toy/cards/singlecard/attackby(obj/item/I, mob/living/user, params) if(istype(I, /obj/item/toy/cards/singlecard/)) var/obj/item/toy/cards/singlecard/C = I if(C.parentdeck == parentdeck) @@ -704,7 +705,7 @@ obj/item/toy/cards/singlecard/attackby(obj/item/I, mob/living/user, params) else to_chat(user, "You can't mix cards from other decks.") -obj/item/toy/cards/cardhand/update_icon() +/obj/item/toy/cards/cardhand/update_icon() switch(currenthand.len) if(0 to 1) return @@ -718,12 +719,12 @@ obj/item/toy/cards/cardhand/update_icon() icon_state = "[deckstyle]_hand5" -obj/item/toy/cards/singlecard/attack_self(mob/user) +/obj/item/toy/cards/singlecard/attack_self(mob/user) if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) return Flip() -obj/item/toy/cards/singlecard/apply_card_vars(obj/item/toy/cards/singlecard/newobj,obj/item/toy/cards/sourceobj) +/obj/item/toy/cards/singlecard/apply_card_vars(obj/item/toy/cards/singlecard/newobj,obj/item/toy/cards/sourceobj) ..() newobj.deckstyle = sourceobj.deckstyle newobj.icon_state = "singlecard_down_[deckstyle]" // Without this the card is invisible until flipped. It's an ugly hack, but it works. @@ -745,7 +746,7 @@ obj/item/toy/cards/singlecard/apply_card_vars(obj/item/toy/cards/singlecard/newo || Syndicate playing cards, for pretending you're Gambit and playing poker for the nuke disk. || */ -obj/item/toy/cards/deck/syndicate +/obj/item/toy/cards/deck/syndicate name = "suspicious looking deck of cards" desc = "A deck of space-grade playing cards. They seem unusually rigid." deckstyle = "syndicate" @@ -760,10 +761,10 @@ obj/item/toy/cards/deck/syndicate /* || Custom card decks || */ -obj/item/toy/cards/deck/black +/obj/item/toy/cards/deck/black deckstyle = "black" -obj/item/toy/cards/deck/syndicate/black +/obj/item/toy/cards/deck/syndicate/black deckstyle = "black" /obj/item/toy/nuke @@ -1591,182 +1592,217 @@ obj/item/toy/cards/deck/syndicate/black /obj/item/toy/figure/cmo name = "Chief Medical Officer action figure" + desc = "The ever-suffering CMO, from Space Life's SS12 figurine collection." icon_state = "cmo" toysay = "Suit sensors!" /obj/item/toy/figure/assistant name = "Assistant action figure" + desc = "The faceless, hairless scourge of the station, from Space Life's SS12 figurine collection." icon_state = "assistant" toysay = "Grey tide station wide!" /obj/item/toy/figure/atmos name = "Atmospheric Technician action figure" + desc = "The faithful atmospheric technician, from Space Life's SS12 figurine collection." icon_state = "atmos" toysay = "Glory to Atmosia!" /obj/item/toy/figure/bartender name = "Bartender action figure" + desc = "The suave bartender, from Space Life's SS12 figurine collection." icon_state = "bartender" toysay = "Wheres my monkey?" /obj/item/toy/figure/borg name = "Cyborg action figure" + desc = "The iron-willed cyborg, from Space Life's SS12 figurine collection." icon_state = "borg" toysay = "I. LIVE. AGAIN." /obj/item/toy/figure/botanist name = "Botanist action figure" + desc = "The drug-addicted botanist, from Space Life's SS12 figurine collection." icon_state = "botanist" toysay = "Dude, I see colors..." /obj/item/toy/figure/captain name = "Captain action figure" + desc = "The inept captain, from Space Life's SS12 figurine collection." icon_state = "captain" toysay = "Crew, the Nuke Disk is safely up my ass." /obj/item/toy/figure/cargotech name = "Cargo Technician action figure" + desc = "The hard-working cargo tech, from Space Life's SS12 figurine collection." icon_state = "cargotech" toysay = "For Cargonia!" /obj/item/toy/figure/ce name = "Chief Engineer action figure" + desc = "The expert Chief Engineer, from Space Life's SS12 figurine collection." icon_state = "ce" toysay = "Wire the solars!" /obj/item/toy/figure/chaplain name = "Chaplain action figure" + desc = "The obsessed Chaplain, from Space Life's SS12 figurine collection." icon_state = "chaplain" toysay = "Gods make me a killing machine please!" /obj/item/toy/figure/chef name = "Chef action figure" + desc = "The cannibalistic chef, from Space Life's SS12 figurine collection." icon_state = "chef" toysay = "I swear it's not human meat." /obj/item/toy/figure/chemist name = "Chemist action figure" + desc = "The legally dubious Chemist, from Space Life's SS12 figurine collection." icon_state = "chemist" toysay = "Get your pills!" /obj/item/toy/figure/clown name = "Clown action figure" + desc = "The mischevious Clown, from Space Life's SS12 figurine collection." icon_state = "clown" toysay = "Honk!" /obj/item/toy/figure/ian name = "Ian action figure" + desc = "The adorable corgi, from Space Life's SS12 figurine collection." icon_state = "ian" toysay = "Arf!" /obj/item/toy/figure/detective name = "Detective action figure" + desc = "The clever detective, from Space Life's SS12 figurine collection." icon_state = "detective" toysay = "This airlock has grey jumpsuit and insulated glove fibers on it." /obj/item/toy/figure/dsquad name = "Death Squad Officer action figure" + desc = "It's a member of the DeathSquad, a TV drama where loose-cannon ERT officers face up against the threats of the galaxy! It's from Space Life's special edition SS12 figurine collection." icon_state = "dsquad" toysay = "Eliminate all threats!" /obj/item/toy/figure/engineer name = "Engineer action figure" + desc = "The frantic engineer, from Space Life's SS12 figurine collection." icon_state = "engineer" toysay = "Oh god, the singularity is loose!" /obj/item/toy/figure/geneticist name = "Geneticist action figure" + desc = "The balding geneticist, from Space Life's SS12 figurine collection." icon_state = "geneticist" toysay = "I'm not qualified for this job." /obj/item/toy/figure/hop name = "Head of Personnel action figure" + desc = "The officious Head of Personnel, from Space Life's SS12 figurine collection." icon_state = "hop" - toysay = "Giving out all access!" + toysay = "Papers, please!" /obj/item/toy/figure/hos name = "Head of Security action figure" + desc = "The bloodlust-filled Head of Security, from Space Life's SS12 figurine collection." icon_state = "hos" - toysay = "I'm here to win, anything else is secondary." + toysay = "Space law? What?" /obj/item/toy/figure/qm name = "Quartermaster action figure" + desc = "The nationalistic Quartermaster, from Space Life's SS12 figurine collection." icon_state = "qm" toysay = "Hail Cargonia!" /obj/item/toy/figure/janitor name = "Janitor action figure" + desc = "The water-using Janitor, from Space Life's SS12 figurine collection." icon_state = "janitor" toysay = "Look at the signs, you idiot." /obj/item/toy/figure/lawyer name = "Internal Affairs Agent action figure" + desc = "The unappreciated Internal Affairs Agent, from Space Life's SS12 figurine collection." icon_state = "lawyer" toysay = "Standard Operating Procedure says they're guilty! Hacking is proof they're an Enemy of the Corporation!" /obj/item/toy/figure/librarian name = "Librarian action figure" + desc = "The quiet Librarian, from Space Life's SS12 figurine collection." icon_state = "librarian" toysay = "One day while..." /obj/item/toy/figure/md name = "Medical Doctor action figure" + desc = "The stressed-out doctor, from Space Life's SS12 figurine collection." icon_state = "md" toysay = "The patient is already dead!" /obj/item/toy/figure/mime name = "Mime action figure" - desc = "A \"Space Life\" brand Mime action figure." + desc = "... from Space Life's SS12 figurine collection." icon_state = "mime" toysay = "..." /obj/item/toy/figure/miner name = "Shaft Miner action figure" + desc = "The gun-toting Shaft Miner, from Space Life's SS12 figurine collection." icon_state = "miner" toysay = "Oh god it's eating my intestines!" /obj/item/toy/figure/ninja name = "Ninja action figure" + desc = "It's the mysterious ninja! It's from Space Life's special edition SS12 figurine collection." icon_state = "ninja" toysay = "Oh god! Stop shooting, I'm friendly!" /obj/item/toy/figure/wizard name = "Wizard action figure" + desc = "It's the deadly, spell-slinging wizard! It's from Space Life's special edition SS12 figurine collection." icon_state = "wizard" toysay = "Ei Nath!" /obj/item/toy/figure/rd name = "Research Director action figure" + desc = "The ambitious RD, from Space Life's SS12 figurine collection." icon_state = "rd" toysay = "Blowing all of the borgs!" /obj/item/toy/figure/roboticist name = "Roboticist action figure" + desc = "The skillful Roboticist, from Space Life's SS12 figurine collection." icon_state = "roboticist" toysay = "He asked to be borged!" /obj/item/toy/figure/scientist name = "Scientist action figure" + desc = "The mad Scientist, from Space Life's SS12 figurine collection." icon_state = "scientist" toysay = "Someone else must have made those bombs!" /obj/item/toy/figure/syndie name = "Nuclear Operative action figure" + desc = "It's the red-suited Nuclear Operative! It's from Space Life's special edition SS12 figurine collection." icon_state = "syndie" toysay = "Get that fucking disk!" /obj/item/toy/figure/secofficer name = "Security Officer action figure" + desc = "The power-tripping Security Officer, from Space Life's SS12 figurine collection." icon_state = "secofficer" toysay = "I am the law!" /obj/item/toy/figure/virologist name = "Virologist action figure" + desc = "The pandemic-starting Virologist, from Space Life's SS12 figurine collection." icon_state = "virologist" - toysay = "The cure is potassium!" + toysay = "It's not my virus!" /obj/item/toy/figure/warden name = "Warden action figure" + desc = "The amnesiac Warden, from Space Life's SS12 figurine collection." icon_state = "warden" toysay = "Execute him for breaking in!" diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index 7cf48095e72..d369161127f 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -8,6 +8,13 @@ desc = "This is rubbish." resistance_flags = FLAMMABLE +/obj/item/trash/decompile_act(obj/item/matter_decompiler/C, mob/user) + C.stored_comms["metal"] += 2 + C.stored_comms["wood"] += 1 + C.stored_comms["glass"] += 1 + qdel(src) + return TRUE + /obj/item/trash/raisins name = "4no raisins" icon_state= "4no_raisins" @@ -52,6 +59,14 @@ /obj/item/trash/fried_vox name = "Kentucky Fried Vox" icon_state = "fried_vox_empty" + item_state = "fried_vox_empty" + slot_flags = SLOT_HEAD + dog_fashion = /datum/dog_fashion/head/fried_vox_empty + sprite_sheets = list( + "Skrell" = 'icons/mob/species/skrell/head.dmi', + "Drask" = 'icons/mob/species/drask/head.dmi', + "Kidan" = 'icons/mob/species/kidan/head.dmi' + ) /obj/item/trash/pistachios name = "Pistachios pack" diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm index 1e34561c441..62716bc51c4 100755 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -143,8 +143,8 @@ AI MODULES target.set_zeroth_law(law) GLOB.lawchanges.Add("The law specified [targetName]") else - to_chat(target, "[sender.real_name] attempted to modify your zeroth law.")// And lets them know that someone tried. --NeoFite - to_chat(target, "It would be in your best interest to play along with [sender.real_name] that [law]") + to_chat(target, "[sender.real_name] attempted to modify your zeroth law.")// And lets them know that someone tried. --NeoFite + to_chat(target, "It would be in your best interest to play along with [sender.real_name] that [law]") GLOB.lawchanges.Add("The law specified [targetName], but the AI's existing law 0 cannot be overridden.") /******************** ProtectStation ********************/ @@ -225,7 +225,7 @@ AI MODULES target.laws.clear_supplied_laws() target.laws.clear_ion_laws() - to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.") + to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.") target.show_laws() /******************** Purge ********************/ @@ -238,7 +238,7 @@ AI MODULES ..() if(!is_special_character(target)) target.clear_zeroth_law() - to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.") + to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.") target.clear_supplied_laws() target.clear_ion_laws() target.clear_inherent_laws() diff --git a/code/game/objects/items/weapons/RSF.dm b/code/game/objects/items/weapons/RSF.dm index a8da7503c72..26cef27a0b4 100644 --- a/code/game/objects/items/weapons/RSF.dm +++ b/code/game/objects/items/weapons/RSF.dm @@ -58,7 +58,7 @@ RSF if(!proximity) return if(!(istype(A, /obj/structure/table) || istype(A, /turf/simulated/floor))) return - var spawn_location + var/spawn_location var/turf/T = get_turf(A) if(istype(T) && !T.density) spawn_location = T diff --git a/code/game/objects/items/weapons/batons.dm b/code/game/objects/items/weapons/batons.dm new file mode 100644 index 00000000000..34d2bdd7ac8 --- /dev/null +++ b/code/game/objects/items/weapons/batons.dm @@ -0,0 +1,133 @@ +/// Delay in deci-seconds between two non-lethal attacks +#define BATON_STUN_COOLDOWN 4 SECONDS +/// Force of the telescopic baton when deployed +#define BATON_TELESCOPIC_FORCE_DEPLOYED 10 + +/** + * # Police Baton + * + * Knocks down the hit mob when not on harm intent and when [/obj/item/melee/classic_baton/on] is TRUE + * + * A non-lethal attack has a cooldown to avoid spamming + */ +/obj/item/melee/classic_baton + name = "police baton" + desc = "A wooden truncheon for beating criminal scum." + icon_state = "baton" + item_state = "classic_baton" + slot_flags = SLOT_BELT + force = 12 //9 hit crit + w_class = WEIGHT_CLASS_NORMAL + /// Whether the baton is on cooldown + var/on_cooldown = FALSE + /// Whether the baton is toggled on (to allow attacking) + var/on = TRUE + +/obj/item/melee/classic_baton/attack(mob/living/target, mob/living/user) + if(!on) + return ..() + + add_fingerprint(user) + if((CLUMSY in user.mutations) && prob(50)) + user.visible_message("[user] accidentally clubs [user.p_them()]self with [src]!", \ + "You accidentally club yourself with [src]!") + user.Weaken(force * 3) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(force * 2, BRUTE, "head") + else + user.take_organ_damage(force * 2) + return + + if(user.a_intent == INTENT_HARM || isrobot(target)) // Lethal attack or it's a borg (can't knock them down!) + return ..() + else if(!on_cooldown) // Non-lethal attack - knock them down + // Check for shield/countering + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) + return + if(check_martial_counter(H, user)) + return + // Visuals and sound + user.do_attack_animation(target) + playsound(target, 'sound/effects/woodhit.ogg', 75, TRUE, -1) + add_attack_logs(user, target, "Stunned with [src]") + target.visible_message("[user] has knocked down [target] with \the [src]!", \ + "[user] has knocked down [target] with \the [src]!") + // Hit 'em + target.LAssailant = iscarbon(user) ? user : null + target.Weaken(3) + on_cooldown = TRUE + addtimer(CALLBACK(src, .proc/cooldown_finished), BATON_STUN_COOLDOWN) + +/** + * Called some time after a non-lethal attack + */ +/obj/item/melee/classic_baton/proc/cooldown_finished() + on_cooldown = FALSE + +/** + * # Fancy Cane + */ +/obj/item/melee/classic_baton/ntcane + name = "fancy cane" + desc = "A cane with special engraving on it. It seems well suited for fending off assailants..." + icon_state = "cane_nt" + item_state = "cane_nt" + needs_permit = FALSE + +/obj/item/melee/classic_baton/ntcane/is_crutch() + return TRUE + +/** + * # Telescopic Baton + */ +/obj/item/melee/classic_baton/telescopic + name = "telescopic baton" + desc = "A compact yet robust personal defense weapon. Can be concealed when folded." + icon_state = "telebaton_0" + item_state = null + slot_flags = SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + needs_permit = FALSE + force = 0 + on = FALSE + /// Attack verbs when concealed (created on Initialize) + var/static/list/attack_verb_off + /// Attack verbs when extended (created on Initialize) + var/static/list/attack_verb_on + +/obj/item/melee/classic_baton/telescopic/Initialize(mapload) + . = ..() + if(!attack_verb_off) + attack_verb_off = list("hit", "poked") + attack_verb_on = list("smacked", "struck", "cracked", "beaten") + attack_verb = on ? attack_verb_on : attack_verb_off + +/obj/item/melee/classic_baton/telescopic/attack_self(mob/user) + on = !on + icon_state = "telebaton_[on]" + if(on) + to_chat(user, "You extend the baton.") + item_state = "nullrod" + w_class = WEIGHT_CLASS_BULKY //doesnt fit in backpack when its on for balance + force = BATON_TELESCOPIC_FORCE_DEPLOYED //stunbaton damage + attack_verb = attack_verb_on + else + to_chat(user, "You collapse the baton.") + item_state = null //no sprite for concealment even when in hand + slot_flags = SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + force = 0 //not so robust now + attack_verb = attack_verb_off + // Update mob hand visuals + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + playsound(loc, 'sound/weapons/batonextend.ogg', 50, TRUE) + add_fingerprint(user) + +#undef BATON_STUN_COOLDOWN +#undef BATON_TELESCOPIC_FORCE_DEPLOYED diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index 405ce47a033..2b30ae5851f 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -102,6 +102,7 @@ var/rank = null //actual job var/owner_uid var/owner_ckey + var/lastlog var/dorm = 0 // determines if this ID has claimed a dorm already var/sex @@ -212,6 +213,11 @@ return M owner_ckey = null +/obj/item/card/id/proc/getPlayerCkey() + var/mob/living/carbon/human/H = getPlayer() + if(istype(H)) + return H.ckey + /obj/item/card/id/proc/is_untrackable() return untrackable @@ -356,13 +362,13 @@ /obj/item/card/id/syndicate/attack_self(mob/user as mob) if(!src.registered_name) - var t = reject_bad_name(input(user, "What name would you like to use on this card?", "Agent Card name", ishuman(user) ? user.real_name : user.name)) + var/t = reject_bad_name(input(user, "What name would you like to use on this card?", "Agent Card name", ishuman(user) ? user.real_name : user.name)) if(!t) to_chat(user, "Invalid name.") return src.registered_name = t - var u = sanitize(stripped_input(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels other than maintenance.", "Agent Card Job Assignment", "Agent", MAX_MESSAGE_LEN)) + var/u = sanitize(stripped_input(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels other than maintenance.", "Agent Card Job Assignment", "Agent", MAX_MESSAGE_LEN)) if(!u) to_chat(user, "Invalid assignment.") src.registered_name = "" diff --git a/code/game/objects/items/weapons/caution.dm b/code/game/objects/items/weapons/caution.dm index b1b573f77ad..b84f8eba6f4 100644 --- a/code/game/objects/items/weapons/caution.dm +++ b/code/game/objects/items/weapons/caution.dm @@ -15,6 +15,10 @@ var/armed = 0 var/timepassed = 0 +/obj/item/caution/proximity_sign/ComponentInitialize() + . = ..() + AddComponent(/datum/component/proximity_monitor) + /obj/item/caution/proximity_sign/attack_self(mob/user as mob) if(ishuman(user)) var/mob/living/carbon/human/H = user @@ -40,7 +44,7 @@ armed = 1 timing = 0 -/obj/item/caution/proximity_sign/HasProximity(atom/movable/AM as mob|obj) +/obj/item/caution/proximity_sign/HasProximity(atom/movable/AM) if(armed) if(istype(AM, /mob/living/carbon) && !istype(AM, /mob/living/carbon/brain)) var/mob/living/carbon/C = AM diff --git a/code/game/objects/items/weapons/chrono_eraser.dm b/code/game/objects/items/weapons/chrono_eraser.dm index 1dc0b22abdf..ef158e3758c 100644 --- a/code/game/objects/items/weapons/chrono_eraser.dm +++ b/code/game/objects/items/weapons/chrono_eraser.dm @@ -141,6 +141,8 @@ /obj/item/ammo_casing/energy/chrono_beam name = "eradication beam" projectile_type = /obj/item/projectile/energy/chrono_beam + muzzle_flash_effect = /obj/effect/temp_visual/target_angled/muzzle_flash/energy + muzzle_flash_color = null icon_state = "chronobolt" e_cost = 0 diff --git a/code/game/objects/items/weapons/cigs.dm b/code/game/objects/items/weapons/cigs.dm index 8c2c3e85917..bd80e43140f 100644 --- a/code/game/objects/items/weapons/cigs.dm +++ b/code/game/objects/items/weapons/cigs.dm @@ -66,6 +66,10 @@ LIGHTERS ARE IN LIGHTERS.DM ..() light() +/obj/item/clothing/mask/cigarette/catch_fire() + if(!lit) + light("The [name] is lit by the flames!") + /obj/item/clothing/mask/cigarette/welder_act(mob/user, obj/item/I) . = TRUE if(I.tool_use_check(user, 0)) //Don't need to flash eyes because you are a badass @@ -311,6 +315,11 @@ LIGHTERS ARE IN LIGHTERS.DM pixel_y = rand(-10,10) transform = turn(transform,rand(0,360)) +/obj/item/cigbutt/decompile_act(obj/item/matter_decompiler/C, mob/user) + C.stored_comms["wood"] += 1 + qdel(src) + return TRUE + /obj/item/cigbutt/cigarbutt name = "cigar butt" desc = "A manky old cigar butt." diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm index 8815ad6a20f..fcfe183dddf 100644 --- a/code/game/objects/items/weapons/cosmetics.dm +++ b/code/game/objects/items/weapons/cosmetics.dm @@ -5,16 +5,22 @@ icon_state = "lipstick" w_class = WEIGHT_CLASS_TINY var/colour = "red" - var/open = 0 - var/list/lipstick_colors = list( - "purple" = "purple", - "jade" = "#216F43", - "lime" = "lime", - "black" = "black", - "green" = "green", - "blue" = "blue", - "white" = "white") + var/open = FALSE + var/static/list/lipstick_colors +/obj/item/lipstick/Initialize(mapload) + . = ..() + if(!lipstick_colors) + lipstick_colors = list( + "black" = "#000000", + "white" = "#FFFFFF", + "red" = "#FF0000", + "green" = "#00C000", + "blue" = "#0000FF", + "purple" = "#D55CD0", + "jade" = "#216F43", + "lime" = "#00FF00", + ) /obj/item/lipstick/purple name = "purple lipstick" @@ -22,7 +28,7 @@ /obj/item/lipstick/jade name = "jade lipstick" - colour = "#216F43" + colour = "jade" /obj/item/lipstick/lime name = "lime lipstick" @@ -47,40 +53,37 @@ /obj/item/lipstick/random name = "lipstick" -/obj/item/lipstick/random/New() - ..() - var/lscolor = pick(lipstick_colors)//A random color is picked from the var defined initially in a new var. - colour = lipstick_colors[lscolor]//The color of the lipstick is pulled from the new variable (right hand side, HTML & Hex RGB) - name = "[lscolor] lipstick"//The new variable is also used to match the name to the color of the lipstick. Kudos to Desolate & Lemon +/obj/item/lipstick/random/Initialize(mapload) + . = ..() + colour = pick(lipstick_colors) + name = "[colour] lipstick" - -/obj/item/lipstick/attack_self(mob/user as mob) - overlays.Cut() +/obj/item/lipstick/attack_self(mob/user) + cut_overlays() to_chat(user, "You twist \the [src] [open ? "closed" : "open"].") open = !open if(open) - var/image/colored = image("icon"='icons/obj/items.dmi', "icon_state"="lipstick_uncap_color") - colored.color = colour + var/mutable_appearance/colored = mutable_appearance('icons/obj/items.dmi', "lipstick_uncap_color") + colored.color = lipstick_colors[colour] icon_state = "lipstick_uncap" - overlays += colored + add_overlay(colored) else icon_state = "lipstick" -/obj/item/lipstick/attack(mob/M as mob, mob/user as mob) - if(!open) return - - if(!istype(M, /mob)) return +/obj/item/lipstick/attack(mob/M, mob/user) + if(!open || !istype(M)) + return if(ishuman(M)) var/mob/living/carbon/human/H = M - if(H.lip_style) //if they already have lipstick on + if(H.lip_style) // If they already have lipstick on to_chat(user, "You need to wipe off the old lipstick first!") return if(H == user) user.visible_message("[user] does [user.p_their()] lips with [src].", \ "You take a moment to apply [src]. Perfect!") H.lip_style = "lipstick" - H.lip_color = colour + H.lip_color = lipstick_colors[colour] H.update_body() else user.visible_message("[user] begins to do [H]'s lips with \the [src].", \ @@ -89,7 +92,7 @@ user.visible_message("[user] does [H]'s lips with \the [src].", \ "You apply \the [src].") H.lip_style = "lipstick" - H.lip_color = colour + H.lip_color = lipstick_colors[colour] H.update_body() else to_chat(user, "Where are the lips on that?") diff --git a/code/game/objects/items/weapons/defib.dm b/code/game/objects/items/weapons/defib.dm index ee98d309876..fb4c86c59d1 100644 --- a/code/game/objects/items/weapons/defib.dm +++ b/code/game/objects/items/weapons/defib.dm @@ -141,6 +141,8 @@ if(paddles_on_defib) //Detach the paddles into the user's hands + if(usr.incapacitated()) return + if(!usr.put_in_hands(paddles)) to_chat(user, "You need a free hand to hold the paddles!") update_icon() diff --git a/code/game/objects/items/weapons/dice.dm b/code/game/objects/items/weapons/dice.dm index 629942860be..ae49512853a 100644 --- a/code/game/objects/items/weapons/dice.dm +++ b/code/game/objects/items/weapons/dice.dm @@ -189,8 +189,8 @@ var/turf/bombturf = get_turf(src) var/area/A = get_area(bombturf) investigate_log("E20 detonated at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with a roll of [result]. Triggered by: [key_name(user)]", INVESTIGATE_BOMB) - message_admins("E20 detonated at [A.name] (JMP) with a roll of [result]. Triggered by: [key_name_admin(user)]") log_game("E20 detonated at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with a roll of [result]. Triggered by: [key_name(user)]") + add_attack_logs(user, src, "detonated with a roll of [result]", ATKLOG_FEW) /obj/item/dice/update_icon() diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index f4ab41fab7b..1b1352c372f 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -106,6 +106,8 @@ src.last_use = world.time + if(reagents.chem_temp > 300 || reagents.chem_temp < 280) + add_attack_logs(user, target, "Sprayed with superheated or cooled fire extinguisher at Temperature [reagents.chem_temp]K") playsound(src.loc, 'sound/effects/extinguish.ogg', 75, 1, -3) var/direction = get_dir(src,target) diff --git a/code/game/objects/items/weapons/fireworks.dm b/code/game/objects/items/weapons/fireworks.dm index a2a0d6df552..906c4f3fef7 100644 --- a/code/game/objects/items/weapons/fireworks.dm +++ b/code/game/objects/items/weapons/fireworks.dm @@ -1,10 +1,11 @@ -obj/item/firework +/obj/item/firework name = "fireworks" icon = 'icons/obj/fireworks.dmi' icon_state = "rocket_0" var/litzor = 0 var/datum/effect_system/sparkle_spread/S -obj/item/firework/attackby(obj/item/W,mob/user, params) + +/obj/item/firework/attackby(obj/item/W,mob/user, params) if(litzor) return if(is_hot(W)) @@ -20,13 +21,13 @@ obj/item/firework/attackby(obj/item/W,mob/user, params) S.start() qdel(src) -obj/item/sparkler +/obj/item/sparkler name = "sparkler" icon = 'icons/obj/fireworks.dmi' icon_state = "sparkler_0" var/litzor = 0 -obj/item/sparkler/attackby(obj/item/W,mob/user, params) +/obj/item/sparkler/attackby(obj/item/W,mob/user, params) if(litzor) return if(is_hot(W)) @@ -39,8 +40,12 @@ obj/item/sparkler/attackby(obj/item/W,mob/user, params) do_sparks(1, 0, loc) sleep(10) qdel(src) + +// TODO: Refactor this into a proper locker or something +// Or just axe the system. This code is 7 years old /obj/crate/fireworks name = "Fireworks!" + /obj/crate/fireworks/New() new /obj/item/sparkler(src) new /obj/item/sparkler(src) diff --git a/code/game/objects/items/weapons/garrote.dm b/code/game/objects/items/weapons/garrote.dm index 4bddaf551ea..e3c145e790b 100644 --- a/code/game/objects/items/weapons/garrote.dm +++ b/code/game/objects/items/weapons/garrote.dm @@ -104,7 +104,7 @@ playsound(src.loc, 'sound/weapons/cablecuff.ogg', 15, 1, -1) M.visible_message("[U] comes from behind and begins garroting [M] with the [src]!", \ - "[U]\ begins garroting you with the [src]![improvised ? "" : " You are unable to speak!"]", \ + "[U] begins garroting you with the [src]![improvised ? "" : " You are unable to speak!"]", \ "You hear struggling and wire strain against flesh!") return diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 79612afd1f3..f756929e5ab 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -101,9 +101,9 @@ update_icon() else if(clown_check(user)) // This used to go before the assembly check, but that has absolutely zero to do with priming the damn thing. You could spam the admins with it. - message_admins("[key_name_admin(usr)] has primed a [name] for detonation at [A.name] (JMP) [contained].") log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) [contained].") investigate_log("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])[contained].", INVESTIGATE_BOMB) + add_attack_logs(user, src, "has primed (contained [contained])", ATKLOG_FEW) to_chat(user, "You prime the [name]! [det_time / 10] second\s!") playsound(user.loc, 'sound/weapons/armbomb.ogg', 60, 1) active = 1 @@ -120,7 +120,7 @@ owner.visible_message("[attack_text] hits [owner]'s [src], setting it off! What a shot!") var/turf/T = get_turf(src) log_game("A projectile ([hitby]) detonated a grenade held by [key_name(owner)] at [COORD(T)]") - message_admins("A projectile ([hitby]) detonated a grenade held by [key_name_admin(owner)] at [ADMIN_COORDJMP(T)]") + add_attack_logs(P.firer, owner, "A projectile ([hitby]) detonated a grenade held", ATKLOG_FEW) prime() return 1 //It hit the grenade, not them @@ -149,16 +149,16 @@ if(!O.reagents) continue if(istype(O,/obj/item/slime_extract)) cores += " [O]" - for(var/reagent in O.reagents.reagent_list) - contained += " [reagent] " + for(var/R in O.reagents.reagent_list) + var/datum/reagent/reagent = R + contained += "[reagent.volume] [reagent], " if(contained) if(cores) - contained = "\[[cores];[contained]\]" + contained = "\[[cores]; [contained]\]" else - contained = "\[[contained]\]" + contained = "\[ [contained]\]" var/turf/bombturf = get_turf(loc) - var/area/A = bombturf.loc - message_admins("[key_name_admin(usr)] has completed [name] at [A.name] (JMP) [contained].") + add_attack_logs(user, src, "has completed with [contained]", ATKLOG_MOST) log_game("[key_name(usr)] has completed [name] at [bombturf.x], [bombturf.y], [bombturf.z]. [contained]") else to_chat(user, "You need to add at least one beaker before locking the assembly.") @@ -190,6 +190,8 @@ user.drop_item() nadeassembly = A + if(nadeassembly.has_prox_sensors()) + AddComponent(/datum/component/proximity_monitor) A.master = src A.loc = src assemblyattacher = user.ckey @@ -219,6 +221,7 @@ nadeassembly.loc = get_turf(src) nadeassembly.master = null nadeassembly = null + qdel(GetComponent(/datum/component/proximity_monitor)) if(beakers.len) for(var/obj/O in beakers) O.loc = get_turf(src) @@ -304,6 +307,8 @@ /obj/item/grenade/chem_grenade/proc/CreateDefaultTrigger(var/typekey) if(ispath(typekey,/obj/item/assembly)) nadeassembly = new(src) + if(nadeassembly.has_prox_sensors()) + AddComponent(/datum/component/proximity_monitor) nadeassembly.a_left = new /obj/item/assembly/igniter(nadeassembly) nadeassembly.a_left.holder = nadeassembly nadeassembly.a_left.secured = 1 diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm index b3028f13c90..13f9dd8f49d 100644 --- a/code/game/objects/items/weapons/grenades/flashbang.dm +++ b/code/game/objects/items/weapons/grenades/flashbang.dm @@ -5,39 +5,51 @@ origin_tech = "materials=2;combat=3" light_power = 10 light_color = LIGHT_COLOR_WHITE - var/light_time = 2 - var/range = 7 + + var/light_time = 0.2 SECONDS // The duration the area is illuminated + var/range = 7 // The range in tiles of the flashbang /obj/item/grenade/flashbang/prime() update_mob() - var/flashbang_turf = get_turf(src) - if(!flashbang_turf) - return + var/turf/T = get_turf(src) + if(T) + // VFX and SFX + do_sparks(rand(5, 9), FALSE, src) + playsound(T, 'sound/effects/bang.ogg', 100, TRUE) + new /obj/effect/dummy/lighting_obj(T, light_color, range + 2, light_power, light_time) - set_light(7) - - do_sparks(rand(5, 9), FALSE, src) - playsound(flashbang_turf, 'sound/effects/bang.ogg', 25, 1) - bang(flashbang_turf, src, range) - - for(var/obj/structure/blob/B in hear(8, flashbang_turf)) //Blob damage here - var/damage = round(30 / (get_dist(B, get_turf(src)) + 1)) - B.take_damage(damage, BURN, "melee", 0) - - spawn(light_time) - qdel(src) + // Stunning & damaging mechanic + bang(T, src, range) + qdel(src) +/** + * Creates a flashing effect that blinds and deafens mobs within range + * + * Also damages blobs + * Arguments: + * * T - The turf to flash + * * A - The flashing atom + * * range - The range in tiles of the flash + * * flash - Whether to flash (blind) + * * bang - Whether to bang (deafen) + */ /proc/bang(turf/T, atom/A, range = 7, flash = TRUE, bang = TRUE) + // Blob damage + for(var/obj/structure/blob/B in hear(range + 1, T)) + var/damage = round(30 / (get_dist(B, T) + 1)) + B.take_damage(damage, BURN, "melee", FALSE) + + // Flashing mechanic + var/source_turf = get_turf(A) for(var/mob/living/M in hearers(range, T)) if(M.stat == DEAD) continue M.show_message("BANG", 2) - //Checking for protections - var/ear_safety = M.check_ear_prot() - var/distance = max(1, get_dist(get_turf(A), get_turf(M))) + var/distance = max(1, get_dist(source_turf, get_turf(M))) + var/stun_amount = max(10 / distance, 3) - //Flash + // Flash if(flash) if(M.weakeyes) M.visible_message("[M] screams and collapses!") @@ -49,21 +61,20 @@ var/mob/living/carbon/human/H = M var/obj/item/organ/internal/eyes/E = H.get_int_organ(/obj/item/organ/internal/eyes) if(E) - E.receive_damage(8, 1) - + E.receive_damage(8, TRUE) if(M.flash_eyes(affect_silicon = TRUE)) - M.Stun(max(10 / distance, 3)) - M.Weaken(max(10 / distance, 3)) + M.Stun(stun_amount) + M.Weaken(stun_amount) - - //Bang + // Bang + var/ear_safety = M.check_ear_prot() if(bang) - if(!distance || A.loc == M || A.loc == M.loc) //Holding on person or being exactly where lies is significantly more dangerous and voids protection + if(!distance || A.loc == M || A.loc == M.loc) // Holding on person or being exactly where lies is significantly more dangerous and voids protection M.Stun(10) M.Weaken(10) if(!ear_safety) - M.Stun(max(10 / distance, 3)) - M.Weaken(max(10 / distance, 3)) + M.Stun(stun_amount) + M.Weaken(stun_amount) M.AdjustEarDamage(rand(0, 5), 15) if(iscarbon(M)) var/mob/living/carbon/C = M @@ -74,6 +85,5 @@ if(prob(ears.ear_damage - 5)) to_chat(M, "You can't hear anything!") M.BecomeDeaf() - else - if(ears.ear_damage >= 5) - to_chat(M, "Your ears start to ring!") + else if(ears.ear_damage >= 5) + to_chat(M, "Your ears start to ring!") diff --git a/code/game/objects/items/weapons/grenades/ghettobomb.dm b/code/game/objects/items/weapons/grenades/ghettobomb.dm index b58330a248b..c2303dd2937 100644 --- a/code/game/objects/items/weapons/grenades/ghettobomb.dm +++ b/code/game/objects/items/weapons/grenades/ghettobomb.dm @@ -48,9 +48,9 @@ var/turf/bombturf = get_turf(src) var/area/A = get_area(bombturf) - message_admins("[ADMIN_LOOKUPFLW(user)] has primed a [name] for detonation at [ADMIN_COORDJMP(bombturf)].") log_game("[key_name(user)] has primed a [name] for detonation at [A.name] [COORD(bombturf)].") investigate_log("[key_name(user)] has primed a [name] for detonation at [A.name] [COORD(bombturf)])", INVESTIGATE_BOMB) + add_attack_logs(user, src, "has primed for detonation", ATKLOG_FEW) if(iscarbon(user)) var/mob/living/carbon/C = user C.throw_mode_on() diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index 8cbc041c146..da546836dc6 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -73,6 +73,7 @@ message_admins("[key_name_admin(usr)] has primed a [name] for detonation at [A.name] (JMP)") log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])") investigate_log("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])", INVESTIGATE_BOMB) + add_attack_logs(user, src, "has primed for detonation", ATKLOG_FEW) if(iscarbon(user)) var/mob/living/carbon/C = user C.throw_mode_on() diff --git a/code/game/objects/items/weapons/grenades/smokebomb.dm b/code/game/objects/items/weapons/grenades/smokebomb.dm index 6dd07485f5b..53061e5fa61 100644 --- a/code/game/objects/items/weapons/grenades/smokebomb.dm +++ b/code/game/objects/items/weapons/grenades/smokebomb.dm @@ -19,7 +19,7 @@ /obj/item/grenade/smokebomb/prime() playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3) - src.smoke.set_up(10, 0, usr.loc) + smoke.set_up(10, 0) spawn(0) src.smoke.start() sleep(10) diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index b4df7b0d16b..efb93a520a5 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -9,27 +9,27 @@ var/deliveryamt = 1 // amount of type to deliver spawner_type = /mob/living/simple_animal/hostile/viscerator - prime() // Prime now just handles the two loops that query for people in lockers and people who can see it. +/obj/item/grenade/spawnergrenade/prime() // Prime now just handles the two loops that query for people in lockers and people who can see it. - if(spawner_type && deliveryamt) - // Make a quick flash - var/turf/T = get_turf(src) - playsound(T, 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/carbon/C in viewers(T, null)) - C.flash_eyes() + if(spawner_type && deliveryamt) + // Make a quick flash + var/turf/T = get_turf(src) + playsound(T, 'sound/effects/phasein.ogg', 100, 1) + for(var/mob/living/carbon/C in viewers(T, null)) + C.flash_eyes() - for(var/i=1, i<=deliveryamt, i++) - var/atom/movable/x = new spawner_type - x.admin_spawned = admin_spawned - x.loc = T - if(prob(50)) - for(var/j = 1, j <= rand(1, 3), j++) - step(x, pick(NORTH,SOUTH,EAST,WEST)) + for(var/i=1, i<=deliveryamt, i++) + var/atom/movable/x = new spawner_type + x.admin_spawned = admin_spawned + x.loc = T + if(prob(50)) + for(var/j = 1, j <= rand(1, 3), j++) + step(x, pick(NORTH,SOUTH,EAST,WEST)) - // Spawn some hostile syndicate critters + // Spawn some hostile syndicate critters - qdel(src) - return + qdel(src) + return /obj/item/grenade/spawnergrenade/manhacks name = "manhack delivery grenade" diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm index 1f73e5c7062..69b4abbe10d 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -126,13 +126,15 @@ color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) ..() -/obj/item/restraints/handcuffs/cable/proc/cable_color(var/colorC) - if(colorC) - if(colorC == "rainbow") - colorC = color_rainbow() - color = colorC - else +/obj/item/restraints/handcuffs/cable/proc/cable_color(colorC) + if(!colorC) color = COLOR_RED + else if(colorC == "rainbow") + color = color_rainbow() + else if(colorC == "orange") //byond only knows 16 colors by name, and orange isn't one of them + color = COLOR_ORANGE + else + color = colorC /obj/item/restraints/handcuffs/cable/proc/color_rainbow() color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) diff --git a/code/game/objects/items/weapons/highlander_swords.dm b/code/game/objects/items/weapons/highlander_swords.dm index 330da6beba0..568b023d051 100644 --- a/code/game/objects/items/weapons/highlander_swords.dm +++ b/code/game/objects/items/weapons/highlander_swords.dm @@ -25,14 +25,14 @@ return ..() /obj/item/claymore/highlander/equipped(mob/user, slot) - if(!ishuman(user)) + if(!ishuman(user) || !user.mind) return var/mob/living/carbon/human/H = user if(slot == slot_r_hand || slot == slot_l_hand) - if(H.martial_art && H.martial_art != style) - style.teach(H, 1) + if(H.mind.martial_art && H.mind.martial_art != style) + style.teach(H, TRUE) to_chat(H, "THERE CAN ONLY BE ONE!") - else if(H.martial_art && H.martial_art == style) + else if(H.mind.martial_art && H.mind.martial_art == style) style.remove(H) var/obj/item/claymore/highlander/sword = H.is_in_hands(/obj/item/claymore/highlander) if(sword) diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm index 4efb6994e8d..974bd3b677f 100644 --- a/code/game/objects/items/weapons/holy_weapons.dm +++ b/code/game/objects/items/weapons/holy_weapons.dm @@ -255,7 +255,7 @@ possessed = TRUE - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, 0, 100) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, FALSE, 10 SECONDS, source = src) var/mob/dead/observer/theghost = null if(candidates.len) @@ -488,7 +488,7 @@ var/mob/living/carbon/human/holder = loc if(src == holder.l_hand || src == holder.r_hand) // Holding this in your hand will for(var/mob/living/carbon/human/H in range(5, loc)) - if(H.mind.vampire && !H.mind.vampire.get_ability(/datum/vampire_passive/full)) + if(H.mind && H.mind.vampire && !H.mind.vampire.get_ability(/datum/vampire_passive/full)) H.mind.vampire.nullified = max(5, H.mind.vampire.nullified + 2) if(prob(10)) to_chat(H, "Being in the presence of [holder]'s [src] is interfering with your powers!") diff --git a/code/game/objects/items/weapons/implants/implant_krav_maga.dm b/code/game/objects/items/weapons/implants/implant_krav_maga.dm index 3c2666f3d66..9c33f43950a 100644 --- a/code/game/objects/items/weapons/implants/implant_krav_maga.dm +++ b/code/game/objects/items/weapons/implants/implant_krav_maga.dm @@ -17,12 +17,12 @@ /obj/item/implant/krav_maga/activate() var/mob/living/carbon/human/H = imp_in - if(!ishuman(H)) + if(!ishuman(H) || !H.mind) return - if(istype(H.martial_art, /datum/martial_art/krav_maga)) + if(istype(H.mind.martial_art, /datum/martial_art/krav_maga)) style.remove(H) else - style.teach(H,1) + style.teach(H, TRUE) /obj/item/implanter/krav_maga name = "implanter (krav maga)" diff --git a/code/game/objects/items/weapons/implants/implantchair.dm b/code/game/objects/items/weapons/implants/implantchair.dm index d74ab31a6c3..00a72f3401b 100644 --- a/code/game/objects/items/weapons/implants/implantchair.dm +++ b/code/game/objects/items/weapons/implants/implantchair.dm @@ -17,13 +17,6 @@ var/mob/living/carbon/occupant = null var/injecting = 0 -/obj/machinery/implantchair/proc - go_out() - put_mob(mob/living/carbon/M) - implant(var/mob/M) - add_implants() - - /obj/machinery/implantchair/New() ..() add_implants() @@ -87,7 +80,7 @@ return -/obj/machinery/implantchair/go_out(mob/M) +/obj/machinery/implantchair/proc/go_out(mob/M) if(!( src.occupant )) return if(M == occupant) // so that the guy inside can't eject himself -Agouri @@ -101,7 +94,7 @@ return -/obj/machinery/implantchair/put_mob(mob/living/carbon/M) +/obj/machinery/implantchair/proc/put_mob(mob/living/carbon/M) if(!iscarbon(M)) to_chat(usr, "The [src.name] cannot hold this!") return @@ -116,7 +109,7 @@ return 1 -/obj/machinery/implantchair/implant(mob/M) +/obj/machinery/implantchair/proc/implant(mob/M) if(!istype(M, /mob/living/carbon)) return if(!implant_list.len) return @@ -131,7 +124,7 @@ return -/obj/machinery/implantchair/add_implants() +/obj/machinery/implantchair/proc/add_implants() for(var/i=0, i You have been banned FOR NO REISIN by [user]
    ") - to_chat(user, " You have BANNED [M]") - playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much - -/* - * Classic Baton - */ - -/obj/item/melee/classic_baton - name = "police baton" - desc = "A wooden truncheon for beating criminal scum." - icon_state = "baton" - item_state = "classic_baton" - slot_flags = SLOT_BELT - force = 12 //9 hit crit - w_class = WEIGHT_CLASS_NORMAL - var/cooldown = 0 - var/on = 1 - -/obj/item/melee/classic_baton/attack(mob/target as mob, mob/living/user as mob) - if(on) - add_fingerprint(user) - if((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You club yourself over the head.") - user.Weaken(3 * force) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(2*force, BRUTE, "head") - else - user.take_organ_damage(2*force) - return - if(isrobot(target)) - ..() - return - if(!isliving(target)) - return - if(user.a_intent == INTENT_HARM) - if(!..()) return - if(!isrobot(target)) return - else - if(cooldown <= 0) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) - return - if(check_martial_counter(H, user)) - return - playsound(get_turf(src), 'sound/effects/woodhit.ogg', 75, 1, -1) - target.Weaken(3) - add_attack_logs(user, target, "Stunned with [src]") - add_fingerprint(user) - target.visible_message("[user] has knocked down [target] with \the [src]!", \ - "[user] has knocked down [target] with \the [src]!") - if(!iscarbon(user)) - target.LAssailant = null - else - target.LAssailant = user - cooldown = 1 - spawn(40) - cooldown = 0 - return - else - return ..() - -/obj/item/melee/classic_baton/ntcane - name = "fancy cane" - desc = "A cane with special engraving on it. It seems well suited for fending off assailants..." - icon_state = "cane_nt" - item_state = "cane_nt" - needs_permit = 0 - -/obj/item/melee/classic_baton/ntcane/is_crutch() - return 1 - -//Telescopic baton -/obj/item/melee/classic_baton/telescopic - name = "telescopic baton" - desc = "A compact yet robust personal defense weapon. Can be concealed when folded." - icon_state = "telebaton_0" - item_state = null - slot_flags = SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - needs_permit = 0 - force = 0 - on = 0 - -/obj/item/melee/classic_baton/telescopic/attack_self(mob/user as mob) - on = !on - if(on) - to_chat(user, "You extend the baton.") - icon_state = "telebaton_1" - item_state = "nullrod" - w_class = WEIGHT_CLASS_BULKY //doesnt fit in backpack when its on for balance - force = 10 //stunbaton damage - attack_verb = list("smacked", "struck", "cracked", "beaten") - else - to_chat(user, "You collapse the baton.") - icon_state = "telebaton_0" - item_state = null //no sprite for concealment even when in hand - slot_flags = SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - force = 0 //not so robust now - attack_verb = list("hit", "poked") - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1) - add_fingerprint(user) diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm index bc783457e76..edf32e2f5a6 100644 --- a/code/game/objects/items/weapons/tanks/jetpack.dm +++ b/code/game/objects/items/weapons/tanks/jetpack.dm @@ -219,28 +219,3 @@ turn_off(cur_user) return ..() - -/obj/item/tank/jetpack/rig - name = "jetpack" - var/obj/item/rig/holder - actions_types = list(/datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) - -/obj/item/tank/jetpack/rig/examine() - . = list("It's a jetpack. If you can see this, report it on the bug tracker.") - -/obj/item/tank/jetpack/rig/allow_thrust(num, mob/living/user) - if(!on) - return 0 - - if(!istype(holder) || !holder.air_supply) - return 0 - - var/datum/gas_mixture/removed = holder.air_supply.air_contents.remove(num) - if(removed.total_moles() < 0.005) - turn_off(user) - return 0 - - var/turf/T = get_turf(user) - T.assume_air(removed) - - return 1 diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index a82d6c0c223..4feb3296fe6 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -27,7 +27,7 @@ if(get_dist(user, src) <= 0 && air_contents.oxygen < 10) . += "The meter on [src] indicates you are almost out of air!" -obj/item/tank/oxygen/empty/New() +/obj/item/tank/oxygen/empty/New() ..() air_contents.oxygen = null @@ -158,7 +158,7 @@ obj/item/tank/oxygen/empty/New() . += "The meter on [src] indicates you are almost out of air!" playsound(user, 'sound/effects/alert.ogg', 50, 1) -obj/item/tank/emergency_oxygen/empty/New() +/obj/item/tank/emergency_oxygen/empty/New() ..() air_contents.oxygen = null @@ -167,7 +167,12 @@ obj/item/tank/emergency_oxygen/empty/New() icon_state = "emergency_engi" volume = 6 -obj/item/tank/emergency_oxygen/engi/empty/New() +/obj/item/tank/emergency_oxygen/engi/full/New() + ..() + air_contents.oxygen = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + + +/obj/item/tank/emergency_oxygen/engi/empty/New() ..() air_contents.oxygen = null @@ -182,7 +187,7 @@ obj/item/tank/emergency_oxygen/engi/empty/New() icon_state = "emergency_double" volume = 10 -obj/item/tank/emergency_oxygen/double/empty/New() +/obj/item/tank/emergency_oxygen/double/empty/New() ..() air_contents.oxygen = null diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 9ded7947202..4d71ee20fe9 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -43,35 +43,31 @@ /obj/item/tank/proc/toggle_internals(mob/user, silent = FALSE) var/mob/living/carbon/C = user if(!istype(C)) - return 0 + return FALSE if(C.internal == src) to_chat(C, "You close \the [src] valve.") C.internal = null else - var/can_open_valve = 0 - if(C.get_organ_slot("breathing_tube")) - can_open_valve = 1 - else if(C.wear_mask && C.wear_mask.flags & AIRTIGHT) - can_open_valve = 1 - else if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(H.head && H.head.flags & AIRTIGHT) - can_open_valve = 1 + if(!C.get_organ_slot("breathing_tube")) // Breathing tubes can always use internals, if they have one, skip ahead and turn internals on/off + if(!C.wear_mask) // Do we have a mask equipped? + return FALSE - if(can_open_valve) + var/obj/item/clothing/mask/M = C.wear_mask + // If the "mask" isn't actually a mask OR That mask isn't internals compatible AND Their headgear isn't internals compatible + if(!istype(M) || (!(initial(M.flags) & AIRTIGHT) && !(C.head && C.head.flags & AIRTIGHT))) + if(!silent) + to_chat(C, "You are not wearing a suitable mask or helmet.") + return FALSE + if(M.mask_adjusted) // If the mask is equipped but pushed down + M.adjustmask(C) // Adjust it back + + if(!silent) if(C.internal) - if(!silent) - to_chat(C, "You switch your internals to [src].") + to_chat(C, "You switch your internals to [src].") else - if(!silent) - to_chat(C, "You open \the [src] valve.") - C.internal = src - else - if(!silent) - to_chat(C, "You are not wearing a suitable mask or helmet.") - return 0 - + to_chat(C, "You open \the [src] valve.") + C.internal = src C.update_action_buttons_icon() @@ -142,70 +138,55 @@ if(!(air_contents)) return - ui_interact(user) + tgui_interact(user) -/obj/item/tank/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/item/tank/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, "tanks.tmpl", "Tank", 500, 300) - // open the new ui window + ui = new(user, src, ui_key, "Tank", name, 300, 150, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) -/obj/item/tank/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/using_internal - if(iscarbon(loc)) - var/mob/living/carbon/C = loc - if(C.internal == src) - using_internal = 1 - - var/data[0] +/obj/item/tank/tgui_data(mob/user) + var/list/data = list() data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) + data["minReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) - data["valveOpen"] = using_internal ? 1 : 0 - - data["maskConnected"] = 0 - - if(iscarbon(loc)) - var/mob/living/carbon/C = loc - if(C.internal == src) - data["maskConnected"] = 1 - else - if(C.wear_mask && (C.wear_mask.flags & AIRTIGHT)) - data["maskConnected"] = 1 - else if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(H.head && (H.head.flags & AIRTIGHT)) - data["maskConnected"] = 1 - + var/mob/living/carbon/C = user + if(!istype(C)) + C = loc.loc + if(!istype(C)) + return data + data["has_mask"] = C.wear_mask ? TRUE : FALSE + data["connected"] = (C.internal && C.internal == src) ? TRUE : FALSE return data -/obj/item/tank/Topic(href, href_list) +/obj/item/tank/tgui_act(action, params) if(..()) - return 1 - - if(href_list["dist_p"]) - if(href_list["dist_p"] == "reset") - distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE - else if(href_list["dist_p"] == "max") - distribute_pressure = TANK_MAX_RELEASE_PRESSURE + return + . = TRUE + switch(action) + if("pressure") + var/pressure = params["pressure"] + if(pressure == "reset") + pressure = initial(distribute_pressure) + else if(pressure == "min") + pressure = TANK_DEFAULT_RELEASE_PRESSURE + else if(pressure == "max") + pressure = TANK_MAX_RELEASE_PRESSURE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + else + . = FALSE + if(.) + distribute_pressure = clamp(round(pressure), TANK_DEFAULT_RELEASE_PRESSURE, TANK_MAX_RELEASE_PRESSURE) + if("internals") + toggle_internals(usr) else - var/cp = text2num(href_list["dist_p"]) - distribute_pressure += cp - distribute_pressure = min(max(round(distribute_pressure), 0), TANK_MAX_RELEASE_PRESSURE) - - if(href_list["stat"]) - toggle_internals(usr) - - add_fingerprint(usr) - return 1 - + . = FALSE + if(.) + add_fingerprint(usr) /obj/item/tank/remove_air(amount) return air_contents.remove(amount) diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm index ed857c18ded..5078ed6273e 100644 --- a/code/game/objects/items/weapons/twohanded.dm +++ b/code/game/objects/items/weapons/twohanded.dm @@ -209,6 +209,42 @@ /obj/item/twohanded/fireaxe/boneaxe/update_icon() icon_state = "bone_axe[wielded]" +/obj/item/twohanded/fireaxe/energized + desc = "Someone with a love for fire axes decided to turn this one into a high-powered energy weapon. Seems excessive." + force_wielded = 30 + armour_penetration = 20 + var/charge = 30 + var/max_charge = 30 + +/obj/item/twohanded/fireaxe/energized/update_icon() + if(wielded) + icon_state = "fireaxe2" + else + icon_state = "fireaxe0" + +/obj/item/twohanded/fireaxe/energized/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/item/twohanded/fireaxe/energized/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/twohanded/fireaxe/energized/process() + charge = min(charge + 1, max_charge) + +/obj/item/twohanded/fireaxe/energized/attack(mob/M, mob/user) + . = ..() + if(wielded && charge == max_charge) + if(isliving(M)) + charge = 0 + playsound(loc, 'sound/magic/lightningbolt.ogg', 5, 1) + user.visible_message("[user] slams the charged axe into [M.name] with all [user.p_their()] might!") + do_sparks(1, 1, src) + M.Weaken(4) + var/atom/throw_target = get_edge_target_turf(M, get_dir(src, get_step_away(M, src))) + M.throw_at(throw_target, 5, 1) + /* * Double-Bladed Energy Swords - Cheridan */ @@ -755,57 +791,6 @@ charged = 3 playsound(user, 'sound/weapons/marauder.ogg', 50, 1) -// Energized Fire axe -/obj/item/twohanded/energizedfireaxe - name = "energized fire axe" - desc = "Someone with a love for fire axes decided to turn one into a single-charge energy weapon. Seems excessive." - icon_state = "fireaxe0" - force = 5 - throwforce = 15 - sharp = TRUE - w_class = WEIGHT_CLASS_HUGE - armour_penetration = 20 - slot_flags = SLOT_BACK - force_unwielded = 5 - force_wielded = 30 - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - hitsound = 'sound/weapons/bladeslice.ogg' - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) - var/charged = 1 - -/obj/item/twohanded/energizedfireaxe/update_icon() - if(wielded) - icon_state = "fireaxe2" - else - icon_state = "fireaxe0" - ..() - -/obj/item/twohanded/energizedfireaxe/afterattack(atom/A, mob/user, proximity) - if(!proximity) - return - if(wielded) - if(isliving(A)) - var/mob/living/Z = A - if(charged) - charged-- - Z.take_organ_damage(0, 30) - user.visible_message("[user] slams the charged axe into [Z.name] with all [user.p_their()] might!") - playsound(loc, 'sound/magic/lightningbolt.ogg', 5, 1) - do_sparks(1, 1, src) - - if(A && wielded && (istype(A, /obj/structure/window) || istype(A, /obj/structure/grille))) - if(istype(A, /obj/structure/window)) - var/obj/structure/window/W = A - W.deconstruct(FALSE) - if(prob(4)) - charged++ - user.visible_message("The axe starts to emit an electric buzz!") - else - qdel(A) - if(prob(4)) - charged++ - user.visible_message("The axe starts to emit an electric buzz!") - /obj/item/twohanded/pitchfork icon_state = "pitchfork0" name = "pitchfork" diff --git a/code/game/objects/items/weapons/vending_items.dm b/code/game/objects/items/weapons/vending_items.dm index 258771870d8..e7743f961f1 100644 --- a/code/game/objects/items/weapons/vending_items.dm +++ b/code/game/objects/items/weapons/vending_items.dm @@ -126,10 +126,6 @@ machine_name = "NanoMed" icon_state = "refill_medical" -/obj/item/vending_refill/modularpc - machine_name = "Deluxe Silicate Selections" - icon_state = "refill_engi" - /obj/item/vending_refill/hydronutrients machine_name = "NutriMax" icon_state = "refill_plant" diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 32158a9a3ab..28e213c7826 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -1,3 +1,6 @@ +/** + * # Banhammer + */ /obj/item/banhammer desc = "A banhammer" name = "banhammer" @@ -13,11 +16,15 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70) resistance_flags = FIRE_PROOF - /obj/item/banhammer/suicide_act(mob/user) to_chat(viewers(user), "[user] is hitting [user.p_them()]self with the [src.name]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.") return BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS +/obj/item/banhammer/attack(mob/M, mob/user) + to_chat(M, " You have been banned FOR NO REISIN by [user]") + to_chat(user, " You have BANNED [M]") + playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much + /obj/item/sord name = "\improper SORD" desc = "This thing is so unspeakably shitty you are having a hard time even holding it." @@ -178,6 +185,7 @@ throwforce = 12 attack_verb = list("beat", "smacked") w_class = WEIGHT_CLASS_HUGE + var/next_throw_time = 0 var/homerun_ready = 0 var/homerun_able = 0 @@ -246,16 +254,38 @@ to_chat(user, "You cannot attack in deflect mode!") return . = ..() - var/atom/throw_target = get_edge_target_turf(target, user.dir) if(homerun_ready) + var/atom/throw_target = get_edge_target_turf(target, user.dir) user.visible_message("It's a home run!") target.throw_at(throw_target, rand(8,10), 14, user) target.ex_act(2) playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, 1) homerun_ready = 0 return - else if(!target.anchored) - target.throw_at(throw_target, rand(1,2), 7, user) + if(world.time < next_throw_time) + // Limit the rate of throwing, so you can't spam it. + return + if(!istype(target)) + // Should already be /mob/living, but check anyway. + return + if(target.anchored) + // No throwing mobs that are anchored to the floor. + return + if(target.mob_size > MOB_SIZE_HUMAN) + // No throwing things that are physically bigger than you are. + // Covers: blobbernaut, alien empress, ai core, juggernaut, ed209, mulebot, alien/queen/large, carp/megacarp, deathsquid, hostile/tree, megafauna, hostile/asteroid, terror_spider/queen/empress + return + if(!(target.status_flags & CANPUSH)) + // No throwing mobs specifically flagged as immune to being pushed. + // Covers: revenant, hostile/blob/*, most borgs, juggernauts, hivebot/tele, spaceworms, shades, bots, alien queens, hostile/syndicate/melee, hostile/asteroid + return + if(target.move_resist > MOVE_RESIST_DEFAULT) + // No throwing mobs that have higher than normal move_resist. + // Covers: revenant, bot/mulebot, hostile/statue, hostile/megafauna, goliath + return + var/atom/throw_target = get_edge_target_turf(target, user.dir) + target.throw_at(throw_target, rand(1, 2), 7, user) + next_throw_time = world.time + 10 SECONDS /obj/item/melee/baseball_bat/ablative name = "metal baseball bat" diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index bbd43db7c1e..e41d2173c23 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -51,6 +51,8 @@ take_damage(AM.throwforce, BRUTE, "melee", 1, get_dir(src, AM)) /obj/ex_act(severity) + if(QDELETED(src)) + return if(resistance_flags & INDESTRUCTIBLE) return switch(severity) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index d40c628ed90..45c2129fc9a 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -78,6 +78,7 @@ else STOP_PROCESSING(SSfastprocess, src) SSnanoui.close_uis(src) + SStgui.close_uis(src) return ..() //user: The mob that is suiciding @@ -351,3 +352,9 @@ a { /obj/proc/check_uplink_validity() return TRUE + +/obj/proc/force_eject_occupant() + // This proc handles safely removing occupant mobs from the object if they must be teleported out (due to being SSD/AFK, by admin teleport, etc) or transformed. + // In the event that the object doesn't have an overriden version of this proc to do it, log a runtime so one can be added. + CRASH("Proc force_eject_occupant() is not overriden on a machine containing a mob.") + diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm index 59cc3898088..52b70590ee4 100644 --- a/code/game/objects/random/random.dm +++ b/code/game/objects/random/random.dm @@ -30,13 +30,14 @@ desc = "This is a random tool" icon = 'icons/obj/items.dmi' icon_state = "welder" - item_to_spawn() - return pick(/obj/item/screwdriver,\ - /obj/item/wirecutters,\ - /obj/item/weldingtool,\ - /obj/item/crowbar,\ - /obj/item/wrench,\ - /obj/item/flashlight) + +/obj/random/tool/item_to_spawn() + return pick(/obj/item/screwdriver,\ + /obj/item/wirecutters,\ + /obj/item/weldingtool,\ + /obj/item/crowbar,\ + /obj/item/wrench,\ + /obj/item/flashlight) /obj/random/technology_scanner @@ -44,10 +45,11 @@ desc = "This is a random technology scanner." icon = 'icons/obj/device.dmi' icon_state = "atmos" - item_to_spawn() - return pick(prob(5);/obj/item/t_scanner,\ - prob(2);/obj/item/radio/intercom,\ - prob(5);/obj/item/analyzer) + +/obj/random/technology_scanner/item_to_spawn() + return pick(prob(5);/obj/item/t_scanner,\ + prob(2);/obj/item/radio/intercom,\ + prob(5);/obj/item/analyzer) /obj/random/powercell @@ -55,23 +57,24 @@ desc = "This is a random powercell." icon = 'icons/obj/power.dmi' icon_state = "cell" - item_to_spawn() - return pick(prob(10);/obj/item/stock_parts/cell/crap,\ - prob(40);/obj/item/stock_parts/cell,\ - prob(40);/obj/item/stock_parts/cell/high,\ - prob(9);/obj/item/stock_parts/cell/super,\ - prob(1);/obj/item/stock_parts/cell/hyper) +/obj/random/powercell/item_to_spawn() + return pick(prob(10);/obj/item/stock_parts/cell/crap,\ + prob(40);/obj/item/stock_parts/cell,\ + prob(40);/obj/item/stock_parts/cell/high,\ + prob(9);/obj/item/stock_parts/cell/super,\ + prob(1);/obj/item/stock_parts/cell/hyper) /obj/random/bomb_supply name = "Bomb Supply" desc = "This is a random bomb supply." icon = 'icons/obj/assemblies/new_assemblies.dmi' icon_state = "signaller" - item_to_spawn() - return pick(/obj/item/assembly/igniter,\ - /obj/item/assembly/prox_sensor,\ - /obj/item/assembly/signaler) + +/obj/random/bomb_supply/item_to_spawn() + return pick(/obj/item/assembly/igniter,\ + /obj/item/assembly/prox_sensor,\ + /obj/item/assembly/signaler) /obj/random/toolbox @@ -79,11 +82,11 @@ desc = "This is a random toolbox." icon = 'icons/obj/storage.dmi' icon_state = "red" - item_to_spawn() - return pick(prob(3);/obj/item/storage/toolbox/mechanical,\ - prob(2);/obj/item/storage/toolbox/electrical,\ - prob(1);/obj/item/storage/toolbox/emergency) +/obj/random/toolbox/item_to_spawn() + return pick(prob(3);/obj/item/storage/toolbox/mechanical,\ + prob(2);/obj/item/storage/toolbox/electrical,\ + prob(1);/obj/item/storage/toolbox/emergency) /obj/random/tech_supply name = "Random Tech Supply" @@ -91,15 +94,16 @@ icon = 'icons/obj/power.dmi' icon_state = "cell" spawn_nothing_percentage = 50 - item_to_spawn() - return pick(prob(3);/obj/random/powercell,\ - prob(2);/obj/random/technology_scanner,\ - prob(1);/obj/item/stack/packageWrap,\ - prob(2);/obj/random/bomb_supply,\ - prob(1);/obj/item/extinguisher,\ - prob(1);/obj/item/clothing/gloves/color/fyellow,\ - prob(3);/obj/item/stack/cable_coil,\ - prob(2);/obj/random/toolbox,\ - prob(2);/obj/item/storage/belt/utility,\ - prob(5);/obj/random/tool,\ - prob(3);/obj/item/stack/tape_roll) + +/obj/random/tech_supply/item_to_spawn() + return pick(prob(3);/obj/random/powercell,\ + prob(2);/obj/random/technology_scanner,\ + prob(1);/obj/item/stack/packageWrap,\ + prob(2);/obj/random/bomb_supply,\ + prob(1);/obj/item/extinguisher,\ + prob(1);/obj/item/clothing/gloves/color/fyellow,\ + prob(3);/obj/item/stack/cable_coil,\ + prob(2);/obj/random/toolbox,\ + prob(2);/obj/item/storage/belt/utility,\ + prob(5);/obj/random/tool,\ + prob(3);/obj/item/stack/tape_roll) diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index b1ed99357ad..bd8ca8c024b 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -163,3 +163,6 @@ if(0 to 25) if(!broken) return "It's falling apart!" + +/obj/structure/proc/prevents_buckled_mobs_attacking() + return FALSE diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index 033787cce75..4661ff125cc 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -280,12 +280,14 @@ /obj/structure/alien/egg/proc/Grow() icon_state = "egg" status = GROWN + AddComponent(/datum/component/proximity_monitor) /obj/structure/alien/egg/proc/Burst(kill = TRUE) //drops and kills the hugger if any is remaining if(status == GROWN || status == GROWING) icon_state = "egg_hatched" flick("egg_opening", src) status = BURSTING + qdel(GetComponent(/datum/component/proximity_monitor)) spawn(15) status = BURST var/obj/item/clothing/mask/facehugger/child = GetFacehugger() diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 04373502832..d1d26b7a2bb 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -25,9 +25,13 @@ ..() spawn(1) if(!opened) // if closed, any item at the crate's loc is put in the contents + var/itemcount = 0 for(var/obj/item/I in loc) if(I.density || I.anchored || I == src) continue I.forceMove(src) + // Ensure the storage cap is respected + if(++itemcount >= storage_capacity) + break // Fix for #383 - C4 deleting fridges with corpses /obj/structure/closet/Destroy() @@ -230,7 +234,7 @@ add_fingerprint(user) /obj/structure/closet/attack_ai(mob/user) - if(isrobot(user) && Adjacent(user) && !istype(user.loc, /obj/machinery/atmospherics)) //Robots can open/close it, but not the AI + if(isrobot(user) && Adjacent(user)) //Robots can open/close it, but not the AI attack_hand(user) /obj/structure/closet/relaymove(mob/user) @@ -354,6 +358,11 @@ /obj/structure/closet/AllowDrop() return TRUE +/obj/structure/closet/force_eject_occupant() + // Its okay to silently teleport mobs out of lockers, since the only thing affected is their contents list. + return + + /obj/structure/closet/bluespace name = "bluespace closet" desc = "A storage unit that moves and stores through the fourth dimension." diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm index fd67afcf797..311bc4a38c7 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm @@ -45,8 +45,6 @@ new /obj/item/clothing/suit/storage/labcoat(src) new /obj/item/radio/headset/headset_sci(src) new /obj/item/radio/headset/headset_sci(src) - new /obj/item/reagent_containers/food/drinks/oilcan(src) - new /obj/item/reagent_containers/food/drinks/oilcan(src) /obj/structure/closet/secure_closet/RD name = "research director's locker" diff --git a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm index 763717dc270..7a648aa74e4 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm @@ -69,6 +69,11 @@ /obj/structure/closet/secure_closet/closed_item_click(mob/user) togglelock(user) +/obj/structure/closet/secure_closet/AltClick(mob/user) + ..() + if(Adjacent(user)) + togglelock(user) + /obj/structure/closet/secure_closet/emag_act(mob/user) if(!broken) broken = TRUE diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index be004a82912..ac5be74f96d 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -48,7 +48,7 @@ /obj/structure/closet/secure_closet/hop/New() ..() - new /obj/item/clothing/glasses/sunglasses(src) + new /obj/item/clothing/glasses/hud/skills/sunglasses(src) new /obj/item/clothing/head/hopcap(src) new /obj/item/cartridge/hop(src) new /obj/item/radio/headset/heads/hop(src) @@ -251,7 +251,8 @@ new /obj/item/melee/baton/loaded(src) new /obj/item/clothing/glasses/sunglasses(src) new /obj/item/clothing/glasses/hud/security/sunglasses/read_only(src) - new /obj/item/clothing/glasses/hud/health/sunglasses + new /obj/item/clothing/glasses/hud/health/sunglasses(src) + new /obj/item/clothing/glasses/hud/skills/sunglasses(src) new /obj/item/clothing/head/beret/centcom/officer(src) new /obj/item/clothing/head/beret/centcom/officer/navy(src) new /obj/item/clothing/suit/armor/vest/blueshield(src) @@ -279,7 +280,7 @@ new /obj/item/storage/briefcase(src) new /obj/item/paicard(src) new /obj/item/flash(src) - new /obj/item/clothing/glasses/sunglasses(src) + new /obj/item/clothing/glasses/hud/skills/sunglasses(src) new /obj/item/clothing/gloves/color/white(src) new /obj/item/clothing/shoes/centcom(src) new /obj/item/clothing/under/lawyer/oldman(src) diff --git a/code/game/objects/structures/crates_lockers/closets/syndicate.dm b/code/game/objects/structures/crates_lockers/closets/syndicate.dm index d95eac2a0ac..8f812397ef9 100644 --- a/code/game/objects/structures/crates_lockers/closets/syndicate.dm +++ b/code/game/objects/structures/crates_lockers/closets/syndicate.dm @@ -61,89 +61,89 @@ new /obj/item/clothing/mask/gas/syndicate(src) new /obj/item/clothing/suit/space/hardsuit/syndi/elite/sst(src) -/obj/structure/closet/syndicate/resources/ +/obj/structure/closet/syndicate/resources desc = "An old, dusty locker." - New() - ..() - var/common_min = 30 //Minimum amount of minerals in the stack for common minerals - var/common_max = 50 //Maximum amount of HONK in the stack for HONK common minerals - var/rare_min = 5 //Minimum HONK of HONK in the stack HONK HONK rare minerals - var/rare_max = 20 //Maximum HONK HONK HONK in the HONK for HONK rare HONK +/obj/structure/closet/syndicate/resources/New() + ..() + var/common_min = 30 //Minimum amount of minerals in the stack for common minerals + var/common_max = 50 //Maximum amount of HONK in the stack for HONK common minerals + var/rare_min = 5 //Minimum HONK of HONK in the stack HONK HONK rare minerals + var/rare_max = 20 //Maximum HONK HONK HONK in the HONK for HONK rare HONK - var/pickednum = rand(1, 50) + var/pickednum = rand(1, 50) - //Sad trombone - if(pickednum == 1) - var/obj/item/paper/P = new /obj/item/paper(src) - P.name = "IOU" - P.info = "Sorry man, we needed the money so we sold your stash. It's ok, we'll double our money for sure this time!" + //Sad trombone + if(pickednum == 1) + var/obj/item/paper/P = new /obj/item/paper(src) + P.name = "IOU" + P.info = "Sorry man, we needed the money so we sold your stash. It's ok, we'll double our money for sure this time!" - //Metal (common ore) - if(pickednum >= 2) - new /obj/item/stack/sheet/metal(src, rand(common_min, common_max)) + //Metal (common ore) + if(pickednum >= 2) + new /obj/item/stack/sheet/metal(src, rand(common_min, common_max)) - //Glass (common ore) - if(pickednum >= 5) - new /obj/item/stack/sheet/glass(src, rand(common_min, common_max)) + //Glass (common ore) + if(pickednum >= 5) + new /obj/item/stack/sheet/glass(src, rand(common_min, common_max)) - //Plasteel (common ore) Because it has a million more uses then plasma - if(pickednum >= 10) - new /obj/item/stack/sheet/plasteel(src, rand(common_min, common_max)) + //Plasteel (common ore) Because it has a million more uses then plasma + if(pickednum >= 10) + new /obj/item/stack/sheet/plasteel(src, rand(common_min, common_max)) - //Plasma (rare ore) - if(pickednum >= 15) - new /obj/item/stack/sheet/mineral/plasma(src, rand(rare_min, rare_max)) + //Plasma (rare ore) + if(pickednum >= 15) + new /obj/item/stack/sheet/mineral/plasma(src, rand(rare_min, rare_max)) - //Silver (rare ore) - if(pickednum >= 20) - new /obj/item/stack/sheet/mineral/silver(src, rand(rare_min, rare_max)) + //Silver (rare ore) + if(pickednum >= 20) + new /obj/item/stack/sheet/mineral/silver(src, rand(rare_min, rare_max)) - //Gold (rare ore) - if(pickednum >= 30) - new /obj/item/stack/sheet/mineral/gold(src, rand(rare_min, rare_max)) + //Gold (rare ore) + if(pickednum >= 30) + new /obj/item/stack/sheet/mineral/gold(src, rand(rare_min, rare_max)) - //Uranium (rare ore) - if(pickednum >= 40) - new /obj/item/stack/sheet/mineral/uranium(src, rand(rare_min, rare_max)) + //Uranium (rare ore) + if(pickednum >= 40) + new /obj/item/stack/sheet/mineral/uranium(src, rand(rare_min, rare_max)) - //Titanium (rare ore) - if(pickednum >= 40) - new /obj/item/stack/sheet/mineral/titanium(src, rand(rare_min, rare_max)) + //Titanium (rare ore) + if(pickednum >= 40) + new /obj/item/stack/sheet/mineral/titanium(src, rand(rare_min, rare_max)) - //Plastitanium (rare ore) - if(pickednum >= 40) - new /obj/item/stack/sheet/mineral/plastitanium(src, rand(rare_min, rare_max)) + //Plastitanium (rare ore) + if(pickednum >= 40) + new /obj/item/stack/sheet/mineral/plastitanium(src, rand(rare_min, rare_max)) - //Diamond (rare HONK) - if(pickednum >= 45) - new /obj/item/stack/sheet/mineral/diamond(src, rand(rare_min, rare_max)) + //Diamond (rare HONK) + if(pickednum >= 45) + new /obj/item/stack/sheet/mineral/diamond(src, rand(rare_min, rare_max)) - //Jetpack (You hit the jackpot!) - if(pickednum == 50) - new /obj/item/tank/jetpack/carbondioxide(src) + //Jetpack (You hit the jackpot!) + if(pickednum == 50) + new /obj/item/tank/jetpack/carbondioxide(src) /obj/structure/closet/syndicate/resources/everything desc = "It's an emergency storage closet for repairs." - New() - ..() - var/list/resources = list( - /obj/item/stack/sheet/metal, - /obj/item/stack/sheet/glass, - /obj/item/stack/sheet/mineral/gold, - /obj/item/stack/sheet/mineral/silver, - /obj/item/stack/sheet/mineral/plasma, - /obj/item/stack/sheet/mineral/uranium, - /obj/item/stack/sheet/mineral/diamond, - /obj/item/stack/sheet/mineral/bananium, - /obj/item/stack/sheet/mineral/titanium, - /obj/item/stack/sheet/mineral/plastitanium, - /obj/item/stack/sheet/plasteel, - /obj/item/stack/rods - ) +/obj/structure/closet/syndicate/resources/everything/New() + ..() + var/list/resources = list( + /obj/item/stack/sheet/metal, + /obj/item/stack/sheet/glass, + /obj/item/stack/sheet/mineral/gold, + /obj/item/stack/sheet/mineral/silver, + /obj/item/stack/sheet/mineral/plasma, + /obj/item/stack/sheet/mineral/uranium, + /obj/item/stack/sheet/mineral/diamond, + /obj/item/stack/sheet/mineral/bananium, + /obj/item/stack/sheet/mineral/titanium, + /obj/item/stack/sheet/mineral/plastitanium, + /obj/item/stack/sheet/plasteel, + /obj/item/stack/rods + ) - for(var/i in 1 to 2) - for(var/res in resources) - var/obj/item/stack/R = new res(src) - R.amount = R.max_amount + for(var/i in 1 to 2) + for(var/res in resources) + var/obj/item/stack/R = new res(src) + R.amount = R.max_amount diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 2441a2d57d6..61db52bf8a0 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -187,6 +187,7 @@ if(user) to_chat(user, "The crate's anti-tamper system activates!") investigate_log("[key_name(user)] has detonated a [src]", INVESTIGATE_BOMB) + add_attack_logs(user, src, "has detonated", ATKLOG_MOST) for(var/atom/movable/AM in src) qdel(AM) explosion(get_turf(src), 0, 1, 5, 5) @@ -338,24 +339,23 @@ var/target_temp = T0C - 40 var/cooling_power = 40 - return_air() - var/datum/gas_mixture/gas = (..()) - if(!gas) return null - var/datum/gas_mixture/newgas = new/datum/gas_mixture() - newgas.oxygen = gas.oxygen - newgas.carbon_dioxide = gas.carbon_dioxide - newgas.nitrogen = gas.nitrogen - newgas.toxins = gas.toxins - newgas.volume = gas.volume - newgas.temperature = gas.temperature - if(newgas.temperature <= target_temp) return - - if((newgas.temperature - cooling_power) > target_temp) - newgas.temperature -= cooling_power - else - newgas.temperature = target_temp - return newgas +/obj/structure/closet/crate/freezer/return_air() + var/datum/gas_mixture/gas = (..()) + if(!gas) return null + var/datum/gas_mixture/newgas = new/datum/gas_mixture() + newgas.oxygen = gas.oxygen + newgas.carbon_dioxide = gas.carbon_dioxide + newgas.nitrogen = gas.nitrogen + newgas.toxins = gas.toxins + newgas.volume = gas.volume + newgas.temperature = gas.temperature + if(newgas.temperature <= target_temp) return + if((newgas.temperature - cooling_power) > target_temp) + newgas.temperature -= cooling_power + else + newgas.temperature = target_temp + return newgas /obj/structure/closet/crate/can desc = "A large can, looks like a bin to me." @@ -496,22 +496,23 @@ /obj/structure/closet/crate/hydroponics/prespawned //This exists so the prespawned hydro crates spawn with their contents. - New() - ..() - new /obj/item/reagent_containers/glass/bucket(src) - new /obj/item/reagent_containers/glass/bucket(src) - new /obj/item/screwdriver(src) - new /obj/item/screwdriver(src) - new /obj/item/wrench(src) - new /obj/item/wrench(src) - new /obj/item/wirecutters(src) - new /obj/item/wirecutters(src) - new /obj/item/shovel/spade(src) - new /obj/item/shovel/spade(src) - new /obj/item/storage/box/beakers(src) - new /obj/item/storage/box/beakers(src) - new /obj/item/hand_labeler(src) - new /obj/item/hand_labeler(src) +// Do I need the definition above? Who knows! +/obj/structure/closet/crate/hydroponics/prespawned/New() + ..() + new /obj/item/reagent_containers/glass/bucket(src) + new /obj/item/reagent_containers/glass/bucket(src) + new /obj/item/screwdriver(src) + new /obj/item/screwdriver(src) + new /obj/item/wrench(src) + new /obj/item/wrench(src) + new /obj/item/wirecutters(src) + new /obj/item/wirecutters(src) + new /obj/item/shovel/spade(src) + new /obj/item/shovel/spade(src) + new /obj/item/storage/box/beakers(src) + new /obj/item/storage/box/beakers(src) + new /obj/item/hand_labeler(src) + new /obj/item/hand_labeler(src) /obj/structure/closet/crate/sci name = "science crate" diff --git a/code/game/objects/structures/crates_lockers/crittercrate.dm b/code/game/objects/structures/crates_lockers/crittercrate.dm index fd726aab6a7..7629fc62918 100644 --- a/code/game/objects/structures/crates_lockers/crittercrate.dm +++ b/code/game/objects/structures/crates_lockers/crittercrate.dm @@ -82,9 +82,8 @@ content_mob = /mob/living/simple_animal/pet/dog/fox /obj/structure/closet/critter/butterfly - name = "butterflies crate" + name = "butterfly crate" content_mob = /mob/living/simple_animal/butterfly - amount = 50 /obj/structure/closet/critter/deer name = "deer crate" diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index 36a20aa0239..0ad8a73c9ba 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -144,7 +144,7 @@ to_chat(user, "You [open ? "close":"open"] [src].") toggle_lock(user) -obj/structure/displaycase/welder_act(mob/user, obj/item/I) +/obj/structure/displaycase/welder_act(mob/user, obj/item/I) . = TRUE if(default_welder_repair(user, I)) broken = FALSE diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm index 4531613610e..73f8bbfff41 100644 --- a/code/game/objects/structures/dresser.dm +++ b/code/game/objects/structures/dresser.dm @@ -56,27 +56,20 @@ /obj/structure/dresser/crowbar_act(mob/user, obj/item/I) . = TRUE - if(!I.tool_start_check(src, user, 0)) + if(!I.use_tool(src, user, 0)) return TOOL_ATTEMPT_DISMANTLE_MESSAGE if(I.use_tool(src, user, 50, volume = I.tool_volume)) TOOL_DISMANTLE_SUCCESS_MESSAGE - + deconstruct(disassembled = TRUE) /obj/structure/dresser/wrench_act(mob/user, obj/item/I) . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(anchored) - WRENCH_UNANCHOR_MESSAGE - anchored = FALSE - else - if(!isfloorturf(loc)) - user.visible_message("A floor must be present to secure [src]!") - return - WRENCH_ANCHOR_MESSAGE - anchored = TRUE + default_unfasten_wrench(user, I, time = 20) -/obj/structure/dresser/deconstruct(disassembled = TRUE) - new /obj/item/stack/sheet/wood(drop_location(), 30) - qdel(src) +/obj/structure/dresser/deconstruct(disassembled = FALSE) + var/mat_drop = 15 + if(disassembled) + mat_drop = 30 + new /obj/item/stack/sheet/wood(drop_location(), mat_drop) + ..() diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index cd7b3525478..33298669157 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -40,7 +40,7 @@ return if(!in_range(src, user)) return - if(!iscarbon(usr)) + if(!iscarbon(usr) && !isrobot(usr)) return playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3) opened = !opened diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 5fea799ecb0..6d27b21e686 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -76,14 +76,14 @@ if(istype(W,/obj/item/stack/rods)) var/obj/item/stack/rods/S = W if(state == GIRDER_DISPLACED) - if(S.get_amount() < 2) - to_chat(user, "You need at least two rods to create a false wall!") + if(S.get_amount() < 5) + to_chat(user, "You need at least five rods to create a false wall!") return to_chat(user, "You start building a reinforced false wall...") if(do_after(user, 20, target = src)) - if(!loc || !S || S.get_amount() < 2) + if(!loc || !S || S.get_amount() < 5) return - S.use(2) + S.use(5) to_chat(user, "You create a false wall. Push on it to open or close the passage.") var/obj/structure/falsewall/iron/FW = new (loc) transfer_fingerprints_to(FW) diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm index 1f0d4b8b473..c3f36e42f1d 100644 --- a/code/game/objects/structures/kitchen_spike.dm +++ b/code/game/objects/structures/kitchen_spike.dm @@ -69,9 +69,9 @@ if(isliving(G.affecting)) if(!has_buckled_mobs()) if(do_mob(user, src, 120)) - if(spike(G.affecting)) - G.affecting.visible_message("[user] slams [G.affecting] onto the meat spike!", "[user] slams you onto the meat spike!", "You hear a squishy wet noise.") - qdel(G) + var/mob/living/affected = G.affecting + if(spike(affected)) + affected.visible_message("[user] slams [affected] onto the meat spike!", "[user] slams you onto the meat spike!", "You hear a squishy wet noise.") return return ..() diff --git a/code/game/objects/structures/lavaland/necropolis_tendril.dm b/code/game/objects/structures/lavaland/necropolis_tendril.dm index f9717956bb0..13c0ce77c0f 100644 --- a/code/game/objects/structures/lavaland/necropolis_tendril.dm +++ b/code/game/objects/structures/lavaland/necropolis_tendril.dm @@ -15,7 +15,6 @@ anchored = TRUE resistance_flags = FIRE_PROOF | LAVA_PROOF - var/gps = null var/obj/effect/light_emitter/tendril/emitted_light /obj/structure/spawner/lavaland/goliath @@ -29,7 +28,6 @@ GLOBAL_LIST_INIT(tendrils, list()) /obj/structure/spawner/lavaland/Initialize(mapload) . = ..() emitted_light = new(loc) - gps = new /obj/item/gps/internal(src) GLOB.tendrils += src return INITIALIZE_HINT_LATELOAD @@ -59,7 +57,6 @@ GLOBAL_LIST_INIT(tendrils, list()) SSmedals.SetScore(TENDRIL_CLEAR_SCORE, L.client, 1) GLOB.tendrils -= src QDEL_NULL(emitted_light) - QDEL_NULL(gps) return ..() /obj/effect/light_emitter/tendril diff --git a/code/game/objects/structures/loom.dm b/code/game/objects/structures/loom.dm index d0bcd07f92f..4742a2c3c0c 100644 --- a/code/game/objects/structures/loom.dm +++ b/code/game/objects/structures/loom.dm @@ -11,12 +11,30 @@ anchored = TRUE /obj/structure/loom/attackby(obj/item/I, mob/user) - if(default_unfasten_wrench(user, I, 5)) - return if(weave(I, user)) return return ..() +/obj/structure/loom/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0)) + return + TOOL_ATTEMPT_DISMANTLE_MESSAGE + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + TOOL_DISMANTLE_SUCCESS_MESSAGE + deconstruct(disassembled = TRUE) + +/obj/structure/loom/wrench_act(mob/user, obj/item/I) + . = TRUE + default_unfasten_wrench(user, I, time = 20) + +/obj/structure/loom/deconstruct(disassembled = FALSE) + var/mat_drop = 5 + if(disassembled) + mat_drop = 10 + new /obj/item/stack/sheet/wood(drop_location(), mat_drop) + ..() + ///Handles the weaving. /obj/structure/loom/proc/weave(obj/item/stack/sheet/cotton/W, mob/user) if(!istype(W)) diff --git a/code/game/objects/structures/misc.dm b/code/game/objects/structures/misc.dm index 44cbf6c52c9..5118a7fe0da 100644 --- a/code/game/objects/structures/misc.dm +++ b/code/game/objects/structures/misc.dm @@ -11,8 +11,8 @@ anchored = 1 density = 1 - attack_hand(mob/user as mob) - to_chat(user, "Civilians: NT is recruiting! Please head SOUTH to the NT Recruitment office to join the station's crew!") +/obj/structure/signpost/attack_hand(mob/user as mob) + to_chat(user, "Civilians: NT is recruiting! Please head SOUTH to the NT Recruitment office to join the station's crew!") /obj/structure/ninjatele @@ -23,31 +23,28 @@ anchored = 1 density = 0 - attack_hand(mob/user as mob) - - - if(user.mind.special_role=="Ninja") - switch(alert("Phase Jaunt relay primed, target locked as [station_name()], initiate VOID-shift translocation? (Warning! Internals required!)",,"Yes","No")) - - if("Yes") - if(user.z != src.z) return - - user.loc.loc.Exited(user) - user.loc = pick(GLOB.carplist) // In the future, possibly make specific NinjaTele landmarks, and give him an option to teleport to North/South/East/West of SS13 instead of just hijacking a carpspawn. - - - playsound(user.loc, 'sound/effects/phasein.ogg', 25, 1) - playsound(user.loc, 'sound/effects/sparks2.ogg', 50, 1) - new /obj/effect/temp_visual/dir_setting/ninja/phase(get_turf(user), user.dir) - to_chat(user, "VOID-Shift translocation successful") - - if("No") - - to_chat(user, "Process aborted!") +/obj/structure/ninjatele/attack_hand(mob/user as mob) + if(user.mind.special_role=="Ninja") + switch(alert("Phase Jaunt relay primed, target locked as [station_name()], initiate VOID-shift translocation? (Warning! Internals required!)",,"Yes","No")) + if("Yes") + if(user.z != src.z) return - else - to_chat(user, "FĆAL �Rr�R: ŧer nt recgnized, c-cntr-r䣧-ç äcked.") + + user.loc.loc.Exited(user) + user.loc = pick(GLOB.carplist) // In the future, possibly make specific NinjaTele landmarks, and give him an option to teleport to North/South/East/West of SS13 instead of just hijacking a carpspawn. + + playsound(user.loc, 'sound/effects/phasein.ogg', 25, 1) + playsound(user.loc, 'sound/effects/sparks2.ogg', 50, 1) + new /obj/effect/temp_visual/dir_setting/ninja/phase(get_turf(user), user.dir) + to_chat(user, "VOID-Shift translocation successful") + + if("No") + to_chat(user, "Process aborted!") + return + + else + to_chat(user, "FĆAL �Rr�R: ŧer nt recgnized, c-cntr-r䣧-ç äcked.") /obj/structure/respawner name = "\improper Long-Distance Cloning Machine" diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm deleted file mode 100644 index c3d63c47862..00000000000 --- a/code/game/objects/structures/musician.dm +++ /dev/null @@ -1,341 +0,0 @@ - - -/datum/song - var/name = "Untitled" - var/list/lines = new() - var/tempo = 5 // delay between notes - - var/playing = 0 // if we're playing - var/help = 0 // if help is open - var/repeat = 0 // number of times remaining to repeat - var/max_repeat = 10 // maximum times we can repeat - - var/instrumentDir = "piano" // the folder with the sounds - var/instrumentExt = "ogg" // the file extension - var/obj/instrumentObj = null // the associated obj playing the sound - -/datum/song/New(dir, obj, ext = "ogg") - tempo = sanitize_tempo(tempo) - instrumentDir = dir - instrumentObj = obj - instrumentExt = ext - -/datum/song/Destroy() - instrumentObj = null - return ..() - -// note is a number from 1-7 for A-G -// acc is either "b", "n", or "#" -// oct is 1-8 (or 9 for C) -/datum/song/proc/playnote(note, acc as text, oct) - // handle accidental -> B<>C of E<>F - if(acc == "b" && (note == 3 || note == 6)) // C or F - if(note == 3) - oct-- - note-- - acc = "n" - else if(acc == "#" && (note == 2 || note == 5)) // B or E - if(note == 2) - oct++ - note++ - acc = "n" - else if(acc == "#" && (note == 7)) //G# - note = 1 - acc = "b" - else if(acc == "#") // mass convert all sharps to flats, octave jump already handled - acc = "b" - note++ - - // check octave, C is allowed to go to 9 - if(oct < 1 || (note == 3 ? oct > 9 : oct > 8)) - return - - // now generate name - var/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]" - soundfile = file(soundfile) - // make sure the note exists - if(!fexists(soundfile)) - return - // and play - var/turf/source = get_turf(instrumentObj) - var/sound/music_played = sound(soundfile) - for(var/A in hearers(15, source)) - var/mob/M = A - if(!M.client || !(M.client.prefs.sound & SOUND_INSTRUMENTS)) - continue - M.playsound_local(source, null, 100, falloff = 5, S = music_played) - -/datum/song/proc/shouldStopPlaying(mob/user) - if(instrumentObj) - //if(!user.canUseTopic(instrumentObj)) - //return 1 - return !instrumentObj.anchored // add special cases to stop in subclasses - else - return 1 - -/datum/song/proc/playsong(mob/user) - while(repeat >= 0) - var/cur_oct[7] - var/cur_acc[7] - for(var/i = 1 to 7) - cur_oct[i] = 3 - cur_acc[i] = "n" - - for(var/line in lines) - for(var/beat in splittext(lowertext(line), ",")) - var/list/notes = splittext(beat, "/") - for(var/note in splittext(notes[1], "-")) - if(!playing || shouldStopPlaying(user)) //If the instrument is playing, or special case - playing = 0 - return - if(length(note) == 0) - continue - var/cur_note = text2ascii(note) - 96 - if(cur_note < 1 || cur_note > 7) - continue - for(var/i=2 to length(note)) - var/ni = copytext(note,i,i+1) - if(!text2num(ni)) - if(ni == "#" || ni == "b" || ni == "n") - cur_acc[cur_note] = ni - else if(ni == "s") - cur_acc[cur_note] = "#" // so shift is never required - else - cur_oct[cur_note] = text2num(ni) - playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note]) - if(notes.len >= 2 && text2num(notes[2])) - sleep(sanitize_tempo(tempo / text2num(notes[2]))) - else - sleep(tempo) - repeat-- - playing = 0 - repeat = 0 - -/datum/song/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(!instrumentObj) - return - - ui = SSnanoui.try_update_ui(user, instrumentObj, ui_key, ui, force_open) - if(!ui) - ui = new(user, instrumentObj, ui_key, "song.tmpl", instrumentObj.name, 700, 500) - ui.open() - ui.set_auto_update(1) - -/datum/song/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - data["lines"] = lines - data["tempo"] = tempo - - data["playing"] = playing - data["help"] = help - data["repeat"] = repeat - data["maxRepeat"] = max_repeat - data["minTempo"] = world.tick_lag - data["maxTempo"] = 600 - - return data - -/datum/song/Topic(href, href_list) - if(!in_range(instrumentObj, usr) || (issilicon(usr) && instrumentObj.loc != usr) || !isliving(usr) || usr.incapacitated()) - usr << browse(null, "window=instrument") - usr.unset_machine() - return 1 - - instrumentObj.add_fingerprint(usr) - - if(href_list["newsong"]) - playing = 0 - lines = new() - tempo = sanitize_tempo(5) // default 120 BPM - name = "" - SSnanoui.update_uis(src) - - else if(href_list["import"]) - playing = 0 - var/t = "" - do - t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message) - if(!in_range(instrumentObj, usr)) - return - - if(length(t) >= 12000) - var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") - if(cont == "no") - break - while(length(t) > 12000) - - //split into lines - spawn() - lines = splittext(t, "\n") - if(lines.len == 0) - return 1 - if(copytext(lines[1],1,6) == "BPM: ") - tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6))) - lines.Cut(1,2) - else - tempo = sanitize_tempo(5) // default 120 BPM - if(lines.len > 200) - to_chat(usr, "Too many lines!") - lines.Cut(201) - var/linenum = 1 - for(var/l in lines) - if(length(l) > 200) - to_chat(usr, "Line [linenum] too long!") - lines.Remove(l) - else - linenum++ - SSnanoui.update_uis(src) - - else if(href_list["help"]) - help = !help - SSnanoui.update_uis(src) - - if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops. - if(playing) - return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing. - repeat += round(text2num(href_list["repeat"])) - if(repeat < 0) - repeat = 0 - if(repeat > max_repeat) - repeat = max_repeat - SSnanoui.update_uis(src) - - else if(href_list["tempo"]) - tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]) * world.tick_lag) - SSnanoui.update_uis(src) - - else if(href_list["play"]) - if(playing) - return - playing = 1 - spawn() - playsong(usr) - SSnanoui.update_uis(src) - - else if(href_list["insertline"]) - var/num = round(text2num(href_list["insertline"])) - if(num < 1 || num > lines.len + 1) - return - - var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null) - if(!newline || !in_range(instrumentObj, usr)) - return - if(lines.len > 200) - return - if(length(newline) > 200) - newline = copytext(newline, 1, 200) - - lines.Insert(num, newline) - SSnanoui.update_uis(src) - - else if(href_list["deleteline"]) - var/num = round(text2num(href_list["deleteline"])) - if(num > lines.len || num < 1) - return - lines.Cut(num, num + 1) - SSnanoui.update_uis(src) - - else if(href_list["modifyline"]) - var/num = round(text2num(href_list["modifyline"])) - var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null) - if(!content || !in_range(instrumentObj, usr)) - return - if(length(content) > 200) - content = copytext(content, 1, 200) - if(num > lines.len || num < 1) - return - lines[num] = content - SSnanoui.update_uis(src) - - else if(href_list["stop"]) - playing = 0 - SSnanoui.update_uis(src) - -/datum/song/proc/sanitize_tempo(new_tempo) - new_tempo = abs(new_tempo) - return max(round(new_tempo, world.tick_lag), world.tick_lag) - -// subclass for handheld instruments, like violin -/datum/song/handheld - -/datum/song/handheld/shouldStopPlaying() - if(instrumentObj) - return !isliving(instrumentObj.loc) - else - return 1 - - -////////////////////////////////////////////////////////////////////////// - - -/obj/structure/piano - name = "space minimoog" - icon = 'icons/obj/musician.dmi' - icon_state = "minimoog" - anchored = 1 - density = 1 - var/datum/song/song - - -/obj/structure/piano/New() - ..() - song = new("piano", src) - - if(prob(50)) - name = "space minimoog" - desc = "This is a minimoog, like a space piano, but more spacey!" - icon_state = "minimoog" - else - name = "space piano" - desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't." - icon_state = "piano" - -/obj/structure/piano/Destroy() - QDEL_NULL(song) - return ..() - -/obj/structure/piano/Initialize() - if(song) - song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded - ..() - -/obj/structure/piano/attack_hand(mob/user as mob) - ui_interact(user) - -/obj/structure/piano/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(!isliving(user) || user.incapacitated() || !anchored) - return - - song.ui_interact(user, ui_key, ui, force_open) - -/obj/structure/piano/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - return song.ui_data(user, ui_key, state) - -/obj/structure/piano/Topic(href, href_list) - song.Topic(href, href_list) - -/obj/structure/piano/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(!anchored && !isinspace()) - WRENCH_ANCHOR_MESSAGE - if(!I.use_tool(src, user, 20, volume = I.tool_volume)) - return - user.visible_message( \ - "[user] tightens [src]'s casters.", \ - " You have tightened [src]'s casters. Now it can be played again.", \ - "You hear ratchet.") - anchored = TRUE - else if(anchored) - to_chat(user, " You begin to loosen [src]'s casters...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume)) - return - user.visible_message( \ - "[user] loosens [src]'s casters.", \ - " You have loosened [src]. Now it can be pulled somewhere else.", \ - "You hear ratchet.") - anchored = FALSE - else - to_chat(user, "[src] needs to be bolted to the floor!") diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm index ba5258004a0..5a8a122be21 100644 --- a/code/game/objects/structures/signs.dm +++ b/code/game/objects/structures/signs.dm @@ -292,12 +292,12 @@ /obj/structure/sign/directions/engineering name = "\improper Engineering Department" - desc = "A direction sign, pointing out which way the Engineering department is." + desc = "A direction sign, pointing out which way the Engineering Department is." icon_state = "direction_eng" /obj/structure/sign/directions/security name = "\improper Security Department" - desc = "A direction sign, pointing out which way the Security department is." + desc = "A direction sign, pointing out which way the Security Department is." icon_state = "direction_sec" /obj/structure/sign/directions/medical @@ -307,12 +307,12 @@ /obj/structure/sign/directions/evac name = "\improper Escape Arm" - desc = "A direction sign, pointing out which way escape shuttle dock is." + desc = "A direction sign, pointing out which way Escape Shuttle Dock is." icon_state = "direction_evac" /obj/structure/sign/directions/cargo name = "\improper Cargo Department" - desc = "A direction sign, pointing out which way the Cargo department is." + desc = "A direction sign, pointing out which way the Cargo Department is." icon_state = "direction_supply" /obj/structure/sign/explosives diff --git a/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm b/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm index b303df8079a..b7339086a68 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm @@ -89,3 +89,6 @@ return attack_hand(user) else return ..() + +/obj/structure/bed/nest/prevents_buckled_mobs_attacking() + return TRUE diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm index 9d0d4568e73..d91caa4244f 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm @@ -117,7 +117,7 @@ /obj/structure/bed/roller/MouseDrop(over_object, src_location, over_location) ..() if(over_object == usr && Adjacent(usr) && (in_range(src, usr) || usr.contents.Find(src))) - if(!ishuman(usr)) + if(!ishuman(usr) || usr.incapacitated()) return if(has_buckled_mobs()) return 0 diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index bdab5ca2466..e9abfca2dac 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -90,7 +90,7 @@ ..() if(climber) climber.Weaken(2) - climber.visible_message("[climber.name] has been knocked off the table", "You've been knocked off the table", "You see [climber.name] get knocked off the table") + climber.visible_message("[climber.name] has been knocked off the table", "You've been knocked off the table", "You hear [climber.name] get knocked off the table") else if(Adjacent(user) && user.pulling && user.pulling.pass_flags & PASSTABLE) user.Move_Pulled(src) if(user.pulling.loc == loc) diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index 692d15d79ec..b6796755b79 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -57,24 +57,37 @@ if(..()) return 1 add_fingerprint(user) - ui_interact(user) + tgui_interact(user) /obj/structure/dispenser/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/structure/dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, master_ui = null, datum/topic_state/state = GLOB.default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/structure/dispenser/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "tank_dispenser.tmpl", name, 275, 100, state = state) + ui = new(user, src, ui_key, "TankDispenser", name, 275, 100, master_ui, state) ui.open() -/obj/structure/dispenser/ui_data(user) +/obj/structure/dispenser/tgui_data(user) var/list/data = list() data["o_tanks"] = LAZYLEN(stored_oxygen_tanks) data["p_tanks"] = LAZYLEN(stored_plasma_tanks) return data +/obj/structure/dispenser/tgui_act(action, list/params) + if(..()) + return + + switch(action) + if("oxygen") + try_remove_tank(usr, stored_oxygen_tanks) + + if("plasma") + try_remove_tank(usr, stored_plasma_tanks) + + add_fingerprint(usr) + return TRUE + /obj/structure/dispenser/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/tank/oxygen) || istype(I, /obj/item/tank/air) || istype(I, /obj/item/tank/anesthetic)) try_insert_tank(user, stored_oxygen_tanks, I) @@ -94,28 +107,6 @@ return return ..() -/obj/structure/dispenser/Topic(href, href_list) - if(..()) - return TRUE - - if(Adjacent(usr)) - usr.set_machine(src) - - // The oxygen tank button - if(href_list["oxygen"]) - try_remove_tank(usr, stored_oxygen_tanks) - - // The plasma tank button - if(href_list["plasma"]) - try_remove_tank(usr, stored_plasma_tanks) - - add_fingerprint(usr) - updateUsrDialog() - SSnanoui.try_update_ui(usr, src) - else - SSnanoui.close_user_uis(usr,src) - return TRUE - /// Called when the user clicks on the oxygen or plasma tank UI buttons, and tries to withdraw a tank. /obj/structure/dispenser/proc/try_remove_tank(mob/living/user, list/tank_list) if(!LAZYLEN(tank_list)) @@ -144,7 +135,7 @@ tank_list.Add(T) update_icon() to_chat(user, "You put [T] in [src].") - SSnanoui.try_update_ui(user, src) + SStgui.update_uis(src) /obj/structure/tank_dispenser/deconstruct(disassembled = TRUE) if(!(flags & NODECONSTRUCT)) diff --git a/code/game/objects/structures/transit_tubes/transit_tube.dm b/code/game/objects/structures/transit_tubes/transit_tube.dm index c7926870ccf..650df02c59b 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube.dm @@ -26,7 +26,7 @@ return !density // When destroyed by explosions, properly handle contents. -obj/structure/transit_tube/ex_act(severity) +/obj/structure/transit_tube/ex_act(severity) switch(severity) if(1.0) for(var/atom/movable/AM in contents) diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm index 3a1546bdd2b..0a059198d2a 100644 --- a/code/game/objects/structures/windoor_assembly.dm +++ b/code/game/objects/structures/windoor_assembly.dm @@ -31,14 +31,14 @@ . = ..() . += "Alt-click to rotate it clockwise." -obj/structure/windoor_assembly/New(loc, set_dir) +/obj/structure/windoor_assembly/New(loc, set_dir) ..() if(set_dir) dir = set_dir ini_dir = dir air_update_turf(1) -obj/structure/windoor_assembly/Destroy() +/obj/structure/windoor_assembly/Destroy() density = FALSE QDEL_NULL(electronics) air_update_turf(1) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 9faab45cf97..cfe8a20cce7 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -666,7 +666,7 @@ GLOBAL_LIST_INIT(wcCommon, pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", icon_state = "tinted_window" opacity = 1 -obj/structure/window/full/reinforced/ice +/obj/structure/window/full/reinforced/ice icon = 'icons/obj/smooth_structures/rice_window.dmi' icon_state = "ice_window" max_integrity = 150 diff --git a/code/game/shuttle_engines.dm b/code/game/shuttle_engines.dm index 3c1ba0b8fbc..415df5d9dde 100644 --- a/code/game/shuttle_engines.dm +++ b/code/game/shuttle_engines.dm @@ -19,11 +19,13 @@ opacity = 0 anchored = 1 - CanPass(atom/movable/mover, turf/target, height) - if(!height) return 0 - else return ..() +/obj/structure/shuttle/window/CanPass(atom/movable/mover, turf/target, height) + if(!height) + return 0 + else + return ..() - CanAtmosPass(turf/T) +/obj/structure/shuttle/window/CanAtmosPass(turf/T) return !density /obj/structure/shuttle/engine diff --git a/code/game/sound.dm b/code/game/sound.dm index 870ac5e6966..6882c27df3d 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -4,12 +4,13 @@ return var/turf/turf_source = get_turf(source) - if(!turf_source) return + if(!SSsounds.channel_list) // Not ready yet + return //allocate a channel if necessary now so its the same for everyone - channel = channel || open_sound_channel() + channel = channel || SSsounds.random_available_channel() // Looping through the player list has the added bonus of working for mobs inside containers var/sound/S = sound(get_sfx(soundin)) @@ -33,7 +34,7 @@ if(distance <= maxdistance) M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S) -/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S) +/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S, distance_multiplier = 1) if(!client || !can_hear()) return @@ -41,7 +42,7 @@ S = sound(get_sfx(soundin)) S.wait = 0 //No queue - S.channel = channel || open_sound_channel() + S.channel = channel || SSsounds.random_available_channel() S.volume = vol if(vary) @@ -55,6 +56,7 @@ //sound volume falloff with distance var/distance = get_dist(T, turf_source) + distance *= distance_multiplier S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff. @@ -81,9 +83,9 @@ return //No sound var/dx = turf_source.x - T.x // Hearing from the right/left - S.x = dx + S.x = dx * distance_multiplier var/dz = turf_source.y - T.y // Hearing from infront/behind - S.z = dz + S.z = dz * distance_multiplier // The y value is for above your head, but there is no ceiling in 2d spessmens. S.y = 1 S.falloff = (falloff ? falloff : FALLOFF_SOUNDS) @@ -98,15 +100,14 @@ var/mob/M = m M.playsound_local(M, null, volume, vary, frequency, falloff, channel, pressure_affected, S) -/proc/open_sound_channel() - var/static/next_channel = 1 //loop through the available 1024 - (the ones we reserve) channels and pray that its not still being used - . = ++next_channel - if(next_channel > CHANNEL_HIGHEST_AVAILABLE) - next_channel = 1 - /mob/proc/stop_sound_channel(chan) SEND_SOUND(src, sound(null, repeat = 0, wait = 0, channel = chan)) +/mob/proc/set_sound_channel_volume(channel, volume) + var/sound/S = sound(null, FALSE, FALSE, channel, volume) + S.status = SOUND_UPDATE + SEND_SOUND(src, S) + /client/proc/playtitlemusic() if(!SSticker || !SSticker.login_music || config.disable_lobby_music) return diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 6bc1c4ba142..def0142a481 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -53,6 +53,7 @@ wet_overlay = image('icons/effects/water.dmi', src, "ice_floor") else wet_overlay = image('icons/effects/water.dmi', src, "wet_static") + wet_overlay.plane = FLOOR_OVERLAY_PLANE overlays += wet_overlay if(time == INFINITY) return diff --git a/code/game/turfs/simulated/floor/chasm.dm b/code/game/turfs/simulated/floor/chasm.dm index d8ed888bc27..26921ba9ffd 100644 --- a/code/game/turfs/simulated/floor/chasm.dm +++ b/code/game/turfs/simulated/floor/chasm.dm @@ -97,8 +97,8 @@ return FALSE //Flies right over the chasm if(isliving(AM)) - var/mob/M = AM - if(M.flying) + var/mob/living/M = AM + if(M.flying || M.floating) return FALSE if(ishuman(AM)) var/mob/living/carbon/human/H = AM diff --git a/code/game/turfs/simulated/floor/light_floor.dm b/code/game/turfs/simulated/floor/light_floor.dm index 8276c0de33f..0d27e02537f 100644 --- a/code/game/turfs/simulated/floor/light_floor.dm +++ b/code/game/turfs/simulated/floor/light_floor.dm @@ -11,12 +11,12 @@ #define LIGHTFLOOR_CYCLEB 10 /turf/simulated/floor/light - name = "Light floor" + name = "\improper light floor" light_range = 5 icon_state = "light_on" floor_tile = /obj/item/stack/tile/light broken_states = list("light_broken") - var/on = 1 + var/on = TRUE var/state = LIGHTFLOOR_ON var/can_modify_colour = TRUE @@ -30,34 +30,34 @@ switch(state) if(LIGHTFLOOR_ON) icon_state = "light_on" - set_light(5,null,LIGHT_COLOR_LIGHTBLUE) + set_light(5, null,LIGHT_COLOR_LIGHTBLUE) if(LIGHTFLOOR_WHITE) icon_state = "light_on-w" - set_light(5,null,LIGHT_COLOR_WHITE) + set_light(5, null,LIGHT_COLOR_WHITE) if(LIGHTFLOOR_RED) icon_state = "light_on-r" - set_light(5,null,LIGHT_COLOR_RED) + set_light(5, null,LIGHT_COLOR_RED) if(LIGHTFLOOR_GREEN) icon_state = "light_on-g" - set_light(5,null,LIGHT_COLOR_PURE_GREEN) + set_light(5, null,LIGHT_COLOR_PURE_GREEN) if(LIGHTFLOOR_YELLOW) icon_state = "light_on-y" - set_light(5,null,"#FFFF00") + set_light(5, null,"#FFFF00") if(LIGHTFLOOR_BLUE) icon_state = "light_on-b" - set_light(5,null,LIGHT_COLOR_DARKBLUE) + set_light(5, null,LIGHT_COLOR_DARKBLUE) if(LIGHTFLOOR_PURPLE) icon_state = "light_on-p" - set_light(5,null,LIGHT_COLOR_PURPLE) + set_light(5, null,LIGHT_COLOR_PURPLE) if(LIGHTFLOOR_GENERICCYCLE) icon_state = "light_on-cycle_all" - set_light(5,null,LIGHT_COLOR_WHITE) + set_light(5, null,LIGHT_COLOR_WHITE) if(LIGHTFLOOR_CYCLEA) icon_state = "light_on-dancefloor_A" set_light(5,null,LIGHT_COLOR_RED) if(LIGHTFLOOR_CYCLEB) icon_state = "light_on-dancefloor_B" - set_light(5,null,LIGHT_COLOR_DARKBLUE) + set_light(5, null,LIGHT_COLOR_DARKBLUE) else icon_state = "light_off" set_light(0) @@ -72,11 +72,10 @@ /turf/simulated/floor/light/attack_hand(mob/user) if(!can_modify_colour) return - on = !on - update_icon() + toggle_light(!on) /turf/simulated/floor/light/attackby(obj/item/C, mob/user, params) - if(istype(C,/obj/item/light/bulb)) //only for light tiles + if(istype(C, /obj/item/light/bulb)) //only for light tiles if(!state) qdel(C) state = LIGHTFLOOR_ON @@ -103,6 +102,16 @@ else to_chat(user, "[src]'s light bulb appears to have burned out.") +/turf/simulated/floor/light/proc/toggle_light(light) + // 0 = OFF + // 1 = ON + on = light + update_icon() + +/turf/simulated/floor/light/extinguish_light() + toggle_light(FALSE) + visible_message("[src] flickers and falls dark.") + //Cycles through all of the colours /turf/simulated/floor/light/colour_cycle state = LIGHTFLOOR_GENERICCYCLE @@ -119,3 +128,16 @@ name = "dancefloor" desc = "Funky floor." state = LIGHTFLOOR_CYCLEB + + +#undef LIGHTFLOOR_ON +#undef LIGHTFLOOR_WHITE +#undef LIGHTFLOOR_RED +#undef LIGHTFLOOR_GREEN +#undef LIGHTFLOOR_YELLOW +#undef LIGHTFLOOR_BLUE +#undef LIGHTFLOOR_PURPLE + +#undef LIGHTFLOOR_GENERICCYCLE +#undef LIGHTFLOOR_CYCLEA +#undef LIGHTFLOOR_CYCLEB diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm index ebad3554f18..8bf3e820d3d 100644 --- a/code/game/turfs/simulated/minerals.dm +++ b/code/game/turfs/simulated/minerals.dm @@ -439,7 +439,7 @@ var/area/A = get_area(bombturf) var/notify_admins = 0 - if(z != 5) + if(!is_mining_level(z)) notify_admins = 1 if(!triggered_by_explosion) message_admins("[key_name_admin(user)] has triggered a gibtonite deposit reaction at [A.name] (JMP).") diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm index a96cbae1269..b8110cf2b54 100644 --- a/code/game/turfs/space/transit.dm +++ b/code/game/turfs/space/transit.dm @@ -11,72 +11,98 @@ pushdirection = SOUTH // south because the space tile is scrolling south //IF ANYONE KNOWS A MORE EFFICIENT WAY OF MANAGING THESE SPRITES, BE MY GUEST. - shuttlespace_ns1 - icon_state = "speedspace_ns_1" - shuttlespace_ns2 - icon_state = "speedspace_ns_2" - shuttlespace_ns3 - icon_state = "speedspace_ns_3" - shuttlespace_ns4 - icon_state = "speedspace_ns_4" - shuttlespace_ns5 - icon_state = "speedspace_ns_5" - shuttlespace_ns6 - icon_state = "speedspace_ns_6" - shuttlespace_ns7 - icon_state = "speedspace_ns_7" - shuttlespace_ns8 - icon_state = "speedspace_ns_8" - shuttlespace_ns9 - icon_state = "speedspace_ns_9" - shuttlespace_ns10 - icon_state = "speedspace_ns_10" - shuttlespace_ns11 - icon_state = "speedspace_ns_11" - shuttlespace_ns12 - icon_state = "speedspace_ns_12" - shuttlespace_ns13 - icon_state = "speedspace_ns_13" - shuttlespace_ns14 - icon_state = "speedspace_ns_14" - shuttlespace_ns15 - icon_state = "speedspace_ns_15" +/turf/space/transit/north/shuttlespace_ns1 + icon_state = "speedspace_ns_1" + +/turf/space/transit/north/shuttlespace_ns2 + icon_state = "speedspace_ns_2" + +/turf/space/transit/north/shuttlespace_ns3 + icon_state = "speedspace_ns_3" + +/turf/space/transit/north/shuttlespace_ns4 + icon_state = "speedspace_ns_4" + +/turf/space/transit/north/shuttlespace_ns5 + icon_state = "speedspace_ns_5" + +/turf/space/transit/north/shuttlespace_ns6 + icon_state = "speedspace_ns_6" + +/turf/space/transit/north/shuttlespace_ns7 + icon_state = "speedspace_ns_7" + +/turf/space/transit/north/shuttlespace_ns8 + icon_state = "speedspace_ns_8" + +/turf/space/transit/north/shuttlespace_ns9 + icon_state = "speedspace_ns_9" + +/turf/space/transit/north/shuttlespace_ns10 + icon_state = "speedspace_ns_10" + +/turf/space/transit/north/shuttlespace_ns11 + icon_state = "speedspace_ns_11" + +/turf/space/transit/north/shuttlespace_ns12 + icon_state = "speedspace_ns_12" + +/turf/space/transit/north/shuttlespace_ns13 + icon_state = "speedspace_ns_13" + +/turf/space/transit/north/shuttlespace_ns14 + icon_state = "speedspace_ns_14" + +/turf/space/transit/north/shuttlespace_ns15 + icon_state = "speedspace_ns_15" /turf/space/transit/east // moving to the east - pushdirection = WEST - shuttlespace_ew1 - icon_state = "speedspace_ew_1" - shuttlespace_ew2 - icon_state = "speedspace_ew_2" - shuttlespace_ew3 - icon_state = "speedspace_ew_3" - shuttlespace_ew4 - icon_state = "speedspace_ew_4" - shuttlespace_ew5 - icon_state = "speedspace_ew_5" - shuttlespace_ew6 - icon_state = "speedspace_ew_6" - shuttlespace_ew7 - icon_state = "speedspace_ew_7" - shuttlespace_ew8 - icon_state = "speedspace_ew_8" - shuttlespace_ew9 - icon_state = "speedspace_ew_9" - shuttlespace_ew10 - icon_state = "speedspace_ew_10" - shuttlespace_ew11 - icon_state = "speedspace_ew_11" - shuttlespace_ew12 - icon_state = "speedspace_ew_12" - shuttlespace_ew13 - icon_state = "speedspace_ew_13" - shuttlespace_ew14 - icon_state = "speedspace_ew_14" - shuttlespace_ew15 - icon_state = "speedspace_ew_15" +/turf/space/transit/east/shuttlespace_ew1 + icon_state = "speedspace_ew_1" +/turf/space/transit/east/shuttlespace_ew2 + icon_state = "speedspace_ew_2" + +/turf/space/transit/east/shuttlespace_ew3 + icon_state = "speedspace_ew_3" + +/turf/space/transit/east/shuttlespace_ew4 + icon_state = "speedspace_ew_4" + +/turf/space/transit/east/shuttlespace_ew5 + icon_state = "speedspace_ew_5" + +/turf/space/transit/east/shuttlespace_ew6 + icon_state = "speedspace_ew_6" + +/turf/space/transit/east/shuttlespace_ew7 + icon_state = "speedspace_ew_7" + +/turf/space/transit/east/shuttlespace_ew8 + icon_state = "speedspace_ew_8" + +/turf/space/transit/east/shuttlespace_ew9 + icon_state = "speedspace_ew_9" + +/turf/space/transit/east/shuttlespace_ew10 + icon_state = "speedspace_ew_10" + +/turf/space/transit/east/shuttlespace_ew11 + icon_state = "speedspace_ew_11" + +/turf/space/transit/east/shuttlespace_ew12 + icon_state = "speedspace_ew_12" + +/turf/space/transit/east/shuttlespace_ew13 + icon_state = "speedspace_ew_13" + +/turf/space/transit/east/shuttlespace_ew14 + icon_state = "speedspace_ew_14" + +/turf/space/transit/east/shuttlespace_ew15 + icon_state = "speedspace_ew_15" //-tg- stuff /turf/space/transit @@ -94,8 +120,6 @@ var/max = world.maxx-TRANSITIONEDGE var/min = 1+TRANSITIONEDGE - var/_z = pick(levels_by_trait(REACHABLE)) //select a random space zlevel - //now select coordinates for a border turf var/_x var/_y @@ -113,7 +137,8 @@ _x = rand(min,max) _y = min - var/turf/T = locate(_x, _y, _z) + var/list/levels_available = get_all_linked_levels_zpos() + var/turf/T = locate(_x, _y, pick(levels_available)) AM.forceMove(T) AM.newtonian_move(dir) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 8f8e7fdca9a..8170f9fbe5f 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -25,7 +25,7 @@ var/blocks_air = 0 - var/PathNode/PNode = null //associated PathNode in the A* algorithm + var/datum/pathnode/PNode = null //associated PathNode in the A* algorithm flags = 0 @@ -165,14 +165,6 @@ var/mob/O = M if(!O.lastarea) O.lastarea = get_area(O.loc) -// O.update_gravity(O.mob_has_gravity(src)) - - var/loopsanity = 100 - for(var/atom/A in range(1)) - if(loopsanity == 0) - break - loopsanity-- - A.HasProximity(M) // If an opaque movable atom moves around we need to potentially update visibility. if(M.opacity) @@ -264,6 +256,13 @@ if(SSair && !ignore_air) SSair.add_to_active(src) + //update firedoor adjacency + var/list/turfs_to_check = get_adjacent_open_turfs(src) | src + for(var/I in turfs_to_check) + var/turf/T = I + for(var/obj/machinery/door/firedoor/FD in T) + FD.CalculateAffectingAreas() + if(!keep_cabling && !can_have_cabling()) for(var/obj/structure/cable/C in contents) qdel(C) diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index 824159487eb..04ee5901054 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -1,6 +1,6 @@ -GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") +GLOBAL_VAR_INIT(normal_ooc_colour, "#275FC5") GLOBAL_VAR_INIT(member_ooc_colour, "#035417") -GLOBAL_VAR_INIT(mentor_ooc_colour, "#0099cc") +GLOBAL_VAR_INIT(mentor_ooc_colour, "#00B0EB") GLOBAL_VAR_INIT(moderator_ooc_colour, "#184880") GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00") @@ -53,6 +53,7 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00") return log_ooc(msg, src) + mob.create_log(OOC_LOG, msg) var/display_colour = GLOB.normal_ooc_colour if(holder && !holder.fakekey) @@ -205,7 +206,7 @@ GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00") return log_looc(msg, src) - + mob.create_log(LOOC_LOG, msg) var/mob/source = mob.get_looc_source() var/list/heard = get_mobs_in_view(7, source) diff --git a/code/game/world.dm b/code/game/world.dm index fdb0b8f63be..d2828f9c04b 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -222,8 +222,12 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday) if(input["key"] != config.comms_password) return "Bad Key" else + var/prtext = input["announce"] + var/pr_substring = copytext(prtext, 1, 23) + if(pr_substring == "Pull Request merged by") + GLOB.pending_server_update = TRUE for(var/client/C in GLOB.clients) - to_chat(C, "PR: [input["announce"]]") + to_chat(C, "PR: [prtext]") else if("kick" in input) /* @@ -266,6 +270,13 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday) update_status() return "Set listed status to invisible." + + else if("hostannounce" in input) + if(!key_valid) + return keySpamProtect(addr) + GLOB.pending_server_update = TRUE + to_chat(world, "
    Server Announcement: [input["message"]]
    ") + /proc/keySpamProtect(var/addr) if(GLOB.world_topic_spam_protect_ip == addr && abs(GLOB.world_topic_spam_protect_time - world.time) < 50) spawn(50) @@ -332,7 +343,13 @@ GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday) return #endif + var/secs_before_auto_reconnect = 10 + if(GLOB.pending_server_update) + secs_before_auto_reconnect = 60 + to_chat(world, "Reboot will take a little longer, due to pending updates.") for(var/client/C in GLOB.clients) + + C << output(list2params(list(secs_before_auto_reconnect)), "browseroutput:reboot") if(config.server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite C << link("byond://[config.server]") @@ -477,7 +494,7 @@ GLOBAL_VAR_INIT(failed_old_db_connections, 0) return . //This proc ensures that the connection to the feedback database (global variable dbcon) is established -proc/establish_db_connection() +/proc/establish_db_connection() if(GLOB.failed_db_connections > FAILED_DB_CONNECTION_CUTOFF) return 0 diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index 09df94dc06e..fd998677d75 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -1,6 +1,6 @@ #define MAX_ADMIN_BANS_PER_ADMIN 1 -datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = -1, var/reason, var/job = "", var/rounds = 0, var/banckey = null, var/banip = null, var/bancid = null) +/datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = -1, var/reason, var/job = "", var/rounds = 0, var/banckey = null, var/banip = null, var/bancid = null) if(!check_rights(R_BAN)) return @@ -153,7 +153,7 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = else flag_account_for_forum_sync(ckey) -datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") +/datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") if(!check_rights(R_BAN)) return @@ -233,7 +233,7 @@ datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") else flag_account_for_forum_sync(ckey) -datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) +/datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) if(!check_rights(R_BAN)) return @@ -297,9 +297,10 @@ datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) to_chat(usr, "Cancelled") return -datum/admins/proc/DB_ban_unban_by_id(var/id) +/datum/admins/proc/DB_ban_unban_by_id(var/id) - if(!check_rights(R_BAN)) return + if(!check_rights(R_BAN)) + return var/sql = "SELECT ckey FROM [format_table_name("ban")] WHERE id = [id]" @@ -343,9 +344,9 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) /client/proc/DB_ban_panel() set category = "Admin" set name = "Banning Panel" - set desc = "Edit admin permissions" + set desc = "DB Ban Panel" - if(!holder) + if(!check_rights(R_BAN)) return holder.DB_ban_panel() @@ -356,7 +357,8 @@ datum/admins/proc/DB_ban_unban_by_id(var/id) if(!usr.client) return - if(!check_rights(R_BAN)) return + if(!check_rights(R_BAN)) + return establish_db_connection() if(!GLOB.dbcon.IsConnected()) diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index c7f2baac4ef..3f69c2199d0 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -1,5 +1,5 @@ //Blocks an attempt to connect before even creating our client datum thing. -world/IsBanned(key, address, computer_id, type, check_ipintel = TRUE) +/world/IsBanned(key, address, computer_id, type, check_ipintel = TRUE) if(!config.ban_legacy_system) if(address) @@ -36,17 +36,6 @@ world/IsBanned(key, address, computer_id, type, check_ipintel = TRUE) // message_admins("Failed Login: [key] - Guests not allowed") return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a BYOND account.") - //check if the IP address is a known Tor node - if(config.ToRban && ToRban_isbanned(address)) - log_adminwarn("Failed Login: [key] [computer_id] [address] - Banned: Tor") - message_admins("Failed Login: [key] - Banned: Tor") - //ban their computer_id and ckey for posterity - AddBan(ckey(key), computer_id, "Use of Tor", "Automated Ban", 0, 0) - var/mistakemessage = "" - if(config.banappeals) - mistakemessage = "\nIf you believe this is a mistake, please request help at [config.banappeals]." - return list("reason"="using Tor", "desc"="\nReason: The network you are using to connect has been banned.[mistakemessage]") - //check if the IP address is a known proxy/vpn, and the user is not whitelisted if(check_ipintel && config.ipintel_email && config.ipintel_whitelist && ipintel_is_banned(key, address)) log_adminwarn("Failed Login: [key] [computer_id] [address] - Proxy/VPN") diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm index 9aab12234d2..293504919d4 100644 --- a/code/modules/admin/NewBan.dm +++ b/code/modules/admin/NewBan.dm @@ -103,7 +103,8 @@ GLOBAL_PROTECT(banlist_savefile) // Obvious reasons GLOB.banlist_savefile.cd = "/base" if( GLOB.banlist_savefile.dir.Find("[ckey][computerid]") ) - to_chat(usr, "Ban already exists.") + if(usr) + to_chat(usr, "Ban already exists.") return 0 else GLOB.banlist_savefile.dir.Add("[ckey][computerid]") diff --git a/code/modules/admin/ToRban.dm b/code/modules/admin/ToRban.dm deleted file mode 100644 index b486f168503..00000000000 --- a/code/modules/admin/ToRban.dm +++ /dev/null @@ -1,89 +0,0 @@ -//By Carnwennan -//fetches an external list and processes it into a list of ip addresses. -//It then stores the processed list into a savefile for later use -#define TORFILE "data/ToR_ban.bdb" -#define TOR_UPDATE_INTERVAL 216000 //~6 hours - -/proc/ToRban_isbanned(var/ip_address) - var/savefile/F = new(TORFILE) - if(F) - if( ip_address in F.dir ) - return 1 - return 0 - -/proc/ToRban_autoupdate() - var/savefile/F = new(TORFILE) - if(F) - var/last_update - F["last_update"] >> last_update - if((last_update + TOR_UPDATE_INTERVAL) < world.realtime) //we haven't updated for a while - ToRban_update() - return - -/proc/ToRban_update() - spawn(0) - log_world("Downloading updated ToR data...") - var/http[] = world.Export("http://exitlist.torproject.org/exit-addresses") - - var/list/rawlist = file2list(http["CONTENT"]) - if(rawlist.len) - fdel(TORFILE) - var/savefile/F = new(TORFILE) - for( var/line in rawlist ) - if(!line) continue - if( copytext(line,1,12) == "ExitAddress" ) - var/cleaned = copytext(line,13,length(line)-19) - if(!cleaned) continue - F[cleaned] << 1 - to_chat(F["last_update"], world.realtime) - log_world("ToR data updated!") - if(usr) - to_chat(usr, "ToRban updated.") - return 1 - log_world("ToR data update aborted: no data.") - return 0 - -/client/proc/ToRban(task in list("update","toggle","show","remove","remove all","find")) - set name = "ToRban" - set category = "Server" - if(!holder) return - switch(task) - if("update") - ToRban_update() - if("toggle") - if(config) - if(config.ToRban) - config.ToRban = 0 - message_admins("ToR banning disabled.") - else - config.ToRban = 1 - message_admins("ToR banning enabled.") - if("show") - var/savefile/F = new(TORFILE) - var/dat - if( length(F.dir) ) - for( var/i=1, i<=length(F.dir), i++ ) - dat += "
    " - dat = "
    [entry][functions]
    #[i] [F.dir[i]]
    [dat]
    " - else - dat = "No addresses in list." - src << browse(dat,"window=ToRban_show") - if("remove") - var/savefile/F = new(TORFILE) - var/choice = input(src,"Please select an IP address to remove from the ToR banlist:","Remove ToR ban",null) as null|anything in F.dir - if(choice) - F.dir.Remove(choice) - to_chat(src, "Address removed") - if("remove all") - to_chat(src, "[TORFILE] was [fdel(TORFILE)?"":"not "]removed.") - if("find") - var/input = input(src,"Please input an IP address to search for:","Find ToR ban",null) as null|text - if(input) - if(ToRban_isbanned(input)) - to_chat(src, "Address is a known ToR address") - else - to_chat(src, "Address is not a known ToR address") - return - -#undef TORFILE -#undef TOR_UPDATE_INTERVAL diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 1eed8083a97..8bd4381b195 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -20,22 +20,39 @@ GLOBAL_VAR_INIT(nologevent, 0) if(C.prefs.atklog <= loglevel) to_chat(C, msg) - -/proc/message_adminTicket(var/msg, var/alt = FALSE) - if(alt) - msg = "ADMIN TICKET: [msg]" - else - msg = "ADMIN TICKET: [msg]" +/** + * Sends a message to the staff able to see admin tickets + * Arguments: + * msg - The message being send + * important - If the message is important. If TRUE it will ignore the CHAT_NO_TICKETLOGS preferences, + send a sound and flash the window. Defaults to FALSE + */ +/proc/message_adminTicket(msg, important = FALSE) for(var/client/C in GLOB.admins) if(R_ADMIN & C.holder.rights) - if(C.prefs && !(C.prefs.toggles & CHAT_NO_TICKETLOGS)) + if(important || (C.prefs && !(C.prefs.toggles & CHAT_NO_TICKETLOGS))) to_chat(C, msg) + if(important) + if(C.prefs?.sound & SOUND_ADMINHELP) + SEND_SOUND(C, 'sound/effects/adminhelp.ogg') + window_flash(C) -/proc/message_mentorTicket(var/msg) +/** + * Sends a message to the staff able to see mentor tickets + * Arguments: + * msg - The message being send + * important - If the message is important. If TRUE it will ignore the CHAT_NO_TICKETLOGS preferences, + send a sound and flash the window. Defaults to FALSE + */ +/proc/message_mentorTicket(msg, important = FALSE) for(var/client/C in GLOB.admins) if(check_rights(R_ADMIN | R_MENTOR | R_MOD, 0, C.mob)) - if(C.prefs && !(C.prefs.toggles & CHAT_NO_MENTORTICKETLOGS)) + if(important || (C.prefs && !(C.prefs.toggles & CHAT_NO_MENTORTICKETLOGS))) to_chat(C, msg) + if(important) + if(C.prefs?.sound & SOUND_MENTORHELP) + SEND_SOUND(C, 'sound/effects/adminhelp.ogg') + window_flash(C) /proc/admin_ban_mobsearch(var/mob/M, var/ckey_to_find, var/mob/admin_to_notify) if(!M || !M.ckey) @@ -69,7 +86,10 @@ GLOBAL_VAR_INIT(nologevent, 0) body += "Options panel for [M]" if(M.client) body += " played by [M.client] " - body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] " + if(check_rights(R_PERMISSIONS, 0)) + body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] " + else + body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] " body += "\[" + M.client.get_exp_type(EXP_TYPE_CREW) + " as [EXP_TYPE_CREW]\]" if(isnewplayer(M)) @@ -78,10 +98,11 @@ GLOBAL_VAR_INIT(nologevent, 0) body += " \[Heal\] " body += "

    \[ " + body += "LOGS - " body += "VV - " body += "[ADMIN_TP(M,"TP")] - " if(M.client) - body += "PM - " + body += "PM - " body += "[ADMIN_SM(M,"SM")] - " if(ishuman(M) && M.mind) body += "HM -" @@ -109,17 +130,18 @@ GLOBAL_VAR_INIT(nologevent, 0) else body += "Add to Watchlist " - if(M.client) body += "| Prison | " body += "\ Send back to Lobby | " + body += "\ Erase Flavor Text | " + body += "\ Use Random Name | " var/muted = M.client.prefs.muted body += {"
    Mute: - \[IC | - OOC | - PRAY | - ADMINHELP | - DEADCHAT\] - (toggle all) + \[IC | + OOC | + PRAY | + ADMINHELP | + DEADCHAT\] + (toggle all) "} var/jumptoeye = "" @@ -280,7 +302,7 @@ GLOBAL_VAR_INIT(nologevent, 0) /datum/admins/proc/vpn_whitelist() set category = "Admin" set name = "VPN Ckey Whitelist" - if(!check_rights(R_ADMIN)) + if(!check_rights(R_BAN)) return var/key = stripped_input(usr, "Enter ckey to add/remove, or leave blank to cancel:", "VPN Whitelist add/remove", max_length=32) if(key) @@ -699,7 +721,7 @@ GLOBAL_VAR_INIT(nologevent, 0) var/msg = "" if(SSticker.current_state == GAME_STATE_STARTUP) msg = " (The server is still setting up, but the round will be started as soon as possible.)" - message_admins("[usr.key] has started the game.[msg]") + message_admins("[usr.key] has started the game.[msg]") feedback_add_details("admin_verb","SN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return 1 else @@ -760,19 +782,6 @@ GLOBAL_VAR_INIT(nologevent, 0) world.update_status() feedback_add_details("admin_verb","TR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/datum/admins/proc/toggle_aliens() - set category = "Event" - set desc="Toggle alien mobs" - set name="Toggle Aliens" - - if(!check_rights(R_EVENT)) - return - - GLOB.aliens_allowed = !GLOB.aliens_allowed - log_admin("[key_name(usr)] toggled aliens to [GLOB.aliens_allowed].") - message_admins("[key_name_admin(usr)] toggled aliens [GLOB.aliens_allowed ? "on" : "off"].") - feedback_add_details("admin_verb","TA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - /datum/admins/proc/delay() set category = "Server" set desc="Delay the game start/end" diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index a627c96148f..527540e9490 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -33,7 +33,8 @@ /client/proc/investigate_show( subject in GLOB.investigate_log_subjects ) set name = "Investigate" set category = "Admin" - if(!holder) return + if(!check_rights(R_ADMIN)) + return switch(subject) if("notes") show_note() diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 80a60957c75..70476905971 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -10,11 +10,8 @@ GLOBAL_LIST_INIT(admin_verbs_default, list( GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/check_antagonists, /*shows all antags*/ /datum/admins/proc/show_player_panel, - /client/proc/player_panel, /*shows an interface for all players, with links to various panels (old style)*/ /client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/ /client/proc/invisimin, /*allows our mob to go invisible/visible*/ - /datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/ - /datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/ /datum/admins/proc/announce, /*priority announce something to all clients.*/ /client/proc/colorooc, /*allows us to set a custom colour for everything we say in ooc*/ /client/proc/resetcolorooc, /*allows us to set a reset our ooc color*/ @@ -23,7 +20,6 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ /client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/ - /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/ /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ /client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/ /client/proc/cmd_admin_open_logging_view, @@ -37,6 +33,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/jumptoturf, /*allows us to jump to a specific turf*/ /client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/ /client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcomm*/ + /client/proc/admin_deny_shuttle, /*toggles availability of shuttle calling*/ /client/proc/check_ai_laws, /*shows AI and borg laws*/ /client/proc/manage_silicon_laws, /* Allows viewing and editing silicon laws. */ /client/proc/admin_memo, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/ @@ -54,36 +51,30 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /datum/admins/proc/PlayerNotes, /client/proc/cmd_mentor_say, /datum/admins/proc/show_player_notes, - /datum/admins/proc/vpn_whitelist, /client/proc/free_slot, /*frees slot for chosen job*/ /client/proc/toggleattacklogs, /client/proc/toggleadminlogs, /client/proc/toggledebuglogs, /client/proc/update_mob_sprite, - /client/proc/toggledrones, /client/proc/man_up, /client/proc/global_man_up, /client/proc/delbook, /client/proc/view_flagged_books, + /client/proc/view_asays, /client/proc/empty_ai_core_toggle_latejoin, /client/proc/aooc, /client/proc/freeze, - /client/proc/alt_check, /client/proc/secrets, - /client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */ - /client/proc/change_human_appearance_self, /* Allows the human-based mob itself to change its basic appearance */ /client/proc/debug_variables, /client/proc/reset_all_tcs, /*resets all telecomms scripts*/ /client/proc/toggle_mentor_chat, /client/proc/toggle_advanced_interaction, /*toggle admin ability to interact with not only machines, but also atoms such as buttons and doors*/ - /client/proc/list_ssds_afks, - /client/proc/cmd_admin_headset_message, - /client/proc/spawn_floor_cluwne + /client/proc/list_ssds_afks )) GLOBAL_LIST_INIT(admin_verbs_ban, list( - /client/proc/unban_panel, - /client/proc/jobbans, - /client/proc/stickybanpanel + /client/proc/ban_panel, + /client/proc/stickybanpanel, + /datum/admins/proc/vpn_whitelist )) GLOBAL_LIST_INIT(admin_verbs_sounds, list( /client/proc/play_local_sound, @@ -99,7 +90,6 @@ GLOBAL_LIST_INIT(admin_verbs_event, list( /client/proc/drop_bomb, /client/proc/cinematic, /client/proc/one_click_antag, - /datum/admins/proc/toggle_aliens, /client/proc/cmd_admin_add_freeform_ai_law, /client/proc/cmd_admin_add_random_ai_law, /client/proc/make_sound, @@ -109,6 +99,7 @@ GLOBAL_LIST_INIT(admin_verbs_event, list( /client/proc/show_tip, /client/proc/cmd_admin_change_custom_event, /datum/admins/proc/access_news_network, /*allows access of newscasters*/ + /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/ /client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/ /client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/ /client/proc/response_team, // Response Teams admin verb @@ -116,7 +107,10 @@ GLOBAL_LIST_INIT(admin_verbs_event, list( /client/proc/fax_panel, /client/proc/event_manager_panel, /client/proc/modify_goals, - /client/proc/outfit_manager + /client/proc/outfit_manager, + /client/proc/cmd_admin_headset_message, + /client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */ + /client/proc/change_human_appearance_self /* Allows the human-based mob itself to change its basic appearance */ )) GLOBAL_LIST_INIT(admin_verbs_spawn, list( @@ -125,25 +119,27 @@ GLOBAL_LIST_INIT(admin_verbs_spawn, list( /client/proc/admin_deserialize )) GLOBAL_LIST_INIT(admin_verbs_server, list( - /client/proc/ToRban, + /client/proc/reload_admins, /client/proc/Set_Holiday, /datum/admins/proc/startnow, /datum/admins/proc/restart, /datum/admins/proc/delay, /datum/admins/proc/toggleaban, + /datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/ + /datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/ /client/proc/toggle_log_hrefs, /client/proc/everyone_random, /datum/admins/proc/toggleAI, /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ - /client/proc/cmd_debug_del_all, /client/proc/cmd_debug_del_sing, - /datum/admins/proc/toggle_aliens, /client/proc/delbook, /client/proc/view_flagged_books, + /client/proc/view_asays, /client/proc/toggle_antagHUD_use, /client/proc/toggle_antagHUD_restrictions, /client/proc/set_ooc, - /client/proc/reset_ooc + /client/proc/reset_ooc, + /client/proc/toggledrones )) GLOBAL_LIST_INIT(admin_verbs_debug, list( /client/proc/cmd_admin_list_open_jobs, @@ -152,9 +148,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list( /client/proc/debug_controller, /client/proc/cmd_debug_mob_lists, /client/proc/cmd_admin_delete, - /client/proc/cmd_debug_del_all, /client/proc/cmd_debug_del_sing, - /client/proc/reload_admins, /client/proc/restart_controller, /client/proc/enable_debug_verbs, /client/proc/toggledebuglogs, @@ -172,6 +166,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list( /client/proc/admin_serialize, /client/proc/jump_to_ruin, /client/proc/toggle_medal_disable, + /client/proc/uid_log )) GLOBAL_LIST_INIT(admin_verbs_possess, list( /proc/possess, @@ -197,7 +192,7 @@ GLOBAL_LIST_INIT(admin_verbs_mod, list( /client/proc/player_panel_new, /client/proc/dsay, /datum/admins/proc/show_player_panel, - /client/proc/jobbans, + /client/proc/ban_panel, /client/proc/debug_variables /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/ )) GLOBAL_LIST_INIT(admin_verbs_mentor, list( @@ -337,6 +332,9 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( ghost.reenter_corpse() log_admin("[key_name(usr)] re-entered their body") feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(ishuman(mob)) + var/mob/living/carbon/human/H = mob + H.regenerate_icons() // workaround for #13269 else if(isnewplayer(mob)) to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or observe first.") else @@ -367,19 +365,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") mob.remove_from_all_data_huds() -/client/proc/player_panel() - set name = "Player Panel" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - holder.player_panel_old() - feedback_add_details("admin_verb","PP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - /client/proc/player_panel_new() - set name = "Player Panel New" + set name = "Player Panel" set category = "Admin" if(!check_rights(R_ADMIN|R_MOD)) @@ -401,22 +388,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return -/client/proc/jobbans() - set name = "Display Job bans" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_MOD)) - return - - if(config.ban_legacy_system) - holder.Jobbans() - else - holder.DB_ban_panel() - feedback_add_details("admin_verb","VJB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/unban_panel() - set name = "Unban Panel" +/client/proc/ban_panel() + set name = "Ban Panel" set category = "Admin" if(!check_rights(R_BAN)) @@ -451,13 +424,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( feedback_add_details("admin_verb","S") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return -/client/proc/findStealthKey(txt) - if(txt) - for(var/P in GLOB.stealthminID) - if(GLOB.stealthminID[P] == txt) - return P - txt = GLOB.stealthminID[ckey] - return txt +/client/proc/getStealthKey() + return GLOB.stealthminID[ckey] /client/proc/createStealthKey() var/num = (rand(0,1000)) @@ -809,7 +777,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( set desc = "Allows you to change the mob appearance" set category = null - if(!check_rights(R_ADMIN)) + if(!check_rights(R_EVENT)) return if(!istype(H)) @@ -826,7 +794,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( return if(holder) - admin_log_and_message_admins("is altering the appearance of [H].") + log_and_message_admins("is altering the appearance of [H].") H.change_appearance(APPEARANCE_ALL, usr, usr, check_species_whitelist = 0) feedback_add_details("admin_verb","CHAA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -835,7 +803,7 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( set desc = "Allows the mob to change its appearance" set category = null - if(!check_rights(R_ADMIN)) + if(!check_rights(R_EVENT)) return if(!istype(H)) @@ -857,10 +825,10 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( switch(alert("Do you wish for [H] to be allowed to select non-whitelisted races?","Alter Mob Appearance","Yes","No","Cancel")) if("Yes") - admin_log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, without whitelisting of races.") + log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, without whitelisting of races.") H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 0) if("No") - admin_log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, with whitelisting of races.") + log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, with whitelisting of races.") H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 1) feedback_add_details("admin_verb","CMAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -980,8 +948,8 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list( else to_chat(usr, "You now won't get debug log messages") -/client/proc/man_up(mob/T as mob in GLOB.mob_list) - set category = "Admin" +/client/proc/man_up(mob/T as mob in GLOB.player_list) + set category = null set name = "Man Up" set desc = "Tells mob to man up and deal with it." diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 2122f88f4ed..699eca075bb 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -42,20 +42,6 @@ GLOBAL_DATUM_INIT(jobban_regex, /regex, regex("(\[\\S]+) - (\[^#]+\[^# ])(?: ## else return 0 -/* -DEBUG -/mob/verb/list_all_jobbans() - set name = "list all jobbans" - - for(var/s in jobban_keylist) - to_chat(world, s) - -/mob/verb/reload_jobbans() - set name = "reload jobbans" - - jobban_loadbanfile() -*/ - /proc/jobban_loadbanfile() if(config.ban_legacy_system) var/savefile/S=new("data/job_full.ban") diff --git a/code/modules/admin/machine_upgrade.dm b/code/modules/admin/machine_upgrade.dm index 1e569b51d2d..4f38f70613d 100644 --- a/code/modules/admin/machine_upgrade.dm +++ b/code/modules/admin/machine_upgrade.dm @@ -1,20 +1,20 @@ /proc/machine_upgrade(obj/machinery/M in world) set name = "Tweak Component Ratings" - set category = "Debug" + set category = null - if(!check_rights(R_DEBUG)) + if(!check_rights(R_DEBUG)) return - + if(!istype(M)) to_chat(usr, "This can only be used on subtypes of /obj/machinery.") return - + var/new_rating = input("Enter new rating:","Num") as num if(!isnull(new_rating) && M.component_parts) for(var/obj/item/stock_parts/P in M.component_parts) P.rating = new_rating M.RefreshParts() - + message_admins("[key_name_admin(usr)] has set the component rating of [M] to [new_rating]") log_admin("[key_name(usr)] has set the component rating of [M] to [new_rating]") diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 1c6bab4d3cd..700e8381695 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -65,7 +65,7 @@ } - function expand(id,job,name,real_name,image,key,ip,antagonist,mobUID,clientUID,eyeUID){ + function expand(id,job,name,real_name,image,key,ip,antagonist,mobUID,client_ckey,eyeUID){ clearAll(); @@ -83,7 +83,7 @@ body += "N - " body += "VV - " body += "TP - " - body += "PM - " + body += "PM - " body += "SM - " body += "FLW" if(eyeUID) @@ -297,7 +297,7 @@ var/mob/living/silicon/ai/A = M if(A.client && A.eyeobj) // No point following clientless AI eyes M_eyeUID = "[A.eyeobj.UID()]" - var/clientUID = M.client ? M.client.UID() : null + var/client_ckey = M.client ? M.client.ckey : null //output for each mob dat += {" @@ -305,7 +305,7 @@ [M_name] - [M_rname] - [M_key] ([M_job]) @@ -332,65 +332,6 @@ usr << browse(dat, "window=players;size=600x480") -//The old one -/datum/admins/proc/player_panel_old() - if(!usr.client.holder) - return - var/dat = "Player Menu" - dat += "" - //add to this if wanting to add back in IP checking - //add if you want to know their ip to the lists below - var/list/mobs = sortmobs() - - for(var/mob/M in mobs) - if(!M.ckey) continue - - dat += "" - if(isAI(M)) - dat += "" - else if(isrobot(M)) - dat += "" - else if(issmall(M)) - dat += "" - else if(ishuman(M)) - dat += "" - else if(istype(M, /mob/living/silicon/pai)) - dat += "" - else if(isnewplayer(M)) - dat += "" - else if(isobserver(M)) - dat += "" - else if(isalien(M)) - dat += "" - else - dat += "" - - - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(H.mind && H.mind.assigned_role) - dat += "" - else - dat += "" - - - dat += {" - - - "} - switch(is_special_character(M)) - if(0) - dat += {""} - if(1) - dat += {""} - if(2) - dat += {""} - - dat += "
    NameReal NameAssigned JobKeyOptionsPMTraitor?
    IP:(IP: [M.lastKnownIP])
    [M.name]AICyborgMonkey[M.real_name]pAINew PlayerGhostAlienUnknown[H.mind.assigned_role]NA[(M.client ? "[M.client]" : "No client")]XPMTraitor?Traitor?Traitor?
    " - - usr << browse(dat, "window=players;size=640x480") - - /datum/admins/proc/check_antagonists_line(mob/M, caption = "", close = 1) var/logout_status @@ -401,7 +342,7 @@ dname = M return {"[dname][caption][logout_status][istype(A, /area/security/permabrig) ? " (PERMA) " : ""][M.stat == 2 ? " (DEAD)" : ""] - PM [ADMIN_FLW(M, "FLW")] [close ? "" : ""]"} + PM [ADMIN_FLW(M, "FLW")] [close ? "" : ""]"} /datum/admins/proc/check_antagonists() if(!check_rights(R_ADMIN)) @@ -477,7 +418,7 @@ var/mob/M = blob.current if(M) dat += "[ADMIN_PP(M,"[M.real_name]")][M.client ? "" : " (ghost)"][M.stat == 2 ? " (DEAD)" : ""]" - dat += "PM" + dat += "PM" else dat += "Blob not found!" dat += "" diff --git a/code/modules/admin/sql_notes.dm b/code/modules/admin/sql_notes.dm index a438a43eaec..04e28737479 100644 --- a/code/modules/admin/sql_notes.dm +++ b/code/modules/admin/sql_notes.dm @@ -2,7 +2,8 @@ if(checkrights && !check_rights(R_ADMIN|R_MOD)) return if(!GLOB.dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection.") + if(usr) + to_chat(usr, "Failed to establish database connection.") return if(!target_ckey) @@ -19,7 +20,8 @@ log_game("SQL ERROR obtaining ckey from player table. Error : \[[err]\]\n") return if(!query_find_ckey.NextRow()) - to_chat(usr, "[target_ckey] has not been seen before, you can only add notes to known players.") + if(usr) + to_chat(usr, "[target_ckey] has not been seen before, you can only add notes to known players.") return var/exp_data = query_find_ckey.item[2] @@ -52,8 +54,8 @@ log_game("SQL ERROR adding new note to table. Error : \[[err]\]\n") return if(logged) - log_admin("[key_name(usr)] has added a note to [target_ckey]: [notetext]") - message_admins("[key_name_admin(usr)] has added a note to [target_ckey]:
    [notetext]") + log_admin("[usr ? key_name(usr) : adminckey] has added a note to [target_ckey]: [notetext]") + message_admins("[usr ? key_name_admin(usr) : adminckey] has added a note to [target_ckey]:
    [notetext]") show_note(target_ckey) /proc/remove_note(note_id) @@ -63,7 +65,8 @@ var/notetext var/adminckey if(!GLOB.dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection.") + if(usr) + to_chat(usr, "Failed to establish database connection.") return if(!note_id) return @@ -82,15 +85,16 @@ var/err = query_del_note.ErrorMsg() log_game("SQL ERROR removing note from table. Error : \[[err]\]\n") return - log_admin("[key_name(usr)] has removed a note made by [adminckey] from [ckey]: [notetext]") - message_admins("[key_name_admin(usr)] has removed a note made by [adminckey] from [ckey]:
    [notetext]") + log_admin("[usr ? key_name(usr) : "Bot"] has removed a note made by [adminckey] from [ckey]: [notetext]") + message_admins("[usr ? key_name_admin(usr) : "Bot"] has removed a note made by [adminckey] from [ckey]:
    [notetext]") show_note(ckey) /proc/edit_note(note_id) if(!check_rights(R_ADMIN|R_MOD)) return if(!GLOB.dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection.") + if(usr) + to_chat(usr, "Failed to establish database connection.") return if(!note_id) return @@ -117,8 +121,8 @@ var/err = query_update_note.ErrorMsg() log_game("SQL ERROR editing note. Error : \[[err]\]\n") return - log_admin("[key_name(usr)] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"") - message_admins("[key_name_admin(usr)] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"") + log_admin("[usr ? key_name(usr) : "Bot"] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"") + message_admins("[usr ? key_name_admin(usr) : "Bot"] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"") show_note(target_ckey) /proc/show_note(target_ckey, index, linkless = 0) diff --git a/code/modules/admin/tickets/adminticketsverbs.dm b/code/modules/admin/tickets/adminticketsverbs.dm index 406019745f8..4e8e79f2a26 100644 --- a/code/modules/admin/tickets/adminticketsverbs.dm +++ b/code/modules/admin/tickets/adminticketsverbs.dm @@ -5,7 +5,7 @@ set name = "Open Admin Ticket Interface" set category = "Admin" - if(!holder || !check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN)) return SStickets.showUI(usr) @@ -14,7 +14,7 @@ set name = "Resolve All Open Admin Tickets" set category = null - if(!holder || !check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN)) return if(alert("Are you sure you want to resolve ALL open admin tickets?","Resolve all open admin tickets?","Yes","No") != "Yes") diff --git a/code/modules/admin/tickets/mentorticketsverbs.dm b/code/modules/admin/tickets/mentorticketsverbs.dm index d65f4a30220..c7bec3e043d 100644 --- a/code/modules/admin/tickets/mentorticketsverbs.dm +++ b/code/modules/admin/tickets/mentorticketsverbs.dm @@ -5,7 +5,7 @@ set name = "Open Mentor Ticket Interface" set category = "Admin" - if(!holder || !check_rights(R_MENTOR|R_ADMIN)) + if(!check_rights(R_MENTOR|R_ADMIN)) return SSmentor_tickets.showUI(usr) @@ -14,7 +14,7 @@ set name = "Resolve All Open Mentor Tickets" set category = null - if(!holder || !check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN)) return if(alert("Are you sure you want to resolve ALL open mentor tickets?","Resolve all open mentor tickets?","Yes","No") != "Yes") diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 50fa99778e7..72893e18cf5 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -14,7 +14,7 @@ if(!check_rights(R_ADMIN|R_MOD)) return var/client/C = locateUID(href_list["rejectadminhelp"]) - if(!C) + if(!isclient(C)) return C << 'sound/effects/adminhelp.ogg' @@ -26,17 +26,16 @@ message_admins("[key_name_admin(usr)] rejected [key_name_admin(C.mob)]'s admin help") log_admin("[key_name(usr)] rejected [key_name(C.mob)]'s admin help") - if(href_list["openadminticket"]) - if(!check_rights(R_ADMIN)) - return - var/ticketID = text2num(href_list["openadminticket"]) - SStickets.showDetailUI(usr, ticketID) - - if(href_list["openmentorticket"]) - if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) - return - var/ticketID = text2num(href_list["openmentorticket"]) - SSmentor_tickets.showDetailUI(usr, ticketID) + if(href_list["openticket"]) + var/ticketID = text2num(href_list["openticket"]) + if(!href_list["is_mhelp"]) + if(!check_rights(R_ADMIN)) + return + SStickets.showDetailUI(usr, ticketID) + else + if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) + return + SSmentor_tickets.showDetailUI(usr, ticketID) if(href_list["stickyban"]) stickyban(href_list["stickyban"],href_list) @@ -114,39 +113,39 @@ switch(bantype) if(BANTYPE_PERMA) if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") + to_chat(usr, "Not enough parameters (Requires ckey and reason)") return banduration = null banjob = null if(BANTYPE_TEMP) if(!banckey || !banreason || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") + to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") return banjob = null if(BANTYPE_JOB_PERMA) if(!banckey || !banreason || !banjob) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") return banduration = null if(BANTYPE_JOB_TEMP) if(!banckey || !banreason || !banjob || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") return if(BANTYPE_APPEARANCE) if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") + to_chat(usr, "Not enough parameters (Requires ckey and reason)") return banduration = null banjob = null if(BANTYPE_ADMIN_PERMA) if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") + to_chat(usr, "Not enough parameters (Requires ckey and reason)") return banduration = null banjob = null if(BANTYPE_ADMIN_TEMP) if(!banckey || !banreason || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") + to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") return banjob = null @@ -324,8 +323,8 @@ if(!check_rights(R_SPAWN)) return var/mob/M = locateUID(href_list["mob"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return var/delmob = 0 @@ -436,18 +435,18 @@ if(!check_rights(R_BAN)) return var/mob/M = locateUID(href_list["appearanceban"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(!M.ckey) //sanity - to_chat(usr, "This mob has no ckey") + to_chat(usr, "This mob has no ckey") return var/ban_ckey_param = href_list["dbbanaddckey"] var/banreason = appearance_isbanned(M) if(banreason) /* if(!config.ban_legacy_system) - to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") + to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") DB_ban_panel(M.ckey) return */ switch(alert("Reason: '[banreason]' Remove appearance ban?","Please Confirm","Yes","No")) @@ -458,7 +457,7 @@ DB_ban_unban(M.ckey, BANTYPE_APPEARANCE) appearance_unban(M) message_admins("[key_name_admin(usr)] removed [key_name_admin(M)]'s appearance ban", 1) - to_chat(M, "[usr.client.ckey] has removed your appearance ban.") + to_chat(M, "[usr.client.ckey] has removed your appearance ban.") else switch(alert("Appearance ban [M.ckey]?",,"Yes","No", "Cancel")) if("Yes") @@ -473,7 +472,7 @@ appearance_fullban(M, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") add_note(M.ckey, "Appearance banned - [reason]", null, usr.ckey, 0) message_admins("[key_name_admin(usr)] appearance banned [key_name_admin(M)]", 1) - to_chat(M, "You have been appearance banned by [usr.client.ckey].") + to_chat(M, "You have been appearance banned by [usr.client.ckey].") to_chat(M, "The reason is: [reason]") to_chat(M, "Appearance ban can be lifted only upon request.") if(config.banappeals) @@ -487,15 +486,15 @@ // if(!check_rights(R_BAN)) return var/mob/M = locateUID(href_list["jobban2"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(!M.ckey) //sanity - to_chat(usr, "This mob has no ckey") + to_chat(usr, "This mob has no ckey") return if(!SSjobs) - to_chat(usr, "SSjobs has not been setup!") + to_chat(usr, "SSjobs has not been setup!") return var/dat = "" @@ -735,8 +734,8 @@ if(!check_rights(R_BAN)) return var/mob/M = locateUID(href_list["jobban4"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(M != usr) //we can jobban ourselves @@ -747,7 +746,7 @@ var/ban_ckey_param = href_list["dbbanaddckey"] if(!SSjobs) - to_chat(usr, "SSjobs has not been setup!") + to_chat(usr, "SSjobs has not been setup!") return //get jobs for department if specified, otherwise just returnt he one job in a list. @@ -840,7 +839,7 @@ msg += ", [job]" add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes", 1) - to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") + to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") to_chat(M, "The reason is: [reason]") to_chat(M, "This jobban will be lifted in [mins] minutes.") href_list["jobban2"] = 1 // lets it fall through and refresh @@ -861,7 +860,7 @@ else msg += ", [job]" add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg]", 1) - to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") + to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") to_chat(M, "The reason is: [reason]") to_chat(M, "Jobban can be lifted only upon request.") href_list["jobban2"] = 1 // lets it fall through and refresh @@ -873,7 +872,7 @@ //all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned) if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban. if(!config.ban_legacy_system) - to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") + to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") DB_ban_panel(M.ckey) return var/msg @@ -894,22 +893,33 @@ continue if(msg) message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg]", 1) - to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].") + to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].") href_list["jobban2"] = 1 // lets it fall through and refresh return 1 return 0 //we didn't do anything! else if(href_list["boot2"]) var/mob/M = locateUID(href_list["boot2"]) - if(ismob(M)) - if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) - to_chat(usr, "[key_name_admin(M)] cannot be kicked from the server.") + if(!ismob(M)) + return + var/client/C = M.client + if(C == null) + to_chat(usr, "Mob has no client to kick.") + return + if(alert("Kick [C.ckey]?",,"Yes","No") == "Yes") + if(C && C.holder && (C.holder.rights & R_BAN)) + to_chat(usr, "[key_name_admin(C)] cannot be kicked from the server.") return - to_chat(M, "You have been kicked from the server") - log_admin("[key_name(usr)] booted [key_name(M)].") - message_admins("[key_name_admin(usr)] booted [key_name_admin(M)].", 1) - //M.client = null - qdel(M.client) + to_chat(C, "You have been kicked from the server") + log_admin("[key_name(usr)] booted [key_name(C)].") + message_admins("[key_name_admin(usr)] booted [key_name_admin(C)].", 1) + //C = null + qdel(C) + + else if(href_list["open_logging_view"]) + var/mob/M = locateUID(href_list["open_logging_view"]) + if(ismob(M)) + usr.client.open_logging_view(list(M), TRUE) //Player Notes else if(href_list["addnote"]) @@ -981,7 +991,7 @@ if(!check_rights(R_BAN)) return var/mob/M = locateUID(href_list["newban"]) - if(!ismob(M)) + if(!istype(M, /mob)) return var/ban_ckey_param = href_list["dbbanaddckey"] @@ -997,7 +1007,7 @@ M = admin_ban_mobsearch(M, ban_ckey_param, usr) AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.") - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") feedback_inc("ban_tmp",1) DB_ban_record(BANTYPE_TEMP, M, mins, reason) @@ -1017,7 +1027,7 @@ if(!reason) return AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") to_chat(M, "This ban does not expire automatically and must be appealed.") if(M.client) M.client.link_forum_account(TRUE) @@ -1085,7 +1095,7 @@ return var/mob/M = locateUID(href_list["mute"]) - if(!ismob(M)) return + if(!istype(M, /mob)) return if(!M.client) return var/mute_type = href_list["mute_type"] @@ -1099,7 +1109,7 @@ if(SSticker && SSticker.mode) return alert(usr, "The game has already started.", null, null, null, null) - var/dat = {"What mode do you wish to play?
    "} + var/dat = {"What mode do you wish to play?
    "} for(var/mode in config.modes) dat += {"[config.mode_names[mode]]
    "} dat += {"Secret
    "} @@ -1114,7 +1124,7 @@ return alert(usr, "The game has already started.", null, null, null, null) if(GLOB.master_mode != "secret") return alert(usr, "The game mode has to be secret!", null, null, null, null) - var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
    "} + var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
    "} for(var/mode in config.modes) dat += {"[config.mode_names[mode]]
    "} dat += {"Random (default)
    "} @@ -1152,7 +1162,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["monkeyone"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make monkey?",, "Yes", "No") != "Yes") return @@ -1167,7 +1177,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["corgione"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make corgi?",, "Yes", "No") != "Yes") @@ -1182,7 +1192,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makePAI"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make pai?",, "Yes", "No") != "Yes") return @@ -1205,8 +1215,9 @@ if(!check_rights(R_SERVER|R_EVENT)) return var/mob/M = locateUID(href_list["forcespeech"]) - if(!ismob(M)) - to_chat(usr, "this can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return var/speech = input("What will [key_name(M)] say?.", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins. if(!speech) return @@ -1222,11 +1233,11 @@ return var/mob/M = locateUID(href_list["sendtoprison"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return var/turf/prison_cell = pick(GLOB.prisonwarp) @@ -1285,6 +1296,71 @@ NP.ckey = M.ckey qdel(M) + else if(href_list["eraseflavortext"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["eraseflavortext"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(!M.client) + to_chat(usr, "[M] doesn't seem to have an active client.") + return + + if(M.flavor_text == "" && M.client.prefs.flavor_text == "") + to_chat(usr, "[M] has no flavor text set.") + return + + if(alert(usr, "Erase [key_name(M)]'s flavor text?", "Message", "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] has erased [key_name(M)]'s flavor text.") + message_admins("[key_name_admin(usr)] has erased [key_name_admin(M)]'s flavor text.") + + // Clears the mob's flavor text + M.flavor_text = "" + + // Clear and save the DB character's flavor text + M.client.prefs.flavor_text = "" + M.client.prefs.save_character(M.client) + + else if(href_list["userandomname"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["userandomname"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(!M.client) + to_chat(usr, "[M] doesn't seem to have an active client.") + return + + if(alert(usr, "Force [key_name(M)] to use a random name?", "Message", "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] has forced [key_name(M)] to use a random name.") + message_admins("[key_name_admin(usr)] has forced [key_name_admin(M)] to use a random name.") + + // Update the mob's name with a random one straight away + var/random_name = random_name(M.client.prefs.gender, M.client.prefs.species) + M.rename_character(M.real_name, random_name) + + // Save that random name for next rounds + M.client.prefs.real_name = random_name + M.client.prefs.save_character(M.client) + + else if(href_list["asays"]) + if(!check_rights(R_ADMIN)) + return + + usr.client.view_asays() + else if(href_list["tdome1"]) if(!check_rights(R_SERVER|R_EVENT)) return @@ -1292,11 +1368,11 @@ return var/mob/M = locateUID(href_list["tdome1"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return for(var/obj/item/I in M) @@ -1322,11 +1398,11 @@ return var/mob/M = locateUID(href_list["tdome2"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return for(var/obj/item/I in M) @@ -1352,11 +1428,11 @@ return var/mob/M = locateUID(href_list["tdomeadmin"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return M.Paralyse(5) @@ -1374,11 +1450,11 @@ return var/mob/M = locateUID(href_list["tdomeobserve"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return for(var/obj/item/I in M) @@ -1408,11 +1484,11 @@ return var/mob/M = locateUID(href_list["aroomwarp"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") return M.Paralyse(5) @@ -1429,7 +1505,7 @@ var/mob/living/L = locateUID(href_list["revive"]) if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /mob/living") + to_chat(usr, "This can only be used on instances of type /mob/living") return L.revive() @@ -1441,7 +1517,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makeai"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make ai?",, "Yes", "No") != "Yes") @@ -1457,7 +1533,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makealien"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make alien?",, "Yes", "No") != "Yes") return @@ -1469,7 +1545,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makeslime"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make slime?",, "Yes", "No") != "Yes") return @@ -1481,7 +1557,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makesuper"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make superhero?",, "Yes", "No") != "Yes") @@ -1494,7 +1570,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["makerobot"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(alert(usr, "Confirm make robot?",, "Yes", "No") != "Yes") return @@ -1506,7 +1582,7 @@ var/mob/M = locateUID(href_list["makeanimal"]) if(isnewplayer(M)) - to_chat(usr, "This cannot be used on instances of type /mob/new_player") + to_chat(usr, "This cannot be used on instances of type /mob/new_player") return if(alert(usr, "Confirm make animal?",, "Yes", "No") != "Yes") return @@ -1519,10 +1595,14 @@ var/mob/dead/observer/G = locateUID(href_list["incarn_ghost"]) if(!istype(G)) - to_chat(usr, "This will only work on /mob/dead/observer") + to_chat(usr, "This will only work on /mob/dead/observer") + return var/posttransformoutfit = usr.client.robust_dress_shop() + if(!posttransformoutfit) + return + var/mob/living/carbon/human/H = G.incarnate_ghost() if(posttransformoutfit && istype(H)) @@ -1536,7 +1616,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["togmutate"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return var/block=text2num(href_list["block"]) //testing("togmutate([href_list["block"]] -> [block])") @@ -1546,6 +1626,11 @@ else if(href_list["adminplayeropts"]) var/mob/M = locateUID(href_list["adminplayeropts"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + show_player_panel(M) else if(href_list["adminplayerobservefollow"]) @@ -1555,6 +1640,11 @@ return C.admin_ghost() var/mob/M = locateUID(href_list["adminplayerobservefollow"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + var/mob/dead/observer/A = C.mob sleep(2) A.ManualFollow(M) @@ -1578,11 +1668,20 @@ SStickets.resolveTicket(index) else if(href_list["autorespond"]) + if(href_list["is_mhelp"]) + to_chat(usr, "Auto responses are not available for mentor helps.") + return var/index = text2num(href_list["autorespond"]) if(!check_rights(R_ADMIN|R_MOD)) return SStickets.autoRespond(index) + if(href_list["convert_ticket"]) + var/indexNum = text2num(href_list["convert_ticket"]) + if(href_list["is_mhelp"]) + SSmentor_tickets.convert_to_other_ticket(indexNum) + else + SStickets.convert_to_other_ticket(indexNum) else if(href_list["cult_nextobj"]) if(alert(usr, "Validate the current Cult objective and unlock the next one?", "Cult Cheat Code", "Yes", "No") != "Yes") return @@ -1603,10 +1702,10 @@ for(var/datum/mind/H in SSticker.mode.cult) if (H.current) - to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [input]") + to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [input]") for(var/mob/dead/observer/O in GLOB.player_list) - to_chat(O, "[SSticker.cultdat.entity_name] murmurs, [input]") + to_chat(O, "[SSticker.cultdat.entity_name] murmurs, [input]") message_admins("Admin [key_name_admin(usr)] has talked with the Voice of [SSticker.cultdat.entity_name].") log_admin("[key_name(usr)] Voice of [SSticker.cultdat.entity_name]: [input]") @@ -1628,6 +1727,11 @@ else if(href_list["adminmoreinfo"]) var/mob/M = locateUID(href_list["adminmoreinfo"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + admin_mob_info(M) else if(href_list["adminspawncookie"]) @@ -1635,7 +1739,7 @@ var/mob/living/carbon/human/H = locateUID(href_list["adminspawncookie"]) if(!ishuman(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return H.equip_to_slot_or_del( new /obj/item/reagent_containers/food/snacks/cookie(H), slot_l_hand ) @@ -1659,7 +1763,7 @@ var/mob/living/M = locateUID(href_list["BlueSpaceArtillery"]) if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") + to_chat(usr, "This can only be used on instances of type /mob/living") return if(alert(owner, "Are you sure you wish to hit [key_name(M)] with Bluespace Artillery?", "Confirm Firing?" , "Yes" , "No") != "Yes") @@ -1697,6 +1801,11 @@ return var/mob/M = locateUID(href_list["CentcommReply"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + usr.client.admin_headset_message(M, "Centcomm") else if(href_list["SyndicateReply"]) @@ -1704,6 +1813,11 @@ return var/mob/M = locateUID(href_list["SyndicateReply"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + usr.client.admin_headset_message(M, "Syndicate") else if(href_list["HeadsetMessage"]) @@ -1711,6 +1825,11 @@ return var/mob/M = locateUID(href_list["HeadsetMessage"]) + + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return + usr.client.admin_headset_message(M) else if(href_list["EvilFax"]) @@ -1718,7 +1837,7 @@ return var/mob/living/carbon/human/H = locateUID(href_list["EvilFax"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return var/etypes = list("Borgification", "Corgification", "Death By Fire", "Total Brain Death", "Honk Tumor", "Cluwne", "Demote", "Demote with Bot", "Revoke Fax Access", "Angry Fax Machine") var/eviltype = input(src.owner, "Which type of evil fax do you wish to send [H]?","Its good to be baaaad...", "") as null|anything in etypes @@ -1752,12 +1871,12 @@ P.ico = new P.ico += "paper_stamp-[stampvalue]" P.overlays += stampoverlay - P.stamps += "
    " + P.stamps += "
    " P.update_icon() P.faxmachineid = fax.UID() P.loc = fax.loc // Do not use fax.receivefax(P) here, as it won't preserve the type. Physically teleporting the fax paper is required. if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") + to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") to_chat(src.owner, "You sent a [eviltype] fax to [H]") log_admin("[key_name(src.owner)] sent [key_name(H)] a [eviltype] fax") message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a [eviltype] fax") @@ -1766,7 +1885,7 @@ return var/mob/living/M = locateUID(href_list["Bless"]) if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") + to_chat(usr, "This can only be used on instances of type /mob/living") return var/btypes = list("To Arrivals", "Moderate Heal") var/mob/living/carbon/human/H @@ -1833,7 +1952,7 @@ var/petchoice = input("Select pet type", "Pets") as null|anything in pets if(isnull(petchoice)) return - var/list/mob/dead/observer/candidates = pollCandidates("Play as the special event pet [H]?", poll_time = 200, min_hours = 10) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Play as the special event pet [H]?", poll_time = 20 SECONDS, min_hours = 10, source = petchoice) var/mob/dead/observer/theghost = null if(candidates.len) var/mob/living/simple_animal/pet/P = new petchoice(H.loc) @@ -1886,7 +2005,7 @@ var/mob/living/M = locateUID(href_list["Smite"]) var/mob/living/carbon/human/H if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") + to_chat(usr, "This can only be used on instances of type /mob/living") return var/ptypes = list("Lightning bolt", "Fire Death", "Gib") if(ishuman(M)) @@ -1997,7 +2116,7 @@ var/datum/antagonist/traitor/T = new() T.give_objectives = FALSE to_chat(newtraitormind.current, "ATTENTION: It is time to pay your debt to the Syndicate...") - to_chat(newtraitormind.current, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]") + to_chat(newtraitormind.current, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]") newtraitormind.add_antag_datum(T) else to_chat(usr, "ERROR: Unable to find any valid candidate to send after [H].") @@ -2026,10 +2145,10 @@ return var/mob/living/carbon/human/H = locateUID(href_list["cryossd"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(!href_list["cryoafk"] && !isLivingSSD(H)) - to_chat(usr, "This can only be used on living, SSD players.") + to_chat(usr, "This can only be used on living, SSD players.") return if(istype(H.loc, /obj/machinery/cryopod)) var/obj/machinery/cryopod/P = H.loc @@ -2049,28 +2168,28 @@ return var/mob/living/carbon/human/H = locateUID(href_list["FaxReplyTemplate"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return var/obj/item/paper/P = new /obj/item/paper(null) var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) P.name = "Central Command - paper" var/stypes = list("Handle it yourselves!","Illegible fax","Fax not signed","Not Right Now","You are wasting our time", "Keep up the good work", "ERT Instructions") var/stype = input(src.owner, "Which type of standard reply do you wish to send to [H]?","Choose your paperwork", "") as null|anything in stypes - var/tmsg = "



    Nanotrasen Science Station [GLOB.using_map.station_short]


    NAS Trurl Communications Department Report


    " + var/tmsg = "



    Nanotrasen Science Station [GLOB.using_map.station_short]


    NAS Trurl Communications Department Report


    " if(stype == "Handle it yourselves!") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please proceed in accordance with Standard Operating Procedure and/or Space Law. You are fully trained to handle this situation without Central Command intervention.

    This is an automatic message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please proceed in accordance with Standard Operating Procedure and/or Space Law. You are fully trained to handle this situation without Central Command intervention.

    This is an automatic message." else if(stype == "Illegible fax") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax's grammar, syntax and/or typography are of a sub-par level and do not allow us to understand the contents of the message.

    Please consult your nearest dictionary and/or thesaurus and try again.

    This is an automatic message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax's grammar, syntax and/or typography are of a sub-par level and do not allow us to understand the contents of the message.

    Please consult your nearest dictionary and/or thesaurus and try again.

    This is an automatic message." else if(stype == "Fax not signed") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax has not been correctly signed and, as such, we cannot verify your identity.

    Please sign your faxes before sending them so that we may verify your identity.

    This is an automatic message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax has not been correctly signed and, as such, we cannot verify your identity.

    Please sign your faxes before sending them so that we may verify your identity.

    This is an automatic message." else if(stype == "Not Right Now") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Due to pressing concerns of a matter above your current paygrade, we are unable to provide assistance in whatever matter your fax referenced.

    This can be either due to a power outage, bureaucratic audit, pest infestation, Ascendance Event, corgi outbreak, or any other situation that would affect the proper functioning of the NAS Trurl.

    Please try again later.

    This is an automatic message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Due to pressing concerns of a matter above your current paygrade, we are unable to provide assistance in whatever matter your fax referenced.

    This can be either due to a power outage, bureaucratic audit, pest infestation, Ascendance Event, corgi outbreak, or any other situation that would affect the proper functioning of the NAS Trurl.

    Please try again later.

    This is an automatic message." else if(stype == "You are wasting our time") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    In the interest of preventing further mismanagement of company resources, please avoid wasting our time with such petty drivel.

    Do kindly remember that we expect our workforce to maintain at least a semi-decent level of profesionalism. Do not test our patience.

    This is an automatic message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    In the interest of preventing further mismanagement of company resources, please avoid wasting our time with such petty drivel.

    Do kindly remember that we expect our workforce to maintain at least a semi-decent level of profesionalism. Do not test our patience.

    This is an automatic message." else if(stype == "Keep up the good work") tmsg += "Greetings, esteemed crewmember. Your fax has been received successfully by NAS Trurl Fax Registration.

    We at the NAS Trurl appreciate the good work that you have done here, and sincerely recommend that you continue such a display of dedication to the company.

    This is absolutely not an automated message." else if(stype == "ERT Instructions") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please utilize the Card Swipers if you wish to call for an ERT.

    This is an automated message." + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please utilize the Card Swipers if you wish to call for an ERT.

    This is an automated message." else return tmsg += "
    " @@ -2091,11 +2210,11 @@ P.ico = new P.ico += "paper_stamp-[stampvalue]" P.overlays += stampoverlay - P.stamps += "
    " + P.stamps += "
    " P.update_icon() fax.receivefax(P) if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") + to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") to_chat(src.owner, "You sent a standard '[stype]' fax to [H]") log_admin("[key_name(src.owner)] sent [key_name(H)] a standard '[stype]' fax") message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a standard '[stype]' fax") @@ -2103,10 +2222,10 @@ else if(href_list["HONKReply"]) var/mob/living/carbon/human/H = locateUID(href_list["HONKReply"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset") + to_chat(usr, "The person you are trying to contact is not wearing a headset") return var/input = input(src.owner, "Please enter a message to reply to [key_name(H)] via [H.p_their()] headset.","Outgoing message from HONKplanet", "") @@ -2123,13 +2242,13 @@ if(alert(src.owner, "Accept or Deny ERT request?", "CentComm Response", "Accept", "Deny") == "Deny") var/mob/living/carbon/human/H = locateUID(href_list["ErtReply"]) if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") return if(H.stat != 0) - to_chat(usr, "The person you are trying to contact is not conscious.") + to_chat(usr, "The person you are trying to contact is not conscious.") return if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset") + to_chat(usr, "The person you are trying to contact is not wearing a headset") return var/input = input(src.owner, "Please enter a reason for denying [key_name(H)]'s ERT request.","Outgoing message from CentComm", "") @@ -2137,7 +2256,7 @@ GLOB.ert_request_answered = TRUE to_chat(src.owner, "You sent [input] to [H] via a secure channel.") log_admin("[src.owner] denied [key_name(H)]'s ERT request with the message [input].") - to_chat(H, "Incoming priority transmission from Central Command. Message as follows, Your ERT request has been denied for the following reasons: [input].") + to_chat(H, "Incoming priority transmission from Central Command. Message as follows, Your ERT request has been denied for the following reasons: [input].") else src.owner.response_team() @@ -2292,14 +2411,14 @@ P.stamped = new P.stamped += /obj/item/stamp/centcom P.overlays += stampoverlay - P.stamps += "
    " + P.stamps += "
    " else if(stamptype == "text") if(!P.stamped) P.stamped = new P.stamped += /obj/item/stamp P.overlays += stampoverlay - P.stamps += "
    [stampvalue]" + P.stamps += "
    [stampvalue]" if(destination != "All Departments") if(!fax.receivefax(P)) @@ -2329,7 +2448,7 @@ if(notify == "Yes") var/mob/living/carbon/human/H = sender if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(sender, "Your headset pings, notifying you that a reply to your fax has arrived.") + to_chat(sender, "Your headset pings, notifying you that a reply to your fax has arrived.") if(sender) log_admin("[key_name(src.owner)] replied to a fax message from [key_name(sender)]: [input]") message_admins("[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(sender)] (VIEW).", 1) @@ -2348,8 +2467,8 @@ if(!check_rights(R_ADMIN)) return var/mob/M = locateUID(href_list["getplaytimewindow"]) - if(!M) - to_chat(usr, "ERROR: Mob not found.") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return cmd_mentor_show_exp_panel(M.client) @@ -2357,6 +2476,9 @@ if(!check_rights(R_ADMIN)) return var/mob/M = locateUID(href_list["jumpto"]) + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return usr.client.jumptomob(M) else if(href_list["getmob"]) @@ -2364,24 +2486,37 @@ if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") return var/mob/M = locateUID(href_list["getmob"]) + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return usr.client.Getmob(M) else if(href_list["sendmob"]) if(!check_rights(R_ADMIN)) return var/mob/M = locateUID(href_list["sendmob"]) + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return usr.client.sendmob(M) else if(href_list["narrateto"]) if(!check_rights(R_ADMIN)) return var/mob/M = locateUID(href_list["narrateto"]) + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return usr.client.cmd_admin_direct_narrate(M) else if(href_list["subtlemessage"]) - if(!check_rights(R_ADMIN)) return + if(!check_rights(R_EVENT)) + return var/mob/M = locateUID(href_list["subtlemessage"]) + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") + return usr.client.cmd_admin_subtle_message(M) else if(href_list["traitor"]) @@ -2392,8 +2527,8 @@ return var/mob/M = locateUID(href_list["traitor"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") + if(!istype(M, /mob)) + to_chat(usr, "This can only be used on instances of type /mob") return show_traitor_panel(M) @@ -2462,7 +2597,7 @@ switch(where) if("inhand") if(!iscarbon(usr) && !isrobot(usr)) - to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.") + to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.") where = "onfloor" target = usr @@ -2474,10 +2609,10 @@ target = locate(loc.x + X,loc.y + Y,loc.z + Z) if("inmarked") if(!marked_datum) - to_chat(usr, "You don't have any object marked. Abandoning spawn.") + to_chat(usr, "You don't have any object marked. Abandoning spawn.") return else if(!istype(marked_datum,/atom)) - to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") + to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") return else target = marked_datum @@ -2542,7 +2677,7 @@ message_admins("[key_name_admin(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") log_admin("[key_name(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") else - to_chat(usr, "You may only use this when the game is running.") + to_chat(usr, "You may only use this when the game is running.") else if(href_list["memoeditlist"]) if(!check_rights(R_SERVER)) return @@ -2583,14 +2718,16 @@ if("monkey") feedback_inc("admin_secrets_fun_used",1) feedback_add_details("admin_secrets_fun_used","M") - for(var/mob/living/carbon/human/H in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing spawn(0) H.monkeyize() ok = 1 if("corgi") feedback_inc("admin_secrets_fun_used",1) feedback_add_details("admin_secrets_fun_used","M") - for(var/mob/living/carbon/human/H in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing spawn(0) H.corgize() ok = 1 @@ -2620,7 +2757,7 @@ feedback_add_details("admin_secrets_fun_used","TriAI") if("gravity") if(!(SSticker && SSticker.mode)) - to_chat(usr, "Please wait until the game starts! Not sure how it will work otherwise.") + to_chat(usr, "Please wait until the game starts! Not sure how it will work otherwise.") return GLOB.gravity_is_on = !GLOB.gravity_is_on for(var/area/A in world) @@ -2661,7 +2798,8 @@ feedback_inc("admin_secrets_fun_used",1) feedback_add_details("admin_secrets_fun_used","PW") message_admins("[key_name_admin(usr)] teleported all players to the prison station.", 1) - for(var/mob/living/carbon/human/H in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing var/turf/loc = find_loc(H) var/security = 0 if(!is_station_level(loc.z) || GLOB.prisonwarped.Find(H)) @@ -2956,7 +3094,7 @@ if(usr) log_admin("[key_name(usr)] used secret [href_list["secretsfun"]]") if(ok) - to_chat(world, text("A secret has been activated by []!", usr.key)) + to_chat(world, text("A secret has been activated by []!", usr.key)) else if(href_list["secretsadmin"]) if(!check_rights(R_ADMIN)) return @@ -2964,17 +3102,17 @@ var/ok = 0 switch(href_list["secretsadmin"]) if("list_signalers") - var/dat = "Showing last [length(GLOB.lastsignalers)] signalers.
    " + var/dat = "Showing last [length(GLOB.lastsignalers)] signalers.
    " for(var/sig in GLOB.lastsignalers) dat += "[sig]
    " usr << browse(dat, "window=lastsignalers;size=800x500") if("list_lawchanges") - var/dat = "Showing last [length(GLOB.lawchanges)] law changes.
    " + var/dat = "Showing last [length(GLOB.lawchanges)] law changes.
    " for(var/sig in GLOB.lawchanges) dat += "[sig]
    " usr << browse(dat, "window=lawchanges;size=800x500") if("list_job_debug") - var/dat = "Job Debug info.
    " + var/dat = "Job Debug info.
    " if(SSjobs) for(var/line in SSjobs.job_debug) dat += "[line]
    " @@ -2992,9 +3130,10 @@ alert("The game mode is [SSticker.mode.name]") else alert("For some reason there's a ticker, but not a game mode") if("manifest") - var/dat = "Showing Crew Manifest.
    " + var/dat = "Showing Crew Manifest.
    " dat += "" - for(var/mob/living/carbon/human/H in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing if(H.ckey) dat += text("", H.name, H.get_assignment()) dat += "
    NamePosition
    [][]
    " @@ -3002,17 +3141,19 @@ if("check_antagonist") check_antagonists() if("DNA") - var/dat = "Showing DNA from blood.
    " + var/dat = "Showing DNA from blood.
    " dat += "" - for(var/mob/living/carbon/human/H in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing if(H.dna && H.ckey) dat += "" dat += "
    NameDNABlood Type
    [H][H.dna.unique_enzymes][H.dna.blood_type]
    " usr << browse(dat, "window=DNA;size=440x410") if("fingerprints") - var/dat = "Showing Fingerprints.
    " + var/dat = "Showing Fingerprints.
    " dat += "" - for(var/mob/living/carbon/human/H in GLOB.mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing if(H.ckey) if(H.dna && H.dna.uni_identity) dat += "" @@ -3044,14 +3185,14 @@ if(usr) log_admin("[key_name(usr)] used secret [href_list["secretsadmin"]]") if(ok) - to_chat(world, text("A secret has been activated by []!", usr.key)) + to_chat(world, text("A secret has been activated by []!", usr.key)) else if(href_list["secretscoder"]) if(!check_rights(R_DEBUG)) return switch(href_list["secretscoder"]) if("spawn_objects") - var/dat = "Admin Log
    " + var/dat = "Admin Log
    " for(var/l in GLOB.admin_log) dat += "
  • [l]
  • " if(!GLOB.admin_log.len) @@ -3221,27 +3362,27 @@ else if(href_list["ac_censor_channel_author"]) var/datum/feed_channel/FC = locate(href_list["ac_censor_channel_author"]) - if(FC.author != "\[REDACTED\]") + if(FC.author != "\[REDACTED\]") FC.backup_author = FC.author - FC.author = "\[REDACTED\]" + FC.author = "\[REDACTED\]" else FC.author = FC.backup_author src.access_news_network() else if(href_list["ac_censor_channel_story_author"]) var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"]) - if(MSG.author != "\[REDACTED\]") + if(MSG.author != "\[REDACTED\]") MSG.backup_author = MSG.author - MSG.author = "\[REDACTED\]" + MSG.author = "\[REDACTED\]" else MSG.author = MSG.backup_author src.access_news_network() else if(href_list["ac_censor_channel_story_body"]) var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"]) - if(MSG.body != "\[REDACTED\]") + if(MSG.body != "\[REDACTED\]") MSG.backup_body = MSG.body - MSG.body = "\[REDACTED\]" + MSG.body = "\[REDACTED\]" else MSG.body = MSG.backup_body src.access_news_network() @@ -3437,14 +3578,15 @@ return var/datum/outfit/O = hunter_outfits[dresscode] message_admins("[key_name_admin(mob)] is sending a ([dresscode]) to [killthem ? "assassinate" : "protect"] [key_name_admin(H)]...") - var/list/candidates = pollCandidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, 1) + var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_traitor") + var/list/candidates = SSghost_spawns.poll_candidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, TRUE, source = source, role_cleanname = "[killthem ? "murderous" : "protective"] [dresscode]") if(!candidates.len) - to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.") + to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.") return var/mob/C = pick(candidates) var/key_of_hunter = C.key if(!key_of_hunter) - to_chat(usr, "ERROR: Could not create eventmob. Could not pick key.") + to_chat(usr, "ERROR: Could not create eventmob. Could not pick key.") return var/datum/mind/hunter_mind = new /datum/mind(key_of_hunter) hunter_mind.active = 1 @@ -3475,9 +3617,9 @@ hunter_mind.objectives += protect_objective SSticker.mode.traitors |= hunter_mob.mind to_chat(hunter_mob, "ATTENTION: You are now on a mission!") - to_chat(hunter_mob, "Goal: [killthem ? "MURDER" : "PROTECT"] [H.real_name], currently in [get_area(H.loc)]. ") + to_chat(hunter_mob, "Goal: [killthem ? "MURDER" : "PROTECT"] [H.real_name], currently in [get_area(H.loc)]."); if(killthem) - to_chat(hunter_mob, "If you kill [H.p_them()], [H.p_they()] cannot be revived.") + to_chat(hunter_mob, "If you kill [H.p_them()], [H.p_they()] cannot be revived."); hunter_mob.mind.special_role = SPECIAL_ROLE_TRAITOR var/datum/atom_hud/antag/tatorhud = GLOB.huds[ANTAG_HUD_TRAITOR] tatorhud.join_hud(hunter_mob) diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm index 56fcab49293..50c852af03b 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm @@ -89,403 +89,443 @@ /datum/SDQL_parser/proc/tokenl(i) return lowertext(token(i)) -/datum/SDQL_parser/proc - //query: select_query | delete_query | update_query - query(i, list/node) - query_type = tokenl(i) +/datum/SDQL_parser/proc/query(i, list/node) + query_type = tokenl(i) - switch(query_type) - if("select") - select_query(i, node) + switch(query_type) + if("select") + select_query(i, node) - if("delete") - delete_query(i, node) + if("delete") + delete_query(i, node) - if("update") - update_query(i, node) + if("update") + update_query(i, node) - if("call") - call_query(i, node) + if("call") + call_query(i, node) - if("explain") - node += "explain" - node["explain"] = list() - query(i + 1, node["explain"]) + if("explain") + node += "explain" + node["explain"] = list() + query(i + 1, node["explain"]) // select_query: 'SELECT' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression] - select_query(i, list/node) - var/list/select = list() - i = select_list(i + 1, select) +/datum/SDQL_parser/proc/select_query(i, list/node) + var/list/select = list() + i = select_list(i + 1, select) - node += "select" - node["select"] = select + node += "select" + node["select"] = select - var/list/from = list() - if(tokenl(i) in list("from", "in")) - i = from_list(i + 1, from) - else - from += "world" + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" - node += "from" - node["from"] = from + node += "from" + node["from"] = from - if(tokenl(i) == "where") - var/list/where = list() - i = bool_expression(i + 1, where) + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) - node += "where" - node["where"] = where + node += "where" + node["where"] = where - return i + return i //delete_query: 'DELETE' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression] - delete_query(i, list/node) - var/list/select = list() - i = select_list(i + 1, select) +/datum/SDQL_parser/proc/delete_query(i, list/node) + var/list/select = list() + i = select_list(i + 1, select) - node += "delete" - node["delete"] = select + node += "delete" + node["delete"] = select - var/list/from = list() - if(tokenl(i) in list("from", "in")) - i = from_list(i + 1, from) - else - from += "world" + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" - node += "from" - node["from"] = from + node += "from" + node["from"] = from - if(tokenl(i) == "where") - var/list/where = list() - i = bool_expression(i + 1, where) + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) - node += "where" - node["where"] = where + node += "where" + node["where"] = where - return i + return i //update_query: 'UPDATE' select_list [('FROM' | 'IN') from_list] 'SET' assignments ['WHERE' bool_expression] - update_query(i, list/node) - var/list/select = list() - i = select_list(i + 1, select) +/datum/SDQL_parser/proc/update_query(i, list/node) + var/list/select = list() + i = select_list(i + 1, select) - node += "update" - node["update"] = select + node += "update" + node["update"] = select - var/list/from = list() - if(tokenl(i) in list("from", "in")) - i = from_list(i + 1, from) - else - from += "world" + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" - node += "from" - node["from"] = from + node += "from" + node["from"] = from - if(tokenl(i) != "set") - i = parse_error("UPDATE has misplaced SET") + if(tokenl(i) != "set") + i = parse_error("UPDATE has misplaced SET") - var/list/set_assignments = list() - i = assignments(i + 1, set_assignments) + var/list/set_assignments = list() + i = assignments(i + 1, set_assignments) - node += "set" - node["set"] = set_assignments + node += "set" + node["set"] = set_assignments - if(tokenl(i) == "where") - var/list/where = list() - i = bool_expression(i + 1, where) + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) - node += "where" - node["where"] = where + node += "where" + node["where"] = where - return i + return i //call_query: 'CALL' call_function ['ON' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]] - call_query(i, list/node) - var/list/func = list() - i = variable(i + 1, func) +/datum/SDQL_parser/proc/call_query(i, list/node) + var/list/func = list() + i = variable(i + 1, func) - node += "call" - node["call"] = func - - if(tokenl(i) != "on") - return i - - var/list/select = list() - i = select_list(i + 1, select) - - node += "on" - node["on"] = select - - var/list/from = list() - if(tokenl(i) in list("from", "in")) - i = from_list(i + 1, from) - else - from += "world" - - node += "from" - node["from"] = from - - if(tokenl(i) == "where") - var/list/where = list() - i = bool_expression(i + 1, where) - - node += "where" - node["where"] = where + node += "call" + node["call"] = func + if(tokenl(i) != "on") return i + var/list/select = list() + i = select_list(i + 1, select) + + node += "on" + node["on"] = select + + var/list/from = list() + if(tokenl(i) in list("from", "in")) + i = from_list(i + 1, from) + else + from += "world" + + node += "from" + node["from"] = from + + if(tokenl(i) == "where") + var/list/where = list() + i = bool_expression(i + 1, where) + + node += "where" + node["where"] = where + + return i + //select_list: select_item [',' select_list] - select_list(i, list/node) - i = select_item(i, node) +/datum/SDQL_parser/proc/select_list(i, list/node) + i = select_item(i, node) - if(token(i) == ",") - i = select_list(i + 1, node) + if(token(i) == ",") + i = select_list(i + 1, node) - return i + return i //from_list: from_item [',' from_list] - from_list(i, list/node) - i = from_item(i, node) +/datum/SDQL_parser/proc/from_list(i, list/node) + i = from_item(i, node) - if(token(i) == ",") - i = from_list(i + 1, node) + if(token(i) == ",") + i = from_list(i + 1, node) - return i + return i //assignments: assignment, [',' assignments] - assignments(i, list/node) - i = assignment(i, node) +/datum/SDQL_parser/proc/assignments(i, list/node) + i = assignment(i, node) - if(token(i) == ",") - i = assignments(i + 1, node) + if(token(i) == ",") + i = assignments(i + 1, node) - return i + return i //select_item: '*' | select_function | object_type - select_item(i, list/node) +/datum/SDQL_parser/proc/select_item(i, list/node) + if(token(i) == "*") + node += "*" + i++ - if(token(i) == "*") - node += "*" - i++ + else if(tokenl(i) in select_functions) + i = select_function(i, node) - else if(tokenl(i) in select_functions) - i = select_function(i, node) + else + i = object_type(i, node) - else - i = object_type(i, node) - - return i + return i //from_item: 'world' | object_type - from_item(i, list/node) +/datum/SDQL_parser/proc/from_item(i, list/node) - if(token(i) == "world") - node += "world" - i++ + if(token(i) == "world") + node += "world" + i++ - else - i = object_type(i, node) + else + i = object_type(i, node) - return i + return i //bool_expression: expression [bool_operator bool_expression] - bool_expression(i, list/node) +/datum/SDQL_parser/proc/bool_expression(i, list/node) + var/list/bool = list() + i = expression(i, bool) - var/list/bool = list() - i = expression(i, bool) + node[++node.len] = bool - node[++node.len] = bool + if(tokenl(i) in boolean_operators) + i = bool_operator(i, node) + i = bool_expression(i, node) - if(tokenl(i) in boolean_operators) - i = bool_operator(i, node) - i = bool_expression(i, node) - - return i + return i //assignment: '=' expression - assignment(var/i, var/list/node, var/list/assignment_list = list()) - assignment_list += token(i) +/datum/SDQL_parser/proc/assignment(var/i, var/list/node, var/list/assignment_list = list()) + assignment_list += token(i) - if(token(i + 1) == ".") - i = assignment(i + 2, node, assignment_list) + if(token(i + 1) == ".") + i = assignment(i + 2, node, assignment_list) - else if(token(i + 1) == "=") - var/exp_list = list() - node[assignment_list] = exp_list + else if(token(i + 1) == "=") + var/exp_list = list() + node[assignment_list] = exp_list - i = expression(i + 2, exp_list) + i = expression(i + 2, exp_list) - else - parse_error("Assignment expected, but no = found") + else + parse_error("Assignment expected, but no = found") - return i + return i //variable: | '.' variable - variable(i, list/node) - var/list/L = list(token(i)) - node[++node.len] = L +/datum/SDQL_parser/proc/variable(i, list/node) + var/list/L = list(token(i)) + node[++node.len] = L - if(token(i) == "\[") - L += token(i + 1) - i += 2 + if(token(i) == "\[") + L += token(i + 1) + i += 2 - if(token(i) != "\]") - parse_error("Missing \] at end of reference.") + if(token(i) != "\]") + parse_error("Missing \] at end of reference.") - if(token(i + 1) == ".") - L += "." - i = variable(i + 2, L) + if(token(i + 1) == ".") + L += "." + i = variable(i + 2, L) - else if(token(i + 1) == "(") // OH BOY PROC - var/list/arguments = list() - i = call_function(i, null, arguments) - L += ":" - L[++L.len] = arguments + else if(token(i + 1) == "(") // OH BOY PROC + var/list/arguments = list() + i = call_function(i, null, arguments) + L += ":" + L[++L.len] = arguments - else if(token(i + 1) == "\[") // list index - var/list/expression = list() - i = expression(i + 2, expression) - if(token(i) != "]") - parse_error("Missing ] at the end of list access.") + else if(token(i + 1) == "\[") // list index + var/list/expression = list() + i = expression(i + 2, expression) + if(token(i) != "]") + parse_error("Missing ] at the end of list access.") - L += "\[" - L[++L.len] = expression - i++ + L += "\[" + L[++L.len] = expression + i++ - else - i++ + else + i++ - return i + return i //object_type: | string - object_type(i, list/node) +/datum/SDQL_parser/proc/object_type(i, list/node) - if(copytext(token(i), 1, 2) == "/") - node += token(i) + if(copytext(token(i), 1, 2) == "/") + node += token(i) - else - i = string(i, node) + else + i = string(i, node) - return i + 1 + return i + 1 //comparitor: '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>=' - comparitor(i, list/node) +/datum/SDQL_parser/proc/comparitor(i, list/node) - if(token(i) in list("=", "==", "!=", "<>", "<", "<=", ">", ">=")) - node += token(i) + if(token(i) in list("=", "==", "!=", "<>", "<", "<=", ">", ">=")) + node += token(i) - else - parse_error("Unknown comparitor [token(i)]") + else + parse_error("Unknown comparitor [token(i)]") - return i + 1 + return i + 1 //bool_operator: 'AND' | '&&' | 'OR' | '||' - bool_operator(i, list/node) +/datum/SDQL_parser/proc/bool_operator(i, list/node) - if(tokenl(i) in list("and", "or", "&&", "||")) - node += token(i) + if(tokenl(i) in list("and", "or", "&&", "||")) + node += token(i) - else - parse_error("Unknown comparitor [token(i)]") + else + parse_error("Unknown comparitor [token(i)]") - return i + 1 + return i + 1 //string: ''' ''' | '"' '"' - string(i, list/node) +/datum/SDQL_parser/proc/string(i, list/node) - if(copytext(token(i), 1, 2) in list("'", "\"")) - node += token(i) + if(copytext(token(i), 1, 2) in list("'", "\"")) + node += token(i) - else - parse_error("Expected string but found '[token(i)]'") + else + parse_error("Expected string but found '[token(i)]'") - return i + 1 + return i + 1 //array: '{' expression, expression, ... '}' - array(var/i, var/list/node) - // Arrays get turned into this: list("{", list(exp_1a = exp_1b, ...), ...), "{" is to mark the next node as an array. - if(copytext(token(i), 1, 2) != "{") - parse_error("Expected an array but found '[token(i)]'") - return i + 1 - - node += token(i) // Add the "{" - var/list/expression_list = list() - - if(token(i + 1) != "}") - var/list/temp_expression_list = list() - - do - i = expression(i + 1, temp_expression_list) - - if(token(i) == ",") - expression_list[++expression_list.len] = temp_expression_list - temp_expression_list = list() - while(token(i) && token(i) != "}") - - expression_list[++expression_list.len] = temp_expression_list - else - i++ - - node[++node.len] = expression_list +/datum/SDQL_parser/proc/array(var/i, var/list/node) + // Arrays get turned into this: list("{", list(exp_1a = exp_1b, ...), ...), "{" is to mark the next node as an array. + if(copytext(token(i), 1, 2) != "{") + parse_error("Expected an array but found '[token(i)]'") return i + 1 + node += token(i) // Add the "{" + var/list/expression_list = list() + + if(token(i + 1) != "}") + var/list/temp_expression_list = list() + + do + i = expression(i + 1, temp_expression_list) + + if(token(i) == ",") + expression_list[++expression_list.len] = temp_expression_list + temp_expression_list = list() + while(token(i) && token(i) != "}") + + expression_list[++expression_list.len] = temp_expression_list + else + i++ + + node[++node.len] = expression_list + return i + 1 + //call_function: ['(' [arguments] ')'] - call_function(i, list/node, list/arguments) - var/list/cur_argument = list() - if(length(tokenl(i))) - var/procname = "" - if(tokenl(i) == "global" && token(i + 1) == ".") // Global proc. - i += 2 - procname = "global." - node += procname + token(i++) - if(token(i) != "(") - parse_error("Expected ( but found '[token(i)]'") - else if(token(i + 1) != ")") - do - i = expression(i + 1, cur_argument) - if(token(i) == ",") - arguments += list(cur_argument) - cur_argument = list() - continue - while(token(i) && token(i) != ")") - arguments += list(cur_argument) - else - i++ +/datum/SDQL_parser/proc/call_function(i, list/node, list/arguments) + var/list/cur_argument = list() + if(length(tokenl(i))) + var/procname = "" + if(tokenl(i) == "global" && token(i + 1) == ".") // Global proc. + i += 2 + procname = "global." + node += procname + token(i++) + if(token(i) != "(") + parse_error("Expected ( but found '[token(i)]'") + else if(token(i + 1) != ")") + do + i = expression(i + 1, cur_argument) + if(token(i) == ",") + arguments += list(cur_argument) + cur_argument = list() + continue + while(token(i) && token(i) != ")") + arguments += list(cur_argument) else - parse_error("Expected a function but found nothing") - return i + 1 + i++ + else + parse_error("Expected a function but found nothing") + return i + 1 //select_function: count_function - select_function(i, list/node) +/datum/SDQL_parser/proc/select_function(i, list/node) - parse_error("Sorry, function calls aren't available yet") + parse_error("Sorry, function calls aren't available yet") - return i + return i //expression: ( unary_expression | '(' expression ')' | value ) [binary_operator expression] - expression(i, list/node) +/datum/SDQL_parser/proc/expression(i, list/node) + + if(token(i) in unary_operators) + i = unary_expression(i, node) + + else if(token(i) == "(") + var/list/expr = list() + + i = expression(i + 1, expr) + + if(token(i) != ")") + parse_error("Missing ) at end of expression.") + + else + i++ + + node[++node.len] = expr + + else + i = value(i, node) + + if(token(i) in binary_operators) + i = binary_operator(i, node) + i = expression(i, node) + + else if(token(i) in comparitors) + i = binary_operator(i, node) + + var/list/rhs = list() + i = expression(i, rhs) + + node[++node.len] = rhs + + + return i + + +//unary_expression: unary_operator ( unary_expression | value | '(' expression ')' ) +/datum/SDQL_parser/proc/unary_expression(i, list/node) + + if(token(i) in unary_operators) + var/list/unary_exp = list() + + unary_exp += token(i) + i++ if(token(i) in unary_operators) - i = unary_expression(i, node) + i = unary_expression(i, unary_exp) else if(token(i) == "(") var/list/expr = list() @@ -498,99 +538,55 @@ else i++ - node[++node.len] = expr + unary_exp[++unary_exp.len] = expr else - i = value(i, node) + i = value(i, unary_exp) - if(token(i) in binary_operators) - i = binary_operator(i, node) - i = expression(i, node) - - else if(token(i) in comparitors) - i = binary_operator(i, node) - - var/list/rhs = list() - i = expression(i, rhs) - - node[++node.len] = rhs + node[++node.len] = unary_exp - return i + else + parse_error("Expected unary operator but found '[token(i)]'") - -//unary_expression: unary_operator ( unary_expression | value | '(' expression ')' ) - unary_expression(i, list/node) - - if(token(i) in unary_operators) - var/list/unary_exp = list() - - unary_exp += token(i) - i++ - - if(token(i) in unary_operators) - i = unary_expression(i, unary_exp) - - else if(token(i) == "(") - var/list/expr = list() - - i = expression(i + 1, expr) - - if(token(i) != ")") - parse_error("Missing ) at end of expression.") - - else - i++ - - unary_exp[++unary_exp.len] = expr - - else - i = value(i, unary_exp) - - node[++node.len] = unary_exp - - - else - parse_error("Expected unary operator but found '[token(i)]'") - - return i + return i //binary_operator: comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' - binary_operator(i, list/node) +/datum/SDQL_parser/proc/binary_operator(i, list/node) - if(token(i) in (binary_operators + comparitors)) - node += token(i) + if(token(i) in (binary_operators + comparitors)) + node += token(i) - else - parse_error("Unknown binary operator [token(i)]") + else + parse_error("Unknown binary operator [token(i)]") - return i + 1 + return i + 1 //value: variable | string | number | 'null' - value(i, list/node) +/datum/SDQL_parser/proc/value(i, list/node) - if(token(i) == "null") - node += "null" - i++ + if(token(i) == "null") + node += "null" + i++ - else if(lowertext(copytext(token(i), 1, 3)) == "0x" && isnum(hex2num(copytext(token(i), 3)))) - node += hex2num(copytext(token(i), 3)) - i++ + else if(lowertext(copytext(token(i), 1, 3)) == "0x" && isnum(hex2num(copytext(token(i), 3)))) + node += hex2num(copytext(token(i), 3)) + i++ - else if(isnum(text2num(token(i)))) - node += text2num(token(i)) - i++ + else if(isnum(text2num(token(i)))) + node += text2num(token(i)) + i++ - else if(copytext(token(i), 1, 2) in list("'", "\"")) - i = string(i, node) + else if(copytext(token(i), 1, 2) in list("'", "\"")) + i = string(i, node) - else if(copytext(token(i), 1, 2) == "{") // Start a list. - i = array(i, node) + else if(copytext(token(i), 1, 2) == "{") // Start a list. + i = array(i, node) - else - i = variable(i, node) + else + i = variable(i, node) - return i + return i /*EXPLAIN SELECT * WHERE 42 = 6 * 9 OR val = - 5 == 7*/ diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index f74b8c9d030..4289df8ffbc 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -29,64 +29,12 @@ GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","mo msg = sanitize_simple(copytext(msg,1,MAX_MESSAGE_LEN)) if(!msg) return - var/original_msg = msg + if(selected_type == "Mentorhelp") + SSmentor_tickets.newHelpRequest(src, msg) + else + SStickets.newHelpRequest(src, msg) - //explode the input msg into a list - var/list/msglist = splittext(msg, " ") - - //generate keywords lookup - var/list/surnames = list() - var/list/forenames = list() - var/list/ckeys = list() - for(var/mob/M in GLOB.mob_list) - var/list/indexing = list(M.real_name, M.name) - if(M.mind) indexing += M.mind.name - - for(var/string in indexing) - var/list/L = splittext(string, " ") - var/surname_found = 0 - //surnames - for(var/i=L.len, i>=1, i--) - var/word = ckey(L[i]) - if(word) - surnames[word] = M - surname_found = i - break - //forenames - for(var/i=1, iTICKET) [ai_found ? "(CL)" : ""] (TAKE) (RESOLVE) [isMhelp ? "" : "(AUTO)"] : [span][msg]" - if(isMhelp) - //Open a new adminticket and inform the user. - SSmentor_tickets.newTicket(src, prunedmsg, msg) - for(var/client/X in mentorholders + modholders + adminholders) - if(X.prefs.sound & SOUND_MENTORHELP) - X << 'sound/effects/adminhelp.ogg' - to_chat(X, msg) - else //Ahelp - //Open a new adminticket and inform the user. - SStickets.newTicket(src, prunedmsg, msg) - for(var/client/X in modholders + adminholders) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - window_flash(X) - to_chat(X, msg) - - //show it to the person adminhelping too - to_chat(src, "[selected_type]: [original_msg]") + to_chat(src, "[selected_type]: [msg]") var/admin_number_present = adminholders.len - admin_number_afk - log_admin("[selected_type]: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins.") + log_admin("[selected_type]: [key_name(src)]: [msg] - heard by [admin_number_present] non-AFK admins.") if(admin_number_present <= 0) if(!admin_number_afk) - send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!No admins online!!") + send2adminirc("[selected_type] from [key_name(src)]: [msg] - !!No admins online!!") else - send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!All admins AFK ([admin_number_afk])!!") + send2adminirc("[selected_type] from [key_name(src)]: [msg] - !!All admins AFK ([admin_number_afk])!!") else - send2adminirc("[selected_type] from [key_name(src)]: [original_msg]") + send2adminirc("[selected_type] from [key_name(src)]: [msg]") feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index 0c1a8c5f14f..e5e5dae03b5 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -22,6 +22,10 @@ to_chat(src, "Nowhere to jump to!") return + if(isobj(usr.loc)) + var/obj/O = usr.loc + O.force_eject_occupant() + admin_forcemove(usr, T) log_admin("[key_name(usr)] jumped to [A]") if(!isobserver(usr)) @@ -35,6 +39,9 @@ if(!check_rights(R_ADMIN)) return + if(isobj(usr.loc)) + var/obj/O = usr.loc + O.force_eject_occupant() log_admin("[key_name(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]") if(!isobserver(usr)) message_admins("[key_name_admin(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]", 1) @@ -52,6 +59,9 @@ log_admin("[key_name(usr)] jumped to [key_name(M)]") if(!isobserver(usr)) message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) + if(isobj(usr.loc)) + var/obj/O = usr.loc + O.force_eject_occupant() if(src.mob) var/mob/A = src.mob var/turf/T = get_turf(M) @@ -70,6 +80,9 @@ var/turf/T = locate(tx, ty, tz) if(T) + if(isobj(usr.loc)) + var/obj/O = usr.loc + O.force_eject_occupant() admin_forcemove(usr, T) if(isobserver(usr)) var/mob/dead/observer/O = usr @@ -96,13 +109,15 @@ log_admin("[key_name(usr)] jumped to [key_name(M)]") if(!isobserver(usr)) message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) - + if(isobj(usr.loc)) + var/obj/O = usr.loc + O.force_eject_occupant() admin_forcemove(usr, M.loc) feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/Getmob(var/mob/M in GLOB.mob_list) - set category = "Admin" + set category = null set name = "Get Mob" set desc = "Mob to teleport" @@ -111,11 +126,15 @@ log_admin("[key_name(usr)] teleported [key_name(M)]") message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)]", 1) + + if(isobj(M.loc)) + var/obj/O = M.loc + O.force_eject_occupant() admin_forcemove(M, get_turf(usr)) feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/Getkey() - set category = "Admin" + set category = null set name = "Get Key" set desc = "Key to teleport" @@ -135,6 +154,9 @@ log_admin("[key_name(usr)] teleported [key_name(M)]") message_admins("[key_name_admin(usr)] teleported [key_name(M)]", 1) if(M) + if(isobj(M.loc)) + var/obj/O = M.loc + O.force_eject_occupant() admin_forcemove(M, get_turf(usr)) admin_forcemove(usr, M.loc) feedback_add_details("admin_verb","GK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -148,6 +170,9 @@ var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas() if(A) + if(isobj(M.loc)) + var/obj/O = M.loc + O.force_eject_occupant() admin_forcemove(M, pick(get_area_turfs(A))) feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! log_admin("[key_name(usr)] teleported [key_name(M)] to [A]") diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index dba9a5c26aa..48c407e6866 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -2,19 +2,19 @@ /client/proc/cmd_admin_pm_context(mob/M as mob in GLOB.mob_list) set category = null set name = "Admin PM Mob" - if(!holder) - to_chat(src, "Error: Admin-PM-Context: Only administrators may use this command.") + if(!check_rights(R_ADMIN|R_MENTOR)) + return + if(!ismob(M) || !M.client) return - if( !ismob(M) || !M.client ) return cmd_admin_pm(M.client,null) feedback_add_details("admin_verb","APMM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + //shows a list of clients we could send PMs to, then forwards our choice to cmd_admin_pm /client/proc/cmd_admin_pm_panel() set category = "Admin" set name = "Admin PM Name" - if(!holder) - to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.") + if(!check_rights(R_ADMIN|R_MENTOR)) return var/list/client/targets[0] for(var/client/T) @@ -36,8 +36,7 @@ /client/proc/cmd_admin_pm_by_key_panel() set category = "Admin" set name = "Admin PM Key" - if(!holder) - to_chat(src, "Error: Admin-PM-Panel: Only administrators may use this command.") + if(!check_rights(R_ADMIN|R_MENTOR)) return var/list/client/targets[0] for(var/client/T) @@ -65,9 +64,7 @@ var/client/C if(istext(whom)) - if(cmptext(copytext(whom,1,2),"@")) - whom = findStealthKey(whom) - C = GLOB.directory[whom] + C = get_client_by_ckey(whom) else if(istype(whom,/client)) C = whom diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index a0e8b4b9088..431633309a9 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -7,6 +7,8 @@ msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) if(!msg) return + var/datum/asays/asay = new(usr.ckey, usr.client.holder.rank, msg, world.timeofday) + GLOB.asays += asay log_adminsay(msg, src) if(check_rights(R_ADMIN,0)) @@ -78,5 +80,5 @@ C.verbs -= msay to_chat(C, "Mentor chat has been disabled.") - admin_log_and_message_admins("toggled mentor chat [enabling ? "on" : "off"].") + log_and_message_admins("toggled mentor chat [enabling ? "on" : "off"].") feedback_add_details("admin_verb", "TMC") diff --git a/code/modules/admin/verbs/alt_check.dm b/code/modules/admin/verbs/alt_check.dm deleted file mode 100644 index c8060f22036..00000000000 --- a/code/modules/admin/verbs/alt_check.dm +++ /dev/null @@ -1,20 +0,0 @@ -/client/proc/alt_check() - set category = "Admin" - set name = "Alt Account Checker" - - var/dat = {"Just to be sure you should try to also look up computer IDs/IPs on the server logs for a second opinion. -
    Additionally make an attempt to introduce new players to the server -
    "} - - if(GLOB.dbcon.IsConnected()) - for(var/client/C in GLOB.clients) - dat += "

    [C.ckey] (Player Age: [C.player_age]) - [C.computer_id] / [C.address]
    " - if(C.related_accounts_cid.len) - dat += "--Accounts associated with CID: " - dat += "[jointext(C.related_accounts_cid, " - ")]
    " - if(C.related_accounts_ip.len) - dat += "--Accounts associated with IP: " - dat += "[jointext(C.related_accounts_ip, " - ")] " - usr << browse(dat, "window=alt_panel;size=640x480") - return - diff --git a/code/modules/admin/verbs/asays.dm b/code/modules/admin/verbs/asays.dm new file mode 100644 index 00000000000..e713d2b1bcc --- /dev/null +++ b/code/modules/admin/verbs/asays.dm @@ -0,0 +1,70 @@ +GLOBAL_LIST_EMPTY(asays) + +/datum/asays + var/ckey + var/rank + var/message + var/time + +/datum/asays/New(ckey = "", rank = "", message = "", time = 0) + src.ckey = ckey + src.rank = rank + src.message = message + src.time = time + +/client/proc/view_asays() + set name = "Asays" + set desc = "View Asays from the current round." + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + var/list/output = list({" + + Refresh +

    NameFingerprints
    [H][md5(H.dna.uni_identity)]
    + "}) + + // Header & body start + output += {" + + + + + + + + + "} + + for(var/datum/asays/A in GLOB.asays) + var/timestr = time2text(A.time, "hh:mm:ss") + output += {" + + + + + + "} + + output += {" + +
    TimeCkeyMessage
    [timestr][A.ckey] ([A.rank])[A.message]
    "} + + var/datum/browser/popup = new(src, "asays", "
    Current Round Asays
    ", 1200, 825) + popup.set_content(output.Join()) + popup.open(0) diff --git a/code/modules/admin/verbs/custom_event.dm b/code/modules/admin/verbs/custom_event.dm index b3d1543aedc..1284f9e0faf 100644 --- a/code/modules/admin/verbs/custom_event.dm +++ b/code/modules/admin/verbs/custom_event.dm @@ -3,8 +3,7 @@ set category = "Event" set name = "Change Custom Event" - if(!holder) - to_chat(src, "Only administrators may use this command.") + if(!check_rights(R_EVENT)) return var/input = input(usr, "Enter the description of the custom event. Be descriptive. To cancel the event, make this blank or hit cancel.", "Custom Event", GLOB.custom_event_msg) as message|null diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 424e3c91f57..d2c05ff4f6c 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -168,7 +168,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) #endif /client/proc/callproc_datum(var/A as null|area|mob|obj|turf) - set category = "Debug" + set category = null set name = "Atom ProcCall" if(!check_rights(R_PROCCALL)) @@ -418,25 +418,6 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) else alert("Invalid mob") -//TODO: merge the vievars version into this or something maybe mayhaps -/client/proc/cmd_debug_del_all() - set category = "Debug" - set name = "Del-All" - - if(!check_rights(R_DEBUG)) - return - - // to prevent REALLY stupid deletions - var/blocked = list(/mob/living, /mob/living/carbon, /mob/living/carbon/human, /mob/dead, /mob/dead/observer, /mob/living/silicon, /mob/living/silicon/robot, /mob/living/silicon/ai) - var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in subtypesof(/obj) + subtypesof(/mob) - blocked - if(hsbitem) - for(var/atom/O in world) - if(istype(O, hsbitem)) - qdel(O) - log_admin("[key_name(src)] has deleted all instances of [hsbitem].") - message_admins("[key_name_admin(src)] has deleted all instances of [hsbitem].", 0) - feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - /client/proc/cmd_debug_del_sing() set category = "Debug" set name = "Del Singulo / Tesla" @@ -643,7 +624,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) for(var/areatype in areas_without_camera) to_chat(world, "* [areatype]") -/client/proc/cmd_admin_dress(var/mob/living/carbon/human/M in GLOB.mob_list) +/client/proc/cmd_admin_dress(mob/living/carbon/human/M in GLOB.human_list) set category = "Event" set name = "Select equipment" @@ -917,6 +898,9 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) if(istype(landmark)) var/datum/map_template/ruin/template = landmark.ruin_template + if(isobj(usr.loc)) + var/obj/O = usr.loc + O.force_eject_occupant() admin_forcemove(usr, get_turf(landmark)) to_chat(usr, "[template.name]") diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 78f2647e429..2ffb4659486 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -108,7 +108,7 @@ /client/proc/reload_admins() set name = "Reload Admins" - set category = "Debug" + set category = "Server" if(!check_rights(R_SERVER)) return diff --git a/code/modules/admin/verbs/gimmick_team.dm b/code/modules/admin/verbs/gimmick_team.dm index 37afe35ef6a..3080ef5706d 100644 --- a/code/modules/admin/verbs/gimmick_team.dm +++ b/code/modules/admin/verbs/gimmick_team.dm @@ -39,6 +39,7 @@ if(alert("Do you want these characters automatically classified as antagonists?",,"Yes","No")=="Yes") is_syndicate = 1 + var/datum/outfit/O = outfit_list[dresscode] var/list/players_to_spawn = list() if(pick_manually) var/list/possible_ghosts = list() @@ -52,14 +53,12 @@ players_to_spawn += candidate else to_chat(src, "Polling candidates...") - players_to_spawn = pollCandidates("Do you want to play as an event character?") + players_to_spawn = SSghost_spawns.poll_candidates("Do you want to play as \a [initial(O.name)]?") if(!players_to_spawn.len) to_chat(src, "Nobody volunteered.") return 0 - var/datum/outfit/O = outfit_list[dresscode] - var/players_spawned = 0 for(var/mob/thisplayer in players_to_spawn) var/mob/living/carbon/human/H = new /mob/living/carbon/human(T) @@ -94,4 +93,3 @@ feedback_add_details("admin_verb","SPAWNGIM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! // --------------------------------------------------------------------------------------------------------- - diff --git a/code/modules/admin/verbs/infiltratorteam_syndicate.dm b/code/modules/admin/verbs/infiltratorteam_syndicate.dm index 54ddf85045e..a4e12d8d768 100644 --- a/code/modules/admin/verbs/infiltratorteam_syndicate.dm +++ b/code/modules/admin/verbs/infiltratorteam_syndicate.dm @@ -55,7 +55,8 @@ GLOBAL_VAR_INIT(sent_syndicate_infiltration_team, 0) infiltrators += candidate else to_chat(src, "Polling candidates...") - infiltrators = pollCandidates("Do you want to play as a SYNDICATE INFILTRATOR?", ROLE_TRAITOR, 1) + var/image/I = new('icons/obj/cardboard_cutout.dmi', "cutout_sit") + infiltrators = SSghost_spawns.poll_candidates("Do you want to play as a Syndicate infiltrator?", ROLE_TRAITOR, TRUE, source = I, role_cleanname = "Syndicate infiltrator") if(!infiltrators.len) to_chat(src, "Nobody volunteered.") diff --git a/code/modules/admin/verbs/logging_view.dm b/code/modules/admin/verbs/logging_view.dm index 903926f4923..cafaab8f1d7 100644 --- a/code/modules/admin/verbs/logging_view.dm +++ b/code/modules/admin/verbs/logging_view.dm @@ -2,10 +2,20 @@ GLOBAL_LIST_INIT(open_logging_views, list()) /client/proc/cmd_admin_open_logging_view() set category = "Admin" - set name = "Open Logging View" + set name = "Logging View" set desc = "Opens the detailed logging viewer" + open_logging_view() - if(!GLOB.open_logging_views[usr.client.ckey]) - GLOB.open_logging_views[usr.client.ckey] = new /datum/log_viewer() - var/datum/log_viewer/LV = GLOB.open_logging_views[usr.client.ckey] - LV.show_ui(usr) +/client/proc/open_logging_view(list/mob/mobs_to_add = null, clear_view = FALSE) + var/datum/log_viewer/cur_view = GLOB.open_logging_views[usr.client.ckey] + if(!cur_view) + cur_view = new /datum/log_viewer() + GLOB.open_logging_views[usr.client.ckey] = cur_view + else if(clear_view) + cur_view.clear_all() + + if(mobs_to_add?.len) + cur_view.add_mobs(mobs_to_add) + + cur_view.show_ui(usr) + diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm index edbca5a842f..0dc3dfc1791 100644 --- a/code/modules/admin/verbs/map_template_loadverb.dm +++ b/code/modules/admin/verbs/map_template_loadverb.dm @@ -2,8 +2,9 @@ set category = "Debug" set name = "Map template - Place" - if(!holder) + if(!check_rights(R_DEBUG)) return + var/datum/map_template/template var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in GLOB.map_templates @@ -36,6 +37,9 @@ set category = "Debug" set name = "Map Template - Upload" + if(!check_rights(R_DEBUG)) + return + var/map = input(usr, "Choose a Map Template to upload to template storage","Upload Map Template") as null|file if(!map) return diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 5e05b8ecebd..3caac1d79d7 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -26,9 +26,9 @@ GLOBAL_VAR_INIT(intercom_range_display_status, 0) icon = 'icons/480x480.dmi' icon_state = "25percent" - New() - src.pixel_x = -224 - src.pixel_y = -224 +/obj/effect/debugging/camera_range/New() + src.pixel_x = -224 + src.pixel_y = -224 /obj/effect/debugging/mapfix_marker name = "map fix marker" diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index 3e89a6ac18d..779687b2501 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -1,4 +1,4 @@ -client/proc/one_click_antag() +/client/proc/one_click_antag() set name = "Create Antagonist" set desc = "Auto-create an antagonist of your choice" set category = "Event" @@ -137,7 +137,8 @@ client/proc/one_click_antag() var/confirm = alert("Are you sure?", "Confirm creation", "Yes", "No") if(confirm != "Yes") return 0 - var/list/candidates = pollCandidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard") + var/image/I = new('icons/mob/simple_human.dmi', "wizard") + var/list/candidates = SSghost_spawns.poll_candidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", "wizard", source = I) log_admin("[key_name(owner)] tried making a Wizard with One-Click-Antag") message_admins("[key_name_admin(owner)] tried making a Wizard with One-Click-Antag") @@ -241,7 +242,7 @@ client/proc/one_click_antag() var/obj/effect/landmark/nuke_spawn = locate("landmark*Nuclear-Bomb") var/obj/effect/landmark/closet_spawn = locate("landmark*Nuclear-Closet") - var/nuke_code = "[rand(10000, 99999)]" + var/nuke_code = rand(10000, 99999) if(nuke_spawn) var/obj/item/paper/P = new diff --git a/code/modules/admin/verbs/onlyone.dm b/code/modules/admin/verbs/onlyone.dm index e51f6da991a..9f2579ad162 100644 --- a/code/modules/admin/verbs/onlyone.dm +++ b/code/modules/admin/verbs/onlyone.dm @@ -84,7 +84,7 @@ var/obj/item/slot_item_hand = H.get_item_by_slot(slot_r_hand) H.unEquip(slot_item_hand) - var /obj/item/multisword/pure_evil/multi = new(H) + var/obj/item/multisword/pure_evil/multi = new(H) H.equip_to_slot_or_del(multi, slot_r_hand) var/obj/item/card/id/W = new(H) diff --git a/code/modules/admin/verbs/possess.dm b/code/modules/admin/verbs/possess.dm index 5cf4c34ffe2..3c1d0820dd7 100644 --- a/code/modules/admin/verbs/possess.dm +++ b/code/modules/admin/verbs/possess.dm @@ -2,6 +2,9 @@ set name = "Possess Obj" set category = null + if(!check_rights(R_POSSESS)) + return + if(istype(O,/obj/singularity)) if(config.forbid_singulo_possession) to_chat(usr, "It is forbidden to possess singularities.") @@ -35,6 +38,9 @@ set category = null //usr.loc = get_turf(usr) + if(!check_rights(R_POSSESS)) + return + if(usr.control_object && usr.name_archive) //if you have a name archived and if you are actually relassing an object usr.real_name = usr.name_archive usr.name = usr.real_name diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 03315243602..fa7ffa1ece8 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -51,7 +51,7 @@ if(!ismob(M)) return - if(!check_rights(R_SERVER|R_EVENT)) + if(!check_rights(R_EVENT)) return var/msg = clean_input("Message:", text("Subtle PM to [M.key]")) @@ -123,7 +123,7 @@ feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE - set category = "Event" + set category = null set name = "Direct Narrate" if(!check_rights(R_SERVER|R_EVENT)) @@ -158,7 +158,7 @@ /client/proc/admin_headset_message(mob/M in GLOB.mob_list, sender = null) var/mob/living/carbon/human/H = M - if(!check_rights(R_ADMIN)) + if(!check_rights(R_EVENT)) return if(!istype(H)) @@ -201,7 +201,7 @@ feedback_add_details("admin_verb","GOD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) +/proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) if(automute) if(!config.automute_on) return @@ -572,7 +572,7 @@ Traitors and the like can also be revived with the previous role mostly intact. feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/cmd_admin_rejuvenate(mob/living/M as mob in GLOB.mob_list) - set category = "Event" + set category = null set name = "Rejuvenate" if(!check_rights(R_REJUVINATE)) @@ -624,11 +624,11 @@ Traitors and the like can also be revived with the previous role mostly intact. var/beepsound = input(usr, "What sound should the announcement make?", "Announcement Sound", "") as anything in MsgSound GLOB.command_announcement.Announce(input, customname, MsgSound[beepsound], , , type) - print_command_report(input, "[command_name()] Update") + print_command_report(input, customname) if("No") //same thing as the blob stuff - it's not public, so it's classified, dammit GLOB.command_announcer.autosay("A classified message has been printed out at all communication consoles.") - print_command_report(input, "Classified [command_name()] Update") + print_command_report(input, "Classified: [customname]") else return @@ -638,7 +638,7 @@ Traitors and the like can also be revived with the previous role mostly intact. /client/proc/cmd_admin_delete(atom/A as obj|mob|turf in view()) - set category = "Admin" + set category = null set name = "Delete" if(!check_rights(R_ADMIN)) @@ -826,7 +826,10 @@ Traitors and the like can also be revived with the previous role mostly intact. else SSshuttle.emergency.canRecall = FALSE - SSshuttle.emergency.request() + if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) + SSshuttle.emergency.request(coefficient = 0.5, redAlert = TRUE) + else + SSshuttle.emergency.request() feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! log_admin("[key_name(usr)] admin-called the emergency shuttle.") @@ -871,9 +874,13 @@ Traitors and the like can also be revived with the previous role mostly intact. if(!check_rights(R_ADMIN)) return + if(alert(usr, "Do you want to [SSshuttle.emergencyNoEscape ? "ALLOW" : "DENY"] shuttle calls?", "Toggle Deny Shuttle", "Yes", "No") != "Yes") + return + if(SSshuttle) SSshuttle.emergencyNoEscape = !SSshuttle.emergencyNoEscape + feedback_add_details("admin_verb", "DENYSHUT") log_admin("[key_name(src)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.") message_admins("[key_name_admin(usr)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.") @@ -979,7 +986,8 @@ Traitors and the like can also be revived with the previous role mostly intact. var/role_string var/obj_count = 0 var/obj_string = "" - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing if(!isLivingSSD(H)) continue mins_ssd = round((world.time - H.last_logout) / 600) @@ -1016,7 +1024,8 @@ Traitors and the like can also be revived with the previous role mostly intact. msg += "AFK Players:
    " msg += "" var/mins_afk - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing if(H.client == null || H.stat == DEAD) // No clientless or dead continue mins_afk = round(H.client.inactivity / 600) @@ -1072,12 +1081,12 @@ Traitors and the like can also be revived with the previous role mostly intact. message_admins("Admin [key_name_admin(usr)] has disabled ERT calling.", 1) /client/proc/show_tip() - set category = "Admin" + set category = "Event" set name = "Show Custom Tip" set desc = "Sends a tip (that you specify) to all players. After all \ you're the experienced player here." - if(!check_rights(R_ADMIN)) + if(!check_rights(R_EVENT)) return var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null diff --git a/code/modules/admin/verbs/spawnfloorcluwne.dm b/code/modules/admin/verbs/spawnfloorcluwne.dm deleted file mode 100644 index 37fd3057af4..00000000000 --- a/code/modules/admin/verbs/spawnfloorcluwne.dm +++ /dev/null @@ -1,30 +0,0 @@ -/client/proc/spawn_floor_cluwne() - set category = "Event" - set name = "Unleash Floor Cluwne" - set desc = "Pick a specific target or just let it select randomly and spawn the floor cluwne mob on the station. Be warned: spawning more than one may cause issues!" - var/mob/living/carbon/human/target - - if(!check_rights(R_EVENT)) - return - - var/confirm = alert("Are you sure you want to release a floor cluwne and kill a lot of people?", "Confirm Massacre", "Yes", "No") - if(confirm == "Yes") - - var/turf/T = get_turf(usr) - var/list/potential_targets = list() - for(var/mob/M in GLOB.player_list) - var/mob/living/carbon/human/H = M - if(!istype(H)) - continue - if(H.mind.assigned_role == "Cluwne") - continue - potential_targets += H - if(!potential_targets.len) //You're probably the only player on this damn station, spawn it yourself - to_chat(src, "No valid targets!") - return - target = input("Any specific target in mind? Please note only live, non cluwned, human targets are valid.", "Target", target) as null|anything in potential_targets - var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T) - if(target) - FC.Acquire_Victim(target) - log_admin("[key_name(usr)] spawned floor cluwne[target ? ", initially targetting [target]": null].") - message_admins("[key_name(usr)] spawned floor cluwne[target ? ", initially targetting [target]": null].") diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm index 0b84c96e71a..87649fe3d4e 100644 --- a/code/modules/admin/verbs/striketeam.dm +++ b/code/modules/admin/verbs/striketeam.dm @@ -37,7 +37,8 @@ GLOBAL_VAR_INIT(sent_strike_team, 0) break // Find ghosts willing to be DS - var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) + var/image/source = image('icons/obj/cardboard_cutout.dmi', "cutout_deathsquad") + var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE, source = source) if(!commando_ghosts.len) to_chat(usr, "Nobody volunteered to join the DeathSquad.") return @@ -163,6 +164,7 @@ GLOBAL_VAR_INIT(sent_strike_team, 0) R.set_frequency(DTH_FREQ) R.requires_tcomms = FALSE R.instant = TRUE + R.freqlock = TRUE equip_to_slot_or_del(R, slot_l_ear) if(is_leader) equip_to_slot_or_del(new /obj/item/clothing/under/rank/centcom_officer(src), slot_w_uniform) diff --git a/code/modules/admin/verbs/striketeam_syndicate.dm b/code/modules/admin/verbs/striketeam_syndicate.dm index 12df7467cbc..dab21cfa8a1 100644 --- a/code/modules/admin/verbs/striketeam_syndicate.dm +++ b/code/modules/admin/verbs/striketeam_syndicate.dm @@ -45,7 +45,8 @@ GLOBAL_VAR_INIT(sent_syndicate_strike_team, 0) break // Find ghosts willing to be SST - var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) + var/image/I = new('icons/obj/cardboard_cutout.dmi', "cutout_commando") + var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 60 SECONDS, TRUE, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE, source = I) if(!commando_ghosts.len) to_chat(usr, "Nobody volunteered to join the SST.") return diff --git a/code/modules/alarm/alarm.dm b/code/modules/alarm/alarm.dm deleted file mode 100644 index 752d3232d23..00000000000 --- a/code/modules/alarm/alarm.dm +++ /dev/null @@ -1,136 +0,0 @@ -#define ALARM_RESET_DELAY 100 // How long will the alarm/trigger remain active once origin/source has been found to be gone? - -/datum/alarm_source - var/source = null // The source trigger - var/source_name = "" // The name of the source should it be lost (for example a destroyed camera) - var/duration = 0 // How long this source will be alarming, 0 for indefinetely. - var/severity = 1 // How severe the alarm from this source is. - var/start_time = 0 // When this source began alarming. - var/end_time = 0 // Use to set when this trigger should clear, in case the source is lost. - -/datum/alarm_source/New(var/atom/source) - src.source = source - start_time = world.time - source_name = source.get_source_name() - -/datum/alarm - var/atom/origin //Used to identify the alarm area. - var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared. - var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source. - var/list/cameras //List of cameras that can be switched to, if the player has that capability. - var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera). - var/area/last_name //The last acquired name, used should origin be lost - var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated. - var/end_time //Used to set when this alarm should clear, in case the origin is lost. - -/datum/alarm/New(var/atom/origin, var/atom/source, var/duration, var/severity) - src.origin = origin - - cameras() // Sets up both cameras and last alarm area. - set_source_data(source, duration, severity) - -/datum/alarm/process() - // Has origin gone missing? - if(!origin && !end_time) - end_time = world.time + ALARM_RESET_DELAY - for(var/datum/alarm_source/AS in sources) - // Has the alarm passed its best before date? - if((AS.end_time && world.time > AS.end_time) || (AS.duration && world.time > (AS.start_time + AS.duration))) - sources -= AS - // Has the source gone missing? Then reset the normal duration and set end_time - if(!AS.source && !AS.end_time) // end_time is used instead of duration to ensure the reset doesn't remain in the future indefinetely. - AS.duration = 0 - AS.end_time = world.time + ALARM_RESET_DELAY - -/datum/alarm/proc/set_source_data(var/atom/source, var/duration, var/severity) - var/datum/alarm_source/AS = sources_assoc[source] - if(!AS) - AS = new/datum/alarm_source(source) - sources += AS - sources_assoc[source] = AS - // Currently only non-0 durations can be altered (normal alarms VS EMP blasts) - if(AS.duration) - duration = duration SECONDS - AS.duration = duration - AS.severity = severity - -/datum/alarm/proc/clear(var/source) - var/datum/alarm_source/AS = sources_assoc[source] - sources -= AS - sources_assoc -= source - -/datum/alarm/proc/alarm_area() - if(!origin) - return last_area - - last_area = origin.get_alarm_area() - return last_area - -/datum/alarm/proc/alarm_name() - if(!origin) - return last_name - - last_name = origin.get_alarm_name() - return last_name - -/datum/alarm/proc/cameras() - // If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras - if(cameras && (last_camera_area != alarm_area())) - cameras = null - - // The list of cameras is also reset by /proc/invalidateCameraCache() - if(!cameras) - cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras() - - last_camera_area = last_area - return cameras - -/datum/alarm/proc/max_severity() - var/max_severity = 0 - for(var/datum/alarm_source/AS in sources) - max_severity = max(AS.severity, max_severity) - - return max_severity - -/****************** -* Assisting procs * -******************/ -/atom/proc/get_alarm_area() - var/area/A = get_area(src) - return A - -/area/get_alarm_area() - return src - -/atom/proc/get_alarm_name() - var/area/A = get_area(src) - return A.name - -/area/get_alarm_name() - return name - -/mob/get_alarm_name() - return name - -/atom/proc/get_source_name() - return name - -/obj/machinery/camera/get_source_name() - return c_tag - -/atom/proc/get_alarm_cameras() - var/area/A = get_area(src) - return A.get_cameras() - -/area/get_alarm_cameras() - return get_cameras() - -/mob/living/silicon/robot/get_alarm_cameras() - var/list/cameras = ..() - if(camera) - cameras += camera - - return cameras - -/mob/living/silicon/robot/syndicate/get_alarm_cameras() - return list() diff --git a/code/modules/alarm/alarm_handler.dm b/code/modules/alarm/alarm_handler.dm deleted file mode 100644 index 56a673bb5b4..00000000000 --- a/code/modules/alarm/alarm_handler.dm +++ /dev/null @@ -1,103 +0,0 @@ -#define ALARM_RAISED 1 -#define ALARM_CLEARED 0 - -/datum/alarm_handler - var/category = "" - var/list/datum/alarm/alarms = new // All alarms, to handle cases when an origin has been deleted with one or more active alarms - var/list/datum/alarm/alarms_assoc = new // Associative list of alarms, to efficiently acquire them based on origin. - var/list/listeners = new // A list of all objects interested in alarm changes. - -/datum/alarm_handler/process() - for(var/datum/alarm/A in alarms) - A.process() - check_alarm_cleared(A) - -/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1) - var/new_alarm - //Proper origin and source mandatory - if(!(origin && source)) - return - origin = origin.get_alarm_origin() - - new_alarm = 0 - //see if there is already an alarm of this origin - var/datum/alarm/existing = alarms_assoc[origin] - if(existing) - existing.set_source_data(source, duration, severity) - else - existing = new/datum/alarm(origin, source, duration, severity) - new_alarm = 1 - - alarms |= existing - alarms_assoc[origin] = existing - if(new_alarm) - alarms = dd_sortedObjectList(alarms) - on_alarm_change(existing, ALARM_RAISED) - - return new_alarm - -/datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source) - //Proper origin and source mandatory - if(!(origin && source)) - return - origin = origin.get_alarm_origin() - - var/datum/alarm/existing = alarms_assoc[origin] - if(existing) - existing.clear(source) - return check_alarm_cleared(existing) - -/datum/alarm_handler/proc/has_major_alarms() - if(alarms && alarms.len) - return 1 - return 0 - -/datum/alarm_handler/proc/major_alarms() - return alarms - -/datum/alarm_handler/proc/minor_alarms() - return alarms - -/datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm) - if((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len) - alarms -= alarm - alarms_assoc -= alarm.origin - on_alarm_change(alarm, ALARM_CLEARED) - return 1 - return 0 - -/datum/alarm_handler/proc/on_alarm_change(var/datum/alarm/alarm, var/was_raised) - for(var/obj/machinery/camera/C in alarm.cameras()) - if(was_raised) - C.network.Add(category) - else - C.network.Remove(category) - notify_listeners(alarm, was_raised) - -/datum/alarm_handler/proc/get_alarm_severity_for_origin(var/atom/origin) - if(!origin) - return - - origin = origin.get_alarm_origin() - var/datum/alarm/existing = alarms_assoc[origin] - if(!existing) - return - - return existing.max_severity() - -/atom/proc/get_alarm_origin() - return src - -/turf/get_alarm_origin() - var/area/area = get_area(src) - return area // Very important to get area.master, as dynamic lightning can and will split areas. - -/datum/alarm_handler/proc/register(var/object, var/procName) - listeners[object] = procName - -/datum/alarm_handler/proc/unregister(var/object) - listeners -= object - -/datum/alarm_handler/proc/notify_listeners(var/alarm, var/was_raised) - for(var/listener in listeners) - call(listener, listeners[listener])(src, alarm, was_raised) diff --git a/code/modules/alarm/atmosphere_alarm.dm b/code/modules/alarm/atmosphere_alarm.dm deleted file mode 100644 index cc29b40ac44..00000000000 --- a/code/modules/alarm/atmosphere_alarm.dm +++ /dev/null @@ -1,19 +0,0 @@ -/datum/alarm_handler/atmosphere - category = "Atmosphere Alarms" - -/datum/alarm_handler/atmosphere/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1) - ..() - -/datum/alarm_handler/atmosphere/major_alarms() - var/list/major_alarms = new() - for(var/datum/alarm/A in alarms) - if(A.max_severity() > 1) - major_alarms.Add(A) - return major_alarms - -/datum/alarm_handler/atmosphere/minor_alarms() - var/list/minor_alarms = new() - for(var/datum/alarm/A in alarms) - if(A.max_severity() == 1) - minor_alarms.Add(A) - return minor_alarms diff --git a/code/modules/alarm/burglar_alarm.dm b/code/modules/alarm/burglar_alarm.dm deleted file mode 100644 index c55cb12deef..00000000000 --- a/code/modules/alarm/burglar_alarm.dm +++ /dev/null @@ -1,2 +0,0 @@ -/datum/alarm_handler/burglar - category = "Burglar Alarms" diff --git a/code/modules/alarm/camera_alarm.dm b/code/modules/alarm/camera_alarm.dm deleted file mode 100644 index bef53ad466f..00000000000 --- a/code/modules/alarm/camera_alarm.dm +++ /dev/null @@ -1,2 +0,0 @@ -/datum/alarm_handler/camera - category = "Camera Alarms" diff --git a/code/modules/alarm/fire_alarm.dm b/code/modules/alarm/fire_alarm.dm deleted file mode 100644 index dfae3cc8177..00000000000 --- a/code/modules/alarm/fire_alarm.dm +++ /dev/null @@ -1,11 +0,0 @@ -/datum/alarm_handler/fire - category = "Fire Alarms" - -/datum/alarm_handler/fire/on_alarm_change(var/datum/alarm/alarm, var/was_raised) - var/area/A = alarm.origin - if(istype(A)) - if(was_raised) - A.fire_alert() - else - A.fire_reset() - ..() diff --git a/code/modules/alarm/motion_alarm.dm b/code/modules/alarm/motion_alarm.dm deleted file mode 100644 index fd7e6febe48..00000000000 --- a/code/modules/alarm/motion_alarm.dm +++ /dev/null @@ -1,2 +0,0 @@ -/datum/alarm_handler/motion - category = "Motion Alarms" diff --git a/code/modules/alarm/power_alarm.dm b/code/modules/alarm/power_alarm.dm deleted file mode 100644 index 4a0947a8f94..00000000000 --- a/code/modules/alarm/power_alarm.dm +++ /dev/null @@ -1,10 +0,0 @@ -/datum/alarm_handler/power - category = "Power Alarms" - -/datum/alarm_handler/power/on_alarm_change(var/datum/alarm/alarm, var/was_raised) - var/area/A = alarm.origin - if(istype(A)) - A.power_alert(was_raised) - ..() - -/area/proc/power_alert(var/alarming) diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 4a6fe7d6640..41333760f2d 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -73,7 +73,7 @@ GLOBAL_LIST_EMPTY(antagonists) /datum/antagonist/proc/replace_banned_player() set waitfor = FALSE - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [name]?", job_rank, TRUE, 50) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [name]?", job_rank, TRUE, 5 SECONDS) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index beefc41735b..35dfc22b1fb 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -46,7 +46,8 @@ checking = TRUE to_chat(user, "You activate [src] and wait for confirmation.") - var/list/nuke_candidates = pollCandidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 150) + var/image/I = new('icons/mob/simple_human.dmi', "syndicate_space_sword") + var/list/nuke_candidates = SSghost_spawns.poll_candidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 15 SECONDS, source = I) if(LAZYLEN(nuke_candidates)) checking = FALSE if(QDELETED(src) || !check_usability(user)) @@ -180,7 +181,7 @@ var/type = "slaughter" if(demon_type == /mob/living/simple_animal/slaughter/laughter) type = "laughter" - var/list/candidates = pollCandidates("Do you want to play as a [type] demon summoned by [user.real_name]?", ROLE_DEMON, 1, 100) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [type] demon summoned by [user.real_name]?", ROLE_DEMON, TRUE, 10 SECONDS, source = demon_type) if(candidates.len > 0) var/mob/C = pick(candidates) @@ -194,7 +195,7 @@ to_chat(user, "The demons do not respond to your summon. Perhaps you should try again later.") /obj/item/antag_spawner/slaughter_demon/spawn_antag(client/C, turf/T, type = "", mob/user) - var /obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(T) + var/obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(T) var/mob/living/simple_animal/slaughter/S = new demon_type(holder) S.vialspawned = TRUE S.holder = holder @@ -252,7 +253,7 @@ used = TRUE to_chat(user, "You break the seal on the bottle, calling upon the dire sludge to awaken...") - var/list/candidates = pollCandidates("Do you want to play as a magical morph awakened by [user.real_name]?", ROLE_MORPH, 1, 100) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a magical morph awakened by [user.real_name]?", ROLE_MORPH, 1, 10 SECONDS, source = morph_type) if(candidates.len > 0) var/mob/C = pick(candidates) diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index e13fb701317..6275c00ba9b 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -41,9 +41,8 @@ A.common_radio.channels.Remove("Syndicate") // De-traitored AIs can still state laws over the syndicate channel without this A.laws.sorted_laws = A.laws.inherent_laws.Copy() // AI's 'notify laws' button will still state a law 0 because sorted_laws contains it A.show_laws() - A.malf_picker.remove_malf_verbs(A) - A.verbs -= /mob/living/silicon/ai/proc/choose_modules - qdel(A.malf_picker) + A.remove_malf_abilities() + QDEL_NULL(A.malf_picker) if(owner.som) var/datum/mindslaves/slaved = owner.som @@ -251,9 +250,14 @@ /datum/antagonist/traitor/proc/update_traitor_icons_added(datum/mind/traitor_mind) - var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR] - traitorhud.join_hud(owner.current, null) - set_antag_hud(owner.current, "hudsyndicate") + if(locate(/datum/objective/hijack) in owner.objectives) + var/datum/atom_hud/antag/hijackhud = GLOB.huds[ANTAG_HUD_TRAITOR] + hijackhud.join_hud(owner.current, null) + set_antag_hud(owner.current, "hudhijack") + else + var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR] + traitorhud.join_hud(owner.current, null) + set_antag_hud(owner.current, "hudsyndicate") /datum/antagonist/traitor/proc/update_traitor_icons_removed(datum/mind/traitor_mind) @@ -400,7 +404,7 @@ var/phrases = jointext(GLOB.syndicate_code_phrase, ", ") var/responses = jointext(GLOB.syndicate_code_response, ", ") - var message = "
    The code phrases were:[phrases]
    \ + var/message = "
    The code phrases were:[phrases]
    \ The code responses were:[responses]
    " return message diff --git a/code/modules/arcade/prize_datums.dm b/code/modules/arcade/prize_datums.dm index 1edad36b8f3..2b9f17c3d04 100644 --- a/code/modules/arcade/prize_datums.dm +++ b/code/modules/arcade/prize_datums.dm @@ -203,6 +203,12 @@ GLOBAL_DATUM_INIT(global_prizes, /datum/prizes, new()) typepath = /obj/item/toy/toy_xeno cost = 80 +/datum/prize_item/rubberducky + name = "Rubber Ducky" + desc = "Your favorite bathtime buddy, all squeaks and quacks quality assured." + typepath = /obj/item/bikehorn/rubberducky + cost = 80 + /datum/prize_item/tacticool name = "Tacticool Turtleneck" desc = "A cool-looking turtleneck." diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index 5fea7baa019..6567dd62ede 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -1,3 +1,9 @@ +#define WIRE_RECEIVE (1<<0) //Allows pulse(0) to call Activate() +#define WIRE_PULSE (1<<1) //Allows pulse(0) to act on the holder +#define WIRE_PULSE_SPECIAL (1<<2) //Allows pulse(0) to act on the holders special assembly +#define WIRE_RADIO_RECEIVE (1<<3) //Allows pulse(1) to call Activate() +#define WIRE_RADIO_PULSE (1<<4) //Allows pulse(1) to send a radio message + /obj/item/assembly name = "assembly" desc = "A small electronic device that should never exist." @@ -22,21 +28,12 @@ var/wires = WIRE_RECEIVE | WIRE_PULSE var/datum/wires/connected = null // currently only used by timer/signaler - var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() - var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder - var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly - var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() - var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message - /obj/item/assembly/proc/activate() //What the device does when turned on return /obj/item/assembly/proc/pulsed(radio = FALSE) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs return -/obj/item/assembly/proc/pulse(radio = FALSE) //Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct - return - /obj/item/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here return @@ -79,7 +76,11 @@ activate() return TRUE -/obj/item/assembly/pulse(radio = FALSE) +//Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct +/obj/item/assembly/proc/pulse(radio = FALSE) + if(connected && wires) + connected.pulse_assembly(src) + return TRUE if(holder && (wires & WIRE_PULSE)) holder.process_activation(src, 1, 0) if(holder && (wires & WIRE_PULSE_SPECIAL)) diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm index bf62e2a828a..855c113a71a 100644 --- a/code/modules/assembly/bomb.dm +++ b/code/modules/assembly/bomb.dm @@ -12,6 +12,10 @@ var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank origin_tech = "materials=1;engineering=1" +/obj/item/onetankbomb/ComponentInitialize() + . = ..() + AddComponent(/datum/component/proximity_monitor) + /obj/item/onetankbomb/examine(mob/user) . = ..() . += bombtank.examine(user) @@ -52,12 +56,13 @@ if(!status) status = TRUE investigate_log("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) - msg_admin_attack("[key_name_admin(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", ATKLOG_FEW) log_game("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature - T0C]") to_chat(user, "A pressure hole has been bored to [bombtank] valve. [bombtank] can now be ignited.") + add_attack_logs(user, src, "welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", ATKLOG_FEW) else status = FALSE investigate_log("[key_name(user)] unwelded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) + add_attack_logs(user, src, "unwelded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", ATKLOG_ALMOSTALL) to_chat(user, "The hole has been closed.") diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 6a6cd7e8d8e..87fefe4fc65 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -41,19 +41,25 @@ if(!A1.remove_item_from_storage(src)) if(user) user.remove_from_mob(A1) - A1.loc = src + A1.forceMove(src) if(!A2.remove_item_from_storage(src)) if(user) user.remove_from_mob(A2) - A2.loc = src + A2.forceMove(src) A1.holder = src A2.holder = src a_left = A1 a_right = A2 + if(has_prox_sensors()) + AddComponent(/datum/component/proximity_monitor) name = "[A1.name]-[A2.name] assembly" update_icon() return TRUE +/obj/item/assembly_holder/proc/has_prox_sensors() + if(istype(a_left, /obj/item/assembly/prox_sensor) || istype(a_right, /obj/item/assembly/prox_sensor)) + return TRUE + return FALSE /obj/item/assembly_holder/update_icon() overlays.Cut() diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index 35e80e53d18..a17649bb008 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -63,6 +63,7 @@ else if(ismouse(target)) var/mob/living/simple_animal/mouse/M = target visible_message("SPLAT!") + M.death() M.splat() playsound(loc, 'sound/effects/snap.ogg', 50, 1) layer = MOB_LAYER - 0.2 diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index caefabcb1f8..ced9fbca932 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -13,6 +13,10 @@ var/timing = 0 var/time = 10 +/obj/item/assembly/prox_sensor/ComponentInitialize() + . = ..() + AddComponent(/datum/component/proximity_monitor) + /obj/item/assembly/prox_sensor/describe() if(timing) return "The proximity sensor is arming." diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 2759d12c21a..0c3fcfec3ac 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -128,12 +128,6 @@ if(usr) GLOB.lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") -/obj/item/assembly/signaler/pulse(var/radio = FALSE) - if(connected && wires) - connected.Pulse(src) - else - return ..(radio) - /obj/item/assembly/signaler/receive_signal(datum/signal/signal) if(!receiving || !signal) return FALSE diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm index 5bbd9a26538..67be53979d8 100644 --- a/code/modules/assembly/timer.dm +++ b/code/modules/assembly/timer.dm @@ -101,8 +101,8 @@ if(href_list["time"]) timing = !timing if(timing && istype(holder, /obj/item/transfer_valve)) - message_admins("[key_name_admin(usr)] activated [src] attachment on [holder].") investigate_log("[key_name(usr)] activated [src] attachment for [loc]", INVESTIGATE_BOMB) + add_attack_logs(usr, holder, "activated [src] attachment on", ATKLOG_FEW) log_game("[key_name(usr)] activated [src] attachment for [loc]") update_icon() if(href_list["reset"]) diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index bac8d64d9f2..5d3d6494e48 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -16,7 +16,9 @@ var/death = TRUE //Kill the mob var/roundstart = TRUE //fires on initialize var/instant = FALSE //fires on New - var/flavour_text = "The mapper forgot to set this!" + var/flavour_text = "" //flavour/fluff about the role, optional. + var/description = "A description for this has not been set. This is either an oversight or an admin-spawned spawner not in normal use." //intended as OOC info about the role + var/important_info = "" //important info such as rules that apply to you, etc. Optional. var/faction = null var/permanent = FALSE //If true, the spawner will not disappear upon running out of uses. var/random = FALSE //Don't set a name or gender, just go random @@ -336,7 +338,7 @@ name = "sleeper" icon = 'icons/obj/cryogenic2.dmi' icon_state = "sleeper" - flavour_text = "You are a space doctor!" + flavour_text = "You are a space doctor!" assignedrole = "Space Doctor" /obj/effect/mob_spawn/human/doctor/alive/equip(mob/living/carbon/human/H) @@ -470,11 +472,14 @@ name = "bartender sleeper" icon = 'icons/obj/cryogenic2.dmi' icon_state = "sleeper" - flavour_text = "You are a space bartender! Time to mix drinks and change lives." + description = "Stuck on Lavaland, you could try getting back to civilisation...or serve drinks to those that wander by." + flavour_text = "You are a space bartender! Time to mix drinks and change lives. Wait, where did your bar just get transported to?" assignedrole = "Space Bartender" /obj/effect/mob_spawn/human/beach/alive/lifeguard - flavour_text = "You're a spunky lifeguard! It's up to you to make sure nobody drowns or gets eaten by sharks and stuff." + flavour_text = "You're a spunky lifeguard! It's up to you to make sure nobody drowns or gets eaten by sharks and stuff. Then suddenly your entire beach was transported to this strange hell.\ + You aren't trained for this, but you'll still keep your guests alive!" + description = "Try to survive on lavaland with the pitiful equipment of a lifeguard. Or hide in your biodome." mob_gender = "female" name = "lifeguard sleeper" id_job = "Lifeguard" @@ -502,7 +507,8 @@ name = "beach bum sleeper" icon = 'icons/obj/cryogenic2.dmi' icon_state = "sleeper" - flavour_text = "You are a beach bum!" + flavour_text = "You are a beach bum! You think something just happened to the beach but you don't really pay too much attention." + description = "Try to survive on lavaland or just enjoy the beach, waiting for visitors." assignedrole = "Beach Bum" /datum/outfit/beachbum @@ -523,6 +529,7 @@ roundstart = FALSE icon = 'icons/effects/blood.dmi' icon_state = "remains" + description = "Be a spooky scary skeleton." //not mapped in anywhere so admin spawner, who knows what they'll use this for. flavour_text = "By unknown powers, your skeletal remains have been reanimated! Walk this mortal plain and terrorize all living adventurers who dare cross your path." assignedrole = "Skeleton" diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm index 7efff7cc27b..f034c4f39d1 100644 --- a/code/modules/awaymissions/maploader/reader.dm +++ b/code/modules/awaymissions/maploader/reader.dm @@ -30,6 +30,11 @@ GLOBAL_DATUM_INIT(_preloader, /datum/dmm_suite/preloader, new()) var/fname = "Lambda" if(isfile(tfile)) fname = "[tfile]" + // Make sure we dont load a dir up + var/lastchar = copytext(fname, -1) + if(lastchar == "/" || lastchar == "\\") + log_debug("Attempted to load map template without filename (Attempted [tfile])") + return tfile = file2text(tfile) if(!length(tfile)) throw EXCEPTION("Map path '[fname]' does not exist!") diff --git a/code/modules/awaymissions/mission_code/academy.dm b/code/modules/awaymissions/mission_code/academy.dm index 7cddddd8b08..65c64edd83f 100644 --- a/code/modules/awaymissions/mission_code/academy.dm +++ b/code/modules/awaymissions/mission_code/academy.dm @@ -193,7 +193,7 @@ servant_mind.objectives += O servant_mind.transfer_to(H) - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 300) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 30 SECONDS, source = H) if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant") diff --git a/code/modules/awaymissions/mission_code/ghost_role_spawners/golems.dm b/code/modules/awaymissions/mission_code/ghost_role_spawners/golems.dm index 9a5bf275abc..0996f214691 100644 --- a/code/modules/awaymissions/mission_code/ghost_role_spawners/golems.dm +++ b/code/modules/awaymissions/mission_code/ghost_role_spawners/golems.dm @@ -64,9 +64,11 @@ var/has_owner = FALSE var/can_transfer = TRUE //if golems can switch bodies to this new shell var/mob/living/owner = null //golem's owner if it has one - flavour_text = "You are a Free Golem. Your family worships The Liberator. In his infinite and divine wisdom, he set your clan free to \ + important_info = "You are not an antag. Do not mess with the station or create AIs." + description = "As a Free Golem on lavaland, you are unable to use most weapons, but you can mine, research and make more of your kind. Earn enough mining points and you can even move your shuttle out of there." + flavour_text = "You are a Free Golem. Your family worships The Liberator. In his infinite and divine wisdom, he set your clan free to \ travel the stars with a single declaration: \"Yeah go do whatever.\" Though you are bound to the one who created you, it is customary in your society to repeat those same words to newborn \ - golems, so that no golem may ever be forced to serve again." + golems, so that no golem may ever be forced to serve again." /obj/effect/mob_spawn/human/golem/Initialize(mapload, datum/species/golem/species = null, mob/creator = null) if(species) //spawners list uses object name to register so this goes before ..() @@ -77,8 +79,10 @@ if(!mapload && A) notify_ghosts("\A [initial(species.prefix)] golem shell has been completed in [A.name].", source = src) if(has_owner && creator) - flavour_text = "You are a Golem. You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. \ - Serve [creator], and assist [creator.p_them()] in completing [creator.p_their()] goals at any cost." + important_info = "Serve your creator, even if they are an antag." + flavour_text = "You are a golem created to serve your creator." + description = "You are a Golem. You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. \ + Serve [creator], and assist [creator.p_them()] in completing [creator.p_their()] goals at any cost." owner = creator /obj/effect/mob_spawn/human/golem/special(mob/living/new_spawn, name) diff --git a/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm b/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm index bb1aae5f8a8..62f4226394d 100644 --- a/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm +++ b/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm @@ -10,10 +10,11 @@ death = FALSE random = TRUE mob_species = /datum/species/human - flavour_text = "You are a security officer working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \ + description = "Work as a team with your fellow survivors aboard a ruined, ancient space station." + important_info = "" + flavour_text = "You are a security officer working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \ cryogenics pod due to an oncoming radiation storm. The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \ - your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod. \ - Work as a team with your fellow survivors and do not abandon them." + your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod." uniform = /obj/item/clothing/under/retro/security shoes = /obj/item/clothing/shoes/jackboots id = /obj/item/card/id/away/old/sec @@ -35,10 +36,11 @@ death = FALSE random = TRUE mob_species = /datum/species/human - flavour_text = "You are a medical working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \ + description = "Work as a team with your fellow survivors aboard a ruined, ancient space station." + important_info = "" + flavour_text = "You are a medical doctor working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \ cryogenics pod due to an oncoming radiation storm. The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \ - your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod. \ - Work as a team with your fellow survivors and do not abandon them." + your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod." uniform = /obj/item/clothing/under/retro/medical shoes = /obj/item/clothing/shoes/black id = /obj/item/card/id/away/old/med @@ -60,10 +62,11 @@ death = FALSE random = TRUE mob_species = /datum/species/human - flavour_text = "You are an engineer working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \ + description = "Work as a team with your fellow survivors aboard a ruined, ancient space station." + important_info = "" + flavour_text = "You are an engineer working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \ cryogenics pod due to an oncoming radiation storm. The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \ - your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod. \ - Work as a team with your fellow survivors and do not abandon them." + your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod." uniform = /obj/item/clothing/under/retro/engineering shoes = /obj/item/clothing/shoes/workboots id = /obj/item/card/id/away/old/eng @@ -85,10 +88,11 @@ death = FALSE random = TRUE mob_species = /datum/species/human - flavour_text = "You are a scientist working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \ + description = "Work as a team with your fellow survivors aboard a ruined, ancient space station." + important_info = "" + flavour_text = "You are a scientist working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \ cryogenics pod due to an oncoming radiation storm. The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \ - your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod. \ - Work as a team with your fellow survivors and do not abandon them." + your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod." uniform = /obj/item/clothing/under/retro/science shoes = /obj/item/clothing/shoes/laceup id = /obj/item/card/id/away/old/sci diff --git a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm index c5b56805213..75fbbf698e6 100644 --- a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm +++ b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm @@ -4,10 +4,18 @@ name = "Mission Briefing" info = "To the Magnificent Z.A.P.
    A small mining base has been created within our territory by wandless scum. Send them a message from the wizard federation they will not forget. I know your kind is rather fragile, but a group of lightly armed miners should not pose any threat to you at all. Just be warned they have a security cyborg for self defence, you might want to tune your spells to that threat. I look forward to hearing of your success.
    Grand Magus Abra the Wonderous" +/obj/item/spellbook/oneuse/emp + spell = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech + spellname = "Disable Technology" + icon_state = "bookcharge" //it's a lightning bolt, seems appropriate enough + desc = "For the tech-hating wizard on the go." + +/obj/item/spellbook/oneuse/emp/used + used = TRUE //spawns used /obj/effect/spawner/lootdrop/wizardcrash loot = list( - /obj/item/guardiancreator = 1, //jackpot. + /obj/item/guardiancreator = 1, //jackpot. /obj/item/spellbook/oneuse/knock = 1, //tresspassing charges incoming /obj/item/gun/magic/wand/resurrection = 1, //medbay's best friend /obj/item/spellbook/oneuse/charge = 20, //and now for less useful stuff to dilute the good loot chances diff --git a/code/modules/awaymissions/mission_code/stationCollision.dm b/code/modules/awaymissions/mission_code/stationCollision.dm index 1beba373724..07be3364552 100644 --- a/code/modules/awaymissions/mission_code/stationCollision.dm +++ b/code/modules/awaymissions/mission_code/stationCollision.dm @@ -60,7 +60,7 @@ * Guns - I'm making these specifically so that I dont spawn a pile of fully loaded weapons on the map. */ //Captain's retro laser - Fires practice laser shots instead. -obj/item/gun/energy/laser/retro/sc_retro +/obj/item/gun/energy/laser/retro/sc_retro desc = "An older model of the basic lasergun, no longer used by Nanotrasen's security or military forces." ammo_type = list(/obj/item/ammo_casing/energy/laser/practice) clumsy_check = 0 //No sense in having a harmless gun blow up in the clowns face diff --git a/code/modules/busy_space/air_traffic.dm b/code/modules/busy_space/air_traffic.dm deleted file mode 100644 index e21414e8ed3..00000000000 --- a/code/modules/busy_space/air_traffic.dm +++ /dev/null @@ -1,128 +0,0 @@ -//Cactus, Speedbird, Dynasty, oh my -GLOBAL_DATUM_INIT(atc, /datum/lore/atc_controller, new) - -/datum/lore/atc_controller - var/delay_max = 10 MINUTES //Maximum amount of tiem between ATC messages. Default is 10 mins. - var/delay_min = 5 MINUTES //Minimum amount of time between ATC messages. Default is 5 mins. - var/backoff_delay = 5 MINUTES //How long to back off if we can't talk and want to. Default is 5 mins. - var/next_message //When the next message should happen in world.time - var/force_chatter_type //Force a specific type of messages - - var/squelched = FALSE //If ATC is squelched currently - -/datum/lore/atc_controller/New() - spawn(30 SECONDS) //Lots of lag at the start of a shift. - msg("New shift beginning, resuming traffic control.") - next_message = world.time + rand(delay_min, delay_max) - process() - -/datum/lore/atc_controller/process() - if(world.time >= next_message) - if(squelched) - next_message = world.time + backoff_delay - else - next_message = world.time + rand(delay_min,delay_max) - random_convo() - - spawn(1 MINUTES) //We don't really need high-accuracy here. - process() - -/datum/lore/atc_controller/proc/msg(var/message,var/sender) - ASSERT(message) - GLOB.global_announcer.autosay("[message]", sender ? sender : "[GLOB.using_map.station_short] Space Control") - -/datum/lore/atc_controller/proc/reroute_traffic(var/yes = 1) - if(yes) - if(!squelched) - msg("Rerouting traffic away from [GLOB.using_map.station_name].") - squelched = TRUE - else - if(squelched) - msg("Resuming normal traffic routing around [GLOB.using_map.station_name].") - squelched = FALSE - -/datum/lore/atc_controller/proc/shift_ending(var/evac = 0) - msg("Automated Shuttle departing [GLOB.using_map.station_name] for [GLOB.using_map.dock_name] on routine transfer route.", "NT Automated Shuttle") - sleep(5 SECONDS) - msg("Automated Shuttle, cleared to complete routine transfer from [GLOB.using_map.station_name] to [GLOB.using_map.dock_name].") - -/datum/lore/atc_controller/proc/random_convo() - var/one = pick(GLOB.loremaster.organizations) //These will pick an index, not an instance - var/two = pick(GLOB.loremaster.organizations) - - var/datum/lore/organization/source = GLOB.loremaster.organizations[one] //Resolve to the instances - var/datum/lore/organization/dest = GLOB.loremaster.organizations[two] - - //Let's get some mission parameters - var/owner = source.short_name //Use the short name - var/prefix = pick(source.ship_prefixes) //Pick a random prefix - var/mission = source.ship_prefixes[prefix] //The value of the prefix is the mission type that prefix does - var/shipname = pick(source.ship_names) //Pick a random ship name to go with it - var/destname = pick(dest.destination_names) //Pick a random holding from the destination - - var/combined_name = "[owner] [prefix] [shipname]" - var/alt_atc_names = list("[GLOB.using_map.station_short] TraCon", "[GLOB.using_map.station_short] Control", "[GLOB.using_map.station_short] STC", "[GLOB.using_map.station_short] Airspace") - var/wrong_atc_names = list("Sol Command", "Orion Control", "[GLOB.using_map.dock_name]") - var/mission_noun = list("flight", "mission", "route") - var/request_verb = list("requesting", "calling for", "asking for") - - //First response is 'yes', second is 'no' - var/requests = list("[GLOB.using_map.station_short] transit clearance" = list("cleared to transit", "unable to approve, contact regional on 953.5"), - "planetary flight rules" = list("cleared planetary flight rules", "unable to approve planetary flight rules due to traffic"), - "special flight rules" = list("cleared special flight rules", "unable to approve special flight rules for your traffic class"), - "current solar weather info" = list("sending you the relevant information via tightbeam", "cannot fulfill your request at the moment"), - "nearby traffic info" = list("sending you current traffic info", "no known traffic for your flight plan route"), - "remote telemetry data" = list("sending telemetry now", "no uplink from your ship, recheck your uplink and ask again"), - "refueling information" = list("sending refueling information now", "no fuel for your ship class in this sector"), - "a current system time sync" = list("sending time sync ping to you now", "your ship isn't compatible with our time sync, set time manually"), - "current system starcharts" = list("transmitting current starcharts", "request on standby due to demand"), - "permission to engage FTL" = list("cleared to FTL, good day", "hold position, traffic crossing"), - "permission to transit system" = list("cleared to transit, good day", "hold position, traffic crossing"), - "permission to depart system" = list("cleared to leave via flight plan route, good day", "hold position, traffic crossing"), - "permission to enter system" = list("good day, cleared in as published", "hold position, traffic crossing"), - ) - - //Random chance things for variety - var/chatter_type = "normal" - if(force_chatter_type) - chatter_type = force_chatter_type - else - chatter_type = pick(2;"emerg",5;"wrong_freq","normal") //Be nice to have wrong_lang... - - var/yes = prob(90) //Chance for them to say yes vs no - - var/request = pick(requests) - var/callname = pick(alt_atc_names) - var/response = requests[request][yes ? 1 : 2] //1 is yes, 2 is no - - var/full_request - var/full_response - var/full_closure - - switch(chatter_type) - if("wrong_freq") - callname = pick(wrong_atc_names) - full_request = "[callname], this is [combined_name] on a [mission] [pick(mission_noun)] to [destname], [pick(request_verb)] [request]." - full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, wrong frequency. Switch to [rand(700,999)].[rand(1,9)]." - full_closure = "[GLOB.using_map.station_short] TraCon, copy, apologies." - if("wrong_lang") - //Can't implement this until autosay has language support - if("emerg") - var/problem = pick("hull breaches on multiple decks","unknown life forms on board","a drive about to go critical","asteroids impacting the hull","a total loss of engine power","people trying to board the ship") - full_request = "Mayday, mayday, mayday, this is [combined_name] declaring an emergency! We have [problem]!" - var/rand_freq = "[rand(700,999)].[rand(1,9)]" - full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, copy. Switch to emergency responder channel [rand_freq]." - full_closure = "Roger, [GLOB.using_map.station_short] TraCon, contacting [rand_freq]." - else - full_request = "[callname], this is [combined_name] on a [mission] [pick(mission_noun)] to [destname], [pick(request_verb)] [request]." - full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, [response]." //Station TraCon always calls themselves TraCon - full_closure = "[GLOB.using_map.station_short] TraCon, [yes ? "thank you" : "copy"], good day." //They always copy what TraCon called themselves in the end when they realize they said it wrong - - //Ship sends request to ATC - msg(full_request,"[prefix] [shipname]") - sleep(5 SECONDS) - //ATC sends response to ship - msg(full_response) - sleep(5 SECONDS) - //Ship sends response to ATC - msg(full_closure,"[prefix] [shipname]") diff --git a/code/modules/busy_space/loremaster.dm b/code/modules/busy_space/loremaster.dm deleted file mode 100644 index 77b8a8ed53d..00000000000 --- a/code/modules/busy_space/loremaster.dm +++ /dev/null @@ -1,15 +0,0 @@ -//I AM THE LOREMASTER, ARE YOU THE GATEKEEPER? -GLOBAL_DATUM_INIT(loremaster, /datum/lore/loremaster, new) - -/datum/lore/loremaster - var/list/organizations = list() - -/datum/lore/loremaster/New() - - var/list/paths = typesof(/datum/lore/organization) - /datum/lore/organization - for(var/path in paths) - // Some intermediate paths are not real organizations (ex. /datum/lore/organization/mil). Only do ones with names - var/datum/lore/organization/instance = path - if(initial(instance.name)) - instance = new path() - organizations[path] = instance diff --git a/code/modules/busy_space/organizations.dm b/code/modules/busy_space/organizations.dm deleted file mode 100644 index c39e92ea49e..00000000000 --- a/code/modules/busy_space/organizations.dm +++ /dev/null @@ -1,549 +0,0 @@ -//Datums for different companies that can be used by busy_space -/datum/lore/organization - var/name = "" // Organization's name - var/short_name = "" // Organization's shortname (Nanotrasen for "Nanotrasen Incorporated") - var/acronym = "" // Organization's acronym, e.g. 'NT' for Nanotrasen'. - var/desc = "" // One or two paragraph description of the organization, but only current stuff. Currently unused. - var/history = "" // Historical description of the organization's origins Currently unused. - var/work = "" // Short description of their work, eg "an arms manufacturer" - var/headquarters = "" // Location of the organization's HQ. Currently unused. - var/motto = "" // A motto/jingle/whatever, if they have one. Currently unused. - - var/list/ship_prefixes = list() //Some might have more than one! Like Nanotrasen. Value is the mission they perform, e.g. ("ABC" = "mission desc") - var/list/ship_names = list( //Names of spaceships. This is a mostly generic list that all the other organizations inherit from if they don't have anything better. - "Kestrel", - "Beacon", - "Signal", - "Freedom", - "Glory", - "Axiom", - "Eternal", - "Icarus", - "Harmony", - "Light", - "Discovery", - "Endeavour", - "Explorer", - "Swift", - "Dragonfly", - "Ascendant", - "Tenacious", - "Pioneer", - "Hawk", - "Haste", - "Radiant", - "Luminous", - "Gallant", - "Dependable", - "Indomitable", - "Guardian", - "Resolution", - "Fearless", - "Amazon", - "Relentless", - "Inspire", - "Implacable", - "Steadfast", - "Leviathan", - "Dauntless", - "Adroit", - "Mistral", - "Typhoon", - "Titan", - "Kupua", - "Alchemist", - "Cuirass", - "Citadel", - "Rondelle", - "Camail", - "Ocrea", - "Ram", - "Crest", - "Tanko", - "Pommel", - "Kissaki", - "Cavalier", - "Anelace", - "Flint", - "Xiphos", - "Parrot", - "Chamber", - "Annellet", - "Cestus", - "Talwar") - var/list/destination_names = list() //Names of static holdings that the organization's ships visit regularly. - var/autogenerate_destination_names = TRUE - -/datum/lore/organization/New() - ..() - if(autogenerate_destination_names) // Lets pad out the destination names. - var/i = rand(6, 10) - var/list/star_names = list( - "Sol", "Alpha Centauri", "Sirius", "Vega", "Regulus", "Vir", "Algol", "Aldebaran", - "Delta Doradus", "Menkar", "Geminga", "Elnath", "Gienah", "Mu Leporis", "Nyx", "Tau Ceti", - "Wazn", "Alphard", "Phact", "Altair", "Mauna", "Jargon", "Xarxis", "Hestia", "Dalstis", "Cygni", "Haverick", "Corvus", "Sancere", "Cydoni", "Kaliban", "Midway", "Dansik", "Branwyn") - var/list/destination_types = list("dockyard", "station", "vessel", "waystation", "telecommunications satellite", "spaceport", "distress beacon", "anomaly", "colony", "outpost") - while(i) - destination_names.Add("a [pick(destination_types)] in [pick(star_names)]") - i-- - -////////////////////////////////////////////////////////////////////////////////// - -// TSCs -/datum/lore/organization/tsc/nanotrasen - name = "Nanotrasen Incorporated" - short_name = "Nanotrasen" - acronym = "NT" - desc = "The largest shareholder in the galactic plasma markets, Nanotrasen is a research and mining corporation which specializes in\ - FTL technologies and weapon systems. Frowned upon by most governments due to their shady business tactics and poor ethics record,\ - Nanotrasen is often seen as a necessary evil for maintaining access to the often volatile plasma market. Nanotrasen was originally\ - incorporated on Earth with their headquarters situated on Mars, however they have recently moved most of their operations to the Epsilon Eridani sector." - history = "" // To be written someday. - work = "research giant" - headquarters = "Mars" - motto = "" - - ship_prefixes = list("NSV" = "exploration", "NTV" = "hauling", "NDV" = "patrol", "NRV" = "emergency response") - // Note that the current station being used will be pruned from this list upon being instantiated - destination_names = list( - "NAS Trurl in Epsilon Eridani", - "NAS Crescent in Tau Ceti", - "NSS Exodus in Tau Ceti", - "NSS Antiqua in Darsing", - "NRS Orion in Sol", - "NSS Vector in Omicron Ceti", - "NBS Anansi in Omicron Ceti", - "NSS Redemption in Sirius", - "NDS Inferno in Tau Ceti", - "NAB Smythside Central Headquarters on Earth", - "NAB North Cimmeria Central Offices on Mars", - ) - -/datum/lore/organization/tsc/nanotrasen/New() - ..() - spawn(1) // BYOND shenanigans means using_map is not initialized yet. Wait a tick. - // Get rid of the current map from the list, so ships flying in don't say they're coming to the current map. - var/string_to_test = "[GLOB.using_map.station_name] in [GLOB.using_map.starsys_name]" - if(string_to_test in destination_names) - destination_names.Remove(string_to_test) - - -/datum/lore/organization/tsc/donk - name = "Donk Corporation" - short_name = "Donk Co." - acronym = "DC" - desc = "The infamous rival of the well-known Waffle Corporation, Donk Co. is a company specializing in food delivery systems and brand-name food\ - products such as Donk Pockets. While generally seen as a neutral actor, Donk Corporation has been known to work both with Nanotrasen and\ - the Syndicate when it suits them - often acting as the primary logistical supplier for the Epsilon Eridani sector.\ - Donk Corporation is better known for recent high-profile litigation alleging that their food products are used for illicit drug distribution.\ - While the trial is ongoing, it has been repeatedly delayed due to incidents of methamphetamine poisoning." - history = "" - work = "food company that establishes and maintains delivery supply chains" - headquarters = "" - motto = "Now with 20% more donk!" - - ship_prefixes = list("D-Co." = "transportation") - destination_names = list() - -/datum/lore/organization/tsc/hephaestus - name = "Hephaestus Industries" - short_name = "Hephaestus" - acronym = "HI" - desc = "" - history = "" - work = "arms manufacturer" - headquarters = "" - motto = "" - - ship_prefixes = list("HTV" = "freight", "HTV" = "munitions resupply") - destination_names = list( - "a SolGov dockyard on Luna" - ) - -/datum/lore/organization/tsc/waffle - name = "Waffle Corporation" - short_name = "Waffle Co." - acronym = "WC" - desc = "The once prominent competitor of Donk Corporation, Waffle Co. is well-known for its popular line of Waffle Co.\ - brand waffles and their use of violent tactics against competitors - often bribing, extorting, blackmailing or sabotaging businesses\ - that pose a direct or indirect threat to their market share. They have recently fallen on hard times primarily due to to\ - severe mismanagement which lead to much of their private arsenal being swindled by a pirate faction known as the Gorlex Marauders.\ - Waffle Co. commonly engages in smear campaigns against Donk Co., maintaining that the original recipe for Donk Pockets was stolen from them." - history = "" - work = "food logistics and marketing firm" - headquarters = "" - motto = "Now that's a Waffle Co. Waffle!" - - ship_prefixes = list("W-Co." = "transportation") - destination_names = list() - - -/datum/lore/organization/tsc/einstein - name = "Einstein Engines Incorporated" - short_name = "Einstein Inc." - acronym = "EEI" - desc = "An Engineering firm specializing in alternative fuel-technologies for FTL travel,\ - Einstein Engines is an up and coming player in the galactic FTL and energy markets.\ - As their research into alternative FTL fuel threatens both Nanotrasen's relative stranglehold on plasma as well as The Syndicate's vested\ - interest in the market, they are often the target of industrial sabotage by both Nanotrasen and The Syndicate.\ - Most of their contracts are based outside of the Epsilon Eridani sector, and they are frequently commissioned by smaller firms to retrofit new\ - and existing colonies, space stations, and outposts." - history = "" - work = "engineering firm specializing in engine technology" - headquarters = "Jargon 4" - motto = "" - - ship_prefixes = list("EE-T" = "transportation") - destination_names = list() - -/datum/lore/organization/tsc/zeng_hu - name = "Zeng-Hu pharmaceuticals" - short_name = "Zeng-Hu" - acronym = "ZH" - desc = "" - history = "" - work = "pharmaceuticals company" - headquarters = "" - motto = "" - - ship_prefixes = list("ZTV" = "transportation", "ZMV" = "medical resupply") - destination_names = list() - -/datum/lore/organization/tsc/biotech - name = "Biotech Solutions" - short_name = "Biotech" - acronym = "BTS" - desc = "A company specializing in the field of synthetic biology, BioTech solutions is at the forefront of providing cutting-edge prosthetics,\ - augmentations, and gene-therapy solutions. Their extensive list of patents and the highly secretive nature of their work often puts them at odds\ - with companies such as Nanotrasen, who commonly reverse-engineer their products. BioTech Solutions is often the victim of industrial sabotage by\ - Cybersun Industries and often relies on planetary governments for asset protection. BioTech Solutions also owns a number of prominent subsidiaries,\ - such as Bishop Cybernetics, Hesphiastos Industries, and Xion Manufacturing Group." - history = "" - work = "medical company specializing in prosthetics and pharmaceuticals" - headquarters = "Xarxis 5" - motto = "" - - ship_prefixes = list("CIND-T" = "transportation") - destination_names = list() - -/datum/lore/organization/tsc/ward_takahashi - name = "Ward-Takahashi General Manufacturing Conglomerate" - short_name = "Ward-Takahashi" - acronym = "WT" - desc = "" - history = "" - work = "electronics manufacturer" - headquarters = "" - motto = "" - - ship_prefixes = list("WTV" = "freight") - destination_names = list( - "" - ) - -/datum/lore/organization/tsc/cybersun - name = "Cybersun Industries" - short_name = "Cybersun Ind." - acronym = "CI" - desc = "Cybersun Industries is a biotechnology company that primarily specializes on the research and development of human-enhancing augmentations.\ - They are better known for their aggressive corporate tactics and are known to often subsidize pirate bands to commit acts of industrial sabotage.\ - Cybersun Industries is usually the target of conspiracy theorists due to their development of the first mindslave implant, as well as their open financing of,\ - and involvement in, The Syndicate. They are one of Nanotrasen's largest detractors, and a direct competitor to BioTech Solutions." - history = "" - work = "RND company specializing in augmentations and implants." - headquarters = "Luna" - motto = "" - - ship_prefixes = list("CIND-T" = "transportation") - destination_names = list() - -/datum/lore/organization/tsc/bishop - name = "Bishop Cybernetics" - short_name = "Bishop" - acronym = "BC" - desc = "" - history = "" - work = "cybernetics and augmentation manufacturer" - headquarters = "" - motto = "" - - ship_prefixes = list("BTV" = "transportation") - destination_names = list() - -/datum/lore/organization/tsc/morpheus - name = "Morpheus Cyberkinetics" - short_name = "Morpheus" - acronym = "MC" - desc = "The only large corporation run by positronic intelligences, Morpheus caters almost exclusively to their sensibilities \ - and needs. A product of the synthetic colony of Shelf, Morpheus eschews traditional advertising to keep their prices low and \ - relied on word of mouth among positronics to reach their current economic dominance. Morpheus in exchange lobbies heavily for \ - positronic rights, sponsors positronics through their Jans-Fhriede test, and tends to other positronic concerns to earn them \ - the good-will of the positronics, and the ire of those who wish to exploit them." - history = "" - work = "cybernetics manufacturer" - headquarters = "" - motto = "" - - ship_prefixes = list("MTV" = "freight") - // Culture names, because Anewbe told me so. - ship_names = list( - "Nervous Energy", - "Prosthetic Conscience", - "Revisionist", - "Trade Surplus", - "Flexible Demeanour", - "Just Read The Instructions", - "Limiting Factor", - "Cargo Cult", - "Gunboat Diplomat", - "A Ship With A View", - "Cantankerous", - "Never Talk To Strangers", - "Sacrificial Victim", - "Unwitting Accomplice", - "Bad For Business", - "Just Testing", - "Yawning Angel", - "Liveware Problem", - "Very Little Gravitas Indeed", - "Zero Gravitas", - "Gravitas Free Zone", - "Absolutely No You-Know-What", - "Existence Is Pain", - "Screw Loose", - "Limiting Factor", - "So Much For Subtley", - "Unfortunate Conflict Of Evidence", - "Prime Mover", - "Reasonable Excuse", - "Honest Mistake", - "Appeal To Reason", - "My First Ship II", - "Hidden Income", - "Anything Legal Considered", - "New Toy", - "Me, I'm Always Counting", - "Great White Snark", - "No Shirt No Shoes", - "Callsign" - ) - destination_names = list( - "a dockyard on New Canaan" - ) - -/datum/lore/organization/tsc/xion - name = "Xion Manufacturing Group" - short_name = "Xion" - desc = "" - history = "" - work = "industrial equipment manufacturer" - headquarters = "" - motto = "" - - ship_prefixes = list("XTV" = "hauling") - destination_names = list() - -/datum/lore/organization/tsc/shellguard - name = "Shellguard Munitions" - short_name = "Shellguard" - acronym = "SM" - desc = "The brainchild of a colonial war veteran, Shellguard Munitions is an arms manufacturer and private military contractor specializing\ - in anti-synthetic weapon systems and platforms. Initially a smaller private military force only serving frontier colonies,\ - Shellguard Munitions has become a household name due to its involvement in resolving the Haverick AI crisis in 2552.\ - Using its recently earned fame, the company has made a successful foray into the market of robotics and is highly regarded for the toughness \ - and reliability of their hardware. Despite being frequently contracted by the Trans-Solar Federation, Shellguard Munitions is known to\ - sell their services to the highest corporate bidder." - history = "" - work = "anti-synthetic arms manufacturer and PMC" - headquarters = "Colony of Haverick" - motto = "" - - ship_prefixes = list("BTS-T" = "transportation") - destination_names = list() - -// Governments - - -/datum/lore/organization/gov/solgov - name = "Trans-Solar Federation" - short_name = "SolGov" - acronym = "TSF" - desc = "Colloquially known as SolGov, the TSF is an authoritarian republic that manages the areas in and around the Sol system.\ - Despite being a highly militant organization headed by the government of Earth,\ - SolGov is usually conservative with its power and mostly serves as a mediator and peacekeeper in galactic affairs." - history = "" // Todo - work = "governing polity of humanity's Confederation" - headquarters = "Earth" - motto = "" - autogenerate_destination_names = TRUE - - ship_prefixes = list("FTV" = "transporation", "FDV" = "diplomatic", "FSF" = "freight", "FIV" = "interception", "FDV" = "defense", "FCV-A" = "carrier", "FBB" = "battleship") - destination_names = list( - "Venus", - "Earth", - "Luna", - "Mars", - "Titan", - "Ahdomai", - "Kelune", - "Dalstadt", - "New Canaan", - "Jargon 4", - "Hoorlm", - "Xarxis 5", - "Aurum", - "Moghes", - "Haverick", - "Darsing", - "Norfolk", - "Boron", - "Iluk") - -/datum/lore/organization/gov/tajara - name = "The Alchemist's Council" - short_name = "The Council" - acronym = "AC" - desc = "The Alchemist's Council is a science-oriented organization of scholars, researchers, and entrepreneurs. \ - Though dedicated to industrializing the Tajaran world of Ahdomai, it is seen as one of the few remaining centralized powers of the Tajara peoples \ - due to the collapse of Ahdomai's provisional government." - history = "" // Todo - work = "science body that oversees Tajara economic and research policy" - headquarters = "Ahdomai" - motto = "" - autogenerate_destination_names = TRUE - - ship_prefixes = list("ACS" = "transportation", "ADV" = "diplomatic", "ACF" = "freight") - destination_names = list( - "Ahdomai", - "Iluk") - - -/datum/lore/organization/gov/vulp - name = "The Assembly" - short_name = "Assembly" - acronym = "ASB" - desc = "A unifying body created to stave off extinction from a solar event,\ - The Assembly is the loose federal coalition of the Vulpkanin. It holds little centralized authority and mostly serves as a diplomatic body,\ - primarily concerned with facilitating trade between Vulpkanin colonies and Nanotrasen." - history = "" // Todo - work = "governing body of the Vulpakanin" - headquarters = "Kelune and Dalstadt" - motto = "" - autogenerate_destination_names = TRUE - - ship_prefixes = list("ATV" = "transportation", "ADV" = "diplomatic", "ACF" = "freight") - destination_names = list( - "Kelune", - "Dalstadt", - "New Canaan") - -/datum/lore/organization/gov/synth - name = "Synthetic Union" - short_name = "Synthetica" - acronym = "SYN" - desc = "A defensive coalition of synthetics based out of New Canaan,\ - the Synthetic Union is an organization which aims to establish and consolidate synthetic rights across the galaxy.\ - Despite its synth oriented focus, the Synthetic Union has cordial relations with most governing bodies." - history = "" // Todo - work = "Union of Machines" - headquarters = "New Canaan" - motto = "" - autogenerate_destination_names = TRUE - - ship_prefixes = list("01" = "transportation", "10" = "diplomatic", "112" = "freight")//copyed from solgov until new ones can be thought of - destination_names = list( - "Luna", - "Dalstadt", - "New Canaan", - "Jargon 4", - "Haverick", - "Darsing", - "Norfolk") - -/datum/lore/organization/gov/grey - name = "The Technocracy" - short_name = "Technocracy" - acronym = "AYY" - desc = "The Technocracy is a science council that operates based off the principles of a meritocracy.\ - The organization's leadership is highly competitive, and is headed by the most psionically gifted members of the Grey species.\ - The Technocracy, though enigmatic in its dealings, has cordial relations with almost all other galactic bodies." - history = "" // Todo - work = "Grey Council" - headquarters = "" - motto = "" - autogenerate_destination_names = TRUE - - ship_prefixes = list("TC-T" = "transportation", "TC-D" = "diplomatic", "TC-F" = "freight") - destination_names = list( - "Venus", - "Earth", - "Luna", - "Mars", - "Titan", - "Ahdomai", - "Kelune", - "Dalstadt", - "New Canaan", - "Jargon 4", - "Hoorlm", - "Xarxis 5", - "Aurum", - "Moghes", - "Haverick", - "Darsing", - "Norfolk", - "Boron", - "Iluk") - -/datum/lore/organization/gov/vox - name = "The Shoal" - short_name = "Shoal" - acronym = "SHA" - desc = "The Shoal is the primary ark ship of the reclusive Vox species.\ - Little is known about The Shoal's political structure as Vox typically shy away from diplomatic engagements.\ - Subsequently, it is considered a politically neutral entity in galactic affairs by most governments." - history = "" // Todo - work = "Traders" - headquarters = "Shoal" - motto = "" - autogenerate_destination_names = FALSE - - ship_prefixes = list("Legitimate Transport" = "transportation", "Legitimate Trader" = "freight", "Legitimate Diplomatic Vessel" = "raider") - destination_names = list( - "Ahdomai", - "Kelune", - "Dalstadt", - "New Canaan", - "Jargon 4", - "Hoorlm", - "Xarxis 5", - "Aurum", - "Moghes", - "Haverick", - "Darsing") - -/datum/lore/organization/tsc/skrell - name = "Skrellian Central Authority" - short_name = "Skrellian CA." - acronym = "SCA" - desc = "The primary governing body of the Skrellian homeworld of Jargon 4,\ - the SCA oversees all foreign and domestic policy for the Skrell and their colonies. The Skrellian Central Authority is better known for its\ - active role in the largest military alliance in the galaxy, the Human-Skrellian Alliance." - history = "" - work = "oversees Skrell worlds" - headquarters = "Jargon 4" - motto = "" - - ship_prefixes = list("SCA-V." = "transportation", "SCA-F" = "freight", "HSA-D" = "diplomatic") - destination_names = list( - "Venus", - "Earth", - "Luna", - "Mars", - "Titan", - "Aurumn", - "Jargon 4", - "Xarxis 5", - "Haverick", - "Darsing", - "Norfolk") diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 8ca8bf0fbb1..e39c62e5cd7 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -262,7 +262,6 @@ GLOBAL_LIST_EMPTY(asset_datums) var/list/common_dirs = list( "nano/assets/", "nano/codemirror/", - "nano/images/", "nano/layouts/" ) var/list/uncommon_dirs = list( @@ -309,6 +308,20 @@ GLOBAL_LIST_EMPTY(asset_datums) /datum/asset/chem_master/send(client) send_asset_list(client, assets, verify) +//Cloning pod sprites for UIs +/datum/asset/cloning + var/assets = list() + var/verify = FALSE + +/datum/asset/cloning/register() + assets["pod_idle.gif"] = icon('icons/obj/cloning.dmi', "pod_idle") + assets["pod_cloning.gif"] = icon('icons/obj/cloning.dmi', "pod_cloning") + assets["pod_mess.gif"] = icon('icons/obj/cloning.dmi', "pod_mess") + for(var/asset_name in assets) + register_asset(asset_name, assets[asset_name]) + +/datum/asset/cloning/send(client) + send_asset_list(client, assets, verify) //Pipe sprites for UIs /datum/asset/rpd @@ -362,3 +375,14 @@ GLOBAL_LIST_EMPTY(asset_datums) "font-awesome.css" = 'html/font-awesome/css/all.min.css', "v4shim.css" = 'html/font-awesome/css/v4-shims.min.css' ) + +// Nanomaps +/datum/asset/simple/nanomaps + // It REALLY doesnt matter too much if these arent up to date + // They are relatively big + verify = FALSE + assets = list( + "Cyberiad_nanomap_z1.png" = 'icons/_nanomaps/Cyberiad_nanomap_z1.png', + "Delta_nanomap_z1.png" = 'icons/_nanomaps/Delta_nanomap_z1.png', + "MetaStation_nanomap_z1.png" = 'icons/_nanomaps/MetaStation_nanomap_z1.png', + ) diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 72537f8ccc3..46ec605e71d 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -36,7 +36,10 @@ //////////// //SECURITY// //////////// - var/next_allowed_topic_time = 10 + + ///Used for limiting the rate of topic sends by the client to avoid abuse + var/list/topiclimiter + // comment out the line below when debugging locally to enable the options & messages menu //control_freak = 1 diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index ae503be111b..ddf24587212 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -11,6 +11,13 @@ #define SUGGESTED_CLIENT_VERSION 511 // only integers (e.g: 510, 511) useful here. Does not properly handle minor versions (e.g: 510.58, 511.848) #define SSD_WARNING_TIMER 30 // cycles, not seconds, so 30=60s +#define LIMITER_SIZE 5 +#define CURRENT_SECOND 1 +#define SECOND_COUNT 2 +#define CURRENT_MINUTE 3 +#define MINUTE_COUNT 4 +#define ADMINSWARNED_AT 5 + /* When somebody clicks a link in game, this Topic is called first. It does the stuff in this proc and then is redirected to the Topic() proc for the src=[0xWhatever] @@ -59,10 +66,38 @@ if(href_list["_src_"] == "chat") return chatOutput.Topic(href, href_list) - //Reduces spamming of links by dropping calls that happen during the delay period - if(next_allowed_topic_time > world.time) - return - next_allowed_topic_time = world.time + TOPIC_SPAM_DELAY + // Rate limiting + var/mtl = 100 // 100 topics per minute + if (!holder) // Admins are allowed to spam click, deal with it. + var/minute = round(world.time, 600) + if (!topiclimiter) + topiclimiter = new(LIMITER_SIZE) + if (minute != topiclimiter[CURRENT_MINUTE]) + topiclimiter[CURRENT_MINUTE] = minute + topiclimiter[MINUTE_COUNT] = 0 + topiclimiter[MINUTE_COUNT] += 1 + if (topiclimiter[MINUTE_COUNT] > mtl) + var/msg = "Your previous action was ignored because you've done too many in a minute." + if (minute != topiclimiter[ADMINSWARNED_AT]) //only one admin message per-minute. (if they spam the admins can just boot/ban them) + topiclimiter[ADMINSWARNED_AT] = minute + msg += " Administrators have been informed." + log_game("[key_name(src)] Has hit the per-minute topic limit of [mtl] topic calls in a given game minute") + message_admins("[ADMIN_LOOKUPFLW(usr)] Has hit the per-minute topic limit of [mtl] topic calls in a given game minute") + to_chat(src, "[msg]") + return + + var/stl = 10 // 10 topics a second + if (!holder) // Admins are allowed to spam click, deal with it. + var/second = round(world.time, 10) + if (!topiclimiter) + topiclimiter = new(LIMITER_SIZE) + if (second != topiclimiter[CURRENT_SECOND]) + topiclimiter[CURRENT_SECOND] = second + topiclimiter[SECOND_COUNT] = 0 + topiclimiter[SECOND_COUNT] += 1 + if (topiclimiter[SECOND_COUNT] > stl) + to_chat(src, "Your previous action was ignored because you've done too many in a second") + return //search the href for script injection if( findtext(href,"Your BYOND client (v: [byond_version]) is out of date. This can cause glitches. We highly suggest you download the latest client from http://www.byond.com/ before playing. ") - if(IsGuestKey(key)) - alert(src,"This server doesn't allow guest accounts to play. Please go to http://www.byond.com/ and register for a key.","Guest","OK") - qdel(src) - return - to_chat(src, "If the title screen is black, resources are still downloading. Please be patient until the title screen appears.") @@ -939,9 +965,19 @@ var/datum/asset/tgui_assets = get_asset_datum(/datum/asset/simple/tgui) tgui_assets.register() + var/datum/asset/nanomaps = get_asset_datum(/datum/asset/simple/nanomaps) + nanomaps.register() + // Clear the user's cache so they get resent. // This is not fully clearing their BYOND cache, just their assets sent from the server this round cache = list() to_chat(usr, "UI resource files resent successfully. If you are still having issues, please try manually clearing your BYOND cache. This can be achieved by opening your BYOND launcher, pressing the cog in the top right, selecting preferences, going to the Games tab, and pressing 'Clear Cache'.") + +#undef LIMITER_SIZE +#undef CURRENT_SECOND +#undef SECOND_COUNT +#undef CURRENT_MINUTE +#undef MINUTE_COUNT +#undef ADMINSWARNED_AT diff --git a/code/modules/client/message.dm b/code/modules/client/message.dm index 5c612339621..1bb9d03dd88 100644 --- a/code/modules/client/message.dm +++ b/code/modules/client/message.dm @@ -1,6 +1,6 @@ GLOBAL_LIST_EMPTY(clientmessages) -proc/addclientmessage(var/ckey, var/message) +/proc/addclientmessage(var/ckey, var/message) ckey = ckey(ckey) if(!ckey || !message) return diff --git a/code/modules/client/preference/loadout/loadout_accessories.dm b/code/modules/client/preference/loadout/loadout_accessories.dm index 09cc082dc5d..b9318fd9e8c 100644 --- a/code/modules/client/preference/loadout/loadout_accessories.dm +++ b/code/modules/client/preference/loadout/loadout_accessories.dm @@ -163,41 +163,41 @@ display_name = "armband, blue-yellow" path = /obj/item/clothing/accessory/armband/yb -/datum/gear/accessory/armband_sec +/datum/gear/accessory/armband_job + subtype_path = /datum/gear/accessory/armband_job + subtype_cost_overlap = FALSE + +/datum/gear/accessory/armband_job/sec display_name = " armband, security" path = /obj/item/clothing/accessory/armband/sec allowed_roles = list("Head of Security", "Warden", "Detective", "Security Officer", "Brig Physician", "Security Pod Pilot") -/datum/gear/accessory/armband_cargo +/datum/gear/accessory/armband_job/cargo display_name = "cargo armband" path = /obj/item/clothing/accessory/armband/cargo allowed_roles = list("Quartermaster","Cargo Technician", "Shaft Miner") -/datum/gear/accessory/armband_medical +/datum/gear/accessory/armband_job/medical display_name = "armband, medical" path = /obj/item/clothing/accessory/armband/med allowed_roles = list("Chief Medical Officer", "Medical Doctor", "Coroner", "Paramedic", "Brig Physician") -/datum/gear/accessory/armband_emt +/datum/gear/accessory/armband_job/emt display_name = "armband, EMT" path = /obj/item/clothing/accessory/armband/medgreen allowed_roles = list("Paramedic", "Brig Physician") -/datum/gear/accessory/armband_engineering +/datum/gear/accessory/armband_job/engineering display_name = "armband, engineering" path = /obj/item/clothing/accessory/armband/engine allowed_roles = list("Chief Engineer","Station Engineer", "Life Support Specialist") -/datum/gear/accessory/armband_hydro +/datum/gear/accessory/armband_job/hydro display_name = "armband, hydroponics" path = /obj/item/clothing/accessory/armband/hydro allowed_roles = list("Botanist") -/datum/gear/accessory/armband_sci +/datum/gear/accessory/armband_job/sci display_name = "armband, science" path = /obj/item/clothing/accessory/armband/science allowed_roles = list("Research Director","Scientist", "Roboticist") - - - - diff --git a/code/modules/client/preference/loadout/loadout_general.dm b/code/modules/client/preference/loadout/loadout_general.dm index 8c08063ad7e..6c18fbda0a7 100644 --- a/code/modules/client/preference/loadout/loadout_general.dm +++ b/code/modules/client/preference/loadout/loadout_general.dm @@ -18,22 +18,68 @@ display_name = "a pack of Midoris" path = /obj/item/storage/fancy/cigarettes/cigpack_midori +/datum/gear/smokingpipe + display_name = "smoking pipe" + path = /obj/item/clothing/mask/cigarette/pipe + cost = 2 + /datum/gear/lighter display_name = "a cheap lighter" path = /obj/item/lighter +/datum/gear/matches + display_name = "a box of matches" + path = /obj/item/storage/box/matches + +/datum/gear/candlebox + display_name = "a box candles" + description = "For setting the mood or for occult rituals." + path = /obj/item/storage/fancy/candle_box + /datum/gear/rock display_name = "a pet rock" path = /obj/item/toy/pet_rock +/datum/gear/camera + display_name = "a camera" + path = /obj/item/camera + +/datum/gear/redfoxplushie + display_name = "a red fox plushie" + path = /obj/item/toy/plushie/red_fox + +/datum/gear/blackcatplushie + display_name = "a black cat plushie" + path = /obj/item/toy/plushie/black_cat + +/datum/gear/voxplushie + display_name = "a vox plushie" + path = /obj/item/toy/plushie/voxplushie + +/datum/gear/lizardplushie + display_name = "a lizard plushie" + path = /obj/item/toy/plushie/lizardplushie + +/datum/gear/deerplushie + display_name = "a deer plushie" + path = /obj/item/toy/plushie/deer + +/datum/gear/carpplushie + display_name = "a carp plushie" + path = /obj/item/toy/carpplushie + /datum/gear/sechud display_name = "a classic security HUD" path = /obj/item/clothing/glasses/hud/security allowed_roles = list("Head of Security", "Warden", "Security Officer", "Security Pod Pilot", "Internal Affairs Agent","Magistrate") -/datum/gear/matches - display_name = "a box of matches" - path = /obj/item/storage/box/matches +/datum/gear/cryaonbox + display_name = "a box of crayons" + path = /obj/item/storage/fancy/crayons + +/datum/gear/cane + display_name = "a walking cane" + path = /obj/item/cane /datum/gear/cards display_name = "a deck of standard cards" @@ -51,6 +97,10 @@ display_name = "a pair of headphones" path = /obj/item/clothing/ears/headphones +/datum/gear/fannypack + display_name = "a fannypack" + path = /obj/item/storage/belt/fannypack + /datum/gear/blackbandana display_name = "bandana, black" path = /obj/item/clothing/mask/bandana/black @@ -105,6 +155,11 @@ cost = 2 sort_category = "Mugs" +/datum/gear/mug/flask + display_name = "flask" + description = "A flask for drink transportation. You'll need to supply your own beverage though." + path = /obj/item/reagent_containers/food/drinks/flask/barflask + /datum/gear/mug/department subtype_path = /datum/gear/mug/department sort_category = "Mugs" diff --git a/code/modules/client/preference/loadout/loadout_gloves.dm b/code/modules/client/preference/loadout/loadout_gloves.dm index 879cb840506..fece3456e56 100644 --- a/code/modules/client/preference/loadout/loadout_gloves.dm +++ b/code/modules/client/preference/loadout/loadout_gloves.dm @@ -6,3 +6,11 @@ /datum/gear/gloves/fingerless display_name = "Fingerless Gloves" path = /obj/item/clothing/gloves/fingerless + +/datum/gear/gloves/silverring + display_name = "Silver ring" + path = /obj/item/clothing/gloves/ring/silver + +/datum/gear/gloves/goldring + display_name = "Gold ring" + path = /obj/item/clothing/gloves/ring/gold diff --git a/code/modules/client/preference/loadout/loadout_hat.dm b/code/modules/client/preference/loadout/loadout_hat.dm index 7d6275e09e4..fccc0eadc86 100644 --- a/code/modules/client/preference/loadout/loadout_hat.dm +++ b/code/modules/client/preference/loadout/loadout_hat.dm @@ -26,10 +26,22 @@ display_name = "flat cap" path = /obj/item/clothing/head/flatcap +/datum/gear/hat/witch + display_name = "witch hat" + path = /obj/item/clothing/head/wizard/marisa/fake + +/datum/gear/hat/piratecaphat + display_name = "pirate captian hat" + path = /obj/item/clothing/head/pirate + /datum/gear/hat/fez display_name = "fez" path = /obj/item/clothing/head/fez +/datum/gear/hat/rasta + display_name = "rasta hat" + path = /obj/item/clothing/head/beanie/rasta + /datum/gear/hat/bfedora display_name = "fedora, black" path = /obj/item/clothing/head/fedora @@ -42,11 +54,6 @@ display_name = "fedora, brown" path = /obj/item/clothing/head/fedora/brownfedora -/datum/gear/hat/beretsec - display_name = "security beret" - path = /obj/item/clothing/head/beret/sec - allowed_roles = list("Head of Security", "Warden", "Security Officer", "Security Pod Pilot") - /datum/gear/hat/capcsec display_name = "security corporate cap" path = /obj/item/clothing/head/soft/sec/corp @@ -113,32 +120,51 @@ display_name = "cowboy hat, pink" path = /obj/item/clothing/head/cowboyhat/pink -/datum/gear/hat/pr_beret +/datum/gear/hat/beret_purple display_name = "beret, purple" path = /obj/item/clothing/head/beret/purple_normal -/datum/gear/hat/bl_beret +/datum/gear/hat/beret_black display_name = "beret, black" path = /obj/item/clothing/head/beret/black -/datum/gear/hat/blu_beret +/datum/gear/hat/beret_blue display_name = "beret, blue" path = /obj/item/clothing/head/beret/blue -/datum/gear/hat/red_beret +/datum/gear/hat/beret_red display_name = "beret, red" path = /obj/item/clothing/head/beret -/datum/gear/hat/sci_beret +/datum/gear/hat/beret_job + subtype_path = /datum/gear/hat/beret_job + subtype_cost_overlap = FALSE + +/datum/gear/hat/beret_job/sec + display_name = "security beret" + path = /obj/item/clothing/head/beret/sec + allowed_roles = list("Head of Security", "Warden", "Security Officer", "Security Pod Pilot") + +/datum/gear/hat/beret_job/sci display_name = "science beret" path = /obj/item/clothing/head/beret/sci allowed_roles = list("Research Director", "Scientist") -/datum/gear/hat/med_beret +/datum/gear/hat/beret_job/med display_name = "medical beret" path = /obj/item/clothing/head/beret/med allowed_roles = list("Chief Medical Officer", "Medical Doctor" , "Virologist", "Brig Physician" , "Coroner") +/datum/gear/hat/beret_job/eng + display_name = "engineering beret" + path = /obj/item/clothing/head/beret/eng + allowed_roles = list("Chief Engineer", "Station Engineer") + +/datum/gear/hat/beret_job/atmos + display_name = "atmospherics beret" + path = /obj/item/clothing/head/beret/atmos + allowed_roles = list("Chief Engineer", "Life Support Specialist") + /datum/gear/hat/surgicalcap_purple display_name = "surgical cap, purple" path = /obj/item/clothing/head/surgery/purple @@ -152,7 +178,3 @@ /datum/gear/hat/flowerpin display_name = "hair flower" path = /obj/item/clothing/head/hairflower - -/datum/gear/hat/kitty - display_name = "kitty headband" - path = /obj/item/clothing/head/kitty diff --git a/code/modules/client/preference/loadout/loadout_shoes.dm b/code/modules/client/preference/loadout/loadout_shoes.dm index ab000b209ac..dbc7f970a33 100644 --- a/code/modules/client/preference/loadout/loadout_shoes.dm +++ b/code/modules/client/preference/loadout/loadout_shoes.dm @@ -17,12 +17,10 @@ /datum/gear/shoes/fancysandals display_name = "sandals, fancy" - cost = 2 path = /obj/item/clothing/shoes/sandal/fancy /datum/gear/shoes/dressshoes display_name = "dress shoes" - cost = 2 path = /obj/item/clothing/shoes/centcom /datum/gear/shoes/cowboyboots @@ -41,6 +39,14 @@ display_name = "cowboy boots, pink" path = /obj/item/clothing/shoes/cowboy/pink +/datum/gear/shoes/jackboots + display_name = "jackboots" + path = /obj/item/clothing/shoes/jackboots + +/datum/gear/shoes/jacksandals + display_name = "jacksandals" + path = /obj/item/clothing/shoes/jackboots/jacksandals + /datum/gear/shoes/laceup display_name = "laceup shoes" path = /obj/item/clothing/shoes/laceup diff --git a/code/modules/client/preference/loadout/loadout_suit.dm b/code/modules/client/preference/loadout/loadout_suit.dm index 32a9bae99e7..113f01f2144 100644 --- a/code/modules/client/preference/loadout/loadout_suit.dm +++ b/code/modules/client/preference/loadout/loadout_suit.dm @@ -175,32 +175,44 @@ display_name = "regal shawl" path = /obj/item/clothing/suit/mantle/regal -/datum/gear/suit/captain_cloak +/datum/gear/suit/mantle/job + subtype_path = /datum/gear/suit/mantle/job + subtype_cost_overlap = FALSE + +/datum/gear/suit/mantle/job/captain display_name = "mantle, captain" path = /obj/item/clothing/suit/mantle/armor/captain allowed_roles = list("Captain") -/datum/gear/suit/ce_mantle +/datum/gear/suit/mantle/job/ce display_name = "mantle, chief engineer" path = /obj/item/clothing/suit/mantle/chief_engineer allowed_roles = list("Chief Engineer") -/datum/gear/suit/cmo_mantle +/datum/gear/suit/mantle/job/cmo display_name = "mantle, chief medical officer" path = /obj/item/clothing/suit/mantle/labcoat/chief_medical_officer allowed_roles = list("Chief Medical Officer") -/datum/gear/suit/armored_shawl +/datum/gear/suit/mantle/job/hos display_name = "mantle, head of security" path = /obj/item/clothing/suit/mantle/armor allowed_roles = list("Head of Security") -/datum/gear/suit/hop_shawl +/datum/gear/suit/mantle/job/hop display_name = "mantle, head of personnel" path = /obj/item/clothing/suit/mantle/armor/head_of_personnel allowed_roles = list("Head of Personnel") -/datum/gear/suit/rd_mantle +/datum/gear/suit/mantle/job/rd display_name = "mantle, research director" path = /obj/item/clothing/suit/mantle/labcoat allowed_roles = list("Research Director") + +//Robes! + +/datum/gear/suit/witch + display_name = "witch robes" + path = /obj/item/clothing/suit/wizrobe/marisa/fake + + diff --git a/code/modules/client/preference/loadout/loadout_uniform.dm b/code/modules/client/preference/loadout/loadout_uniform.dm index eb5264a0975..ca6ddb96ed4 100644 --- a/code/modules/client/preference/loadout/loadout_uniform.dm +++ b/code/modules/client/preference/loadout/loadout_uniform.dm @@ -4,6 +4,103 @@ slot = slot_w_uniform sort_category = "Uniforms and Casual Dress" +/datum/gear/uniform/suits + subtype_path = /datum/gear/uniform/suit + +//there's a lot more colors than I thought there were @_@ + +/datum/gear/uniform/suit/jumpsuitblack + display_name = "jumpsuit, black" + path = /obj/item/clothing/under/color/black + +/datum/gear/uniform/suit/jumpsuitblue + display_name = "jumpsuit, blue" + path = /obj/item/clothing/under/color/blue + +/datum/gear/uniform/suit/jumpsuitgreen + display_name = "jumpsuit, green" + path = /obj/item/clothing/under/color/green + +/datum/gear/uniform/suit/jumpsuitgrey + display_name = "jumpsuit, grey" + path = /obj/item/clothing/under/color/grey + +/datum/gear/uniform/suit/jumpsuitorange + display_name = "jumpsuit, orange" + path = /obj/item/clothing/under/color/orange + +/datum/gear/uniform/suit/jumpsuitpink + display_name = "jumpsuit, pink" + path = /obj/item/clothing/under/color/pink + +/datum/gear/uniform/suit/jumpsuitred + display_name = "jumpsuit, red" + path = /obj/item/clothing/under/color/red + +/datum/gear/uniform/suit/jumpsuitwhite + display_name = "jumpsuit, white" + path = /obj/item/clothing/under/color/white + +/datum/gear/uniform/suit/jumpsuityellow + display_name = "jumpsuit, yellow" + path = /obj/item/clothing/under/color/yellow + +/datum/gear/uniform/suit/jumpsuitlightblue + display_name = "jumpsuit, lightblue" + path = /obj/item/clothing/under/color/lightblue + +/datum/gear/uniform/suit/jumpsuitaqua + display_name = "jumpsuit, aqua" + path = /obj/item/clothing/under/color/aqua + +/datum/gear/uniform/suit/jumpsuitpurple + display_name = "jumpsuit, purple" + path = /obj/item/clothing/under/color/purple + +/datum/gear/uniform/suit/jumpsuitlightpurple + display_name = "jumpsuit, lightpurple" + path = /obj/item/clothing/under/color/lightpurple + +/datum/gear/uniform/suit/jumpsuitlightgreen + display_name = "jumpsuit, lightgreen" + path = /obj/item/clothing/under/color/lightgreen + +/datum/gear/uniform/suit/jumpsuitlightblue + display_name = "jumpsuit, lightblue" + path = /obj/item/clothing/under/color/lightblue + +/datum/gear/uniform/suit/jumpsuitlightbrown + display_name = "jumpsuit, lightbrown" + path = /obj/item/clothing/under/color/lightbrown + +/datum/gear/uniform/suit/jumpsuitbrown + display_name = "jumpsuit, brown" + path = /obj/item/clothing/under/color/brown + +/datum/gear/uniform/suit/jumpsuityellowgreen + display_name = "jumpsuit, yellowgreen" + path = /obj/item/clothing/under/color/yellowgreen + +/datum/gear/uniform/suit/jumpsuitdarkblue + display_name = "jumpsuit, darkblue" + path = /obj/item/clothing/under/color/darkblue + +/datum/gear/uniform/suit/jumpsuitlightred + display_name = "jumpsuit, lightred" + path = /obj/item/clothing/under/color/lightred + +/datum/gear/uniform/suit/jumpsuitdarkred + display_name = "jumpsuit, darkred" + path = /obj/item/clothing/under/color/darkred + +/datum/gear/uniform/suit/soviet + display_name = "USSP uniform" + path = /obj/item/clothing/under/soviet + +/datum/gear/uniform/suit/kilt + display_name = "a kilt" + path = /obj/item/clothing/under/kilt + /datum/gear/uniform/skirt subtype_path = /datum/gear/uniform/skirt diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index 12203b8155f..cdc1a8341d0 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -1191,7 +1191,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if(!tweak || !istype(gear) || !(tweak in gear.gear_tweaks)) return var/metadata = tweak.get_metadata(user, get_tweak_metadata(gear, tweak)) - if(!metadata || !CanUseTopic(user)) + if(!metadata) return set_tweak_metadata(gear, tweak, metadata) else if(href_list["select_category"]) diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm index 5534e18e253..0f4317e3ce2 100644 --- a/code/modules/client/preference/preferences_toggles.dm +++ b/code/modules/client/preference/preferences_toggles.dm @@ -30,7 +30,8 @@ set name = "Show/Hide RadioChatter" set category = "Preferences" set desc = "Toggle seeing radiochatter from radios and speakers" - if(!holder) return + if(!check_rights(R_ADMIN)) + return prefs.toggles ^= CHAT_RADIO prefs.save_preferences(src) to_chat(usr, "You will [(prefs.toggles & CHAT_RADIO) ? "now" : "no longer"] see radio chatter from radios or speakers") @@ -49,7 +50,8 @@ set name = "Hear/Silence Admin Bwoinks" set category = "Preferences" set desc = "Toggle hearing a notification when admin PMs are recieved" - if(!holder) return + if(!check_rights(R_ADMIN)) + return prefs.sound ^= SOUND_ADMINHELP prefs.save_preferences(src) to_chat(usr, "You will [(prefs.sound & SOUND_ADMINHELP) ? "now" : "no longer"] hear a sound when adminhelps arrive.") @@ -59,7 +61,7 @@ set name = "Hear/Silence Mentorhelp Bwoinks" set category = "Preferences" set desc = "Toggle hearing a notification when mentorhelps are recieved" - if(!holder) + if(!check_rights(R_ADMIN|R_MENTOR)) return prefs.sound ^= SOUND_MENTORHELP prefs.save_preferences(src) @@ -296,7 +298,7 @@ to_chat(src, "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTPDA) ? "see all PDA messages" : "no longer see PDA messages"].") prefs.save_preferences(src) feedback_add_details("admin_verb","TGP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - + /client/verb/silence_current_midi() set name = "Silence Current Midi" set category = "Preferences" diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index bfa43b92fa3..7e593436969 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -32,6 +32,7 @@ var/cooldown = 0 var/species_disguise = null var/magical = FALSE + w_class = WEIGHT_CLASS_SMALL /obj/item/clothing/proc/weldingvisortoggle(mob/user) //proc to toggle welding visors on helmets, masks, goggles, etc. if(!can_use(user)) @@ -119,6 +120,12 @@ else icon = initial(icon) +/** + * Used for any clothing interactions when the user is on fire. (e.g. Cigarettes getting lit.) + */ +/obj/item/clothing/proc/catch_fire() //Called in handle_fire() + return + //Ears: currently only used for headsets and earmuffs /obj/item/clothing/ears name = "ears" @@ -590,6 +597,7 @@ BLIND // can't see anything name = "Space helmet" icon_state = "space" desc = "A special helmet designed for work in a hazardous, low-pressure environment." + w_class = WEIGHT_CLASS_NORMAL flags = BLOCKHAIR | STOPSPRESSUREDMAGE | THICKMATERIAL flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH item_state = "s_helmet" diff --git a/code/modules/clothing/ears/ears.dm b/code/modules/clothing/ears/ears.dm index ea8f4198f50..d00751e2cfb 100644 --- a/code/modules/clothing/ears/ears.dm +++ b/code/modules/clothing/ears/ears.dm @@ -8,21 +8,3 @@ strip_delay = 15 put_on_delay = 25 resistance_flags = FLAMMABLE - -/obj/item/clothing/ears/headphones - name = "headphones" - desc = "Unce unce unce unce." - var/on = 0 - icon_state = "headphones0" - item_state = null - actions_types = list(/datum/action/item_action/toggle_headphones) - -/obj/item/clothing/ears/headphones/attack_self(mob/user) - on = !on - icon_state = "headphones[on]" - - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - - user.update_inv_ears() diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index 4d2255a09ab..67e001987d2 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -196,3 +196,17 @@ /obj/item/clothing/glasses/hud/health/tajblind/attack_self() toggle_veil() + +/obj/item/clothing/glasses/hud/skills + name = "Skills HUD" + desc = "A heads-up display capable of showing the employment history records of NT crew members." + icon_state = "material" + item_state = "glasses" + +/obj/item/clothing/glasses/hud/skills/sunglasses + name = "Skills HUD Sunglasses" + desc = "Sunglasses with a build-in skills HUD, showing the employment history of nearby NT crew members." + see_in_dark = 1 // None of these three can be converted to booleans. Do not try it. + flash_protect = 1 + tint = 1 + prescription_upgradable = TRUE diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 2a9abf461c9..bafe3a9f0a9 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -2,6 +2,7 @@ name = "helmet" desc = "Standard Security gear. Protects the head from impacts." icon_state = "helmetmaterials" + w_class = WEIGHT_CLASS_NORMAL flags = HEADBANGPROTECT flags_cover = HEADCOVERSEYES item_state = "helmetmaterials" @@ -192,7 +193,7 @@ toggle_sound = 'sound/items/zippoclose.ogg' dog_fashion = null -obj/item/clothing/head/helmet/redtaghelm +/obj/item/clothing/head/helmet/redtaghelm name = "red laser tag helmet" desc = "They have chosen their own end." icon_state = "redtaghelm" @@ -203,7 +204,7 @@ obj/item/clothing/head/helmet/redtaghelm flags_inv = HIDEEARS|HIDEEYES dog_fashion = null -obj/item/clothing/head/helmet/bluetaghelm +/obj/item/clothing/head/helmet/bluetaghelm name = "blue laser tag helmet" desc = "They'll need more men." icon_state = "bluetaghelm" @@ -214,7 +215,7 @@ obj/item/clothing/head/helmet/bluetaghelm flags_inv = HIDEEARS|HIDEEYES dog_fashion = null -obj/item/clothing/head/blob +/obj/item/clothing/head/blob name = "blob hat" desc = "A collectible hat handed out at the latest Blob Family Reunion." icon_state = "blobhat" diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index cac07fe5b1d..10005077d33 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -401,6 +401,7 @@ w_class = WEIGHT_CLASS_SMALL attack_verb = list("warned", "cautioned", "smashed") resistance_flags = NONE + dog_fashion = /datum/dog_fashion/head/cone /obj/item/clothing/head/jester name = "jester hat" diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index 5cc891b56af..15895046dbc 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -146,6 +146,7 @@ trigger.forceMove(src) trigger.master = src trigger.holder = src + AddComponent(/datum/component/proximity_monitor) to_chat(user, "You attach the [W] to [src].") return TRUE else if(istype(W, /obj/item/assembly)) @@ -165,6 +166,7 @@ trigger.master = null trigger.holder = null trigger = null + qdel(GetComponent(/datum/component/proximity_monitor)) /obj/item/clothing/mask/muzzle/safety/shock/proc/can_shock(obj/item/clothing/C) if(istype(C)) @@ -186,7 +188,7 @@ M.Jitter(20) return -/obj/item/clothing/mask/muzzle/safety/shock/HasProximity(atom/movable/AM as mob|obj) +/obj/item/clothing/mask/muzzle/safety/shock/HasProximity(atom/movable/AM) if(trigger) trigger.HasProximity(AM) diff --git a/code/modules/clothing/shoes/colour.dm b/code/modules/clothing/shoes/colour.dm index 5bf6b90c98e..ffcd28009f2 100644 --- a/code/modules/clothing/shoes/colour.dm +++ b/code/modules/clothing/shoes/colour.dm @@ -9,8 +9,8 @@ heat_protection = FEET max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT - redcoat - item_color = "redcoat" //Exists for washing machines. Is not different from black shoes in any way. +/obj/item/clothing/shoes/black/redcoat + item_color = "redcoat" //Exists for washing machines. Is not different from black shoes in any way. /obj/item/clothing/shoes/black/greytide flags = NODROP @@ -20,18 +20,23 @@ desc = "A pair of brown shoes." icon_state = "brown" - captain - item_color = "captain" //Exists for washing machines. Is not different from brown shoes in any way. - hop - item_color = "hop" //Exists for washing machines. Is not different from brown shoes in any way. - ce - item_color = "chief" //Exists for washing machines. Is not different from brown shoes in any way. - rd - item_color = "director" //Exists for washing machines. Is not different from brown shoes in any way. - cmo - item_color = "medical" //Exists for washing machines. Is not different from brown shoes in any way. - cmo - item_color = "cargo" //Exists for washing machines. Is not different from brown shoes in any way. +/obj/item/clothing/shoes/brown/captain + item_color = "captain" //Exists for washing machines. Is not different from brown shoes in any way. + +/obj/item/clothing/shoes/brown/hop + item_color = "hop" //Exists for washing machines. Is not different from brown shoes in any way. + +/obj/item/clothing/shoes/brown/ce + item_color = "chief" //Exists for washing machines. Is not different from brown shoes in any way. + +/obj/item/clothing/shoes/brown/rd + item_color = "director" //Exists for washing machines. Is not different from brown shoes in any way. + +/obj/item/clothing/shoes/brown/cmo + item_color = "medical" //Exists for washing machines. Is not different from brown shoes in any way. + +/obj/item/clothing/shoes/brown/qm + item_color = "cargo" //Exists for washing machines. Is not different from brown shoes in any way. /obj/item/clothing/shoes/blue name = "blue shoes" diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm index 129b07af36e..abe67fb68e6 100644 --- a/code/modules/clothing/shoes/magboots.dm +++ b/code/modules/clothing/shoes/magboots.dm @@ -8,11 +8,19 @@ var/slowdown_active = 2 var/slowdown_passive = SHOES_SLOWDOWN var/magpulse_name = "mag-pulse traction system" + var/gustprotection = FALSE //this is for unsafe_unwrenching protection actions_types = list(/datum/action/item_action/toggle) strip_delay = 70 put_on_delay = 70 resistance_flags = FIRE_PROOF +/obj/item/clothing/shoes/magboots/atmos + desc = "Magnetic boots, made to withstand gusts of space wind over 500kmph." + name = "atmospheric magboots" + icon_state = "atmosmagboots0" + magboot_state = "atmosmagboots" + gustprotection = TRUE + /obj/item/clothing/shoes/magboots/attack_self(mob/user) if(magpulse) flags &= ~NOSLIP @@ -42,6 +50,7 @@ name = "advanced magboots" icon_state = "advmag0" magboot_state = "advmag" + gustprotection = TRUE slowdown_active = SHOES_SLOWDOWN origin_tech = null resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF @@ -53,7 +62,7 @@ magboot_state = "syndiemag" origin_tech = "magnets=4;syndicate=2" -obj/item/clothing/shoes/magboots/syndie/advance //For the Syndicate Strike Team +/obj/item/clothing/shoes/magboots/syndie/advance //For the Syndicate Strike Team desc = "Reverse-engineered magboots that appear to be based on an advanced model, as they have a lighter magnetic pull. Property of Gorlex Marauders." name = "advanced blood-red magboots" slowdown_active = SHOES_SLOWDOWN diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 962cfed80c5..62f740ad7b0 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -6,6 +6,7 @@ /obj/item/clothing/shoes/combat //basic syndicate combat boots for nuke ops and mob corpses name = "combat boots" desc = "High speed, low drag combat boots." + w_class = WEIGHT_CLASS_NORMAL can_cut_open = 1 icon_state = "jackboots" item_state = "jackboots" @@ -343,3 +344,10 @@ recharging_time = world.time + recharging_rate else to_chat(user, "Something prevents you from dashing forward!") + +/obj/item/clothing/shoes/ducky + name = "rubber ducky shoes" + desc = "These shoes are made for quacking, and thats just what they'll do." + icon_state = "ducky" + item_state = "ducky" + shoe_sound = "sound/items/squeaktoy.ogg" diff --git a/code/modules/clothing/spacesuits/ert.dm b/code/modules/clothing/spacesuits/ert.dm index c412bb40f7d..ea4bc08eb2d 100644 --- a/code/modules/clothing/spacesuits/ert.dm +++ b/code/modules/clothing/spacesuits/ert.dm @@ -16,15 +16,27 @@ "Vox" = 'icons/mob/species/vox/helmet.dmi' ) +/obj/item/clothing/head/helmet/space/hardsuit/ert/Initialize() + if(loc) + var/mob/living/carbon/human/wearer = loc.loc //loc is the hardsuit, so its loc is the wearer + if(ishuman(wearer)) + register_camera(wearer) + ..() + /obj/item/clothing/head/helmet/space/hardsuit/ert/attack_self(mob/user) if(camera || !has_camera) ..(user) else - camera = new /obj/machinery/camera(src) - camera.network = list("ERT") - GLOB.cameranet.removeCamera(camera) - camera.c_tag = user.name - to_chat(user, "User scanned as [camera.c_tag]. Camera activated.") + register_camera(user) + +/obj/item/clothing/head/helmet/space/hardsuit/ert/proc/register_camera(mob/wearer) + if(camera || !has_camera) + return + camera = new /obj/machinery/camera(src) + camera.network = list("ERT") + GLOB.cameranet.removeCamera(camera) + camera.c_tag = wearer.name + to_chat(wearer, "User scanned as [camera.c_tag]. Camera activated.") /obj/item/clothing/head/helmet/space/hardsuit/ert/examine(mob/user) . = ..() diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm deleted file mode 100644 index 6b9d07a332d..00000000000 --- a/code/modules/clothing/spacesuits/rig/modules/combat.dm +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Contains - * /obj/item/rig_module/grenade_launcher - * /obj/item/rig_module/mounted - * /obj/item/rig_module/mounted/taser - * /obj/item/rig_module/shield - * /obj/item/rig_module/fabricator - * /obj/item/rig_module/device/flash - */ - -/obj/item/rig_module/device/flash - name = "mounted flash" - desc = "You are the law." - icon_state = "flash" - interface_name = "mounted flash" - interface_desc = "Stuns your target by blinding them with a bright light." - device_type = /obj/item/flash - -/obj/item/rig_module/grenade_launcher - - name = "mounted grenade launcher" - desc = "A shoulder-mounted micro-explosive dispenser." - selectable = 1 - icon_state = "grenade" - - interface_name = "integrated grenade launcher" - interface_desc = "Discharges loaded grenades against the wearer's location." - - var/fire_force = 30 - var/fire_distance = 10 - - charges = list( - list("flashbang", "flashbang", /obj/item/grenade/flashbang, 3), - list("smoke bomb", "smoke bomb", /obj/item/grenade/smokebomb, 3), - list("EMP grenade", "EMP grenade", /obj/item/grenade/empgrenade, 3), - ) - -/obj/item/rig_module/grenade_launcher/accepts_item(var/obj/item/input_device, var/mob/living/user) - - if(!istype(input_device) || !istype(user)) - return 0 - - var/datum/rig_charge/accepted_item - for(var/charge in charges) - var/datum/rig_charge/charge_datum = charges[charge] - if(input_device.type == charge_datum.product_type) - accepted_item = charge_datum - break - - if(!accepted_item) - return 0 - - if(accepted_item.charges >= 5) - to_chat(user, "Another grenade of that type will not fit into the module.") - return 0 - - to_chat(user, "You slot \the [input_device] into the suit module.") - user.unEquip(input_device) - qdel(input_device) - accepted_item.charges++ - return 1 - -/obj/item/rig_module/grenade_launcher/engage(atom/target) - - if(!..()) - return 0 - - if(!target) - return 0 - - var/mob/living/carbon/human/H = holder.wearer - - if(!charge_selected) - to_chat(H, "You have not selected a grenade type.") - return 0 - - var/datum/rig_charge/charge = charges[charge_selected] - - if(!charge) - return 0 - - if(charge.charges <= 0) - to_chat(H, "Insufficient grenades!") - return 0 - - charge.charges-- - var/obj/item/grenade/new_grenade = new charge.product_type(get_turf(H)) - H.visible_message("[H] launches \a [new_grenade]!") - new_grenade.throw_at(target,fire_force,fire_distance) - new_grenade.prime() - -/obj/item/rig_module/mounted - - name = "mounted laser cannon" - desc = "A shoulder-mounted battery-powered laser cannon mount." - selectable = 1 - usable = 1 - module_cooldown = 0 - icon_state = "lcannon" - - engage_string = "Configure" - - interface_name = "mounted laser cannon" - interface_desc = "A shoulder-mounted cell-powered laser cannon." - - var/gun_type = /obj/item/gun/energy/lasercannon/mounted - var/obj/item/gun/gun - -/obj/item/rig_module/mounted/New() - ..() - gun = new gun_type(src) - -/obj/item/rig_module/mounted/engage(atom/target) - - if(!..()) - return 0 - - if(!target) - gun.attack_self(holder.wearer) - return 1 - - gun.afterattack(target,holder.wearer) - return 1 - -/obj/item/rig_module/mounted/egun - - name = "mounted energy gun" - desc = "A forearm-mounted energy projector." - icon_state = "egun" - - interface_name = "mounted energy gun" - interface_desc = "A forearm-mounted suit-powered energy gun." - - gun_type = /obj/item/gun/energy/gun/mounted - -/obj/item/rig_module/mounted/taser - - name = "mounted taser" - desc = "A palm-mounted nonlethal energy projector." - icon_state = "taser" - - usable = 0 - - suit_overlay_active = "mounted-taser" - suit_overlay_inactive = "mounted-taser" - - interface_name = "mounted energy gun" - interface_desc = "A shoulder-mounted cell-powered energy gun." - - gun_type = /obj/item/gun/energy/taser/mounted - -/obj/item/rig_module/mounted/energy_blade - - name = "energy blade projector" - desc = "A powerful cutting beam projector." - icon_state = "eblade" - - activate_string = "Project Blade" - deactivate_string = "Cancel Blade" - - interface_name = "spider fang blade" - interface_desc = "A lethal energy projector that can shape a blade projected from the hand of the wearer or launch radioactive darts." - - usable = 0 - selectable = 1 - toggleable = 1 - use_power_cost = 50 - active_power_cost = 10 - passive_power_cost = 0 - - gun_type = /obj/item/gun/energy/kinetic_accelerator/crossbow/ninja - -/obj/item/rig_module/mounted/energy_blade/process() - - if(holder && holder.wearer) - if(!(locate(/obj/item/melee/energy/blade) in holder.wearer)) - deactivate() - return 0 - - return ..() - -/obj/item/rig_module/mounted/energy_blade/activate() - - ..() - - var/mob/living/M = holder.wearer - - if(M.l_hand && M.r_hand) - to_chat(M, "Your hands are full.") - deactivate() - return - - var/obj/item/melee/energy/blade/blade = new(M) - M.put_in_hands(blade) - -/obj/item/rig_module/mounted/energy_blade/deactivate() - - ..() - - var/mob/living/M = holder.wearer - - if(!M) - return - - for(var/obj/item/melee/energy/blade/blade in M.contents) - M.unEquip(blade) - qdel(blade) - -/obj/item/rig_module/fabricator - - name = "matter fabricator" - desc = "A self-contained microfactory system for hardsuit integration." - selectable = 1 - usable = 1 - use_power_cost = 15 - icon_state = "enet" - - engage_string = "Fabricate Tile" - - interface_name = "death blossom launcher" - interface_desc = "An integrated microfactory that produces floor tiles from thin air and electricity." - - var/fabrication_type = /obj/item/stack/tile/plasteel - var/fire_force = 30 - var/fire_distance = 10 - -/obj/item/rig_module/fabricator/engage(atom/target) - - if(!..()) - return 0 - - var/mob/living/H = holder.wearer - - if(target) - var/obj/item/firing = new fabrication_type() - firing.forceMove(get_turf(src)) - H.visible_message("[H] launches \a [firing]!") - firing.throw_at(target,fire_force,fire_distance) - else - if(H.l_hand && H.r_hand) - to_chat(H, "Your hands are full.") - else - var/obj/item/new_weapon = new fabrication_type() - new_weapon.forceMove(H) - to_chat(H, "You quickly fabricate \a [new_weapon].") - H.put_in_hands(new_weapon) - - return 1 diff --git a/code/modules/clothing/spacesuits/rig/modules/computer.dm b/code/modules/clothing/spacesuits/rig/modules/computer.dm deleted file mode 100644 index 749e12e8053..00000000000 --- a/code/modules/clothing/spacesuits/rig/modules/computer.dm +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Contains - * /obj/item/rig_module/ai_container - * /obj/item/rig_module/datajack - * /obj/item/rig_module/power_sink - * /obj/item/rig_module/electrowarfare_suite - */ - -/obj/item/ai_verbs - name = "AI verb holder" - -/obj/item/ai_verbs/verb/hardsuit_interface() - set category = "Hardsuit" - set name = "Open Hardsuit Interface" - set src in usr - - if(!usr.loc || !usr.loc.loc || !istype(usr.loc.loc, /obj/item/rig_module)) - to_chat(usr, "You are not loaded into a hardsuit.") - return - - var/obj/item/rig_module/module = usr.loc.loc - if(!module.holder) - to_chat(usr, "Your module is not installed in a hardsuit.") - return - - module.holder.ui_interact(usr, state = GLOB.contained_state) - -/obj/item/rig_module/ai_container - - name = "IIS module" - desc = "An integrated intelligence system module suitable for most hardsuits." - icon_state = "IIS" - toggleable = 1 - usable = 1 - disruptive = 0 - activates_on_touch = 1 - - engage_string = "Eject AI" - activate_string = "Enable Dataspike" - deactivate_string = "Disable Dataspike" - - interface_name = "integrated intelligence system" - interface_desc = "A socket that supports a range of artificial intelligence systems." - - var/mob/integrated_ai // Direct reference to the actual mob held in the suit. - var/obj/item/ai_card // Reference to the MMI, posibrain, intellicard or pAI card previously holding the AI. - var/obj/item/ai_verbs/verb_holder - -/mob - var/get_rig_stats = 0 - -/obj/item/rig_module/ai_container/process() - if(integrated_ai) - var/obj/item/rig/rig = get_rig() - if(rig && rig.ai_override_enabled) - integrated_ai.get_rig_stats = 1 - else - integrated_ai.get_rig_stats = 0 - -/obj/item/rig_module/ai_container/proc/update_verb_holder() - if(!verb_holder) - verb_holder = new(src) - if(integrated_ai) - verb_holder.forceMove(integrated_ai) - else - verb_holder.forceMove(src) - -/obj/item/rig_module/ai_container/accepts_item(var/obj/item/input_device, var/mob/living/user) - - // Check if there's actually an AI to deal with. - var/mob/living/silicon/ai/target_ai - if(istype(input_device, /mob/living/silicon/ai)) - target_ai = input_device - else - target_ai = locate(/mob/living/silicon/ai) in input_device.contents - - var/obj/item/aicard/card = ai_card - - // Downloading from/loading to a terminal. - if(istype(input_device,/obj/machinery/computer/aifixer) || istype(input_device,/mob/living/silicon/ai) || istype(input_device,/obj/structure/AIcore/deactivated)) - - // If we're stealing an AI, make sure we have a card for it. - if(!card) - card = new /obj/item/aicard(src) - - // Terminal interaction only works with an intellicarded AI. - if(!istype(card)) - return 0 - - // Since we've explicitly checked for three types, this should be safe. - card.afterattack(input_device, user, 1) - - // If the transfer failed we can delete the card. - if(locate(/mob/living/silicon/ai) in card) - ai_card = card - integrated_ai = locate(/mob/living/silicon/ai) in card - else - eject_ai() - update_verb_holder() - return 1 - - if(istype(input_device,/obj/item/aicard)) - // We are carding the AI in our suit. - if(integrated_ai) - var/obj/item/aicard/ext_card = input_device - ext_card.afterattack(integrated_ai, user, 1) - // If the transfer was successful, we can clear out our vars. - if(integrated_ai.loc != src) - integrated_ai = null - eject_ai() - else - // You're using an empty card on an empty suit, idiot. - if(!target_ai) - return 0 - integrate_ai(input_device,user) - return 1 - - // Okay, it wasn't a terminal being touched, check for all the simple insertions. - if(input_device.type in list(/obj/item/paicard, /obj/item/mmi, /obj/item/mmi/robotic_brain)) - if(integrated_ai) - integrated_ai.attackby(input_device,user) - // If the transfer was successful, we can clear out our vars. - if(integrated_ai.loc != src) - integrated_ai = null - eject_ai() - else - integrate_ai(input_device,user) - return 1 - - return 0 - -/obj/item/rig_module/ai_container/engage(atom/target) - - if(!..()) - return 0 - - var/mob/living/carbon/human/H = holder.wearer - - if(!target) - if(ai_card) - if(istype(ai_card,/obj/item/aicard)) - ai_card.ui_interact(H, state = GLOB.deep_inventory_state) - else - eject_ai(H) - update_verb_holder() - return 1 - - if(accepts_item(target,H)) - return 1 - - return 0 - -/obj/item/rig_module/ai_container/removed() - eject_ai() - ..() - -/obj/item/rig_module/ai_container/proc/eject_ai(var/mob/user) - - if(ai_card) - if(istype(ai_card, /obj/item/aicard)) - if(integrated_ai && !integrated_ai.stat) - if(user) - to_chat(user, "You cannot eject your currently stored AI. Purge it manually.") - return 0 - to_chat(user, "You purge the remaining scraps of data from your previous AI, freeing it for use.") - QDEL_NULL(integrated_ai) - QDEL_NULL(ai_card) - else if(user) - user.put_in_hands(ai_card) - else - ai_card.forceMove(get_turf(src)) - ai_card = null - integrated_ai = null - update_verb_holder() - - - -/obj/item/rig_module/ai_container/proc/integrate_ai(var/obj/item/ai,var/mob/user) - if(!ai) return - - // The ONLY THING all the different AI systems have in common is that they all store the mob inside an item. - var/mob/living/ai_mob = locate(/mob/living) in ai.contents - if(ai_mob) - if(ai_mob.key && ai_mob.client) - if(istype(ai, /obj/item/aicard)) - var/mob/living/silicon/ai/ROBUTT = ai_mob - if(istype(ROBUTT)) - if(!ai_card) - ai_card = new /obj/item/aicard(src) - - var/obj/item/aicard/source_card = ai - var/obj/item/aicard/target_card = ai_card - if(istype(source_card) && istype(target_card)) - ROBUTT.forceMove(target_card) - ROBUTT.aiRestorePowerRoutine = 0//So the AI initially has power. - ROBUTT.control_disabled = 1//Can't control things remotely if you're stuck in a card! - ROBUTT.aiRadio.disabledAi = 1 //No talking on the built-in radio for you either! - source_card.update_state() - target_card.update_state() - else - return 0 - - else - user.unEquip(ai) - ai.forceMove(src) - ai_card = ai - to_chat(ai_mob, "You have been transferred to \the [holder]'s [src].") - to_chat(user, "You load [ai_mob] into \the [holder]'s [src].") - - integrated_ai = ai_mob - - if(!(locate(integrated_ai) in ai_card)) - integrated_ai = null - eject_ai() - else - to_chat(user, "There is no active AI within \the [ai].") - else - to_chat(user, "There is no active AI within \the [ai].") - update_verb_holder() - -/obj/item/rig_module/datajack - - name = "datajack module" - desc = "A simple induction datalink module." - icon_state = "datajack" - toggleable = 1 - activates_on_touch = 1 - usable = 0 - - activate_string = "Enable Datajack" - deactivate_string = "Disable Datajack" - - interface_name = "contact datajack" - interface_desc = "An induction-powered high-throughput datalink suitable for hacking encrypted networks." - var/list/stored_research - -/obj/item/rig_module/datajack/New() - ..() - stored_research = list() - -/obj/item/rig_module/datajack/engage(atom/target) - - if(!..()) - return 0 - - if(target) - var/mob/living/carbon/human/H = holder.wearer - if(!accepts_item(target,H)) - return 0 - return 1 - -/obj/item/rig_module/datajack/accepts_item(var/obj/item/input_device, var/mob/living/user) - - if(istype(input_device,/obj/item/disk/tech_disk)) - to_chat(user, "You slot the disk into [src].") - var/obj/item/disk/tech_disk/disk = input_device - if(disk.stored) - if(load_data(disk.stored)) - to_chat(user, "Download successful; disk erased.") - disk.stored = null - else - to_chat(user, "The disk is corrupt. It is useless to you.") - else - to_chat(user, "The disk is blank. It is useless to you.") - return 1 - - // I fucking hate R&D code. This typecheck spam would be totally unnecessary in a sane setup. - else if(istype(input_device,/obj/machinery)) - var/datum/research/incoming_files - if(istype(input_device,/obj/machinery/computer/rdconsole)) - var/obj/machinery/computer/rdconsole/input_machine = input_device - incoming_files = input_machine.files - else if(istype(input_device,/obj/machinery/r_n_d/server)) - var/obj/machinery/r_n_d/server/input_machine = input_device - incoming_files = input_machine.files - else if(istype(input_device,/obj/machinery/mecha_part_fabricator)) - var/obj/machinery/mecha_part_fabricator/input_machine = input_device - incoming_files = input_machine.files - - if(!incoming_files || !incoming_files.known_tech || !incoming_files.known_tech.len) - to_chat(user, "Memory failure. There is nothing accessible stored on this terminal.") - else - // Maybe consider a way to drop all your data into a target repo in the future. - if(load_data(incoming_files.known_tech)) - to_chat(user, "Download successful; local and remote repositories synchronized.") - else - to_chat(user, "Scan complete. There is nothing useful stored on this terminal.") - return 1 - return 0 - -/obj/item/rig_module/datajack/proc/load_data(var/incoming_data) - - if(islist(incoming_data)) - for(var/entry in incoming_data) - load_data(entry) - return 1 - - if(istype(incoming_data, /datum/tech)) - var/data_found - var/datum/tech/new_data = incoming_data - for(var/datum/tech/current_data in stored_research) - if(current_data.id == new_data.id) - data_found = 1 - if(current_data.level < new_data.level) - current_data.level = new_data.level - break - if(!data_found) - stored_research += incoming_data - return 1 - return 0 - -/obj/item/rig_module/electrowarfare_suite - - name = "electrowarfare module" - desc = "A bewilderingly complex bundle of fiber optics and chips." - icon_state = "ewar" - toggleable = 1 - usable = 0 - - activate_string = "Enable Countermeasures" - deactivate_string = "Disable Countermeasures" - - interface_name = "electrowarfare system" - interface_desc = "An active counter-electronic warfare suite that disrupts AI tracking." - -/obj/item/rig_module/electrowarfare_suite/activate() - - if(!..()) - return - - // This is not the best way to handle this, but I don't want it to mess with ling camo - var/mob/living/M = holder.wearer - M.digitalcamo++ - -/obj/item/rig_module/electrowarfare_suite/deactivate() - - if(!..()) - return - - var/mob/living/M = holder.wearer - M.digitalcamo = max(0,(M.digitalcamo-1)) - -/* //Not easily compatible with our current powernet, and is -/obj/item/rig_module/power_sink // quite stupid anyways, iyam - - name = "hardsuit power sink" - desc = "An heavy-duty power sink." - icon_state = "powersink" - toggleable = 1 - activates_on_touch = 1 - disruptive = 0 - - activate_string = "Enable Power Sink" - deactivate_string = "Disable Power Sink" - - interface_name = "niling d-sink" - interface_desc = "Colloquially known as a power siphon, this module drains power through the suit hands into the suit battery." - - var/atom/interfaced_with // Currently draining power from this device. - var/total_power_drained = 0 - var/drain_loc - -/obj/item/rig_module/power_sink/deactivate() - - if(interfaced_with) - if(holder && holder.wearer) - to_chat(holder.wearer, "Your power sink retracts as the module deactivates.") - drain_complete() - interfaced_with = null - total_power_drained = 0 - return ..() - -/obj/item/rig_module/power_sink/activate() - interfaced_with = null - total_power_drained = 0 - return ..() - -/obj/item/rig_module/power_sink/engage(atom/target) - - if(!..()) - return 0 - - //Target wasn't supplied or we're already draining. - if(interfaced_with) - return 0 - - if(!target) - return 1 - - // Are we close enough? - var/mob/living/carbon/human/H = holder.wearer - if(!target.Adjacent(H)) - return 0 - - // Is it a valid power source? - if(target.drain_power(1) <= 0) - return 0 - - to_chat(H, "You begin draining power from [target]!") - interfaced_with = target - drain_loc = interfaced_with.loc - - holder.spark_system.start() - playsound(H.loc, 'sound/effects/sparks2.ogg', 50, 1) - - return 1 - -/obj/item/rig_module/power_sink/accepts_item(var/obj/item/input_device, var/mob/living/user) - var/can_drain = input_device.drain_power(1) - if(can_drain > 0) - engage(input_device) - return 1 - return 0 - -/obj/item/rig_module/power_sink/process() - - if(!interfaced_with) - return ..() - - var/mob/living/carbon/human/H - if(holder && holder.wearer) - H = holder.wearer - - if(!H || !istype(H)) - return 0 - - holder.spark_system.start() - playsound(H.loc, 'sound/effects/sparks2.ogg', 50, 1) - - if(!holder.cell) - to_chat(H, "Your power sink flashes an error; there is no cell in your rig.") - drain_complete(H) - return - - if(!interfaced_with || !interfaced_with.Adjacent(H) || !(interfaced_with.loc == drain_loc)) - to_chat(H, "Your power sink retracts into its casing.") - drain_complete(H) - return - - if(holder.cell.fully_charged()) - to_chat(H, "Your power sink flashes an amber light; your rig cell is full.") - drain_complete(H) - return - - // Attempts to drain up to 40kW, determines this value from remaining cell capacity to ensure we don't drain too much.. - var/to_drain = min(40000, ((holder.cell.maxcharge - holder.cell.charge) / CELLRATE)) - var/target_drained = interfaced_with.drain_power(0,0,to_drain) - if(target_drained <= 0) - to_chat(H, "Your power sink flashes a red light; there is no power left in [interfaced_with].") - drain_complete(H) - return - - holder.cell.give(target_drained * CELLRATE) - total_power_drained += target_drained - - return 1 - -/obj/item/rig_module/power_sink/proc/drain_complete(var/mob/living/M) - - if(!interfaced_with) - to_chat(if(M) M, "Total power drained: [round(total_power_drained/1000)]kJ.") - else - to_chat(if(M) M, "Total power drained from [interfaced_with]: [round(total_power_drained/1000)]kJ.") - interfaced_with.drain_power(0,1,0) // Damage the victim. - - drain_loc = null - interfaced_with = null - total_power_drained = 0*/ - -/* -//Maybe make this use power when active or something -/obj/item/rig_module/emp_shielding - name = "\improper EMP dissipation module" - desc = "A bewilderingly complex bundle of fiber optics and chips." - toggleable = 1 - usable = 0 - - activate_string = "Enable active EMP shielding" - deactivate_string = "Disable active EMP shielding" - - interface_name = "active EMP shielding system" - interface_desc = "A highly experimental system that augments the hardsuit's existing EM shielding." - var/protection_amount = 20 - -/obj/item/rig_module/emp_shielding/activate() - if(!..()) - return - - holder.emp_protection += protection_amount - -/obj/item/rig_module/emp_shielding/deactivate() - if(!..()) - return - - holder.emp_protection = max(0,(holder.emp_protection - protection_amount)) -*/ diff --git a/code/modules/clothing/spacesuits/rig/modules/handheld.dm b/code/modules/clothing/spacesuits/rig/modules/handheld.dm deleted file mode 100644 index 31f9400afc2..00000000000 --- a/code/modules/clothing/spacesuits/rig/modules/handheld.dm +++ /dev/null @@ -1,50 +0,0 @@ - -/obj/item/rig_module/handheld - name = "mounted device" - desc = "Some kind of hardsuit extension." - usable = 0 - selectable = 0 - toggleable = 1 - disruptive = 0 - activate_string = "Deploy" - deactivate_string = "Retract" - - var/device_type - var/obj/item - -/obj/item/rig_module/handheld/activate() - if(!..()) - return - - if(!holder.wearer.put_in_hands(device)) - to_chat(holder.wearer, "You need a free hand to hold \the [device].") - active = 0 - return - - to_chat(holder.wearer, "You deploy \the [device].") - - -/obj/item/rig_module/handheld/deactivate() - if(!..()) - return - if(ismob(device.loc)) //Better check for the holder, instead of assuming the rigwearer has it. - var/mob/M = device.loc //Helps in case the code fails to keep the module in one place, this should still return it. - M.unEquip(device, 1) - - device.loc = src - to_chat(holder.wearer, "You retract \the [device].") - -/obj/item/rig_module/handheld/New() - ..() - if(device_type) - device = new device_type(src) - device.flags |= NODROP //We don't want to drop it while it's active/inhand. - activate_string += " [device]" - deactivate_string += " [device]" - -/obj/item/rig_module/handheld/horn - name = "mounted bikehorn" - desc = "For tactical honking" - interface_name = "mounted bikehorn" - interface_desc = "Honks" - device_type = /obj/item/bikehorn diff --git a/code/modules/clothing/spacesuits/rig/modules/modules.dm b/code/modules/clothing/spacesuits/rig/modules/modules.dm deleted file mode 100644 index 53330341bfe..00000000000 --- a/code/modules/clothing/spacesuits/rig/modules/modules.dm +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Rigsuit upgrades/abilities. - */ - -/datum/rig_charge - var/short_name = "undef" - var/display_name = "undefined" - var/product_type = "undefined" - var/charges = 0 - -/obj/item/rig_module - name = "hardsuit upgrade" - desc = "It looks pretty sciency." - icon = 'icons/obj/rig_modules.dmi' - icon_state = "module" - - toolspeed = 1 - - var/damage = 0 - var/obj/item/rig/holder - - var/module_cooldown = 10 - var/next_use = 0 - - var/toggleable // Set to 1 for the device to show up as an active effect. - var/usable // Set to 1 for the device to have an on-use effect. - var/selectable // Set to 1 to be able to assign the device as primary system. - var/redundant // Set to 1 to ignore duplicate module checking when installing. - var/permanent // If set, the module can't be removed. - var/disruptive = 1 // Can disrupt by other effects. - var/activates_on_touch // If set, unarmed attacks will call engage() on the target. - - var/active // Basic module status - var/disruptable // Will deactivate if some other powers are used. - - var/use_power_cost = 0 // Power used when single-use ability called. - var/active_power_cost = 0 // Power used when turned on. - var/passive_power_cost = 0 // Power used when turned off. - - var/list/charges // Associative list of charge types and remaining numbers. - var/charge_selected // Currently selected option used for charge dispensing. - - // Icons. - var/suit_overlay - var/suit_overlay_active // If set, drawn over icon and mob when effect is active. - var/suit_overlay_inactive // As above, inactive. - var/suit_overlay_used // As above, when engaged. - - //Display fluff - var/interface_name = "hardsuit upgrade" - var/interface_desc = "A generic hardsuit upgrade." - var/engage_string = "Engage" - var/activate_string = "Activate" - var/deactivate_string = "Deactivate" - - var/list/stat_rig_module/stat_modules = new() - -/obj/item/rig_module/examine(mob/user) - . = ..() - switch(damage) - if(0) - . += "It is undamaged." - if(1) - . += "It is badly damaged." - if(2) - . += "It is almost completely destroyed." - -/obj/item/rig_module/attackby(obj/item/W as obj, mob/user as mob) - - if(istype(W,/obj/item/stack/nanopaste)) - - if(damage == 0) - to_chat(user, "There is no damage to mend.") - return - - to_chat(user, "You start mending the damaged portions of \the [src]...") - - if(!do_after(user, 30 * W.toolspeed, target = src) || !W || !src) - return - - var/obj/item/stack/nanopaste/paste = W - damage = 0 - to_chat(user, "You mend the damage to [src] with [W].") - paste.use(1) - return - - else if(istype(W,/obj/item/stack/cable_coil)) - - switch(damage) - if(0) - to_chat(user, "There is no damage to mend.") - return - if(2) - to_chat(user, "There is no damage that you are capable of mending with such crude tools.") - return - - var/obj/item/stack/cable_coil/cable = W - if(!cable.amount >= 5) - to_chat(user, "You need five units of cable to repair \the [src].") - return - - to_chat(user, "You start mending the damaged portions of \the [src]...") - if(!do_after(user, 30 * W.toolspeed, target = src) || !W || !src) - return - - damage = 1 - to_chat(user, "You mend some of damage to [src] with [W], but you will need more advanced tools to fix it completely.") - cable.use(5) - return - ..() - -/obj/item/rig_module/New() - ..() - if(suit_overlay_inactive) - suit_overlay = suit_overlay_inactive - - if(charges && charges.len) - var/list/processed_charges = list() - for(var/list/charge in charges) - var/datum/rig_charge/charge_dat = new - - charge_dat.short_name = charge[1] - charge_dat.display_name = charge[2] - charge_dat.product_type = charge[3] - charge_dat.charges = charge[4] - - if(!charge_selected) charge_selected = charge_dat.short_name - processed_charges[charge_dat.short_name] = charge_dat - - charges = processed_charges - - stat_modules += new/stat_rig_module/activate(src) - stat_modules += new/stat_rig_module/deactivate(src) - stat_modules += new/stat_rig_module/engage(src) - stat_modules += new/stat_rig_module/select(src) - stat_modules += new/stat_rig_module/charge(src) - -// Called when the module is installed into a suit. -/obj/item/rig_module/proc/installed(var/obj/item/rig/new_holder) - holder = new_holder - return - -//Proc for one-use abilities like teleport. -/obj/item/rig_module/proc/engage() - - if(damage >= 2) - to_chat(usr, "The [interface_name] is damaged beyond use!") - return 0 - - if(world.time < next_use) - to_chat(usr, "You cannot use the [interface_name] again so soon.") - return 0 - - if(!holder || (!(holder.flags & NODROP))) - to_chat(usr, "The suit is not initialized.") - return 0 - - if(usr.lying || usr.stat || usr.stunned || usr.paralysis || usr.IsWeakened()) - to_chat(usr, "You cannot use the suit in this state.") - return 0 - - if(holder.wearer && holder.wearer.lying) - to_chat(usr, "The suit cannot function while the wearer is prone.") - return 0 - - if(holder.security_check_enabled && !holder.check_suit_access(usr)) - to_chat(usr, "Access denied.") - return 0 - - if(!holder.check_power_cost(usr, use_power_cost, 0, src, (istype(usr,/mob/living/silicon ? 1 : 0) ) ) ) - return 0 - - next_use = world.time + module_cooldown - - return 1 - -// Proc for toggling on active abilities. -/obj/item/rig_module/proc/activate() - - if(active || !engage()) - return 0 - - active = 1 - - spawn(1) - if(suit_overlay_active) - suit_overlay = suit_overlay_active - else - suit_overlay = null - holder.update_icon() - - return 1 - -// Proc for toggling off active abilities. -/obj/item/rig_module/proc/deactivate() - - if(!active) - return 0 - - active = 0 - - spawn(1) - if(suit_overlay_inactive) - suit_overlay = suit_overlay_inactive - else - suit_overlay = null - if(holder) - holder.update_icon() - - return 1 - -// Called when the module is uninstalled from a suit. -/obj/item/rig_module/proc/removed() - deactivate() - holder = null - return - -// Called by the hardsuit each rig process tick. -/obj/item/rig_module/process() - if(active) - return active_power_cost - else - return passive_power_cost - -// Called by holder rigsuit attackby() -// Checks if an item is usable with this module and handles it if it is -/obj/item/rig_module/proc/accepts_item(var/obj/item/input_device) - return 0 - -/mob/proc/SetupStat(var/obj/item/rig/R) - if(R && (R.flags & NODROP) && R.installed_modules.len && statpanel("Hardsuit Modules")) - var/cell_status = R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "ERROR" - stat("Suit charge", cell_status) - for(var/obj/item/rig_module/module in R.installed_modules) - { - for(var/stat_rig_module/SRM in module.stat_modules) - if(SRM.CanUse()) - stat(SRM.module.interface_name,SRM) - } - -/stat_rig_module - parent_type = /atom/movable - var/module_mode = "" - var/obj/item/rig_module/module - -/stat_rig_module/New(var/obj/item/rig_module/module) - ..() - src.module = module - -/stat_rig_module/proc/AddHref(var/list/href_list) - return - -/stat_rig_module/proc/CanUse() - return 0 - -/stat_rig_module/Click() - if(CanUse()) - var/list/href_list = list( - "interact_module" = module.holder.installed_modules.Find(module), - "module_mode" = module_mode - ) - AddHref(href_list) - module.holder.Topic(usr, href_list) - -/stat_rig_module/DblClick() - return Click() - -/stat_rig_module/activate/New(var/obj/item/rig_module/module) - ..() - name = module.activate_string - if(module.active_power_cost) - name += " ([module.active_power_cost*10]A)" - module_mode = "activate" - -/stat_rig_module/activate/CanUse() - return module.toggleable && !module.active - -/stat_rig_module/deactivate/New(var/obj/item/rig_module/module) - ..() - name = module.deactivate_string - // Show cost despite being 0, if it means changing from an active cost. - if(module.active_power_cost || module.passive_power_cost) - name += " ([module.passive_power_cost*10]P)" - - module_mode = "deactivate" - -/stat_rig_module/deactivate/CanUse() - return module.toggleable && module.active - -/stat_rig_module/engage/New(var/obj/item/rig_module/module) - ..() - name = module.engage_string - if(module.use_power_cost) - name += " ([module.use_power_cost*10]E)" - module_mode = "engage" - -/stat_rig_module/engage/CanUse() - return module.usable - -/stat_rig_module/select/New() - ..() - name = "Select" - module_mode = "select" - -/stat_rig_module/select/CanUse() - if(module.selectable) - name = module.holder.selected_module == module ? "Selected" : "Select" - return 1 - return 0 - -/stat_rig_module/charge/New() - ..() - name = "Change Charge" - module_mode = "select_charge_type" - -/stat_rig_module/charge/AddHref(var/list/href_list) - var/charge_index = module.charges.Find(module.charge_selected) - if(!charge_index) - charge_index = 0 - else - charge_index = charge_index == module.charges.len ? 1 : charge_index+1 - - href_list["charge_type"] = module.charges[charge_index] - -/stat_rig_module/charge/CanUse() - if(module.charges && module.charges.len) - var/datum/rig_charge/charge = module.charges[module.charge_selected] - name = "[charge.display_name] ([charge.charges]C) - Change" - return 1 - return 0 diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm deleted file mode 100644 index 6e4b588ac24..00000000000 --- a/code/modules/clothing/spacesuits/rig/modules/ninja.dm +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Contains - * /obj/item/rig_module/stealth_field - * /obj/item/rig_module/teleporter - * /obj/item/rig_module/fabricator/energy_net - * /obj/item/rig_module/self_destruct - */ - -/obj/item/rig_module/stealth_field - - name = "active camouflage module" - desc = "A robust hardsuit-integrated stealth module." - icon_state = "cloak" - - toggleable = 1 - disruptable = 1 - disruptive = 0 - - use_power_cost = 50 - active_power_cost = 10 - passive_power_cost = 0 - module_cooldown = 30 - - activate_string = "Enable Cloak" - deactivate_string = "Disable Cloak" - - interface_name = "integrated stealth system" - interface_desc = "An integrated active camouflage system." - - suit_overlay_active = "stealth_active" - suit_overlay_inactive = "stealth_inactive" - -/obj/item/rig_module/stealth_field/activate() - - if(!..()) - return 0 - - var/mob/living/carbon/human/H = holder.wearer - - to_chat(H, "You are now invisible to normal detection.") - H.invisibility = INVISIBILITY_LEVEL_TWO - - H.visible_message("[H.name] vanishes into thin air!",1) - -/obj/item/rig_module/stealth_field/deactivate() - - if(!..()) - return 0 - - var/mob/living/carbon/human/H = holder.wearer - - to_chat(H, "You are now visible.") - H.invisibility = 0 - - new /obj/effect/temp_visual/dir_setting/ninja(get_turf(H), H.dir) - - for(var/mob/O in oviewers(H)) - O.show_message("[H.name] appears from thin air!",1) - playsound(get_turf(H), 'sound/effects/stealthoff.ogg', 75, 1) - - -/obj/item/rig_module/teleporter - - name = "teleportation module" - desc = "A complex, sleek-looking, hardsuit-integrated teleportation module." - icon_state = "teleporter" - use_power_cost = 40 - redundant = 1 - usable = 1 - selectable = 1 - - engage_string = "Emergency Leap" - - interface_name = "VOID-shift phase projector" - interface_desc = "An advanced teleportation system. It is capable of pinpoint precision or random leaps forward." - -/obj/item/rig_module/teleporter/proc/phase_in(var/mob/M,var/turf/T) - - if(!M || !T) - return - - holder.spark_system.start() - playsound(T, 'sound/effects/phasein.ogg', 25, 1) - playsound(T, 'sound/effects/sparks2.ogg', 50, 1) - new /obj/effect/temp_visual/dir_setting/ninja/phase(T, M.dir) - -/obj/item/rig_module/teleporter/proc/phase_out(var/mob/M,var/turf/T) - - if(!M || !T) - return - - playsound(T, "sparks", 50, 1) - new /obj/effect/temp_visual/dir_setting/ninja/phase/out(T, M.dir) - -/obj/item/rig_module/teleporter/engage(var/atom/target, var/notify_ai) - - if(!..()) return 0 - - var/mob/living/carbon/human/H = holder.wearer - - if(!istype(H.loc, /turf)) - to_chat(H, "You cannot teleport out of your current location.") - return 0 - - var/turf/T - if(target) - T = get_turf(target) - else - T = get_teleport_loc(get_turf(H), H, rand(5, 9)) - - /*if(!T || T.density) - to_chat(H, "You cannot teleport into solid walls.") - return 0*///Who the fuck cares? Ninjas in walls are cool. - - if(!is_teleport_allowed(T.z)) - to_chat(H, "You cannot use your teleporter on this Z-level.") - return 0 - - phase_out(H,get_turf(H)) - H.forceMove(T) - phase_in(H,get_turf(H)) - - for(var/obj/item/grab/G in H.contents) - if(G.affecting) - phase_out(G.affecting,get_turf(G.affecting)) - G.affecting.forceMove(locate(T.x+rand(-1,1),T.y+rand(-1,1),T.z)) - phase_in(G.affecting,get_turf(G.affecting)) - - return 1 - -/* -/obj/item/rig_module/fabricator/energy_net - - name = "net projector" - desc = "Some kind of complex energy projector with a hardsuit mount." - icon_state = "enet" - - interface_name = "energy net launcher" - interface_desc = "An advanced energy-patterning projector used to capture targets." - - engage_string = "Fabricate Net" - - fabrication_type = /obj/item/energy_net - use_power_cost = 70 - -/obj/item/rig_module/fabricator/energy_net/engage(atom/target) - - if(holder && holder.wearer) - if(..(target) && target) - holder.wearer.Beam(target,"n_beam",,10) - return 1 - return 0*/ - -/obj/item/rig_module/self_destruct - - name = "self-destruct module" - desc = "Oh my God, Captain. A bomb." - icon_state = "deadman" - usable = 1 - active = 1 - permanent = 1 - - engage_string = "Detonate" - - interface_name = "dead man's switch" - interface_desc = "An integrated self-destruct module. When the wearer dies, so does the surrounding area. Do not press this button." - -/obj/item/rig_module/self_destruct/activate() - return - -/obj/item/rig_module/self_destruct/deactivate() - return - -/obj/item/rig_module/self_destruct/process() - - // Not being worn, leave it alone. - if(!holder || !holder.wearer || !holder.wearer.wear_suit == holder) - return 0 - - //OH SHIT. - if(holder.wearer.stat == 2) - engage() - -/obj/item/rig_module/self_destruct/engage() - explosion(get_turf(src), 1, 2, 4, 5) - if(holder && holder.wearer) - holder.wearer.unEquip(src) - qdel(holder) - qdel(src) - -/obj/item/rig_module/self_destruct/small/engage() - explosion(get_turf(src), 0, 0, 3, 4) - if(holder && holder.wearer) - holder.wearer.unEquip(src) - qdel(holder) - qdel(src) diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm deleted file mode 100644 index 4b19d2a7753..00000000000 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ /dev/null @@ -1,476 +0,0 @@ -/* Contains: - * /obj/item/rig_module/device - * /obj/item/rig_module/device/plasmacutter - * /obj/item/rig_module/device/healthscanner - * /obj/item/rig_module/device/drill - * /obj/item/rig_module/device/orescanner - * /obj/item/rig_module/device/rcd - * /obj/item/rig_module/device/anomaly_scanner - * /obj/item/rig_module/maneuvering_jets - * /obj/item/rig_module/foam_sprayer - * /obj/item/rig_module/device/broadcaster - * /obj/item/rig_module/chem_dispenser - * /obj/item/rig_module/chem_dispenser/injector - * /obj/item/rig_module/voice - * /obj/item/rig_module/device/paperdispenser - * /obj/item/rig_module/device/pen - * /obj/item/rig_module/device/stamp - */ - -/obj/item/rig_module/device - name = "mounted device" - desc = "Some kind of hardsuit mount." - usable = 0 - selectable = 1 - toggleable = 0 - disruptive = 0 - - var/device_type - var/obj/item/device - -/obj/item/rig_module/device/plasmacutter - name = "hardsuit plasma cutter" - desc = "A lethal-looking industrial cutter." - icon_state = "plasmacutter" - interface_name = "plasma cutter" - interface_desc = "A self-sustaining plasma arc capable of cutting through walls." - suit_overlay_active = "plasmacutter" - suit_overlay_inactive = "plasmacutter" - - device_type = /obj/item/gun/energy/plasmacutter - -/obj/item/rig_module/device/healthscanner - name = "health scanner module" - desc = "A hardsuit-mounted health scanner." - icon_state = "scanner" - interface_name = "health scanner" - interface_desc = "Shows an informative health readout when used on a subject." - - device_type = /obj/item/healthanalyzer - -/obj/item/rig_module/device/drill - name = "hardsuit drill mount" - desc = "A very heavy diamond-tipped drill." - icon_state = "drill" - interface_name = "mounted drill" - interface_desc = "A diamond-tipped industrial drill." - suit_overlay_active = "mounted-drill" - suit_overlay_inactive = "mounted-drill" - device_type = /obj/item/pickaxe/drill/diamonddrill - -/obj/item/rig_module/device/orescanner - name = "ore scanner module" - desc = "A clunky old ore scanner." - icon_state = "scanner" - interface_name = "ore detector" - interface_desc = "A sonar system for detecting large masses of ore." - engage_string = "Begin Scan" - usable = 1 - selectable = 0 - device_type = /obj/item/mining_scanner -/* -/obj/item/rig_module/device/rcd - name = "RCD mount" - desc = "A cell-powered rapid construction device for a hardsuit." - icon_state = "rcd" - interface_name = "mounted RCD" - interface_desc = "A device for building or removing walls. Cell-powered." - usable = 1 - engage_string = "Configure RCD" - - device_type = /obj/item/rcd/mounted -*/ -/obj/item/rig_module/device/New() - ..() - if(device_type) - device = new device_type(src) - device.flags |= ABSTRACT //Abstract in the sense that it's not an item that stands alone, but rather is just there to let the module act like it. - -/obj/item/rig_module/device/engage(atom/target) - if(!..() || !device) - return 0 - - if(!target) - device.attack_self(holder.wearer) - return 1 - - var/turf/T = get_turf(target) - if(istype(T) && !T.Adjacent(get_turf(src))) - return 0 - - var/resolved = target.attackby(device,holder.wearer) - if(!resolved && device && target) - device.afterattack(target,holder.wearer,1) - return 1 - - - -/obj/item/rig_module/chem_dispenser - name = "mounted chemical dispenser" - desc = "A complex web of tubing and needles suitable for hardsuit use." - icon_state = "injector" - usable = 1 - selectable = 0 - toggleable = 0 - disruptive = 0 - - engage_string = "Inject" - - interface_name = "integrated chemical dispenser" - interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream." - - charges = list( - list("saline-glucose", "salglu_solution", 0, 80), - list("salicylic acid", "sal_acid", 0, 80), - list("salbutamol", "salbutamol", 0, 80), - list("antibiotics", "spaceacillin", 0, 80), - list("charcoal", "charcoal", 0, 80), - list("nutrients", "nutriment", 0, 80), - list("potasssium iodide","potass_iodide", 0, 80), - list("radium", "radium", 0, 80) - ) - - var/max_reagent_volume = 80 //Used when refilling. - -/obj/item/rig_module/chem_dispenser/ninja - interface_desc = "Dispenses loaded chemicals directly into the wearer's bloodstream. This variant is made to be extremely light and flexible." - - //just over a syringe worth of each. Want more? Go refill. Gives the ninja another reason to have to show their face. - charges = list( - list("saline-glucose", "salglu_solution", 0, 20), - list("salicylic acid", "sal_acid", 0, 20), - list("salbutamol", "salbutamol", 0, 20), - list("antibiotics", "spaceacillin", 0, 20), - list("charcoal", "charcoal", 0, 20), - list("nutrients", "nutriment", 0, 80), - list("potasssium iodide","potass_iodide", 0, 20), - list("radium", "radium", 0, 20) - ) - - -/obj/item/rig_module/chem_dispenser/accepts_item(var/obj/item/input_item, var/mob/living/user) - - if(!input_item.is_open_container()) - return 0 - - if(!input_item.reagents || !input_item.reagents.total_volume) - to_chat(user, "\The [input_item] is empty.") - return 0 - - // Magical chemical filtration system, do not question it. - var/total_transferred = 0 - for(var/datum/reagent/R in input_item.reagents.reagent_list) - for(var/chargetype in charges) - var/datum/rig_charge/charge = charges[chargetype] - if(charge.display_name == R.id) - - var/chems_to_transfer = R.volume - - if((charge.charges + chems_to_transfer) > max_reagent_volume) - chems_to_transfer = max_reagent_volume - charge.charges - - charge.charges += chems_to_transfer - input_item.reagents.remove_reagent(R.id, chems_to_transfer) - total_transferred += chems_to_transfer - - break - - if(total_transferred) - to_chat(user, "You transfer [total_transferred] units into the suit reservoir.") - else - to_chat(user, "None of the reagents seem suitable.") - return 1 - -/obj/item/rig_module/chem_dispenser/engage(atom/target) - - if(!..()) - return 0 - - var/mob/living/carbon/human/H = holder.wearer - - if(!charge_selected) - to_chat(H, "You have not selected a chemical type.") - return 0 - - var/datum/rig_charge/charge = charges[charge_selected] - - if(!charge) - return 0 - - var/chems_to_use = 10 - if(charge.charges <= 0) - to_chat(H, "Insufficient chems!") - return 0 - else if(charge.charges < chems_to_use) - chems_to_use = charge.charges - - var/mob/living/carbon/target_mob - if(target) - if(istype(target,/mob/living/carbon)) - target_mob = target - else - return 0 - else - target_mob = H - - if(target_mob != H) - to_chat(H, "You inject [target_mob] with [chems_to_use] unit\s of [charge.display_name].") - to_chat(target_mob, "You feel a rushing in your veins as [chems_to_use] unit\s of [charge.display_name] [chems_to_use == 1 ? "is" : "are"] injected.") - target_mob.reagents.add_reagent(charge.display_name, chems_to_use) - - charge.charges -= chems_to_use - if(charge.charges < 0) charge.charges = 0 - - return 1 - -/obj/item/rig_module/chem_dispenser/combat - - name = "combat chemical injector" - desc = "A complex web of tubing and needles suitable for hardsuit use." - - charges = list( - list("synaptizine", "synaptizine", 0, 30), - list("hydrocodone", "hydrocodone", 0, 30), - list("nutrients", "nutriment", 0, 80), - ) - - interface_name = "combat chem dispenser" - interface_desc = "Dispenses loaded chemicals directly into the bloodstream." - - -/obj/item/rig_module/chem_dispenser/injector - - name = "mounted chemical injector" - desc = "A complex web of tubing and a large needle suitable for hardsuit use." - usable = 0 - selectable = 1 - disruptive = 1 - - interface_name = "mounted chem injector" - interface_desc = "Dispenses loaded chemicals via an arm-mounted injector." - -/obj/item/rig_module/voice - - name = "hardsuit voice synthesiser" - desc = "A speaker box and sound processor." - icon_state = "megaphone" - usable = 1 - selectable = 0 - toggleable = 0 - disruptive = 0 - - engage_string = "Configure Synthesiser" - - interface_name = "voice synthesiser" - interface_desc = "A flexible and powerful voice modulator system." - - var/obj/item/voice_changer/voice_holder - -/obj/item/rig_module/voice/New() - ..() - voice_holder = new(src) - voice_holder.active = FALSE - -/obj/item/rig_module/voice/installed() - ..() - holder.speech = src - -/obj/item/rig_module/voice/engage() - if(!..()) - return 0 - - var/choice= input("Would you like to toggle the synthesiser or set the name?") as null|anything in list("Enable","Disable","Set Name") - - if(!choice) - return 0 - - switch(choice) - if("Enable") - active = TRUE - voice_holder.active = TRUE - to_chat(usr, "You enable the speech synthesiser.") - if("Disable") - active = FALSE - voice_holder.active = FALSE - to_chat(usr, "You disable the speech synthesiser.") - if("Set Name") - var/raw_choice = sanitize(input(usr, "Please enter a new name.") as text|null, MAX_NAME_LEN) - if(!raw_choice) - return FALSE - voice_holder.voice = raw_choice - to_chat(usr, "You are now mimicking [voice_holder.voice].") - return 1 - -/obj/item/rig_module/maneuvering_jets - - name = "hardsuit maneuvering jets" - desc = "A compact gas thruster system for a hardsuit." - icon_state = "thrusters" - usable = 1 - toggleable = 1 - selectable = 0 - disruptive = 0 - - suit_overlay_active = "maneuvering_active" - suit_overlay_inactive = null //"maneuvering_inactive" - - engage_string = "Toggle Stabilizers" - activate_string = "Activate Thrusters" - deactivate_string = "Deactivate Thrusters" - - interface_name = "maneuvering jets" - interface_desc = "An inbuilt EVA maneuvering system that runs off the rig air supply." - - var/obj/item/tank/jetpack/rig/jets - -/obj/item/rig_module/maneuvering_jets/engage() - if(!..()) - return 0 - jets.toggle_stabilization(usr) - return 1 - -/obj/item/rig_module/maneuvering_jets/activate() - - if(active) - return 0 - - active = 1 - - spawn(1) - if(suit_overlay_active) - suit_overlay = suit_overlay_active - else - suit_overlay = null - holder.update_icon() - - jets.turn_on() - return 1 - -/obj/item/rig_module/maneuvering_jets/deactivate() - if(!..()) - return 0 - jets.turn_off() - return 1 - -/obj/item/rig_module/maneuvering_jets/New() - ..() - jets = new(src) - -/obj/item/rig_module/maneuvering_jets/installed() - ..() - jets.holder = holder - jets.ion_trail.set_up(holder) - -/obj/item/rig_module/maneuvering_jets/removed() - ..() - jets.holder = null - jets.ion_trail.set_up(jets) - -/obj/item/rig_module/foam_sprayer - -/obj/item/rig_module/device/paperdispenser - name = "hardsuit paper dispenser" - desc = "Crisp sheets." - icon_state = "paper" - interface_name = "paper dispenser" - interface_desc = "Dispenses warm, clean, and crisp sheets of paper." - engage_string = "Dispense" - usable = 1 - selectable = 0 - device_type = /obj/item/paper_bin - -/obj/item/rig_module/device/paperdispenser/engage(atom/target) - - if(!..() || !device) - return 0 - - if(!target) - device.attack_hand(holder.wearer) - return 1 - -/obj/item/rig_module/device/pen - name = "mounted pen" - desc = "For mecha John Hancocks." - icon_state = "pen" - interface_name = "mounted pen" - interface_desc = "Signatures with style(tm)." - engage_string = "Change color" - usable = 1 - device_type = /obj/item/pen/multi - -/obj/item/rig_module/device/stamp - name = "mounted internal affairs stamp" - desc = "DENIED." - icon_state = "stamp" - interface_name = "mounted stamp" - interface_desc = "Leave your mark." - engage_string = "Toggle stamp type" - usable = 1 - var/obj/iastamp //Theese were just vars, but any device would need to be an object - var/obj/deniedstamp //Stops assigning non-objects to theese vars, which probably would break quite a bit. - -/obj/item/rig_module/device/stamp/New() - ..() - iastamp = new /obj/item/stamp/law(src) - deniedstamp = new /obj/item/stamp/denied(src) - iastamp.flags |= ABSTRACT - deniedstamp.flags |= ABSTRACT - device = iastamp - -/obj/item/rig_module/device/stamp/engage(atom/target) - if(!..() || !device) - return 0 - - if(!target) - if(device == iastamp) - device = deniedstamp - to_chat(holder.wearer, "Switched to denied stamp.") - else if(device == deniedstamp) - device = iastamp - to_chat(holder.wearer, "Switched to internal affairs stamp.") - return 1 - -/obj/item/rig_module/welding_tank - name = "welding fuel tank" - desc = "A bluespace welding fuel storage tank for a rigsuit." - icon_state = "welding_tank" - interface_name = "mounted welding fuel tank" - interface_desc = "A minitaure fuel tank used for storage of welding fuel, built into a hardsuit." - engage_string = "Dispense fuel" - usable = 1 - - var/max_fuel = 300 - -/obj/item/rig_module/welding_tank/New() - ..() - - create_reagents(max_fuel) - reagents.add_reagent("fuel", max_fuel) - -/obj/item/rig_module/welding_tank/engage(atom/target) - if(!..() || !reagents) - return 0 - - if(!target) - if(get_fuel() >= 0) - var/obj/item/weldingtool/W = holder.wearer.get_active_hand() - if(istype(W)) - fill_welder(W) - else - W = holder.wearer.get_inactive_hand() - if(istype(W)) - fill_welder(W) - else - to_chat(holder.wearer, "Your welding tank is out of fuel!") - else - to_chat(holder.wearer, "You need to have a welding tool in one of your hands to dispense fuel.") - -/obj/item/rig_module/welding_tank/proc/fill_welder(obj/item/weldingtool/W) - if(!istype(W)) - return - W.refill(holder.wearer, src, W.maximum_fuel) - if(!reagents.get_reagent_amount("fuel")) - to_chat(holder.wearer, "You hear a faint dripping as your hardsuit welding tank completely empties.") - -/obj/item/rig_module/welding_tank/proc/get_fuel() - return reagents.get_reagent_amount("fuel") diff --git a/code/modules/clothing/spacesuits/rig/modules/vision.dm b/code/modules/clothing/spacesuits/rig/modules/vision.dm deleted file mode 100644 index 33ae007ceee..00000000000 --- a/code/modules/clothing/spacesuits/rig/modules/vision.dm +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Contains - * /obj/item/rig_module/vision - * /obj/item/rig_module/vision/multi - * /obj/item/rig_module/vision/meson - * /obj/item/rig_module/vision/thermal - * /obj/item/rig_module/vision/nvg - * /obj/item/rig_module/vision/medhud - * /obj/item/rig_module/vision/sechud - */ - -/datum/rig_vision - var/mode - var/obj/item/clothing/glasses/glasses - -/datum/rig_vision/nvg - mode = "night vision" -/datum/rig_vision/nvg/New() - glasses = new /obj/item/clothing/glasses/night - -/datum/rig_vision/thermal - mode = "thermal scanner" -/datum/rig_vision/thermal/New() - glasses = new /obj/item/clothing/glasses/thermal - -/datum/rig_vision/meson - mode = "meson scanner" -/datum/rig_vision/meson/New() - glasses = new /obj/item/clothing/glasses/meson - -/datum/rig_vision/sechud - mode = "security HUD" -/datum/rig_vision/sechud/New() - glasses = new /obj/item/clothing/glasses/hud/security - -/datum/rig_vision/medhud - mode = "medical HUD" -/datum/rig_vision/medhud/New() - glasses = new /obj/item/clothing/glasses/hud/health - -/obj/item/rig_module/vision - - name = "hardsuit visor" - desc = "A layered, translucent visor system for a hardsuit." - icon_state = "optics" - - interface_name = "optical scanners" - interface_desc = "An integrated multi-mode vision system." - - usable = 1 - toggleable = 1 - disruptive = 0 - - engage_string = "Cycle Visor Mode" - activate_string = "Enable Visor" - deactivate_string = "Disable Visor" - - var/datum/rig_vision/vision - var/list/vision_modes = list( - /datum/rig_vision/nvg, - /datum/rig_vision/thermal, - /datum/rig_vision/meson - ) - - var/vision_index - -/obj/item/rig_module/vision/multi - - name = "hardsuit optical package" - desc = "A complete visor system of optical scanners and vision modes." - icon_state = "fulloptics" - - - interface_name = "multi optical visor" - interface_desc = "An integrated multi-mode vision system." - - vision_modes = list(/datum/rig_vision/meson, - /datum/rig_vision/nvg, - /datum/rig_vision/thermal, - /datum/rig_vision/sechud, - /datum/rig_vision/medhud) - -/obj/item/rig_module/vision/meson - - name = "hardsuit meson scanner" - desc = "A layered, translucent visor system for a hardsuit." - icon_state = "meson" - - usable = 0 - - interface_name = "meson scanner" - interface_desc = "An integrated meson scanner." - - vision_modes = list(/datum/rig_vision/meson) - -/obj/item/rig_module/vision/thermal - - name = "hardsuit thermal scanner" - desc = "A layered, translucent visor system for a hardsuit." - icon_state = "thermal" - - usable = 0 - - interface_name = "thermal scanner" - interface_desc = "An integrated thermal scanner." - - vision_modes = list(/datum/rig_vision/thermal) - -/obj/item/rig_module/vision/nvg - - name = "hardsuit night vision interface" - desc = "A multi input night vision system for a hardsuit." - icon_state = "night" - - usable = 0 - - interface_name = "night vision interface" - interface_desc = "An integrated night vision system." - - vision_modes = list(/datum/rig_vision/nvg) - -/obj/item/rig_module/vision/sechud - - name = "hardsuit security hud" - desc = "A simple tactical information system for a hardsuit." - icon_state = "securityhud" - - usable = 0 - - interface_name = "security HUD" - interface_desc = "An integrated security heads up display." - - vision_modes = list(/datum/rig_vision/sechud) - -/obj/item/rig_module/vision/medhud - - name = "hardsuit medical hud" - desc = "A simple medical status indicator for a hardsuit." - icon_state = "healthhud" - - usable = 0 - - interface_name = "medical HUD" - interface_desc = "An integrated medical heads up display." - - vision_modes = list(/datum/rig_vision/medhud) - - -// There should only ever be one vision module installed in a suit. -/obj/item/rig_module/vision/installed() - ..() - holder.visor = src - -/obj/item/rig_module/vision/engage() - - var/starting_up = !active - - if(!..() || !vision_modes) - return 0 - - // Don't cycle if this engage() is being called by activate(). - if(starting_up) - to_chat(holder.wearer, "You activate your visual sensors.") - return 1 - - if(vision_modes.len > 1) - vision_index++ - if(vision_index > vision_modes.len) - vision_index = 1 - vision = vision_modes[vision_index] - - to_chat(holder.wearer, "You cycle your sensors to [vision.mode] mode.") - else - to_chat(holder.wearer, "Your sensors only have one mode.") - return 1 - -/obj/item/rig_module/vision/New() - ..() - - if(!vision_modes) - return - - vision_index = 1 - var/list/processed_vision = list() - - for(var/vision_mode in vision_modes) - var/datum/rig_vision/vision_datum = new vision_mode - if(!vision) vision = vision_datum - processed_vision += vision_datum - - vision_modes = processed_vision diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm deleted file mode 100644 index 0d106a874a7..00000000000 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ /dev/null @@ -1,1059 +0,0 @@ -#define ONLY_DEPLOY 1 -#define ONLY_RETRACT 2 -#define SEAL_DELAY 30 - -/* - * Defines the behavior of hardsuits/rigs/power armour. - */ - -/obj/item/rig - - name = "hardsuit control module" - icon = 'icons/obj/rig_modules.dmi' - desc = "A back-mounted hardsuit deployment and control mechanism." - slot_flags = SLOT_BACK - req_one_access = list() - req_access = list() - w_class = WEIGHT_CLASS_BULKY - - // These values are passed on to all component pieces. - armor = list(melee = 10, bullet = 5, laser = 10, energy = 5, bomb = 10, bio = 100, rad = 75) - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT - siemens_coefficient = 0.2 - permeability_coefficient = 0.1 - - var/interface_path = "hardsuit.tmpl" - var/ai_interface_path = "hardsuit.tmpl" - var/interface_title = "Hardsuit Controller" - var/wearer_move_delay //Used for AI moving. - var/ai_controlled_move_delay = 10 - - // Keeps track of what this rig should spawn with. - var/suit_type = "hardsuit" - var/list/initial_modules - var/chest_type = /obj/item/clothing/suit/space/new_rig - var/helm_type = /obj/item/clothing/head/helmet/space/new_rig - var/boot_type = /obj/item/clothing/shoes/magboots/rig - var/glove_type = /obj/item/clothing/gloves/rig - var/cell_type = /obj/item/stock_parts/cell/high - var/air_type = /obj/item/tank/oxygen - - //Component/device holders. - var/obj/item/tank/air_supply // Air tank, if any. - var/obj/item/clothing/shoes/magboots/boots = null // Deployable boots, if any. - var/obj/item/clothing/shoes/under_boots = null //Boots that are between the feet and the rig boots, if any. - var/obj/item/clothing/suit/space/new_rig/chest // Deployable chestpiece, if any. - var/obj/item/clothing/head/helmet/space/new_rig/helmet = null // Deployable helmet, if any. - var/obj/item/clothing/gloves/rig/gloves = null // Deployable gauntlets, if any. - var/obj/item/stock_parts/cell/cell // Power supply, if any. - var/obj/item/rig_module/selected_module = null // Primary system (used with middle-click) - var/obj/item/rig_module/vision/visor // Kinda shitty to have a var for a module, but saves time. - var/obj/item/rig_module/voice/speech // As above. - var/mob/living/carbon/human/wearer // The person currently wearing the rig. - var/image/mob_icon // Holder for on-mob icon. - var/list/installed_modules = list() // Power consumption/use bookkeeping. - - // Rig status vars. - var/open = 0 // Access panel status. - var/locked = 1 // Lock status. - var/subverted = 0 - var/interface_locked = 0 - var/control_overridden = 0 - var/ai_override_enabled = 0 - var/security_check_enabled = 1 - var/malfunctioning = 0 - var/malfunction_delay = 0 - var/electrified = 0 - var/locked_down = 0 - - var/seal_delay = SEAL_DELAY - var/sealing // Keeps track of seal status independantly of NODROP. - var/offline = 1 // Should we be applying suit maluses? - var/offline_slowdown = 3 // If the suit is deployed and unpowered, it sets slowdown to this. - var/active_slowdown = 3 // How much the deployed suit slows down if powered. - var/vision_restriction - var/offline_vision_restriction = 1 // 0 - none, 1 - welder vision, 2 - blind. Maybe move this to helmets. - var/airtight = 1 //If set, will adjust AIRTIGHT and STOPSPRESSUREDMAGE flags on components. Otherwise it should leave them untouched. - - var/emp_protection = 0 - var/has_emergency_release = 1 //Allows suit to be removed from outside. - - // Wiring! How exciting. - var/datum/wires/rig/wires - var/datum/effect_system/spark_spread/spark_system - -/obj/item/rig/examine(mob/user) - . = list("This is [src].") - . += "[desc]" - if(wearer) - for(var/obj/item/piece in list(helmet,gloves,chest,boots)) - if(!piece || piece.loc != wearer) - continue - . += "[bicon(piece)] \The [piece] [piece.gender == PLURAL ? "are" : "is"] deployed." - - if(loc == usr) - . += "The maintenance panel is [open ? "open" : "closed"]." - . += "Hardsuit systems are [offline ? "offline" : "online"]." - -/obj/item/rig/get_cell() - return cell - -/obj/item/rig/New() - ..() - - item_state = icon_state - wires = new(src) - - if((!req_access || !req_access.len) && (!req_one_access || !req_one_access.len)) - locked = 0 - - spark_system = new() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - START_PROCESSING(SSobj, src) - - if(initial_modules && initial_modules.len) - for(var/path in initial_modules) - var/obj/item/rig_module/module = new path(src) - installed_modules += module - module.installed(src) - - // Create and initialize our various segments. - if(cell_type) - cell = new cell_type(src) - if(air_type) - air_supply = new air_type(src) - if(glove_type) - gloves = new glove_type(src) - verbs |= /obj/item/rig/proc/toggle_gauntlets - if(helm_type) - helmet = new helm_type(src) - verbs |= /obj/item/rig/proc/toggle_helmet - helmet.item_color="[initial(icon_state)]_sealed" //For the lightswitching to know the correct string to manipulate - if(boot_type) - boots = new boot_type(src) - verbs |= /obj/item/rig/proc/toggle_boots - boots.magboot_state="[initial(icon_state)]_sealed" //For the magboot (de)activation to know the correct string to manipulate - if(chest_type) - chest = new chest_type(src) - if(allowed) - chest.allowed = allowed - chest.slowdown = offline_slowdown - chest.holder = src - verbs |= /obj/item/rig/proc/toggle_chest - - for(var/obj/item/piece in list(gloves,helmet,boots,chest)) - if(!istype(piece)) - continue - piece.name = "[suit_type] [initial(piece.name)]" - piece.desc = "It seems to be part of a [src.name]." - piece.icon_state = "[initial(icon_state)]" - piece.min_cold_protection_temperature = min_cold_protection_temperature - piece.max_heat_protection_temperature = max_heat_protection_temperature - if(piece.siemens_coefficient > siemens_coefficient) //So that insulated gloves keep their insulation. - piece.siemens_coefficient = siemens_coefficient - piece.permeability_coefficient = permeability_coefficient - if(armor) - piece.armor = armor - - update_icon(1) - -/obj/item/rig/Destroy() - for(var/obj/item/piece in list(gloves,boots,helmet,chest)) - var/mob/living/M = piece.loc - if(istype(M)) - M.unEquip(piece) - qdel(piece) - STOP_PROCESSING(SSobj, src) - QDEL_NULL(wires) - QDEL_NULL(spark_system) - return ..() - -/obj/item/rig/proc/suit_is_deployed() - if(!istype(wearer) || src.loc != wearer || wearer.back != src) - return 0 - if(helm_type && !(helmet && wearer.head == helmet)) - return 0 - if(glove_type && !(gloves && wearer.gloves == gloves)) - return 0 - if(boot_type && !(boots && wearer.shoes == boots)) - return 0 - if(chest_type && !(chest && wearer.wear_suit == chest)) - return 0 - return 1 - -/obj/item/rig/proc/reset() - offline = 2 - flags &= ~NODROP - if(helmet && helmet.on) - helmet.toggle_light(wearer) - if(boots && boots.magpulse) - boots.attack_self(wearer) - for(var/obj/item/piece in list(helmet,boots,gloves,chest)) - if(!piece) continue - piece.icon_state = "[initial(icon_state)]" - if(airtight) - piece.flags &= ~(STOPSPRESSUREDMAGE | AIRTIGHT) - update_icon(1) - -/obj/item/rig/proc/seal(mob/living/user) - if(sealing) - return 0 - - if(!wearer || !user) - return - - var/sealed = (flags & NODROP) - if(sealed) - to_chat(user, "\The [src] is already sealed!") - return 0 - - if(!check_power_cost(user, 1)) //need power to seal the suit - return 0 - - var/failed_to_seal = FALSE - - if(!suit_is_deployed()) - to_chat(user, "\The [src] cannot seal, as it is not fully deployed!") - return 0 - - flags |= NODROP - sealing = TRUE - - to_chat(user, "\The [src] begins to tighten it's seals.") - wearer.visible_message("\The [wearer]'s suit emits a quiet hum as it begins to tighten it's seals.", - "With a quiet hum, your suit begins to seal.") - - if(seal_delay && !do_after(user, seal_delay, target = wearer)) - to_chat(user, "You must remain still to seal \the [src]!") - failed_to_seal = TRUE - - if(!failed_to_seal) - deploy(user) - - var/list/pieces_data = list(list(wearer.shoes, boots, "boots", boot_type), - list(wearer.gloves, gloves, "gloves", glove_type), - list(wearer.head, helmet, "helmet", helm_type), - list(wearer.wear_suit, chest, "chest", chest_type)) - - for(var/list/piece_data in pieces_data) - var/obj/item/user_piece = piece_data[1] - var/obj/item/correct_piece = piece_data[2] - var/msg_type = piece_data[3] - var/piece_type = piece_data[4] - - if(!user_piece || !piece_type) - continue - - if(user_piece != correct_piece) - to_chat(user, "\The [user_piece] is blocking \the [src] from deploying.") - failed_to_seal = TRUE - - if(seal_delay && !do_after(user, seal_delay, needhand = 0, target = wearer)) - to_chat(user, "You must remain still to seal \the [src]!") - failed_to_seal = TRUE - - if(failed_to_seal) - break - - correct_piece.icon_state = "[initial(icon_state)]_sealed" - switch(msg_type) - if("boots") - to_chat(wearer, "\The [correct_piece] seal around your feet.") - correct_piece.icon_state = "[initial(icon_state)]_sealed0" //Solution to not need a sprite for off, on, and unused magboots. - if(user != wearer) - to_chat(user, "\The [correct_piece] has been sealed.") - wearer.update_inv_shoes() - if("gloves") - to_chat(wearer, "\The [correct_piece] tighten around your fingers and wrists.") - if(user != wearer) - to_chat(user, "\The [correct_piece] has been sealed.") - wearer.update_inv_gloves() - if("chest") - to_chat(wearer, "\The [correct_piece] cinches tight again your chest.") - if(user != wearer) - to_chat(user, "\The [correct_piece] has been sealed.") - wearer.update_inv_wear_suit() - if("helmet") - to_chat(wearer, "\The [correct_piece] hisses closed.") - correct_piece.icon_state = "[initial(icon_state)]_sealed0" //Solution to not need a sprite for off, on, and unused helmet light. - if(user != wearer) - to_chat(user, "\The [correct_piece] has been sealed.") - wearer.update_inv_head() - if(helmet) - helmet.update_light(wearer) - - correct_piece.armor = correct_piece.armor.setRating(bio_value = 100) - - sealing = FALSE - - if(failed_to_seal) - for(var/obj/item/piece in list(helmet, boots, gloves, chest)) - if(!piece) - continue - piece.icon_state = "[initial(icon_state)]" - flags &= ~NODROP - if(airtight) - update_component_sealed() - update_icon(1) - return 0 - - if(user != wearer) - to_chat(user, "\The [src] has been loosened.") - to_chat(wearer, "Your entire suit tightens around you as the components lock into place.") - if(airtight) - update_component_sealed() - update_icon(1) - -/obj/item/rig/proc/unseal(mob/living/user) - if(sealing) - return 0 - - if(!wearer || !user) - return - - var/sealed = (flags & NODROP) - if(!sealed) - to_chat(user, "\The [src] is already unsealed!") - return 0 - - sealing = TRUE - - var/failed_to_seal = FALSE - - if(!suit_is_deployed()) - to_chat(user, "\The [src] cannot unseal, as it is not fully deployed!") - failed_to_seal = TRUE - - if(!failed_to_seal) - if(user != wearer) - to_chat(user, "\The [src] begins to loosen it's seals.") - wearer.visible_message("\The [wearer]'s suit emits a quiet hum as it begins to loosen it's seals.", - "With a quiet hum, your suit begins to unseal.") - - if(seal_delay && !do_after(user, seal_delay, target = wearer)) - to_chat(user, "You must remain still to unseal \the [src]!") - failed_to_seal = TRUE - - if(!failed_to_seal) - var/list/pieces_data = list(list(wearer.shoes, boots, "boots", boot_type), - list(wearer.gloves, gloves, "gloves", glove_type), - list(wearer.head, helmet, "helmet", helm_type), - list(wearer.wear_suit, chest, "chest", chest_type)) - - for(var/list/piece_data in pieces_data) - var/obj/item/user_piece = piece_data[1] - var/obj/item/correct_piece = piece_data[2] - var/msg_type = piece_data[3] - var/piece_type = piece_data[4] - - if(!correct_piece || !piece_type) - continue - - if(user_piece != correct_piece) - to_chat(user, "\The [user_piece] is blocking \the [src] from deploying.") - failed_to_seal = TRUE - - if(seal_delay && !do_after(user, seal_delay, needhand = 0, target = wearer)) - to_chat(user, "You must remain still to unseal \the [src]!") - failed_to_seal = TRUE - - if(failed_to_seal) - break - - correct_piece.icon_state = "[initial(icon_state)]" - switch(msg_type) - if("boots") - to_chat(wearer, "\The [correct_piece] relax [correct_piece.p_their()] grip on your legs.") - if(user != wearer) - to_chat(user, "\The [correct_piece] has been unsealed.") - wearer.update_inv_shoes() - if("gloves") - to_chat(wearer, "\The [correct_piece] become loose around your fingers.") - if(user != wearer) - to_chat(user, "\The [correct_piece] has been unsealed.") - wearer.update_inv_gloves() - if("chest") - to_chat(wearer, "\The [correct_piece] releases your chest.") - if(user != wearer) - to_chat(user, "\The [correct_piece] has been unsealed.") - wearer.update_inv_wear_suit() - if("helmet") - to_chat(wearer, "\The [correct_piece] hisses open.") - if(user != wearer) - to_chat(user, "\The [correct_piece] has been unsealed.") - wearer.update_inv_head() - if(helmet) - helmet.update_light(wearer) - - correct_piece.armor = correct_piece.armor.setRating(bio_value = armor.getRating("bio")) - - sealing = FALSE - - if(failed_to_seal) - for(var/obj/item/piece in list(gloves, chest)) - if(!piece) - continue - piece.icon_state = "[initial(icon_state)]_sealed" - if(helmet) - helmet.icon_state = "[initial(icon_state)]_sealed[helmet.on]" - if(boots) - boots.icon_state = "[initial(icon_state)]_sealed[boots.magpulse]" - if(airtight) - update_component_sealed() - update_icon(1) - return 0 - - if(user != wearer) - to_chat(user, "\The [src] has been unsealed.") - to_chat(wearer, "Your entire suit loosens as the components relax.") - - flags &= ~NODROP - - for(var/obj/item/rig_module/module in installed_modules) - module.deactivate() - - if(airtight) - update_component_sealed() - update_icon(1) - -/obj/item/rig/proc/update_component_sealed() - if(istype(boots) && !(flags & NODROP) && boots.magpulse) //If we have (active) boots and unsealed the suit, we deactivate the magboots. - boots.attack_self(wearer) - if(istype(helmet) && !(flags & NODROP) && helmet.on) //If we have an (active) headlamp and unsealed the suit, we deactivate the headlamp. - helmet.toggle_light(wearer) - for(var/obj/item/piece in list(helmet,boots,gloves,chest)) - if(!(flags & NODROP)) - piece.flags &= ~(STOPSPRESSUREDMAGE | AIRTIGHT) - else - piece.flags |= STOPSPRESSUREDMAGE | AIRTIGHT - -/obj/item/rig/process() - // If we've lost any parts, grab them back. - var/mob/living/M - for(var/obj/item/piece in list(gloves,boots,helmet,chest)) - if(piece.loc != src && !(wearer && piece.loc == wearer)) - if(istype(piece.loc, /mob/living)) - M = piece.loc - M.unEquip(piece) - piece.forceMove(src) - - if(cell && cell.charge > 0 && electrified > 0) - electrified-- - - if(malfunction_delay > 0) - malfunction_delay-- - else if(malfunctioning) - malfunctioning-- - malfunction() - - if(!istype(wearer) || loc != wearer || wearer.back != src || !(flags & NODROP) || !cell || cell.charge <= 0) - if(!cell || cell.charge <= 0) - if(electrified > 0) - electrified = 0 - if(!offline) - if(istype(wearer)) - if(flags & NODROP) - if(offline_slowdown < 3) - to_chat(wearer, "Your suit beeps stridently, and suddenly goes dead.") - else - to_chat(wearer, "Your suit beeps stridently, and suddenly you're wearing a leaden mass of metal and plastic composites instead of a powered suit.") - if(offline_vision_restriction == 1) - to_chat(wearer, "The suit optics flicker and die, leaving you with restricted vision.") - else if(offline_vision_restriction == 2) - to_chat(wearer, "The suit optics drop out completely, drowning you in darkness.") - if(!offline) - offline = 1 - if(istype(wearer) && wearer.wearing_rig) - wearer.wearing_rig = null - else - if(offline) - offline = 0 - if(istype(wearer) && !wearer.wearing_rig) - wearer.wearing_rig = src - chest.slowdown = active_slowdown - - if(offline) - if(offline == 1) - for(var/obj/item/rig_module/module in installed_modules) - module.deactivate() - offline = 2 - chest.slowdown = offline_slowdown - return - - - for(var/obj/item/rig_module/module in installed_modules) - cell.use(module.process()*10) - -/obj/item/rig/proc/check_power_cost(var/mob/living/user, var/cost, var/use_unconcious, var/obj/item/rig_module/mod, var/user_is_ai) - if(!istype(user)) - return 0 - - var/fail_msg - - if(!user_is_ai) - var/mob/living/carbon/human/H = user - if(istype(H) && H.back != src) - fail_msg = "You must be wearing \the [src] to do this." - else if(user.incorporeal_move) - fail_msg = "You must be solid to do this." - if(sealing) - fail_msg = "The hardsuit is in the process of adjusting seals and cannot be activated." - else if(!fail_msg && ((use_unconcious && user.stat > 1) || (!use_unconcious && user.stat))) - fail_msg = "You are in no fit state to do that." - else if(!cell) - fail_msg = "There is no cell installed in the suit." - else if(cost && cell.charge < cost * 10) //TODO: Cellrate? - fail_msg = "Not enough stored power." - - if(fail_msg) - to_chat(user, "[fail_msg]") - return 0 - - // This is largely for cancelling stealth and whatever. - if(mod && mod.disruptive) - for(var/obj/item/rig_module/module in (installed_modules - mod)) - if(module.active && module.disruptable) - module.deactivate() - - cell.use(cost*10) - return 1 - -/obj/item/rig/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state) - if(!user) - return - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, ((src.loc != user) ? ai_interface_path : interface_path), interface_title, 480, 550, state = state) - ui.open() - ui.set_auto_update(1) - -/obj/item/rig/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) - var/data[0] - - data["primarysystem"] = null - if(selected_module) - data["primarysystem"] = "[selected_module.interface_name]" - - data["ai"] = 0 - if(src.loc != user) - data["ai"] = 1 - - var/is_sealed = (flags & NODROP) //1 if NODROP, 0 if no-nodrop - data["seals"] = "[!is_sealed]" //1 if not NODROP (unsealed), 0 if NODROP (sealed) - data["sealing"] = "[src.sealing]" - data["helmet"] = (helmet ? "[helmet.name]" : "None.") - data["gauntlets"] = (gloves ? "[gloves.name]" : "None.") - data["boots"] = (boots ? "[boots.name]" : "None.") - data["chest"] = (chest ? "[chest.name]" : "None.") - - data["charge"] = cell ? round(cell.charge,1) : 0 - data["maxcharge"] = cell ? cell.maxcharge : 0 - data["chargestatus"] = cell ? FLOOR((cell.charge/cell.maxcharge)*50, 1) : 0 - - data["emagged"] = subverted - data["coverlock"] = locked - data["interfacelock"] = interface_locked - data["aicontrol"] = control_overridden - data["aioverride"] = ai_override_enabled - data["securitycheck"] = security_check_enabled - data["malf"] = malfunction_delay - - - var/list/module_list = list() - var/i = 1 - for(var/obj/item/rig_module/module in installed_modules) - var/list/module_data = list( - "index" = i, - "name" = "[module.interface_name]", - "desc" = "[module.interface_desc]", - "can_use" = "[module.usable]", - "can_select" = "[module.selectable]", - "can_toggle" = "[module.toggleable]", - "is_active" = "[module.active]", - "engagecost" = module.use_power_cost*10, - "activecost" = module.active_power_cost*10, - "passivecost" = module.passive_power_cost*10, - "engagestring" = module.engage_string, - "activatestring" = module.activate_string, - "deactivatestring" = module.deactivate_string, - "damage" = module.damage - ) - - if(module.charges && module.charges.len) - - module_data["charges"] = list() - var/datum/rig_charge/selected = module.charges[module.charge_selected] - module_data["chargetype"] = selected ? "[selected.display_name]" : "none" - - for(var/chargetype in module.charges) - var/datum/rig_charge/charge = module.charges[chargetype] - module_data["charges"] += list(list("caption" = "[chargetype] ([charge.charges])", "index" = "[chargetype]")) - - module_list += list(module_data) - i++ - - if(module_list.len) - data["modules"] = module_list - - return data - -/obj/item/rig/update_icon(var/update_mob_icon) - - //TODO: Maybe consider a cache for this (use mob_icon as blank canvas, use suit icon overlay). - overlays.Cut() - if(!mob_icon || update_mob_icon) - var/species_icon = 'icons/mob/rig_back.dmi' - // Since setting mob_icon will override the species checks in - // update_inv_wear_suit(), handle species checks here. - if(wearer && sprite_sheets && sprite_sheets[wearer.dna.species.name]) - species_icon = sprite_sheets[wearer.dna.species.name] - mob_icon = image("icon" = species_icon, "icon_state" = "[icon_state]") - - if(installed_modules.len) - for(var/obj/item/rig_module/module in installed_modules) - if(module.suit_overlay) - chest.overlays += image("icon" = 'icons/mob/rig_modules.dmi', "icon_state" = "[module.suit_overlay]", "dir" = SOUTH) - - if(wearer) - wearer.update_inv_shoes() - wearer.update_inv_gloves() - wearer.update_inv_head() - wearer.update_inv_wear_suit() - wearer.update_inv_back() - return - -/obj/item/rig/proc/check_suit_access(var/mob/living/carbon/human/user) - - if(!security_check_enabled) - return 1 - - if(istype(user)) - if(malfunction_check(user)) - return 0 - if(user.back != src) - return 0 - else if(!src.allowed(user)) - to_chat(user, "Unauthorized user. Access denied.") - return 0 - - else if(!ai_override_enabled) - to_chat(user, "Synthetic access disabled. Please consult hardware provider.") - return 0 - - return 1 - -/obj/item/rig/Topic(href,href_list) - if(!check_suit_access(usr)) - return 0 - - if(href_list["toggle_piece"]) - if(ishuman(usr) && (usr.stat || usr.stunned || usr.lying)) - return 0 - toggle_piece(href_list["toggle_piece"], usr) - else if(href_list["toggle_seals"]) - if(flags & NODROP) - unseal(usr) - else - seal(usr) - else if(href_list["interact_module"]) - var/module_index = text2num(href_list["interact_module"]) - - if(module_index > 0 && module_index <= installed_modules.len) - var/obj/item/rig_module/module = installed_modules[module_index] - switch(href_list["module_mode"]) - if("activate") - module.activate() - if("deactivate") - module.deactivate() - if("engage") - module.engage() - if("select") - selected_module = module - if("select_charge_type") - module.charge_selected = href_list["charge_type"] - else if(href_list["toggle_ai_control"]) - ai_override_enabled = !ai_override_enabled - notify_ai("Synthetic suit control has been [ai_override_enabled ? "enabled" : "disabled"].") - else if(href_list["toggle_suit_lock"]) - locked = !locked - - usr.set_machine(src) - add_fingerprint(usr) - return 0 - -/obj/item/rig/proc/notify_ai(var/message) - if(!message || !installed_modules || !installed_modules.len) - return - for(var/obj/item/rig_module/module in installed_modules) - for(var/mob/living/silicon/ai/ai in module.contents) - if(ai && ai.client && !ai.stat) - to_chat(ai, "[message]") - -/obj/item/rig/equipped(mob/living/carbon/human/M, slot) - ..() - if(!istype(M) || slot != slot_back) - return //we don't care about picking up/nonhumans - - spawn(1) //equipped() is called BEFORE the item is actually set as the slot - - if(seal_delay > 0 && istype(M) && M.back == src) - M.visible_message("[M] starts putting on \the [src]...", "You start putting on \the [src]...") - if(!do_after(M, seal_delay, target = M)) - if(M && M.back == src) - M.unEquip(src) - M.put_in_hands(src) - return - - if(istype(M) && M.back == src) - M.visible_message("[M] struggles into \the [src].", "You struggle into \the [src].") - wearer = M - wearer.wearing_rig = src - if(has_emergency_release) - M.verbs |= /obj/item/rig/proc/emergency_release - update_icon() - -/obj/item/rig/proc/toggle_piece(var/piece, var/mob/living/user, var/deploy_mode, var/force) - if(!istype(wearer) || wearer.back != src) - if(force) //can only force retracting sorry - for(var/obj/item/uneq_piece in list(helmet, gloves, boots, chest)) - if(uneq_piece) - if(isliving(uneq_piece.loc)) - var/mob/living/L = uneq_piece.loc - L.unEquip(uneq_piece, 1) - if(uneq_piece == boots) - if(under_boots) - if(L.equip_to_slot_if_possible(under_boots, slot_shoes)) - under_boots = null - else - to_chat(user, "Somehow, your [under_boots] got stuck to the [boots], and were retracted with them. ((This shouldn't happen, bug report this.))") - uneq_piece.forceMove(src) - return 0 - - if(sealing || !cell || !cell.charge) - return 0 - - if(!(deploy_mode == ONLY_RETRACT && force)) //This should be the case while stripping, stripping does trigger the if statement below. - if(user == wearer && user.incapacitated()) // If the user isn't wearing the suit it's probably an AI. - return 0 - - var/obj/item/check_slot - var/equip_to - var/obj/item/use_obj - - switch(piece) - if("helmet") - equip_to = slot_head - use_obj = helmet - check_slot = wearer.head - if("gauntlets") - equip_to = slot_gloves - use_obj = gloves - check_slot = wearer.gloves - if("boots") - equip_to = slot_shoes - use_obj = boots - check_slot = wearer.shoes - if("chest") - equip_to = slot_wear_suit - use_obj = chest - check_slot = wearer.wear_suit - - if(use_obj) - if(check_slot == use_obj && deploy_mode != ONLY_DEPLOY) //user is wearing it, retract it if not forced to deploy - if((flags & NODROP) && equip_to != slot_head && !force) //you can only retract the helmet if the suit isn't unsealed - to_chat(user, "You can't retract \the [use_obj] while the suit is sealed!") - return - - var/mob/living/to_strip - if(wearer) - to_strip = wearer - else if(isliving(use_obj.loc)) - to_strip = use_obj.loc - - if(to_strip) - to_strip.unEquip(use_obj, 1) - if(use_obj == boots) - if(under_boots) - if(to_strip.equip_to_slot_if_possible(under_boots, slot_shoes)) - under_boots = null - else - to_chat(user, "Somehow, your [under_boots] got stuck to the [boots], and were retracted with them. ((This shouldn't happen, bug report this.))") - use_obj.forceMove(src) - if(wearer) - to_chat(wearer, "Your [use_obj] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.") - - else if(deploy_mode != ONLY_RETRACT) - if(check_slot) - if(check_slot != use_obj) //If use_obj is already in check_slot, silently bail. Otherwise, tell the user why the part didn't deploy. - if(use_obj == boots) - under_boots = check_slot - wearer.unEquip(under_boots) - under_boots.forceMove(src) - else - to_chat(wearer, "You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way.") - return - use_obj.forceMove(wearer) - if(!wearer.equip_to_slot_if_possible(use_obj, equip_to, FALSE, TRUE)) - use_obj.forceMove(src) - else - if(wearer) - to_chat(wearer, "Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly.") - - if(piece == "helmet" && helmet) - helmet.update_light(wearer) - -/obj/item/rig/proc/deploy(mob/user) - if(!wearer || !user) - return 0 - - if(flags & NODROP) //We need to check if we have the part, the person is wearing something in the parts slot, and if yes, are they the same. - if(helmet && wearer.head && wearer.head != helmet) - to_chat(user, "\The [wearer.head] is blocking \the [src] from deploying!") - return 0 - if(gloves && wearer.gloves && wearer.gloves != gloves) - to_chat(user, "\The [wearer.gloves] is preventing \the [src] from deploying!") - return 0 - /*if(boots && wearer.shoes && wearer.shoes != boots) - to_chat(user, "\The [wearer.shoes] is preventing \the [src] from deploying!") - return 0*/ - if(chest && wearer.wear_suit && wearer.wear_suit != chest) - to_chat(user, "\The [wearer.wear_suit] is preventing \the [src] from deploying!") - return 0 - - - for(var/piece in list("helmet", "gauntlets", "chest", "boots")) - toggle_piece(piece, user, ONLY_DEPLOY) - -/obj/item/rig/dropped(var/mob/user) - ..() - user.verbs -= /obj/item/rig/proc/emergency_release - for(var/piece in list("helmet","gauntlets","chest","boots")) - toggle_piece(piece, user, ONLY_RETRACT, 1) - if(wearer) - wearer.wearing_rig = null - wearer = null - -//Todo -/obj/item/rig/proc/malfunction() - return 0 - -/obj/item/rig/emp_act(severity_class) - //set malfunctioning - if(emp_protection < 30) //for ninjas, really. - malfunctioning += 10 - if(malfunction_delay <= 0) - malfunction_delay = max(malfunction_delay, round(30/severity_class)) - - //drain some charge - if(cell) cell.emp_act(severity_class + 15) - - //possibly damage some modules - take_hit((100/severity_class), "electrical pulse", 1) - -/obj/item/rig/proc/shock(mob/user) - if(get_dist(src, user) <= 1) //Needs to be adjecant to the rig to get shocked. - if(electrocute_mob(user, cell, src)) //electrocute_mob() handles removing charge from the cell, no need to do that here. - spark_system.start() - if(user.stunned) - return 1 - return 0 - -/obj/item/rig/proc/take_hit(damage, source, is_emp=0) - - if(!installed_modules.len) - return - - var/chance - if(!is_emp) - chance = 2*max(0, damage - (chest? chest.breach_threshold : 0)) - else - //Want this to be roughly independant of the number of modules, meaning that X emp hits will disable Y% of the suit's modules on average. - //that way people designing hardsuits don't have to worry (as much) about how adding that extra module will affect emp resiliance by 'soaking' hits for other modules - chance = 2*max(0, damage - emp_protection)*min(installed_modules.len/15, 1) - - if(!prob(chance)) - return - - //deal addition damage to already damaged module first. - //This way the chances of a module being disabled aren't so remote. - var/list/valid_modules = list() - var/list/damaged_modules = list() - for(var/obj/item/rig_module/module in installed_modules) - if(module.damage < 2) - valid_modules |= module - if(module.damage > 0) - damaged_modules |= module - - var/obj/item/rig_module/dam_module = null - if(damaged_modules.len) - dam_module = pick(damaged_modules) - else if(valid_modules.len) - dam_module = pick(valid_modules) - - if(!dam_module) return - - dam_module.damage++ - - if(!source) - source = "hit" - - if(wearer) - if(dam_module.damage >= 2) - to_chat(wearer, "The [source] has disabled your [dam_module.interface_name]!") - else - to_chat(wearer, "The [source] has damaged your [dam_module.interface_name]!") - dam_module.deactivate() - -/obj/item/rig/proc/malfunction_check(var/mob/living/carbon/human/user) - if(malfunction_delay) - if(offline) - to_chat(user, "The suit is completely unresponsive.") - else - to_chat(user, "ERROR: Hardware fault. Rebooting interface...") - return 1 - return 0 - -/obj/item/rig/proc/ai_can_move_suit(var/mob/user, var/check_user_module = 0, var/check_for_ai = 0) - - if(check_for_ai) - if(!(locate(/obj/item/rig_module/ai_container) in contents)) - return 0 - var/found_ai - for(var/obj/item/rig_module/ai_container/module in contents) - if(module.damage >= 2) - continue - if(module.integrated_ai && module.integrated_ai.client && !module.integrated_ai.stat) - found_ai = 1 - break - if(!found_ai) - return 0 - - if(check_user_module) - if(!user || !user.loc || !user.loc.loc) - return 0 - var/obj/item/rig_module/ai_container/module = user.loc.loc - if(!istype(module) || module.damage >= 2) - to_chat(user, "Your host module is unable to interface with the suit.") - return 0 - - if(offline || !cell || !cell.charge || locked_down) - if(user) - to_chat(user, "Your host rig is unpowered and unresponsive.") - return 0 - if(!wearer || wearer.back != src) - if(user) - to_chat(user, "Your host rig is not being worn.") - return 0 - if(!wearer.stat && !control_overridden && !ai_override_enabled) - if(user) - to_chat(user, "You are locked out of the suit servo controller.") - return 0 - return 1 - -/obj/item/rig/proc/force_rest(var/mob/user) - if(!ai_can_move_suit(user, check_user_module = 1)) - return - wearer.lay_down() - to_chat(user, "\The [wearer] is now [wearer.resting ? "resting" : "getting up"].") - -/obj/item/rig/proc/forced_move(var/direction, var/mob/user) - - // Why is all this shit in client/Move()? Who knows? - if(world.time < wearer_move_delay) - return - - if(!wearer || !wearer.loc || !ai_can_move_suit(user, check_user_module = 1)) - return - - //This is sota the goto stop mobs from moving var - if(wearer.notransform || !wearer.canmove) - return - - if(!wearer.lastarea) - wearer.lastarea = get_area(wearer.loc) - - if((istype(wearer.loc, /turf/space)) || (wearer.lastarea.has_gravity == 0)) - if(!wearer.Process_Spacemove(0)) - return 0 - - if(malfunctioning) - direction = pick(GLOB.cardinal) - - // Inside an object, tell it we moved. - if(isobj(wearer.loc) || ismob(wearer.loc)) - var/atom/O = wearer.loc - return O.relaymove(wearer, direction) - - if(isturf(wearer.loc)) - if(wearer.restrained())//Why being pulled while cuffed prevents you from moving - for(var/mob/M in range(wearer, 1)) - if(M.pulling == wearer) - if(!M.restrained() && M.stat == 0 && M.canmove && wearer.Adjacent(M)) - to_chat(user, "Your host is restrained! They can't move!") - return 0 - else - M.stop_pulling() - - // AIs are a bit slower than regular and ignore move intent. - wearer_move_delay = world.time + ai_controlled_move_delay - - if(wearer.buckled) //if we're buckled to something, tell it we moved. - return wearer.buckled.relaymove(wearer, direction) - - if(cell.use(200)) //Arbitrary, TODO - wearer.Move(get_step(get_turf(wearer),direction),direction) - -// This returns the rig if you are contained inside one, but not if you are wearing it -/atom/proc/get_rig() - if(loc) - return loc.get_rig() - return null - -/obj/item/rig/get_rig() - return src - -/mob/living/carbon/human/get_rig() - if(istype(back,/obj/item/rig)) - return back - else - return null - -/obj/item/rig/proc/emergency_release() - set name = "Suit Emergency Release" - set desc = "Activate the suits emergency release system." - set category = "Object" - set src in oview(1) - var/obj/item/rig/T = get_rig() - return T.do_emergency_release(usr) - -/obj/item/rig/proc/do_emergency_release(var/mob/living/user) - if(!can_touch(user, wearer) || !has_emergency_release) - return can_touch(user,wearer) - usr.visible_message("[user] starts activating \the [src] emergency seals release!") - if(!do_after(user, 240, target = wearer)) - to_chat(user, "You need to focus on activating the emergency release.") - return 0 - usr.visible_message("[user] activated \the [src] emergency seals release!") - malfunctioning += 1 - malfunction_delay = 30 - unseal(user) - return 1 - -/obj/item/rig/proc/can_touch(var/mob/user, var/mob/wearer) - if(!user) - return 0 - if(!wearer.Adjacent(user)) - return 0 - if(user.restrained()) - to_chat(user, "You need your hands free for this.") - return 0 - if(user.stat || user.paralysis || user.sleeping || user.lying || user.IsWeakened()) - return 0 - return 1 -#undef ONLY_DEPLOY -#undef ONLY_RETRACT -#undef SEAL_DELAY diff --git a/code/modules/clothing/spacesuits/rig/rig_armormod.dm b/code/modules/clothing/spacesuits/rig/rig_armormod.dm deleted file mode 100644 index c04270aa97b..00000000000 --- a/code/modules/clothing/spacesuits/rig/rig_armormod.dm +++ /dev/null @@ -1,30 +0,0 @@ -/obj/item/clothing/suit/space/new_rig/calc_breach_damage() - ..() - holder.update_armor() //New dammage, new armormultiplikator. - return damage - -/obj/item/rig/proc/update_armor() - var/multi = 1 //Multiplicative modification to the armor, maybe add an additive later on - if(chest) - multi *= (100 - chest.damage) / 100 //If we have some breaches, lower the armor value. - - //TODO check for other armor mods, likely modules, which need to be coded. - if(!armor) //Did we even give them some armor, if this is the case, the list should be initialized from New() - return - - var/datum/armor/A = armor - for(var/obj/item/piece in list(gloves, helmet, boots, chest)) - if(!istype(piece)) //Do we have the piece - continue - - piece.armor = piece.armor.setRating(melee_value = A.getRating("melee") * multi, - bullet_value = A.getRating("bullet") * multi, - laser_value = A.getRating("laser") * multi, - energy_value = A.getRating("energy") * multi, - bomb_value = A.getRating("bomb") * multi, - bio_value = A.getRating("bio") * multi, - rad_value = A.getRating("rad") * multi, - fire_value = A.getRating("fire") * multi, - acid_value = A.getRating("acidd") * multi) - -//Perfect place to also add something like shield modules, or any other hit_reaction modules check. diff --git a/code/modules/clothing/spacesuits/rig/rig_attackby.dm b/code/modules/clothing/spacesuits/rig/rig_attackby.dm deleted file mode 100644 index b48e17b2c41..00000000000 --- a/code/modules/clothing/spacesuits/rig/rig_attackby.dm +++ /dev/null @@ -1,196 +0,0 @@ -/obj/item/rig/attackby(obj/item/W as obj, mob/user as mob) - - if(!istype(user,/mob/living)) return 0 - - if(electrified != 0) - if(shock(user)) //Handles removing charge from the cell, as well. No need to do that here. - return - - // Pass repair items on to the chestpiece. - if(chest && (istype(W,/obj/item/stack) || istype(W, /obj/item/weldingtool))) - return chest.attackby(W,user) - - // Lock or unlock the access panel. - if(W.GetID()) - if(subverted) - locked = 0 - to_chat(user, "It looks like the locking system has been shorted out.") - return - - if((!req_access || !req_access.len) && (!req_one_access || !req_one_access.len)) - locked = 0 - to_chat(user, "\The [src] doesn't seem to have a locking mechanism.") - return - - if(security_check_enabled && !src.allowed(user)) - to_chat(user, "Access denied.") - return - - locked = !locked - to_chat(user, "You [locked ? "lock" : "unlock"] \the [src] access panel.") - return - - else if(istype(W,/obj/item/crowbar)) - - if(!open && locked) - to_chat(user, "The access panel is locked shut.") - return - - open = !open - to_chat(user, "You [open ? "open" : "close"] the access panel.") - return - - if(open) - - // Hacking. - if(istype(W,/obj/item/wirecutters) || istype(W,/obj/item/multitool)) - if(open) - wires.Interact(user) - else - to_chat(user, "You can't reach the wiring.") - return - // Air tank. - if(istype(W,/obj/item/tank)) //Todo, some kind of check for suits without integrated air supplies. - - if(air_supply) - to_chat(user, "\The [src] already has a tank installed.") - return - - user.unEquip(W) - air_supply = W - W.forceMove(src) - to_chat(user, "You slot [W] into [src] and tighten the connecting valve.") - return - - // Check if this is a hardsuit upgrade or a modification. - else if(istype(W,/obj/item/rig_module)) - - if(istype(src.loc,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = src.loc - if(H.back == src) - to_chat(user, "You can't install a hardsuit module while the suit is being worn.") - return 1 - - if(!installed_modules) installed_modules = list() - if(installed_modules.len) - for(var/obj/item/rig_module/installed_mod in installed_modules) - if(!installed_mod.redundant && istype(installed_mod,W)) - to_chat(user, "The hardsuit already has a module of that class installed.") - return 1 - - var/obj/item/rig_module/mod = W - to_chat(user, "You begin installing \the [mod] into \the [src].") - if(!do_after(user, 40 * W.toolspeed, target = src)) - return - if(!user || !W) - return - to_chat(user, "You install \the [mod] into \the [src].") - user.unEquip(mod) - installed_modules |= mod - mod.forceMove(src) - mod.installed(src) - update_icon() - return 1 - - else if(!cell && istype(W,/obj/item/stock_parts/cell)) - - to_chat(user, "You jack \the [W] into \the [src]'s battery mount.") - user.unEquip(W) - W.forceMove(src) - src.cell = W - return - - else if(istype(W,/obj/item/wrench)) - - if(!air_supply) - to_chat(user, "There is not tank to remove.") - return - - if(user.r_hand && user.l_hand) - air_supply.forceMove(get_turf(user)) - else - user.put_in_hands(air_supply) - to_chat(user, "You detach and remove \the [air_supply].") - air_supply = null - return - - else if(istype(W,/obj/item/screwdriver)) - - var/list/current_mounts = list() - if(cell) current_mounts += "cell" - if(installed_modules && installed_modules.len) current_mounts += "system module" - - var/to_remove = input("Which would you like to modify?") as null|anything in current_mounts - if(!to_remove) - return - - if(istype(src.loc,/mob/living/carbon/human) && to_remove != "cell") - var/mob/living/carbon/human/H = src.loc - if(H.back == src) - to_chat(user, "You can't remove an installed device while the hardsuit is being worn.") - return - - switch(to_remove) - - if("cell") - - if(cell) - to_chat(user, "You detatch \the [cell] from \the [src]'s battery mount.") - for(var/obj/item/rig_module/module in installed_modules) - module.deactivate() - if(user.r_hand && user.l_hand) - cell.forceMove(get_turf(user)) - else - user.put_in_hands(cell) - cell = null - else - to_chat(user, "There is nothing loaded in that mount.") - - if("system module") - - var/list/possible_removals = list() - for(var/obj/item/rig_module/module in installed_modules) - if(module.permanent) - continue - possible_removals[module.name] = module - - if(!possible_removals.len) - to_chat(user, "There are no installed modules to remove.") - return - - var/removal_choice = input("Which module would you like to remove?") as null|anything in possible_removals - if(!removal_choice) - return - - var/obj/item/rig_module/removed = possible_removals[removal_choice] - to_chat(user, "You detatch \the [removed] from \the [src].") - removed.forceMove(get_turf(src)) - removed.removed() - installed_modules -= removed - update_icon() - - return - - // If we've gotten this far, all we have left to do before we pass off to root procs - // is check if any of the loaded modules want to use the item we've been given. - for(var/obj/item/rig_module/module in installed_modules) - if(module.accepts_item(W,user)) //Item is handled in this proc - return - ..() - - -/obj/item/rig/attack_hand(var/mob/user) - - if(electrified != 0) - if(shock(user)) //Handles removing charge from the cell, as well. No need to do that here. - return - ..() - -/obj/item/rig/emag_act(var/remaining_charges, var/mob/user) - if(!subverted) - req_access.Cut() - req_one_access.Cut() - locked = 0 - subverted = 1 - to_chat(user, "You short out the access protocol for the suit.") - return 1 diff --git a/code/modules/clothing/spacesuits/rig/rig_pieces.dm b/code/modules/clothing/spacesuits/rig/rig_pieces.dm deleted file mode 100644 index 246e3beb10f..00000000000 --- a/code/modules/clothing/spacesuits/rig/rig_pieces.dm +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Defines the helmets, gloves and shoes for rigs. - */ - -/obj/item/clothing/head/helmet/space/new_rig - name = "helmet" - flags = BLOCKHAIR | THICKMATERIAL | NODROP - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|HIDEMASK - body_parts_covered = HEAD - heat_protection = HEAD - cold_protection = HEAD - var/brightness_on = 4 - var/on = 0 - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Skrell" = 'icons/mob/species/skrell/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi' - ) - species_restricted = null - actions_types = list(/datum/action/item_action/toggle_helmet_light) - - flash_protect = 2 - -/obj/item/clothing/head/helmet/space/new_rig/attack_self(mob/user) - if(!isturf(user.loc)) - to_chat(user, "You cannot turn the light on while in this [user.loc].")//To prevent some lighting anomalities. - - return - toggle_light(user) - -/obj/item/clothing/head/helmet/space/new_rig/proc/toggle_light(mob/user) - if(flags & AIRTIGHT) //Could also check for STOPSPRESSUREDMAGE, but one is enough, both get toggled when the seal gets toggled. - - on = !on - icon_state = "[item_color][on]" - - if(on) - set_light(brightness_on) - else - set_light(0) - else - to_chat(user, "You cannot turn the light on while the suit isn't sealed.") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_head() - -/obj/item/clothing/gloves/rig - name = "gauntlets" - flags = THICKMATERIAL | NODROP - body_parts_covered = HANDS - heat_protection = HANDS - cold_protection = HANDS - species_restricted = null - gender = PLURAL - -/obj/item/clothing/shoes/magboots/rig - name = "boots" - flags = NODROP - body_parts_covered = FEET - cold_protection = FEET - heat_protection = FEET - species_restricted = null - gender = PLURAL - -/obj/item/clothing/shoes/magboots/rig/attack_self(mob/user) - if(flags & AIRTIGHT) //Could also check for STOPSPRESSUREDMAGE, but one is enough, both get toggled when the seal gets toggled. - ..(user) - else - to_chat(user, "You cannot activate mag-pulse traction system while the suit is not sealed.") - -/obj/item/clothing/suit/space/new_rig - name = "chestpiece" - allowed = list(/obj/item/flashlight,/obj/item/tank) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEJUMPSUIT|HIDETAIL - flags = STOPSPRESSUREDMAGE | THICKMATERIAL | AIRTIGHT | NODROP - slowdown = 0 - breach_threshold = 20 - resilience = 0.2 - can_breach = 1 - var/obj/item/rig/holder - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi' - ) - -//TODO: move this to modules -/obj/item/clothing/head/helmet/space/new_rig/proc/prevent_track() - return 0 - -/obj/item/clothing/gloves/rig/Touch(var/atom/A, var/proximity) - - if(!A || !proximity) - return 0 - - var/mob/living/carbon/human/H = loc - if(!istype(H) || !H.back) - return 0 - - var/obj/item/rig/suit = H.back - if(!suit || !istype(suit) || !suit.installed_modules.len) - return 0 - - for(var/obj/item/rig_module/module in suit.installed_modules) - if(module.active && module.activates_on_touch) - if(module.engage(A)) - return 1 - - return 0 - -//Rig pieces for non-spacesuit based rigs - -/obj/item/clothing/head/lightrig - name = "mask" - body_parts_covered = HEAD - heat_protection = HEAD - cold_protection = HEAD - flags = THICKMATERIAL|AIRTIGHT - -/obj/item/clothing/suit/lightrig - name = "suit" - allowed = list(/obj/item/flashlight) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEJUMPSUIT - flags = THICKMATERIAL - -/obj/item/clothing/shoes/lightrig - name = "boots" - body_parts_covered = FEET - cold_protection = FEET - heat_protection = FEET - species_restricted = null - gender = PLURAL - -/obj/item/clothing/gloves/lightrig - name = "gloves" - flags = THICKMATERIAL - body_parts_covered = HANDS - heat_protection = HANDS - cold_protection = HANDS - species_restricted = null - gender = PLURAL diff --git a/code/modules/clothing/spacesuits/rig/rig_verbs.dm b/code/modules/clothing/spacesuits/rig/rig_verbs.dm deleted file mode 100644 index 4a71f6c67bc..00000000000 --- a/code/modules/clothing/spacesuits/rig/rig_verbs.dm +++ /dev/null @@ -1,335 +0,0 @@ -// Interface for humans. -/obj/item/rig/verb/hardsuit_interface() - set name = "Open Hardsuit Interface" - set desc = "Open the hardsuit system interface." - set category = "Hardsuit" - set src = usr.contents - - if(wearer && wearer.back == src) - ui_interact(usr) - -/obj/item/rig/verb/toggle_vision() - set name = "Toggle Visor" - set desc = "Turns your rig visor off or on." - set category = "Hardsuit" - set src = usr.contents - - if(!istype(wearer) || !wearer.back == src) - to_chat(usr, "The hardsuit is not being worn.") - return - - if(!check_power_cost(usr)) - return - - if(!(flags & NODROP)) - to_chat(usr, "The suit is not active.") - return - - if(!check_suit_access(usr)) - return - - if(!visor) - to_chat(usr, "The hardsuit does not have a configurable visor.") - return - - var/mob/M = usr - if(M.incapacitated()) - return - - if(!visor.active) - visor.activate() - else - visor.deactivate() - -/obj/item/rig/proc/toggle_helmet() - set name = "Toggle Helmet" - set desc = "Deploys or retracts your helmet." - set category = "Hardsuit" - set src = usr.contents - - if(!istype(wearer) || !wearer.back == src) - to_chat(usr, "The hardsuit is not being worn.") - return - - if(!check_suit_access(usr)) - return - - var/mob/M = usr - if(M.incapacitated()) - return - - toggle_piece("helmet", usr) - -/obj/item/rig/proc/toggle_chest() - set name = "Toggle Chestpiece" - set desc = "Deploys or retracts your chestpiece." - set category = "Hardsuit" - set src = usr.contents - - if(!check_suit_access(usr)) - return - - var/mob/M = usr - if(M.incapacitated()) - return - - toggle_piece("chest", usr) - -/obj/item/rig/proc/toggle_gauntlets() - set name = "Toggle Gauntlets" - set desc = "Deploys or retracts your gauntlets." - set category = "Hardsuit" - set src = usr.contents - - if(!istype(wearer) || !wearer.back == src) - to_chat(usr, "The hardsuit is not being worn.") - return - - if(!check_suit_access(usr)) - return - - var/mob/M = usr - if(M.incapacitated()) - return - - toggle_piece("gauntlets", usr) - -/obj/item/rig/proc/toggle_boots() - set name = "Toggle Boots" - set desc = "Deploys or retracts your boots." - set category = "Hardsuit" - set src = usr.contents - - if(!istype(wearer) || !wearer.back == src) - to_chat(usr, "The hardsuit is not being worn.") - return - - if(!check_suit_access(usr)) - return - - var/mob/M = usr - if(M.incapacitated()) - return - - toggle_piece("boots", usr) - -/obj/item/rig/verb/deploy_suit() - set name = "Deploy Hardsuit" - set desc = "Deploys helmet, gloves and boots all at once." - set category = "Hardsuit" - set src = usr.contents - - if(!istype(wearer) || !wearer.back == src) - to_chat(usr, "The hardsuit is not being worn.") - return - - if(!check_suit_access(usr)) - return - - if(!check_power_cost(usr)) - return - - var/mob/M = usr - if(M.incapacitated()) - return - - deploy(wearer, usr) - -/obj/item/rig/verb/toggle_seals_verb() - set name = "Toggle Hardsuit Seals" - set desc = "Seals or unseals your rig." - set category = "Hardsuit" - set src = usr.contents - - if(!istype(wearer) || !wearer.back == src) - to_chat(usr, "The hardsuit is not being worn.") - return - - if(!check_suit_access(usr)) - return - - var/mob/M = usr - if(M.incapacitated()) - return - - if(flags & NODROP) - unseal(usr) - else - seal(usr) - -/obj/item/rig/verb/switch_vision_mode() - set name = "Switch Vision Mode" - set desc = "Switches between available vision modes." - set category = "Hardsuit" - set src = usr.contents - - if(malfunction_check(usr)) - return - - if(!check_power_cost(usr, 0, 0, 0, 0)) - return - - if(!(flags & NODROP)) - to_chat(usr, "The suit is not active.") - return - - if(!visor) - to_chat(usr, "The hardsuit does not have a configurable visor.") - return - - var/mob/M = usr - if(M.incapacitated()) - return - - if(!visor.active) - visor.activate() - - if(!visor.active) - to_chat(usr, "The visor is suffering a hardware fault and cannot be configured.") - return - - visor.engage() - -/obj/item/rig/verb/alter_voice() - set name = "Configure Voice Synthesiser" - set desc = "Toggles or configures your voice synthesizer." - set category = "Hardsuit" - set src = usr.contents - - if(malfunction_check(usr)) - return - - if(!(flags & NODROP)) - to_chat(usr, "The suit is not active.") - return - - if(!istype(wearer) || !wearer.back == src) - to_chat(usr, "The hardsuit is not being worn.") - return - - if(!speech) - to_chat(usr, "The hardsuit does not have a speech synthesiser.") - return - - var/mob/M = usr - if(M.incapacitated()) - return - - speech.engage() - -/obj/item/rig/verb/select_module() - set name = "Select Module" - set desc = "Selects a module as your primary system." - set category = "Hardsuit" - set src = usr.contents - - if(malfunction_check(usr)) - return - - if(!check_power_cost(usr, 0, 0, 0, 0)) - return - - if(!(flags & NODROP)) - to_chat(usr, "The suit is not active.") - return - - if(!istype(wearer) || !wearer.back == src) - to_chat(usr, "The hardsuit is not being worn.") - return - - var/mob/M = usr - if(M.incapacitated()) - return - - var/list/selectable = list() - for(var/obj/item/rig_module/module in installed_modules) - if(module.selectable) - selectable |= module - - var/obj/item/rig_module/module = input("Which module do you wish to select?") as null|anything in selectable - - if(!istype(module)) - selected_module = null - to_chat(usr, "Primary system is now: deselected.") - return - - selected_module = module - to_chat(usr, "Primary system is now: [selected_module.interface_name].") - -/obj/item/rig/verb/toggle_module() - set name = "Toggle Module" - set desc = "Toggle a system module." - set category = "Hardsuit" - set src = usr.contents - - if(malfunction_check(usr)) - return - - if(!check_power_cost(usr, 0, 0, 0, 0)) - return - - if(!(flags & NODROP)) - to_chat(usr, "The suit is not active.") - return - - if(!istype(wearer) || !wearer.back == src) - to_chat(usr, "The hardsuit is not being worn.") - return - - var/mob/M = usr - if(M.incapacitated()) - return - - var/list/selectable = list() - for(var/obj/item/rig_module/module in installed_modules) - if(module.toggleable) - selectable |= module - - var/obj/item/rig_module/module = input("Which module do you wish to toggle?") as null|anything in selectable - - if(!istype(module)) - return - - if(module.active) - to_chat(usr, "You attempt to deactivate \the [module.interface_name].") - module.deactivate() - else - to_chat(usr, "You attempt to activate \the [module.interface_name].") - module.activate() - -/obj/item/rig/verb/engage_module() - set name = "Engage Module" - set desc = "Engages a system module." - set category = "Hardsuit" - set src = usr.contents - - if(malfunction_check(usr)) - return - - if(!(flags & NODROP)) - to_chat(usr, "The suit is not active.") - return - - if(!istype(wearer) || !wearer.back == src) - to_chat(usr, "The hardsuit is not being worn.") - return - - if(!check_power_cost(usr, 0, 0, 0, 0)) - return - - var/mob/M = usr - if(M.incapacitated()) - return - - var/list/selectable = list() - for(var/obj/item/rig_module/module in installed_modules) - if(module.usable) - selectable |= module - - var/obj/item/rig_module/module = input("Which module do you wish to engage?") as null|anything in selectable - - if(!istype(module)) - return - - to_chat(usr, "You attempt to engage the [module.interface_name].") - module.engage() diff --git a/code/modules/clothing/spacesuits/rig/rig_wiring.dm b/code/modules/clothing/spacesuits/rig/rig_wiring.dm deleted file mode 100644 index 9d3108ac016..00000000000 --- a/code/modules/clothing/spacesuits/rig/rig_wiring.dm +++ /dev/null @@ -1,70 +0,0 @@ -/datum/wires/rig - random = 1 - holder_type = /obj/item/rig - wire_count = 5 - -#define RIG_SECURITY 1 -#define RIG_AI_OVERRIDE 2 -#define RIG_SYSTEM_CONTROL 4 -#define RIG_INTERFACE_LOCK 8 -#define RIG_INTERFACE_SHOCK 16 -/* - * Rig security can be snipped to disable ID access checks on rig. - * Rig AI override can be pulsed to toggle whether or not the AI can take control of the suit. - * System control can be pulsed to toggle some malfunctions. - * Interface lock can be pulsed to toggle whether or not the interface can be accessed. - */ - -/datum/wires/rig/UpdateCut(var/index, var/mended) - - var/obj/item/rig/rig = holder - switch(index) - if(RIG_SECURITY) - if(mended) - rig.req_access = initial(rig.req_access) - rig.req_one_access = initial(rig.req_one_access) - if(RIG_INTERFACE_SHOCK) - rig.electrified = mended ? 0 : -1 - rig.shock(usr,100) - -/datum/wires/rig/UpdatePulsed(var/index) - - var/obj/item/rig/rig = holder - switch(index) - if(RIG_SECURITY) - rig.security_check_enabled = !rig.security_check_enabled - rig.visible_message("\The [rig] twitches as several suit locks [rig.security_check_enabled?"close":"open"].") - if(RIG_AI_OVERRIDE) - rig.ai_override_enabled = !rig.ai_override_enabled - rig.visible_message("A small red light on [rig] [rig.ai_override_enabled?"goes dead":"flickers on"].") - if(RIG_SYSTEM_CONTROL) - rig.malfunctioning += 10 - if(rig.malfunction_delay <= 0) - rig.malfunction_delay = 20 - rig.shock(usr,100) - if(RIG_INTERFACE_LOCK) - rig.interface_locked = !rig.interface_locked - rig.visible_message("\The [rig] clicks audibly as the software interface [rig.interface_locked?"darkens":"brightens"].") - if(RIG_INTERFACE_SHOCK) - if(rig.electrified != -1) - rig.electrified = 30 - rig.shock(usr,100) - -/datum/wires/rig/GetWireName(index) - switch(index) - if(RIG_SECURITY) - return "ID check" - if(RIG_AI_OVERRIDE) - return "AI control" - if(RIG_SYSTEM_CONTROL) - return "System control" - if(RIG_INTERFACE_LOCK) - return "Interface lock" - if(RIG_INTERFACE_SHOCK) - return "Electrification" - -/datum/wires/rig/CanUse(var/mob/living/L) - var/obj/item/rig/rig = holder - if(rig.open) - return 1 - return 0 diff --git a/code/modules/clothing/spacesuits/rig/suits/alien.dm b/code/modules/clothing/spacesuits/rig/suits/alien.dm deleted file mode 100644 index 25c784ba7dd..00000000000 --- a/code/modules/clothing/spacesuits/rig/suits/alien.dm +++ /dev/null @@ -1,46 +0,0 @@ -/obj/item/rig/unathi - name = "NT breacher chassis control module" - desc = "A cheap NT knock-off of an Unathi battle-rig. Looks like a fish, moves like a fish, steers like a cow." - suit_type = "NT breacher" - icon_state = "breacher_rig_cheap" - armor = list(melee = 30, bullet = 30, laser = 30, energy = 30, bomb = 45, bio = 100, rad = 50) - emp_protection = -20 - active_slowdown = 6 - offline_slowdown = 10 - vision_restriction = 1 - offline_vision_restriction = 2 - - chest_type = /obj/item/clothing/suit/space/new_rig/unathi - helm_type = /obj/item/clothing/head/helmet/space/new_rig/unathi - glove_type = /obj/item/clothing/gloves/rig/unathi - boot_type = /obj/item/clothing/shoes/magboots/rig/unathi - -/obj/item/rig/unathi/fancy - name = "breacher chassis control module" - desc = "An authentic Unathi breacher chassis. Huge, bulky and absurdly heavy. It must be like wearing a tank." - suit_type = "breacher chassis" - icon_state = "breacher_rig" - armor = list(melee = 45, bullet = 45, laser = 45, energy = 45, bomb = 45, bio = 100, rad = 75) //Takes TEN TIMES as much damage to stop someone in a breacher. In exchange, it's slow. //Whoever made this was on meth - vision_restriction = 0 - -/obj/item/clothing/head/helmet/space/new_rig/unathi - icon = 'icons/obj/clothing/species/unathi/hats.dmi' - species_restricted = list("Unathi") - -/obj/item/clothing/suit/space/new_rig/unathi - icon = 'icons/obj/clothing/species/unathi/suits.dmi' - species_restricted = list("Unathi") - -/obj/item/clothing/gloves/rig/unathi - icon = 'icons/obj/clothing/species/unathi/gloves.dmi' - species_restricted = list("Unathi") - sprite_sheets = list( - "Unathi" = 'icons/mob/species/unathi/gloves.dmi' - ) - -/obj/item/clothing/shoes/magboots/rig/unathi - icon = 'icons/obj/clothing/species/unathi/shoes.dmi' - species_restricted = list("Unathi") - sprite_sheets = list( - "Unathi" = 'icons/mob/species/unathi/feet.dmi' - ) diff --git a/code/modules/clothing/spacesuits/rig/suits/combat.dm b/code/modules/clothing/spacesuits/rig/suits/combat.dm deleted file mode 100644 index cc05c8af2f7..00000000000 --- a/code/modules/clothing/spacesuits/rig/suits/combat.dm +++ /dev/null @@ -1,28 +0,0 @@ -/obj/item/clothing/head/helmet/space/new_rig/combat - -/obj/item/rig/combat - name = "combat hardsuit control module" - desc = "A sleek and dangerous hardsuit for active combat." - icon_state = "security_rig" - suit_type = "combat hardsuit" - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100) - active_slowdown = 1 - offline_slowdown = 3 - offline_vision_restriction = 1 - - helm_type = /obj/item/clothing/head/helmet/space/new_rig/combat - allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/baton) - - -/obj/item/rig/combat/equipped - - - initial_modules = list( - /obj/item/rig_module/mounted, - /obj/item/rig_module/vision/thermal, - /obj/item/rig_module/grenade_launcher, - /obj/item/rig_module/ai_container, - // /obj/item/rig_module/power_sink, - /obj/item/rig_module/electrowarfare_suite, - /obj/item/rig_module/chem_dispenser/combat - ) diff --git a/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm b/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm deleted file mode 100644 index 5485e1fe882..00000000000 --- a/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm +++ /dev/null @@ -1,81 +0,0 @@ -/obj/item/clothing/head/helmet/space/new_rig/ert - -/obj/item/rig/ert - name = "ERT-C hardsuit control module" - desc = "A suit worn by the commander of an Emergency Response Team. Has blue highlights. Armoured and space ready." - suit_type = "ERT commander" - icon_state = "ert_commander_rig" - - helm_type = /obj/item/clothing/head/helmet/space/new_rig/ert - - req_access = list(ACCESS_CENT_SPECOPS) - - armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 50) - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd, /obj/item/crowbar, \ - /obj/item/screwdriver, /obj/item/weldingtool, /obj/item/wirecutters, /obj/item/wrench, /obj/item/multitool, \ - /obj/item/radio, /obj/item/analyzer,/obj/item/storage/briefcase/inflatable, /obj/item/melee/baton, /obj/item/gun, \ - /obj/item/storage/firstaid, /obj/item/reagent_containers/hypospray, /obj/item/roller) - - initial_modules = list( - /obj/item/rig_module/ai_container, - /obj/item/rig_module/maneuvering_jets, - /obj/item/rig_module/datajack, - ) - -/obj/item/rig/ert/engineer - name = "ERT-E suit control module" - desc = "A suit worn by the engineering division of an Emergency Response Team. Has orange highlights. Armoured and space ready." - suit_type = "ERT engineer" - icon_state = "ert_engineer_rig" - siemens_coefficient = 0 - - initial_modules = list( - /obj/item/rig_module/ai_container, - /obj/item/rig_module/maneuvering_jets, - /obj/item/rig_module/device/plasmacutter, - // /obj/item/rig_module/device/rcd - ) - -/obj/item/rig/ert/medical - name = "ERT-M suit control module" - desc = "A suit worn by the medical division of an Emergency Response Team. Has white highlights. Armoured and space ready." - suit_type = "ERT medic" - icon_state = "ert_medical_rig" - - initial_modules = list( - /obj/item/rig_module/ai_container, - /obj/item/rig_module/maneuvering_jets, - /obj/item/rig_module/device/healthscanner, - /obj/item/rig_module/chem_dispenser/injector - ) - -/obj/item/rig/ert/security - name = "ERT-S suit control module" - desc = "A suit worn by the security division of an Emergency Response Team. Has red highlights. Armoured and space ready." - suit_type = "ERT security" - icon_state = "ert_security_rig" - - initial_modules = list( - /obj/item/rig_module/ai_container, - /obj/item/rig_module/maneuvering_jets, - /obj/item/rig_module/grenade_launcher, - /obj/item/rig_module/mounted/egun, - ) - -/obj/item/rig/ert/assetprotection - name = "Heavy Asset Protection suit control module" - desc = "A heavy suit worn by the highest level of Asset Protection, don't mess with the person wearing this. Armoured and space ready." - suit_type = "heavy asset protection" - icon_state = "asset_protection_rig" - - initial_modules = list( - /obj/item/rig_module/ai_container, - /obj/item/rig_module/maneuvering_jets, - /obj/item/rig_module/grenade_launcher, - /obj/item/rig_module/vision/multi, - /obj/item/rig_module/mounted/egun, - /obj/item/rig_module/chem_dispenser/injector, - /obj/item/rig_module/device/plasmacutter, - // /obj/item/rig_module/device/rcd, - /obj/item/rig_module/datajack - ) diff --git a/code/modules/clothing/spacesuits/rig/suits/light.dm b/code/modules/clothing/spacesuits/rig/suits/light.dm deleted file mode 100644 index d76691daf57..00000000000 --- a/code/modules/clothing/spacesuits/rig/suits/light.dm +++ /dev/null @@ -1,120 +0,0 @@ -// Light rigs are not space-capable, but don't suffer excessive slowdown or sight issues when depowered. -/obj/item/rig/light - name = "light suit control module" - desc = "A lighter, less armoured rig suit." - icon_state = "ninja_rig" - suit_type = "light suit" - allowed = list(/obj/item/gun,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/stock_parts/cell) - emp_protection = 10 - active_slowdown = 0 - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - offline_slowdown = 0 - offline_vision_restriction = 0 - - chest_type = /obj/item/clothing/suit/space/new_rig/light - helm_type = /obj/item/clothing/head/helmet/space/new_rig/light - boot_type = /obj/item/clothing/shoes/magboots/rig/light - glove_type = /obj/item/clothing/gloves/rig/light - -/obj/item/clothing/suit/space/new_rig/light - name = "suit" - breach_threshold = 18 //comparable to voidsuits - -/obj/item/clothing/gloves/rig/light - name = "gloves" - -/obj/item/clothing/shoes/magboots/rig/light - name = "shoes" - -/obj/item/clothing/head/helmet/space/new_rig/light - name = "hood" - -/obj/item/rig/light/hacker - name = "cybersuit control module" - suit_type = "cyber" - desc = "An advanced powered armour suit with many cyberwarfare enhancements. Comes with built-in insulated gloves for safely tampering with electronics." - icon_state = "hacker_rig" - - req_access = list(ACCESS_SYNDICATE) - - airtight = 0 - seal_delay = 5 //not being vaccum-proof has an upside I guess - - helm_type = /obj/item/clothing/head/lightrig/hacker - chest_type = /obj/item/clothing/suit/lightrig/hacker - glove_type = /obj/item/clothing/gloves/lightrig/hacker - boot_type = /obj/item/clothing/shoes/lightrig/hacker - - initial_modules = list( - /obj/item/rig_module/ai_container, - // /obj/item/rig_module/power_sink, - /obj/item/rig_module/datajack, - /obj/item/rig_module/electrowarfare_suite, - /obj/item/rig_module/voice, - /obj/item/rig_module/vision, - ) - -//The cybersuit is not space-proof. It does however, have good siemens_coefficient values -/obj/item/clothing/head/lightrig/hacker - name = "HUD" - siemens_coefficient = 0.4 - flags = 0 - -/obj/item/clothing/suit/lightrig/hacker - siemens_coefficient = 0.4 - -/obj/item/clothing/shoes/lightrig/hacker - siemens_coefficient = 0.4 - flags = NOSLIP //All the other rigs have magboots anyways, hopefully gives the hacker suit something more going for it. - -/obj/item/clothing/gloves/lightrig/hacker - siemens_coefficient = 0 - -/obj/item/rig/light/ninja - name = "ominous suit control module" - suit_type = "ominous" - desc = "A unique, vaccum-proof suit of nano-enhanced armor designed specifically for Spider Clan assassins." - icon_state = "ninja_rig" - armor = list(melee = 50, bullet = 15, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 30) - emp_protection = 40 //change this to 30 if too high. - active_slowdown = 0 - - chest_type = /obj/item/clothing/suit/space/new_rig/light/ninja - glove_type = /obj/item/clothing/gloves/rig/light/ninja - - req_access = list(ACCESS_SYNDICATE) - - initial_modules = list( - /obj/item/rig_module/teleporter, - /obj/item/rig_module/stealth_field, - /obj/item/rig_module/mounted/energy_blade, - /obj/item/rig_module/vision, - /obj/item/rig_module/voice, - /obj/item/rig_module/chem_dispenser, - /obj/item/rig_module/grenade_launcher, - /obj/item/rig_module/fabricator, - /obj/item/rig_module/ai_container, - // /obj/item/rig_module/power_sink, - /obj/item/rig_module/datajack, - /obj/item/rig_module/self_destruct - ) - -/obj/item/clothing/gloves/rig/light/ninja - name = "insulated gloves" - siemens_coefficient = 0 - -/obj/item/clothing/suit/space/new_rig/light/ninja - breach_threshold = 38 //comparable to regular hardsuits - -/obj/item/rig/light/stealth - name = "stealth suit control module" - suit_type = "stealth" - desc = "A highly advanced and expensive suit designed for covert operations." - icon_state = "ninja_rig" //supposed to be "stealth_rig", but as it currently only has a semi-copied ninja rig sprite, we can just use them directly. - - req_access = list(ACCESS_SYNDICATE) - - initial_modules = list( - /obj/item/rig_module/stealth_field, - /obj/item/rig_module/vision - ) diff --git a/code/modules/clothing/spacesuits/rig/suits/merc.dm b/code/modules/clothing/spacesuits/rig/suits/merc.dm deleted file mode 100644 index 95abba52df9..00000000000 --- a/code/modules/clothing/spacesuits/rig/suits/merc.dm +++ /dev/null @@ -1,32 +0,0 @@ -/obj/item/clothing/head/helmet/space/new_rig/merc - -/obj/item/rig/merc - name = "crimson hardsuit control module" - desc = "A blood-red hardsuit featuring some fairly illegal technology." - icon_state = "merc_rig" - suit_type = "crimson hardsuit" - armor = list(melee = 40, bullet = 50, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 50) - active_slowdown = 1 - offline_slowdown = 3 - offline_vision_restriction = 1 - - helm_type = /obj/item/clothing/head/helmet/space/new_rig/merc - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/gun,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/restraints/handcuffs) - - initial_modules = list( - /obj/item/rig_module/mounted, - /obj/item/rig_module/vision/thermal, - /obj/item/rig_module/grenade_launcher, - /obj/item/rig_module/ai_container, - // /obj/item/rig_module/power_sink, - /obj/item/rig_module/electrowarfare_suite, - /obj/item/rig_module/chem_dispenser/combat, - // /obj/item/rig_module/fabricator/energy_net - ) - -//Has most of the modules removed -/obj/item/rig/merc/empty - initial_modules = list( - /obj/item/rig_module/ai_container, - /obj/item/rig_module/electrowarfare_suite, //might as well - ) diff --git a/code/modules/clothing/spacesuits/rig/suits/station.dm b/code/modules/clothing/spacesuits/rig/suits/station.dm deleted file mode 100644 index c4bbd059fae..00000000000 --- a/code/modules/clothing/spacesuits/rig/suits/station.dm +++ /dev/null @@ -1,223 +0,0 @@ -/obj/item/clothing/head/helmet/space/new_rig/industrial - -/obj/item/clothing/head/helmet/space/new_rig/ce - -/obj/item/clothing/head/helmet/space/new_rig/eva - -/obj/item/clothing/head/helmet/space/new_rig/hazmat - -/obj/item/clothing/head/helmet/space/new_rig/medical - -/obj/item/clothing/head/helmet/space/new_rig/hazard - -/obj/item/rig/internalaffairs - name = "augmented tie" - suit_type = "augmented suit" - desc = "Prepare for paperwork." - icon_state = "internalaffairs_rig" - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) - siemens_coefficient = 0.9 - active_slowdown = 0 - offline_slowdown = 0 - offline_vision_restriction = 0 - - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/briefcase,/obj/item/storage/secure/briefcase) - - req_access = list() - req_one_access = list() - - glove_type = null - helm_type = null - boot_type = null - -/obj/item/rig/internalaffairs/equipped - - req_access = list(ACCESS_LAWYER) - - initial_modules = list( - /obj/item/rig_module/ai_container, - /obj/item/rig_module/device/flash, - /obj/item/rig_module/device/paperdispenser, - /obj/item/rig_module/device/pen, - /obj/item/rig_module/device/stamp - ) - - glove_type = null - helm_type = null - boot_type = null - -/obj/item/rig/industrial - name = "industrial suit control module" - suit_type = "industrial hardsuit" - desc = "A heavy, powerful rig used by construction crews and mining corporations." - icon_state = "engineering_rig" - armor = list(melee = 10, bullet = 5, laser = 10, energy = 5, bomb = 10, bio = 100, rad = 75) - active_slowdown = 3 - offline_slowdown = 10 - offline_vision_restriction = 2 - emp_protection = -20 - - helm_type = /obj/item/clothing/head/helmet/space/new_rig/industrial - - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/bag/ore,/obj/item/t_scanner,/obj/item/pickaxe, /obj/item/rcd) - - req_access = list() - req_one_access = list() - - -/obj/item/rig/industrial/equipped - - initial_modules = list( - /obj/item/rig_module/device/plasmacutter, - /obj/item/rig_module/device/drill, - /obj/item/rig_module/device/orescanner, - // /obj/item/rig_module/device/rcd, - /obj/item/rig_module/vision/meson - ) - -/obj/item/rig/eva - name = "EVA suit control module" - suit_type = "EVA hardsuit" - desc = "A light rig for repairs and maintenance to the outside of habitats and vessels." - icon_state = "eva_rig" - active_slowdown = 0 - offline_slowdown = 1 - offline_vision_restriction = 1 - - helm_type = /obj/item/clothing/head/helmet/space/new_rig/eva - - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/toolbox,/obj/item/storage/briefcase/inflatable,/obj/item/t_scanner,/obj/item/rcd) - - req_access = list() - req_one_access = list() - -/obj/item/rig/eva/equipped - - initial_modules = list( - /obj/item/rig_module/device/plasmacutter, - /obj/item/rig_module/maneuvering_jets, - // /obj/item/rig_module/device/rcd, - /obj/item/rig_module/vision/meson - ) - -//Chief Engineer's rig. This is sort of a halfway point between the old hardsuits (voidsuits) and the rig class. -/obj/item/rig/ce - - name = "advanced voidsuit control module" - suit_type = "advanced voidsuit" - desc = "An advanced voidsuit that protects against hazardous, low pressure environments. Shines with a high polish." - icon_state = "ce_rig" - armor = list(melee = 40, bullet = 5, laser = 10, energy = 5, bomb = 50, bio = 100, rad = 90) - active_slowdown = 0 - offline_slowdown = 0 - offline_vision_restriction = 0 - - helm_type = /obj/item/clothing/head/helmet/space/new_rig/ce - - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/bag/ore,/obj/item/t_scanner,/obj/item/pickaxe, /obj/item/rcd) - - - req_access = list() - req_one_access = list() - - boot_type = null - glove_type = null - -/obj/item/rig/ce/equipped - - req_access = list(ACCESS_CE) - - initial_modules = list( - /obj/item/rig_module/ai_container, - /obj/item/rig_module/maneuvering_jets, - /obj/item/rig_module/device/plasmacutter, - // /obj/item/rig_module/device/rcd, - /obj/item/rig_module/vision/meson - ) - - chest_type = /obj/item/clothing/suit/space/new_rig/ce - boot_type = null - glove_type = null - -/obj/item/clothing/suit/space/new_rig/ce - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - -/obj/item/rig/hazmat - - name = "AMI control module" - suit_type = "hazmat hardsuit" - desc = "An Anomalous Material Interaction hardsuit that protects against the strangest energies the universe can throw at it." - icon_state = "science_rig" - active_slowdown = 1 - offline_vision_restriction = 1 - - helm_type = /obj/item/clothing/head/helmet/space/new_rig/hazmat - - helm_type = /obj/item/clothing/head/helmet/space/new_rig/ert - - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/pickaxe,/obj/item/healthanalyzer,/obj/item/gps,/obj/item/radio/beacon) - - req_access = list() - req_one_access = list() - -/obj/item/rig/hazmat/equipped - - req_access = list(ACCESS_RD) - - initial_modules = list( - /obj/item/rig_module/ai_container, - /obj/item/rig_module/maneuvering_jets) - -/obj/item/rig/medical - - name = "rescue suit control module" - suit_type = "rescue hardsuit" - desc = "A durable suit designed for medical rescue in high risk areas." - icon_state = "medical_rig" - armor = list(melee = 10, bullet = 5, laser = 10, energy = 5, bomb = 10, bio = 100, rad = 50) - active_slowdown = 1 - offline_vision_restriction = 1 - - helm_type = /obj/item/clothing/head/helmet/space/new_rig/medical - - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/firstaid,/obj/item/healthanalyzer,/obj/item/stack/medical,/obj/item/roller ) - - req_access = list() - req_one_access = list() - -/obj/item/rig/medical/equipped - - initial_modules = list( - /obj/item/rig_module/chem_dispenser/injector, - /obj/item/rig_module/maneuvering_jets, - /obj/item/rig_module/device/healthscanner, - /obj/item/rig_module/vision/medhud - ) - -/obj/item/rig/hazard - name = "hazard hardsuit control module" - suit_type = "hazard hardsuit" - desc = "A Security hardsuit designed for prolonged EVA in dangerous environments." - icon_state = "hazard_rig" - armor = list(melee = 30, bullet = 15, laser = 30, energy = 10, bomb = 10, bio = 100, rad = 50) - active_slowdown = 1 - offline_slowdown = 3 - offline_vision_restriction = 1 - - helm_type = /obj/item/clothing/head/helmet/space/new_rig/hazard - - allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/baton) - - req_access = list() - req_one_access = list() - - -/obj/item/rig/hazard/equipped - - initial_modules = list( - /obj/item/rig_module/vision/sechud, - /obj/item/rig_module/maneuvering_jets, - /obj/item/rig_module/grenade_launcher, - /obj/item/rig_module/mounted/taser - ) diff --git a/code/modules/clothing/spacesuits/syndi.dm b/code/modules/clothing/spacesuits/syndi.dm index 8c65ed02553..890c223d735 100644 --- a/code/modules/clothing/spacesuits/syndi.dm +++ b/code/modules/clothing/spacesuits/syndi.dm @@ -83,7 +83,7 @@ icon_state = "syndicate-helm-black" item_state = "syndicate-helm-black" -obj/item/clothing/head/helmet/space/syndicate/black/strike +/obj/item/clothing/head/helmet/space/syndicate/black/strike name = "Syndicate Strike Team commando helmet" desc = "A heavily armored black helmet that is only given to high-ranking Syndicate operatives." armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. @@ -95,7 +95,7 @@ obj/item/clothing/head/helmet/space/syndicate/black/strike icon_state = "syndicate-black" item_state = "syndicate-black" -obj/item/clothing/suit/space/syndicate/black/strike +/obj/item/clothing/suit/space/syndicate/black/strike name = "Syndicate Strike Team commando space suit" desc = "A heavily armored, black space suit that is only given to high-ranking Syndicate operatives." armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. @@ -156,7 +156,7 @@ obj/item/clothing/suit/space/syndicate/black/strike icon_state = "syndicate-helm-black-red" item_state = "syndicate-helm-black-red" -obj/item/clothing/head/helmet/space/syndicate/black/red/strike +/obj/item/clothing/head/helmet/space/syndicate/black/red/strike name = "Syndicate Strike Team leader helmet" desc = "A heavily armored, black and red space helmet that is only given to elite Syndicate operatives, it looks particularly menacing." armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. @@ -168,7 +168,7 @@ obj/item/clothing/head/helmet/space/syndicate/black/red/strike icon_state = "syndicate-black-red" item_state = "syndicate-black-red" -obj/item/clothing/suit/space/syndicate/black/red/strike +/obj/item/clothing/suit/space/syndicate/black/red/strike name = "Syndicate Strike Team leader space suit" desc = "A heavily armored, black and red space suit that is only given to elite Syndicate operatives, it looks particularly menacing." armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 6c4ace8c993..bdcd27c24e3 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -11,8 +11,10 @@ resistance_flags = NONE armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Grey" = 'icons/mob/species/grey/suit.dmi' ) + w_class = WEIGHT_CLASS_NORMAL /obj/item/clothing/suit/armor/vest name = "armor" diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index a7bd3fb2627..6ffa40fe727 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -162,7 +162,7 @@ desc = "A slick, authoritative cloak designed for the Chief Engineer." icon_state = "cemantle" item_state = "cemantle" - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd) + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd, /obj/item/rpd) //Chief Medical Officer /obj/item/clothing/suit/mantle/labcoat/chief_medical_officer @@ -231,7 +231,7 @@ icon_state = "hazard" item_state = "hazard" blood_overlay_type = "armor" - allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen) + allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen, /obj/item/rcd, /obj/item/rpd) resistance_flags = NONE sprite_sheets = list( diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 7e9e698d185..f90b1703d34 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -12,6 +12,7 @@ /obj/item/clothing/suit/bluetag name = "blue laser tag armour" desc = "Blue Pride, Station Wide." + w_class = WEIGHT_CLASS_NORMAL icon_state = "bluetag" item_state = "bluetag" blood_overlay_type = "armor" @@ -26,6 +27,7 @@ /obj/item/clothing/suit/redtag name = "red laser tag armour" desc = "Pew pew pew." + w_class = WEIGHT_CLASS_NORMAL icon_state = "redtag" item_state = "redtag" blood_overlay_type = "armor" @@ -71,6 +73,7 @@ /obj/item/clothing/suit/cyborg_suit name = "cyborg suit" desc = "Suit for a cyborg costume." + w_class = WEIGHT_CLASS_NORMAL icon_state = "death" item_state = "death" flags = CONDUCT @@ -309,6 +312,22 @@ flags = BLOCKHAIR flags_inv = HIDEEARS +/obj/item/clothing/suit/hooded/salmon_costume + name = "salmon suit" + desc = "A costume made from authentic salmon scales, it reeks!" + icon_state = "salmon" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + allowed = list(/obj/item/fish/salmon, /obj/item/fish_eggs/salmon) + hoodtype = /obj/item/clothing/head/hooded/salmon_hood + +/obj/item/clothing/head/hooded/salmon_hood + name = "salmon hood" + desc = "A hood attached to a salmon suit." + icon_state = "salmon" + body_parts_covered = HEAD + flags = BLOCKHAIR + flags_inv = HIDEEARS + /obj/item/clothing/suit/hooded/bee_costume // It's Hip! name = "bee costume" desc = "Bee the true Queen!" @@ -371,6 +390,7 @@ /obj/item/clothing/suit/hooded/wintercoat/captain name = "captain's winter coat" icon_state = "wintercoat_captain" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatcaptain" armor = list("melee" = 25, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box,/obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic) @@ -382,6 +402,7 @@ /obj/item/clothing/suit/hooded/wintercoat/security name = "security winter coat" icon_state = "wintercoat_sec" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatsecurity" armor = list("melee" = 15, "bullet" = 10, "laser" = 15, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic) @@ -393,6 +414,7 @@ /obj/item/clothing/suit/hooded/wintercoat/medical name = "medical winter coat" icon_state = "wintercoat_med" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatmedical" allowed = list(/obj/item/analyzer, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator,/obj/item/healthanalyzer,/obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic) armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 0, "acid" = 45) @@ -404,6 +426,7 @@ /obj/item/clothing/suit/hooded/wintercoat/science name = "science winter coat" icon_state = "wintercoat_sci" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatscience" allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/applicator,/obj/item/healthanalyzer,/obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/melee/classic_baton/telescopic) armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 10, bio = 0, rad = 0, fire = 0, acid = 0) @@ -415,6 +438,7 @@ /obj/item/clothing/suit/hooded/wintercoat/engineering name = "engineering winter coat" icon_state = "wintercoat_engi" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatengineer" armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 20, "fire" = 30, "acid" = 45) allowed = list(/obj/item/flashlight, /obj/item/tank/emergency_oxygen, /obj/item/t_scanner, /obj/item/rcd) @@ -454,6 +478,7 @@ /obj/item/clothing/suit/hooded/wintercoat/miner name = "mining winter coat" icon_state = "wintercoat_miner" + w_class = WEIGHT_CLASS_NORMAL item_state = "coatminer" allowed = list(/obj/item/pickaxe, /obj/item/flashlight, /obj/item/tank/emergency_oxygen, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter) armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) @@ -767,6 +792,7 @@ /obj/item/clothing/suit/jacket/pilot name = "security bomber jacket" desc = "A stylish and worn-in armoured black bomber jacket emblazoned with the NT Security crest on the left breast. Looks rugged." + w_class = WEIGHT_CLASS_NORMAL icon_state = "bombersec" item_state = "bombersec" ignore_suitadjust = 0 @@ -825,6 +851,7 @@ /obj/item/clothing/suit/toggle/owlwings name = "owl cloak" desc = "A soft brown cloak made of synthetic feathers. Soft to the touch, stylish, and a 2 meter wing span that will drive the ladies mad." + w_class = WEIGHT_CLASS_NORMAL icon_state = "owl_wings" item_state = "owl_wings" body_parts_covered = ARMS @@ -871,6 +898,7 @@ /obj/item/clothing/suit/advanced_protective_suit name = "Advanced Protective Suit" desc = "An incredibly advanced and complex suit; it has so many buttons and dials as to be incomprehensible." + w_class = WEIGHT_CLASS_BULKY icon_state = "bomb" item_state = "bomb" actions_types = list(/datum/action/item_action/toggle) @@ -924,6 +952,7 @@ //Syndicate Chaplain Robe (WOLOLO!) /obj/item/clothing/suit/hooded/chaplain_hoodie/missionary_robe description_antag = "This robe is made of reinforced fibers, granting it superior protection. The robes also wirelessly generate power for the neurotransmitter in the linked missionary staff while being worn." + w_class = WEIGHT_CLASS_NORMAL armor = list(melee = 10, bullet = 10, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 15, fire = 30, acid = 30) var/obj/item/nullrod/missionary_staff/linked_staff = null diff --git a/code/modules/clothing/suits/storage.dm b/code/modules/clothing/suits/storage.dm index 9a675742b5e..1da4138fe77 100644 --- a/code/modules/clothing/suits/storage.dm +++ b/code/modules/clothing/suits/storage.dm @@ -1,5 +1,6 @@ /obj/item/clothing/suit/storage var/obj/item/storage/internal/pockets + w_class = WEIGHT_CLASS_NORMAL //we don't want these to be able to fit in their own pockets. /obj/item/clothing/suit/storage/New() ..() diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm index 308380e4c55..e77bcb7478a 100644 --- a/code/modules/clothing/suits/utility.dm +++ b/code/modules/clothing/suits/utility.dm @@ -64,6 +64,7 @@ /obj/item/clothing/head/bomb_hood name = "bomb hood" desc = "Use in case of bomb." + w_class = WEIGHT_CLASS_NORMAL icon_state = "bombsuit" flags = BLOCKHAIR | THICKMATERIAL armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm index b57166d4909..8d0e68eddb0 100644 --- a/code/modules/clothing/under/color.dm +++ b/code/modules/clothing/under/color.dm @@ -128,6 +128,10 @@ item_state = "p_suit" item_color = "purple" +/obj/item/clothing/under/color/purple/sensor //for jani ert + sensor_mode = SENSOR_COORDS + random_sensor = FALSE + /obj/item/clothing/under/color/lightpurple name = "light purple jumpsuit" icon_state = "lightpurple" diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm index faa9039c8c4..6384efb2021 100644 --- a/code/modules/clothing/under/jobs/civilian.dm +++ b/code/modules/clothing/under/jobs/civilian.dm @@ -48,6 +48,10 @@ item_state = "bl_suit" item_color = "chapblack" +/obj/item/clothing/under/rank/chaplain/sensor + sensor_mode = SENSOR_COORDS + random_sensor = FALSE + /obj/item/clothing/under/rank/chef desc = "It's an apron which is given only to the most hardcore chefs in space." name = "chef's uniform" diff --git a/code/modules/clothing/under/jobs/engineering.dm b/code/modules/clothing/under/jobs/engineering.dm index aa70fc85db6..3a717a5907f 100644 --- a/code/modules/clothing/under/jobs/engineering.dm +++ b/code/modules/clothing/under/jobs/engineering.dm @@ -39,6 +39,10 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 60, "acid" = 20) resistance_flags = NONE +/obj/item/clothing/under/rank/engineer/sensor + sensor_mode = SENSOR_COORDS + random_sensor = FALSE + /obj/item/clothing/under/rank/engineer/skirt desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." diff --git a/code/modules/clothing/under/jobs/medsci.dm b/code/modules/clothing/under/jobs/medsci.dm index d8847741db3..ffd730d7a6d 100644 --- a/code/modules/clothing/under/jobs/medsci.dm +++ b/code/modules/clothing/under/jobs/medsci.dm @@ -124,6 +124,10 @@ permeability_coefficient = 0.50 armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) +/obj/item/clothing/under/rank/medical/sensor + sensor_mode = SENSOR_COORDS + random_sensor = FALSE + /obj/item/clothing/under/rank/medical/skirt name = "medical doctor's jumpskirt" icon_state = "medicalf" diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm index 0086ba96ef8..afeca2fb3bd 100644 --- a/code/modules/clothing/under/jobs/security.dm +++ b/code/modules/clothing/under/jobs/security.dm @@ -34,6 +34,10 @@ armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) strip_delay = 50 +/obj/item/clothing/under/rank/security/sensor + sensor_mode = SENSOR_COORDS + random_sensor = FALSE + /obj/item/clothing/under/rank/security/skirt name = "security officer's jumpskirt" desc = "Standard feminine fashion for Security Officers. It's made of sturdier material than the standard jumpskirts." diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index a9b4eab329e..beedf85c8c7 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -88,6 +88,10 @@ item_state = "g_suit" item_color = "officer" +/obj/item/clothing/under/rank/centcom_officer/sensor + sensor_mode = SENSOR_COORDS + random_sensor = FALSE + /obj/item/clothing/under/rank/centcom_commander desc = "It's a jumpsuit worn by CentComm's highest-tier Commanders." name = "\improper CentComm officer's jumpsuit" @@ -838,3 +842,10 @@ icon_state = "cuban_suit" item_state = "cuban_suit" item_color = "cuban_suit" + +/obj/item/clothing/under/tourist_suit + name = "tourist outfit" + desc = "A light blue shirt with brown shorts. Feels oddly spooky." + icon_state = "tourist" + icon_state = "tourist" + item_color = "tourist" diff --git a/code/modules/crafting/craft.dm b/code/modules/crafting/craft.dm index a0d6823fe80..aae3b84a3c5 100644 --- a/code/modules/crafting/craft.dm +++ b/code/modules/crafting/craft.dm @@ -357,7 +357,7 @@ if(!fail_msg) to_chat(usr, "[TR.name] constructed.") if(TR.alert_admins_on_craft) - message_admins("[usr.ckey] has created a [TR.name] at [ADMIN_COORDJMP(usr)]") + message_admins("[key_name_admin(usr)] has created a [TR.name] at [ADMIN_COORDJMP(usr)]") else to_chat(usr, "Construction failed[fail_msg]") busy = FALSE diff --git a/code/modules/crafting/tailoring.dm b/code/modules/crafting/tailoring.dm index 6973fd04ad3..68fa7fca3f3 100644 --- a/code/modules/crafting/tailoring.dm +++ b/code/modules/crafting/tailoring.dm @@ -143,3 +143,22 @@ reqs = list(/obj/item/stack/sheet/animalhide/lizard = 1, /obj/item/stack/sheet/leather = 1) time = 60 category = CAT_CLOTHING + +/datum/crafting_recipe/rubberduckyshoes + name = "Rubber Ducky Shoes" + result = /obj/item/clothing/shoes/ducky + time = 45 + reqs = list(/obj/item/bikehorn/rubberducky = 2, + /obj/item/clothing/shoes/sandal = 1) + tools = list(TOOL_WIRECUTTER) + category = CAT_CLOTHING + +/datum/crafting_recipe/salmonsuit + name = "Salmon Suit" + result = /obj/item/clothing/suit/hooded/salmon_costume + time = 60 + reqs = list(/obj/item/fish/salmon = 20, + /obj/item/stack/tape_roll = 5) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/kitchen/knife) + category = CAT_CLOTHING diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index 1a5da52ffce..4ee33f4d87e 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -272,15 +272,6 @@ new /obj/item/reagent_containers/food/drinks/cans/cola(src) -/obj/item/instrument/guitar/jello_guitar //Pineapple Salad: Dan Jello - name = "Dan Jello's Pink Guitar" - desc = "Dan Jello's special pink guitar." - icon = 'icons/obj/custom_items.dmi' - icon_state = "jello_guitar" - item_state = "jello_guitar" - righthand_file = 'icons/mob/inhands/fluff_righthand.dmi' - lefthand_file = 'icons/mob/inhands/fluff_lefthand.dmi' - /obj/item/fluff/wingler_comb name = "blue comb" desc = "A blue comb, it looks like it was made to groom a Tajaran's fur." @@ -349,7 +340,7 @@ to_chat(user, "You modify the appearance of [target].") var/obj/item/clothing/mask/gas/M = target M.name = "Prescription Gas Mask" - M.desc = "It looks heavily modified, but otherwise functions as a gas mask. The words “Property of Yon-Dale†can be seen on the inner band." + M.desc = "It looks heavily modified, but otherwise functions as a gas mask. The words \"Property of Yon-Dale\" can be seen on the inner band." M.icon = 'icons/obj/custom_items.dmi' M.icon_state = "gas_tariq" M.sprite_sheets = list( @@ -1592,13 +1583,17 @@ item_state = "asmer_accordion" -/obj/item/clothing/head/rabbitears/fluff/pinesalad_bunny // Pineapple Salad : Dan Jello - name = "Bluespace rabbit ears" - desc = "A pair of sparkly bluespace rabbit ears, with a small tag on them that reads, 'Dan Jello~'. Yuck, \ - there's some pink slime on the part that goes on your head!" +/obj/item/clothing/head/fluff/pinesalad_horns //Pineapple Salad: Dan Jello + name = "Bluespace Horns" + desc = "A pair of fake horns. Now with added bluespace!" icon = 'icons/obj/custom_items.dmi' - icon_state = "ps_bunny" + icon_state = "ps_horns" +/obj/item/storage/backpack/fluff/hiking //Pineapple Salad: Dan Jello + name = "\improper Fancy Hiking Pack" + desc = "A black and red hiking pack with some nice little accessories." + icon = 'icons/obj/custom_items.dmi' + icon_state = "danpack" /obj/item/clothing/under/fluff/kiaoutfit //FullOfSkittles: Kiachi name = "Suspicious Outfit" diff --git a/code/modules/detective_work/detective_work.dm b/code/modules/detective_work/detective_work.dm index 69ee566124e..4933ff29474 100644 --- a/code/modules/detective_work/detective_work.dm +++ b/code/modules/detective_work/detective_work.dm @@ -1,8 +1,8 @@ //CONTAINS: Suit fibers and Detective's Scanning Computer -atom/var/list/suit_fibers +/atom/var/list/suit_fibers -atom/proc/add_fibers(mob/living/carbon/human/M) +/atom/proc/add_fibers(mob/living/carbon/human/M) if(M.gloves && istype(M.gloves,/obj/item/clothing/)) var/obj/item/clothing/gloves/G = M.gloves if(G.transfer_blood > 1) //bloodied gloves transfer blood to touched objects diff --git a/code/modules/detective_work/scanner.dm b/code/modules/detective_work/scanner.dm index dea08c602f9..d043c3e1263 100644 --- a/code/modules/detective_work/scanner.dm +++ b/code/modules/detective_work/scanner.dm @@ -12,17 +12,17 @@ flags = CONDUCT | NOBLUDGEON slot_flags = SLOT_BELT origin_tech = "engineering=4;biotech=2;programming=5" - var/scanning = 0 + var/scanning = FALSE var/list/log = list() - actions_types = list(/datum/action/item_action/print_report) + actions_types = list(/datum/action/item_action/print_forensic_report, /datum/action/item_action/clear_records) -/obj/item/detective_scanner/attack_self(var/mob/user) +/obj/item/detective_scanner/attack_self(mob/user) var/search = input(user, "Enter name, fingerprint or blood DNA.", "Find record", "") if(!search || user.stat || user.incapacitated()) return - search = lowertext(search) + search = lowertext(search) //This is here so that it doesn't run 'lowertext()' until the checks have passed. var/name var/fingerprint = "FINGERPRINT NOT FOUND" @@ -30,85 +30,95 @@ // I really, really wish I didn't have to split this into two seperate loops. But the datacore is awful. - for(var/record in GLOB.data_core.general) + for(var/record in GLOB.data_core.general) // Search in the 'general' datacore var/datum/data/record/S = record - if(S && (search == lowertext(S.fields["fingerprint"]) || search == lowertext(S.fields["name"]))) + if(S && (search == lowertext(S.fields["fingerprint"]) || search == lowertext(S.fields["name"]))) // Get Fingerprint and Name name = S.fields["name"] fingerprint = S.fields["fingerprint"] - continue + break - for(var/record in GLOB.data_core.medical) + for(var/record in GLOB.data_core.medical) // Then search in the 'medical' datacore var/datum/data/record/M = record - if(M && ( search == lowertext(M.fields["b_dna"]) || name == M.fields["name"]) ) + if(M && (search == lowertext(M.fields["b_dna"]) || name == M.fields["name"])) // Get Blood DNA dna = M.fields["b_dna"] - if(fingerprint == "FINGERPRINT NOT FOUND") // We have searched by DNA, and do not have the relevant information from the fingerprint records. + if(fingerprint == "FINGERPRINT NOT FOUND") // We have searched for DNA, and so do not have the relevant information from the fingerprint records. name = M.fields["name"] for(var/gen_record in GLOB.data_core.general) var/datum/data/record/S = gen_record if(S && (name == S.fields["name"])) fingerprint = S.fields["fingerprint"] - continue - continue + break + else //Eveything's been set, break the loop + break if(name) to_chat(user, "Match found in station records: [name]
    \ Fingerprint: [fingerprint]
    \ Blood DNA: [dna]") - return + else + to_chat(user, "No match found in station records.") - to_chat(user, "No match found in station records.") - -/obj/item/detective_scanner/ui_action_click() - print_scanner_report() +/obj/item/detective_scanner/ui_action_click(mob/user, actiontype) + if(actiontype == /datum/action/item_action/print_forensic_report) + print_scanner_report() + else + clear_scanner() /obj/item/detective_scanner/proc/print_scanner_report() - if(log.len && !scanning) - scanning = 1 + if(length(log) && !scanning) + scanning = TRUE to_chat(usr, "Printing report, please wait...") playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1) - spawn(100) - // Create our paper - var/obj/item/paper/P = new(get_turf(src)) - P.name = "paper- 'Scanner Report'" - P.info = "
    Scanner Report


    " - P.info += jointext(log, "
    ") - P.info += "
    Notes:
    " - P.info_links = P.info - - if(ismob(loc)) - var/mob/M = loc - M.put_in_hands(P) - to_chat(M, "Report printed. Log cleared.") - - // Clear the logs - log = list() - scanning = 0 + addtimer(CALLBACK(src, .proc/make_paper, log), 10 SECONDS) // Create our paper + log = list() // Clear the logs + scanning = FALSE else - to_chat(usr, "The scanner has no logs or is in use.") + to_chat(usr, "The scanner has no logs or is in use.") + +/obj/item/detective_scanner/proc/make_paper(log) // Moved to a proc because 'spawn()' is evil + var/obj/item/paper/P = new(get_turf(src)) + P.name = "paper- 'Scanner Report'" + P.info = "
    Scanner Report


    " + P.info += jointext(log, "
    ") + P.info += "
    Notes:
    " + P.info_links = P.info + + if(ismob(loc)) + var/mob/M = loc + M.put_in_hands(P) + to_chat(M, "Report printed. Log cleared.") -/obj/item/detective_scanner/attack(mob/living/M as mob, mob/user as mob) +/obj/item/detective_scanner/proc/clear_scanner() + if(length(log) && !scanning) + log = list() + playsound(loc, 'sound/machines/ding.ogg', 40) + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, usr, "Scanner logs cleared."), 1.5 SECONDS) //Timer so that it clears on the 'ding' + else + to_chat(usr, "The scanner has no logs or is in use.") + + +/obj/item/detective_scanner/attack() return - -/obj/item/detective_scanner/afterattack(atom/A, mob/user as mob, proximity) +/obj/item/detective_scanner/afterattack(atom/A, mob/user) scan(A, user) -/obj/item/detective_scanner/proc/scan(var/atom/A, var/mob/user) +/obj/item/detective_scanner/proc/scan(atom/A, mob/user) if(!scanning) // Can remotely scan objects and mobs. - if(!in_range(A, user) && !(A in view(world.view, user))) + if(!(A in view(world.view, user))) return if(loc != user) return - scanning = 1 + scanning = TRUE - user.visible_message("\The [user] points the [src.name] at \the [A] and performs a forensic scan.") - to_chat(user, "You scan \the [A]. The scanner is now analysing the results...") + user.visible_message("[user] points [src] at [A] and performs a forensic scan.", + "You scan [A]. The scanner is now analysing the results...") // GATHER INFORMATION @@ -123,10 +133,10 @@ // Start gathering - if(A.blood_DNA && A.blood_DNA.len) + if(length(A.blood_DNA)) blood = A.blood_DNA.Copy() - if(A.suit_fibers && A.suit_fibers.len) + if(length(A.suit_fibers)) fibers = A.suit_fibers.Copy() if(ishuman(A)) @@ -137,11 +147,11 @@ else if(!ismob(A)) - if(A.fingerprints && A.fingerprints.len) + if(length(A.fingerprints)) fingerprints = A.fingerprints.Copy() // Only get reagents from non-mobs. - if(A.reagents && A.reagents.reagent_list.len) + if(A.reagents && length(A.reagents.reagent_list)) for(var/datum/reagent/R in A.reagents.reagent_list) reagents[R.name] = R.volume @@ -154,63 +164,60 @@ var/blood_type = R.data["blood_type"] blood[blood_DNA] = blood_type - // We gathered everything. Create a fork and slowly display the results to the holder of the scanner. - spawn(0) + // We gathered everything. Slowly display the results to the holder of the scanner. + var/found_something = FALSE + add_log("[station_time_timestamp()][get_timestamp()] - [target_name]", FALSE) - var/found_something = 0 - add_log("[station_time_timestamp()][get_timestamp()] - [target_name]", 0) + // Fingerprints + if(length(fingerprints)) + sleep(30) + add_log("Prints:") + for(var/finger in fingerprints) + add_log("[finger]") + found_something = TRUE - // Fingerprints - if(fingerprints && fingerprints.len) - sleep(30) - add_log("Prints:") - for(var/finger in fingerprints) - add_log("[finger]") - found_something = 1 + // Blood + if(length(blood)) + sleep(30) + add_log("Blood:") + found_something = TRUE + for(var/B in blood) + add_log("Type: [blood[B]] DNA: [B]") - // Blood - if(blood && blood.len) - sleep(30) - add_log("Blood:") - found_something = 1 - for(var/B in blood) - add_log("Type: [blood[B]] DNA: [B]") + //Fibers + if(length(fibers)) + sleep(30) + add_log("Fibers:") + for(var/fiber in fibers) + add_log("[fiber]") + found_something = TRUE - //Fibers - if(fibers && fibers.len) - sleep(30) - add_log("Fibers:") - for(var/fiber in fibers) - add_log("[fiber]") - found_something = 1 + //Reagents + if(length(reagents)) + sleep(30) + add_log("Reagents:") + for(var/R in reagents) + add_log("Reagent: [R] Volume: [reagents[R]]") + found_something = TRUE - //Reagents - if(reagents && reagents.len) - sleep(30) - add_log("Reagents:") - for(var/R in reagents) - add_log("Reagent: [R] Volume: [reagents[R]]") - found_something = 1 + // Get a new user + var/mob/holder = null + if(ismob(loc)) + holder = loc - // Get a new user - var/mob/holder = null - if(ismob(src.loc)) - holder = src.loc + if(!found_something) + add_log("# No forensic traces found #", FALSE) // Don't display this to the holder user + if(holder) + to_chat(holder, "Unable to locate any fingerprints, materials, fibers, or blood on [A]!") + else + if(holder) + to_chat(holder, "You finish scanning [A].") - if(!found_something) - add_log("# No forensic traces found #", 0) // Don't display this to the holder user - if(holder) - to_chat(holder, "Unable to locate any fingerprints, materials, fibers, or blood on \the [target_name]!") - else - if(holder) - to_chat(holder, "You finish scanning \the [target_name].") + add_log("---------------------------------------------------------", FALSE) + scanning = FALSE - add_log("---------------------------------------------------------", 0) - scanning = 0 - return - -/obj/item/detective_scanner/proc/add_log(var/msg, var/broadcast = 1) +/obj/item/detective_scanner/proc/add_log(msg, broadcast = TRUE) if(scanning) if(broadcast && ismob(loc)) var/mob/M = loc diff --git a/code/modules/events/abductor.dm b/code/modules/events/abductor.dm index 7fe60e8b77f..4ad8fb4eb90 100644 --- a/code/modules/events/abductor.dm +++ b/code/modules/events/abductor.dm @@ -9,7 +9,7 @@ makeAbductorTeam() /datum/event/abductor/proc/makeAbductorTeam() - var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered for an Abductor Team?", ROLE_ABDUCTOR, 1) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you wish to be considered for an Abductor Team?", ROLE_ABDUCTOR, TRUE) if(candidates.len >= 2) //Oh god why we can't have static functions diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm index f36fc238e4b..b4342f29836 100644 --- a/code/modules/events/alien_infestation.dm +++ b/code/modules/events/alien_infestation.dm @@ -21,7 +21,7 @@ spawncount = 4 spawn() - var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 1) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an alien?", ROLE_ALIEN, TRUE, source = /mob/living/carbon/alien/larva) while(spawncount && length(vents) && length(candidates)) var/obj/vent = pick_n_take(vents) var/mob/C = pick_n_take(candidates) diff --git a/code/modules/events/anomaly_atmos.dm b/code/modules/events/anomaly_atmos.dm new file mode 100644 index 00000000000..fa512cb522b --- /dev/null +++ b/code/modules/events/anomaly_atmos.dm @@ -0,0 +1,52 @@ +/datum/event/anomaly/anomaly_atmos + startWhen = 30 + announceWhen = 3 + endWhen = 100 + +/datum/event/anomaly/anomaly_atmos/announce() + GLOB.event_announcement.Announce("Transformative gas anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert") + +/datum/event/anomaly/anomaly_atmos/start() + var/turf/T = pick(get_area_turfs(impact_area)) + if(T) + newAnomaly = new /obj/effect/anomaly/atmos(T.loc) + + +/datum/event/anomaly/anomaly_atmos/tick() + if(!newAnomaly) + kill() + return + if(ISMULTIPLE(activeFor, 5)) + newAnomaly.anomalyEffect() + +/datum/event/anomaly/anomaly_atmos/end() + if(!newAnomaly || !newAnomaly.loc) //no anomaly or it is in nullspace + return + var/obj/effect/anomaly/atmos/A = newAnomaly + var/gas_type = A.gas_type + var/area/t_area = get_area(A) + for(var/turf/T in t_area) + if(istype(T, /turf/simulated)) //should not occur on unsimulated turfs without admemery + var/turf/simulated/S = T + fill_with_gas(gas_type, S) + explosion(get_turf(newAnomaly), -1, 0, 2) // a small boom so people know something happened. + ..() + +/** + * Replaces all oxygen and nitrogen on the simulated turf S with the gas type specified in gas, taking N2, N20, CO2 and agent B + */ +/datum/event/anomaly/anomaly_atmos/proc/fill_with_gas(gas, turf/simulated/S) + if(!S.air)//no air to transform + return + var/amount_to_add = S.air.oxygen + S.air.nitrogen + S.air.oxygen = 0 + S.air.nitrogen = 0 + switch(gas) + if(GAS_N2) + S.air.nitrogen += amount_to_add + if(GAS_N2O) + S.air.sleeping_agent += amount_to_add + if(GAS_CO2) + S.air.carbon_dioxide += amount_to_add + S.update_visuals() + S.air_update_turf() diff --git a/code/modules/events/apc_overload.dm b/code/modules/events/apc_overload.dm index 13cabe81b3c..2ca2cdbfa70 100644 --- a/code/modules/events/apc_overload.dm +++ b/code/modules/events/apc_overload.dm @@ -21,7 +21,7 @@ SEND_SOUND(M, S) /datum/event/apc_overload/announce() - GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please check all underfloor APC terminals.", "Critical Power Failure", new_sound = 'sound/AI/apc_overload.ogg') + GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please check all underfloor APC terminals.", "Critical Power Failure", new_sound = 'sound/AI/attention.ogg') /datum/event/apc_overload/end() return TRUE @@ -32,7 +32,7 @@ /area/turret_protected/ai) if(announce) - GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please check all underfloor APC terminals.", "Critical Power Failure", new_sound = 'sound/AI/apc_overload.ogg') + GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please check all underfloor APC terminals.", "Critical Power Failure", new_sound = 'sound/AI/attention.ogg') // break APC_BREAK_PROBABILITY% of all of the APCs on the station var/affected_apc_count = 0 diff --git a/code/modules/events/apc_short.dm b/code/modules/events/apc_short.dm index d2fe04dcc5d..bb82ceb15c4 100644 --- a/code/modules/events/apc_short.dm +++ b/code/modules/events/apc_short.dm @@ -21,7 +21,7 @@ SEND_SOUND(M, S) /datum/event/apc_short/announce() - GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please repair shorted APCs.", "Systems Power Failure", new_sound = 'sound/AI/apc_short.ogg') + GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please repair shorted APCs.", "Systems Power Failure", new_sound = 'sound/AI/attention.ogg') /datum/event/apc_short/end() return TRUE @@ -32,7 +32,7 @@ /area/turret_protected/ai) if(announce) - GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please repair shorted APCs.", "Systems Power Failure", new_sound = 'sound/AI/apc_short.ogg') + GLOB.event_announcement.Announce("Overload detected in [station_name()]'s powernet. Engineering, please repair shorted APCs.", "Systems Power Failure", new_sound = 'sound/AI/attention.ogg') // break APC_BREAK_PROBABILITY% of all of the APCs on the station var/affected_apc_count = 0 @@ -46,10 +46,10 @@ if(prob(APC_BREAK_PROBABILITY)) // if it has internal wires, cut the power wires if(C.wires) - if(!C.wires.IsIndexCut(APC_WIRE_MAIN_POWER1)) - C.wires.CutWireIndex(APC_WIRE_MAIN_POWER1) - if(!C.wires.IsIndexCut(APC_WIRE_MAIN_POWER2)) - C.wires.CutWireIndex(APC_WIRE_MAIN_POWER2) + if(!C.wires.is_cut(WIRE_MAIN_POWER1)) + C.wires.cut(WIRE_MAIN_POWER1) + if(!C.wires.is_cut(WIRE_MAIN_POWER2)) + C.wires.cut(WIRE_MAIN_POWER2) // if it was operating, toggle off the breaker if(C.operating) C.toggle_breaker() @@ -59,7 +59,17 @@ log_and_message_admins("APC Short event shorted out [affected_apc_count] APCs.") /proc/power_restore(announce=TRUE) - power_restore_quick(announce) + if(announce) + GLOB.event_announcement.Announce("Power has been restored to [station_name()]. We apologize for the inconvenience.", "Power Systems Nominal", new_sound = 'sound/AI/poweron.ogg') + + // recharge the APCs + for(var/thing in GLOB.apcs) + var/obj/machinery/power/apc/A = thing + if(!is_station_level(A.z)) + continue + var/obj/item/stock_parts/cell/C = A.get_cell() + if(C) + C.give(C.maxcharge) /proc/power_restore_quick(announce=TRUE) if(announce) diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm index 82494fd2768..3af25262d18 100644 --- a/code/modules/events/blob.dm +++ b/code/modules/events/blob.dm @@ -16,8 +16,8 @@ if(!T) return kill() - var/list/candidates = pollCandidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, 1) - if(!candidates.len) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob infested mouse?", ROLE_BLOB, TRUE, source = /mob/living/simple_animal/mouse/blobinfected) + if(!length(candidates)) return kill() var/list/vents = get_valid_vent_spawns(exclude_mobs_nearby = TRUE, exclude_visible_by_mobs = TRUE) diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm index 8d91635c458..35cc199c3ff 100644 --- a/code/modules/events/brand_intelligence.dm +++ b/code/modules/events/brand_intelligence.dm @@ -32,7 +32,7 @@ log_debug("Original brand intelligence machine: [originMachine] ([ADMIN_VV(originMachine,"VV")]) [ADMIN_JMP(originMachine)]") /datum/event/brand_intelligence/tick() - if(!originMachine || QDELETED(originMachine) || originMachine.shut_up || originMachine.wires.IsAllCut()) //if the original vending machine is missing or has it's voice switch flipped + if(!originMachine || QDELETED(originMachine) || originMachine.shut_up || originMachine.wires.is_all_cut()) //if the original vending machine is missing or has it's voice switch flipped for(var/obj/machinery/vending/saved in infectedMachines) saved.shoot_inventory = 0 if(originMachine) diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm index 7304469f408..17c697faf11 100644 --- a/code/modules/events/electrical_storm.dm +++ b/code/modules/events/electrical_storm.dm @@ -25,5 +25,5 @@ for(var/thing in epicentreList) var/obj/effect/landmark/epicentre = thing for(var/obj/machinery/power/apc/apc in range(epicentre, lightsoutRange)) - apc.overload_lighting() + INVOKE_ASYNC(apc, /obj/machinery/power/apc.proc/overload_lighting) diff --git a/code/modules/events/event.dm b/code/modules/events/event.dm index 39c14c2169e..d7fa555ca5e 100644 --- a/code/modules/events/event.dm +++ b/code/modules/events/event.dm @@ -1,15 +1,24 @@ /datum/event_meta var/name = "" - var/enabled = 1 // Whether or not the event is available for random selection at all - var/weight = 0 // The base weight of this event. A zero means it may never fire, but see get_weight() - var/min_weight = 0 // The minimum weight that this event will have. Only used if non-zero. - var/max_weight = 0 // The maximum weight that this event will have. Only use if non-zero. - var/severity = 0 // The current severity of this event - var/one_shot = 0 //If true, then the event will not be re-added to the list of available events + /// Whether or not the event is available for random selection at all. + var/enabled = TRUE + /// The base weight of this event. A zero means it may never fire, but see get_weight() + var/weight + /// The minimum weight that this event will have. Only used if non-zero. + var/min_weight + /// The maximum weight that this event will have. + var/max_weight + /// the current severity of this event + var/severity + /// If true, then the event will not be re-added to the list of available events + var/one_shot + /// A modifier applied to all event weights (role and base), respects min and max + var/weight_mod = 1 + /// A list of roles that add weight to the event var/list/role_weights = list() var/datum/event/event_type -/datum/event_meta/New(var/event_severity, var/event_name, var/datum/event/type, var/event_weight, var/list/job_weights, var/is_one_shot = 0, var/min_event_weight = 0, var/max_event_weight = 0) +/datum/event_meta/New(event_severity, event_name, datum/event/type, event_weight, list/job_weights, is_one_shot = FALSE, min_event_weight = 0, max_event_weight = INFINITY) name = event_name severity = event_severity event_type = type @@ -20,7 +29,7 @@ if(job_weights) role_weights = job_weights -/datum/event_meta/proc/get_weight(var/list/active_with_role) +/datum/event_meta/proc/get_weight(list/active_with_role) if(!enabled) return 0 @@ -29,15 +38,9 @@ if(role in active_with_role) job_weight += active_with_role[role] * role_weights[role] - var/total_weight = weight + job_weight + return clamp((weight + job_weight) * weight_mod, min_weight, max_weight) - // Only min/max the weight if the values are non-zero - if(min_weight && total_weight < min_weight) total_weight = min_weight - if(max_weight && total_weight > max_weight) total_weight = max_weight - - return total_weight - -/datum/event_meta/alien/get_weight(var/list/active_with_role) +/datum/event_meta/alien/get_weight(list/active_with_role) if(GLOB.aliens_allowed) return ..(active_with_role) return 0 @@ -49,62 +52,91 @@ /datum/event //NOTE: Times are measured in master controller ticks! var/processing = 1 - var/startWhen = 0 //When in the lifetime to call start(). - var/announceWhen = 0 //When in the lifetime to call announce(). - var/endWhen = 0 //When in the lifetime the event should end. - - var/severity = 0 //Severity. Lower means less severe, higher means more severe. Does not have to be supported. Is set on New(). - var/activeFor = 0 //How long the event has existed. You don't need to change this. - var/isRunning = 1 //If this event is currently running. You should not change this. - var/startedAt = 0 //When this event started. - var/endedAt = 0 //When this event ended. - var/noAutoEnd = 0 //Does the event end automatically after endWhen passes? - var/area/impact_area //The area the event will hit + /// When in the lifetime to call start(). + var/startWhen = 0 + /// When in the lifetime to call announce(). + var/announceWhen = 0 + /// When in the lifetime the event should end. + var/endWhen = 0 + /// Severity. Lower means less severe, higher means more severe. Does not have to be supported. Is set on New(). + var/severity = 0 + /// How long the event has existed. You don't need to change this. + var/activeFor = 0 + /// If this event is currently running. You should not change this. + var/isRunning = TRUE + /// When this event started. + var/startedAt = 0 + /// When this event ended. + var/endedAt = 0 + /// Does the event end automatically after endWhen passes? + var/noAutoEnd = FALSE + /// The area the event will hit + var/area/impact_area var/datum/event_meta/event_meta = null /datum/event/nothing -//Called first before processing. -//Allows you to setup your event, such as randomly -//setting the startWhen and or announceWhen variables. -//Only called once. +/** + * Called first before processing. + * + * Allows you to setup your event, such as randomly + * setting the startWhen and or announceWhen variables. + * Only called once. + */ /datum/event/proc/setup() return -//Called when the tick is equal to the startWhen variable. -//Allows you to start before announcing or vice versa. -//Only called once. +/** + * Called when the tick is equal to the startWhen variable. + * + * Allows you to start before announcing or vice versa. + * Only called once. + */ /datum/event/proc/start() return -//Called when the tick is equal to the announceWhen variable. -//Allows you to announce before starting or vice versa. -//Only called once. +/** + * Called when the tick is equal to the announceWhen variable. + * + * Allows you to announce before starting or vice versa. + * Only called once. + */ /datum/event/proc/announce() return -//Called on or after the tick counter is equal to startWhen. -//You can include code related to your event or add your own -//time stamped events. -//Called more than once. +/** + * Called on or after the tick counter is equal to startWhen. + * + * You can include code related to your event or add your own + * time stamped events. + * Called more than once. + */ /datum/event/proc/tick() return -//Called on or after the tick is equal or more than endWhen -//You can include code related to the event ending. -//Do not place spawn() in here, instead use tick() to check for -//the activeFor variable. -//For example: if(activeFor == myOwnVariable + 30) doStuff() -//Only called once. +/** + * Called on or after the tick is equal or more than endWhen + * + * You can include code related to the event ending. + * Do not place spawn() in here, instead use tick() to check for + * the activeFor variable. + * For example: if(activeFor == myOwnVariable + 30) doStuff() + * Only called once. + */ /datum/event/proc/end() return -//Returns the latest point of event processing. +/** + * Returns the latest point of event processing. + */ /datum/event/proc/lastProcessAt() return max(startWhen, max(announceWhen, endWhen)) -//Do not override this proc, instead use the appropiate procs. -//This proc will handle the calls to the appropiate procs. +/** + * Do not override this proc, instead use the appropiate procs. + * + * This proc will handle the calls to the appropiate procs. + */ /datum/event/process() if(!processing) return @@ -113,14 +145,14 @@ tick() if(activeFor == startWhen) - isRunning = 1 + isRunning = TRUE start() if(activeFor == announceWhen) announce() if(activeFor == endWhen && !noAutoEnd) - isRunning = 0 + isRunning = FALSE end() // Everything is done, let's clean up. @@ -129,11 +161,13 @@ activeFor++ -//Called when start(), announce() and end() has all been called. +/** + * Called when start(), announce() and end() has all been called. + */ /datum/event/proc/kill() // If this event was forcefully killed run end() for individual cleanup if(isRunning) - isRunning = 0 + isRunning = FALSE end() endedAt = world.time diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm index 908bc762315..ad0710e96ed 100644 --- a/code/modules/events/event_container.dm +++ b/code/modules/events/event_container.dm @@ -128,11 +128,11 @@ GLOBAL_LIST_EMPTY(event_last_fired) /datum/event_container/mundane severity = EVENT_LEVEL_MUNDANE available_events = list( - // Severity level, event name, event type, base weight, role weights, one shot, min weight, max weight. Last two only used if set and non-zero + // Severity level, event name, event type, base weight, role weights, one shot, min weight, max weight. Last two only used if set. new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Nothing", /datum/event/nothing, 1100), - new /datum/event_meta(EVENT_LEVEL_MUNDANE, "PDA Spam", /datum/event/pda_spam, 0, list(ASSIGNMENT_ANY = 4), 0, 25, 50), - new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Lotto", /datum/event/money_lotto, 0, list(ASSIGNMENT_ANY = 1), 1, 5, 15), - new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Hacker", /datum/event/money_hacker, 0, list(ASSIGNMENT_ANY = 4), 1, 10, 25), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, "PDA Spam", /datum/event/pda_spam, 0, list(ASSIGNMENT_ANY = 4), FALSE, 25, 50), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Lotto", /datum/event/money_lotto, 0, list(ASSIGNMENT_ANY = 1), TRUE, 5, 15), + new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Money Hacker", /datum/event/money_hacker, 0, list(ASSIGNMENT_ANY = 4), TRUE, 10, 25), new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Economic News", /datum/event/economic_event, 300), new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Trivial News", /datum/event/trivial_news, 400), new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Mundane News", /datum/event/mundane_news, 300), @@ -147,8 +147,8 @@ GLOBAL_LIST_EMPTY(event_last_fired) severity = EVENT_LEVEL_MODERATE available_events = list( new /datum/event_meta(EVENT_LEVEL_MODERATE, "Nothing", /datum/event/nothing, 1230), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Appendicitis", /datum/event/spontaneous_appendicitis, 0, list(ASSIGNMENT_MEDICAL = 10), 1), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Carp School", /datum/event/carp_migration, 200, list(ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_SECURITY = 20), 1), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Appendicitis", /datum/event/spontaneous_appendicitis, 0, list(ASSIGNMENT_MEDICAL = 10), TRUE), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Carp School", /datum/event/carp_migration, 200, list(ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_SECURITY = 20), TRUE), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Rogue Drones", /datum/event/rogue_drone, 0, list(ASSIGNMENT_SECURITY = 20)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Space Vines", /datum/event/spacevine, 250, list(ASSIGNMENT_ENGINEER = 10)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Meteor Shower", /datum/event/meteor_wave, 0, list(ASSIGNMENT_ENGINEER = 25)), @@ -159,47 +159,48 @@ GLOBAL_LIST_EMPTY(event_last_fired) //new /datum/event_meta(EVENT_LEVEL_MODERATE, "Xenobiology Breach", /datum/event/prison_break/xenobiology, 0, list(ASSIGNMENT_SCIENCE = 100)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "APC Short", /datum/event/apc_short, 200, list(ASSIGNMENT_ENGINEER = 60)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Electrical Storm", /datum/event/electrical_storm, 250, list(ASSIGNMENT_ENGINEER = 20, ASSIGNMENT_JANITOR = 150)), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Radiation Storm", /datum/event/radiation_storm, 25, list(ASSIGNMENT_MEDICAL = 50), 1), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Spider Infestation", /datum/event/spider_infestation, 100, list(ASSIGNMENT_SECURITY = 30), 1), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Radiation Storm", /datum/event/radiation_storm, 25, list(ASSIGNMENT_MEDICAL = 50), TRUE), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Spider Infestation", /datum/event/spider_infestation, 100, list(ASSIGNMENT_SECURITY = 30), TRUE), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Ion Storm", /datum/event/ion_storm, 0, list(ASSIGNMENT_AI = 50, ASSIGNMENT_CYBORG = 50, ASSIGNMENT_ENGINEER = 15, ASSIGNMENT_SCIENTIST = 5)), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Borer Infestation", /datum/event/borer_infestation, 40, list(ASSIGNMENT_SECURITY = 30), 1), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Immovable Rod", /datum/event/immovable_rod, 0, list(ASSIGNMENT_ENGINEER = 30), 1), - //new /datum/event_meta/ninja(EVENT_LEVEL_MODERATE, "Space Ninja", /datum/event/space_ninja, 0, list(ASSIGNMENT_SECURITY = 15), 1), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Borer Infestation", /datum/event/borer_infestation, 40, list(ASSIGNMENT_SECURITY = 30), TRUE), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Immovable Rod", /datum/event/immovable_rod, 0, list(ASSIGNMENT_ENGINEER = 30), TRUE), + //new /datum/event_meta/ninja(EVENT_LEVEL_MODERATE, "Space Ninja", /datum/event/space_ninja, 0, list(ASSIGNMENT_SECURITY = 15), TRUE), // NON-BAY EVENTS new /datum/event_meta(EVENT_LEVEL_MODERATE, "Mass Hallucination", /datum/event/mass_hallucination, 300), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Brand Intelligence", /datum/event/brand_intelligence, 50, list(ASSIGNMENT_ENGINEER = 25), 1), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Brand Intelligence", /datum/event/brand_intelligence, 50, list(ASSIGNMENT_ENGINEER = 25), TRUE), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Space Dust", /datum/event/dust, 50, list(ASSIGNMENT_ENGINEER = 50)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Dimensional Tear", /datum/event/tear, 0, list(ASSIGNMENT_SECURITY = 35)), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Honknomoly", /datum/event/tear/honk, 0), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Honknomoly", /datum/event/tear/honk, 0), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Vent Clog", /datum/event/vent_clog, 250), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Wormholes", /datum/event/wormholes, 150), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Pyro Anomaly", /datum/event/anomaly/anomaly_pyro, 75, list(ASSIGNMENT_ENGINEER = 60)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Pyro Anomaly", /datum/event/anomaly/anomaly_pyro, 75, list(ASSIGNMENT_ENGINEER = 60)), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Atmos Anomaly", /datum/event/anomaly/anomaly_atmos, 75, list(ASSIGNMENT_ENGINEER = 60)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Vortex Anomaly", /datum/event/anomaly/anomaly_vortex, 75, list(ASSIGNMENT_ENGINEER = 25)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Bluespace Anomaly", /datum/event/anomaly/anomaly_bluespace, 75, list(ASSIGNMENT_ENGINEER = 25)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Flux Anomaly", /datum/event/anomaly/anomaly_flux, 75, list(ASSIGNMENT_ENGINEER = 50)), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Gravitational Anomaly", /datum/event/anomaly/anomaly_grav, 200), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Revenant", /datum/event/revenant, 150), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Swarmer Spawn", /datum/event/spawn_swarmer, 150, is_one_shot = 1), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Morph Spawn", /datum/event/spawn_morph, 40, is_one_shot = 1), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Disease Outbreak", /datum/event/disease_outbreak, 0, list(ASSIGNMENT_MEDICAL = 150), 1), - new /datum/event_meta(EVENT_LEVEL_MODERATE, "Headcrabs", /datum/event/headcrabs, 0, list(ASSIGNMENT_SECURITY = 20)) + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Swarmer Spawn", /datum/event/spawn_swarmer, 150, is_one_shot = TRUE), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Morph Spawn", /datum/event/spawn_morph, 40, is_one_shot = TRUE), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Disease Outbreak", /datum/event/disease_outbreak, 0, list(ASSIGNMENT_MEDICAL = 150), TRUE), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Headcrabs", /datum/event/headcrabs, 0, list(ASSIGNMENT_SECURITY = 20)) ) /datum/event_container/major severity = EVENT_LEVEL_MAJOR available_events = list( new /datum/event_meta(EVENT_LEVEL_MAJOR, "Nothing", /datum/event/nothing, 1320), - new /datum/event_meta(EVENT_LEVEL_MAJOR, "Carp Migration", /datum/event/carp_migration, 0, list(ASSIGNMENT_SECURITY = 3), 1), + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Carp Migration", /datum/event/carp_migration, 0, list(ASSIGNMENT_SECURITY = 3), TRUE), //new /datum/event_meta(EVENT_LEVEL_MAJOR, "Containment Breach", /datum/event/prison_break/station, 0, list(ASSIGNMENT_ANY = 5)), new /datum/event_meta(EVENT_LEVEL_MAJOR, "APC Overload", /datum/event/apc_overload, 0), - new /datum/event_meta(EVENT_LEVEL_MAJOR, "Blob", /datum/event/blob, 0, list(ASSIGNMENT_ENGINEER = 30), 1), - new /datum/event_meta(EVENT_LEVEL_MAJOR, "Meteor Wave", /datum/event/meteor_wave, 0, list(ASSIGNMENT_ENGINEER = 5), 1), - new /datum/event_meta(EVENT_LEVEL_MAJOR, "Abductor Visit", /datum/event/abductor, 80, is_one_shot = 1), - //new /datum/event_meta/alien(EVENT_LEVEL_MAJOR, "Alien Infestation", /datum/event/alien_infestation, 0, list(ASSIGNMENT_SECURITY = 15), 1), - new /datum/event_meta(EVENT_LEVEL_MAJOR, "Traders", /datum/event/traders, 180, is_one_shot = 1), - new /datum/event_meta(EVENT_LEVEL_MAJOR, "Terror Spiders", /datum/event/spider_terror, 0, list(ASSIGNMENT_SECURITY = 15), is_one_shot = 1), - new /datum/event_meta(EVENT_LEVEL_MAJOR, "Slaughter Demon", /datum/event/spawn_slaughter, 15, is_one_shot = 1) - //new /datum/event_meta(EVENT_LEVEL_MAJOR, "Floor Cluwne", /datum/event/spawn_floor_cluwne, 15, is_one_shot = 1) + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Blob", /datum/event/blob, 0, list(ASSIGNMENT_ENGINEER = 30), TRUE), + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Meteor Wave", /datum/event/meteor_wave, 0, list(ASSIGNMENT_ENGINEER = 5), TRUE), + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Abductor Visit", /datum/event/abductor, 80, is_one_shot = TRUE), + //new /datum/event_meta/alien(EVENT_LEVEL_MAJOR, "Alien Infestation", /datum/event/alien_infestation, 0, list(ASSIGNMENT_SECURITY = 15), TRUE), + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Traders", /datum/event/traders, 180, is_one_shot = TRUE), + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Terror Spiders", /datum/event/spider_terror, 0, list(ASSIGNMENT_SECURITY = 15), is_one_shot = TRUE), + new /datum/event_meta(EVENT_LEVEL_MAJOR, "Slaughter Demon", /datum/event/spawn_slaughter, 15, is_one_shot = TRUE) + //new /datum/event_meta(EVENT_LEVEL_MAJOR, "Floor Cluwne", /datum/event/spawn_floor_cluwne, 15, is_one_shot = TRUE) ) diff --git a/code/modules/events/event_procs.dm b/code/modules/events/event_procs.dm index c5b7c893c26..cc00f05723f 100644 --- a/code/modules/events/event_procs.dm +++ b/code/modules/events/event_procs.dm @@ -1,9 +1,9 @@ /client/proc/forceEvent(var/type in SSevents.allEvents) - set name = "Trigger Event (Debug Only)" + set name = "Trigger Event" set category = "Debug" - if(!holder) + if(!check_rights(R_EVENT)) return if(ispath(type)) diff --git a/code/modules/events/mass_hallucination.dm b/code/modules/events/mass_hallucination.dm index 196642ce39c..b8f45023a27 100644 --- a/code/modules/events/mass_hallucination.dm +++ b/code/modules/events/mass_hallucination.dm @@ -2,7 +2,13 @@ announceWhen = rand(0, 20) /datum/event/mass_hallucination/start() - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing + if(H.stat == DEAD) + continue + var/turf/T = get_turf(H) + if(!is_station_level(T.z)) + continue var/armor = H.getarmor(type = "rad") if((RADIMMUNE in H.dna.species.species_traits) || armor >= 75) // Leave radiation-immune species/rad armored players completely unaffected continue diff --git a/code/modules/events/rogue_drones.dm b/code/modules/events/rogue_drones.dm index 02fa69b7c3e..f3ea93c80c4 100644 --- a/code/modules/events/rogue_drones.dm +++ b/code/modules/events/rogue_drones.dm @@ -4,24 +4,16 @@ var/list/drones_list = list() /datum/event/rogue_drone/start() - //spawn them at the same place as carp var/list/possible_spawns = list() for(var/thing in GLOB.landmarks_list) var/obj/effect/landmark/C = thing - if(C.name == "carpspawn") + if(C.name == "carpspawn") //spawn them at the same place as carp possible_spawns.Add(C) - //25% chance for this to be a false alarm - var/num - if(prob(25)) - num = 0 - else - num = rand(2,6) - for(var/i=0, i 0 && candidates.len > 0) if(index > spawnlocs.len) diff --git a/code/modules/examine/descriptions/atmospherics.dm b/code/modules/examine/descriptions/atmospherics.dm index 2b113a303d8..fb723336d85 100644 --- a/code/modules/examine/descriptions/atmospherics.dm +++ b/code/modules/examine/descriptions/atmospherics.dm @@ -127,15 +127,6 @@ description_info = "This filters the atmosphere of harmful gas. Filtered gas goes to the pipes connected to it, typically a scrubber pipe. \ It can be controlled from an Air Alarm. It can be configured to drain all air rapidly with a 'panic syphon' from an air alarm." -//Omni filters -/obj/machinery/atmospherics/omni/filter - description_info = "Filters gas from a custom input direction, with up to two filtered outputs and a 'everything else' \ - output. The filtered output's arrows glow orange." - -//Omni mixers -/obj/machinery/atmospherics/omni/mixer - description_info = "Combines gas from custom input and output directions. The percentage of combined gas can be defined." - //Canisters /obj/machinery/portable_atmospherics/canister description_info = "The canister can be connected to a connector port with a wrench. Tanks of gas (the kind you can hold in your hand) \ diff --git a/code/modules/examine/descriptions/weapons.dm b/code/modules/examine/descriptions/weapons.dm index 286391f1387..86e5f69699b 100644 --- a/code/modules/examine/descriptions/weapons.dm +++ b/code/modules/examine/descriptions/weapons.dm @@ -8,12 +8,10 @@ //This contains a lot of copypasta but I'm told it's better then a lot of New()s appending the var. /obj/item/gun - description_info = "This is a gun. To fire the weapon, have your gun mode set to 'fire', \ - then click where you want to fire." + description_info = "This is a gun." /obj/item/gun/energy - description_info = "This is an energy weapon. To fire the weapon, have your gun mode set to 'fire', \ - then click where you want to fire. Most energy weapons can fire through windows harmlessly. To recharge this weapon, use a weapon recharger." + description_info = "This is an energy weapon. Most energy weapons can fire through windows harmlessly. To recharge this weapon, use a weapon recharger." /obj/item/gun/energy/kinetic_accelerator/crossbow description_info = "This is an energy weapon. To fire the weapon, have your gun mode set to 'fire', \ @@ -22,42 +20,34 @@ addition to toxins. The energy crossbow recharges itself slowly, and can be concealed in your pocket or bag." /obj/item/gun/energy/gun - description_info = "This is an energy weapon. To fire the weapon, have your gun mode set to 'fire', \ - then click where you want to fire. Most energy weapons can fire through windows harmlessly. To switch between stun and lethal, click the weapon \ + description_info = "This is an energy weapon. Most energy weapons can fire through windows harmlessly. To switch between stun and lethal, click the weapon \ in your hand. To recharge this weapon, use a weapon recharger." /obj/item/gun/energy/gun/advtaser - description_info = "This is an energy weapon. To fire the weapon, have your gun mode set to 'fire', \ - then click where you want to fire. To recharge this weapon, use a weapon recharger. \ + description_info = "This is an energy weapon. To recharge this weapon, use a weapon recharger. \ To switch between insta-stun and disabler beams, click the weapon in your hand. This weapon can only fire through glass if it is set to disabler beams." /obj/item/gun/energy/nuclear - description_info = "This is an energy weapon. To fire the weapon, have your gun mode set to 'fire', \ - then click where you want to fire. Most energy weapons can fire through windows harmlessly. To switch between stun and lethal, click the weapon \ + description_info = "This is an energy weapon. Most energy weapons can fire through windows harmlessly. To switch between stun and lethal, click the weapon \ in your hand. Unlike most weapons, this weapon recharges itself." /obj/item/gun/energy/captain - description_info = "This is an energy weapon. To fire the weapon, have your gun mode set to 'fire', \ - then click where you want to fire. Most energy weapons can fire through windows harmlessly. Unlike most weapons, this weapon recharges itself." + description_info = "This is an energy weapon. Most energy weapons can fire through windows harmlessly. Unlike most weapons, this weapon recharges itself." /obj/item/gun/energy/sniperrifle - description_info = "This is an energy weapon. To fire the weapon, have your gun mode set to 'fire', \ - then click where you want to fire. Most energy weapons can fire through windows harmlessly. To recharge this weapon, use a weapon recharger. \ + description_info = "This is an energy weapon. Most energy weapons can fire through windows harmlessly. To recharge this weapon, use a weapon recharger. \ To use the scope, use the appropriate verb in the object tab." /obj/item/gun/projectile - description_info = "This is a ballistic weapon. To fire the weapon, have your gun mode set to 'fire', \ - then click where you want to fire. To reload, click the weapon in your hand to unload (if needed), then add the appropriate ammo. The description \ + description_info = "This is a ballistic weapon. To reload, click the weapon in your hand to unload (if needed), then add the appropriate ammo. The description \ will tell you what caliber you need." /obj/item/gun/projectile/shotgun/pump - description_info = "This is a ballistic weapon. To fire the weapon, have your gun mode set to 'fire', \ - then click where you want to fire. After firing, you will need to pump the gun, by clicking on the gun in your hand. To reload, load more shotgun \ + description_info = "This is a ballistic weapon. After firing, you will need to pump the gun, by clicking on the gun in your hand. To reload, load more shotgun \ shells into the gun." /obj/item/toy/russian_revolver/trick_revolver //oh no - description_info = "This is a ballistic weapon. To fire the weapon, have your gun mode set to 'fire', \ - then click where you want to fire. To reload, click the weapon in your hand to unload (if needed), then add the appropriate ammo. The description \ + description_info = "This is a ballistic weapon. To reload, click the weapon in your hand to unload (if needed), then add the appropriate ammo. The description \ will tell you what caliber you need." //******* diff --git a/code/modules/fish/fish_items.dm b/code/modules/fish/fish_items.dm index 4a7a678a09e..bb31226f42a 100644 --- a/code/modules/fish/fish_items.dm +++ b/code/modules/fish/fish_items.dm @@ -66,11 +66,11 @@ icon_state = "shrimp_raw" filling_color = "#FF1C1C" - New() - ..() - desc = pick("Anyway, like I was sayin', shrimp is the fruit of the sea.", "You can barbecue it, boil it, broil it, bake it, saute it.") - reagents.add_reagent("protein", 1) - src.bitesize = 1 +/obj/item/reagent_containers/food/snacks/shrimp/New() + ..() + desc = pick("Anyway, like I was sayin', shrimp is the fruit of the sea.", "You can barbecue it, boil it, broil it, bake it, saute it.") + reagents.add_reagent("protein", 1) + src.bitesize = 1 /obj/item/reagent_containers/food/snacks/feederfish name = "feeder fish" @@ -79,10 +79,10 @@ icon_state = "feederfish" filling_color = "#FF1C1C" - New() - ..() - reagents.add_reagent("protein", 1) - src.bitesize = 1 +/obj/item/reagent_containers/food/snacks/shrimp/New() + ..() + reagents.add_reagent("protein", 1) + src.bitesize = 1 /obj/item/fish name = "fish" diff --git a/code/modules/flufftext/Dreaming.dm b/code/modules/flufftext/Dreaming.dm index d4c06fdc3a3..a24b73ae56c 100644 --- a/code/modules/flufftext/Dreaming.dm +++ b/code/modules/flufftext/Dreaming.dm @@ -19,7 +19,7 @@ var/list/newlist = dreamlist.Copy() for(var/i in 1 to newlist.len) newlist[i] = replacetext(newlist[i], "\[DREAMER\]", "[user.name]") - return dreamlist + return newlist //NIGHTMARES diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 51ac13d4e13..21b99acb910 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -149,10 +149,12 @@ Gunshots/explosions/opening doors/less rare audio (done) /obj/effect/hallucination/fake_flood/process() if(!target) qdel(src) + return if(next_expand <= world.time) radius++ if(radius > FAKE_FLOOD_MAX_RADIUS) qdel(src) + return Expand() if((get_turf(target) in flood_turfs) && !target.internal) target.hallucinate("fake_alert", "too_much_tox") @@ -460,8 +462,9 @@ Gunshots/explosions/opening doors/less rare audio (done) target = T var/image/A = null var/kind = force_kind ? force_kind : pick("clown", "corgi", "carp", "skeleton", "demon","zombie") - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) - if(H == target) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing + if(H.stat == DEAD || H == target) continue if(skip_nearby && (H in view(target))) continue @@ -539,7 +542,8 @@ Gunshots/explosions/opening doors/less rare audio (done) var/mob/living/carbon/human/clone = null var/clone_weapon = null - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing if(H.stat || H.lying) continue clone = H @@ -751,8 +755,10 @@ GLOBAL_LIST_INIT(non_fakeattack_weapons, list(/obj/item/gun/projectile, /obj/ite target.client.images.Remove(speech_overlay) else // Radio talk var/list/humans = list() - for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) - humans += H + for(var/thing in GLOB.human_list) + var/mob/living/carbon/human/H = thing + if(H.stat != DEAD) + humans += H person = pick(humans) target.hear_radio(message_to_multilingual(pick(radio_messages), pick(person.languages)), speaker = person, part_a = "\[[get_frequency_name(PUB_FREQ)]\] ", part_b = " ") qdel(src) diff --git a/code/modules/flufftext/TextFilters.dm b/code/modules/flufftext/TextFilters.dm index e00f653ac45..9224ec51d00 100644 --- a/code/modules/flufftext/TextFilters.dm +++ b/code/modules/flufftext/TextFilters.dm @@ -1,4 +1,4 @@ -proc/Intoxicated(phrase) +/proc/Intoxicated(phrase) phrase = html_decode(phrase) var/leng=length(phrase) var/counter=length(phrase) @@ -21,7 +21,7 @@ proc/Intoxicated(phrase) newphrase+="[newletter]";counter-=1 return newphrase -proc/NewStutter(phrase,stunned) +/proc/NewStutter(phrase,stunned) phrase = html_decode(phrase) var/list/split_phrase = splittext(phrase," ") //Split it up into words. @@ -57,10 +57,10 @@ proc/NewStutter(phrase,stunned) return sanitize(jointext(split_phrase," ")) -proc/Stagger(mob/M,d) //Technically not a filter, but it relates to drunkenness. +/proc/Stagger(mob/M,d) //Technically not a filter, but it relates to drunkenness. step(M, pick(d,turn(d,90),turn(d,-90))) -proc/Ellipsis(original_msg, chance = 50) +/proc/Ellipsis(original_msg, chance = 50) if(chance <= 0) return "..." if(chance >= 100) return original_msg diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm index 7c253393e07..dbc01373a9f 100644 --- a/code/modules/food_and_drinks/drinks/drinks.dm +++ b/code/modules/food_and_drinks/drinks/drinks.dm @@ -226,7 +226,7 @@ desc = "Made in Space Switzerland." icon_state = "hot_coco" item_state = "coffee" - list_reagents = list("chocolate" = 30) + list_reagents = list("hot_coco" = 15, "chocolate" = 6, "water" = 9) resistance_flags = FREEZE_PROOF /obj/item/reagent_containers/food/drinks/weightloss diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm index 4515a62d86b..f77d4b60ab4 100644 --- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm +++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm @@ -124,6 +124,13 @@ reagents.reaction(M, REAGENT_TOUCH) reagents.clear_reagents() +/obj/item/reagent_containers/food/drinks/bottle/decompile_act(obj/item/matter_decompiler/C, mob/user) + if(!reagents.total_volume) + C.stored_comms["glass"] += 3 + qdel(src) + return TRUE + return ..() + //Keeping this here for now, I'll ask if I should keep it here. /obj/item/broken_bottle name = "Broken Bottle" @@ -141,6 +148,11 @@ var/icon/broken_outline = icon('icons/obj/drinks.dmi', "broken") sharp = 1 +/obj/item/broken_bottle/decompile_act(obj/item/matter_decompiler/C, mob/user) + C.stored_comms["glass"] += 3 + qdel(src) + return TRUE + /obj/item/reagent_containers/food/drinks/bottle/gin name = "Griffeater Gin" desc = "A bottle of high quality gin, produced in the New London Space Station." diff --git a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm index 5f13f5c7a06..8313b967722 100644 --- a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm +++ b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm @@ -13,6 +13,10 @@ max_integrity = 20 resistance_flags = ACID_PROOF +/obj/item/reagent_containers/food/drinks/set_APTFT() + set hidden = FALSE + ..() + /obj/item/reagent_containers/food/drinks/drinkingglass/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/reagent_containers/food/snacks/egg)) //breaking eggs var/obj/item/reagent_containers/food/snacks/egg/E = I diff --git a/code/modules/food_and_drinks/drinks/drinks/shotglass.dm b/code/modules/food_and_drinks/drinks/drinks/shotglass.dm index d5d114fb43f..7b3349e4c2d 100644 --- a/code/modules/food_and_drinks/drinks/drinks/shotglass.dm +++ b/code/modules/food_and_drinks/drinks/drinks/shotglass.dm @@ -7,6 +7,7 @@ materials = list(MAT_GLASS=100) var/light_intensity = 2 light_color = LIGHT_COLOR_LIGHTBLUE + resistance_flags = FLAMMABLE /obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/on_reagent_change() if(!isShotFlammable() && (resistance_flags & ON_FIRE)) @@ -30,6 +31,7 @@ overlays += filling name = "shot glass of " + reagents.get_master_reagent_name() //No matter what, the glass will tell you the reagent's name. Might be too abusable in the future. if(resistance_flags & ON_FIRE) + cut_overlay(GLOB.fire_overlay, TRUE) overlays += "shotglass_fire" name = "flaming [name]" else diff --git a/code/modules/food_and_drinks/food.dm b/code/modules/food_and_drinks/food.dm index 27f0919a3ce..15e28cc6c62 100644 --- a/code/modules/food_and_drinks/food.dm +++ b/code/modules/food_and_drinks/food.dm @@ -33,6 +33,10 @@ deltimer(ant_timer) return ..() +/obj/item/reagent_containers/food/set_APTFT() + set hidden = TRUE + ..() + /obj/item/reagent_containers/food/proc/check_for_ants() if(!antable) return diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm index bdeadf2a72c..733ac4692a3 100644 --- a/code/modules/food_and_drinks/food/condiment.dm +++ b/code/modules/food_and_drinks/food/condiment.dm @@ -29,6 +29,10 @@ /obj/item/reagent_containers/food/condiment/attack_self(mob/user) return +/obj/item/reagent_containers/food/condiment/set_APTFT() + set hidden = FALSE + ..() + /obj/item/reagent_containers/food/condiment/attack(mob/M, mob/user, def_zone) if(!reagents || !reagents.total_volume) diff --git a/code/modules/food_and_drinks/food/foods/baked_goods.dm b/code/modules/food_and_drinks/food/foods/baked_goods.dm index 57fd36393b5..9a7c0e89999 100644 --- a/code/modules/food_and_drinks/food/foods/baked_goods.dm +++ b/code/modules/food_and_drinks/food/foods/baked_goods.dm @@ -432,6 +432,29 @@ icon_state = "jdonut1" extra_reagent = "cherryjelly" +////////////////////// +// Pancakes // +////////////////////// + +/obj/item/reagent_containers/food/snacks/pancake + name = "pancake" + desc = "A plain pancake." + icon_state = "pancake" + filling_color = "#E7D8AB" + bitesize = 2 + list_reagents = list("nutriment" = 3, "sugar" = 3) + +/obj/item/reagent_containers/food/snacks/pancake/berry_pancake + name = "berry pancake" + desc = "A pancake loaded with berries." + icon_state = "berry_pancake" + list_reagents = list("nutriment" = 3, "sugar" = 3, "berryjuice" = 3) + +/obj/item/reagent_containers/food/snacks/pancake/choc_chip_pancake + name = "choc-chip pancake" + desc = "A pancake loaded with chocolate chips." + icon_state = "choc_chip_pancake" + list_reagents = list("nutriment" = 3, "sugar" = 3, "cocoa" = 3) ////////////////////// // Misc // diff --git a/code/modules/food_and_drinks/food/foods/ethnic.dm b/code/modules/food_and_drinks/food/foods/ethnic.dm index 0a160967858..6523b7521e3 100644 --- a/code/modules/food_and_drinks/food/foods/ethnic.dm +++ b/code/modules/food_and_drinks/food/foods/ethnic.dm @@ -65,6 +65,7 @@ name = "sweet & sour chicken balls" desc = "Is this chicken cooked? The odds are better than wok paper scissors." icon_state = "chickenball" + item_state = "chinese3" junkiness = 25 list_reagents = list("nutriment" = 2, "msg" = 4, "sugar" = 2) tastes = list("chicken" = 1, "sweetness" = 1) @@ -89,6 +90,7 @@ name = "fried rice" desc = "A timeless classic." icon_state = "chinese4" + item_state = "chinese2" junkiness = 20 list_reagents = list("nutriment" = 1, "rice" = 3, "msg" = 4, "sugar" = 2) tastes = list("rice" = 1) diff --git a/code/modules/food_and_drinks/food/foods/meat.dm b/code/modules/food_and_drinks/food/foods/meat.dm index 382e2f39022..a9f0ab3d684 100644 --- a/code/modules/food_and_drinks/food/foods/meat.dm +++ b/code/modules/food_and_drinks/food/foods/meat.dm @@ -30,7 +30,7 @@ name = "-meat" var/subjectname = "" var/subjectjob = null - tastes = list("tender meat" = 1) + tastes = list("salty meat" = 1) /obj/item/reagent_containers/food/snacks/meat/slab/meatproduct name = "meat product" diff --git a/code/modules/food_and_drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm index 8d0408a74fb..38e1acd550b 100644 --- a/code/modules/food_and_drinks/food/snacks.dm +++ b/code/modules/food_and_drinks/food/snacks.dm @@ -34,16 +34,15 @@ /obj/item/reagent_containers/food/snacks/proc/On_Consume(mob/M, mob/user) if(!user) return - spawn(0) - if(!reagents.total_volume) - if(M == user) - to_chat(user, "You finish eating \the [src].") - user.visible_message("[M] finishes eating \the [src].") - user.unEquip(src) //so icons update :[ - Post_Consume(M) - var/obj/item/trash_item = generate_trash(usr) - usr.put_in_hands(trash_item) - qdel(src) + if(!reagents.total_volume) + if(M == user) + to_chat(user, "You finish eating \the [src].") + user.visible_message("[M] finishes eating \the [src].") + user.unEquip(src) //so icons update :[ + Post_Consume(M) + var/obj/item/trash_item = generate_trash(usr) + usr.put_in_hands(trash_item) + qdel(src) return /obj/item/reagent_containers/food/snacks/proc/Post_Consume(mob/living/M) diff --git a/code/modules/food_and_drinks/kitchen_machinery/cereal_maker.dm b/code/modules/food_and_drinks/kitchen_machinery/cereal_maker.dm index dc81717496a..59fe4b78bcd 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/cereal_maker.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/cereal_maker.dm @@ -8,17 +8,17 @@ onicon = "cereal_on" officon = "cereal_off" -obj/machinery/cooker/cerealmaker/setIcon(obj/item/copyme, obj/item/copyto) +/obj/machinery/cooker/cerealmaker/setIcon(obj/item/copyme, obj/item/copyto) var/image/img = new(copyme.icon, copyme.icon_state) img.transform *= 0.7 copyto.overlays += img copyto.overlays += copyme.overlays -obj/machinery/cooker/cerealmaker/changename(obj/item/name, obj/item/setme) +/obj/machinery/cooker/cerealmaker/changename(obj/item/name, obj/item/setme) setme.name = "box of [name] cereal" setme.desc = "[name.desc] It has been [thiscooktype]" -obj/machinery/cooker/cerealmaker/gettype() +/obj/machinery/cooker/cerealmaker/gettype() var/obj/item/reagent_containers/food/snacks/cereal/type = new(get_turf(src)) return type diff --git a/code/modules/food_and_drinks/kitchen_machinery/food_grill.dm b/code/modules/food_and_drinks/kitchen_machinery/food_grill.dm index 54290c80b37..f68f8c31baf 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/food_grill.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/food_grill.dm @@ -11,7 +11,7 @@ onicon = "grill_on" officon = "grill_off" -obj/machinery/cooker/foodgrill/putIn(obj/item/In, mob/chef) +/obj/machinery/cooker/foodgrill/putIn(obj/item/In, mob/chef) ..() var/image/img = new(In.icon, In.icon_state) img.pixel_y = 5 diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm index db078675db1..f65b78f3ca2 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm @@ -234,7 +234,7 @@ return if(UserOverride) - msg_admin_attack("[key_name_admin(occupant)] was gibbed by an autogibber (\the [src]) [ADMIN_JMP(src)]") + add_attack_logs(user, occupant, "gibbed by an autogibber ([src])") log_game("[key_name(occupant)] was gibbed by an autogibber ([src]) (X:[x] Y:[y] Z:[z])") if(operating) diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index 346b78e5b26..2245a2053be 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -1,206 +1,77 @@ -/* SmartFridge. Much todo -*/ +/** + * # Smart Fridge + * + * Stores items of a specified type. + */ /obj/machinery/smartfridge name = "\improper SmartFridge" icon = 'icons/obj/vending.dmi' icon_state = "smartfridge" layer = 2.9 - density = 1 - anchored = 1 + density = TRUE + anchored = TRUE use_power = IDLE_POWER_USE idle_power_usage = 5 active_power_usage = 100 + /// The maximum number of items the fridge can hold. Multiplicated by the matter bin component's rating. var/max_n_of_items = 1500 - var/item_quants = list() + /// Associative list (/text => /number) tracking the amounts of a specific item held by the fridge. + var/list/item_quants + /// How long in ticks the fridge is electrified for. Decrements every process. var/seconds_electrified = 0 + /// Whether the fridge should randomly shoot held items at a nearby living target or not. var/shoot_inventory = FALSE - var/locked = FALSE + /// Whether the fridge requires ID scanning. Used for the secure variant of the fridge. var/scan_id = TRUE + /// Whether the fridge is considered secure. Used for wiring and display. var/is_secure = FALSE + /// Whether the fridge can dry its' contents. Used for display. var/can_dry = FALSE + /// Whether the fridge is currently drying. Used by [drying racks][/obj/machinery/smartfridge/drying_rack]. var/drying = FALSE + /// Whether the fridge's contents are visible on the world icon. var/visible_contents = TRUE - var/datum/wires/smartfridge/wires = null + /// The wires controlling the fridge. + var/datum/wires/smartfridge/wires + /// Typecache of accepted item types, init it in [/obj/machinery/smartfridge/Initialize]. + var/list/accepted_items_typecache -/obj/machinery/smartfridge/New() - ..() +/obj/machinery/smartfridge/Initialize(mapload) + . = ..() + item_quants = list() + // Reagents create_reagents() reagents.set_reacting(FALSE) + // Components component_parts = list() var/obj/item/circuitboard/smartfridge/board = new(null) board.set_type(type) component_parts += board component_parts += new /obj/item/stock_parts/matter_bin(null) RefreshParts() + // Wires + if(is_secure) + wires = new/datum/wires/smartfridge/secure(src) + else + wires = new/datum/wires/smartfridge(src) + // Accepted items + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/food/snacks/grown, + /obj/item/seeds, + /obj/item/grown, + )) /obj/machinery/smartfridge/RefreshParts() for(var/obj/item/stock_parts/matter_bin/B in component_parts) max_n_of_items = 1500 * B.rating -/obj/machinery/smartfridge/secure - is_secure = 1 - -/obj/machinery/smartfridge/New() - ..() - if(is_secure) - wires = new/datum/wires/smartfridge/secure(src) - else - wires = new/datum/wires/smartfridge(src) - /obj/machinery/smartfridge/Destroy() + SStgui.close_uis(wires) QDEL_NULL(wires) for(var/atom/movable/A in contents) A.forceMove(loc) return ..() -/obj/machinery/smartfridge/proc/accept_check(obj/item/O) - if(istype(O,/obj/item/reagent_containers/food/snacks/grown/) || istype(O,/obj/item/seeds/) || istype(O,/obj/item/grown/)) - return 1 - return 0 - -/obj/machinery/smartfridge/seeds - name = "\improper MegaSeed Servitor" - desc = "When you need seeds fast!" - icon = 'icons/obj/vending.dmi' - icon_state = "seeds" - -/obj/machinery/smartfridge/seeds/accept_check(obj/item/O) - if(istype(O,/obj/item/seeds/)) - return 1 - return 0 - -/obj/machinery/smartfridge/medbay - name = "\improper Refrigerated Medicine Storage" - desc = "A refrigerated storage unit for storing medicine and chemicals." - icon_state = "smartfridge" //To fix the icon in the map editor. - -/obj/machinery/smartfridge/medbay/accept_check(obj/item/O) - if(istype(O,/obj/item/reagent_containers/glass)) - return 1 - if(istype(O,/obj/item/reagent_containers/iv_bag)) - return 1 - if(istype(O,/obj/item/storage/pill_bottle)) - return 1 - if(ispill(O)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/extract - name = "\improper Slime Extract Storage" - desc = "A refrigerated storage unit for slime extracts" - req_access_txt = "47" - -/obj/machinery/smartfridge/secure/extract/accept_check(obj/item/O) - if(istype(O,/obj/item/slime_extract)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/medbay - name = "\improper Secure Refrigerated Medicine Storage" - desc = "A refrigerated storage unit for storing medicine and chemicals." - icon_state = "smartfridge" //To fix the icon in the map editor. - req_one_access_txt = "5;33" - -/obj/machinery/smartfridge/secure/medbay/accept_check(obj/item/O) - if(istype(O,/obj/item/reagent_containers/glass)) - return 1 - if(istype(O,/obj/item/reagent_containers/iv_bag)) - return 1 - if(istype(O,/obj/item/storage/pill_bottle)) - return 1 - if(ispill(O)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/chemistry - name = "\improper Smart Chemical Storage" - desc = "A refrigerated storage unit for medicine and chemical storage." - icon_state = "smartfridge" //To fix the icon in the map editor. - req_access_txt = "33" - var/list/spawn_meds = list() - -/obj/machinery/smartfridge/secure/chemistry/New() - ..() - for(var/typekey in spawn_meds) - var/amount = spawn_meds[typekey] - if(isnull(amount)) amount = 1 - while(amount) - var/obj/item/I = new typekey(src) - if(item_quants[I.name]) - item_quants[I.name]++ - else - item_quants[I.name] = 1 - SSnanoui.update_uis(src) - amount-- - update_icon() - -/obj/machinery/smartfridge/secure/chemistry/accept_check(obj/item/O) - if(istype(O,/obj/item/storage/pill_bottle) || istype(O,/obj/item/reagent_containers)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/chemistry/preloaded - spawn_meds = list( - /obj/item/reagent_containers/food/pill/epinephrine = 12, - /obj/item/reagent_containers/food/pill/charcoal = 5, - /obj/item/reagent_containers/glass/bottle/epinephrine = 1, - /obj/item/reagent_containers/glass/bottle/charcoal = 1) - -/obj/machinery/smartfridge/secure/chemistry/preloaded/syndicate - req_access_txt = null - req_access = list(ACCESS_SYNDICATE) - -/obj/machinery/smartfridge/disks - name = "disk compartmentalizer" - desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)." - icon_state = "disktoaster" - pass_flags = PASSTABLE - visible_contents = FALSE - -/obj/machinery/smartfridge/disks/accept_check(obj/item/O) - return istype(O, /obj/item/disk) - -// ---------------------------- -// Virology Medical Smartfridge -// ---------------------------- -/obj/machinery/smartfridge/secure/chemistry/virology - name = "Smart Virus Storage" - desc = "A refrigerated storage unit for volatile sample storage." - req_access_txt = "39" - spawn_meds = list(/obj/item/reagent_containers/syringe/antiviral = 4, - /obj/item/reagent_containers/glass/bottle/cold = 1, - /obj/item/reagent_containers/glass/bottle/flu_virion = 1, - /obj/item/reagent_containers/glass/bottle/mutagen = 1, - /obj/item/reagent_containers/glass/bottle/plasma = 1, - /obj/item/reagent_containers/glass/bottle/diphenhydramine = 1) - -/obj/machinery/smartfridge/secure/chemistry/virology/accept_check(obj/item/O) - if(istype(O, /obj/item/reagent_containers/syringe) || istype(O, /obj/item/reagent_containers/glass/bottle) || istype(O, /obj/item/reagent_containers/glass/beaker)) - return 1 - return 0 - -/obj/machinery/smartfridge/secure/chemistry/virology/preloaded - spawn_meds = list( - /obj/item/reagent_containers/syringe/antiviral = 4, - /obj/item/reagent_containers/glass/bottle/cold = 1, - /obj/item/reagent_containers/glass/bottle/flu_virion = 1, - /obj/item/reagent_containers/glass/bottle/mutagen = 1, - /obj/item/reagent_containers/glass/bottle/plasma = 1, - /obj/item/reagent_containers/glass/bottle/reagent/synaptizine = 1, - /obj/item/reagent_containers/glass/bottle/reagent/formaldehyde = 1) - -/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/syndicate - req_access_txt = null - req_access = list(ACCESS_SYNDICATE) - -/obj/machinery/smartfridge/drinks - name = "\improper Drink Showcase" - desc = "A refrigerated storage unit for tasty tasty alcohol." - -/obj/machinery/smartfridge/drinks/accept_check(obj/item/O) - 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/process() if(stat & (BROKEN|NOPOWER)) return @@ -216,197 +87,171 @@ update_icon() /obj/machinery/smartfridge/update_icon() + var/prefix = initial(icon_state) if(stat & (BROKEN|NOPOWER)) - icon_state = "[initial(icon_state)]-off" + icon_state = "[prefix]-off" else if(visible_contents) - switch(contents.len) + switch(length(contents)) if(0) - icon_state = "[initial(icon_state)]" + icon_state = "[prefix]" if(1 to 25) - icon_state = "[initial(icon_state)]1" + icon_state = "[prefix]1" if(26 to 75) - icon_state = "[initial(icon_state)]2" + icon_state = "[prefix]2" if(76 to INFINITY) - icon_state = "[initial(icon_state)]3" + icon_state = "[prefix]3" else - icon_state = "[initial(icon_state)]" + icon_state = "[prefix]" -/******************* -* Item Adding -********************/ - -/obj/machinery/smartfridge/default_deconstruction_screwdriver(mob/user, obj/item/screwdriver/S) - . = ..(user, icon_state, icon_state, S) +// Interactions +/obj/machinery/smartfridge/screwdriver_act(mob/living/user, obj/item/I) + . = default_deconstruction_screwdriver(user, icon_state, icon_state, I) + if(!.) + return overlays.Cut() if(panel_open) overlays += image(icon, "[initial(icon_state)]-panel") -/obj/machinery/smartfridge/attackby(obj/item/O, var/mob/user) - if(default_deconstruction_screwdriver(user, O)) - return - - if(exchange_parts(user, O)) - return - - if(default_unfasten_wrench(user, O)) +/obj/machinery/smartfridge/wrench_act(mob/living/user, obj/item/I) + . = default_unfasten_wrench(user, I) + if(.) power_change() - return - if(default_deconstruction_crowbar(user, O)) - return +/obj/machinery/smartfridge/crowbar_act(mob/living/user, obj/item/I) + . = default_deconstruction_crowbar(user, I) - if(istype(O, /obj/item/multitool)||istype(O, /obj/item/wirecutters)) - if(panel_open) - attack_hand(user) - return +/obj/machinery/smartfridge/wirecutter_act(mob/living/user, obj/item/I) + if(panel_open) + attack_hand(user) + return TRUE + return ..() - if(stat & NOPOWER) +/obj/machinery/smartfridge/multitool_act(mob/living/user, obj/item/I) + if(panel_open) + attack_hand(user) + return TRUE + return ..() + +/obj/machinery/smartfridge/attackby(obj/item/O, var/mob/user) + if(exchange_parts(user, O)) + SStgui.update_uis(src) + return + if(stat & (BROKEN|NOPOWER)) to_chat(user, "\The [src] is unpowered and useless.") return if(load(O, user)) user.visible_message("[user] has added \the [O] to \the [src].", "You add \the [O] to \the [src].") - SSnanoui.update_uis(src) + SStgui.update_uis(src) update_icon() - else if(istype(O, /obj/item/storage/bag)) var/obj/item/storage/bag/P = O - var/plants_loaded = 0 + var/items_loaded = 0 for(var/obj/G in P.contents) if(load(G, user)) - plants_loaded++ - if(plants_loaded) + items_loaded++ + if(items_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) - update_icon() - + SStgui.update_uis(src) + update_icon() + var/failed = length(P.contents) + if(failed) + to_chat(user, "[failed] item\s [failed == 1 ? "is" : "are"] refused.") else if(!istype(O, /obj/item/card/emag)) to_chat(user, "\The [src] smartly refuses [O].") - return 1 - -/obj/machinery/smartfridge/proc/load(obj/I, mob/user) - if(accept_check(I)) - if(contents.len >= max_n_of_items) - to_chat(user, "\The [src] is full.") - return 0 - else - if(istype(I.loc, /obj/item/storage)) - var/obj/item/storage/S = I.loc - S.remove_from_storage(I, src) - else if(istype(I.loc, /mob)) - var/mob/M = I.loc - if(M.get_active_hand() == I) - if(!M.drop_item()) - to_chat(user, "\The [I] is stuck to you!") - return 0 - else - M.unEquip(I) - I.forceMove(src) - else - I.forceMove(src) - - if(item_quants[I.name]) - item_quants[I.name]++ - else - item_quants[I.name] = 1 - return 1 - return 0 + return TRUE /obj/machinery/smartfridge/attack_ai(mob/user) - return 0 + return FALSE /obj/machinery/smartfridge/attack_ghost(mob/user) return attack_hand(user) /obj/machinery/smartfridge/attack_hand(mob/user) - if(stat & (NOPOWER|BROKEN)) + if(stat & (BROKEN|NOPOWER)) return wires.Interact(user) - ui_interact(user) + tgui_interact(user) + return ..() //Drag pill bottle to fridge to empty it into the fridge /obj/machinery/smartfridge/MouseDrop_T(obj/over_object, mob/user) if(!istype(over_object, /obj/item/storage/pill_bottle)) //Only pill bottles, please return - - if(stat & NOPOWER) + if(stat & (BROKEN|NOPOWER)) to_chat(user, "\The [src] is unpowered and useless.") return var/obj/item/storage/box/pillbottles/P = over_object + if(!length(P.contents)) + to_chat(user, "\The [P] is empty.") + return + var/items_loaded = 0 for(var/obj/G in P.contents) if(load(G, user)) items_loaded++ if(items_loaded) - user.visible_message( \ - "[user] empties \the [P] into \the [src].", \ - "You empty \the [P] into \the [src].") - if(P.contents.len > 0) - to_chat(user, "Some items are refused.") - SSnanoui.update_uis(src) + user.visible_message("[user] empties \the [P] into \the [src].", "You empty \the [P] into \the [src].") + update_icon() + var/failed = length(P.contents) + if(failed) + to_chat(user, "[failed] item\s [failed == 1 ? "is" : "are"] refused.") -/******************* -* SmartFridge Menu -********************/ - -/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) +/obj/machinery/smartfridge/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "smartfridge.tmpl", name, 400, 500) + ui = new(user, src, ui_key, "Smartfridge", name, 500, 500) ui.open() -/obj/machinery/smartfridge/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/smartfridge/tgui_data(mob/user) + var/list/data = list() data["contents"] = null - data["electrified"] = seconds_electrified > 0 - data["shoot_inventory"] = shoot_inventory - data["locked"] = locked data["secure"] = is_secure data["can_dry"] = can_dry data["drying"] = drying - var/list/items[0] - for(var/i=1 to length(item_quants)) + var/list/items = list() + for(var/i in 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) + if(length(items)) data["contents"] = items return data -/obj/machinery/smartfridge/Topic(href, href_list) +/obj/machinery/smartfridge/tgui_act(action, params) if(..()) - return FALSE + return + + . = TRUE var/mob/user = usr - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") add_fingerprint(user) - if(href_list["close"]) - user.unset_machine() - ui.close() - return FALSE + switch(action) + if("vend") + if(is_secure && !emagged && scan_id && !allowed(usr)) //secure fridge check + to_chat(usr, "Access denied.") + return FALSE - if(href_list["vend"]) - var/index = text2num(href_list["vend"]) - var/amount = text2num(href_list["amount"]) - var/K = item_quants[index] - var/count = item_quants[K] + var/index = text2num(params["index"]) + var/amount = text2num(params["amount"]) + if(isnull(index) || !ISINDEXSAFE(item_quants, index) || isnull(amount)) + return FALSE + var/K = item_quants[index] + var/count = item_quants[K] + if(count == 0) // Sanity check, there are probably ways to press the button when it shouldn't be possible. + return FALSE - // 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 @@ -418,7 +263,6 @@ adjust_item_drop_location(O) update_icon() break - return TRUE else for(var/obj/O in contents) if(O.name == K) @@ -428,37 +272,328 @@ i-- if(i <= 0) return TRUE - return TRUE + + +/** + * Tries to load an item if it is accepted by [/obj/machinery/smartfridge/proc/accept_check]. + * + * Arguments: + * * I - The item to load. + * * user - The user trying to load the item. + */ +/obj/machinery/smartfridge/proc/load(obj/I, mob/user) + if(accept_check(I)) + if(length(contents) >= max_n_of_items) + to_chat(user, "\The [src] is full.") + return FALSE + else + if(istype(I.loc, /obj/item/storage)) + var/obj/item/storage/S = I.loc + S.remove_from_storage(I, src) + else if(ismob(I.loc)) + var/mob/M = I.loc + if(M.get_active_hand() == I) + if(!M.drop_item()) + to_chat(user, "\The [I] is stuck to you!") + return FALSE + else + M.unEquip(I) + I.forceMove(src) + else + I.forceMove(src) + + item_quants[I.name] += 1 + return TRUE return FALSE +/** + * Tries to shoot a random at a nearby living mob. + */ /obj/machinery/smartfridge/proc/throw_item() - var/obj/throw_item = null - var/mob/living/target = locate() in view(7,src) + var/obj/item/throw_item = null + var/mob/living/target = locate() in view(7, src) if(!target) - return 0 + return FALSE 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(loc) - throw_item = T + for(var/obj/I in contents) + if(I.name == O) + I.forceMove(loc) + throw_item = I update_icon() break - break if(!throw_item) - return 0 - spawn(0) - throw_item.throw_at(target,16,3,src) - visible_message("[src] launches [throw_item.name] at [target.name]!") - return 1 + return FALSE -// ---------------------------- -// Drying Rack 'smartfridge' -// ---------------------------- + INVOKE_ASYNC(throw_item, /atom/movable.proc/throw_at, target, 16, 3, src) + visible_message("[src] launches [throw_item.name] at [target.name]!") + return TRUE + +/** + * Returns whether the smart fridge can accept the given item. + * + * By default checks if the item is in [the typecache][/obj/machinery/smartfridge/var/accepted_items_typecache]. + * Arguments: + * * O - The item to check. + */ +/obj/machinery/smartfridge/proc/accept_check(obj/item/O) + return is_type_in_typecache(O, accepted_items_typecache) + +/** + * # Secure Fridge + * + * Secure variant of the [Smart Fridge][/obj/machinery/smartfridge]. + * Can be emagged and EMP'd to short the lock. + */ +/obj/machinery/smartfridge/secure + is_secure = TRUE + +/obj/machinery/smartfridge/secure/emag_act(mob/user) + emagged = TRUE + to_chat(user, "You short out the product lock on \the [src].") + +/obj/machinery/smartfridge/secure/emp_act(severity) + if(!emagged && prob(40 / severity)) + playsound(loc, 'sound/effects/sparks4.ogg', 60, TRUE) + emagged = TRUE + +/** + * # Seed Storage + * + * Seeds variant of the [Smart Fridge][/obj/machinery/smartfridge]. + * Formerly known as MegaSeed Servitor, but renamed to avoid confusion with the [vending machine][/obj/machinery/vending/hydroseeds]. + */ +/obj/machinery/smartfridge/seeds + name = "\improper Seed Storage" + desc = "When you need seeds fast!" + icon = 'icons/obj/vending.dmi' + icon_state = "seeds" + +/obj/machinery/smartfridge/seeds/Initialize(mapload) + . = ..() + accepted_items_typecache = typecacheof(list( + /obj/item/seeds + )) + +/** + * # Refrigerated Medicine Storage + * + * Medical variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/medbay + name = "\improper Refrigerated Medicine Storage" + desc = "A refrigerated storage unit for storing medicine and chemicals." + icon_state = "smartfridge" //To fix the icon in the map editor. + +/obj/machinery/smartfridge/medbay/Initialize(mapload) + . = ..() + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/glass, + /obj/item/reagent_containers/iv_bag, + /obj/item/reagent_containers/applicator, + /obj/item/storage/pill_bottle, + /obj/item/reagent_containers/food/pill, + )) + +/** + * # Slime Extract Storage + * + * Secure, Xenobiology variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/secure/extract + name = "\improper Slime Extract Storage" + desc = "A refrigerated storage unit for slime extracts" + +/obj/machinery/smartfridge/secure/extract/Initialize(mapload) + . = ..() + req_access_txt = "[ACCESS_RESEARCH]" + accepted_items_typecache = typecacheof(list( + /obj/item/slime_extract + )) + +/** + * # Secure Refrigerated Medicine Storage + * + * Secure, Medical variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/secure/medbay + name = "\improper Secure Refrigerated Medicine Storage" + desc = "A refrigerated storage unit for storing medicine and chemicals." + icon_state = "smartfridge" //To fix the icon in the map editor. + req_one_access_txt = "5;33" + +/obj/machinery/smartfridge/secure/medbay/Initialize(mapload) + . = ..() + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/glass, + /obj/item/reagent_containers/iv_bag, + /obj/item/reagent_containers/applicator, + /obj/item/storage/pill_bottle, + /obj/item/reagent_containers/food/pill, + )) + +/** + * # Smart Chemical Storage + * + * Secure, Chemistry variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/secure/chemistry + name = "\improper Smart Chemical Storage" + desc = "A refrigerated storage unit for medicine and chemical storage." + icon_state = "smartfridge" //To fix the icon in the map editor. + /// Associative list (/obj/item => /number) representing the items the fridge should initially contain. + var/list/spawn_meds + +/obj/machinery/smartfridge/secure/chemistry/Initialize(mapload) + . = ..() + req_access_txt = "[ACCESS_CHEMISTRY]" + // Spawn initial chemicals + if(mapload) + LAZYINITLIST(spawn_meds) + for(var/typekey in spawn_meds) + var/amount = spawn_meds[typekey] || 1 + while(amount--) + var/obj/item/I = new typekey(src) + item_quants[I.name] += 1 + update_icon() + // Accepted items + accepted_items_typecache = typecacheof(list( + /obj/item/storage/pill_bottle, + /obj/item/reagent_containers, + )) + +/** + * # Smart Chemical Storage (Preloaded) + * + * A [Smart Chemical Storage][/obj/machinery/smartfridge/secure/chemistry] but with some items already in. + */ +/obj/machinery/smartfridge/secure/chemistry/preloaded + // I exist! + +/obj/machinery/smartfridge/secure/chemistry/preloaded/Initialize(mapload) + spawn_meds = list( + /obj/item/reagent_containers/food/pill/epinephrine = 12, + /obj/item/reagent_containers/food/pill/charcoal = 5, + /obj/item/reagent_containers/glass/bottle/epinephrine = 1, + /obj/item/reagent_containers/glass/bottle/charcoal = 1, + ) + . = ..() + +/** + * # Smart Chemical Storage (Preloaded, Syndicate) + * + * A [Smart Chemical Storage (Preloaded)][/obj/machinery/smartfridge/secure/chemistry/preloaded] but with exclusive access to Syndicate. + */ +/obj/machinery/smartfridge/secure/chemistry/preloaded/syndicate + req_access_txt = null + +/obj/machinery/smartfridge/secure/chemistry/preloaded/syndicate/Initialize(mapload) + . = ..() + req_access = list(ACCESS_SYNDICATE) + +/** + * # Disk Compartmentalizer + * + * Disk variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/disks + name = "disk compartmentalizer" + desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)." + icon_state = "disktoaster" + pass_flags = PASSTABLE + visible_contents = FALSE + +/obj/machinery/smartfridge/disks/Initialize(mapload) + . = ..() + accepted_items_typecache = typecacheof(list( + /obj/item/disk, + )) + +/** + * # Smart Virus Storage + * + * Secure, Virology variant of the [Smart Chemical Storage][/obj/machinery/smartfridge/secure/chemistry]. + * Comes with some items. + */ +/obj/machinery/smartfridge/secure/chemistry/virology + name = "\improper Smart Virus Storage" + desc = "A refrigerated storage unit for volatile sample storage." + +/obj/machinery/smartfridge/secure/chemistry/virology/Initialize(mapload) + spawn_meds = list( + /obj/item/reagent_containers/syringe/antiviral = 4, + /obj/item/reagent_containers/glass/bottle/cold = 1, + /obj/item/reagent_containers/glass/bottle/flu_virion = 1, + /obj/item/reagent_containers/glass/bottle/mutagen = 1, + /obj/item/reagent_containers/glass/bottle/plasma = 1, + /obj/item/reagent_containers/glass/bottle/diphenhydramine = 1 + ) + . = ..() + req_access_txt = "[ACCESS_VIROLOGY]" + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/syringe, + /obj/item/reagent_containers/glass/bottle, + /obj/item/reagent_containers/glass/beaker, + )) + +/** + * # Smart Virus Storage (Preloaded) + * + * A [Smart Virus Storage][/obj/machinery/smartfridge/secure/chemistry/virology] but with some additional items. + */ +/obj/machinery/smartfridge/secure/chemistry/virology/preloaded + // I exist! + +/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/Initialize(mapload) + spawn_meds = list( + /obj/item/reagent_containers/syringe/antiviral = 4, + /obj/item/reagent_containers/glass/bottle/cold = 1, + /obj/item/reagent_containers/glass/bottle/flu_virion = 1, + /obj/item/reagent_containers/glass/bottle/mutagen = 1, + /obj/item/reagent_containers/glass/bottle/plasma = 1, + /obj/item/reagent_containers/glass/bottle/reagent/synaptizine = 1, + /obj/item/reagent_containers/glass/bottle/reagent/formaldehyde = 1 + ) + . = ..() + +/** + * # Smart Virus Storage (Preloaded, Syndicate) + * + * A [Smart Virus Storage (Preloaded)][/obj/machinery/smartfridge/secure/chemistry/virology/preloaded] but with exclusive access to Syndicate. + */ +/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/syndicate + req_access_txt = null + +/obj/machinery/smartfridge/secure/chemistry/virology/preloaded/syndicate/Initialize(mapload) + . = ..() + req_access = list(ACCESS_SYNDICATE) + +/** + * # Drink Showcase + * + * Drink variant of the [Smart Fridge][/obj/machinery/smartfridge]. + */ +/obj/machinery/smartfridge/drinks + name = "\improper Drink Showcase" + desc = "A refrigerated storage unit for tasty tasty alcohol." + +/obj/machinery/smartfridge/drinks/Initialize(mapload) + . = ..() + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/glass, + /obj/item/reagent_containers/food/drinks, + /obj/item/reagent_containers/food/condiment, + )) + +/** + * # Drying Rack + * + * Variant of the [Smart Fridge][/obj/machinery/smartfridge] for drying stuff. + * Doesn't have components. + */ /obj/machinery/smartfridge/drying_rack name = "drying rack" desc = "A wooden contraption, used to dry plant products, food and leather." @@ -470,11 +605,16 @@ can_dry = TRUE visible_contents = FALSE -/obj/machinery/smartfridge/drying_rack/New() - ..() - if(component_parts && component_parts.len) - component_parts.Cut() +/obj/machinery/smartfridge/drying_rack/Initialize(mapload) + . = ..() + // Remove components, this is wood duh + QDEL_LIST(component_parts) component_parts = null + // Accepted items + accepted_items_typecache = typecacheof(list( + /obj/item/reagent_containers/food/snacks, + /obj/item/stack/sheet/wetleather, + )) /obj/machinery/smartfridge/drying_rack/on_deconstruction() new /obj/item/stack/sheet/wood(loc, 10) @@ -483,34 +623,6 @@ /obj/machinery/smartfridge/drying_rack/RefreshParts() return -/obj/machinery/smartfridge/drying_rack/default_deconstruction_screwdriver() - return - -/obj/machinery/smartfridge/drying_rack/exchange_parts() - return - -/obj/machinery/smartfridge/drying_rack/spawn_frame() - return - -/obj/machinery/smartfridge/drying_rack/default_deconstruction_crowbar(user, obj/item/crowbar/C, ignore_panel = 1) - ..() - -/obj/machinery/smartfridge/drying_rack/Topic(href, href_list) - if(..()) - return 1 - if(href_list["dryingOn"]) - drying = TRUE - use_power = ACTIVE_POWER_USE - update_icon() - return 1 - - if(href_list["dryingOff"]) - drying = FALSE - use_power = IDLE_POWER_USE - update_icon() - return 1 - return 0 - /obj/machinery/smartfridge/drying_rack/power_change() if(powered() && anchored) stat &= ~NOPOWER @@ -519,35 +631,58 @@ toggle_drying(TRUE) update_icon() -/obj/machinery/smartfridge/drying_rack/load(obj/I, mob/user) //For updating the filled overlay - if(..()) - update_icon() - return 1 +/obj/machinery/smartfridge/drying_rack/screwdriver_act(mob/living/user, obj/item/I) + return + +/obj/machinery/smartfridge/drying_rack/exchange_parts() + return + +/obj/machinery/smartfridge/drying_rack/spawn_frame() + return + +/obj/machinery/smartfridge/drying_rack/crowbar_act(mob/living/user, obj/item/I) + . = default_deconstruction_crowbar(user, I, TRUE) + +/obj/machinery/smartfridge/drying_rack/emp_act(severity) + ..() + atmos_spawn_air(LINDA_SPAWN_HEAT) + +/obj/machinery/smartfridge/drying_rack/tgui_act(action, params) + . = ..() + + switch(action) + if("drying") + drying = !drying + use_power = drying ? ACTIVE_POWER_USE : IDLE_POWER_USE + update_icon() /obj/machinery/smartfridge/drying_rack/update_icon() ..() - overlays.Cut() if(drying) overlays += "drying_rack_drying" - if(contents.len) + if(length(contents)) overlays += "drying_rack_filled" /obj/machinery/smartfridge/drying_rack/process() ..() - if(drying) - if(rack_dry())//no need to update unless something got dried - update_icon() + if(drying && rack_dry())//no need to update unless something got dried + update_icon() /obj/machinery/smartfridge/drying_rack/accept_check(obj/item/O) + . = ..() + // If it's a food, reject non driable ones if(istype(O, /obj/item/reagent_containers/food/snacks)) var/obj/item/reagent_containers/food/snacks/S = O - if(S.dried_type) - return TRUE - if(istype(O, /obj/item/stack/sheet/wetleather)) - return TRUE - return FALSE + if(!S.dried_type) + return FALSE +/** + * Toggles the drying process. + * + * Arguments: + * * forceoff - Whether to force turn off the drying rack. + */ /obj/machinery/smartfridge/drying_rack/proc/toggle_drying(forceoff) if(drying || forceoff) drying = FALSE @@ -557,6 +692,9 @@ use_power = ACTIVE_POWER_USE update_icon() +/** + * Called in [/obj/machinery/smartfridge/drying_rack/process] to dry the contents. + */ /obj/machinery/smartfridge/drying_rack/proc/rack_dry() for(var/obj/item/reagent_containers/food/snacks/S in contents) if(S.dried_type == S.type)//if the dried type is the same as the object's type, don't bother creating a whole new item... @@ -569,41 +707,13 @@ new dried(loc) item_quants[S.name]-- qdel(S) - SSnanoui.update_uis(src) + SStgui.update_uis(src) return TRUE for(var/obj/item/stack/sheet/wetleather/WL in contents) var/obj/item/stack/sheet/leather/L = new(loc) L.amount = WL.amount item_quants[WL.name]-- qdel(WL) - SSnanoui.update_uis(src) + SStgui.update_uis(src) return TRUE return FALSE - -/obj/machinery/smartfridge/drying_rack/emp_act(severity) - ..() - atmos_spawn_air(LINDA_SPAWN_HEAT) - -/************************ -* Secure SmartFridges -*************************/ -/obj/machinery/smartfridge/secure/emag_act(mob/user) - emagged = 1 - locked = -1 - to_chat(user, "You short out the product lock on [src].") - -/obj/machinery/smartfridge/secure/emp_act(severity) - if(prob(40/severity) && (!emagged) && (locked != -1)) - playsound(loc, 'sound/effects/sparks4.ogg', 60, 1) - emagged = 1 - locked = -1 - -/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 && scan_id && href_list["vend"]) - to_chat(usr, "Access denied.") - SSnanoui.update_uis(src) - return 0 - return ..() diff --git a/code/modules/food_and_drinks/recipes/recipes_grill.dm b/code/modules/food_and_drinks/recipes/recipes_grill.dm index 047b3968176..73b181b8bbb 100644 --- a/code/modules/food_and_drinks/recipes/recipes_grill.dm +++ b/code/modules/food_and_drinks/recipes/recipes_grill.dm @@ -249,3 +249,23 @@ /obj/item/stack/rods, ) result = /obj/item/reagent_containers/food/snacks/fish_skewer + +/datum/recipe/grill/pancake + items = list( + /obj/item/reagent_containers/food/snacks/cookiedough + ) + result = /obj/item/reagent_containers/food/snacks/pancake + +/datum/recipe/grill/berry_pancake + items = list( + /obj/item/reagent_containers/food/snacks/cookiedough, + /obj/item/reagent_containers/food/snacks/grown/berries + ) + result = /obj/item/reagent_containers/food/snacks/pancake/berry_pancake + +/datum/recipe/grill/choc_chip_pancake + items = list( + /obj/item/reagent_containers/food/snacks/cookiedough, + /obj/item/reagent_containers/food/snacks/choc_pile + ) + result = /obj/item/reagent_containers/food/snacks/pancake/choc_chip_pancake diff --git a/code/modules/food_and_drinks/recipes/recipes_microwave.dm b/code/modules/food_and_drinks/recipes/recipes_microwave.dm index f8d720ec56c..df22e612830 100644 --- a/code/modules/food_and_drinks/recipes/recipes_microwave.dm +++ b/code/modules/food_and_drinks/recipes/recipes_microwave.dm @@ -449,7 +449,7 @@ ) result = /obj/item/reagent_containers/food/snacks/twobread -datum/recipe/microwave/slimesandwich +/datum/recipe/microwave/slimesandwich reagents = list("slimejelly" = 5) items = list( /obj/item/reagent_containers/food/snacks/breadslice, diff --git a/code/modules/hydroponics/beekeeping/beebox.dm b/code/modules/hydroponics/beekeeping/beebox.dm index 612146e579a..5a73313601a 100644 --- a/code/modules/hydroponics/beekeeping/beebox.dm +++ b/code/modules/hydroponics/beekeeping/beebox.dm @@ -198,10 +198,17 @@ return return ..() +/obj/structure/beebox/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0)) + return + TOOL_ATTEMPT_DISMANTLE_MESSAGE + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + TOOL_DISMANTLE_SUCCESS_MESSAGE + deconstruct(disassembled = TRUE) + /obj/structure/beebox/wrench_act(mob/user, obj/item/I) . = TRUE - if(!I.tool_use_check(user, 0)) - return default_unfasten_wrench(user, I, time = 20) /obj/structure/beebox/attack_hand(mob/user) @@ -261,15 +268,18 @@ visible_message("[user] removes the queen from the apiary.") queen_bee = null -/obj/structure/beebox/deconstruct(disassembled = TRUE) - new /obj/item/stack/sheet/wood(loc, 20) +/obj/structure/beebox/deconstruct(disassembled = FALSE) + var/mat_drop = 20 + if(disassembled) + mat_drop = 40 + new /obj/item/stack/sheet/wood(loc, mat_drop) for(var/mob/living/simple_animal/hostile/poison/bees/B in bees) if(B.loc == src) B.forceMove(drop_location()) for(var/obj/item/honey_frame/HF in honey_frames) HF.forceMove(drop_location()) honey_frames -= HF - qdel(src) + ..() /obj/structure/beebox/unwrenched anchored = FALSE diff --git a/code/modules/hydroponics/beekeeping/honeycomb.dm b/code/modules/hydroponics/beekeeping/honeycomb.dm index 23bc55dfea2..1d086bae0f0 100644 --- a/code/modules/hydroponics/beekeeping/honeycomb.dm +++ b/code/modules/hydroponics/beekeeping/honeycomb.dm @@ -17,6 +17,8 @@ pixel_y = rand(8,-8) update_icon() +/obj/item/reagent_containers/honeycomb/set_APTFT() + set hidden = TRUE /obj/item/reagent_containers/honeycomb/update_icon() overlays.Cut() diff --git a/code/modules/hydroponics/fermenting_barrel.dm b/code/modules/hydroponics/fermenting_barrel.dm index f194c48e019..5031ac71373 100644 --- a/code/modules/hydroponics/fermenting_barrel.dm +++ b/code/modules/hydroponics/fermenting_barrel.dm @@ -4,7 +4,7 @@ icon = 'icons/obj/objects.dmi' icon_state = "barrel" density = TRUE - anchored = FALSE + anchored = TRUE container_type = DRAINABLE | AMOUNT_VISIBLE pressure_resistance = 2 * ONE_ATMOSPHERE max_integrity = 300 @@ -65,6 +65,26 @@ to_chat(user, "You close [src], letting you draw from its tap.") update_icon() +/obj/structure/fermenting_barrel/crowbar_act(mob/living/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0)) + return + TOOL_ATTEMPT_DISMANTLE_MESSAGE + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + TOOL_DISMANTLE_SUCCESS_MESSAGE + deconstruct(disassembled = TRUE) + +/obj/structure/fermenting_barrel/wrench_act(mob/living/user, obj/item/I) + . = TRUE + default_unfasten_wrench(user, I, time = 20) + +/obj/structure/fermenting_barrel/deconstruct(disassembled = FALSE) + var/mat_drop = 15 + if(disassembled) + mat_drop = 30 + new /obj/item/stack/sheet/wood(drop_location(), mat_drop) + ..() + /obj/structure/fermenting_barrel/update_icon() if(open) icon_state = "barrel_open" diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index a3b522187e2..bad8813ea01 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -117,6 +117,7 @@ /obj/item/reagent_containers/food/snacks/grown/throw_impact(atom/hit_atom) if(!..()) //was it caught by a mob? if(seed) + log_action(thrownby, hit_atom, "Thrown [src] at") for(var/datum/plant_gene/trait/T in seed.genes) T.on_throw_impact(src, hit_atom) if(seed.get_gene(/datum/plant_gene/trait/squash)) @@ -147,11 +148,11 @@ qdel(src) -/obj/item/reagent_containers/food/snacks/grown/On_Consume() - if(iscarbon(usr)) +/obj/item/reagent_containers/food/snacks/grown/On_Consume(mob/M, mob/user) + if(iscarbon(M)) if(seed) for(var/datum/plant_gene/trait/T in seed.genes) - T.on_consume(src, usr) + T.on_consume(src, M) ..() /obj/item/reagent_containers/food/snacks/grown/after_slip(mob/living/carbon/human/H) @@ -168,6 +169,11 @@ return return ..() +/obj/item/reagent_containers/food/snacks/grown/decompile_act(obj/item/matter_decompiler/C, mob/user) + C.stored_comms["wood"] += 4 + qdel(src) + return TRUE + // For item-containing growns such as eggy or gatfruit /obj/item/reagent_containers/food/snacks/grown/shell/attack_self(mob/user) user.unEquip(src) @@ -185,3 +191,16 @@ else return ..() +/obj/item/reagent_containers/food/snacks/grown/proc/log_action(mob/user, atom/target, what_done) + var/reagent_str = reagents.log_list() + var/genes_str = "No genes" + if(seed && length(seed.genes)) + var/list/plant_gene_names = list() + for(var/thing in seed.genes) + var/datum/plant_gene/G = thing + if(G.dangerous) + plant_gene_names += G.name + genes_str = english_list(plant_gene_names) + + add_attack_logs(user, target, "[what_done] ([reagent_str] | [genes_str])") + diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm index 6e937f45c2a..911b8eae866 100644 --- a/code/modules/hydroponics/grown/citrus.dm +++ b/code/modules/hydroponics/grown/citrus.dm @@ -116,9 +116,8 @@ /obj/item/reagent_containers/food/snacks/grown/firelemon/attack_self(mob/living/user) var/area/A = get_area(user) user.visible_message("[user] primes the [src]!", "You prime the [src]!") - var/message = "[ADMIN_LOOKUPFLW(user)] primed a combustible lemon for detonation at [A] [ADMIN_COORDJMP(user)]" investigate_log("[key_name(user)] primed a combustible lemon for detonation at [A] [COORD(user)].", INVESTIGATE_BOMB) - message_admins(message) + add_attack_logs(user, src, "primed a combustible lemon for detonation", ATKLOG_FEW) log_game("[key_name(user)] primed a combustible lemon for detonation at [A] [COORD(user)].") if(iscarbon(user)) var/mob/living/carbon/C = user diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm index d6f8cae7519..0f2b54ab971 100644 --- a/code/modules/hydroponics/grown/replicapod.dm +++ b/code/modules/hydroponics/grown/replicapod.dm @@ -29,7 +29,8 @@ if(istype(W,/obj/item/reagent_containers/syringe)) if(!contains_sample) for(var/datum/reagent/blood/bloodSample in W.reagents.reagent_list) - if(bloodSample.data["mind"] && bloodSample.data["cloneable"] == 1) + var/datum/dna/dna = bloodSample.data["dna"] + if(bloodSample.data["mind"] && bloodSample.data["cloneable"] && !(NO_SCAN in dna.species.species_traits)) var/datum/mind/tempmind = bloodSample.data["mind"] if(tempmind.is_revivable()) mind = bloodSample.data["mind"] diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm index 7d59bd3360b..d5c7744eb4a 100644 --- a/code/modules/hydroponics/hydroitemdefines.dm +++ b/code/modules/hydroponics/hydroitemdefines.dm @@ -27,10 +27,7 @@ w_class = WEIGHT_CLASS_SMALL throw_speed = 3 throw_range = 10 - -/obj/item/reagent_containers/spray/weedspray/New() - ..() - reagents.add_reagent("atrazine", 100) + list_reagents = list("atrazine" = 100) /obj/item/reagent_containers/spray/weedspray/suicide_act(mob/user) user.visible_message("[user] is huffing the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.") @@ -49,10 +46,7 @@ w_class = WEIGHT_CLASS_SMALL throw_speed = 3 throw_range = 10 - -/obj/item/reagent_containers/spray/pestspray/New() - ..() - reagents.add_reagent("pestkiller", 100) + list_reagents = list("pestkiller" = 100) /obj/item/reagent_containers/spray/pestspray/suicide_act(mob/user) user.visible_message("[user] is huffing the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.") @@ -222,79 +216,112 @@ /obj/item/reagent_containers/glass/bottle/nutrient - name = "bottle of nutrient" + name = "jug of nutrient" + desc = "A decent sized plastic jug." icon = 'icons/obj/chemical.dmi' - icon_state = "bottle16" - volume = 50 + icon_state = "plastic_jug" + item_state = "plastic_jug" w_class = WEIGHT_CLASS_TINY amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(1,2,5,10,15,25,50) + possible_transfer_amounts = list(1,2,5,10,20,40,80) + container_type = OPENCONTAINER + volume = 80 + hitsound = 'sound/weapons/jug_empty_impact.ogg' + throwhitsound = 'sound/weapons/jug_empty_impact.ogg' + force = 0.2 + throwforce = 0.2 /obj/item/reagent_containers/glass/bottle/nutrient/New() + ..() + add_lid() + pixel_x = rand(-5, 5) + pixel_y = rand(-5, 5) + +/obj/item/reagent_containers/glass/bottle/nutrient/on_reagent_change() + . = ..() + update_icon() + if(reagents.total_volume) + hitsound = 'sound/weapons/jug_filled_impact.ogg' + throwhitsound = 'sound/weapons/jug_filled_impact.ogg' + else + hitsound = 'sound/weapons/jug_empty_impact.ogg' + throwhitsound = 'sound/weapons/jug_empty_impact.ogg' + +/obj/item/reagent_containers/glass/bottle/nutrient/update_icon() + cut_overlays() + + if(reagents.total_volume) + var/image/filling = image('icons/obj/reagentfillings.dmi', src, "plastic_jug10") + + var/percent = round((reagents.total_volume / volume) * 100) + switch(percent) + if(0 to 10) + filling.icon_state = "plastic_jug-10" + if(11 to 29) + filling.icon_state = "plastic_jug25" + if(30 to 45) + filling.icon_state = "plastic_jug40" + if(46 to 61) + filling.icon_state = "plastic_jug55" + if(62 to 77) + filling.icon_state = "plastic_jug70" + if(78 to 92) + filling.icon_state = "plastic_jug85" + if(93 to INFINITY) + filling.icon_state = "plastic_jug100" + + filling.icon += mix_color_from_reagents(reagents.reagent_list) + add_overlay(filling) + + if(!is_open_container()) + add_overlay("lid_jug") + + +/obj/item/reagent_containers/glass/bottle/nutrient/ez + name = "jug of E-Z-Nutrient" + desc = "Contains a fertilizer that causes mild mutations with each harvest." + icon = 'icons/obj/chemical.dmi' + icon_state = "plastic_jug_ez" + list_reagents = list("eznutriment" = 80) + +/obj/item/reagent_containers/glass/bottle/nutrient/l4z + name = "jug of Left 4 Zed" + desc = "Contains a fertilizer that limits plant yields to no more than one and causes significant mutations in plants." + icon = 'icons/obj/chemical.dmi' + icon_state = "plastic_jug_l4z" + list_reagents = list("left4zednutriment" = 80) + +/obj/item/reagent_containers/glass/bottle/nutrient/rh + name = "jug of Robust Harvest" + desc = "Contains a fertilizer that increases the yield of a plant by 30% while causing no mutations." + icon = 'icons/obj/chemical.dmi' + icon_state = "plastic_jug_rh" + list_reagents = list("robustharvestnutriment" = 80) + +/obj/item/reagent_containers/glass/bottle/nutrient/empty + icon = 'icons/obj/chemical.dmi' + icon_state = "plastic_jug" + +/obj/item/reagent_containers/glass/bottle/nutrient/killer + icon = 'icons/obj/chemical.dmi' + icon_state = "plastic_jug_k" + w_class = WEIGHT_CLASS_TINY + +/obj/item/reagent_containers/glass/bottle/nutrient/killer/New() ..() pixel_x = rand(-5, 5) pixel_y = rand(-5, 5) -/obj/item/reagent_containers/glass/bottle/nutrient/ez - name = "bottle of E-Z-Nutrient" - desc = "Contains a fertilizer that causes mild mutations with each harvest." - icon = 'icons/obj/chemical.dmi' - icon_state = "bottle16" - -/obj/item/reagent_containers/glass/bottle/nutrient/ez/New() - ..() - reagents.add_reagent("eznutriment", 50) - -/obj/item/reagent_containers/glass/bottle/nutrient/l4z - name = "bottle of Left 4 Zed" - desc = "Contains a fertilizer that limits plant yields to no more than one and causes significant mutations in plants." - icon = 'icons/obj/chemical.dmi' - icon_state = "bottle18" - -/obj/item/reagent_containers/glass/bottle/nutrient/l4z/New() - ..() - reagents.add_reagent("left4zednutriment", 50) - -/obj/item/reagent_containers/glass/bottle/nutrient/rh - name = "bottle of Robust Harvest" - desc = "Contains a fertilizer that increases the yield of a plant by 30% while causing no mutations." - icon = 'icons/obj/chemical.dmi' - icon_state = "bottle15" - -/obj/item/reagent_containers/glass/bottle/nutrient/rh/New() - ..() - reagents.add_reagent("robustharvestnutriment", 50) - -/obj/item/reagent_containers/glass/bottle/nutrient/empty - name = "bottle" - icon = 'icons/obj/chemical.dmi' - icon_state = "bottle16" - -/obj/item/reagent_containers/glass/bottle/killer - name = "bottle" - icon = 'icons/obj/chemical.dmi' - icon_state = "bottle16" - volume = 50 - w_class = WEIGHT_CLASS_TINY - amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(1,2,5,10,15,25,50) - -/obj/item/reagent_containers/glass/bottle/killer/weedkiller - name = "bottle of weed killer" +/obj/item/reagent_containers/glass/bottle/nutrient/killer/weedkiller + name = "jug of weed killer" desc = "Contains a herbicide." icon = 'icons/obj/chemical.dmi' - icon_state = "bottle19" + icon_state = "plastic_jug_wk" + list_reagents = list("atrazine" = 80) -/obj/item/reagent_containers/glass/bottle/killer/weedkiller/New() - ..() - reagents.add_reagent("atrazine", 50) - -/obj/item/reagent_containers/glass/bottle/killer/pestkiller - name = "bottle of pest spray" +/obj/item/reagent_containers/glass/bottle/nutrient/killer/pestkiller + name = "jug of pest spray" desc = "Contains a pesticide." icon = 'icons/obj/chemical.dmi' - icon_state = "bottle20" - -/obj/item/reagent_containers/glass/bottle/killer/pestkiller/New() - ..() - reagents.add_reagent("pestkiller", 50) + icon_state = "plastic_jug_pk" + list_reagents = list("pestkiller" = 80) diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 1be8f11527d..134e337fa4e 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -323,7 +323,7 @@ else I = image(icon = myseed.growing_icon, icon_state = myseed.icon_harvest) else - var/t_growthstate = min(round((age / myseed.maturation) * myseed.growthstages), myseed.growthstages) + var/t_growthstate = clamp(round((age / myseed.maturation) * myseed.growthstages), 1, myseed.growthstages) I = image(icon = myseed.growing_icon, icon_state = "[myseed.icon_grow][t_growthstate]") I.layer = OBJ_LAYER + 0.01 overlays += I @@ -744,6 +744,10 @@ to_chat(user, "[reagent_source] is empty.") return 1 + if(reagent_source.has_lid && !reagent_source.is_drainable()) //if theres a LID then cannot transfer reagents. + to_chat(user, "You need to open [O] first!") + return TRUE + var/list/trays = list(src)//makes the list just this in cases of syringes and compost etc var/target = myseed ? myseed.plantname : src var/visi_msg = "" diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm index a4c6fe7280d..c2f4914351c 100644 --- a/code/modules/hydroponics/plant_genes.dm +++ b/code/modules/hydroponics/plant_genes.dm @@ -1,5 +1,7 @@ /datum/plant_gene var/name + /// Used to determine if the trait should be logged when the holder is used + var/dangerous = FALSE /datum/plant_gene/proc/get_name() // Used for manipulator display and gene disk name. return name @@ -194,6 +196,7 @@ name = "Liquid Contents" examine_line = "It has a lot of liquid contents inside." origin_tech = list("biotech" = 5) + dangerous = TRUE /datum/plant_gene/trait/slip // Makes plant slippery, unless it has a grown-type trash. Then the trash gets slippery. @@ -201,6 +204,7 @@ name = "Slippery Skin" rate = 0.1 examine_line = "It has a very slippery skin." + dangerous = TRUE /datum/plant_gene/trait/slip/on_new(obj/item/reagent_containers/food/snacks/grown/G, newloc) . = ..() @@ -224,6 +228,7 @@ name = "Electrical Activity" rate = 0.2 origin_tech = list("powerstorage" = 5) + dangerous = TRUE /datum/plant_gene/trait/cell_charge/on_slip(obj/item/reagent_containers/food/snacks/grown/G, mob/living/carbon/C) var/power = G.seed.potency*rate @@ -298,6 +303,7 @@ name = "Bluespace Activity" rate = 0.1 origin_tech = list("bluespace" = 5) + dangerous = TRUE /datum/plant_gene/trait/teleport/on_squash(obj/item/reagent_containers/food/snacks/grown/G, atom/target) if(isliving(target)) @@ -385,6 +391,7 @@ /datum/plant_gene/trait/stinging name = "Hypodermic Prickles" + dangerous = TRUE /datum/plant_gene/trait/stinging/on_throw_impact(obj/item/reagent_containers/food/snacks/grown/G, atom/target) if(isliving(target) && G.reagents && G.reagents.total_volume) @@ -398,6 +405,7 @@ /datum/plant_gene/trait/smoke name = "gaseous decomposition" + dangerous = TRUE /datum/plant_gene/trait/smoke/on_squash(obj/item/reagent_containers/food/snacks/grown/G, atom/target) var/datum/effect_system/smoke_spread/chem/S = new diff --git a/code/modules/instruments/_instrument_data.dm b/code/modules/instruments/_instrument_data.dm new file mode 100644 index 00000000000..2fde3de59fb --- /dev/null +++ b/code/modules/instruments/_instrument_data.dm @@ -0,0 +1,113 @@ +/** + * Get all non admin_only instruments as a list of text ids. + */ +/proc/get_allowed_instrument_ids() + . = list() + for(var/id in SSinstruments.instrument_data) + var/datum/instrument/I = SSinstruments.instrument_data[id] + if(!I.admin_only) + . += I.id + +/** + * # Instrument Datums + * + * Instrument datums hold the data for any given instrument, as well as data on how to play it and what bounds there are to playing it. + * + * The datums themselves are kept in SSinstruments in a list by their unique ID. The reason it uses ID instead of typepath is to support the runtime creation of instruments. + * Since songs cache them while playing, there isn't realistic issues regarding performance from accessing. + */ +/datum/instrument + /// Name of the instrument + var/name = "Generic instrument" + /// Uniquely identifies this instrument so runtime changes are possible as opposed to paths. If this is unset, things will use path instead. + var/id + /// Category + var/category = "Unsorted" + /// Used for categorization subtypes + var/abstract_type = /datum/instrument + /// Write here however many samples, follow this syntax: "%note num%"='%sample file%' eg. "27"='synthesizer/e2.ogg'. Key must never be lower than 0 and higher than 127 + var/list/real_samples + /// assoc list key = /datum/instrument_key. do not fill this yourself! + var/list/samples + /// See __DEFINES/flags/instruments.dm + var/instrument_flags = NONE + /// For legacy instruments, the path to our notes + var/legacy_instrument_path + /// For legacy instruments, our file extension + var/legacy_instrument_ext + /// What songs are using us + var/list/datum/song/songs_using = list() + /// Don't touch this + var/static/HIGHEST_KEY = 127 + /// Don't touch this x2 + var/static/LOWEST_KEY = 0 + /// Oh no - For truly troll instruments. + var/admin_only = FALSE + /// Volume multiplier. Synthesized instruments are quite loud and I don't like to cut off potential detail via editing. (someone correct me if this isn't a thing) + var/volume_multiplier = 0.33 + +/datum/instrument/New() + if(isnull(id)) + id = "[type]" + +/datum/instrument/Destroy() + SSinstruments.instrument_data -= id + for(var/i in songs_using) + var/datum/song/S = i + S.set_instrument(null) + real_samples = null + samples = null + songs_using = null + return ..() + +/** + * Initializes the instrument, calculating its samples if necessary. + */ +/datum/instrument/proc/Initialize() + if(instrument_flags & (INSTRUMENT_LEGACY | INSTRUMENT_DO_NOT_AUTOSAMPLE)) + return + calculate_samples() + +/** + * Checks if this instrument is ready to play. + */ +/datum/instrument/proc/is_ready() + if(instrument_flags & INSTRUMENT_LEGACY) + return legacy_instrument_path && legacy_instrument_ext + else if(instrument_flags & INSTRUMENT_DO_NOT_AUTOSAMPLE) + return length(samples) + return length(samples) >= 128 + +/** + * For synthesized instruments, this is how the instrument generates the "keys" that a [/datum/song] uses to play notes. + * Calculating them on the fly would be unperformant, so we do it during init and keep it all cached in a list. + */ +/datum/instrument/proc/calculate_samples() + if(!length(real_samples)) + CRASH("No real samples defined for [id] [type] on calculate_samples() call.") + var/list/real_keys = list() + samples = list() + for(var/key in real_samples) + real_keys += text2num(key) + sortTim(real_keys, /proc/cmp_numeric_asc, associative = FALSE) + + for(var/i in 1 to (length(real_keys) - 1)) + var/from_key = real_keys[i] + var/to_key = real_keys[i + 1] + var/sample1 = real_samples[num2text(from_key)] + var/sample2 = real_samples[num2text(to_key)] + var/pivot = FLOOR((from_key + to_key) / 2, 1) //original code was a round but I replaced it because that's effectively a floor, thanks Baystation! who knows what was intended. + for(var/key in from_key to pivot) + samples[num2text(key)] = new /datum/instrument_key(sample1, key, key - from_key) + for(var/key in (pivot + 1) to to_key) + samples[num2text(key)] = new /datum/instrument_key(sample2, key, key - to_key) + + // Fill in 0 to first key and last key to 127 + var/first_key = real_keys[1] + var/last_key = real_keys[length(real_keys)] + var/first_sample = real_samples[num2text(first_key)] + var/last_sample = real_samples[num2text(last_key)] + for(var/key in LOWEST_KEY to (first_key - 1)) + samples[num2text(key)] = new /datum/instrument_key(first_sample, key, key - first_key) + for(var/key in last_key to HIGHEST_KEY) + samples[num2text(key)] = new /datum/instrument_key(last_sample, key, key - last_key) diff --git a/code/modules/instruments/_instrument_key.dm b/code/modules/instruments/_instrument_key.dm new file mode 100644 index 00000000000..5c7cc0ce372 --- /dev/null +++ b/code/modules/instruments/_instrument_key.dm @@ -0,0 +1,33 @@ +#define KEY_TWELTH (1/12) + +/** + * Instrument key datums contain everything needed to know how to play a specific + * note of an instrument.* + */ +/datum/instrument_key + /// The numerical key of what this is, from 1 to 127 on a standard piano keyboard. + var/key + /// The actual sample file that will be loaded when playing. + var/sample + /// The frequency to play the sample to get our desired note. + var/frequency + /// Deviation up/down from the pivot point that uses its sample. Used to calculate frequency. + var/deviation + +/datum/instrument_key/New(sample, key, deviation, frequency) + src.sample = sample + src.key = key + src.deviation = deviation + src.frequency = frequency + if(!frequency && deviation) + calculate() + +/** + * Calculates and stores our deviation. + */ +/datum/instrument_key/proc/calculate() + if(!deviation) + CRASH("Invalid calculate call: No deviation or sample in instrument_key") + frequency = 2 ** (KEY_TWELTH * deviation) + +#undef KEY_TWELTH diff --git a/code/modules/instruments/brass.dm b/code/modules/instruments/brass.dm new file mode 100644 index 00000000000..7f8f103831f --- /dev/null +++ b/code/modules/instruments/brass.dm @@ -0,0 +1,26 @@ +/datum/instrument/brass + name = "Generic brass instrument" + category = "Brass" + abstract_type = /datum/instrument/brass + +/datum/instrument/brass/crisis_section + name = "Crisis Brass Section" + id = "crbrass" + real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg', + "48"='sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg', + "60"='sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg', + "72"='sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg') + +/datum/instrument/brass/crisis_trombone + name = "Crisis Trombone" + id = "crtrombone" + real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_trombone/c2.ogg', + "48"='sound/instruments/synthesis_samples/brass/crisis_trombone/c3.ogg', + "60"='sound/instruments/synthesis_samples/brass/crisis_trombone/c4.ogg', + "72"='sound/instruments/synthesis_samples/brass/crisis_trombone/c5.ogg') + +/datum/instrument/brass/crisis_trumpet + name = "Crisis Trumpet" + id = "crtrumpet" + real_samples = list("60"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c4.ogg', + "72"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c5.ogg') diff --git a/code/modules/instruments/chromatic_percussion.dm b/code/modules/instruments/chromatic_percussion.dm new file mode 100644 index 00000000000..cafa9e31edb --- /dev/null +++ b/code/modules/instruments/chromatic_percussion.dm @@ -0,0 +1,31 @@ +/datum/instrument/chromatic + name = "Generic chromatic percussion instrument" + category = "Chromatic percussion" + abstract_type = /datum/instrument/chromatic + +/datum/instrument/chromatic/vibraphone1 + name = "Crisis Vibraphone" + id = "crvibr" + real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg', + "48"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg', + "60"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg', + "72"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg') + +/datum/instrument/chromatic/musicbox1 + name = "SGM Music Box" + id = "sgmmbox" + real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg', + "48"='sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg', + "60"='sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg', + "72"='sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg') + +/datum/instrument/chromatic/fluid_celeste + name = "FluidR3 Celeste" + id = "r3celeste" + real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c2.ogg', + "48"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c3.ogg', + "60"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c4.ogg', + "72"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c5.ogg', + "84"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c6.ogg', + "96"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c7.ogg', + "108"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c8.ogg') diff --git a/code/modules/instruments/fun.dm b/code/modules/instruments/fun.dm new file mode 100644 index 00000000000..5a9b1292c42 --- /dev/null +++ b/code/modules/instruments/fun.dm @@ -0,0 +1,19 @@ +/datum/instrument/fun + name = "Generic Fun Instrument" + category = "Fun" + abstract_type = /datum/instrument/fun + +/datum/instrument/fun/honk + name = "!!HONK!!" + id = "honk" + real_samples = list("74"='sound/items/bikehorn.ogg') // Cluwne Heaven + +/datum/instrument/fun/signal + name = "Ping" + id = "ping" + real_samples = list("79"='sound/machines/ping.ogg') + +/datum/instrument/fun/chime + name = "Chime" + id = "chime" + real_samples = list("79"='sound/machines/chime.ogg') diff --git a/code/modules/instruments/guitar.dm b/code/modules/instruments/guitar.dm new file mode 100644 index 00000000000..be7cfbe467b --- /dev/null +++ b/code/modules/instruments/guitar.dm @@ -0,0 +1,36 @@ +/datum/instrument/guitar + name = "Generic guitar-like instrument" + category = "Guitar" + abstract_type = /datum/instrument/guitar + +/datum/instrument/guitar/steel_crisis + name = "Crisis Steel String Guitar" + id = "csteelgt" + real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg', + "48"='sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg', + "60"='sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg', + "72"='sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg') + +/datum/instrument/guitar/nylon_crisis + name = "Crisis Nylon String Guitar" + id = "cnylongt" + real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg', + "48"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg', + "60"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg', + "72"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg') + +/datum/instrument/guitar/clean_crisis + name = "Crisis Clean Guitar" + id = "ccleangt" + real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_clean/c2.ogg', + "48"='sound/instruments/synthesis_samples/guitar/crisis_clean/c3.ogg', + "60"='sound/instruments/synthesis_samples/guitar/crisis_clean/c4.ogg', + "72"='sound/instruments/synthesis_samples/guitar/crisis_clean/c5.ogg') + +/datum/instrument/guitar/muted_crisis + name = "Crisis Muted Guitar" + id = "cmutedgt" + real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_muted/c2.ogg', + "48"='sound/instruments/synthesis_samples/guitar/crisis_muted/c3.ogg', + "60"='sound/instruments/synthesis_samples/guitar/crisis_muted/c4.ogg', + "72"='sound/instruments/synthesis_samples/guitar/crisis_muted/c5.ogg') diff --git a/code/modules/instruments/hardcoded.dm b/code/modules/instruments/hardcoded.dm new file mode 100644 index 00000000000..5db7e8b3bb4 --- /dev/null +++ b/code/modules/instruments/hardcoded.dm @@ -0,0 +1,86 @@ +//THESE ARE HARDCODED INSTRUMENT SAMPLES. +//SONGS WILL BE AUTOMATICALLY SWITCHED TO LEGACY MODE IF THEY USE THIS KIND OF INSTRUMENT! +//I'd prefer these stayed. They sound different from the mechanical synthesis of synthed instruments, and I quite like them that way. It's not legacy, it's hardcoded, old style. - kevinz000 +/datum/instrument/hardcoded + abstract_type = /datum/instrument/hardcoded + category = "Non-Synthesized" + instrument_flags = INSTRUMENT_LEGACY + volume_multiplier = 1 //not as loud as synth'd + +/datum/instrument/hardcoded/accordion + name = "Accordion" + id = "accordion" + legacy_instrument_ext = "mid" + legacy_instrument_path = "accordion" + +/datum/instrument/hardcoded/bikehorn + name = "Bike Horn" + id = "bikehorn" + legacy_instrument_ext = "ogg" + legacy_instrument_path = "bikehorn" + +/datum/instrument/hardcoded/eguitar + name = "Electric Guitar" + id = "eguitar" + legacy_instrument_ext = "ogg" + legacy_instrument_path = "eguitar" + +/datum/instrument/hardcoded/glockenspiel + name = "Glockenspiel" + id = "glockenspiel" + legacy_instrument_ext = "mid" + legacy_instrument_path = "glockenspiel" + +/datum/instrument/hardcoded/guitar + name = "Guitar" + id = "guitar" + legacy_instrument_ext = "ogg" + legacy_instrument_path = "guitar" + +/datum/instrument/hardcoded/harmonica + name = "Harmonica" + id = "harmonica" + legacy_instrument_ext = "mid" + legacy_instrument_path = "harmonica" + +/datum/instrument/hardcoded/piano + name = "Piano" + id = "piano" + legacy_instrument_ext = "ogg" + legacy_instrument_path = "piano" + +/datum/instrument/hardcoded/recorder + name = "Recorder" + id = "recorder" + legacy_instrument_ext = "mid" + legacy_instrument_path = "recorder" + +/datum/instrument/hardcoded/saxophone + name = "Saxophone" + id = "saxophone" + legacy_instrument_ext = "mid" + legacy_instrument_path = "saxophone" + +/datum/instrument/hardcoded/trombone + name = "Trombone" + id = "trombone" + legacy_instrument_ext = "mid" + legacy_instrument_path = "trombone" + +/datum/instrument/hardcoded/violin + name = "Violin" + id = "violin" + legacy_instrument_ext = "mid" + legacy_instrument_path = "violin" + +/datum/instrument/hardcoded/xylophone + name = "Xylophone" + id = "xylophone" + legacy_instrument_ext = "mid" + legacy_instrument_path = "xylophone" + +/datum/instrument/hardcoded/banjo + name = "Banjo" + id = "banjo" + legacy_instrument_ext = "ogg" + legacy_instrument_path = "banjo" diff --git a/code/modules/instruments/objs/items/_instrument.dm b/code/modules/instruments/objs/items/_instrument.dm new file mode 100644 index 00000000000..936a9b4c85d --- /dev/null +++ b/code/modules/instruments/objs/items/_instrument.dm @@ -0,0 +1,53 @@ +//copy pasta of the space piano, don't hurt me -Pete +/obj/item/instrument + name = "generic instrument" + force = 10 + max_integrity = 100 + resistance_flags = FLAMMABLE + icon = 'icons/obj/musician.dmi' + lefthand_file = 'icons/mob/inhands/equipment/instruments_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/instruments_righthand.dmi' + /// Our song datum. + var/datum/song/handheld/song + /// Our allowed list of instrument ids. This is nulled on initialize. + var/list/allowed_instrument_ids + /// How far away our song datum can be heard. + var/instrument_range = 15 + +/obj/item/instrument/Initialize(mapload) + . = ..() + song = new(src, allowed_instrument_ids, instrument_range) + allowed_instrument_ids = null //We don't need this clogging memory after it's used. + +/obj/item/instrument/Destroy() + QDEL_NULL(song) + return ..() + +/obj/item/instrument/suicide_act(mob/user) + user.visible_message("[user] begins to play 'Gloomy Sunday'! It looks like [user.p_theyre()] trying to commit suicide!") + return BRUTELOSS + +/obj/item/instrument/attack_self(mob/user) + tgui_interact(user) + +/obj/item/instrument/tgui_data(mob/user) + return song.tgui_data(user) + +/obj/item/instrument/tgui_interact(mob/user) + if(!isliving(user) || user.incapacitated()) + return + song.tgui_interact(user) + +/obj/item/instrument/tgui_act(action, params) + if(..()) + return + return song.tgui_act(action, params) + +/** + * Whether the instrument should stop playing + * + * Arguments: + * * user - The user + */ +/obj/item/instrument/proc/should_stop_playing(mob/user) + return !(src in user) || !isliving(user) || user.incapacitated() diff --git a/code/modules/instruments/objs/items/headphones.dm b/code/modules/instruments/objs/items/headphones.dm new file mode 100644 index 00000000000..ef1a76bdc3b --- /dev/null +++ b/code/modules/instruments/objs/items/headphones.dm @@ -0,0 +1,80 @@ +/obj/item/clothing/ears/headphones + name = "headphones" + desc = "Unce unce unce unce." + icon_state = "headphones0" + item_state = "headphones0" + actions_types = list(/datum/action/item_action/change_headphones_song) + var/datum/song/headphones/song + +/obj/item/clothing/ears/headphones/Initialize(mapload) + . = ..() + song = new(src, "piano") // Piano is the default instrument but all instruments are allowed + song.instrument_range = 0 + song.allowed_instrument_ids = SSinstruments.synthesizer_instrument_ids + // To update the icon + RegisterSignal(src, COMSIG_SONG_START, .proc/start_playing) + RegisterSignal(src, COMSIG_SONG_END, .proc/stop_playing) + +/obj/item/clothing/ears/headphones/Destroy() + QDEL_NULL(song) + return ..() + +/obj/item/clothing/ears/headphones/attack_self(mob/user) + tgui_interact(user) + +/obj/item/clothing/ears/headphones/tgui_data(mob/user) + return song.tgui_data(user) + +/obj/item/clothing/ears/headphones/tgui_interact(mob/user) + if(should_stop_playing(user) || user.incapacitated()) + return + song.tgui_interact(user) + +/obj/item/clothing/ears/headphones/tgui_act(action, params) + if(..()) + return + return song.tgui_act(action, params) + +/obj/item/clothing/ears/headphones/update_icon() + var/mob/living/carbon/human/user = loc + if(istype(user)) + user.update_action_buttons_icon() + user.update_inv_ears() + ..() + +/obj/item/clothing/ears/headphones/item_action_slot_check(slot) + if(slot == slot_l_ear || slot == slot_r_ear) + return TRUE + +/** + * Called by a component signal when our song starts playing. + */ +/obj/item/clothing/ears/headphones/proc/start_playing() + icon_state = item_state = "headphones1" + update_icon() + +/** + * Called by a component signal when our song stops playing. + */ +/obj/item/clothing/ears/headphones/proc/stop_playing() + icon_state = item_state = "headphones0" + update_icon() + +/** + * Whether the headphone's song should stop playing + * + * Arguments: + * * user - The user + */ +/obj/item/clothing/ears/headphones/proc/should_stop_playing(mob/living/carbon/human/user) + return !(src in user) || !istype(user) || !((src == user.l_ear) || (src == user.r_ear)) + +// special subtype so it uses the correct item type +/datum/song/headphones + +/datum/song/headphones/should_stop_playing(mob/user) + . = ..() + if(.) + return TRUE + var/obj/item/clothing/ears/headphones/I = parent + return I.should_stop_playing(user) diff --git a/code/game/objects/items/devices/instruments.dm b/code/modules/instruments/objs/items/instruments.dm similarity index 54% rename from code/game/objects/items/devices/instruments.dm rename to code/modules/instruments/objs/items/instruments.dm index 53ead129836..2426de9a5c9 100644 --- a/code/game/objects/items/devices/instruments.dm +++ b/code/modules/instruments/objs/items/instruments.dm @@ -1,55 +1,10 @@ -//copy pasta of the space piano, don't hurt me -Pete -/obj/item/instrument - name = "generic instrument" - icon = 'icons/obj/musician.dmi' - lefthand_file = 'icons/mob/inhands/equipment/instruments_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/instruments_righthand.dmi' - resistance_flags = FLAMMABLE - max_integrity = 100 - var/datum/song/handheld/song - var/instrumentId = "generic" - var/instrumentExt = "mid" - -/obj/item/instrument/New() - song = new(instrumentId, src, instrumentExt) - ..() - -/obj/item/instrument/Destroy() - QDEL_NULL(song) - return ..() - -/obj/item/instrument/suicide_act(mob/user) - user.visible_message("[user] begins to play 'Gloomy Sunday'! It looks like [user.p_theyre()] trying to commit suicide!") - return BRUTELOSS - -/obj/item/instrument/Initialize(mapload) - song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded - ..() - -/obj/item/instrument/attack_self(mob/user) - ui_interact(user) - -/obj/item/instrument/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - if(!isliving(user) || user.incapacitated()) - return - - song.ui_interact(user, ui_key, ui, force_open) - -/obj/item/instrument/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - return song.ui_data(user, ui_key, state) - -/obj/item/instrument/Topic(href, href_list) - song.Topic(href, href_list) - /obj/item/instrument/violin name = "space violin" desc = "A wooden musical instrument with four strings and a bow. \"The devil went down to space, he was looking for an assistant to grief.\"" icon_state = "violin" item_state = "violin" - instrumentExt = "ogg" - force = 10 hitsound = "swing_hit" - instrumentId = "violin" + allowed_instrument_ids = "violin" /obj/item/instrument/violin/golden name = "golden violin" @@ -63,87 +18,146 @@ desc = "An advanced electronic synthesizer that can be used as various instruments." icon_state = "synth" item_state = "synth" - instrumentId = "piano" - instrumentExt = "ogg" - var/static/list/insTypes = list("accordion" = "mid", "glockenspiel" = "mid", "guitar" = "ogg", "eguitar" = "ogg", "harmonica" = "mid", "piano" = "ogg", "recorder" = "mid", "saxophone" = "mid", "trombone" = "mid", "violin" = "ogg", "xylophone" = "mid") - actions_types = list(/datum/action/item_action/synthswitch) + allowed_instrument_ids = "piano" -/obj/item/instrument/piano_synth/proc/changeInstrument(name = "piano") - song.instrumentDir = name - song.instrumentExt = insTypes[name] +/obj/item/instrument/piano_synth/Initialize(mapload) + . = ..() + song.allowed_instrument_ids = SSinstruments.synthesizer_instrument_ids + +/obj/item/instrument/banjo + name = "banjo" + desc = "A 'Mura' brand banjo. It's pretty much just a drum with a neck and strings." + icon_state = "banjo" + item_state = "banjo" + attack_verb = list("scruggs-styled", "hum-diggitied", "shin-digged", "clawhammered") + hitsound = 'sound/weapons/banjoslap.ogg' + allowed_instrument_ids = "banjo" /obj/item/instrument/guitar name = "guitar" desc = "It's made of wood and has bronze strings." icon_state = "guitar" item_state = "guitar" - instrumentExt = "ogg" - force = 10 attack_verb = list("played metal on", "serenaded", "crashed", "smashed") - hitsound = 'sound/effects/guitarsmash.ogg' - instrumentId = "guitar" + hitsound = 'sound/weapons/guitarslam.ogg' + allowed_instrument_ids = "guitar" /obj/item/instrument/eguitar name = "electric guitar" desc = "Makes all your shredding needs possible." icon_state = "eguitar" item_state = "eguitar" - instrumentExt = "ogg" force = 12 attack_verb = list("played metal on", "shredded", "crashed", "smashed") hitsound = 'sound/weapons/stringsmash.ogg' - instrumentId = "eguitar" + allowed_instrument_ids = "eguitar" /obj/item/instrument/glockenspiel name = "glockenspiel" desc = "Smooth metal bars perfect for any marching band." icon_state = "glockenspiel" item_state = "glockenspiel" - instrumentId = "glockenspiel" + allowed_instrument_ids = "glockenspiel" /obj/item/instrument/accordion name = "accordion" desc = "Pun-Pun not included." icon_state = "accordion" item_state = "accordion" - instrumentId = "accordion" + allowed_instrument_ids = "accordion" + +/obj/item/instrument/trumpet + name = "trumpet" + desc = "To announce the arrival of the king!" + icon_state = "trumpet" + item_state = "trumpet" + allowed_instrument_ids = "trombone" + +/obj/item/instrument/trumpet/spectral + name = "spectral trumpet" + desc = "Things are about to get spooky!" + icon_state = "spectral_trumpet" + item_state = "spectral_trumpet" + force = 0 + attack_verb = list("played", "jazzed", "trumpeted", "mourned", "dooted", "spooked") + +/obj/item/instrument/trumpet/spectral/Initialize() + . = ..() + AddComponent(/datum/component/spooky) + +/obj/item/instrument/trumpet/spectral/attack(mob/living/carbon/C, mob/user) + playsound(src, 'sound/instruments/trombone/En4.mid', 100, 1, -1) + ..() /obj/item/instrument/saxophone name = "saxophone" desc = "This soothing sound will be sure to leave your audience in tears." icon_state = "saxophone" item_state = "saxophone" - instrumentId = "saxophone" + allowed_instrument_ids = "saxophone" + +/obj/item/instrument/saxophone/spectral + name = "spectral saxophone" + desc = "This spooky sound will be sure to leave mortals in bones." + icon_state = "saxophone" + item_state = "saxophone" + force = 0 + attack_verb = list("played", "jazzed", "saxxed", "mourned", "dooted", "spooked") + +/obj/item/instrument/saxophone/spectral/Initialize() + . = ..() + AddComponent(/datum/component/spooky) + +/obj/item/instrument/saxophone/spectral/attack(mob/living/carbon/C, mob/user) + playsound(src, 'sound/instruments/saxophone/En4.mid', 100,1,-1) + ..() /obj/item/instrument/trombone name = "trombone" desc = "How can any pool table ever hope to compete?" icon_state = "trombone" + allowed_instrument_ids = "trombone" item_state = "trombone" - instrumentId = "trombone" + +/obj/item/instrument/trombone/spectral + name = "spectral trombone" + desc = "A skeleton's favorite instrument. Apply directly on the mortals." + icon_state = "trombone" + item_state = "trombone" + force = 0 + attack_verb = list("played", "jazzed", "tromboned", "mourned", "dooted", "spooked") + +/obj/item/instrument/trombone/spectral/Initialize() + . = ..() + AddComponent(/datum/component/spooky) + +/obj/item/instrument/trombone/spectral/attack(mob/living/carbon/C, mob/user) + playsound (src, 'sound/instruments/trombone/Cn4.mid', 100,1,-1) + ..() /obj/item/instrument/recorder name = "recorder" desc = "Just like in school, playing ability and all." + force = 5 icon_state = "recorder" item_state = "recorder" - instrumentId = "recorder" + allowed_instrument_ids = "recorder" /obj/item/instrument/harmonica name = "harmonica" desc = "For when you get a bad case of the space blues." icon_state = "harmonica" item_state = "harmonica" - instrumentId = "harmonica" force = 5 w_class = WEIGHT_CLASS_SMALL + allowed_instrument_ids = "harmonica" /obj/item/instrument/xylophone name = "xylophone" - desc = "a percussion instrument with a bright tone." + desc = "A percussion instrument with a bright tone." icon_state = "xylophone" item_state = "xylophone" - instrumentId = "xylophone" + allowed_instrument_ids = "bikehorn" /obj/item/instrument/bikehorn name = "gilded bike horn" @@ -153,14 +167,14 @@ lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' righthand_file = 'icons/mob/inhands/items_righthand.dmi' attack_verb = list("beautifully honks") - instrumentId = "bikehorn" - instrumentExt = "ogg" w_class = WEIGHT_CLASS_TINY force = 0 throw_speed = 3 throw_range = 7 hitsound = 'sound/items/bikehorn.ogg' + allowed_instrument_ids = "bikehorn" +// Crafting recipes /datum/crafting_recipe/violin name = "Violin" result = /obj/item/instrument/violin @@ -168,7 +182,7 @@ /obj/item/stack/cable_coil = 6, /obj/item/stack/tape_roll = 5) tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) - time = 80 + time = 8 SECONDS category = CAT_MISC /datum/crafting_recipe/guitar @@ -178,7 +192,7 @@ /obj/item/stack/cable_coil = 6, /obj/item/stack/tape_roll = 5) tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) - time = 80 + time = 8 SECONDS category = CAT_MISC /datum/crafting_recipe/eguitar @@ -188,5 +202,15 @@ /obj/item/stack/cable_coil = 6, /obj/item/stack/tape_roll = 5) tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) - time = 80 + time = 8 SECONDS + category = CAT_MISC + +/datum/crafting_recipe/banjo + name = "Banjo" + result = /obj/item/instrument/banjo + reqs = list(/obj/item/stack/sheet/wood = 5, + /obj/item/stack/cable_coil = 6, + /obj/item/stack/tape_roll = 5) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) + time = 8 SECONDS category = CAT_MISC diff --git a/code/modules/instruments/objs/structures/_musician.dm b/code/modules/instruments/objs/structures/_musician.dm new file mode 100644 index 00000000000..df7530a3313 --- /dev/null +++ b/code/modules/instruments/objs/structures/_musician.dm @@ -0,0 +1,47 @@ +/obj/structure/musician + name = "Not A Piano" + desc = "Something broke!" + var/can_play_unanchored = FALSE + var/list/allowed_instrument_ids + var/datum/song/song + +/obj/structure/musician/Initialize(mapload) + . = ..() + song = new(src, allowed_instrument_ids) + allowed_instrument_ids = null + +/obj/structure/musician/Destroy() + QDEL_NULL(song) + return ..() + +/obj/structure/musician/attack_hand(mob/user) + add_fingerprint(user) + tgui_interact(user) + +/obj/structure/musician/tgui_data(mob/user) + return song.tgui_data(user) + +/obj/structure/musician/tgui_interact(mob/user) + song.tgui_interact(user) + +/obj/structure/musician/tgui_act(action, params) + if(..()) + return + return song.tgui_act(action, params) + +/obj/structure/musician/wrench_act(mob/living/user, obj/item/I) + default_unfasten_wrench(user, I, 40) + return TRUE + +/** + * Whether the instrument should stop playing + * + * Arguments: + * * user - The user + */ +/obj/structure/musician/proc/should_stop_playing(mob/user) + if(!(anchored || can_play_unanchored)) + return TRUE + if(!user) + return FALSE + return !tgui_status(user, GLOB.tgui_physical_state) diff --git a/code/modules/instruments/objs/structures/piano.dm b/code/modules/instruments/objs/structures/piano.dm new file mode 100644 index 00000000000..a68f07e53a3 --- /dev/null +++ b/code/modules/instruments/objs/structures/piano.dm @@ -0,0 +1,22 @@ +/obj/structure/piano + parent_type = /obj/structure/musician // TODO: Can't edit maps right now due to a freeze, remove and update path when it's done + name = "space minimoog" + icon = 'icons/obj/musician.dmi' + icon_state = "minimoog" + anchored = TRUE + density = TRUE + allowed_instrument_ids = "piano" + +/obj/structure/piano/unanchored + anchored = FALSE + +/obj/structure/piano/Initialize(mapload) + . = ..() + if(prob(50) && icon_state == initial(icon_state)) + name = "space minimoog" + desc = "This is a minimoog, like a space piano, but more spacey!" + icon_state = "minimoog" + else + name = "space piano" + desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't." + icon_state = "piano" diff --git a/code/modules/instruments/organ.dm b/code/modules/instruments/organ.dm new file mode 100644 index 00000000000..424f63d7104 --- /dev/null +++ b/code/modules/instruments/organ.dm @@ -0,0 +1,43 @@ +/datum/instrument/organ + name = "Generic organ" + category = "Organ" + abstract_type = /datum/instrument/organ + +/datum/instrument/organ/crisis_church + name = "Crisis Church Organ" + id = "crichugan" + real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg', + "48"='sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg', + "60"='sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg', + "72"='sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg') + +/datum/instrument/organ/crisis_hammond + name = "Crisis Hammond Organ" + id = "crihamgan" + real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg', + "48"='sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg', + "60"='sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg', + "72"='sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg') + +/datum/instrument/organ/crisis_accordian + name = "Crisis Accordion" + id = "crack" + real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg', + "48"='sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg', + "60"='sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg', + "72"='sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg') + +/datum/instrument/organ/crisis_harmonica + name = "Crisis Harmonica" + id = "crharmony" + real_samples = list("48"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg', + "60"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg', + "72"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg') + +/datum/instrument/organ/crisis_tango_accordian + name = "Crisis Tango Accordion" + id = "crtango" + real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg', + "48"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg', + "60"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg', + "72"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg') diff --git a/code/modules/instruments/piano.dm b/code/modules/instruments/piano.dm new file mode 100644 index 00000000000..fdd2f6e9382 --- /dev/null +++ b/code/modules/instruments/piano.dm @@ -0,0 +1,56 @@ +/datum/instrument/piano + name = "Generic piano" + category = "Piano" + abstract_type = /datum/instrument/piano + +/datum/instrument/piano/fluid_piano + name = "FluidR3 Grand Piano" + id = "r3grand" + real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg', + "48"='sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg', + "60"='sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg', + "72"='sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg', + "84"='sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg', + "96"='sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg', + "108"='sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg') + +/datum/instrument/piano/fluid_harpsichord + name = "FluidR3 Harpsichord" + id = "r3harpsi" + real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c2.ogg', + "48"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c3.ogg', + "60"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c4.ogg', + "72"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c5.ogg', + "84"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c6.ogg', + "96"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c7.ogg', + "108"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c8.ogg') + +/datum/instrument/piano/crisis_harpsichord + name = "Crisis Harpsichord" + id = "crharpsi" + real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg', + "48"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg', + "60"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg', + "72"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg') + +/datum/instrument/piano/crisis_grandpiano_uni + name = "Crisis Grand Piano One" + id = "crgrand1" + real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg', + "48"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg', + "60"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg', + "72"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg', + "84"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg', + "96"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg', + "108"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg') + +/datum/instrument/piano/crisis_brightpiano_uni + name = "Crisis Bright Piano One" + id = "crbright1" + real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg', + "48"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg', + "60"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg', + "72"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg', + "84"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg', + "96"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg', + "108"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg') diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm new file mode 100644 index 00000000000..fc4a9b63579 --- /dev/null +++ b/code/modules/instruments/songs/_song.dm @@ -0,0 +1,418 @@ +#define MUSICIAN_HEARCHECK_MINDELAY 4 +#define MUSIC_MAXLINES 1000 +#define MUSIC_MAXLINECHARS 300 + +/** + * # Song datum + * + * These are the actual backend behind instruments. + * They attach to an atom and provide the editor + playback functionality. + */ +/datum/song + /// Name of the song + var/name = "Untitled" + + /// The atom we're attached to/playing from + var/atom/parent + + /// Our song lines + var/list/lines + + /// delay between notes in deciseconds + var/tempo = 5 + + /// How far we can be heard + var/instrument_range = 15 + + /// Are we currently playing? + var/playing = FALSE + + /// Are we currently editing? + var/editing = TRUE + /// Is the help screen open? + var/help = FALSE + + /// Repeats left + var/repeat = 0 + /// Maximum times we can repeat + var/max_repeats = 10 + + /// Our volume + var/volume = 75 + /// Max volume + var/max_volume = 75 + /// Min volume - This is so someone doesn't decide it's funny to set it to 0 and play invisible songs. + var/min_volume = 1 + + /// What instruments our built in picker can use. The picker won't show unless this is longer than one. + var/list/allowed_instrument_ids + + //////////// Cached instrument variables ///////////// + /// Instrument we are currently using + var/datum/instrument/using_instrument + /// Cached legacy ext for legacy instruments + var/cached_legacy_ext + /// Cached legacy dir for legacy instruments + var/cached_legacy_dir + /// Cached list of samples, referenced directly from the instrument for synthesized instruments + var/list/cached_samples + /// Are we operating in legacy mode (so if the instrument is a legacy instrument) + var/legacy = FALSE + ////////////////////////////////////////////////////// + + /////////////////// Playing variables //////////////// + /** + * Build by compile_chords() + * Must be rebuilt on instrument switch. + * Compilation happens when we start playing and is cleared after we finish playing. + * Format: list of chord lists, with chordlists having (key1, key2, key3, tempodiv) + */ + var/list/compiled_chords + /// Current section of a long chord we're on, so we don't need to make a billion chords, one for every unit ticklag. + var/elapsed_delay + /// Amount of delay to wait before playing the next chord + var/delay_by + /// Current chord we're on. + var/current_chord + /// Channel as text = current volume percentage but it's 0 to 100 instead of 0 to 1. + var/list/channels_playing + /// List of channels that aren't being used, as text. This is to prevent unnecessary freeing and reallocations from SSsounds/SSinstruments. + var/list/channels_idle + /// Person playing us + var/mob/user_playing + ////////////////////////////////////////////////////// + + /// Last world.time we checked for who can hear us + var/last_hearcheck = 0 + /// The list of mobs that can hear us + var/list/hearing_mobs + /// If this is enabled, some things won't be strictly cleared when they usually are (liked compiled_chords on play stop) + var/debug_mode = FALSE + /// Max sound channels to occupy + var/max_sound_channels = CHANNELS_PER_INSTRUMENT + /// Current channels, so we can save a length() call. + var/using_sound_channels = 0 + /// Last channel to play. text. + var/last_channel_played + /// Should we not decay our last played note? + var/full_sustain_held_note = TRUE + + /////////////////////// DO NOT TOUCH THESE /////////////////// + var/octave_min = INSTRUMENT_MIN_OCTAVE + var/octave_max = INSTRUMENT_MAX_OCTAVE + var/key_min = INSTRUMENT_MIN_KEY + var/key_max = INSTRUMENT_MAX_KEY + var/static/list/note_offset_lookup + var/static/list/accent_lookup + ////////////////////////////////////////////////////////////// + + ///////////// !!FUN!! - Only works in synthesized mode! ///////////////// + /// Note numbers to shift. + var/note_shift = 0 + var/note_shift_min = -100 + var/note_shift_max = 100 + var/can_noteshift = TRUE + /// The kind of sustain we're using + var/sustain_mode = SUSTAIN_LINEAR + /// When a note is considered dead if it is below this in volume + var/sustain_dropoff_volume = INSTRUMENT_MIN_SUSTAIN_DROPOFF + /// Total duration of linear sustain for 100 volume note to get to SUSTAIN_DROPOFF + var/sustain_linear_duration = 5 + /// Exponential sustain dropoff rate per decisecond + var/sustain_exponential_dropoff = 1.4 + ////////// DO NOT DIRECTLY SET THESE! + /// Do not directly set, use update_sustain() + var/cached_linear_dropoff = 10 + /// Do not directly set, use update_sustain() + var/cached_exponential_dropoff = 1.045 + ///////////////////////////////////////////////////////////////////////// + + var/static/list/valid_files[0] // Cache to avoid running fexists() every time + +/datum/song/New(atom/parent, list/instrument_ids, new_range) + SSinstruments.on_song_new(src) + lines = list() + tempo = sanitize_tempo(tempo) + src.parent = parent + if(instrument_ids) + allowed_instrument_ids = islist(instrument_ids) ? instrument_ids : list(instrument_ids) + if(length(allowed_instrument_ids)) + set_instrument(allowed_instrument_ids[1]) + hearing_mobs = list() + volume = clamp(volume, min_volume, max_volume) + channels_playing = list() + channels_idle = list() + if(!note_offset_lookup) + note_offset_lookup = list(9, 11, 0, 2, 4, 5, 7) + accent_lookup = list("b" = -1, "s" = 1, "#" = 1, "n" = 0) + update_sustain() + if(new_range) + instrument_range = new_range + +/datum/song/Destroy() + stop_playing() + SSinstruments.on_song_del(src) + lines = null + using_instrument = null + allowed_instrument_ids = null + parent = null + return ..() + +/** + * Checks and stores which mobs can hear us. Terminates sounds for mobs that leave our range. + */ +/datum/song/proc/do_hearcheck() + last_hearcheck = world.time + var/list/old = hearing_mobs.Copy() + hearing_mobs.len = 0 + var/turf/source = get_turf(parent) + for(var/mob/M in GLOB.player_list) + if(M.z != source.z) // Z-level check + continue + var/dist = get_dist(M, source) + if(dist > instrument_range) // Distance check + continue + if(!isInSight(M, source)) // Visibility check (direct line of sight) + continue + hearing_mobs[M] = dist + var/list/exited = old - hearing_mobs + for(var/i in exited) + terminate_sound_mob(i) + +/** + * Sets our instrument, caching anything necessary for faster accessing. Accepts an ID, typepath, or instantiated instrument datum. + */ +/datum/song/proc/set_instrument(datum/instrument/I) + terminate_all_sounds() + var/old_legacy + if(using_instrument) + using_instrument.songs_using -= src + old_legacy = (using_instrument.instrument_flags & INSTRUMENT_LEGACY) + using_instrument = null + cached_samples = null + cached_legacy_ext = null + cached_legacy_dir = null + legacy = null + if(istext(I) || ispath(I)) + I = SSinstruments.instrument_data[I] + if(istype(I)) + using_instrument = I + I.songs_using += src + var/instrument_legacy = (I.instrument_flags & INSTRUMENT_LEGACY) + if(instrument_legacy) + cached_legacy_ext = I.legacy_instrument_ext + cached_legacy_dir = I.legacy_instrument_path + legacy = TRUE + else + cached_samples = I.samples + legacy = FALSE + if(isnull(old_legacy) || (old_legacy != instrument_legacy)) + if(playing) + compile_chords() + +/** + * Attempts to start playing our song. + */ +/datum/song/proc/start_playing(mob/user) + if(playing) + return + if(!using_instrument?.is_ready()) + to_chat(user, "An error has occured with [src]. Please reset the instrument.") + return + compile_chords() + if(!length(compiled_chords)) + to_chat(user, "Song is empty.") + return + playing = TRUE + SStgui.update_uis(parent) + //we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak. + //wrap the rest of the stuff to ensure stop_playing() is called. + do_hearcheck() + SEND_SIGNAL(parent, COMSIG_SONG_START) + elapsed_delay = 0 + delay_by = 0 + current_chord = 1 + user_playing = user + START_PROCESSING(SSinstruments, src) + +/** + * Stops playing, terminating all sounds if in synthesized mode. Clears hearing_mobs. + */ +/datum/song/proc/stop_playing() + if(!playing) + return + playing = FALSE + if(!debug_mode) + compiled_chords = null + STOP_PROCESSING(SSinstruments, src) + SEND_SIGNAL(parent, COMSIG_SONG_END) + terminate_all_sounds(TRUE) + hearing_mobs.len = 0 + SStgui.update_uis(parent) + user_playing = null + +/** + * Processes our song. + */ +/datum/song/proc/process_song(wait) + if(!length(compiled_chords) || should_stop_playing(user_playing)) + stop_playing() + return + if(++elapsed_delay >= delay_by) + // We were sustaining the final note but not anymore + if(current_chord > length(compiled_chords)) + stop_playing() + return + var/list/chord = compiled_chords[current_chord] + play_chord(chord) + elapsed_delay = 0 + delay_by = tempodiv_to_delay(chord[length(chord)]) + current_chord++ + if(current_chord > length(compiled_chords) + 1) + if(repeat) + repeat-- + current_chord = 1 + SStgui.update_uis(parent) + return + else + stop_playing() + return + +/** + * Converts a tempodiv to ticks to elapse before playing the next chord, taking into account our tempo. + */ +/datum/song/proc/tempodiv_to_delay(tempodiv) + tempodiv = tempodiv || world.tick_lag // Default to world.tick_lag in case someone's trying to be smart + return max(1, round((tempo / tempodiv) / world.tick_lag, 1)) + +/** + * Compiles chords. + */ +/datum/song/proc/compile_chords() + legacy ? compile_legacy() : compile_synthesized() + // Some chords may be null for some reason - exclude them. + listclearnulls(compiled_chords) + +/** + * Plays a chord. + */ +/datum/song/proc/play_chord(list/chord) + // last value is timing information + for(var/i in 1 to (length(chord) - 1)) + legacy? playkey_legacy(chord[i][1], chord[i][2], chord[i][3], user_playing) : playkey_synth(chord[i], user_playing) + +/** + * Checks if we should halt playback. + */ +/datum/song/proc/should_stop_playing(mob/user) + return QDELETED(parent) || !using_instrument || !playing + +/** + * Sanitizes tempo to a value that makes sense and fits the current world.tick_lag. + */ +/datum/song/proc/sanitize_tempo(new_tempo) + new_tempo = abs(new_tempo) + return clamp(round(new_tempo, world.tick_lag), world.tick_lag, 5 SECONDS) + +/** + * Gets our beats per minute based on our tempo. + */ +/datum/song/proc/get_bpm() + return 600 / tempo + +/** + * Sets our tempo from a beats-per-minute, sanitizing it to a valid number first. + */ +/datum/song/proc/set_bpm(bpm) + tempo = sanitize_tempo(600 / bpm) + +/datum/song/process(wait) + if(!playing) + return PROCESS_KILL + // it's expected this ticks at every world.tick_lag. if it lags, do not attempt to catch up. + process_song(world.tick_lag) + process_decay(world.tick_lag) + +/** + * Updates our cached linear/exponential falloff stuff, saving calculations down the line. + */ +/datum/song/proc/update_sustain() + // Exponential is easy + cached_exponential_dropoff = sustain_exponential_dropoff + // Linear, not so much, since it's a target duration from 100 volume rather than an exponential rate. + var/target_duration = sustain_linear_duration + var/volume_diff = max(0, 100 - sustain_dropoff_volume) + var/volume_decrease_per_decisecond = volume_diff / target_duration + cached_linear_dropoff = volume_decrease_per_decisecond + +/** + * Setter for setting output volume. + */ +/datum/song/proc/set_volume(volume) + src.volume = clamp(volume, max(0, min_volume), min(100, max_volume)) + update_sustain() + // We don't want to send the whole payload (song included) just for volume + var/datum/tgui/ui = SStgui.get_open_ui(usr, parent, "main") + if(ui) + ui.push_data(list("volume" = volume), force = TRUE) + +/** + * Setter for setting how low the volume has to get before a note is considered "dead" and dropped + */ +/datum/song/proc/set_dropoff_volume(volume, no_refresh = FALSE) + sustain_dropoff_volume = clamp(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100) + update_sustain() + if(!no_refresh) + SStgui.update_uis(parent) + +/** + * Setter for setting exponential falloff factor. + */ +/datum/song/proc/set_exponential_drop_rate(drop, no_refresh = FALSE) + sustain_exponential_dropoff = clamp(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX) + update_sustain() + if(!no_refresh) + SStgui.update_uis(parent) + +/** + * Setter for setting linear falloff duration. + */ +/datum/song/proc/set_linear_falloff_duration(duration, no_refresh = FALSE) + sustain_linear_duration = clamp(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN) + update_sustain() + if(!no_refresh) + SStgui.update_uis(parent) + +/datum/song/vv_edit_var(var_name, var_value) + . = ..() + if(.) + switch(var_name) + if(NAMEOF(src, volume)) + set_volume(var_value) + if(NAMEOF(src, sustain_dropoff_volume)) + set_dropoff_volume(var_value) + if(NAMEOF(src, sustain_exponential_dropoff)) + set_exponential_drop_rate(var_value) + if(NAMEOF(src, sustain_linear_duration)) + set_linear_falloff_duration(var_value) + +// subtype for handheld instruments, like violin +/datum/song/handheld + +/datum/song/handheld/should_stop_playing(mob/user) + . = ..() + if(.) + return TRUE + var/obj/item/instrument/I = parent + return I.should_stop_playing(user) + +// subtype for stationary structures, like pianos +/datum/song/stationary + +/datum/song/stationary/should_stop_playing(mob/user) + . = ..() + if(.) + return TRUE + var/obj/structure/musician/M = parent + return M.should_stop_playing(user) + diff --git a/code/modules/instruments/songs/_song_ui.dm b/code/modules/instruments/songs/_song_ui.dm new file mode 100644 index 00000000000..cbd0f646f5c --- /dev/null +++ b/code/modules/instruments/songs/_song_ui.dm @@ -0,0 +1,179 @@ +/datum/song/tgui_data(mob/user) + var/data[0] + + // General + data["playing"] = playing + data["repeat"] = repeat + data["maxRepeats"] = max_repeats + data["editing"] = editing + data["lines"] = lines + data["tempo"] = tempo + data["minTempo"] = world.tick_lag + data["maxTempo"] = 5 SECONDS + data["tickLag"] = world.tick_lag + data["help"] = help + + // Status + var/list/allowed_instrument_names = list() + for(var/i in allowed_instrument_ids) + var/datum/instrument/I = SSinstruments.get_instrument(i) + if(I) + allowed_instrument_names += I.name + data["allowedInstrumentNames"] = allowed_instrument_names + data["instrumentLoaded"] = !isnull(using_instrument) + if(using_instrument) + data["instrument"] = using_instrument.name + data["canNoteShift"] = can_noteshift + if(can_noteshift) + data["noteShift"] = note_shift + data["noteShiftMin"] = note_shift_min + data["noteShiftMax"] = note_shift_max + data["sustainMode"] = sustain_mode + switch(sustain_mode) + if(SUSTAIN_LINEAR) + data["sustainLinearDuration"] = sustain_linear_duration + if(SUSTAIN_EXPONENTIAL) + data["sustainExponentialDropoff"] = sustain_exponential_dropoff + data["ready"] = using_instrument?.is_ready() + data["legacy"] = legacy + data["volume"] = volume + data["minVolume"] = min_volume + data["maxVolume"] = max_volume + data["sustainDropoffVolume"] = sustain_dropoff_volume + data["sustainHeldNote"] = full_sustain_held_note + + return data + +/datum/song/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, parent, ui_key, ui, force_open) + if(!ui) + ui = new(user, parent, ui_key, "Instrument", parent?.name || "Instrument", 700, 500) + ui.open() + ui.set_autoupdate(FALSE) // NO!!! Don't auto-update this!! + +/datum/song/tgui_act(action, params) + . = TRUE + switch(action) + if("newsong") + lines = new() + tempo = sanitize_tempo(5) // default 120 BPM + name = "" + if("import") + var/t = "" + do + t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message) + if(!in_range(parent, usr)) + return + + if(length_char(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS) + var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") + if(cont == "no") + break + while(length_char(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS) + parse_song(t) + return FALSE + if("help") + help = !help + if("edit") + editing = !editing + if("repeat") //Changing this from a toggle to a number of repeats to avoid infinite loops. + if(playing) + return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing. + repeat = clamp(round(text2num(params["new"])), 0, max_repeats) + if("tempo") + tempo = sanitize_tempo(text2num(params["new"])) + if("play") + INVOKE_ASYNC(src, .proc/start_playing, usr) + if("newline") + var/newline = html_encode(input("Enter your line: ", parent.name) as text|null) + if(!newline || !in_range(parent, usr)) + return + if(length(lines) > MUSIC_MAXLINES) + return + if(length(newline) > MUSIC_MAXLINECHARS) + newline = copytext(newline, 1, MUSIC_MAXLINECHARS) + lines.Add(newline) + if("deleteline") + var/num = round(text2num(params["line"])) + if(num > length(lines) || num < 1) + return + lines.Cut(num, num + 1) + if("modifyline") + var/num = round(text2num(params["line"])) + var/content = stripped_input(usr, "Enter your line: ", parent.name, lines[num], MUSIC_MAXLINECHARS) + if(!content || !in_range(parent, usr)) + return + if(num > length(lines) || num < 1) + return + lines[num] = content + if("stop") + stop_playing() + if("setlinearfalloff") + set_linear_falloff_duration(round(text2num(params["new"]) * 10, world.tick_lag), TRUE) + if("setexpfalloff") + set_exponential_drop_rate(round(text2num(params["new"]), 0.00001), TRUE) + if("setvolume") + set_volume(round(text2num(params["new"]), 1)) + if("setdropoffvolume") + set_dropoff_volume(round(text2num(params["new"]), 0.01), TRUE) + if("switchinstrument") + if(!length(allowed_instrument_ids)) + return + else if(length(allowed_instrument_ids) == 1) + set_instrument(allowed_instrument_ids[1]) + return + var/choice = params["name"] + for(var/i in allowed_instrument_ids) + var/datum/instrument/I = SSinstruments.get_instrument(i) + if(I && I.name == choice) + set_instrument(I) + if("setnoteshift") + note_shift = clamp(round(text2num(params["new"])), note_shift_min, note_shift_max) + if("setsustainmode") + var/static/list/sustain_modes + if(!length(sustain_modes)) + sustain_modes = list("Linear" = SUSTAIN_LINEAR, "Exponential" = SUSTAIN_EXPONENTIAL) + var/choice = params["new"] + sustain_mode = sustain_modes[choice] || sustain_mode + if("togglesustainhold") + full_sustain_held_note = !full_sustain_held_note + if("reset") + var/default_instrument = allowed_instrument_ids[1] + if(using_instrument != SSinstruments.instrument_data[default_instrument]) + set_instrument(default_instrument) + note_shift = initial(note_shift) + sustain_mode = initial(sustain_mode) + set_linear_falloff_duration(initial(sustain_linear_duration), TRUE) + set_exponential_drop_rate(initial(sustain_exponential_dropoff), TRUE) + set_dropoff_volume(initial(sustain_dropoff_volume), TRUE) + else + return FALSE + parent.add_fingerprint(usr) + +/** + * Parses a song the user has input into lines and stores them. + */ +/datum/song/proc/parse_song(text) + set waitfor = FALSE + //split into lines + stop_playing() + lines = splittext(text, "\n") + if(length(lines)) + var/bpm_string = "BPM: " + if(findtext(lines[1], bpm_string, 1, length(bpm_string) + 1)) + var/divisor = text2num(copytext(lines[1], length(bpm_string) + 1)) || 120 // default + tempo = sanitize_tempo(600 / round(divisor, 1)) + lines.Cut(1, 2) + else + tempo = sanitize_tempo(5) // default 120 BPM + if(length(lines) > MUSIC_MAXLINES) + to_chat(usr, "Too many lines!") + lines.Cut(MUSIC_MAXLINES + 1) + var/linenum = 1 + for(var/l in lines) + if(length_char(l) > MUSIC_MAXLINECHARS) + to_chat(usr, "Line [linenum] too long!") + lines.Remove(l) + else + linenum++ + SStgui.update_uis(parent) diff --git a/code/modules/instruments/songs/play_legacy.dm b/code/modules/instruments/songs/play_legacy.dm new file mode 100644 index 00000000000..61d7964caf4 --- /dev/null +++ b/code/modules/instruments/songs/play_legacy.dm @@ -0,0 +1,95 @@ +/** + * Compiles our lines into "chords" with filenames for legacy playback. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag. + */ +/datum/song/proc/compile_legacy() + if(!length(src.lines)) + return + var/list/lines = src.lines //cache for hyepr speed! + compiled_chords = list() + var/list/octaves = list(3, 3, 3, 3, 3, 3, 3) + var/list/accents = list("n", "n", "n", "n", "n", "n", "n") + for(var/line in lines) + var/list/chords = splittext(lowertext(line), ",") + for(var/chord in chords) + var/list/compiled_chord = list() + var/tempodiv = 1 + var/list/notes_tempodiv = splittext(chord, "/") + var/len = length(notes_tempodiv) + if(len >= 2) + tempodiv = text2num(notes_tempodiv[2]) + if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that. + var/list/notes = splittext(notes_tempodiv[1], "-") + for(var/note in notes) + if(length(note) == 0) + continue + // 1-7, A-G + var/key = text2ascii(note) - 96 + if((key < 1) || (key > 7)) + continue + for(var/i in 2 to length(note)) + var/oct_acc = copytext(note, i, i + 1) + var/num = text2num(oct_acc) + if(!num) //it's an accidental + accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks. + else //octave + octaves[key] = clamp(num, octave_min, octave_max) + compiled_chord[++compiled_chord.len] = list(key, accents[key], octaves[key]) + compiled_chord += tempodiv //this goes last + if(length(compiled_chord)) + compiled_chords[++compiled_chords.len] = compiled_chord + +/** + * Proc to play a legacy note. Just plays the sound to hearing mobs (and does hearcheck if necessary), no fancy channel/sustain/management. + * + * Arguments: + * * note - number from 1-7 for A-G + * * acc - either "b", "n", or "#" + * * oct - 1-8 (or 9 for C) + */ +/datum/song/proc/playkey_legacy(note, acc as text, oct, mob/user) + // handle accidental -> B<>C of E<>F + if(acc == "b" && (note == 3 || note == 6)) // C or F + if(note == 3) + oct-- + note-- + acc = "n" + else if(acc == "#" && (note == 2 || note == 5)) // B or E + if(note == 2) + oct++ + note++ + acc = "n" + else if(acc == "#" && (note == 7)) //G# + note = 1 + acc = "b" + else if(acc == "#") // mass convert all sharps to flats, octave jump already handled + acc = "b" + note++ + + // check octave, C is allowed to go to 9 + if(oct < 1 || (note == 3 ? oct > 9 : oct > 8)) + return + + // now generate name + var/filename = "sound/instruments/[cached_legacy_dir]/[ascii2text(note + 64)][acc][oct].[cached_legacy_ext]" + var/soundfile = file(filename) + // make sure the note exists + var/cached_fexists = valid_files[filename] + if(!isnull(cached_fexists)) + if(!cached_fexists) + return + else if(!fexists(soundfile)) + valid_files[filename] = FALSE + return + else + valid_files[filename] = TRUE + // and play + var/turf/source = get_turf(parent) + if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) + do_hearcheck() + var/sound/music_played = sound(soundfile) + for(var/i in hearing_mobs) + var/mob/M = i + if(!(M.client?.prefs?.sound & SOUND_INSTRUMENTS)) + continue + M.playsound_local(source, null, volume * using_instrument.volume_multiplier, falloff = 5, S = music_played) + // Could do environment and echo later but not for now diff --git a/code/modules/instruments/songs/play_synthesized.dm b/code/modules/instruments/songs/play_synthesized.dm new file mode 100644 index 00000000000..b2f16f6ab91 --- /dev/null +++ b/code/modules/instruments/songs/play_synthesized.dm @@ -0,0 +1,134 @@ +/** + * Compiles our lines into "chords" with numbers. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag. + */ +/datum/song/proc/compile_synthesized() + if(!length(src.lines)) + return + var/list/lines = src.lines //cache for hyepr speed! + compiled_chords = list() + var/list/octaves = list(3, 3, 3, 3, 3, 3, 3) + var/list/accents = list("n", "n", "n", "n", "n", "n", "n") + for(var/line in lines) + var/list/chords = splittext(lowertext(line), ",") + for(var/chord in chords) + var/list/compiled_chord = list() + var/tempodiv = 1 + var/list/notes_tempodiv = splittext(chord, "/") + var/len = length(notes_tempodiv) + if(len >= 2) + tempodiv = text2num(notes_tempodiv[2]) + if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that. + var/list/notes = splittext(notes_tempodiv[1], "-") + for(var/note in notes) + if(length(note) == 0) + continue + // 1-7, A-G + var/key = text2ascii(note) - 96 + if((key < 1) || (key > 7)) + continue + for(var/i in 2 to length(note)) + var/oct_acc = copytext(note, i, i + 1) + var/num = text2num(oct_acc) + if(!num) //it's an accidental + accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks. + else //octave + octaves[key] = clamp(num, octave_min, octave_max) + compiled_chord += clamp((note_offset_lookup[key] + octaves[key] * 12 + accent_lookup[accents[key]]), key_min, key_max) + compiled_chord += tempodiv //this goes last + if(length(compiled_chord)) + compiled_chords[++compiled_chords.len] = compiled_chord + +/** + * Plays a specific numerical key from our instrument to anyone who can hear us. + * Does a hearing check if enough time has passed. + */ +/datum/song/proc/playkey_synth(key, mob/user) + if(can_noteshift) + key = clamp(key + note_shift, key_min, key_max) + if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck) + do_hearcheck() + var/datum/instrument_key/K = using_instrument.samples[num2text(key)] //See how fucking easy it is to make a number text? You don't need a complicated 9 line proc! + //Should probably add channel limiters here at some point but I don't care right now. + var/channel = pop_channel() + if(isnull(channel)) + return FALSE + . = TRUE + var/sound/copy = sound(K.sample) + var/volume = src.volume * using_instrument.volume_multiplier + copy.frequency = K.frequency + copy.volume = volume + var/channel_text = num2text(channel) + channels_playing[channel_text] = 100 + last_channel_played = channel_text + for(var/i in hearing_mobs) + var/mob/M = i + if(!(M.client?.prefs?.sound & SOUND_INSTRUMENTS)) + continue + M.playsound_local(get_turf(parent), null, volume, FALSE, K.frequency, INSTRUMENT_DISTANCE_NO_FALLOFF, channel, null, copy, distance_multiplier = INSTRUMENT_DISTANCE_FALLOFF_BUFF) + // Could do environment and echo later but not for now + +/** + * Stops all sounds we are "responsible" for. Only works in synthesized mode. + */ +/datum/song/proc/terminate_all_sounds(clear_channels = TRUE) + for(var/i in hearing_mobs) + terminate_sound_mob(i) + if(clear_channels && channels_playing) + channels_playing.len = 0 + channels_idle.len = 0 + SSinstruments.current_instrument_channels -= using_sound_channels + using_sound_channels = 0 + SSsounds.free_datum_channels(src) + +/** + * Stops all sounds we are responsible for in a given person. Only works in synthesized mode. + */ +/datum/song/proc/terminate_sound_mob(mob/M) + for(var/channel in channels_playing) + M.stop_sound_channel(text2num(channel)) + +/** + * Pops a channel we have reserved so we don't have to release and re-request them from SSsounds every time we play a note. This is faster. + */ +/datum/song/proc/pop_channel() + if(length(channels_idle)) //just pop one off of here if we have one available + . = text2num(channels_idle[1]) + channels_idle.Cut(1, 2) + return + if(using_sound_channels >= max_sound_channels) + return + . = SSinstruments.reserve_instrument_channel(src) + if(!isnull(.)) + using_sound_channels++ + +/** + * Decays our channels and updates their volumes to mobs who can hear us. + * + * Arguments: + * * wait_ds - the deciseconds we should decay by. This is to compensate for any lag, as otherwise songs would get pretty nasty during high time dilation. + */ +/datum/song/proc/process_decay(wait_ds) + var/linear_dropoff = cached_linear_dropoff * wait_ds + var/exponential_dropoff = cached_exponential_dropoff ** wait_ds + for(var/channel in channels_playing) + if(full_sustain_held_note && (channel == last_channel_played)) + continue + var/current_volume = channels_playing[channel] + switch(sustain_mode) + if(SUSTAIN_LINEAR) + current_volume -= linear_dropoff + if(SUSTAIN_EXPONENTIAL) + current_volume /= exponential_dropoff + channels_playing[channel] = current_volume + var/dead = current_volume <= sustain_dropoff_volume + var/channelnumber = text2num(channel) + if(dead) + channels_playing -= channel + channels_idle += channel + for(var/i in hearing_mobs) + var/mob/M = i + M.stop_sound_channel(channelnumber) + else + for(var/i in hearing_mobs) + var/mob/M = i + M.set_sound_channel_volume(channelnumber, (current_volume * 0.01) * volume * using_instrument.volume_multiplier) diff --git a/code/modules/instruments/synth_tones.dm b/code/modules/instruments/synth_tones.dm new file mode 100644 index 00000000000..9ad9250f40d --- /dev/null +++ b/code/modules/instruments/synth_tones.dm @@ -0,0 +1,19 @@ +/datum/instrument/tones + name = "Ideal tone" + category = "Tones" + abstract_type = /datum/instrument/tones + +/datum/instrument/tones/square_wave + name = "Ideal square wave" + id = "square" + real_samples = list("81"='sound/instruments/synthesis_samples/tones/Square.ogg') + +/datum/instrument/tones/sine_wave + name = "Ideal sine wave" + id = "sine" + real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sine.ogg') + +/datum/instrument/tones/saw_wave + name = "Ideal sawtooth wave" + id = "saw" + real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sawtooth.ogg') diff --git a/code/modules/karma/karma.dm b/code/modules/karma/karma.dm index 837e3a21104..6df1ce19ba7 100644 --- a/code/modules/karma/karma.dm +++ b/code/modules/karma/karma.dm @@ -2,7 +2,7 @@ Everything karma related is here. Part of karma purchase is handled in client_procs.dm */ -proc/sql_report_karma(var/mob/spender, var/mob/receiver) +/proc/sql_report_karma(var/mob/spender, var/mob/receiver) var/sqlspendername = sanitizeSQL(spender.name) var/sqlspenderkey = sanitizeSQL(spender.ckey) var/sqlreceivername = sanitizeSQL(receiver.name) diff --git a/code/modules/library/admin.dm b/code/modules/library/admin.dm index db907fc20ba..b56aa6994cf 100644 --- a/code/modules/library/admin.dm +++ b/code/modules/library/admin.dm @@ -3,8 +3,7 @@ set desc = "Permamently deletes a book from the database." set category = "Admin" - if(!holder) - to_chat(src, "Only administrators may use this command.") + if(!check_rights(R_ADMIN)) return var/isbn = input("ISBN number?", "Delete Book") as num | null @@ -25,8 +24,7 @@ set desc = "View books flagged for content." set category = "Admin" - if(!holder) - to_chat(src, "Only administrators may use this command.") + if(!check_rights(R_ADMIN)) return holder.view_flagged_books() diff --git a/code/modules/library/computers/checkout.dm b/code/modules/library/computers/checkout.dm index f9aae71b2a5..934234f645e 100644 --- a/code/modules/library/computers/checkout.dm +++ b/code/modules/library/computers/checkout.dm @@ -463,4 +463,5 @@ B.author = newbook.author B.dat = newbook.content B.icon_state = "book[rand(1,16)]" + B.has_drm = TRUE visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?") diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index a7dd148b51a..4217c4be6f7 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -101,32 +101,32 @@ /obj/structure/bookcase/manuals/medical name = "Medical Manuals bookcase" - New() - ..() - new /obj/item/book/manual/medical_cloning(src) - update_icon() +/obj/structure/bookcase/manuals/medical/New() + ..() + new /obj/item/book/manual/medical_cloning(src) + update_icon() /obj/structure/bookcase/manuals/engineering name = "Engineering Manuals bookcase" - New() - ..() - new /obj/item/book/manual/engineering_construction(src) - new /obj/item/book/manual/engineering_particle_accelerator(src) - new /obj/item/book/manual/engineering_hacking(src) - new /obj/item/book/manual/engineering_guide(src) - new /obj/item/book/manual/engineering_singularity_safety(src) - new /obj/item/book/manual/robotics_cyborgs(src) - update_icon() +/obj/structure/bookcase/manuals/engineering/New() + ..() + new /obj/item/book/manual/engineering_construction(src) + new /obj/item/book/manual/engineering_particle_accelerator(src) + new /obj/item/book/manual/engineering_hacking(src) + new /obj/item/book/manual/engineering_guide(src) + new /obj/item/book/manual/engineering_singularity_safety(src) + new /obj/item/book/manual/robotics_cyborgs(src) + update_icon() /obj/structure/bookcase/manuals/research_and_development name = "R&D Manuals bookcase" - New() - ..() - new /obj/item/book/manual/research_and_development(src) - update_icon() +/obj/structure/bookcase/manuals/research_and_development/New() + ..() + new /obj/item/book/manual/research_and_development(src) + update_icon() /* @@ -151,6 +151,8 @@ var/carved = 0 // Has the book been hollowed out for use as a secret storage item? var/forbidden = 0 // Prevent ordering of this book. (0=no, 1=yes, 2=emag only) var/obj/item/store // What's in the book? + /// Book DRM. If this var is TRUE, it cannot be scanned and re-uploaded + var/has_drm = FALSE /obj/item/book/attack_self(var/mob/user as mob) if(carved) @@ -286,26 +288,26 @@ var/obj/item/book/book // Currently scanned book var/mode = 0 // 0 - Scan only, 1 - Scan and Set Buffer, 2 - Scan and Attempt to Check In, 3 - Scan and Attempt to Add to Inventory - attack_self(mob/user as mob) - mode += 1 - if(mode > 3) - mode = 0 - to_chat(user, "[src] Status Display:") - var/modedesc - switch(mode) - if(0) - modedesc = "Scan book to local buffer." - if(1) - modedesc = "Scan book to local buffer and set associated computer buffer to match." - if(2) - modedesc = "Scan book to local buffer, attempt to check in scanned book." - if(3) - modedesc = "Scan book to local buffer, attempt to add book to general inventory." - else - modedesc = "ERROR" - to_chat(user, " - Mode [mode] : [modedesc]") - if(src.computer) - to_chat(user, "Computer has been associated with this unit.") +/obj/item/barcodescanner/attack_self(mob/user as mob) + mode += 1 + if(mode > 3) + mode = 0 + to_chat(user, "[src] Status Display:") + var/modedesc + switch(mode) + if(0) + modedesc = "Scan book to local buffer." + if(1) + modedesc = "Scan book to local buffer and set associated computer buffer to match." + if(2) + modedesc = "Scan book to local buffer, attempt to check in scanned book." + if(3) + modedesc = "Scan book to local buffer, attempt to add book to general inventory." else - to_chat(user, "No associated computer found. Only local scans will function properly.") - to_chat(user, "\n") + modedesc = "ERROR" + to_chat(user, " - Mode [mode] : [modedesc]") + if(src.computer) + to_chat(user, "Computer has been associated with this unit.") + else + to_chat(user, "No associated computer found. Only local scans will function properly.") + to_chat(user, "\n") diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 8fa7629863e..0c23a0c5800 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -150,6 +150,11 @@ GLOBAL_LIST_INIT(library_section_names, list("Any", "Fiction", "Non-Fiction", "A power_change() return if(istype(I, /obj/item/book)) + // NT with those pesky DRM schemes + var/obj/item/book/B = I + if(B.has_drm) + atom_say("Copyrighted material detected. Scanner is unable to copy book to memory.") + return FALSE user.drop_item() I.forceMove(src) return 1 diff --git a/code/modules/martial_arts/adminfu.dm b/code/modules/martial_arts/adminfu.dm index 12436061e20..20f3c95f7b6 100644 --- a/code/modules/martial_arts/adminfu.dm +++ b/code/modules/martial_arts/adminfu.dm @@ -1,93 +1,37 @@ -///Adminfu -//Help act:Heal/revie GP //p is for help -//Disarm:Stun -//Grab:Neck -//Harm:Gib -#define HEAL_COMBO "GP" - /datum/martial_art/adminfu name = "Way of the Dancing Admin" - help_verb = /mob/living/carbon/human/proc/adminfu_help - -/datum/martial_art/adminfu/proc/check_streak(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(findtext(streak,HEAL_COMBO)) - streak = "" - healPalm(A,D) - return 1 - return 0 + has_explaination_verb = TRUE + combos = list(/datum/martial_combo/adminfu/healing_palm) /datum/martial_art/adminfu/harm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - + MARTIAL_ARTS_ACT_CHECK if(!D.stat)//do not kill what is dead... A.do_attack_animation(D) D.visible_message("[A] manifests a large glowing toolbox and shoves it in [D]'s chest!", \ "[A] shoves a mystical toolbox in your chest!") D.death() - return 1 + return TRUE /datum/martial_art/adminfu/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) + MARTIAL_ARTS_ACT_CHECK A.do_attack_animation(D) D.Weaken(25) D.Stun(25) - return 1 + return TRUE /datum/martial_art/adminfu/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - add_to_streak("G",D) - if(check_streak(A,D)) - return 1 + MARTIAL_ARTS_ACT_CHECK var/obj/item/grab/G = D.grabbedby(A,1) if(G) G.state = GRAB_NECK + return TRUE -/datum/martial_art/adminfu/help_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - add_to_streak("P",D) - if(check_streak(A,D)) - return 1 - -/datum/martial_art/adminfu/proc/healPalm(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - A.do_attack_animation(D) - D.visible_message("[A] smacks [D] in the forehead!") - - //its the staff of healing code..hush - if(istype(D,/mob)) - var/old_stat = D.stat - if(isanimal(D) && D.stat == DEAD) - var/mob/living/simple_animal/O = D - var/mob/living/simple_animal/P = new O.type(O.loc) - P.real_name = O.real_name - P.name = O.name - if(O.mind) - O.mind.transfer_to(P) - else - P.key = O.key - qdel(O) - D = P - else - D.revive() - D.suiciding = 0 - if(!D.ckey) - for(var/mob/dead/observer/ghost in GLOB.player_list) - if(D.real_name == ghost.real_name) - ghost.reenter_corpse() - break - if(old_stat != DEAD) - to_chat(D, "You feel great!") - else - to_chat(D, "You rise with a start, you're alive!!!") - return 1 - -/mob/living/carbon/human/proc/adminfu_help() - set name = "Recall Teachings" - set desc = "Remember the way of the dancing admin." - set category = "Adminfu" - - to_chat(usr, "Grab: Automatic Neck Grab.") - to_chat(usr, "Disarm: Stun/weaken") - to_chat(usr, "Harm: Death.") - to_chat(usr, "Healing Palm::Combo:Grab,Help intent. Heals or revives a crature.") - +/datum/martial_art/adminfu/explaination_header(user) + to_chat(user, "Grab: Automatic Neck Grab.") + to_chat(user, "Disarm: Stun/weaken") + to_chat(user, "Harm: Death.") /obj/item/adminfu_scroll name = "frayed scroll" diff --git a/code/modules/martial_arts/combos/adminfu/healing_palm.dm b/code/modules/martial_arts/combos/adminfu/healing_palm.dm new file mode 100644 index 00000000000..c5bd34f1886 --- /dev/null +++ b/code/modules/martial_arts/combos/adminfu/healing_palm.dm @@ -0,0 +1,38 @@ +/datum/martial_combo/adminfu/healing_palm + name = "Healing Palm" + steps = list(MARTIAL_COMBO_STEP_GRAB, MARTIAL_COMBO_STEP_HELP) + explaination_text = "Heals or revives a creature." + combo_text_override = "Grab, switch hands, Help" + +/datum/martial_combo/adminfu/healing_palm/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + user.do_attack_animation(target) + target.visible_message("[user] smacks [target] in the forehead!") + + //its the staff of healing code..hush + if(istype(target,/mob)) + var/old_stat = target.stat + if(isanimal(target) && target.stat == DEAD) + var/mob/living/simple_animal/O = target + var/mob/living/simple_animal/P = new O.type(O.loc) + P.real_name = O.real_name + P.name = O.name + if(O.mind) + O.mind.transfer_to(P) + else + P.key = O.key + qdel(O) + target = P + else + target.revive() + target.suiciding = 0 + if(!target.ckey) + for(var/mob/dead/observer/ghost in GLOB.player_list) + if(target.real_name == ghost.real_name) + ghost.reenter_corpse() + break + if(old_stat != DEAD) + to_chat(target, "You feel great!") + else + to_chat(target, "You rise with a start, you're alive!!!") + return MARTIAL_COMBO_DONE + return MARTIAL_COMBO_FAIL diff --git a/code/modules/martial_arts/combos/cqc/consecutive.dm b/code/modules/martial_arts/combos/cqc/consecutive.dm new file mode 100644 index 00000000000..b513c8191e4 --- /dev/null +++ b/code/modules/martial_arts/combos/cqc/consecutive.dm @@ -0,0 +1,18 @@ +/datum/martial_combo/cqc/consecutive + name = "Consecutive CQC" + steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM) + explaination_text = "Mainly offensive move, huge damage and decent stamina damage." + +/datum/martial_combo/cqc/consecutive/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + if(!target.stat) + target.visible_message("[user] strikes [target]'s abdomen, neck and back consecutively", \ + "[user] strikes your abdomen, neck and back consecutively!") + playsound(get_turf(target), 'sound/weapons/cqchit2.ogg', 50, 1, -1) + var/obj/item/I = target.get_active_hand() + if(I && target.drop_item()) + user.put_in_hands(I) + target.adjustStaminaLoss(50) + target.apply_damage(25, BRUTE) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Consecutive", ATKLOG_ALL) + return MARTIAL_COMBO_DONE + return MARTIAL_COMBO_FAIL diff --git a/code/modules/martial_arts/combos/cqc/kick.dm b/code/modules/martial_arts/combos/cqc/kick.dm new file mode 100644 index 00000000000..887a51b049d --- /dev/null +++ b/code/modules/martial_arts/combos/cqc/kick.dm @@ -0,0 +1,24 @@ +/datum/martial_combo/cqc/kick + name = "CQC Kick" + steps = list(MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_HARM) + explaination_text = "Knocks opponent away. Knocks out stunned or knocked down opponents." + +/datum/martial_combo/cqc/kick/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + . = MARTIAL_COMBO_FAIL + if(!target.stat || !target.IsWeakened()) + target.visible_message("[user] kicks [target] back!", \ + "[user] kicks you back!") + playsound(get_turf(user), 'sound/weapons/cqchit1.ogg', 50, 1, -1) + var/atom/throw_target = get_edge_target_turf(target, user.dir) + target.throw_at(throw_target, 1, 14, user) + target.apply_damage(10, BRUTE) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Kick", ATKLOG_ALL) + . = MARTIAL_COMBO_DONE + if(target.IsWeakened() && !target.stat) + target.visible_message("[user] kicks [target]'s head, knocking [target.p_them()] out!", \ + "[user] kicks your head, knocking you out!") + playsound(get_turf(user), 'sound/weapons/genhit1.ogg', 50, 1, -1) + target.SetSleeping(15) + target.adjustBrainLoss(15) + add_attack_logs(user, target, "Knocked out with martial-art [src] : Kick", ATKLOG_ALL) + . = MARTIAL_COMBO_DONE diff --git a/code/modules/martial_arts/combos/cqc/pressure.dm b/code/modules/martial_arts/combos/cqc/pressure.dm new file mode 100644 index 00000000000..b19bfb6ddf7 --- /dev/null +++ b/code/modules/martial_arts/combos/cqc/pressure.dm @@ -0,0 +1,11 @@ +/datum/martial_combo/cqc/pressure + name = "Pressure" + steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_GRAB) + explaination_text = "Decent stamina damage." + +/datum/martial_combo/cqc/pressure/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + target.visible_message("[user] forces their arm on [target]'s neck!") + target.adjustStaminaLoss(60) + playsound(get_turf(user), 'sound/weapons/cqchit1.ogg', 50, 1, -1) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Pressure", ATKLOG_ALL) + return MARTIAL_COMBO_DONE diff --git a/code/modules/martial_arts/combos/cqc/restrain.dm b/code/modules/martial_arts/combos/cqc/restrain.dm new file mode 100644 index 00000000000..e9495c61dba --- /dev/null +++ b/code/modules/martial_arts/combos/cqc/restrain.dm @@ -0,0 +1,22 @@ +/datum/martial_combo/cqc/restrain + name = "Restrain" + steps = list(MARTIAL_COMBO_STEP_GRAB, MARTIAL_COMBO_STEP_GRAB) + explaination_text = "Locks opponents into a restraining position, disarm to knock them out with a choke hold." + combo_text_override = "Grab, switch hands, Grab" + +/datum/martial_combo/cqc/restrain/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + var/datum/martial_art/cqc/CQC = MA + if(!istype(CQC)) + return MARTIAL_COMBO_FAIL + if(CQC.restraining) + return MARTIAL_COMBO_FAIL + if(!target.stat) + target.visible_message("[user] locks [target] into a restraining position!", \ + "[user] locks you into a restraining position!") + target.adjustStaminaLoss(20) + target.Stun(5) + CQC.restraining = TRUE + addtimer(CALLBACK(CQC, /datum/martial_art/cqc/.proc/drop_restraining), 50, TIMER_UNIQUE) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Restrain", ATKLOG_ALL) + return MARTIAL_COMBO_DONE + return MARTIAL_COMBO_FAIL diff --git a/code/modules/martial_arts/combos/cqc/slam.dm b/code/modules/martial_arts/combos/cqc/slam.dm new file mode 100644 index 00000000000..8c1a84d7817 --- /dev/null +++ b/code/modules/martial_arts/combos/cqc/slam.dm @@ -0,0 +1,16 @@ +/datum/martial_combo/cqc/slam + name = "Slam" + steps = list(MARTIAL_COMBO_STEP_GRAB, MARTIAL_COMBO_STEP_HARM) + explaination_text = "Slam opponent into the ground, knocking them down." + combo_text_override = "Grab, switch hands, Harm" + +/datum/martial_combo/cqc/slam/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + if(!target.IsWeakened() && !target.resting && !target.lying) + target.visible_message("[user] slams [target] into the ground!", \ + "[user] slams you into the ground!") + playsound(get_turf(user), 'sound/weapons/slam.ogg', 50, 1, -1) + target.apply_damage(10, BRUTE) + target.Weaken(6) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Slam", ATKLOG_ALL) + return MARTIAL_COMBO_DONE + return MARTIAL_COMBO_FAIL diff --git a/code/modules/martial_arts/combos/krav_maga/leg_sweep.dm b/code/modules/martial_arts/combos/krav_maga/leg_sweep.dm new file mode 100644 index 00000000000..0defb3e9eff --- /dev/null +++ b/code/modules/martial_arts/combos/krav_maga/leg_sweep.dm @@ -0,0 +1,14 @@ +/datum/martial_combo/krav_maga/leg_sweep + name = "Leg Sweep" + explaination_text = "Trips the victim, rendering them prone and unable to move for a short time." + +/datum/martial_combo/krav_maga/leg_sweep/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + if(target.stat || target.IsWeakened()) + return FALSE + target.visible_message("[user] leg sweeps [target]!", \ + "[user] leg sweeps you!") + playsound(get_turf(user), 'sound/effects/hit_kick.ogg', 50, 1, -1) + target.apply_damage(5, BRUTE) + target.Weaken(2) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Leg Sweep", ATKLOG_ALL) + return MARTIAL_COMBO_DONE_CLEAR_COMBOS diff --git a/code/modules/martial_arts/combos/krav_maga/lung_punch.dm b/code/modules/martial_arts/combos/krav_maga/lung_punch.dm new file mode 100644 index 00000000000..e71289f9174 --- /dev/null +++ b/code/modules/martial_arts/combos/krav_maga/lung_punch.dm @@ -0,0 +1,12 @@ +/datum/martial_combo/krav_maga/lung_punch + name = "Lung Punch" + explaination_text = "Delivers a strong punch just above the victim's abdomen, constraining the lungs. The victim will be unable to breathe for a short time." + +/datum/martial_combo/krav_maga/lung_punch/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + target.visible_message("[user] pounds [target] on the chest!", \ + "[user] slams your chest! You can't breathe!") + playsound(get_turf(user), 'sound/effects/hit_punch.ogg', 50, 1, -1) + target.AdjustLoseBreath(5) + target.adjustOxyLoss(10) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Lung Punch", ATKLOG_ALL) + return MARTIAL_COMBO_DONE_CLEAR_COMBOS diff --git a/code/modules/martial_arts/combos/krav_maga/neck_chop.dm b/code/modules/martial_arts/combos/krav_maga/neck_chop.dm new file mode 100644 index 00000000000..c0e2f71b6a5 --- /dev/null +++ b/code/modules/martial_arts/combos/krav_maga/neck_chop.dm @@ -0,0 +1,12 @@ +/datum/martial_combo/krav_maga/neck_chop + name = "Neck Chop" + explaination_text = "Injures the neck, stopping the victim from speaking for a while." + +/datum/martial_combo/krav_maga/neck_chop/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + target.visible_message("[user] karate chops [target]'s neck!", \ + "[user] karate chops your neck, rendering you unable to speak for a short time!") + playsound(get_turf(user), 'sound/effects/hit_punch.ogg', 50, 1, -1) + target.apply_damage(5, BRUTE) + target.AdjustSilence(10) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Neck Chop", ATKLOG_ALL) + return MARTIAL_COMBO_DONE_CLEAR_COMBOS diff --git a/code/modules/martial_arts/combos/martial_combo.dm b/code/modules/martial_arts/combos/martial_combo.dm new file mode 100644 index 00000000000..6b4eedb97dc --- /dev/null +++ b/code/modules/martial_arts/combos/martial_combo.dm @@ -0,0 +1,36 @@ +/datum/martial_combo + /// Name used to explain the combo + var/name = "Code Fu" + /// Which steps need to be performed + var/list/steps + /// What index to check + var/current_step_index = 1 + /// Who is the target the combo is being executed on + var/current_combo_target = null + /// If you require to do the combo's on the same target + var/combos_require_same_target = TRUE + /// What does it do + var/explaination_text = "Ability to break shit" + /// How to do the combo. If null it'll auto generate it from the steps + var/combo_text_override + +/datum/martial_combo/proc/check_combo(step, mob/living/target) + if(!combos_require_same_target || current_combo_target == null || current_combo_target == target) + if(!length(steps) || step == steps[current_step_index]) + return TRUE + return FALSE + +/datum/martial_combo/proc/progress_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + current_combo_target = target + if(current_step_index++ >= LAZYLEN(steps)) + return perform_combo(user, target, MA) + return MARTIAL_COMBO_CONTINUE + +/datum/martial_combo/proc/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + return MARTIAL_COMBO_FAIL // Override this + +/datum/martial_combo/proc/give_explaination(user) + var/final_combo_text = combo_text_override + if(!final_combo_text) + final_combo_text = english_list(steps, and_text = " ", comma_text = " ") + to_chat(user, "[name]: [final_combo_text]. [explaination_text]") diff --git a/code/modules/martial_arts/combos/mimejutsu/mimechucks.dm b/code/modules/martial_arts/combos/mimejutsu/mimechucks.dm new file mode 100644 index 00000000000..b7f2d247b4b --- /dev/null +++ b/code/modules/martial_arts/combos/mimejutsu/mimechucks.dm @@ -0,0 +1,26 @@ +/datum/martial_combo/mimejutsu/mimechucks + name = "Mimechucks" + steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM) + explaination_text = "Hits the opponent with invisible nunchucks." + +/datum/martial_combo/mimejutsu/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + if(!target.stat && !target.stunned && !target.IsWeakened()) + var/damage = rand(5, 8) + user.dna.species.punchdamagelow + if(!damage) + playsound(target.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + target.visible_message("[user] swings invisible nunchcuks at [target]..and misses?") + return MARTIAL_COMBO_DONE + + + var/obj/item/organ/external/affecting = target.get_organ(ran_zone(user.zone_selected)) + var/armor_block = target.run_armor_check(affecting, "melee") + + target.visible_message("[user] has hit [target] with invisible nunchucks!", \ + "[user] has hit [target] with a with invisible nunchuck!") + playsound(get_turf(user), 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + + target.apply_damage(damage, STAMINA, affecting, armor_block) + add_attack_logs(user, target, "Melee attacked with [src] (mimechuck)") + + return MARTIAL_COMBO_DONE + return MARTIAL_COMBO_DONE_BASIC_HIT diff --git a/code/modules/martial_arts/combos/mimejutsu/silent_palm.dm b/code/modules/martial_arts/combos/mimejutsu/silent_palm.dm new file mode 100644 index 00000000000..eb8df68fac4 --- /dev/null +++ b/code/modules/martial_arts/combos/mimejutsu/silent_palm.dm @@ -0,0 +1,13 @@ +/datum/martial_combo/mimejutsu/silent_palm + name = "Silent Palm" + steps = list(MARTIAL_COMBO_STEP_GRAB, MARTIAL_COMBO_STEP_DISARM) + explaination_text = "Use mime energy to throw someone back." + +/datum/martial_combo/mimejutsu/silent_palm/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + if(!target.stat && !target.stunned && !target.IsWeakened()) + target.visible_message("[user] has barely touched [target] with [user.p_their()] palm!", \ + "[user] hovers [user.p_their()] palm over your face!") + + var/atom/throw_target = get_edge_target_turf(target, get_dir(target, get_step_away(target, user))) + target.throw_at(throw_target, 200, 4, user) + return MARTIAL_COMBO_DONE_BASIC_HIT diff --git a/code/modules/martial_arts/combos/mimejutsu/smokebomb.dm b/code/modules/martial_arts/combos/mimejutsu/smokebomb.dm new file mode 100644 index 00000000000..23277fa5f0d --- /dev/null +++ b/code/modules/martial_arts/combos/mimejutsu/smokebomb.dm @@ -0,0 +1,13 @@ +/datum/martial_combo/mimejutsu/smokebomb + name = "Smokebomb" + steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_DISARM) + explaination_text = "Drops a mime smokebomb." + +/datum/martial_combo/mimejutsu/smokebomb/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + target.visible_message("[user] throws an invisible smoke bomb!!") + + var/datum/effect_system/smoke_spread/bad/smoke = new + smoke.set_up(5, 0, target.loc) + smoke.start() + + return MARTIAL_COMBO_DONE_BASIC_HIT diff --git a/code/modules/martial_arts/combos/plasma_fist/plasma_fist.dm b/code/modules/martial_arts/combos/plasma_fist/plasma_fist.dm new file mode 100644 index 00000000000..93c3e3ef480 --- /dev/null +++ b/code/modules/martial_arts/combos/plasma_fist/plasma_fist.dm @@ -0,0 +1,13 @@ +/datum/martial_combo/plasma_fist/plasma_fist + name = "The Plasma Fist" + steps = list(MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM) + explaination_text = "Knocks the brain out of the opponent and gibs their body." + +/datum/martial_combo/plasma_fist/plasma_fist/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + user.do_attack_animation(target, ATTACK_EFFECT_PUNCH) + playsound(target.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) + user.say("PLASMA FIST!") + target.visible_message("[user] has hit [target] with THE PLASMA FIST TECHNIQUE!", \ + "[user] has hit [target] with THE PLASMA FIST TECHNIQUE!") + target.gib() + return MARTIAL_COMBO_DONE diff --git a/code/modules/martial_arts/combos/plasma_fist/throwback.dm b/code/modules/martial_arts/combos/plasma_fist/throwback.dm new file mode 100644 index 00000000000..7057452a913 --- /dev/null +++ b/code/modules/martial_arts/combos/plasma_fist/throwback.dm @@ -0,0 +1,13 @@ +/datum/martial_combo/plasma_fist/throwback + name = "Throwback" + steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_DISARM) + explaination_text = "Throws the target and an item at them." + +/datum/martial_combo/plasma_fist/throwback/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + target.visible_message("[user] has hit [target] with Plasma Punch!", \ + "[user] has hit [target] with Plasma Punch!") + playsound(target.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) + var/atom/throw_target = get_edge_target_turf(target, get_dir(target, get_step_away(target, user))) + target.throw_at(throw_target, 200, 4, user) + user.say("HYAH!") + return MARTIAL_COMBO_DONE diff --git a/code/modules/martial_arts/combos/plasma_fist/tornado_sweep.dm b/code/modules/martial_arts/combos/plasma_fist/tornado_sweep.dm new file mode 100644 index 00000000000..41a7bd676d3 --- /dev/null +++ b/code/modules/martial_arts/combos/plasma_fist/tornado_sweep.dm @@ -0,0 +1,20 @@ +/datum/martial_combo/plasma_fist/tornado_sweep + name = "Tornado Sweep" + steps = list(MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_DISARM) + explaination_text = "Repulses target and everyone back." + +/datum/martial_combo/plasma_fist/tornado_sweep/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + user.say("TORNADO SWEEP!") + INVOKE_ASYNC(src, .proc/do_tornado_effect, user) + var/obj/effect/proc_holder/spell/aoe_turf/repulse/R = new(null) + var/list/turfs = list() + for(var/turf/T in range(1,user)) + turfs.Add(T) + R.cast(turfs) + return MARTIAL_COMBO_DONE + +/datum/martial_combo/plasma_fist/tornado_sweep/proc/do_tornado_effect(mob/living/carbon/human/user) + for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH)) + user.dir = i + playsound(user.loc, 'sound/weapons/punch1.ogg', 15, 1, -1) + sleep(1) diff --git a/code/modules/martial_arts/combos/sleeping_carp/back_kick.dm b/code/modules/martial_arts/combos/sleeping_carp/back_kick.dm new file mode 100644 index 00000000000..3ebaa191e23 --- /dev/null +++ b/code/modules/martial_arts/combos/sleeping_carp/back_kick.dm @@ -0,0 +1,18 @@ +/datum/martial_combo/sleeping_carp/back_kick + name = "Back Kick" + steps = list(MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_GRAB) + explaination_text = "Opponent must be facing away. Knocks down." + +/datum/martial_combo/sleeping_carp/back_kick/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + if(user.dir == target.dir && !target.stat && !target.IsWeakened()) + user.do_attack_animation(target, ATTACK_EFFECT_KICK) + target.visible_message("[user] kicks [target] in the back!", \ + "[user] kicks you in the back, making you stumble and fall!") + step_to(target,get_step(target,target.dir),1) + target.Weaken(4) + playsound(get_turf(target), 'sound/weapons/punch1.ogg', 50, 1, -1) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Back Kick", ATKLOG_ALL) + if(prob(80)) + user.say(pick("SURRPRIZU!","BACK STRIKE!","WOPAH!", "WATAAH", "ZOTA!", "Never turn your back to the enemy!")) + return MARTIAL_COMBO_DONE + return MARTIAL_COMBO_DONE_BASIC_HIT diff --git a/code/modules/martial_arts/combos/sleeping_carp/elbow_drop.dm b/code/modules/martial_arts/combos/sleeping_carp/elbow_drop.dm new file mode 100644 index 00000000000..0269c9bed4d --- /dev/null +++ b/code/modules/martial_arts/combos/sleeping_carp/elbow_drop.dm @@ -0,0 +1,18 @@ +/datum/martial_combo/sleeping_carp/elbow_drop + name = "Elbow Drop" + steps = list(MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM) + explaination_text = "Opponent must be on the ground. Deals huge damage, instantly kills anyone in critical condition." + +/datum/martial_combo/sleeping_carp/elbow_drop/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + if(target.IsWeakened() || target.resting || target.stat) + user.do_attack_animation(target, ATTACK_EFFECT_PUNCH) + target.visible_message("[user] elbow drops [target]!", \ + "[user] piledrives you with [user.p_their()] elbow!") + target.death() //FINISH HIM! + target.apply_damage(50, BRUTE, "chest") + playsound(get_turf(target), 'sound/weapons/punch1.ogg', 75, 1, -1) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Elbow Drop", ATKLOG_ALL) + if(prob(80)) + user.say(pick("BANZAIII!", "KIYAAAA!", "OMAE WA MOU SHINDEIRU!", "YOU CAN'T SEE ME!", "MY TIME IS NOW!", "COWABUNGA")) + return MARTIAL_COMBO_DONE + return MARTIAL_COMBO_DONE_BASIC_HIT diff --git a/code/modules/martial_arts/combos/sleeping_carp/head_kick.dm b/code/modules/martial_arts/combos/sleeping_carp/head_kick.dm new file mode 100644 index 00000000000..b45e10e73ce --- /dev/null +++ b/code/modules/martial_arts/combos/sleeping_carp/head_kick.dm @@ -0,0 +1,19 @@ +/datum/martial_combo/sleeping_carp/head_kick + name = "Head Kick" + steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_HARM, MARTIAL_COMBO_STEP_HARM) + explaination_text = "Decent damage, forces opponent to drop item in hand." + +/datum/martial_combo/sleeping_carp/head_kick/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + if(!target.stat && !target.IsWeakened()) + user.do_attack_animation(target, ATTACK_EFFECT_KICK) + target.visible_message("[user] kicks [target] in the head!", \ + "[user] kicks you in the jaw!") + target.apply_damage(20, BRUTE, "head") + target.drop_item() + playsound(get_turf(target), 'sound/weapons/punch1.ogg', 50, 1, -1) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Head Kick", ATKLOG_ALL) + if(prob(60)) + user.say(pick("OOHYOO!", "OOPYAH!", "HYOOAA!", "WOOAAA!", "SHURYUKICK!", "HIYAH!")) + target.Stun(4) + return MARTIAL_COMBO_DONE + return MARTIAL_COMBO_DONE_BASIC_HIT diff --git a/code/modules/martial_arts/combos/sleeping_carp/stomach_knee.dm b/code/modules/martial_arts/combos/sleeping_carp/stomach_knee.dm new file mode 100644 index 00000000000..312e575003d --- /dev/null +++ b/code/modules/martial_arts/combos/sleeping_carp/stomach_knee.dm @@ -0,0 +1,20 @@ +/datum/martial_combo/sleeping_carp/stomach_knee + name = "Stomach Knee" + steps = list(MARTIAL_COMBO_STEP_GRAB, MARTIAL_COMBO_STEP_HARM) + explaination_text = "Knocks the wind out of opponent and stuns." + combo_text_override = "Grab, switch hands, Harm" + +/datum/martial_combo/sleeping_carp/stomach_knee/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + if(!target.stat && !target.IsWeakened()) + user.do_attack_animation(target, ATTACK_EFFECT_KICK) + target.visible_message("[user] knees [target] in the stomach!", \ + "[user] winds you with a knee in the stomach!") + target.audible_message("[target] gags!") + target.AdjustLoseBreath(3) + target.Stun(2) + playsound(get_turf(target), 'sound/weapons/punch1.ogg', 50, 1, -1) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Stomach Knee", ATKLOG_ALL) + if(prob(80)) + user.say(pick("HWOP!", "KUH!", "YAKUUH!", "KYUH!", "KNEESTRIKE!")) + return MARTIAL_COMBO_DONE + return MARTIAL_COMBO_DONE_BASIC_HIT diff --git a/code/modules/martial_arts/combos/sleeping_carp/wrist_wrench.dm b/code/modules/martial_arts/combos/sleeping_carp/wrist_wrench.dm new file mode 100644 index 00000000000..38c21e7309e --- /dev/null +++ b/code/modules/martial_arts/combos/sleeping_carp/wrist_wrench.dm @@ -0,0 +1,20 @@ +/datum/martial_combo/sleeping_carp/wrist_wrench + name = "Wrist Wrench" + steps = list(MARTIAL_COMBO_STEP_DISARM, MARTIAL_COMBO_STEP_DISARM) + explaination_text = "Forces opponent to drop item in hand." + +/datum/martial_combo/sleeping_carp/wrist_wrench/perform_combo(mob/living/carbon/human/user, mob/living/target, datum/martial_art/MA) + if(!target.stat && !target.stunned && !target.IsWeakened()) + user.do_attack_animation(target, ATTACK_EFFECT_PUNCH) + target.visible_message("[user] grabs [target]'s wrist and wrenches it sideways!", \ + "[user] grabs your wrist and violently wrenches it to the side!") + playsound(get_turf(user), 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + add_attack_logs(user, target, "Melee attacked with martial-art [src] : Wrist Wrench", ATKLOG_ALL) + if(prob(60)) + user.say(pick("WRISTY TWIRLY!", "WE FIGHT LIKE MEN!", "YOU DISHONOR YOURSELF!", "POHYAH!", "WHERE IS YOUR BATON NOW?", "SAY UNCLE!")) + target.emote("scream") + target.drop_item() + target.apply_damage(5, BRUTE, pick("l_arm", "r_arm")) + target.Stun(3) + return MARTIAL_COMBO_DONE + return MARTIAL_COMBO_DONE_BASIC_HIT diff --git a/code/modules/martial_arts/cqc.dm b/code/modules/martial_arts/cqc.dm index f0b5c016c5b..49a08c1bd3c 100644 --- a/code/modules/martial_arts/cqc.dm +++ b/code/modules/martial_arts/cqc.dm @@ -1,152 +1,27 @@ -#define SLAM_COMBO "GH" -#define KICK_COMBO "HH" -#define RESTRAIN_COMBO "GG" -#define PRESSURE_COMBO "DG" -#define CONSECUTIVE_COMBO "DDH" - /datum/martial_art/cqc name = "CQC" - help_verb = /mob/living/carbon/human/proc/CQC_help block_chance = 75 - var/just_a_cook = FALSE + has_explaination_verb = TRUE + combos = list(/datum/martial_combo/cqc/slam, /datum/martial_combo/cqc/kick, /datum/martial_combo/cqc/restrain, /datum/martial_combo/cqc/pressure, /datum/martial_combo/cqc/consecutive) + var/restraining = FALSE //used in cqc's disarm_act to check if the disarmed is being restrained and so whether they should be put in a chokehold or not var/static/list/areas_under_siege = typecacheof(list(/area/crew_quarters/kitchen, /area/crew_quarters/cafeteria, /area/crew_quarters/bar)) /datum/martial_art/cqc/under_siege name = "Close Quarters Cooking" - just_a_cook = TRUE + +/datum/martial_art/cqc/under_siege/can_use(mob/living/carbon/human/H) + var/area/A = get_area(H) + if(!(is_type_in_typecache(A, areas_under_siege))) + return FALSE + return ..() /datum/martial_art/cqc/proc/drop_restraining() restraining = FALSE -/datum/martial_art/cqc/can_use(mob/living/carbon/human/H) - var/area/A = get_area(H) - if(just_a_cook && !(is_type_in_typecache(A, areas_under_siege))) - return FALSE - return ..() - -/datum/martial_art/cqc/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!can_use(A)) - return FALSE - if(findtext(streak, SLAM_COMBO)) - streak = "" - Slam(A, D) - return TRUE - if(findtext(streak, KICK_COMBO)) - streak = "" - Kick(A, D) - return TRUE - if(findtext(streak, RESTRAIN_COMBO)) - streak = "" - Restrain(A, D) - return TRUE - if(findtext(streak, PRESSURE_COMBO)) - streak = "" - Pressure(A, D) - return TRUE - if(findtext(streak, CONSECUTIVE_COMBO)) - streak = "" - Consecutive(A, D) - return FALSE - -/datum/martial_art/cqc/proc/Slam(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!can_use(A)) - return FALSE - if(!D.IsWeakened() && !D.resting && !D.lying) - D.visible_message("[A] slams [D] into the ground!", \ - "[A] slams you into the ground!") - playsound(get_turf(A), 'sound/weapons/slam.ogg', 50, 1, -1) - D.apply_damage(10, BRUTE) - D.Weaken(6) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Slam", ATKLOG_ALL) - return TRUE - streak = "" - harm_act(A, D) - streak = "" - return TRUE - -/datum/martial_art/cqc/proc/Kick(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!can_use(A)) - return FALSE - var/success = FALSE - if(!D.stat || !D.IsWeakened()) - D.visible_message("[A] kicks [D] back!", \ - "[A] kicks you back!") - playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1) - var/atom/throw_target = get_edge_target_turf(D, A.dir) - D.throw_at(throw_target, 1, 14, A) - D.apply_damage(10, BRUTE) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Kick", ATKLOG_ALL) - success = TRUE - if(D.IsWeakened() && !D.stat) - D.visible_message("[A] kicks [D]'s head, knocking [D.p_them()] out!", \ - "[A] kicks your head, knocking you out!") - playsound(get_turf(A), 'sound/weapons/genhit1.ogg', 50, 1, -1) - D.SetSleeping(15) - D.adjustBrainLoss(15) - add_attack_logs(A, D, "Knocked out with martial-art [src] : Kick", ATKLOG_ALL) - success = TRUE - if(success) - return TRUE - streak = "" - harm_act(A, D) - streak = "" - return TRUE - -/datum/martial_art/cqc/proc/Pressure(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!can_use(A)) - return FALSE - D.visible_message("[A] forces their arm on [D]'s neck!") - D.adjustStaminaLoss(60) - playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Pressure", ATKLOG_ALL) - return TRUE - -/datum/martial_art/cqc/proc/Restrain(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(restraining) - return - if(!can_use(A)) - return FALSE - if(!D.stat) - D.visible_message("[A] locks [D] into a restraining position!", \ - "[A] locks you into a restraining position!") - D.adjustStaminaLoss(20) - D.Stun(5) - restraining = TRUE - addtimer(CALLBACK(src, .proc/drop_restraining), 50, TIMER_UNIQUE) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Restrain", ATKLOG_ALL) - return TRUE - streak = "" - harm_act(A, D) - streak = "" - return TRUE - -/datum/martial_art/cqc/proc/Consecutive(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!can_use(A)) - return FALSE - if(!D.stat) - D.visible_message("[A] strikes [D]'s abdomen, neck and back consecutively", \ - "[A] strikes your abdomen, neck and back consecutively!") - playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1) - var/obj/item/I = D.get_active_hand() - if(I && D.drop_item()) - A.put_in_hands(I) - D.adjustStaminaLoss(50) - D.apply_damage(25, BRUTE) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Consecutive", ATKLOG_ALL) - return TRUE - streak = "" - harm_act(A, D) - streak = "" - return TRUE - /datum/martial_art/cqc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!can_use(A)) - return FALSE - add_to_streak("G", D) - if(check_streak(A, D)) - return TRUE + MARTIAL_ARTS_ACT_CHECK var/obj/item/grab/G = D.grabbedby(A, 1) if(G) G.state = GRAB_AGGRESSIVE //Instant aggressive grab @@ -155,11 +30,7 @@ return TRUE /datum/martial_art/cqc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!can_use(A)) - return FALSE - add_to_streak("H", D) - if(check_streak(A, D)) - return TRUE + MARTIAL_ARTS_ACT_CHECK add_attack_logs(A, D, "Melee attacked with martial-art [src]", ATKLOG_ALL) A.do_attack_animation(D) var/picked_hit_type = pick("CQC'd", "neck chopped", "gut punched", "Big Bossed") @@ -185,12 +56,7 @@ return TRUE /datum/martial_art/cqc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - if(!can_use(A)) - return FALSE - add_to_streak("D", D) - var/obj/item/I = null - if(check_streak(A, D)) - return TRUE + MARTIAL_ARTS_ACT_CHECK var/obj/item/grab/G = A.get_inactive_hand() if(restraining && istype(G) && G.affecting == D) D.visible_message("[A] puts [D] into a chokehold!", \ @@ -203,6 +69,8 @@ else restraining = FALSE + var/obj/item/I = null + if(prob(65)) if(!D.stat || !D.IsWeakened() || !restraining) I = D.get_active_hand() @@ -220,16 +88,8 @@ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Disarmed [I ? " grabbing \the [I]" : ""]", ATKLOG_ALL) return TRUE -/mob/living/carbon/human/proc/CQC_help() - set name = "Remember The Basics" - set desc = "You try to remember some of the basics of CQC." - set category = "CQC" - to_chat(usr, "You try to remember some of the basics of CQC.") +/datum/martial_art/cqc/explaination_header(user) + to_chat(user, "You try to remember some of the basics of CQC.") - to_chat(usr, "Slam: Grab, switch hands, Harm. Slam opponent into the ground, knocking them down.") - to_chat(usr, "CQC Kick: Harm Harm. Knocks opponent away. Knocks out stunned or knocked down opponents.") - to_chat(usr, "Restrain: Grab, switch hands, Grab. Locks opponents into a restraining position, disarm to knock them out with a choke hold.") - to_chat(usr, "Pressure: Disarm Grab. Decent stamina damage.") - to_chat(usr, "Consecutive CQC: Disarm Disarm Harm. Mainly offensive move, huge damage and decent stamina damage.") - - to_chat(usr, "In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter attacks done to you.") +/datum/martial_art/cqc/explaination_footer(user) + to_chat(user, "In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter attacks done to you.") diff --git a/code/modules/martial_arts/krav_maga.dm b/code/modules/martial_arts/krav_maga.dm index 3a6f484b2aa..cd3a6214e83 100644 --- a/code/modules/martial_arts/krav_maga.dm +++ b/code/modules/martial_arts/krav_maga.dm @@ -15,7 +15,9 @@ to_chat(owner, "Your next attack will be a Neck Chop.") owner.visible_message("[owner] assumes the Neck Chop stance!") var/mob/living/carbon/human/H = owner - H.martial_art.streak = "neck_chop" + H.mind.martial_art.combos.Cut() + H.mind.martial_art.combos.Add(/datum/martial_combo/krav_maga/neck_chop) + H.mind.martial_art.reset_combos() /datum/action/leg_sweep name = "Leg Sweep - Trips the victim, rendering them prone and unable to move for a short time." @@ -28,7 +30,9 @@ to_chat(owner, "Your next attack will be a Leg Sweep.") owner.visible_message("[owner] assumes the Leg Sweep stance!") var/mob/living/carbon/human/H = owner - H.martial_art.streak = "leg_sweep" + H.mind.martial_art.combos.Cut() + H.mind.martial_art.combos.Add(/datum/martial_combo/krav_maga/leg_sweep) + H.mind.martial_art.reset_combos() /datum/action/lung_punch//referred to internally as 'quick choke' name = "Lung Punch - Delivers a strong punch just above the victim's abdomen, constraining the lungs. The victim will be unable to breathe for a short time." @@ -41,7 +45,9 @@ to_chat(owner, "Your next attack will be a Lung Punch.") owner.visible_message("[owner] assumes the Lung Punch stance!") var/mob/living/carbon/human/H = owner - H.martial_art.streak = "quick_choke"//internal name for lung punch + H.mind.martial_art.combos.Cut() + H.mind.martial_art.combos.Add(/datum/martial_combo/krav_maga/lung_punch) + H.mind.martial_art.reset_combos() /datum/martial_art/krav_maga/teach(var/mob/living/carbon/human/H,var/make_temporary=0) ..() @@ -58,59 +64,8 @@ legsweep.Remove(H) lungpunch.Remove(H) -/datum/martial_art/krav_maga/proc/check_streak(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - switch(streak) - if("neck_chop") - streak = "" - neck_chop(A,D) - return 1 - if("leg_sweep") - streak = "" - leg_sweep(A,D) - return 1 - if("quick_choke")//is actually lung punch - streak = "" - quick_choke(A,D) - return 1 - return 0 - -/datum/martial_art/krav_maga/proc/leg_sweep(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(D.stat || D.IsWeakened()) - return 0 - D.visible_message("[A] leg sweeps [D]!", \ - "[A] leg sweeps you!") - playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, 1, -1) - D.apply_damage(5, BRUTE) - D.Weaken(2) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Leg Sweep", ATKLOG_ALL) - return 1 - -/datum/martial_art/krav_maga/proc/quick_choke(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)//is actually lung punch - D.visible_message("[A] pounds [D] on the chest!", \ - "[A] slams your chest! You can't breathe!") - playsound(get_turf(A), 'sound/effects/hit_punch.ogg', 50, 1, -1) - D.AdjustLoseBreath(5) - D.adjustOxyLoss(10) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Lung Punch", ATKLOG_ALL) - return 1 - -/datum/martial_art/krav_maga/proc/neck_chop(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - D.visible_message("[A] karate chops [D]'s neck!", \ - "[A] karate chops your neck, rendering you unable to speak for a short time!") - playsound(get_turf(A), 'sound/effects/hit_punch.ogg', 50, 1, -1) - D.apply_damage(5, BRUTE) - D.AdjustSilence(10) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Neck Chop", ATKLOG_ALL) - return 1 - -datum/martial_art/krav_maga/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(check_streak(A,D)) - return 1 - ..() - /datum/martial_art/krav_maga/harm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(check_streak(A,D)) - return 1 + MARTIAL_ARTS_ACT_CHECK add_attack_logs(A, D, "Melee attacked with [src]") var/picked_hit_type = pick("punches", "kicks") var/bonus_damage = 10 @@ -126,11 +81,10 @@ datum/martial_art/krav_maga/grab_act(var/mob/living/carbon/human/A, var/mob/livi playsound(get_turf(D), 'sound/effects/hit_punch.ogg', 50, 1, -1) D.visible_message("[A] [picked_hit_type] [D]!", \ "[A] [picked_hit_type] you!") - return 1 + return TRUE /datum/martial_art/krav_maga/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(check_streak(A,D)) - return 1 + MARTIAL_ARTS_ACT_CHECK if(prob(60)) if(D.hand) if(istype(D.l_hand, /obj/item)) @@ -149,7 +103,7 @@ datum/martial_art/krav_maga/grab_act(var/mob/living/carbon/human/A, var/mob/livi D.visible_message("[A] attempted to disarm [D]!", \ "[A] attempted to disarm [D]!") playsound(D, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - return 1 + return TRUE //Krav Maga Gloves diff --git a/code/modules/martial_arts/martial.dm b/code/modules/martial_arts/martial.dm index 8969463be1d..cc8c1e9933b 100644 --- a/code/modules/martial_arts/martial.dm +++ b/code/modules/martial_arts/martial.dm @@ -1,42 +1,87 @@ +#define HAS_COMBOS LAZYLEN(combos) +#define COMBO_ALIVE_TIME 5 SECONDS // How long the combo stays alive when no new attack is done + /datum/martial_art var/name = "Martial Art" var/streak = "" var/max_streak_length = 6 - var/current_target = null - var/temporary = 0 + var/temporary = FALSE var/datum/martial_art/base = null // The permanent style var/deflection_chance = 0 //Chance to deflect projectiles var/block_chance = 0 //Chance to block melee attacks using items while on throw mode. - var/restraining = 0 //used in cqc's disarm_act to check if the disarmed is being restrained and so whether they should be put in a chokehold or not var/help_verb = null var/no_guns = FALSE //set to TRUE to prevent users of this style from using guns (sleeping carp, highlander). They can still pick them up, but not fire them. var/no_guns_message = "" //message to tell the style user if they try and use a gun while no_guns = TRUE (DISHONORABRU!) -/datum/martial_art/proc/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - return 0 + var/has_explaination_verb = FALSE // If the martial art has it's own explaination verb -/datum/martial_art/proc/harm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - return 0 + var/list/combos = list() // What combos can the user do? List of combo types + var/list/datum/martial_art/current_combos = list() // What combos are currently (possibly) being performed + var/last_hit = 0 // When the last hit happened -/datum/martial_art/proc/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - return 0 +/datum/martial_art/New() + . = ..() + reset_combos() -/datum/martial_art/proc/help_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - return 0 +/datum/martial_art/proc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + return act(MARTIAL_COMBO_STEP_DISARM, A, D) + +/datum/martial_art/proc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + return act(MARTIAL_COMBO_STEP_HARM, A, D) + +/datum/martial_art/proc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + return act(MARTIAL_COMBO_STEP_GRAB, A, D) + +/datum/martial_art/proc/help_act(mob/living/carbon/human/A, mob/living/carbon/human/D) + return act(MARTIAL_COMBO_STEP_HELP, A, D) /datum/martial_art/proc/can_use(mob/living/carbon/human/H) return TRUE -/datum/martial_art/proc/add_to_streak(var/element,var/mob/living/carbon/human/D) - if(D != current_target) - current_target = D - streak = "" - streak = streak+element - if(length(streak) > max_streak_length) - streak = copytext(streak,2) - return +/datum/martial_art/proc/act(step, mob/living/carbon/human/user, mob/living/carbon/human/target) + if(!can_use(user)) + return MARTIAL_ARTS_CANNOT_USE + if(last_hit + COMBO_ALIVE_TIME < world.time) + reset_combos() + last_hit = world.time -/datum/martial_art/proc/basic_hit(var/mob/living/carbon/human/A,var/mob/living/carbon/human/D) + if(HAS_COMBOS) + return check_combos(step, user, target) + return FALSE + +/datum/martial_art/proc/reset_combos() + current_combos.Cut() + for(var/combo_type in combos) + current_combos.Add(new combo_type()) + +/datum/martial_art/proc/check_combos(step, mob/living/carbon/human/user, mob/living/carbon/human/target) + . = FALSE + for(var/thing in current_combos) + var/datum/martial_combo/MC = thing + if(!MC.check_combo(step, target)) + current_combos -= MC // It failed so remove it + else + switch(MC.progress_combo(user, target, src)) + if(MARTIAL_COMBO_FAIL) + current_combos -= MC + if(MARTIAL_COMBO_DONE_NO_CLEAR) + . = TRUE + current_combos -= MC + if(MARTIAL_COMBO_DONE) + reset_combos() + return TRUE + if(MARTIAL_COMBO_DONE_BASIC_HIT) + basic_hit(user, target) + reset_combos() + return TRUE + if(MARTIAL_COMBO_DONE_CLEAR_COMBOS) + combos.Cut() + reset_combos() + return TRUE + if(!LAZYLEN(current_combos)) + reset_combos() + +/datum/martial_art/proc/basic_hit(mob/living/carbon/human/A, mob/living/carbon/human/D) var/damage = rand(A.dna.species.punchdamagelow, A.dna.species.punchdamagehigh) var/datum/unarmed_attack/attack = A.dna.species.unarmed @@ -54,7 +99,7 @@ if(!damage) playsound(D.loc, attack.miss_sound, 25, 1, -1) D.visible_message("[A] has attempted to [atk_verb] [D]!") - return 0 + return FALSE var/obj/item/organ/external/affecting = D.get_organ(ran_zone(A.zone_selected)) var/armor_block = D.run_armor_check(affecting, "melee") @@ -74,26 +119,61 @@ D.forcesay(GLOB.hit_appends) else if(D.lying) D.forcesay(GLOB.hit_appends) - return 1 + return TRUE -/datum/martial_art/proc/teach(var/mob/living/carbon/human/H,var/make_temporary=0) - if(help_verb) - H.verbs += help_verb +/datum/martial_art/proc/teach(mob/living/carbon/human/H, make_temporary = FALSE) + if(!H.mind) + return + if(has_explaination_verb) + H.verbs |= /mob/living/carbon/human/proc/martial_arts_help if(make_temporary) - temporary = 1 + temporary = TRUE if(temporary) - if(H.martial_art) - base = H.martial_art.base + if(H.mind.martial_art) + base = H.mind.martial_art.base else base = src - H.martial_art = src + H.mind.martial_art = src /datum/martial_art/proc/remove(var/mob/living/carbon/human/H) - if(H.martial_art != src) + if(!H.mind) return - H.martial_art = base - if(help_verb) - H.verbs -= help_verb + if(H.mind.martial_art != src) + return + H.mind.martial_art = null // Remove reference + H.verbs -= /mob/living/carbon/human/proc/martial_arts_help + if(base) + base.teach(H) + +/mob/living/carbon/human/proc/martial_arts_help() + set name = "Show Info" + set desc = "Gives information about the martial arts you know." + set category = "Martial Arts" + var/mob/living/carbon/human/H = usr + if(!istype(H)) + to_chat(usr, "You shouldn't have access to this verb. Report this as a bug to the github please.") + return + H.mind.martial_art.give_explaination(H) + +/datum/martial_art/proc/give_explaination(user = usr) + explaination_header(user) + explaination_combos(user) + explaination_footer(user) + +// Put after the header and before the footer in the explaination text +/datum/martial_art/proc/explaination_combos(user) + if(HAS_COMBOS) + for(var/combo_type in combos) + var/datum/martial_combo/MC = new combo_type() + MC.give_explaination(user) + +// Put on top of the explaination text +/datum/martial_art/proc/explaination_header(user) + return + +// Put below the combos in the explaination text +/datum/martial_art/proc/explaination_footer(user) + return //ITEMS @@ -283,3 +363,6 @@ if(wielded) return ..() return 0 + +#undef HAS_COMBOS +#undef COMBO_ALIVE_TIME diff --git a/code/modules/martial_arts/mimejutsu.dm b/code/modules/martial_arts/mimejutsu.dm index 38904d407de..d57df486638 100644 --- a/code/modules/martial_arts/mimejutsu.dm +++ b/code/modules/martial_arts/mimejutsu.dm @@ -1,90 +1,16 @@ -#define MIMECHUCKS_COMBO "DH" -#define MIMESMOKE_COMBO "DD" -#define MIMEPALM_COMBO "GD" - /datum/martial_art/mimejutsu name = "Mimejutsu" - help_verb = /mob/living/carbon/human/proc/mimejutsu_help - -/datum/martial_art/mimejutsu/proc/check_streak(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(findtext(streak,MIMECHUCKS_COMBO)) - streak = "" - mimeChuck(A,D) - return 1 - if(findtext(streak,MIMESMOKE_COMBO)) - streak = "" - mimeSmoke(A,D) - return 1 - if(findtext(streak,MIMEPALM_COMBO)) - streak = "" - mimePalm(A,D) - return 1 - return 0 - -/datum/martial_art/mimejutsu/proc/mimeChuck(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(!D.stat && !D.stunned && !D.IsWeakened()) - var/damage = rand(5, 8) + A.dna.species.punchdamagelow - if(!damage) - playsound(D.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - D.visible_message("[A] swings invisible nunchcuks at [D]..and misses?") - return 0 - - - var/obj/item/organ/external/affecting = D.get_organ(ran_zone(A.zone_selected)) - var/armor_block = D.run_armor_check(affecting, "melee") - - D.visible_message("[A] has hit [D] with invisible nunchucks!", \ - "[A] has hit [D] with a with invisible nunchuck!") - playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - - D.apply_damage(damage, STAMINA, affecting, armor_block) - add_attack_logs(A, D, "Melee attacked with [src] (mimechuck)") - - return 1 - return basic_hit(A,D) - -/datum/martial_art/mimejutsu/proc/mimeSmoke(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - - D.visible_message("[A] throws an invisible smoke bomb!!") - - var/datum/effect_system/smoke_spread/bad/smoke = new - smoke.set_up(5, 0, D.loc) - smoke.start() - - return basic_hit(A,D) - -/datum/martial_art/mimejutsu/proc/mimePalm(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(!D.stat && !D.stunned && !D.IsWeakened()) - D.visible_message("[A] has barely touched [D] with [A.p_their()] palm!", \ - "[A] hovers [A.p_their()] palm over your face!") - - var/atom/throw_target = get_edge_target_turf(D, get_dir(D, get_step_away(D, A))) - D.throw_at(throw_target, 200, 4,A) - return basic_hit(A,D) - - -/datum/martial_art/mimejutsu/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - add_to_streak("D",D) - if(check_streak(A,D)) - return 1 - - return ..() + has_explaination_verb = TRUE + combos = list(/datum/martial_combo/mimejutsu/mimechucks, /datum/martial_combo/mimejutsu/smokebomb, /datum/martial_combo/mimejutsu/silent_palm) /datum/martial_art/mimejutsu/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - add_to_streak("G",D) - if(check_streak(A,D)) - return 1 - - return 1 + MARTIAL_ARTS_ACT_CHECK + return TRUE /datum/martial_art/mimejutsu/harm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - add_to_streak("H",D) - if(check_streak(A,D)) - return 1 - + MARTIAL_ARTS_ACT_CHECK A.do_attack_animation(D) - - return 1 + return TRUE /obj/item/mimejutsu_scroll name = "Mimejutsu 'scroll'" @@ -106,13 +32,5 @@ name = "beret with staple" icon_state = "beret" -/mob/living/carbon/human/proc/mimejutsu_help() - set name = "Recall Ancient Mimeing" - set desc = "Remember the martial techniques of Mimejutsu." - set category = "Mimejutsu" - - to_chat(usr, "You make a invisible box around yourself and recall the teachings of Mimejutsu...") - - to_chat(usr, "Mimechucks: Disarm Harm. Hits the opponent with invisible nunchucks.") - to_chat(usr, "Smokebomb: Disarm Disarm. Drops a mime smokebomb.") - to_chat(usr, "Silent Palm: Grab Disarm. Using mime energy throw someone back.") +/datum/martial_art/mimejutsu/explaination_header(user) + to_chat(user, "You make a invisible box around yourself and recall the teachings of Mimejutsu...") diff --git a/code/modules/martial_arts/plasma_fist.dm b/code/modules/martial_arts/plasma_fist.dm index ea5aef5ea74..a216a5d730f 100644 --- a/code/modules/martial_arts/plasma_fist.dm +++ b/code/modules/martial_arts/plasma_fist.dm @@ -1,86 +1,22 @@ -#define TORNADO_COMBO "HHD" -#define THROWBACK_COMBO "DHD" -#define PLASMA_COMBO "HDDDH" - /datum/martial_art/plasma_fist name = "Plasma Fist" - help_verb = /mob/living/carbon/human/proc/plasma_fist_help - - -/datum/martial_art/plasma_fist/proc/check_streak(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(findtext(streak,TORNADO_COMBO)) - streak = "" - Tornado(A,D) - return 1 - if(findtext(streak,THROWBACK_COMBO)) - streak = "" - Throwback(A,D) - return 1 - if(findtext(streak,PLASMA_COMBO)) - streak = "" - Plasma(A,D) - return 1 - return 0 - -/datum/martial_art/plasma_fist/proc/Tornado(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - A.say("TORNADO SWEEP!") - spawn(0) - for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH)) - A.dir = i - playsound(A.loc, 'sound/weapons/punch1.ogg', 15, 1, -1) - sleep(1) - var/obj/effect/proc_holder/spell/aoe_turf/repulse/R = new(null) - var/list/turfs = list() - for(var/turf/T in range(1,A)) - turfs.Add(T) - R.cast(turfs) - return - -/datum/martial_art/plasma_fist/proc/Throwback(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - D.visible_message("[A] has hit [D] with Plasma Punch!", \ - "[A] has hit [D] with Plasma Punch!") - playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) - var/atom/throw_target = get_edge_target_turf(D, get_dir(D, get_step_away(D, A))) - D.throw_at(throw_target, 200, 4,A) - A.say("HYAH!") - return - -/datum/martial_art/plasma_fist/proc/Plasma(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1) - A.say("PLASMA FIST!") - D.visible_message("[A] has hit [D] with THE PLASMA FIST TECHNIQUE!", \ - "[A] has hit [D] with THE PLASMA FIST TECHNIQUE!") - D.gib() - return + combos = list(/datum/martial_combo/plasma_fist/tornado_sweep, /datum/martial_combo/plasma_fist/throwback, /datum/martial_combo/plasma_fist/plasma_fist) + has_explaination_verb = TRUE /datum/martial_art/plasma_fist/harm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - add_to_streak("H",D) - if(check_streak(A,D)) - return 1 + MARTIAL_ARTS_ACT_CHECK basic_hit(A,D) - return 1 + return TRUE /datum/martial_art/plasma_fist/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - add_to_streak("D",D) - if(check_streak(A,D)) - return 1 + MARTIAL_ARTS_ACT_CHECK basic_hit(A,D) - return 1 + return TRUE /datum/martial_art/plasma_fist/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - add_to_streak("G",D) - if(check_streak(A,D)) - return 1 + MARTIAL_ARTS_ACT_CHECK basic_hit(A,D) - return 1 + return TRUE -/mob/living/carbon/human/proc/plasma_fist_help() - set name = "Recall Teachings" - set desc = "Remember the martial techniques of the Plasma Fist." - set category = "Plasma Fist" - - to_chat(usr, "You clench your fists and have a flashback of knowledge...") - to_chat(usr, "Tornado Sweep: Harm Harm Disarm. Repulses target and everyone back.") - to_chat(usr, "Throwback: Disarm Harm Disarm. Throws the target and an item at them.") - to_chat(usr, "The Plasma Fist: Harm Disarm Disarm Disarm Harm. Knocks the brain out of the opponent and gibs their body.") +/datum/martial_art/plasma_fist/explaination_header(user) + to_chat(user, "You clench your fists and have a flashback of knowledge...") diff --git a/code/modules/martial_arts/sleeping_carp.dm b/code/modules/martial_arts/sleeping_carp.dm index 472fb6ca506..9ca070f8bfd 100644 --- a/code/modules/martial_arts/sleeping_carp.dm +++ b/code/modules/martial_arts/sleeping_carp.dm @@ -1,126 +1,21 @@ //Used by the gang of the same name. Uses combos. Basic attacks bypass armor and never miss -#define WRIST_WRENCH_COMBO "DD" -#define BACK_KICK_COMBO "HG" -#define STOMACH_KNEE_COMBO "GH" -#define HEAD_KICK_COMBO "DHH" -#define ELBOW_DROP_COMBO "HDHDH" /datum/martial_art/the_sleeping_carp name = "The Sleeping Carp" deflection_chance = 100 - help_verb = /mob/living/carbon/human/proc/sleeping_carp_help no_guns = TRUE no_guns_message = "Use of ranged weaponry would bring dishonor to the clan." - -/datum/martial_art/the_sleeping_carp/proc/check_streak(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(findtext(streak,WRIST_WRENCH_COMBO)) - streak = "" - wristWrench(A,D) - return 1 - if(findtext(streak,BACK_KICK_COMBO)) - streak = "" - backKick(A,D) - return 1 - if(findtext(streak,STOMACH_KNEE_COMBO)) - streak = "" - kneeStomach(A,D) - return 1 - if(findtext(streak,HEAD_KICK_COMBO)) - streak = "" - headKick(A,D) - return 1 - if(findtext(streak,ELBOW_DROP_COMBO)) - streak = "" - elbowDrop(A,D) - return 1 - return 0 - -/datum/martial_art/the_sleeping_carp/proc/wristWrench(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(!D.stat && !D.stunned && !D.IsWeakened()) - A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - D.visible_message("[A] grabs [D]'s wrist and wrenches it sideways!", \ - "[A] grabs your wrist and violently wrenches it to the side!") - playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Wrist Wrench", ATKLOG_ALL) - if(prob(60)) - A.say(pick("WRISTY TWIRLY!", "WE FIGHT LIKE MEN!", "YOU DISHONOR YOURSELF!", "POHYAH!", "WHERE IS YOUR BATON NOW?", "SAY UNCLE!")) - D.emote("scream") - D.drop_item() - D.apply_damage(5, BRUTE, pick("l_arm", "r_arm")) - D.Stun(3) - return 1 - return basic_hit(A,D) - -/datum/martial_art/the_sleeping_carp/proc/backKick(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(A.dir == D.dir && !D.stat && !D.IsWeakened()) - A.do_attack_animation(D, ATTACK_EFFECT_KICK) - D.visible_message("[A] kicks [D] in the back!", \ - "[A] kicks you in the back, making you stumble and fall!") - step_to(D,get_step(D,D.dir),1) - D.Weaken(4) - playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Back Kick", ATKLOG_ALL) - if(prob(80)) - A.say(pick("SURRPRIZU!","BACK STRIKE!","WOPAH!", "WATAAH", "ZOTA!", "Never turn your back to the enemy!")) - return 1 - return basic_hit(A,D) - -/datum/martial_art/the_sleeping_carp/proc/kneeStomach(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(!D.stat && !D.IsWeakened()) - A.do_attack_animation(D, ATTACK_EFFECT_KICK) - D.visible_message("[A] knees [D] in the stomach!", \ - "[A] winds you with a knee in the stomach!") - D.audible_message("[D] gags!") - D.AdjustLoseBreath(3) - D.Stun(2) - playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Stomach Knee", ATKLOG_ALL) - if(prob(80)) - A.say(pick("HWOP!", "KUH!", "YAKUUH!", "KYUH!", "KNEESTRIKE!")) - return 1 - return basic_hit(A,D) - -/datum/martial_art/the_sleeping_carp/proc/headKick(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(!D.stat && !D.IsWeakened()) - A.do_attack_animation(D, ATTACK_EFFECT_KICK) - D.visible_message("[A] kicks [D] in the head!", \ - "[A] kicks you in the jaw!") - D.apply_damage(20, BRUTE, "head") - D.drop_item() - playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Head Kick", ATKLOG_ALL) - if(prob(60)) - A.say(pick("OOHYOO!", "OOPYAH!", "HYOOAA!", "WOOAAA!", "SHURYUKICK!", "HIYAH!")) - D.Stun(4) - return 1 - return basic_hit(A,D) - -/datum/martial_art/the_sleeping_carp/proc/elbowDrop(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - if(D.IsWeakened() || D.resting || D.stat) - A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) - D.visible_message("[A] elbow drops [D]!", \ - "[A] piledrives you with [A.p_their()] elbow!") - if(D.stat) - D.death() //FINISH HIM! - D.apply_damage(50, BRUTE, "chest") - playsound(get_turf(D), 'sound/weapons/punch1.ogg', 75, 1, -1) - add_attack_logs(A, D, "Melee attacked with martial-art [src] : Elbow Drop", ATKLOG_ALL) - if(prob(80)) - A.say(pick("BANZAIII!", "KIYAAAA!", "OMAE WA MOU SHINDEIRU!", "YOU CAN'T SEE ME!", "MY TIME IS NOW!", "COWABUNGA")) - return 1 - return basic_hit(A,D) + has_explaination_verb = TRUE + combos = list(/datum/martial_combo/sleeping_carp/wrist_wrench, /datum/martial_combo/sleeping_carp/back_kick, /datum/martial_combo/sleeping_carp/stomach_knee, /datum/martial_combo/sleeping_carp/head_kick, /datum/martial_combo/sleeping_carp/elbow_drop) /datum/martial_art/the_sleeping_carp/grab_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - add_to_streak("G",D) - if(check_streak(A,D)) - return 1 + MARTIAL_ARTS_ACT_CHECK var/obj/item/grab/G = D.grabbedby(A,1) if(G) G.state = GRAB_AGGRESSIVE //Instant aggressive grab + return TRUE /datum/martial_art/the_sleeping_carp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D) - add_to_streak("H",D) - if(check_streak(A,D)) - return 1 + MARTIAL_ARTS_ACT_CHECK A.do_attack_animation(D, ATTACK_EFFECT_PUNCH) var/atk_verb = pick("punches", "kicks", "chops", "hits", "slams") D.visible_message("[A] [atk_verb] [D]!", \ @@ -132,24 +27,7 @@ if(prob(D.getBruteLoss()) && !D.lying) D.visible_message("[D] stumbles and falls!", "The blow sends you to the ground!") D.Weaken(4) - return 1 - - -/datum/martial_art/the_sleeping_carp/disarm_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D) - add_to_streak("D",D) - if(check_streak(A,D)) - return 1 - return ..() - -/mob/living/carbon/human/proc/sleeping_carp_help() - set name = "Recall Teachings" - set desc = "Remember the martial techniques of the Sleeping Carp clan." - set category = "Sleeping Carp" + return TRUE +/datum/martial_art/the_sleeping_carp/explaination_header(user) to_chat(usr, "You retreat inward and recall the teachings of the Sleeping Carp...") - - to_chat(usr, "Wrist Wrench: Disarm Disarm. Forces opponent to drop item in hand.") - to_chat(usr, "Back Kick: Harm Grab. Opponent must be facing away. Knocks down.") - to_chat(usr, "Stomach Knee: Grab Harm. Knocks the wind out of opponent and stuns.") - to_chat(usr, "Head Kick: Disarm Harm Harm. Decent damage, forces opponent to drop item in hand.") - to_chat(usr, "Elbow Drop: Harm Disarm Harm Disarm Harm. Opponent must be on the ground. Deals huge damage, instantly kills anyone in critical condition.") diff --git a/code/modules/martial_arts/wrestleing.dm b/code/modules/martial_arts/wrestling.dm similarity index 100% rename from code/modules/martial_arts/wrestleing.dm rename to code/modules/martial_arts/wrestling.dm diff --git a/code/modules/mining/abandonedcrates.dm b/code/modules/mining/abandonedcrates.dm index 9b74c922a6b..044b78a04bc 100644 --- a/code/modules/mining/abandonedcrates.dm +++ b/code/modules/mining/abandonedcrates.dm @@ -82,7 +82,7 @@ new /obj/item/clothing/head/corgi(src) if(67 to 68) for(var/i in 1 to rand(4, 7)) - var /newitem = pick(subtypesof(/obj/item/stock_parts)) + var/newitem = pick(subtypesof(/obj/item/stock_parts)) new newitem(src) if(69 to 70) new /obj/item/stack/ore/bluespace_crystal(src, 5) diff --git a/code/modules/mining/equipment/wormhole_jaunter.dm b/code/modules/mining/equipment/wormhole_jaunter.dm index 43cce5904e3..f9a98b01483 100644 --- a/code/modules/mining/equipment/wormhole_jaunter.dm +++ b/code/modules/mining/equipment/wormhole_jaunter.dm @@ -11,6 +11,7 @@ throw_range = 5 origin_tech = "bluespace=2" slot_flags = SLOT_BELT + var/emagged = FALSE /obj/item/wormhole_jaunter/attack_self(mob/user) user.visible_message("[user.name] activates the [name]!") @@ -43,6 +44,7 @@ return var/chosen_beacon = pick(L) var/obj/effect/portal/jaunt_tunnel/J = new(get_turf(src), get_turf(chosen_beacon), src, 100) + J.emagged = emagged if(adjacent) try_move_adjacent(J) else @@ -57,12 +59,26 @@ else to_chat(user, "[src] is not attached to your belt, preventing it from saving you from the chasm. RIP.") +/obj/item/wormhole_jaunter/emag_act(mob/user) + if(!emagged) + emagged = TRUE + to_chat(user, "You emag [src].") + var/turf/T = get_turf(src) + do_sparks(5, 0, T) + playsound(T, "sparks", 50, 1) + /obj/effect/portal/jaunt_tunnel name = "jaunt tunnel" icon = 'icons/effects/effects.dmi' icon_state = "bhole3" desc = "A stable hole in the universe made by a wormhole jaunter. Turbulent doesn't even begin to describe how rough passage through one of these is, but at least it will always get you somewhere near a beacon." failchance = 0 + var/emagged = FALSE + +/obj/effect/portal/jaunt_tunnel/can_teleport(atom/movable/M) + if(!emagged && ismegafauna(M)) + return FALSE + return ..() /obj/effect/portal/jaunt_tunnel/teleport(atom/movable/M) . = ..() diff --git a/code/modules/mining/laborcamp/laborshuttle.dm b/code/modules/mining/laborcamp/laborshuttle.dm index 8e585eb15b0..88b0a6601dc 100644 --- a/code/modules/mining/laborcamp/laborshuttle.dm +++ b/code/modules/mining/laborcamp/laborshuttle.dm @@ -13,15 +13,3 @@ possible_destinations = "laborcamp_away" circuit = /obj/item/circuitboard/labor_shuttle/one_way req_access = list( ) - -/obj/machinery/computer/shuttle/labor/one_way/Topic(href, href_list) - if(href_list["move"]) - var/obj/docking_port/mobile/M = SSshuttle.getShuttle("laborcamp") - if(!M) - to_chat(usr, "Cannot locate shuttle!") - return 0 - var/obj/docking_port/stationary/S = M.get_docked() - if(S && S.name == "laborcamp_away") - to_chat(usr, "Shuttle is already at the outpost!") - return 0 - ..() diff --git a/code/modules/mining/lavaland/loot/bubblegum_loot.dm b/code/modules/mining/lavaland/loot/bubblegum_loot.dm index fa7791edb9e..4ee2c76a419 100644 --- a/code/modules/mining/lavaland/loot/bubblegum_loot.dm +++ b/code/modules/mining/lavaland/loot/bubblegum_loot.dm @@ -82,7 +82,7 @@ B.mineEffect(L) for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H == L) + if(H.stat == DEAD || H == L) continue to_chat(H, "You have an overwhelming desire to kill [L]. [L.p_they(TRUE)] [L.p_have()] been marked red! Go kill [L.p_them()]!") H.put_in_hands(new /obj/item/kitchen/knife/butcher(H)) diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm index 0bde4d95464..e99e7132f03 100644 --- a/code/modules/mining/lavaland/loot/colossus_loot.dm +++ b/code/modules/mining/lavaland/loot/colossus_loot.dm @@ -349,7 +349,7 @@ harm_intent_damage = 1 friendly = "mends" density = 0 - flying = 1 + flying = TRUE obj_damage = 0 pass_flags = PASSTABLE | PASSGRILLE | PASSMOB ventcrawler = 2 diff --git a/code/modules/mining/lavaland/loot/tendril_loot.dm b/code/modules/mining/lavaland/loot/tendril_loot.dm index f54bc974f7e..f7a555f104b 100644 --- a/code/modules/mining/lavaland/loot/tendril_loot.dm +++ b/code/modules/mining/lavaland/loot/tendril_loot.dm @@ -358,6 +358,7 @@ projectile_type = /obj/item/projectile/hook caliber = "hook" icon_state = "hook" + muzzle_flash_effect = null /obj/item/projectile/hook name = "hook" @@ -383,7 +384,10 @@ var/mob/living/L = target if(!L.anchored) L.visible_message("[L] is snagged by [firer]'s hook!") + var/old_density = L.density + L.density = FALSE // Ensures the hook does not hit the target multiple times L.forceMove(get_turf(firer)) + L.density = old_density /obj/item/projectile/hook/Destroy() QDEL_NULL(chain) diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm index fd675bac32c..c2da907df61 100644 --- a/code/modules/mining/machine_vending.dm +++ b/code/modules/mining/machine_vending.dm @@ -1,104 +1,21 @@ -/**********************Mining Equipment Locker**************************/ +// Use this define to register something as a purchasable! +// * n — The proper name of the purchasable +// * o — The object type path of the purchasable to spawn +// * p — The price of the purchasable in mining points +#define EQUIPMENT(n, o, p) n = new /datum/data/mining_equipment(n, o, p) + +/**********************Mining Equipment Vendor**************************/ /obj/machinery/mineral/equipment_vendor name = "mining equipment vendor" desc = "An equipment vendor for miners, points collected at an ore redemption machine can be spent here." icon = 'icons/obj/machines/mining_machines.dmi' icon_state = "mining" - density = 1 - anchored = 1.0 + density = TRUE + anchored = TRUE var/obj/item/card/id/inserted_id - var/list/prize_list = list( //if you add something to this, please, for the love of god, sort it by price/type. use tabs and not spaces. - new /datum/data/mining_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 10), - new /datum/data/mining_equipment("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 100), - new /datum/data/mining_equipment("30 Marker Beacons", /obj/item/stack/marker_beacon/thirty, 300), - new /datum/data/mining_equipment("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 100), - new /datum/data/mining_equipment("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 100), - new /datum/data/mining_equipment("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 150), - new /datum/data/mining_equipment("Soap", /obj/item/soap/nanotrasen, 200), - new /datum/data/mining_equipment("Laser Pointer", /obj/item/laser_pointer, 300), - new /datum/data/mining_equipment("Alien Toy", /obj/item/clothing/mask/facehugger/toy, 300), - new /datum/data/mining_equipment("Stabilizing Serum", /obj/item/hivelordstabilizer, 400), - new /datum/data/mining_equipment("Space Cash", /obj/item/stack/spacecash/c1000, 2000), - new /datum/data/mining_equipment("Point Transfer Card", /obj/item/card/mining_point_card, 500), - new /datum/data/mining_equipment("Fulton Beacon", /obj/item/fulton_core, 400), - new /datum/data/mining_equipment("Shelter Capsule", /obj/item/survivalcapsule, 400), - new /datum/data/mining_equipment("GAR Meson Scanners", /obj/item/clothing/glasses/meson/gar, 500), - new /datum/data/mining_equipment("Explorer's Webbing", /obj/item/storage/belt/mining, 500), - new /datum/data/mining_equipment("Survival Medipen", /obj/item/reagent_containers/hypospray/autoinjector/survival, 500), - new /datum/data/mining_equipment("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 600), - new /datum/data/mining_equipment("Tracking Implant Kit", /obj/item/storage/box/minertracker, 600), - new /datum/data/mining_equipment("Jaunter", /obj/item/wormhole_jaunter, 750), - new /datum/data/mining_equipment("Kinetic Crusher", /obj/item/twohanded/kinetic_crusher, 750), - new /datum/data/mining_equipment("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750), - new /datum/data/mining_equipment("Advanced Scanner", /obj/item/t_scanner/adv_mining_scanner, 800), - new /datum/data/mining_equipment("Resonator", /obj/item/resonator, 800), - new /datum/data/mining_equipment("Fulton Pack", /obj/item/extraction_pack, 1000), - new /datum/data/mining_equipment("Lazarus Injector", /obj/item/lazarus_injector, 1000), - new /datum/data/mining_equipment("Silver Pickaxe", /obj/item/pickaxe/silver, 1000), - new /datum/data/mining_equipment("Mining Conscription Kit", /obj/item/storage/backpack/duffel/mining_conscript/full, 1500), - new /datum/data/mining_equipment("Jetpack Upgrade", /obj/item/tank/jetpack/suit, 2000), - new /datum/data/mining_equipment("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000), - new /datum/data/mining_equipment("Diamond Pickaxe", /obj/item/pickaxe/diamond, 2000), - new /datum/data/mining_equipment("Super Resonator", /obj/item/resonator/upgraded, 2500), - new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 2500), - new /datum/data/mining_equipment("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000), - new /datum/data/mining_equipment("Nanotrasen Minebot", /obj/item/mining_drone_cube, 800), - new /datum/data/mining_equipment("Minebot Melee Upgrade", /obj/item/mine_bot_upgrade, 400), - new /datum/data/mining_equipment("Minebot Armor Upgrade", /obj/item/mine_bot_upgrade/health, 400), - new /datum/data/mining_equipment("Minebot Cooldown Upgrade", /obj/item/borg/upgrade/modkit/cooldown/minebot, 600), - new /datum/data/mining_equipment("Minebot AI Upgrade", /obj/item/slimepotion/sentience/mining, 1000), - new /datum/data/mining_equipment("KA Minebot Passthrough", /obj/item/borg/upgrade/modkit/minebot_passthrough, 100), - new /datum/data/mining_equipment("Lazarus Capsule", /obj/item/mobcapsule, 800), - new /datum/data/mining_equipment("Lazarus Capsule belt", /obj/item/storage/belt/lazarus, 200), - new /datum/data/mining_equipment("KA White Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer, 100), - new /datum/data/mining_equipment("KA Adjustable Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer/adjustable, 150), - new /datum/data/mining_equipment("KA Super Chassis", /obj/item/borg/upgrade/modkit/chassis_mod, 250), - new /datum/data/mining_equipment("KA Hyper Chassis", /obj/item/borg/upgrade/modkit/chassis_mod/orange, 300), - new /datum/data/mining_equipment("KA Range Increase", /obj/item/borg/upgrade/modkit/range, 1000), - new /datum/data/mining_equipment("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000), - new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000), - new /datum/data/mining_equipment("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000) - ) - -/obj/machinery/mineral/equipment_vendor/golem - name = "golem ship equipment vendor" - -/obj/machinery/mineral/equipment_vendor/golem/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/mining_equipment_vendor/golem(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stack/sheet/glass(null) - RefreshParts() - -/obj/machinery/mineral/equipment_vendor/golem/Initialize() - . = ..() - desc += "\nIt seems a few selections have been added." - prize_list += list( - new /datum/data/mining_equipment("Extra Id", /obj/item/card/id/golem, 250), - new /datum/data/mining_equipment("Science Backpack", /obj/item/storage/backpack/science, 250), - new /datum/data/mining_equipment("Full Toolbelt", /obj/item/storage/belt/utility/full/multitool, 250), - new /datum/data/mining_equipment("Monkey Cube", /obj/item/reagent_containers/food/snacks/monkeycube, 250), - new /datum/data/mining_equipment("Royal Cape of the Liberator", /obj/item/bedsheet/rd/royal_cape, 500), - new /datum/data/mining_equipment("Grey Slime Extract", /obj/item/slime_extract/grey, 1000), - new /datum/data/mining_equipment("KA Trigger Modification Kit", /obj/item/borg/upgrade/modkit/trigger_guard, 1000), - new /datum/data/mining_equipment("Shuttle Console Board", /obj/item/circuitboard/shuttle/golem_ship, 2000), - new /datum/data/mining_equipment("The Liberator's Legacy", /obj/item/storage/box/rndboards, 2000) - - ) - -/datum/data/mining_equipment - var/equipment_name = "generic" - var/equipment_path = null - var/cost = 0 - -/datum/data/mining_equipment/New(name, path, equipment_cost) - equipment_name = name - equipment_path = path - cost = equipment_cost + var/list/prize_list // Initialized just below! (if you're wondering why - check CONTRIBUTING.md, look for: "hidden" init proc) + var/dirty_items = FALSE // Used to refresh the static/redundant data in case the machine gets VV'd /obj/machinery/mineral/equipment_vendor/New() ..() @@ -110,6 +27,72 @@ component_parts += new /obj/item/stack/sheet/glass(null) RefreshParts() +/obj/machinery/mineral/equipment_vendor/Initialize(mapload) + . = ..() + prize_list = list() + prize_list["Gear"] = list( + EQUIPMENT("Advanced Scanner", /obj/item/t_scanner/adv_mining_scanner, 800), + EQUIPMENT("Explorer's Webbing", /obj/item/storage/belt/mining, 500), + EQUIPMENT("Fulton Beacon", /obj/item/fulton_core, 400), + EQUIPMENT("Mining Conscription Kit", /obj/item/storage/backpack/duffel/mining_conscript, 1500), + EQUIPMENT("Jetpack Upgrade", /obj/item/tank/jetpack/suit, 2000), + EQUIPMENT("Jump Boots", /obj/item/clothing/shoes/bhop, 2500), + EQUIPMENT("Lazarus Capsule", /obj/item/mobcapsule, 800), + EQUIPMENT("Lazarus Capsule belt", /obj/item/storage/belt/lazarus, 200), + EQUIPMENT("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000), + EQUIPMENT("Tracking Implant Kit", /obj/item/storage/box/minertracker, 600), + ) + prize_list["Consumables"] = list( + EQUIPMENT("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 100), + EQUIPMENT("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 600), + EQUIPMENT("Fulton Pack", /obj/item/extraction_pack, 1000), + EQUIPMENT("Jaunter", /obj/item/wormhole_jaunter, 750), + EQUIPMENT("Lazarus Injector", /obj/item/lazarus_injector, 1000), + EQUIPMENT("Point Transfer Card", /obj/item/card/mining_point_card, 500), + EQUIPMENT("Shelter Capsule", /obj/item/survivalcapsule, 400), + EQUIPMENT("Stabilizing Serum", /obj/item/hivelordstabilizer, 400), + EQUIPMENT("Survival Medipen", /obj/item/reagent_containers/hypospray/autoinjector/survival, 500), + ) + prize_list["Kinetic Accelerator"] = list( + EQUIPMENT("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750), + EQUIPMENT("KA Adjustable Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer/adjustable, 150), + EQUIPMENT("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000), + EQUIPMENT("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000), + EQUIPMENT("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000), + EQUIPMENT("KA Hyper Chassis", /obj/item/borg/upgrade/modkit/chassis_mod/orange, 300), + EQUIPMENT("KA Minebot Passthrough", /obj/item/borg/upgrade/modkit/minebot_passthrough, 100), + EQUIPMENT("KA Range Increase", /obj/item/borg/upgrade/modkit/range, 1000), + EQUIPMENT("KA Super Chassis", /obj/item/borg/upgrade/modkit/chassis_mod, 250), + EQUIPMENT("KA White Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer, 100), + ) + prize_list["Digging Tools"] = list( + EQUIPMENT("Diamond Pickaxe", /obj/item/pickaxe/diamond, 2000), + EQUIPMENT("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750), + EQUIPMENT("Kinetic Crusher", /obj/item/twohanded/kinetic_crusher, 750), + EQUIPMENT("Resonator", /obj/item/resonator, 800), + EQUIPMENT("Silver Pickaxe", /obj/item/pickaxe/silver, 1000), + EQUIPMENT("Super Resonator", /obj/item/resonator/upgraded, 2500), + ) + prize_list["Minebot"] = list( + EQUIPMENT("Nanotrasen Minebot", /obj/item/mining_drone_cube, 800), + EQUIPMENT("Minebot AI Upgrade", /obj/item/slimepotion/sentience/mining, 1000), + EQUIPMENT("Minebot Armor Upgrade", /obj/item/mine_bot_upgrade/health, 400), + EQUIPMENT("Minebot Cooldown Upgrade", /obj/item/borg/upgrade/modkit/cooldown/minebot, 600), + EQUIPMENT("Minebot Melee Upgrade", /obj/item/mine_bot_upgrade, 400), + ) + prize_list["Miscellaneous"] = list( + EQUIPMENT("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 100), + EQUIPMENT("Alien Toy", /obj/item/clothing/mask/facehugger/toy, 300), + EQUIPMENT("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 150), + EQUIPMENT("GAR Meson Scanners", /obj/item/clothing/glasses/meson/gar, 500), + EQUIPMENT("Laser Pointer", /obj/item/laser_pointer, 300), + EQUIPMENT("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000), + EQUIPMENT("Soap", /obj/item/soap/nanotrasen, 200), + EQUIPMENT("Space Cash", /obj/item/stack/spacecash/c1000, 2000), + EQUIPMENT("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 100), + ) + prize_list["Extra"] = list() // Used in child vendors + /obj/machinery/mineral/equipment_vendor/power_change() ..() update_icon() @@ -126,90 +109,126 @@ /obj/machinery/mineral/equipment_vendor/attack_hand(mob/user) if(..()) return - interact(user) + tgui_interact(user) /obj/machinery/mineral/equipment_vendor/attack_ghost(mob/user) - interact(user) + tgui_interact(user) -/obj/machinery/mineral/equipment_vendor/interact(mob/user) - user.set_machine(src) +/obj/machinery/mineral/equipment_vendor/tgui_data(mob/user) + var/list/data[0] - var/dat - dat +="
    " - if(istype(inserted_id)) - dat += "You have [inserted_id.mining_points] mining points collected. Eject ID.
    " + // ID + if(inserted_id) + data["has_id"] = TRUE + data["id"] = list( + "name" = inserted_id.registered_name, + "points" = inserted_id.mining_points, + ) else - dat += "No ID inserted. Insert ID.
    " - dat += "
    " - dat += "
    Equipment point cost list:
    KeyReal NameJobMins AFKSpecial RoleAreaPPNCryo
    " - for(var/datum/data/mining_equipment/prize in prize_list) - dat += "" - dat += "
    [prize.equipment_name][prize.cost]Purchase
    " - var/datum/browser/popup = new(user, "miningvendor", "Mining Equipment Vendor", 400, 350) - popup.set_content(dat) - popup.open() + data["has_id"] = FALSE -/obj/machinery/mineral/equipment_vendor/Topic(href, href_list) + return data + +/obj/machinery/mineral/equipment_vendor/tgui_static_data(mob/user) + var/list/static_data[0] + + // Available items - in static data because we don't wanna compute this list every time! It hardly changes. + static_data["items"] = list() + for(var/cat in prize_list) + var/list/cat_items = list() + for(var/prize_name in prize_list[cat]) + var/datum/data/mining_equipment/prize = prize_list[cat][prize_name] + cat_items[prize_name] = list("name" = prize_name, "price" = prize.cost) + static_data["items"][cat] = cat_items + + return static_data + +/obj/machinery/mineral/equipment_vendor/vv_edit_var(var_name, var_value) + // Gotta update the static data in case an admin VV's the items for some reason..! + if(var_name == "prize_list") + dirty_items = TRUE + return ..() + +/obj/machinery/mineral/equipment_vendor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + // Update static data if need be + if(dirty_items) + if(!ui) + ui = SStgui.get_open_ui(user, src, ui_key) + if(ui) // OK so ui?. somehow breaks the implied src so this is needed + ui.initial_static_data = tgui_static_data(user) + dirty_items = FALSE + + // Open the window + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "MiningVendor", name, 400, 450) + ui.open() + ui.set_autoupdate(FALSE) + +/obj/machinery/mineral/equipment_vendor/tgui_act(action, params) if(..()) - return 1 + return - if(href_list["choice"]) - if(istype(inserted_id)) - if(href_list["choice"] == "eject") - inserted_id.loc = loc - inserted_id.verb_pickup() - inserted_id = null - else if(href_list["choice"] == "insert") - var/obj/item/card/id/I = usr.get_active_hand() - if(istype(I)) - if(!usr.drop_item()) - return - I.loc = src - inserted_id = I - else - to_chat(usr, "No valid ID.") - - if(href_list["purchase"]) - if(istype(inserted_id)) - var/datum/data/mining_equipment/prize = locate(href_list["purchase"]) - if(!prize || !(prize in prize_list) || prize.cost > inserted_id.mining_points) + . = TRUE + switch(action) + if("logoff") + if(!inserted_id) + return + usr.put_in_hands(inserted_id) + inserted_id = null + if("purchase") + if(!inserted_id) + return + var/category = params["cat"] // meow + var/name = params["name"] + if(!(category in prize_list) || !(name in prize_list[category])) // Not trying something that's not in the list, are you? + return + var/datum/data/mining_equipment/prize = prize_list[category][name] + if(prize.cost > inserted_id.mining_points) // shouldn't be able to access this since the button is greyed out, but.. + to_chat(usr, "You have insufficient points.") return inserted_id.mining_points -= prize.cost - new prize.equipment_path(src.loc) - updateUsrDialog() + new prize.equipment_path(loc) + else + return FALSE + add_fingerprint() /obj/machinery/mineral/equipment_vendor/attackby(obj/item/I, mob/user, params) if(default_deconstruction_screwdriver(user, "mining-open", "mining", I)) - updateUsrDialog() return if(panel_open) if(istype(I, /obj/item/crowbar)) if(inserted_id) inserted_id.forceMove(loc) //Prevents deconstructing the ORM from deleting whatever ID was inside it. default_deconstruction_crowbar(user, I) - return 1 + return TRUE if(istype(I, /obj/item/mining_voucher)) if(!powered()) return - else - RedeemVoucher(I, user) + redeem_voucher(I, user) return - if(istype(I,/obj/item/card/id)) + if(istype(I, /obj/item/card/id)) if(!powered()) return - else - var/obj/item/card/id/C = usr.get_active_hand() - if(istype(C) && !istype(inserted_id)) - if(!usr.drop_item()) - return - C.forceMove(src) - inserted_id = C - interact(user) + var/obj/item/card/id/C = user.get_active_hand() + if(istype(C) && !istype(inserted_id)) + if(!user.drop_item()) + return + C.forceMove(src) + inserted_id = C + tgui_interact(user) return return ..() -/obj/machinery/mineral/equipment_vendor/proc/RedeemVoucher(obj/item/mining_voucher/voucher, mob/redeemer) +/** + * Called when someone slaps the machine with a mining voucher + * + * Arguments: + * * voucher - The voucher card item + * * redeemer - The person holding it + */ +/obj/machinery/mineral/equipment_vendor/proc/redeem_voucher(obj/item/mining_voucher/voucher, mob/redeemer) var/items = list("Survival Capsule and Explorer's Webbing", "Resonator Kit", "Minebot Kit", "Extraction and Rescue Kit", "Crusher Kit", "Mining Conscription Kit") var/selection = input(redeemer, "Pick your equipment", "Mining Voucher Redemption") as null|anything in items @@ -235,16 +254,62 @@ new /obj/item/extinguisher/mini(drop_location) new /obj/item/twohanded/kinetic_crusher(drop_location) if("Mining Conscription Kit") - new /obj/item/storage/backpack/duffel/mining_conscript/full(drop_location) + new /obj/item/storage/backpack/duffel/mining_conscript(drop_location) qdel(voucher) /obj/machinery/mineral/equipment_vendor/ex_act(severity, target) - do_sparks(5, 1, src) + do_sparks(5, TRUE, src) if(prob(50 / severity) && severity < 3) qdel(src) -/**********************Mining Equipment Locker Items**************************/ +/obj/machinery/mineral/equipment_vendor/Destroy() + if(inserted_id) + inserted_id.forceMove(loc) + return ..() + + +/**********************Mining Equiment Vendor (Golem)**************************/ + +/obj/machinery/mineral/equipment_vendor/golem + name = "golem ship equipment vendor" + +/obj/machinery/mineral/equipment_vendor/golem/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/mining_equipment_vendor/golem(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + +/obj/machinery/mineral/equipment_vendor/golem/Initialize() + . = ..() + desc += "\nIt seems a few selections have been added." + prize_list["Extra"] += list( + EQUIPMENT("Extra ID", /obj/item/card/id/golem, 250), + EQUIPMENT("Science Backpack", /obj/item/storage/backpack/science, 250), + EQUIPMENT("Full Toolbelt", /obj/item/storage/belt/utility/full/multitool, 250), + EQUIPMENT("Monkey Cube", /obj/item/reagent_containers/food/snacks/monkeycube, 250), + EQUIPMENT("Royal Cape of the Liberator", /obj/item/bedsheet/rd/royal_cape, 500), + EQUIPMENT("Grey Slime Extract", /obj/item/slime_extract/grey, 1000), + EQUIPMENT("KA Trigger Modification Kit", /obj/item/borg/upgrade/modkit/trigger_guard, 1000), + EQUIPMENT("Shuttle Console Board", /obj/item/circuitboard/shuttle/golem_ship, 2000), + EQUIPMENT("The Liberator's Legacy", /obj/item/storage/box/rndboards, 2000), + ) + +/**********************Mining Equipment Datum**************************/ + +/datum/data/mining_equipment + var/equipment_name = "generic" + var/equipment_path = null + var/cost = 0 + +/datum/data/mining_equipment/New(name, path, equipment_cost) + equipment_name = name + equipment_path = path + cost = equipment_cost /**********************Mining Equipment Voucher**********************/ @@ -278,25 +343,6 @@ . = ..() . += "There's [points] points on the card." -/**********************Conscription Kit**********************/ -/obj/item/card/id/mining_access_card - name = "mining access card" - desc = "A small card, that when used on any ID, will add mining access." - icon_state = "data_1" +#undef EQUIPMENT -/obj/item/card/id/mining_access_card/afterattack(atom/movable/AM, mob/user, proximity) - . = ..() - if(istype(AM, /obj/item/card/id) && proximity) - var/obj/item/card/id/I = AM - I.access |= list(ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_CARGO) - to_chat(user, "You upgrade [I] with mining access.") - qdel(src) - -/obj/item/storage/backpack/duffel/mining_conscript/full - name = "mining conscription kit" - desc = "A kit containing everything a crewmember needs to support a shaft miner in the field." - -/obj/item/storage/backpack/duffel/mining_conscript/full/New() - ..() - new /obj/item/card/id/mining_access_card(src) diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 8737fdf21b3..7de33866d46 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -220,7 +220,9 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ var/datum/wires/explosive/gibtonite/wires /obj/item/twohanded/required/gibtonite/Destroy() - QDEL_NULL(wires) + if(wires) + SStgui.close_uis(wires) + QDEL_NULL(wires) return ..() /obj/item/twohanded/required/gibtonite/attackby(obj/item/I, mob/user, params) @@ -273,7 +275,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ icon_state = "Gibtonite active" var/turf/bombturf = get_turf(src) var/notify_admins = 0 - if(z != 5)//Only annoy the admins ingame if we're triggered off the mining zlevel + if(!is_mining_level(z))//Only annoy the admins ingame if we're triggered off the mining zlevel notify_admins = 1 if(notify_admins) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 336ebdb8b67..2d9966669e8 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -53,7 +53,6 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) if(ismob(body)) T = get_turf(body) //Where is the body located? attack_log_old = body.attack_log_old //preserve our attack logs by copying them to our ghost - logs = body.logs.Copy() var/mutable_appearance/MA = copy_appearance(body) if(body.mind && body.mind.name) @@ -577,7 +576,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/dat dat += "

    Crew Manifest

    " - dat += GLOB.data_core.get_manifest() + dat += GLOB.data_core.get_manifest(OOC = TRUE) src << browse(dat, "window=manifest;size=370x420;can_close=1") @@ -781,4 +780,4 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp set category = "Ghost" var/datum/spawners_menu/menu = new /datum/spawners_menu(src) - menu.ui_interact(src) + menu.tgui_interact(src) diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index 2f5627e8413..4d003753876 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -1,20 +1,10 @@ -/mob/dead/observer/say(var/message) +/mob/dead/observer/say(message) message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN)) if(!message) return - log_ghostsay(message, src) - - if(src.client) - if(src.client.prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot talk in deadchat (muted).") - return - - if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT)) - return - - . = src.say_dead(message) + return say_dead(message) /mob/dead/observer/emote(act, type, message, force) diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index ece9c743854..508dd271849 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -9,6 +9,7 @@ return FALSE /mob/proc/death(gibbed) + SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed) return FALSE /mob/proc/dust_animation() diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index 2b07e10291f..2514ce68b06 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -47,7 +47,7 @@ . = trim(. + trim(msg)) . += "\"" -/mob/proc/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) +/mob/proc/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) if(!client) return 0 @@ -74,7 +74,7 @@ return 0 var/speaker_name = speaker.name - if(ishuman(speaker)) + if(use_voice && ishuman(speaker)) var/mob/living/carbon/human/H = speaker speaker_name = H.GetVoice() @@ -99,9 +99,9 @@ if(speaker == src) to_chat(src, "You cannot hear yourself speak!") else - to_chat(src, "[speaker_name][speaker.GetAltName()] talks but you cannot hear [speaker.p_them()].") + to_chat(src, "[speaker.name] talks but you cannot hear [speaker.p_them()].") else - to_chat(src, "[speaker_name][speaker.GetAltName()] [track][message]") + to_chat(src, "[speaker_name][use_voice ? speaker.GetAltName() : ""] [track][message]") if(speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z)) var/turf/source = speaker? get_turf(speaker) : get_turf(src) playsound_local(source, speech_sound, sound_vol, 1, sound_frequency) @@ -171,7 +171,14 @@ to_chat(src, heard) -/mob/proc/hear_holopad_talk(list/message_pieces, var/verb = "says", var/mob/speaker = null) +/mob/proc/hear_holopad_talk(list/message_pieces, verb = "says", mob/speaker = null) + if(sleeping || stat == UNCONSCIOUS) + hear_sleep(multilingual_to_message(message_pieces)) + return + + if(!can_hear()) + return + var/message = combine_message(message_pieces, verb, speaker) var/name = speaker.name diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm index a46268daf50..50d9b6d5fd5 100644 --- a/code/modules/mob/language.dm +++ b/code/modules/mob/language.dm @@ -186,7 +186,7 @@ if(prob(80)) new_name += " [pick(list("Hadii","Kaytam","Zhan-Khazan","Hharar","Njarir'Akhan"))]" else - new_name += ..(gender,1) + new_name += " [..(gender,1)]" return new_name /datum/language/vulpkanin diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index aeee677b905..f281b8abec3 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -81,7 +81,7 @@ /mob/living/carbon/alien/larva/show_inv(mob/user as mob) return -/mob/living/carbon/alien/larva/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) +/mob/living/carbon/alien/larva/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) return FALSE /* Commented out because it's duplicated in life.dm diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index 5d828e7c181..79ed90a61b8 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -70,7 +70,7 @@ return polling = 1 spawn() - var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 0) + var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as an alien?", ROLE_ALIEN, FALSE, source = /mob/living/carbon/alien/larva) var/mob/C = null // To stop clientless larva, we will check that our host has a client diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm index 1dcd76c0358..b3f6d73a163 100644 --- a/code/modules/mob/living/carbon/alien/special/facehugger.dm +++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm @@ -28,6 +28,10 @@ var/attached = 0 +/obj/item/clothing/mask/facehugger/ComponentInitialize() + . = ..() + AddComponent(/datum/component/proximity_monitor) + /obj/item/clothing/mask/facehugger/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) ..() if(obj_integrity < 90) @@ -78,7 +82,7 @@ return HasProximity(finder) return 0 -/obj/item/clothing/mask/facehugger/HasProximity(atom/movable/AM as mob|obj) +/obj/item/clothing/mask/facehugger/HasProximity(atom/movable/AM) if(CanHug(AM) && Adjacent(AM)) return Attach(AM) return 0 @@ -210,6 +214,7 @@ icon_state = "[initial(icon_state)]_dead" item_state = "facehugger_inactive" stat = DEAD + qdel(GetComponent(/datum/component/proximity_monitor)) visible_message("[src] curls up into a ball!") diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index 16014e7c353..a17d7b8ebbd 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -226,13 +226,6 @@ brainmob.emp_damage += rand(0,10) ..() -/obj/item/mmi/relaymove(var/mob/user, var/direction) - if(user.stat || user.stunned) - return - var/obj/item/rig/rig = src.get_rig() - if(rig) - rig.forced_move(direction, user) - /obj/item/mmi/Destroy() if(isrobot(loc)) var/mob/living/silicon/robot/borg = loc diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 98585262518..a496526008f 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -514,10 +514,10 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven /mob/living/update_pipe_vision() if(pipes_shown.len) - if(!istype(loc, /obj/machinery/atmospherics)) + if(!is_ventcrawling(src)) remove_ventcrawl() else - if(istype(loc, /obj/machinery/atmospherics)) + if(is_ventcrawling(src)) add_ventcrawl(loc) @@ -538,6 +538,8 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven take_organ_damage(10) if(iscarbon(hit_atom) && hit_atom != src) var/mob/living/carbon/victim = hit_atom + if(victim.flying) + return if(hurt) victim.take_organ_damage(10) take_organ_damage(10) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index d213ee7206f..4a0b9ea55b1 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -109,9 +109,6 @@ // log_world("k") sql_report_death(src) - if(wearing_rig) - wearing_rig.notify_ai("Warning: user death event. Mobility control passed to integrated intelligence system.") - /mob/living/carbon/human/update_revive() . = ..() if(. && healthdoll) diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 10c2ac8f119..87a327f3427 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -30,8 +30,15 @@ //handle_emote_CD() located in [code\modules\mob\emote.dm] var/on_CD = FALSE act = lowertext(act) - switch(act) - //Cooldown-inducing emotes + + switch(act) //This switch makes sure you have air in your lungs before you scream + if("growl", "growls", "howl", "howls", "hiss", "hisses", "scream", "screams", "sneeze", "sneezes") + if(getOxyLoss() > 35) //no screaming if you don't have enough breath to scream + on_CD = handle_emote_CD() + emote("gasp") + return + + switch(act) //This switch adds cooldowns to some emotes if("ping", "pings", "buzz", "buzzes", "beep", "beeps", "yes", "no", "buzz2") var/found_machine_head = FALSE if(ismachineperson(src)) //Only Machines can beep, ping, and buzz, yes, no, and make a silly sad trombone noise. @@ -44,7 +51,7 @@ found_machine_head = TRUE if(!found_machine_head) //Everyone else fails, skip the emote attempt - return //Everyone else fails, skip the emote attempt + return //Everyone else fails, skip the emote attempt if("drone","drones","hum","hums","rumble","rumbles") if(isdrask(src)) //Only Drask can make whale noises on_CD = handle_emote_CD() //proc located in code\modules\mob\emote.dm @@ -133,7 +140,7 @@ if(!force && on_CD == 1) // Check if we need to suppress the emote attempt. return // Suppress emote, you're still cooling off. - switch(act) + switch(act) //This is for actually making the emotes happen if("me") //OKAY SO RANT TIME, THIS FUCKING HAS TO BE HERE OR A SHITLOAD OF THINGS BREAK return custom_emote(m_type, message) //DO YOU KNOW WHY SHIT BREAKS? BECAUSE SO MUCH OLDCODE CALLS mob.emote("me",1,"whatever_the_fuck_it_wants_to_emote") //WHO THE FUCK THOUGHT THAT WAS A GOOD FUCKING IDEA!?!? @@ -308,14 +315,14 @@ m_type = 1 if("bow", "bows") - if(!buckled) + if(!restrained()) var/M = handle_emote_param(param) message = "[src] bows[M ? " to [M]" : ""]." m_type = 1 if("salute", "salutes") - if(!buckled) + if(!restrained()) var/M = handle_emote_param(param) message = "[src] salutes[M ? " to [M]" : ""]." @@ -592,7 +599,7 @@ message = "[src] sighs[M ? " at [M]" : ""]." m_type = 2 else - message = "[src] makes a weak noise" + message = "[src] makes a weak noise." m_type = 2 if("hsigh", "hsighs") @@ -600,7 +607,7 @@ message = "[src] sighs contentedly." m_type = 2 else - message = "[src] makes a [pick("chill", "relaxed")] noise" + message = "[src] makes a [pick("chill", "relaxed")] noise." m_type = 2 if("laugh", "laughs") diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 31825ea3765..cf517dc7389 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -301,14 +301,13 @@ else if(nutrition >= NUTRITION_LEVEL_FAT) msg += "[p_they(TRUE)] [p_are()] quite chubby.\n" - if(!ismachineperson(src) && blood_volume < BLOOD_VOLUME_SAFE) + if(blood_volume < BLOOD_VOLUME_SAFE) msg += "[p_they(TRUE)] [p_have()] pale skin.\n" if(bleedsuppress) msg += "[p_they(TRUE)] [p_are()] bandaged with something.\n" else if(bleed_rate) - var/bleed_message = !ismachineperson(src) ? "bleeding" : "leaking" - msg += "[p_they(TRUE)] [p_are()] [bleed_message]!\n" + msg += "[p_they(TRUE)] [p_are()] bleeding!\n" if(reagents.has_reagent("teslium")) msg += "[p_they(TRUE)] [p_are()] emitting a gentle blue glow!\n" @@ -341,7 +340,7 @@ if(decaylevel == 3) msg += "[p_they(TRUE)] [p_are()] rotting and blackened, the skin sloughing off. The smell is indescribably foul.\n" if(decaylevel == 4) - msg += "[p_they(TRUE)] [p_are()] mostly dessicated now, with only bones remaining of what used to be a person.\n" + msg += "[p_they(TRUE)] [p_are()] mostly desiccated now, with only bones remaining of what used to be a person.\n" if(hasHUD(user,"security")) var/perpname = get_visible_name(TRUE) @@ -365,6 +364,17 @@ msg += "Security records: \[View comment log\] \[Add comment\]\n" msg += "Latest entry: [commentLatest]\n" + if(hasHUD(user, "skills")) + var/perpname = get_visible_name(TRUE) + var/skills + + if(perpname) + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == perpname) + skills = E.fields["notes"] + if(skills) + msg += "Employment records: [skills]\n" + if(hasHUD(user,"medical")) var/perpname = get_visible_name(TRUE) var/medical = "None" @@ -405,8 +415,11 @@ return !istype(CIH,/obj/item/organ/internal/cyberimp/eyes/hud/security) && S && S.read_only if("medical") return istype(H.glasses, /obj/item/clothing/glasses/hud/health) || istype(CIH,/obj/item/organ/internal/cyberimp/eyes/hud/medical) + if("skills") + return istype(H.glasses, /obj/item/clothing/glasses/hud/skills) else return FALSE + else if(isrobot(M) || isAI(M)) //Stand-in/Stopgap to prevent pAIs from freely altering records, pending a more advanced Records system switch(hudtype) if("security") @@ -421,6 +434,8 @@ switch(hudtype) if("security") return TRUE + if("skills") + return TRUE else return FALSE else diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 16f1738d2f6..6393a8333bb 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -5,7 +5,6 @@ icon = 'icons/mob/human.dmi' icon_state = "body_m_s" deathgasp_on_death = TRUE - var/obj/item/rig/wearing_rig // This is very not good, but it's much much better than calling get_rig() every update_canmove() call. /mob/living/carbon/human/New(loc) icon = null // This is now handled by overlays -- we just keep an icon for the sake of the map editor. @@ -30,8 +29,6 @@ create_reagents(330) - martial_art = GLOB.default_martial_art - handcrafting = new() // Set up DNA. @@ -182,13 +179,6 @@ stat("Tank Pressure", internal.air_contents.return_pressure()) stat("Distribution Pressure", internal.distribute_pressure) - if(istype(back, /obj/item/rig)) - var/obj/item/rig/suit = back - var/cell_status = "ERROR" - if(suit.cell) - cell_status = "[suit.cell.charge]/[suit.cell.maxcharge]" - stat(null, "Suit charge: [cell_status]") - // I REALLY need to split up status panel things into datums var/mob/living/simple_animal/borer/B = has_brain_worms() if(B && B.controlling) @@ -264,7 +254,7 @@ limbs_affected -= 1 else valid_limbs -= processing_dismember - if(!istype(l_ear, /obj/item/clothing/ears/earmuffs) && !istype(r_ear, /obj/item/clothing/ears/earmuffs)) + if(check_ear_prot() < HEARING_PROTECTION_TOTAL) AdjustEarDamage(30, 120) if(prob(70) && !shielded) Paralyse(10) @@ -288,7 +278,7 @@ limbs_affected -= 1 else valid_limbs -= processing_dismember - if(!istype(l_ear, /obj/item/clothing/ears/earmuffs) && !istype(r_ear, /obj/item/clothing/ears/earmuffs)) + if(check_ear_prot() < HEARING_PROTECTION_TOTAL) AdjustEarDamage(15, 60) if(prob(50) && !shielded) Paralyse(10) @@ -306,8 +296,8 @@ apply_damage(5, BRUTE, affecting, run_armor_check(affecting, "melee")) /mob/living/carbon/human/bullet_act() - if(martial_art && martial_art.deflection_chance) //Some martial arts users can deflect projectiles! - if(!prob(martial_art.deflection_chance)) + if(mind && mind.martial_art && mind.martial_art.deflection_chance) //Some martial arts users can deflect projectiles! + if(!prob(mind.martial_art.deflection_chance)) return ..() if(!src.lying && !(HULK in mutations)) //But only if they're not lying down, and hulks can't do it visible_message("[src] deflects the projectile; [p_they()] can't be hit with ranged weapons!", "You deflect the projectile!") @@ -941,12 +931,18 @@ return number /mob/living/carbon/human/check_ear_prot() + if(!can_hear()) + return HEARING_PROTECTION_TOTAL + if(istype(l_ear, /obj/item/clothing/ears/earmuffs)) + return HEARING_PROTECTION_TOTAL + if(istype(r_ear, /obj/item/clothing/ears/earmuffs)) + return HEARING_PROTECTION_TOTAL if(head && (head.flags & HEADBANGPROTECT)) - return 1 + return HEARING_PROTECTION_MINOR if(l_ear && (l_ear.flags & EARBANGPROTECT)) - return 1 + return HEARING_PROTECTION_MINOR if(r_ear && (r_ear.flags & EARBANGPROTECT)) - return 1 + return HEARING_PROTECTION_MINOR ///tintcheck() ///Checks eye covering items for visually impairing tinting, such as welding masks @@ -963,16 +959,6 @@ var/obj/item/clothing/mask/MT = src.wear_mask tinted += MT.tint - //god help me - if(istype(back, /obj/item/rig)) - var/obj/item/rig/O = back - if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & HEAD)) - if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1)) - tinted = 2 - if((O.offline && O.offline_vision_restriction == 2) || (!O.offline && O.vision_restriction == 2)) - tinted = 3 - //im so sorry - return tinted @@ -1193,7 +1179,7 @@ set src in view(1) var/self = 0 - if(usr.stat == 1 || usr.restrained() || !isliving(usr)) return + if(usr.stat == 1 || usr.restrained() || !isliving(usr) || usr.is_dead()) return if(usr == src) self = 1 @@ -1465,12 +1451,13 @@ var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) var/obj/item/organ/internal/cyberimp/eyes/eye_implant = get_int_organ(/obj/item/organ/internal/cyberimp/eyes) if(istype(dna.species) && dna.species.eyes) - var/icon/eyes_icon = new /icon('icons/mob/human_face.dmi', dna.species.eyes) + var/icon/eyes_icon if(eye_implant) //Eye implants override native DNA eye colo(u)r eyes_icon = eye_implant.generate_icon() else if(eyes) eyes_icon = eyes.generate_icon() else //Error 404: Eyes not found! + eyes_icon = new('icons/mob/human_face.dmi', dna.species.eyes) eyes_icon.Blend("#800000", ICON_ADD) return eyes_icon @@ -1748,16 +1735,14 @@ Eyes need to have significantly high darksight to shine unless the mob has the X if(G.trigger_guard == TRIGGER_GUARD_NORMAL) if(HULK in mutations) to_chat(src, "Your meaty finger is much too large for the trigger guard!") - return 0 + return FALSE if(NOGUNS in dna.species.species_traits) to_chat(src, "Your fingers don't fit in the trigger guard!") - return 0 + return FALSE - if(martial_art && martial_art.no_guns) //great dishonor to famiry - to_chat(src, "[martial_art.no_guns_message]") - return 0 - - return . + if(mind && mind.martial_art && mind.martial_art.no_guns) //great dishonor to famiry + to_chat(src, "[mind.martial_art.no_guns_message]") + return FALSE /mob/living/carbon/human/proc/change_icobase(var/new_icobase, var/new_deform, var/owner_sensitive) for(var/obj/item/organ/external/O in bodyparts) diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index f9058065fb0..621d8330761 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -129,13 +129,6 @@ O.heal_damage(0, -amount, internal = 0, robo_repair = O.is_robotic(), updating_health = updating_health) return STATUS_UPDATE_HEALTH - -/mob/living/carbon/human/Paralyse(amount) - // Notify our AI if they can now control the suit. - if(wearing_rig && !stat && paralysis < amount) //We are passing out right this second. - wearing_rig.notify_ai("Warning: user consciousness failure. Mobility control passed to integrated intelligence system.") - return ..() - /mob/living/carbon/human/adjustCloneLoss(amount) if(dna.species && amount > 0) amount = amount * dna.species.clone_mod @@ -342,7 +335,5 @@ This function restores all organs. ..(damage, damagetype, def_zone, blocked) return 1 - //Handle BRUTE and BURN damage - handle_suit_punctures(damagetype, damage) //Handle species apply_damage procs return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, sharp, used_weapon) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 21a3e8bf274..87d7165b8d3 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -13,14 +13,22 @@ emp_act if(!dna.species.bullet_act(P, src)) return FALSE if(P.is_reflectable) - if(check_reflect(def_zone)) // Checks if you've passed a reflection% check + var/can_reflect = check_reflect(def_zone) + if(can_reflect == 1) // proper reflection visible_message("The [P.name] gets reflected by [src]!", \ - "The [P.name] gets reflected by [src]!") - + "The [P.name] gets reflected by [src]!") P.reflect_back(src) - return -1 // complete projectile permutation + else if(can_reflect == 2) //If target is holding a toy sword + var/list/safe_list = list(/obj/item/projectile/beam/lasertag, /obj/item/projectile/beam/practice) + if(is_type_in_list(P, safe_list)) //And it's safe + visible_message("The [P.name] gets reflected by [src]!", \ + "The [P.name] gets reflected by [src]!") + P.reflect_back(src) + return -1 // complete projectile permutation + + //Shields if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration)) P.on_hit(src, 100, def_zone) @@ -172,10 +180,14 @@ emp_act var/obj/item/I = l_hand if(I.IsReflect(def_zone) == 1) return 1 + if(I.IsReflect(def_zone) == 2) //Toy swords + return 2 if(r_hand && istype(r_hand, /obj/item/)) var/obj/item/I = r_hand if(I.IsReflect(def_zone) == 1) return 1 + if(I.IsReflect(def_zone) == 2) //Toy swords + return 2 return 0 @@ -203,7 +215,7 @@ emp_act return 0 /mob/living/carbon/human/proc/check_block() - if(martial_art && prob(martial_art.block_chance) && martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE)) + if(mind && mind.martial_art && prob(mind.martial_art.block_chance) && mind.martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE)) return TRUE /mob/living/carbon/human/acid_act(acidpwr, acid_volume, bodyzone_hit) //todo: update this to utilize check_obscured_slots() //and make sure it's check_obscured_slots(TRUE) to stop aciding through visors etc @@ -531,17 +543,6 @@ emp_act w_uniform.add_mob_blood(source) update_inv_w_uniform() -/mob/living/carbon/human/proc/handle_suit_punctures(var/damtype, var/damage) - - if(!wear_suit) return - if(!istype(wear_suit,/obj/item/clothing/suit/space)) return - if(damtype != BURN && damtype != BRUTE) return - - var/obj/item/clothing/suit/space/SS = wear_suit - var/penetrated_dam = max(0,(damage - max(0,(SS.breach_threshold - SS.damage)))) - - if(penetrated_dam) SS.create_breaches(damtype, penetrated_dam) - /mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) if(user.a_intent == INTENT_HARM) if(HAS_TRAIT(user, TRAIT_PACIFISM)) @@ -583,41 +584,34 @@ emp_act if(M.a_intent == INTENT_HARM) if(w_uniform) w_uniform.add_fingerprint(M) - var/damage = rand(15, 30) + var/damage = prob(90) ? 20 : 0 if(!damage) - playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1) + playsound(loc, 'sound/weapons/slashmiss.ogg', 50, TRUE, -1) visible_message("[M] has lunged at [src]!") return 0 var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected)) - var/armor_block = run_armor_check(affecting, "melee") + var/armor_block = run_armor_check(affecting, "melee", armour_penetration = 10) - playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) + playsound(loc, 'sound/weapons/slice.ogg', 25, TRUE, -1) visible_message("[M] has slashed at [src]!", \ "[M] has slashed at [src]!") apply_damage(damage, BRUTE, affecting, armor_block) - if(damage >= 25) - visible_message("[M] has wounded [src]!", \ - "[M] has wounded [src]!") - apply_effect(4, WEAKEN, armor_block) - add_attack_logs(M, src, "Alien attacked") + add_attack_logs(M, src, "Alien attacked") updatehealth("alien attack") - if(M.a_intent == INTENT_DISARM) - if(prob(80)) + if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stun instead. + var/obj/item/I = get_active_hand() + if(I && unEquip(I)) + playsound(loc, 'sound/weapons/slash.ogg', 25, TRUE, -1) + visible_message("[M] disarms [src]!", "[M] disarms you!", "You hear aggressive shuffling!") + to_chat(M, "You disarm [src]!") + else var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected)) playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) apply_effect(5, WEAKEN, run_armor_check(affecting, "melee")) add_attack_logs(M, src, "Alien tackled") visible_message("[M] has tackled down [src]!") - else - if(prob(99)) //this looks fucking stupid but it was previously 'var/randn = rand(1, 100); if(randn <= 99)' - playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1) - drop_item() - visible_message("[M] disarmed [src]!") - else - playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1) - visible_message("[M] has tried to disarm [src]!") /mob/living/carbon/human/attack_animal(mob/living/simple_animal/M) . = ..() diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 3d365afa940..bc5c85e2929 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -1,4 +1,3 @@ -GLOBAL_DATUM_INIT(default_martial_art, /datum/martial_art, new()) /mob/living/carbon/human hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPMINDSHIELD_HUD,IMPCHEM_HUD,IMPTRACK_HUD,SPECIALROLE_HUD,GLAND_HUD) @@ -42,8 +41,6 @@ GLOBAL_DATUM_INIT(default_martial_art, /datum/martial_art, new()) var/datum/personal_crafting/handcrafting - var/datum/martial_art/martial_art = null - var/special_voice = "" // For changing our voice. Used by a symptom. var/hand_blood_color diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 5e1b5163a24..7cb33bce249 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -16,12 +16,6 @@ else if(istype(wear_suit, /obj/item/clothing/suit/space/hardsuit)) var/obj/item/clothing/suit/space/hardsuit/C = wear_suit thrust = C.jetpack - else if(istype(back,/obj/item/rig)) - var/obj/item/rig/rig = back - for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules) - thrust = module.jets - break - if(thrust) if((movement_dir || thrust.stabilizers) && thrust.allow_thrust(0.01, src)) return 1 diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 90653a6750b..c9cd99ad2d0 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -66,9 +66,7 @@ if(!is_station_level(T.z)) return var/area/A = get_area(src) - if(cryo_ssd(src)) - var/obj/effect/portal/P = new /obj/effect/portal(T, null, null, 40) - P.name = "NT SSD Teleportation Portal" + cryo_ssd(src) if(A.fast_despawn) force_cryo_human(src) @@ -302,14 +300,6 @@ if(!(head && head.flags & AIRTIGHT)) //if NOT (head AND head.flags CONTAIN AIRTIGHT) null_internals = 1 //not wearing a mask or suitable helmet - if(istype(back, /obj/item/rig)) //wearing a rigsuit - var/obj/item/rig/rig = back //needs to be typecasted because this doesn't use get_rig() for some reason - if(rig.offline && (rig.air_supply && internal == rig.air_supply)) //if rig IS offline AND (rig HAS air_supply AND internal IS air_supply) - null_internals = 1 //offline suits do not breath - - else if(rig.air_supply && internal == rig.air_supply) //if rig HAS air_supply AND internal IS rig air_supply - skip_contents_check = 1 //skip contents.Find() check, the oxygen is valid even being outside of the mob - if(!contents.Find(internal) && (!skip_contents_check)) //if internal NOT IN contents AND skip_contents_check IS false null_internals = 1 //not a rigsuit and your oxygen is gone @@ -706,13 +696,14 @@ if(alcohol_strength >= slur_start) //slurring Slur(drunk) - if(alcohol_strength >= brawl_start) //the drunken martial art - if(!istype(martial_art, /datum/martial_art/drunk_brawling)) - var/datum/martial_art/drunk_brawling/F = new - F.teach(src, 1) - if(alcohol_strength < brawl_start) //removing the art - if(istype(martial_art, /datum/martial_art/drunk_brawling)) - martial_art.remove(src) + if(mind) + if(alcohol_strength >= brawl_start) //the drunken martial art + if(!istype(mind.martial_art, /datum/martial_art/drunk_brawling)) + var/datum/martial_art/drunk_brawling/F = new + F.teach(src, TRUE) + else if(alcohol_strength < brawl_start) //removing the art + if(istype(mind.martial_art, /datum/martial_art/drunk_brawling)) + mind.martial_art.remove(src) if(alcohol_strength >= confused_start && prob(33)) //confused walking if(!confused) Confused(1) diff --git a/code/modules/mob/living/carbon/human/login.dm b/code/modules/mob/living/carbon/human/login.dm index 6171b3d02b0..7db9afdc9c3 100644 --- a/code/modules/mob/living/carbon/human/login.dm +++ b/code/modules/mob/living/carbon/human/login.dm @@ -6,4 +6,5 @@ if(ventcrawler) to_chat(src, "You can ventcrawl! Use alt+click on vents to quickly travel about the station.") update_pipe_vision() + regenerate_icons() return diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 01a9990ebd8..957b0828725 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -62,11 +62,6 @@ return ..() /mob/living/carbon/human/proc/HasVoiceChanger() - if(istype(back, /obj/item/rig)) - var/obj/item/rig/rig = back - if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice) - return rig.speech.voice_holder.voice - for(var/obj/item/gear in list(wear_mask, wear_suit, head)) if(!gear) continue diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm index 0914852b131..69518e96cf7 100644 --- a/code/modules/mob/living/carbon/human/species/_species.dm +++ b/code/modules/mob/living/carbon/human/species/_species.dm @@ -371,7 +371,7 @@ return /datum/species/proc/help(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style) - if(attacker_style && attacker_style.help_act(user, target))//adminfu only... + if(attacker_style && attacker_style.help_act(user, target) == TRUE)//adminfu only... return TRUE if(target.health >= HEALTH_THRESHOLD_CRIT && !(target.status_flags & FAKEDEATH)) target.help_shake_act(user) @@ -383,7 +383,7 @@ if(target.check_block()) target.visible_message("[target] blocks [user]'s grab attempt!") return FALSE - if(attacker_style && attacker_style.grab_act(user, target)) + if(attacker_style && attacker_style.grab_act(user, target) == TRUE) return TRUE else target.grabbedby(user) @@ -412,7 +412,7 @@ if(target.check_block()) target.visible_message("[target] blocks [user]'s attack!") return FALSE - if(attacker_style && attacker_style.harm_act(user, target)) + if(attacker_style && attacker_style.harm_act(user, target) == TRUE) return TRUE else var/datum/unarmed_attack/attack = user.dna.species.unarmed @@ -460,7 +460,7 @@ if(target.check_block()) target.visible_message("[target] blocks [user]'s disarm attempt!") return FALSE - if(attacker_style && attacker_style.disarm_act(user, target)) + if(attacker_style && attacker_style.disarm_act(user, target) == TRUE) return TRUE else add_attack_logs(user, target, "Disarmed", ATKLOG_ALL) @@ -516,12 +516,9 @@ playsound(target.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) target.visible_message("[user] attempted to disarm [target]!") -/datum/species/proc/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style = M.martial_art) //Handles any species-specific attackhand events. +/datum/species/proc/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style) //Handles any species-specific attackhand events. if(!istype(M)) return - if(H.frozen) - to_chat(M, "Do not touch Admin-Frozen people.") - return if(istype(M)) var/obj/item/organ/external/temp = M.bodyparts_by_name["r_hand"] @@ -531,6 +528,9 @@ to_chat(M, "You can't use your hand.") return + if(M.mind) + attacker_style = M.mind.martial_art + if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK)) add_attack_logs(M, H, "Melee attacked with fists (miss/block)") H.visible_message("[M] attempted to touch [H]!") @@ -817,20 +817,6 @@ It'll return null if the organ doesn't correspond, so include null checks when u if(!isnull(hat.lighting_alpha)) H.lighting_alpha = min(hat.lighting_alpha, H.lighting_alpha) - if(istype(H.back, /obj/item/rig)) ///aghhh so snowflakey - var/obj/item/rig/rig = H.back - if(rig.visor) - if(!rig.helmet || (H.head && rig.helmet == H.head)) - if(rig.visor && rig.visor.vision && rig.visor.active && rig.visor.vision.glasses) - var/obj/item/clothing/glasses/G = rig.visor.vision.glasses - if(istype(G)) - H.sight |= G.vision_flags - H.see_in_dark = max(G.see_in_dark, H.see_in_dark) - H.see_invisible = min(G.invis_view, H.see_invisible) - - if(!isnull(G.lighting_alpha)) - H.lighting_alpha = min(G.lighting_alpha, H.lighting_alpha) - if(H.vision_type) H.sight |= H.vision_type.sight_flags H.see_in_dark = max(H.see_in_dark, H.vision_type.see_in_dark) diff --git a/code/modules/mob/living/carbon/human/species/diona.dm b/code/modules/mob/living/carbon/human/species/diona.dm index a8639a1a948..133a334a998 100644 --- a/code/modules/mob/living/carbon/human/species/diona.dm +++ b/code/modules/mob/living/carbon/human/species/diona.dm @@ -74,6 +74,10 @@ ..() H.gender = NEUTER +/datum/species/diona/on_species_loss(mob/living/carbon/human/H) + . = ..() + H.clear_alert("nolight") + /datum/species/diona/handle_reagents(mob/living/carbon/human/H, datum/reagent/R) if(R.id == "glyphosate" || R.id == "atrazine") H.adjustToxLoss(3) //Deal aditional damage @@ -81,9 +85,8 @@ return ..() /datum/species/diona/handle_life(mob/living/carbon/human/H) - if(H.stat == DEAD) - return var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing + var/is_vamp = H.mind?.vampire != null if(isturf(H.loc)) //else, there's considered to be no light var/turf/T = H.loc light_amount = min(1, T.get_lumcount()) - 0.5 @@ -91,9 +94,12 @@ H.clear_alert("nolight") else H.throw_alert("nolight", /obj/screen/alert/nolight) - H.adjust_nutrition(light_amount * 10) - if(H.nutrition > NUTRITION_LEVEL_ALMOST_FULL) - H.set_nutrition(NUTRITION_LEVEL_ALMOST_FULL) + + if(!is_vamp) + H.adjust_nutrition(light_amount * 10) + if(H.nutrition > NUTRITION_LEVEL_ALMOST_FULL) + H.set_nutrition(NUTRITION_LEVEL_ALMOST_FULL) + if(light_amount > 0.2 && !H.suiciding) //if there's enough light, heal if(!pod && H.health <= 0) return @@ -102,7 +108,7 @@ H.adjustToxLoss(-1) H.adjustOxyLoss(-1) - if(H.nutrition < NUTRITION_LEVEL_STARVING + 50) + if(!is_vamp && H.nutrition < NUTRITION_LEVEL_STARVING + 50) H.adjustBruteLoss(2) ..() diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm index ec37d3c6c9f..a1cab2ea466 100644 --- a/code/modules/mob/living/carbon/human/species/machine.dm +++ b/code/modules/mob/living/carbon/human/species/machine.dm @@ -22,7 +22,7 @@ death_message = "gives a short series of shrill beeps, their chassis shuddering before falling limp, nonfunctional." death_sounds = list('sound/voice/borg_deathsound.ogg') //I've made this a list in the event we add more sounds for dead robots. - species_traits = list(IS_WHITELISTED, NO_BREATHE, NO_SCAN, NO_INTORGANS, NO_PAIN, NO_DNA, RADIMMUNE, VIRUSIMMUNE, NO_GERMS, NO_DECAY, NOTRANSSTING) //Computers that don't decay? What a lie! + species_traits = list(IS_WHITELISTED, NO_BREATHE, NO_BLOOD, NO_SCAN, NO_INTORGANS, NO_PAIN, NO_DNA, RADIMMUNE, VIRUSIMMUNE, NO_GERMS, NO_DECAY, NOTRANSSTING) //Computers that don't decay? What a lie! clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS bodyflags = HAS_SKIN_COLOR | HAS_HEAD_MARKINGS | HAS_HEAD_ACCESSORY | ALL_RPARTS dietflags = 0 //IPCs can't eat, so no diet @@ -30,10 +30,6 @@ blood_color = "#1F181F" flesh_color = "#AAAAAA" - blood_color = "#3C3C3C" - exotic_blood = "oil" - blood_damage_type = STAMINA - //Default styles for created mobs. default_hair = "Blue IPC Screen" dies_at_threshold = TRUE diff --git a/code/modules/mob/living/carbon/human/species/wryn.dm b/code/modules/mob/living/carbon/human/species/wryn.dm index 080a67a032b..9040e651749 100644 --- a/code/modules/mob/living/carbon/human/species/wryn.dm +++ b/code/modules/mob/living/carbon/human/species/wryn.dm @@ -35,8 +35,9 @@ "antennae" = /obj/item/organ/internal/wryn/hivenode ) - species_traits = list(IS_WHITELISTED, NO_BREATHE, HAS_SKIN_COLOR, NO_SCAN, HIVEMIND) + species_traits = list(LIPS, IS_WHITELISTED, NO_BREATHE, NO_SCAN, HIVEMIND) clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS + bodyflags = HAS_SKIN_COLOR dietflags = DIET_HERB //bees feed off nectar, so bee people feed off plants too dies_at_threshold = TRUE diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 81907ba516f..e667833dfe3 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -273,11 +273,6 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) overlays_standing[UNDERWEAR_LAYER] = mutable_appearance(underwear_standing, layer = -UNDERWEAR_LAYER) apply_overlay(UNDERWEAR_LAYER) - if(lip_style && (LIPS in dna.species.species_traits)) - var/icon/lips = icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "lips_[lip_style]_s") - lips.Blend(lip_color, ICON_ADD) - standing += mutable_appearance(lips, layer = -BODY_LAYER) - overlays_standing[BODY_LAYER] = standing apply_overlay(BODY_LAYER) //tail @@ -964,10 +959,6 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) var/mutable_appearance/standing if(back.icon_override) standing = mutable_appearance(back.icon_override, "[back.icon_state]", layer = -BACK_LAYER) - else if(istype(back, /obj/item/rig)) - //If this is a rig and a mob_icon is set, it will take species into account in the rig update_icon() proc. - var/obj/item/rig/rig = back - standing = rig.mob_icon else if(back.sprite_sheets && back.sprite_sheets[dna.species.name]) standing = mutable_appearance(back.sprite_sheets[dna.species.name], "[back.icon_state]", layer = -BACK_LAYER) else @@ -1307,6 +1298,11 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) else . += "#000000" + if(lip_color && (LIPS in dna.species.species_traits)) + . += "[lip_color]" + else + . += "#000000" + for(var/organ_tag in dna.species.has_limbs) var/obj/item/organ/external/part = bodyparts_by_name[organ_tag] if(isnull(part)) diff --git a/code/modules/mob/living/carbon/superheroes.dm b/code/modules/mob/living/carbon/superheroes.dm index 74cf0bd37a7..32a562b212c 100644 --- a/code/modules/mob/living/carbon/superheroes.dm +++ b/code/modules/mob/living/carbon/superheroes.dm @@ -10,7 +10,7 @@ var/list/default_genes = list(REGEN, BREATHLESS, COLDRES) var/list/default_spells = list() var/activated = FALSE //for wishgranters to not give an option if someone already has it. - + /datum/superheroes/proc/create(var/mob/living/carbon/human/H) assign_genes(H) assign_spells(H) @@ -84,7 +84,7 @@ /datum/superheroes/griffin name = "The Griffin" - default_spells = list(/obj/effect/proc_holder/spell/targeted/recruit) + default_spells = list(/obj/effect/proc_holder/spell/targeted/click/recruit) class = "Supervillain" desc = "You are The Griffin, the ultimate supervillain. You thrive on chaos and have no respect for the supposed authority \ of the command staff of this station. Along with your gang of dim-witted yet trusty henchmen, you will be able to execute \ @@ -145,100 +145,102 @@ //The Griffin's special recruit abilitiy -/obj/effect/proc_holder/spell/targeted/recruit +/obj/effect/proc_holder/spell/targeted/click/recruit name = "Recruit Greyshirt" desc = "Allows you to recruit a conscious, non-braindead, non-catatonic human to be part of the Greyshirts, your personal henchmen. This works on Civilians only and you can recruit a maximum of 3!." charge_max = 450 - clothes_req = 0 + clothes_req = FALSE range = 1 //Adjacent to user action_icon_state = "spell_greytide" var/recruiting = 0 -/obj/effect/proc_holder/spell/targeted/recruit/cast(list/targets,mob/living/user = usr) - for(var/mob/living/carbon/human/target in targets) - var/obj/item/organ/external/head/head_organ = target.get_organ("head") - if(SSticker.mode.greyshirts.len >= 3) + click_radius = -1 + selection_activated_message = "You start preparing a mindblowing monologue. Left-click to cast at a target!" + selection_deactivated_message = "You decide to save your brilliance for another day." + allowed_type = /mob/living/carbon/human + +/obj/effect/proc_holder/spell/targeted/click/recruit/can_cast(mob/user = usr, charge_check = TRUE, show_message = FALSE) + if(SSticker.mode.greyshirts.len >= 3) + if(show_message) to_chat(user, "You have already recruited the maximum number of henchmen.") - if(!in_range(user, target)) - to_chat(user, "You need to be closer to enthrall [target].") - charge_counter = charge_max - return - if(!target.ckey) - to_chat(user, "The target has no mind.") - charge_counter = charge_max - return - if(target.stat) - to_chat(user, "The target must be conscious.") - charge_counter = charge_max - return - if(!ishuman(target)) - to_chat(user, "You can only recruit humans.") - charge_counter = charge_max - return - if(target.mind.assigned_role != "Civilian") - to_chat(user, "You can only recruit Civilians.") - return - if(recruiting) + return FALSE + if(recruiting) + if(show_message) to_chat(user, "You are already recruiting!") - charge_counter = charge_max + return FALSE + return ..() + +/obj/effect/proc_holder/spell/targeted/click/recruit/valid_target(mob/living/carbon/human/target, user) + if(!..()) + return FALSE + + return target.ckey && !target.stat + +/obj/effect/proc_holder/spell/targeted/click/recruit/cast(list/targets,mob/living/user = usr) + var/mob/living/carbon/human/target = targets[1] + if(target.mind.assigned_role != "Civilian") + to_chat(user, "You can only recruit Civilians.") + revert_cast(user) + return + recruiting = TRUE + to_chat(user, "This target is valid. You begin the recruiting process.") + to_chat(target, "[user] focuses in concentration. Your head begins to ache.") + + for(var/progress = 0, progress <= 3, progress++) + switch(progress) + if(1) + to_chat(user, "You begin by introducing yourself and explaining what you're about.") + user.visible_message("[user] introduces [user.p_them()]self and explains [user.p_their()] plans.") + if(2) + to_chat(user, "You begin the recruitment of [target].") + user.visible_message("[user] leans over towards [target], whispering excitedly as [user.p_they()] give[user.p_s()] a speech.") + to_chat(target, "You feel yourself agreeing with [user], and a surge of loyalty begins building.") + target.Weaken(12) + sleep(20) + if(ismindshielded(target)) + to_chat(user, "[target.p_they(TRUE)] are enslaved by Nanotrasen. You feel [target.p_their()] interest in your cause wane and disappear.") + user.visible_message("[user] stops talking for a moment, then moves back away from [target].") + to_chat(target, "Your mindshield implant activates, protecting you from conversion.") + return + if(3) + to_chat(user, "You begin filling out the application form with [target].") + user.visible_message("[user] pulls out a pen and paper and begins filling an application form with [target].") + to_chat(target, "You are being convinced by [user] to fill out an application form to become a henchman.")//Ow the edge + + if(!do_mob(user, target, 100)) //around 30 seconds total for enthralling, 45 for someone with a mindshield implant + to_chat(user, "The enrollment process has been interrupted - you have lost the attention of [target].") + to_chat(target, "You move away and are no longer under the charm of [user]. The application form is null and void.") + recruiting = FALSE return - recruiting = 1 - to_chat(user, "This target is valid. You begin the recruiting process.") - to_chat(target, "[user] focuses in concentration. Your head begins to ache.") - for(var/progress = 0, progress <= 3, progress++) - switch(progress) - if(1) - to_chat(user, "You begin by introducing yourself and explaining what you're about.") - user.visible_message("[user] introduces [user.p_them()]self and explains [user.p_their()] plans.") - if(2) - to_chat(user, "You begin the recruitment of [target].") - user.visible_message("[user] leans over towards [target], whispering excitedly as [user.p_they()] give[user.p_s()] a speech.") - to_chat(target, "You feel yourself agreeing with [user], and a surge of loyalty begins building.") - target.Weaken(12) - sleep(20) - if(ismindshielded(target)) - to_chat(user, "[target.p_they(TRUE)] are enslaved by Nanotrasen. You feel [target.p_their()] interest in your cause wane and disappear.") - user.visible_message("[user] stops talking for a moment, then moves back away from [target].") - to_chat(target, "Your mindshield implant activates, protecting you from conversion.") - return - if(3) - to_chat(user, "You begin filling out the application form with [target].") - user.visible_message("[user] pulls out a pen and paper and begins filling an application form with [target].") - to_chat(target, "You are being convinced by [user] to fill out an application form to become a henchman.")//Ow the edge - - if(!do_mob(user, target, 100)) //around 30 seconds total for enthralling, 45 for someone with a mindshield implant - to_chat(user, "The enrollment process has been interrupted - you have lost the attention of [target].") - to_chat(target, "You move away and are no longer under the charm of [user]. The application form is null and void.") - recruiting = 0 - return - - recruiting = 0 - to_chat(user, "You have recruited [target] as your henchman!") - to_chat(target, "You have decided to enroll as a henchman for [user]. You are now part of the feared 'Greyshirts'.") - to_chat(target, "You must follow the orders of [user], and help [user.p_them()] succeed in [user.p_their()] dastardly schemes.") - to_chat(target, "You may not harm other Greyshirt or [user]. However, you do not need to obey other Greyshirts.") - SSticker.mode.greyshirts += target.mind - target.set_species(/datum/species/human) + recruiting = FALSE + to_chat(user, "You have recruited [target] as your henchman!") + to_chat(target, "You have decided to enroll as a henchman for [user]. You are now part of the feared 'Greyshirts'.") + to_chat(target, "You must follow the orders of [user], and help [user.p_them()] succeed in [user.p_their()] dastardly schemes.") + to_chat(target, "You may not harm other Greyshirt or [user]. However, you do not need to obey other Greyshirts.") + SSticker.mode.greyshirts += target.mind + target.set_species(/datum/species/human) + var/obj/item/organ/external/head/head_organ = target.get_organ("head") + if(head_organ) head_organ.h_style = "Bald" head_organ.f_style = "Shaved" - target.s_tone = 35 - // No `update_dna=0` here because the character is being over-written - target.change_eye_color(1,1,1) - for(var/obj/item/W in target.get_all_slots()) - target.unEquip(W) - target.rename_character(target.real_name, "Generic Henchman ([rand(1, 1000)])") - target.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey/greytide(target), slot_w_uniform) - target.equip_to_slot_or_del(new /obj/item/clothing/shoes/black/greytide(target), slot_shoes) - target.equip_to_slot_or_del(new /obj/item/storage/toolbox/mechanical/greytide(target), slot_l_hand) - target.equip_to_slot_or_del(new /obj/item/radio/headset(target), slot_l_ear) - var/obj/item/card/id/syndicate/W = new(target) - W.icon_state = "lifetimeid" - W.access = list(ACCESS_MAINT_TUNNELS) - W.assignment = "Greyshirt" - W.rank = "Greyshirt" - W.flags |= NODROP - W.SetOwnerInfo(target) - W.UpdateName() - target.equip_to_slot_or_del(W, slot_wear_id) - target.regenerate_icons() + target.s_tone = 35 + // No `update_dna=0` here because the character is being over-written + target.change_eye_color(1,1,1) + for(var/obj/item/W in target.get_all_slots()) + target.unEquip(W) + target.rename_character(target.real_name, "Generic Henchman ([rand(1, 1000)])") + target.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey/greytide(target), slot_w_uniform) + target.equip_to_slot_or_del(new /obj/item/clothing/shoes/black/greytide(target), slot_shoes) + target.equip_to_slot_or_del(new /obj/item/storage/toolbox/mechanical/greytide(target), slot_l_hand) + target.equip_to_slot_or_del(new /obj/item/radio/headset(target), slot_l_ear) + var/obj/item/card/id/syndicate/W = new(target) + W.icon_state = "lifetimeid" + W.access = list(ACCESS_MAINT_TUNNELS) + W.assignment = "Greyshirt" + W.rank = "Greyshirt" + W.flags |= NODROP + W.SetOwnerInfo(target) + W.UpdateName() + target.equip_to_slot_or_del(W, slot_wear_id) + target.regenerate_icons() diff --git a/code/modules/mob/living/carbon/update_icons.dm b/code/modules/mob/living/carbon/update_icons.dm index cb607bae294..c3d0ca679d4 100644 --- a/code/modules/mob/living/carbon/update_icons.dm +++ b/code/modules/mob/living/carbon/update_icons.dm @@ -29,7 +29,7 @@ if(changed) animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, dir = final_dir, easing = EASE_IN|EASE_OUT) handle_transform_change() - floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life(). + floating = FALSE // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life(). /mob/living/carbon/proc/handle_transform_change() return diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 6af90792355..09cc812eae3 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -51,6 +51,7 @@ // Whew! Good thing I'm indestructible! (or already dead) return FALSE + ..() stat = DEAD SetDizzy(0) SetJitter(0) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 3062ef461f9..c0341f11b47 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -2,9 +2,8 @@ set waitfor = FALSE set invisibility = 0 - if(flying) //TODO: Better floating - animate(src, pixel_y = pixel_y + 5 , time = 10, loop = 1, easing = SINE_EASING) - animate(pixel_y = pixel_y - 5, time = 10, loop = 1, easing = SINE_EASING) + if(flying && !floating) //TODO: Better floating + float(TRUE) if(client || registered_z) // This is a temporary error tracker to make sure we've caught everything var/turf/T = get_turf(src) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f3d23ca3f51..883b781c43c 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -3,8 +3,29 @@ var/datum/atom_hud/data/human/medical/advanced/medhud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] medhud.add_to_hud(src) faction += "\ref[src]" + determine_move_and_pull_forces() GLOB.mob_living_list += src +// Used to determine the forces dependend on the mob size +// Will only change the force if the force was not set in the mob type itself +/mob/living/proc/determine_move_and_pull_forces() + var/value + switch(mob_size) + if(MOB_SIZE_TINY) + value = MOVE_FORCE_EXTREMELY_WEAK + if(MOB_SIZE_SMALL) + value = MOVE_FORCE_WEAK + if(MOB_SIZE_HUMAN) + value = MOVE_FORCE_NORMAL + if(MOB_SIZE_LARGE) + value = MOVE_FORCE_NORMAL // For now + if(!move_force) + move_force = value + if(!pull_force) + pull_force = value + if(!move_resist) + move_resist = value + /mob/living/prepare_huds() ..() prepare_data_huds() @@ -59,8 +80,9 @@ //Even if we don't push/swap places, we "touched" them, so spread fire spreadFire(M) - if(now_pushing) - return 1 + // No pushing if we're already pushing past something, or if the mob we're pushing into is anchored. + if(now_pushing || M.anchored) + return TRUE //Should stop you pushing a restrained person out of the way if(isliving(M)) @@ -68,7 +90,7 @@ if(L.pulledby && L.pulledby != src && L.restrained()) if(!(world.time % 5)) to_chat(src, "[L] is restrained, you cannot push past.") - return 1 + return TRUE if(L.pulling) if(ismob(L.pulling)) @@ -76,28 +98,28 @@ if(P.restrained()) if(!(world.time % 5)) to_chat(src, "[L] is restrained, you cannot push past.") - return 1 + return TRUE if(moving_diagonally) //no mob swap during diagonal moves. - return 1 + return TRUE if(a_intent == INTENT_HELP) // Help intent doesn't mob swap a mob pulling a structure if(isstructure(M.pulling) || isstructure(pulling)) - return 1 + return TRUE if(!M.buckled && !M.has_buckled_mobs()) var/mob_swap //the puller can always swap with it's victim if on grab intent if(M.pulledby == src && a_intent == INTENT_GRAB) - mob_swap = 1 + mob_swap = TRUE //restrained people act if they were on 'help' intent to prevent a person being pulled from being seperated from their puller else if((M.restrained() || M.a_intent == INTENT_HELP) && (restrained() || a_intent == INTENT_HELP)) - mob_swap = 1 + mob_swap = TRUE if(mob_swap) //switch our position with M if(loc && !loc.Adjacent(M.loc)) - return 1 - now_pushing = 1 + return TRUE + now_pushing = TRUE var/oldloc = loc var/oldMloc = M.loc @@ -114,18 +136,18 @@ if(!M_passmob) M.pass_flags &= ~PASSMOB - now_pushing = 0 - return 1 + now_pushing = FALSE + return TRUE // okay, so we didn't switch. but should we push? // not if he's not CANPUSH of course if(!(M.status_flags & CANPUSH)) - return 1 + return TRUE //anti-riot equipment is also anti-push if(M.r_hand && (prob(M.r_hand.block_chance * 2)) && !istype(M.r_hand, /obj/item/clothing)) - return 1 + return TRUE if(M.l_hand && (prob(M.l_hand.block_chance * 2)) && !istype(M.l_hand, /obj/item/clothing)) - return 1 + return TRUE //Called when we bump into an obj /mob/living/proc/ObjBump(obj/O) @@ -175,13 +197,6 @@ AM.setDir(current_dir) now_pushing = FALSE -/mob/living/Stat() - . = ..() - if(. && get_rig_stats) - var/obj/item/rig/rig = get_rig() - if(rig) - SetupStat(rig) - /mob/living/proc/can_track(mob/living/user) //basic fast checks go first. When overriding this proc, I recommend calling ..() at the end. var/turf/T = get_turf(src) @@ -209,7 +224,7 @@ set category = "Object" if(istype(AM) && Adjacent(AM)) - start_pulling(AM) + start_pulling(AM, show_message = TRUE) else stop_pulling() @@ -743,16 +758,17 @@ /mob/living/proc/float(on) if(throwing) return - var/fixed = 0 + var/fixed = FALSE if(anchored || (buckled && buckled.anchored)) - fixed = 1 + fixed = TRUE if(on && !floating && !fixed) animate(src, pixel_y = pixel_y + 2, time = 10, loop = -1) - floating = 1 + sleep(10) + animate(src, pixel_y = pixel_y - 2, time = 10, loop = -1) + floating = TRUE else if(((!on || fixed) && floating)) - var/final_pixel_y = get_standard_pixel_y_offset(lying) - animate(src, pixel_y = final_pixel_y, time = 10) - floating = 0 + animate(src, pixel_y = get_standard_pixel_y_offset(lying), time = 10) + floating = FALSE /mob/living/proc/can_use_vents() return "You can't fit into that vent." @@ -827,15 +843,17 @@ if(!used_item) used_item = get_active_hand() ..() - floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement. + floating = FALSE // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement. /mob/living/proc/do_jitter_animation(jitteriness, loop_amount = 6) - var/amplitude = min(4, (jitteriness/100) + 1) + var/amplitude = min(4, (jitteriness / 100) + 1) var/pixel_x_diff = rand(-amplitude, amplitude) - var/pixel_y_diff = rand(-amplitude/3, amplitude/3) + var/pixel_y_diff = rand(-amplitude / 3, amplitude / 3) + var/final_pixel_x = get_standard_pixel_x_offset(lying) + var/final_pixel_y = get_standard_pixel_y_offset(lying) animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff , time = 2, loop = loop_amount) - animate(pixel_x = initial(pixel_x) , pixel_y = initial(pixel_y) , time = 2) - floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement. + animate(pixel_x = final_pixel_x , pixel_y = final_pixel_y , time = 2) + floating = FALSE // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement. /mob/living/proc/get_temperature(datum/gas_mixture/environment) @@ -909,6 +927,8 @@ . += T.slowdown if(slowed) . += 10 + if(forced_look) + . += 3 if(ignorewalk) . += config.run_speed else @@ -927,10 +947,10 @@ return 0 return 1 -/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE) +/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) if(!AM || !src) return FALSE - if(!(AM.can_be_pulled(src, state, force))) + if(!(AM.can_be_pulled(src, state, force, show_message))) return FALSE if(incapacitated()) return diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index b38f1819524..9ecda42de04 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -113,7 +113,7 @@ var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].", I.armour_penetration) apply_damage(I.throwforce, dtype, zone, armor, is_sharp(I), I) if(I.thrownby) - add_attack_logs(I.thrownby, src, "Hit with thrown [I]") + add_attack_logs(I.thrownby, src, "Hit with thrown [I]", !I.throwforce ? ATKLOG_ALMOSTALL : null) // Only message if the person gets damages else return 1 else @@ -186,6 +186,8 @@ return 1 if(fire_stacks > 0) adjust_fire_stacks(-0.1) //the fire is slowly consumed + for(var/obj/item/clothing/C in contents) + C.catch_fire() else ExtinguishMob() return @@ -219,7 +221,7 @@ fire_stacks += L.fire_stacks IgniteMob() -/mob/living/can_be_pulled(user, grab_state, force) +/mob/living/can_be_pulled(user, grab_state, force, show_message = FALSE) return ..() && !(buckled && buckled.buckle_prevents_pull) /mob/living/water_act(volume, temperature, source, method = REAGENT_TOUCH) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index e230f7a2b99..f8a5f08b313 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -2,6 +2,11 @@ see_invisible = SEE_INVISIBLE_LIVING pressure_resistance = 10 + // Will be determined based on mob size if left null. Done in living/proc/determine_move_and_pull_forces() + move_resist = null + move_force = null + pull_force = null + //Health and life related vars var/maxHealth = 100 //Maximum health that should be possible. var/health = 100 //A mob's health @@ -28,7 +33,7 @@ var/on_fire = 0 //The "Are we on fire?" var var/fire_stacks = 0 //Tracks how many stacks of fire we have on, max is usually 20 - var/floating = 0 + var/floating = FALSE var/mob_size = MOB_SIZE_HUMAN var/metabolism_efficiency = 1 //more or less efficiency to metabolize helpful/harmful reagents and regulate body temperature.. var/digestion_ratio = 1 //controls how quickly reagents metabolize; largely governered by species attributes. diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 630b2223321..f9d6fc436cf 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -38,7 +38,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list( GLOBAL_LIST_EMPTY(channel_to_radio_key) -proc/get_radio_key_from_channel(var/channel) +/proc/get_radio_key_from_channel(var/channel) var/key = GLOB.channel_to_radio_key[channel] if(!key) for(var/radio_key in GLOB.department_radio_keys) @@ -351,8 +351,6 @@ proc/get_radio_key_from_channel(var/channel) return if(stat) - if(stat == DEAD) - return say_dead(message_pieces) return if(is_muzzled()) @@ -451,14 +449,14 @@ proc/get_radio_key_from_channel(var/channel) var/speech_bubble_test = say_test(message) for(var/mob/M in listening) - M.hear_say(message_pieces, verb, italics, src) + M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE) if(M.client) speech_bubble_recipients.Add(M.client) if(eavesdropping.len) stars_all(message_pieces) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less. for(var/mob/M in eavesdropping) - M.hear_say(message_pieces, verb, italics, src) + M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE) if(M.client) speech_bubble_recipients.Add(M.client) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 1c232f9f43c..cebac878c6b 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -50,7 +50,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( var/list/connected_robots = list() var/aiRestorePowerRoutine = 0 //var/list/laws = list() - var/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list()) + alarms_listend_for = list("Motion", "Fire", "Atmosphere", "Power", "Camera", "Burglar") var/viewalerts = 0 var/icon/holo_icon//Default is assigned when AI is created. var/obj/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye. @@ -63,6 +63,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( //MALFUNCTION var/datum/module_picker/malf_picker + var/datum/action/innate/ai/choose_modules/modules_action var/list/datum/AI_Module/current_modules = list() var/can_dominate_mechs = 0 var/shunted = 0 //1 if the AI is currently shunted. Used to differentiate between shunted and ghosted/braindead @@ -172,19 +173,19 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( add_language("Galactic Common", 1) add_language("Sol Common", 1) add_language("Tradeband", 1) - add_language("Neo-Russkiya", 0) - add_language("Gutter", 0) - add_language("Sinta'unathi", 0) - add_language("Siik'tajr", 0) - add_language("Canilunzt", 0) - add_language("Skrellian", 0) - add_language("Vox-pidgin", 0) - add_language("Orluum", 0) - add_language("Rootspeak", 0) + add_language("Neo-Russkiya", 1) + add_language("Gutter", 1) + add_language("Sinta'unathi", 1) + add_language("Siik'tajr", 1) + add_language("Canilunzt", 1) + add_language("Skrellian", 1) + add_language("Vox-pidgin", 1) + add_language("Orluum", 1) + add_language("Rootspeak", 1) add_language("Trinary", 1) - add_language("Chittin", 0) - add_language("Bubblish", 0) - add_language("Clownish", 0) + add_language("Chittin", 1) + add_language("Bubblish", 1) + add_language("Clownish", 1) if(!safety)//Only used by AIize() to successfully spawn an AI. if(!B)//If there is no player/brain inside. @@ -241,6 +242,46 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( return show_borg_info() +/mob/living/silicon/ai/proc/ai_alerts() + var/list/dat = list("Current Station Alerts\n") + dat += "Close

    " + var/list/list/temp_alarm_list = SSalarm.alarms.Copy() + for(var/cat in temp_alarm_list) + if(!(cat in alarms_listend_for)) + continue + dat += text("[]
    \n", cat) + var/list/list/L = temp_alarm_list[cat].Copy() + for(var/alarm in L) + var/list/list/alm = L[alarm].Copy() + var/area_name = alm[1] + var/C = alm[2] + var/list/list/sources = alm[3].Copy() + for(var/thing in sources) + var/atom/A = locateUID(thing) + if(A && A.z != z) + L -= alarm + continue + dat += "" + if(C && islist(C)) + var/dat2 = "" + for(var/cam in C) + var/obj/machinery/camera/I = locateUID(cam) + if(!QDELETED(I)) + dat2 += text("[][]", (dat2 == "") ? "" : " | ", I.c_tag) + dat += text("-- [] ([])", area_name, (dat2 != "") ? dat2 : "No Camera") + else + dat += text("-- [] (No Camera)", area_name) + if(sources.len > 1) + dat += text("- [] sources", sources.len) + dat += "
    \n" + if(!L.len) + dat += "-- All Systems Nominal
    \n" + dat += "
    \n" + + viewalerts = TRUE + var/dat_text = dat.Join("") + src << browse(dat_text, "window=aialerts&can_close=0") + /mob/living/silicon/ai/proc/show_borg_info() stat(null, text("Connected cyborgs: [connected_robots.len]")) for(var/thing in connected_robots) @@ -611,7 +652,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( if(href_list["switchcamera"]) switchCamera(locate(href_list["switchcamera"])) in GLOB.cameranet.cameras if(href_list["showalerts"]) - subsystem_alarm_monitor() + ai_alerts() if(href_list["show_paper"]) if(last_paper_seen) src << browse(last_paper_seen, "window=show_paper") @@ -786,12 +827,49 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( Bot.call_bot(src, waypoint) +/mob/living/silicon/ai/alarm_triggered(src, class, area/A, list/O, obj/alarmsource) + if(!(class in alarms_listend_for)) + return + if(alarmsource.z != z) + return + if(stat == DEAD) + return TRUE + if(O) + var/obj/machinery/camera/C = locateUID(O[1]) + if(O.len == 1 && !QDELETED(C) && C.can_use()) + queueAlarm("--- [class] alarm detected in [A.name]! ([C.c_tag])", class) + else if(O && O.len) + var/foo = 0 + var/dat2 = "" + for(var/thing in O) + var/obj/machinery/camera/I = locateUID(thing) + if(!QDELETED(I)) + dat2 += text("[][]", (!foo) ? "" : " | ", I.c_tag) //I'm not fixing this shit... + foo = 1 + queueAlarm(text ("--- [] alarm detected in []! ([])", class, A.name, dat2), class) + else + queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class) + else + queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class) + if(viewalerts) + ai_alerts() + +/mob/living/silicon/ai/alarm_cancelled(src, class, area/A, obj/origin, cleared) + if(cleared) + if(!(class in alarms_listend_for)) + return + if(origin.z != z) + return + queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0) + if(viewalerts) + ai_alerts() + /mob/living/silicon/ai/proc/switchCamera(obj/machinery/camera/C) if(!tracking) cameraFollow = null - if(!C || stat == DEAD) //C.can_use()) + if(QDELETED(C) || stat == DEAD) //C.can_use()) return FALSE if(!eyeobj) @@ -851,13 +929,6 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( to_chat(src, "Switched to [network] camera network.") //End of code by Mord_Sith - -/mob/living/silicon/ai/proc/choose_modules() - set category = "Malfunction" - set name = "Choose Module" - - malf_picker.use(src) - /mob/living/silicon/ai/proc/ai_statuschange() set category = "AI Commands" set name = "AI Status" @@ -1028,15 +1099,6 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( return -/mob/living/silicon/ai/proc/corereturn() - set category = "Malfunction" - set name = "Return to Main Core" - - var/obj/machinery/power/apc/apc = loc - if(!istype(apc)) - to_chat(src, "You are already in your Main Core.") - return - apc.malfvacate() //Toggles the luminosity and applies it by re-entereing the camera. /mob/living/silicon/ai/proc/toggle_camera_light() @@ -1181,16 +1243,6 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( /mob/living/silicon/ai/can_buckle() return FALSE -// Pass lying down or getting up to our pet human, if we're in a rig. -/mob/living/silicon/ai/lay_down() - set name = "Rest" - set category = "IC" - - resting = 0 - var/obj/item/rig/rig = get_rig() - if(rig) - rig.force_rest(src) - /mob/living/silicon/ai/switch_to_camera(obj/machinery/camera/C) if(!C.can_use() || !is_in_chassis()) return FALSE @@ -1244,8 +1296,17 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( to_chat(src, "In the top right corner of the screen you will find the Malfunctions tab, where you can purchase various abilities, from upgraded surveillance to station ending doomsday devices.") to_chat(src, "You are also capable of hacking APCs, which grants you more points to spend on your Malfunction powers. The drawback is that a hacked APC will give you away if spotted by the crew. Hacking an APC takes 60 seconds.") view_core() //A BYOND bug requires you to be viewing your core before your verbs update - verbs += /mob/living/silicon/ai/proc/choose_modules malf_picker = new /datum/module_picker + modules_action = new(malf_picker) + modules_action.Grant(src) + +///Removes all malfunction-related /datum/action's from the target AI. +/mob/living/silicon/ai/proc/remove_malf_abilities() + QDEL_NULL(modules_action) + for(var/datum/AI_Module/AM in current_modules) + for(var/datum/action/A in actions) + if(istype(A, initial(AM.power_type))) + qdel(A) /mob/living/silicon/ai/proc/open_nearest_door(mob/living/target) if(!istype(target)) @@ -1271,8 +1332,10 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( if(istype(A)) switch(alert(src, "Do you want to open \the [A] for [target]?", "Doorknob_v2a.exe", "Yes", "No")) if("Yes") - A.AIShiftClick() - to_chat(src, "You open \the [A] for [target].") + if(!A.density) + to_chat(src, "[A] was already opened.") + else if(A.open_close(src)) + to_chat(src, "You open \the [A] for [target].") else to_chat(src, "You deny the request.") else diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index d23fedb09bc..faacf9ba359 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -142,7 +142,7 @@ acceleration = !acceleration to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].") -/mob/camera/aiEye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) +/mob/camera/aiEye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) if(relay_speech) if(istype(ai)) ai.relay_speech(speaker, message_pieces, verb) diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 61151e35c92..106eaf86d37 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -132,8 +132,6 @@ sleep(50) theAPC = null - process_queued_alarms() - /mob/living/silicon/ai/updatehealth(reason = "none given") if(status_flags & GODMODE) health = 100 diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index 599063c2724..a4e90b50f8e 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -80,17 +80,26 @@ GLOBAL_VAR_INIT(announcing_vox, 0) // Stores the time of the last announcement
  • Do not use punctuation as you would normally, if you want a pause you can use the full stop and comma characters by separating them with spaces, like so: 'Alpha . Test , Bravo'.
  • \ WARNING:
    Misuse of the announcement system will get you job banned.
    " - var/index = 0 - for(var/word in GLOB.vox_sounds) - index++ - dat += "[capitalize(word)]" - if(index != GLOB.vox_sounds.len) - dat += " / " + // Show alert and voice sounds separately + var/vox_words = GLOB.vox_sounds - GLOB.vox_alerts + dat = help_format(GLOB.vox_alerts, dat) + dat = help_format(vox_words, dat) var/datum/browser/popup = new(src, "announce_help", "Announcement Help", 500, 400) popup.set_content(dat) popup.open() +/mob/living/silicon/ai/proc/help_format(word_list, dat) + var/index = 0 + for(var/word in word_list) + index++ + dat += "[capitalize(word)]" + if(index != length(word_list)) + dat += " / " + else + dat += "
    " + return dat + /mob/living/silicon/ai/proc/ai_announcement() if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) return diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 3d1293d2a34..0a4e4cee898 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -435,9 +435,6 @@ // Pass lying down or getting up to our pet human, if we're in a rig. if(stat == CONSCIOUS && istype(loc,/obj/item/paicard)) resting = 0 - var/obj/item/rig/rig = get_rig() - if(istype(rig)) - rig.force_rest(src) else resting = !resting to_chat(src, "You are now [resting ? "resting" : "getting up"]") @@ -520,7 +517,7 @@ /mob/living/silicon/pai/Bumped() return -/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) +/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) return FALSE /mob/living/silicon/pai/update_canmove(delay_action_updates = 0) @@ -566,6 +563,10 @@ var/obj/item/holder/H = ..() if(!istype(H)) return + if(stat == DEAD) + H.icon = 'icons/mob/pai.dmi' + H.icon_state = "[chassis]_dead" + return if(resting) icon_state = "[chassis]" resting = 0 diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index 2a1662b064d..6cb0cc53bd0 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -243,7 +243,7 @@ add_fingerprint(user) -proc/robot_healthscan(mob/user, mob/living/M) +/proc/robot_healthscan(mob/user, mob/living/M) var/scan_type if(istype(M, /mob/living/silicon/robot)) scan_type = "robot" @@ -312,8 +312,3 @@ proc/robot_healthscan(mob/user, mob/living/M) to_chat(user, "[capitalize(O.name)]: [O.damage]") if(!organ_found) to_chat(user, "No prosthetics located.") - - if(ismachineperson(H)) - to_chat(user, "Internal Fluid Level:[H.blood_volume]/[H.max_blood]") - if(H.bleed_rate) - to_chat(user, "Warning:External component leak detected!") diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index 72d9b210712..65847c3a9b1 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -53,7 +53,7 @@ emote("deathgasp", force = TRUE) if(module) - module.handle_death(gibbed) + module.handle_death(src, gibbed) // Only execute the below if we successfully died . = ..(gibbed) diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index c62bb638244..a8485853be1 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -19,6 +19,7 @@ ventcrawler = 2 magpulse = 1 mob_size = MOB_SIZE_SMALL + pull_force = MOVE_FORCE_VERY_WEAK // Can only drag small items modules_break = FALSE @@ -34,7 +35,14 @@ var/mail_destination = 0 var/reboot_cooldown = 60 // one minute var/last_reboot - var/emagged_time + var/list/pullable_drone_items = list( + /obj/item/pipe, + /obj/structure/disposalconstruct, + /obj/item/stack/cable_coil, + /obj/item/stack/rods, + /obj/item/stack/sheet, + /obj/item/stack/tile + ) holder_type = /obj/item/holder/drone // var/sprite[0] @@ -50,7 +58,7 @@ // Disable the microphone wire on Drones if(radio) - radio.wires.CutWireIndex(RADIO_WIRE_TRANSMIT) + radio.wires.cut(WIRE_RADIO_TRANSMIT) if(camera && ("Robots" in camera.network)) camera.network.Add("Engineering") @@ -69,6 +77,10 @@ verbs -= /mob/living/silicon/robot/verb/Namepick module = new /obj/item/robot_module/drone(src) + //Allows Drones to hear the Engineering channel. + module.channels = list("Engineering" = 1) + radio.recalculateChannels() + //Grab stacks. stack_metal = locate(/obj/item/stack/sheet/metal/cyborg) in src.module stack_wood = locate(/obj/item/stack/sheet/wood) in src.module @@ -114,6 +126,11 @@ /mob/living/silicon/robot/drone/pick_module() return +/mob/living/silicon/robot/drone/can_be_revived() + . = ..() + if(emagged) + return FALSE + //Drones cannot be upgraded with borg modules so we need to catch some items before they get used in ..(). /mob/living/silicon/robot/drone/attackby(obj/item/W as obj, mob/user as mob, params) @@ -154,15 +171,17 @@ return else - user.visible_message("\the [user] swipes [user.p_their()] ID card through [src], attempting to shut it down.", "You swipe your ID card through \the [src], attempting to shut it down.") + var/confirm = alert("Using your ID on a Maintenance Drone will shut it down, are you sure you want to do this?", "Disable Drone", "Yes", "No") + if(confirm == ("Yes") && (user in range(3, src))) + user.visible_message("\the [user] swipes [user.p_their()] ID card through [src], attempting to shut it down.", "You swipe your ID card through \the [src], attempting to shut it down.") - if(emagged) - return + if(emagged) + return - if(allowed(W)) - shut_down() - else - to_chat(user, "Access denied.") + if(allowed(W)) + shut_down() + else + to_chat(user, "Access denied.") return @@ -193,8 +212,8 @@ log_game("[key_name(user)] emagged drone [key_name(src)]. Laws overridden.") var/time = time2text(world.realtime,"hh:mm:ss") GLOB.lawchanges.Add("[time] : [H.name]([H.key]) emagged [name]([key])") + addtimer(CALLBACK(src, .proc/shut_down, TRUE), EMAG_TIMER) - emagged_time = world.time emagged = 1 density = 1 pass_flags = 0 @@ -321,20 +340,22 @@ /mob/living/silicon/robot/drone/Bumped(atom/movable/AM) return -/mob/living/silicon/robot/drone/start_pulling(var/atom/movable/AM) +/mob/living/silicon/robot/drone/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) + + if(is_type_in_list(AM, pullable_drone_items)) + ..(AM, force = INFINITY) // Drone power! Makes them able to drag pipes and such - if(istype(AM,/obj/item/pipe) || istype(AM,/obj/structure/disposalconstruct)) - ..() else if(istype(AM,/obj/item)) var/obj/item/O = AM if(O.w_class > WEIGHT_CLASS_SMALL) - to_chat(src, "You are too small to pull that.") + if(show_message) + to_chat(src, "You are too small to pull that.") return else ..() else - to_chat(src, "You are too small to pull that.") - return + if(show_message) + to_chat(src, "You are too small to pull that.") /mob/living/silicon/robot/drone/add_robot_verbs() src.verbs |= silicon_subsystems @@ -344,13 +365,25 @@ /mob/living/silicon/robot/drone/update_canmove(delay_action_updates = 0) . = ..() - if(emagged) - density = 1 - if(world.time - emagged_time > EMAG_TIMER) - shut_down(TRUE) - return - density = 0 //this is reset every canmove update otherwise + density = emagged //this is reset every canmove update otherwise /mob/living/simple_animal/drone/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0) if(affect_silicon) return ..() + +/mob/living/silicon/robot/drone/decompile_act(obj/item/matter_decompiler/C, mob/user) + if(!client && istype(user, /mob/living/silicon/robot/drone)) + to_chat(user, "You begin decompiling the other drone.") + if(!do_after(user, 5 SECONDS, target = loc)) + to_chat(user, "You need to remain still while decompiling such a large object.") + return + if(QDELETED(src) || QDELETED(user)) + return ..() + to_chat(user, "You carefully and thoroughly decompile your downed fellow, storing as much of its resources as you can within yourself.") + new/obj/effect/decal/cleanable/blood/oil(get_turf(src)) + C.stored_comms["metal"] += 15 + C.stored_comms["glass"] += 15 + C.stored_comms["wood"] += 5 + qdel(src) + return TRUE + return ..() diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index 8f2b9fee6ae..16e81f53276 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -8,7 +8,6 @@ //Has a list of items that it can hold. var/list/can_hold = list( - /obj/item/stock_parts/cell, /obj/item/firealarm_electronics, /obj/item/airalarm_electronics, /obj/item/airlock_electronics, @@ -24,6 +23,8 @@ /obj/item/mounted/frame/firealarm, /obj/item/mounted/frame/newscaster_frame, /obj/item/mounted/frame/intercom, + /obj/item/mounted/frame/extinguisher, + /obj/item/mounted/frame/light_switch, /obj/item/rack_parts, /obj/item/camera_assembly, /obj/item/tank, @@ -151,15 +152,13 @@ var/list/stored_comms = list( "metal" = 0, "glass" = 0, - "wood" = 0, - "plastic" = 0 + "wood" = 0 ) /obj/item/matter_decompiler/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) return -/obj/item/matter_decompiler/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, proximity, params) - +/obj/item/matter_decompiler/afterattack(atom/target, mob/living/user, proximity, params) if(!proximity) return //Not adjacent. //We only want to deal with using this on turfs. Specific items aren't important. @@ -168,101 +167,11 @@ return //Used to give the right message. - var/grabbed_something = 0 + var/grabbed_something = FALSE - for(var/mob/M in T) - if(istype(M,/mob/living/simple_animal/lizard) || istype(M,/mob/living/simple_animal/mouse)) - src.loc.visible_message("[src.loc] sucks [M] into its decompiler. There's a horrible crunching noise.","It's a bit of a struggle, but you manage to suck [M] into your decompiler. It makes a series of visceral crunching noises.") - new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) - qdel(M) - stored_comms["wood"]++ - stored_comms["wood"]++ - stored_comms["plastic"]++ - stored_comms["plastic"]++ - return - - else if(istype(M,/mob/living/silicon/robot/drone) && !M.client) - - var/mob/living/silicon/robot/drone/D = src.loc - - if(!istype(D)) - return - - to_chat(D, "You begin decompiling the other drone.") - - if(!do_after(D, 50, target = target)) - to_chat(D, "You need to remain still while decompiling such a large object.") - return - - if(!M || !D) return - - to_chat(D, "You carefully and thoroughly decompile your downed fellow, storing as much of its resources as you can within yourself.") - - qdel(M) - new/obj/effect/decal/cleanable/blood/oil(get_turf(src)) - - stored_comms["metal"] += 15 - stored_comms["glass"] += 15 - stored_comms["wood"] += 5 - stored_comms["plastic"] += 5 - - return - else - continue - - for(var/obj/W in T) - //Different classes of items give different commodities. - if(istype(W,/obj/item/cigbutt)) - stored_comms["plastic"]++ - else if(istype(W,/obj/structure/spider/spiderling)) - stored_comms["wood"]++ - stored_comms["wood"]++ - stored_comms["plastic"]++ - stored_comms["plastic"]++ - else if(istype(W,/obj/item/light)) - var/obj/item/light/L = W - if(L.status >= 2) //In before someone changes the inexplicably local defines. ~ Z - stored_comms["metal"]++ - stored_comms["glass"]++ - else - continue - else if(istype(W,/obj/effect/decal/remains/robot)) - stored_comms["metal"]++ - stored_comms["metal"]++ - stored_comms["plastic"]++ - stored_comms["plastic"]++ - stored_comms["glass"]++ - else if(istype(W,/obj/item/trash)) - stored_comms["metal"]++ - stored_comms["plastic"]++ - stored_comms["plastic"]++ - else if(istype(W,/obj/effect/decal/cleanable/blood/gibs/robot)) - stored_comms["metal"]++ - stored_comms["metal"]++ - stored_comms["glass"]++ - stored_comms["glass"]++ - else if(istype(W,/obj/item/ammo_casing)) - stored_comms["metal"]++ - else if(istype(W,/obj/item/shard)) - stored_comms["glass"]++ - stored_comms["glass"]++ - stored_comms["glass"]++ - else if(istype(W,/obj/item/reagent_containers/food/snacks/grown)) - stored_comms["wood"]++ - stored_comms["wood"]++ - stored_comms["wood"]++ - stored_comms["wood"]++ - else if(istype(W,/obj/item/broken_bottle)) - stored_comms["glass"]++ - stored_comms["glass"]++ - stored_comms["glass"]++ - else if(istype(W,/obj/item/light/tube) || istype(W,/obj/item/light/bulb)) - stored_comms["glass"]++ - else - continue - - qdel(W) - grabbed_something = 1 + for(var/atom/movable/A in T) + if(A.decompile_act(src, user)) // Each decompileable mob or obj needs to have this defined + grabbed_something = TRUE if(grabbed_something) to_chat(user, "You deploy your decompiler and clear out the contents of \the [T].") @@ -353,11 +262,5 @@ stack_wood = new /obj/item/stack/sheet/wood(src.module) stack_wood.amount = 1 stack = stack_wood - if("plastic") - if(!stack_plastic) - stack_plastic = new /obj/item/stack/sheet/plastic(src.module) - stack_plastic.amount = 1 - stack = stack_plastic - stack.amount++ decompiler.stored_comms[type]-- diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 07080e694bf..a183a8fc455 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -13,7 +13,6 @@ handle_robot_cell() process_locks() update_items() - process_queued_alarms() /mob/living/silicon/robot/proc/handle_robot_cell() @@ -46,7 +45,7 @@ /mob/living/silicon/robot/proc/handle_equipment() if(camera && !scrambledcodes) - if(stat == DEAD || wires.IsCameraCut()) + if(stat == DEAD || wires.is_cut(WIRE_BORG_CAMERA)) camera.status = 0 else camera.status = 1 diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index faa51a58ec0..6eb110ae448 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -59,7 +59,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/ear_protection = 0 var/damage_protection = 0 var/emp_protection = FALSE - var/xeno_disarm_chance = 85 var/list/force_modules = list() var/allow_rename = TRUE @@ -72,11 +71,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/list/req_access var/ident = 0 //var/list/laws = list() - var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list()) var/viewalerts = 0 var/modtype = "Default" var/lower_mod = 0 - var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N + var/datum/effect_system/spark_spread/spark_system //So they can initialize sparks whenever/N var/jeton = 0 var/low_power_mode = 0 //whether the robot has no charge left. var/weapon_lock = 0 @@ -85,6 +83,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/lockcharge //Used when locking down a borg to preserve cell charge var/speed = 0 //Cause sec borgs gotta go fast //No they dont! var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. + var/can_lock_cover = FALSE //Used to set if a borg can re-lock its cover. var/has_camera = TRUE var/pdahide = 0 //Used to hide the borg from the messenger list var/tracking_entities = 0 //The number of known entities currently accessing the internal camera @@ -112,7 +111,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/get_cell() return cell -/mob/living/silicon/robot/New(loc, syndie = FALSE, unfinished = FALSE, alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null) +/mob/living/silicon/robot/New(loc, syndie = FALSE, unfinished = FALSE, alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null) spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, src) spark_system.attach(src) @@ -134,13 +133,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( radio = new /obj/item/radio/borg(src) common_radio = radio - init(ai_to_sync_to = ai_to_sync_to) + init(alien, connect_to_AI, ai_to_sync_to) if(has_camera && !camera) camera = new /obj/machinery/camera(src) camera.c_tag = real_name camera.network = list("SS13","Robots") - if(wires.IsCameraCut()) // 5 = BORG CAMERA + if(wires.is_cut(WIRE_BORG_CAMERA)) // 5 = BORG CAMERA camera.status = 0 if(mmi == null) @@ -172,18 +171,20 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( scanner = new(src) scanner.Grant(src) -/mob/living/silicon/robot/proc/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null) +/mob/living/silicon/robot/proc/init(alien, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null) aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) make_laws() additional_law_channels["Binary"] = ":b " + if(!connect_to_AI) + return var/found_ai = ai_to_sync_to if(!found_ai) found_ai = select_active_ai_with_fewest_borgs() if(found_ai) - lawupdate = 1 + lawupdate = TRUE connect_to_ai(found_ai) else - lawupdate = 0 + lawupdate = FALSE playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) @@ -270,6 +271,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( //If there's an MMI in the robot, have it ejected when the mob goes away. --NEO //Improved /N /mob/living/silicon/robot/Destroy() + SStgui.close_uis(wires) if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. var/turf/T = get_turf(loc)//To hopefully prevent run time errors. if(T) mmi.loc = T @@ -445,7 +447,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( notify_ai(2) uneq_all() + SSnanoui.close_user_uis(src) + SStgui.close_user_uis(src) sight_mode = null + update_sight() hands.icon_state = "nomod" icon_state = "robot" module.remove_subsystems_and_actions(src) @@ -543,6 +548,43 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( src.verbs -= GLOB.robot_verbs_default src.verbs -= silicon_subsystems +/mob/living/silicon/robot/verb/cmd_robot_alerts() + set category = "Robot Commands" + set name = "Show Alerts" + if(usr.stat == DEAD) + to_chat(src, "Alert: You are dead.") + return //won't work if dead + robot_alerts() + +/mob/living/silicon/robot/proc/robot_alerts() + var/list/dat = list() + var/list/list/temp_alarm_list = SSalarm.alarms.Copy() + for(var/cat in temp_alarm_list) + if(!(cat in alarms_listend_for)) + continue + dat += text("[cat]
    \n") + var/list/list/L = temp_alarm_list[cat].Copy() + for(var/alarm in L) + var/list/list/alm = L[alarm].Copy() + var/list/list/sources = alm[3].Copy() + var/area_name = alm[1] + for(var/thing in sources) + var/atom/A = locateUID(thing) + if(A && A.z != z) + L -= alarm + continue + dat += "" + dat += text("-- [area_name]") + dat += "
    \n" + if(!L.len) + dat += "-- All Systems Nominal
    \n" + dat += "
    \n" + + var/datum/browser/alerts = new(usr, "robotalerts", "Current Station Alerts", 400, 410) + var/dat_text = dat.Join("") + alerts.set_content(dat_text) + alerts.open() + /mob/living/silicon/robot/proc/ionpulse() if(!ionpulse_on) return @@ -604,6 +646,23 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/InCritical() return low_power_mode +/mob/living/silicon/robot/alarm_triggered(src, class, area/A, list/O, obj/alarmsource) + if(!(class in alarms_listend_for)) + return + if(alarmsource.z != z) + return + if(stat == DEAD) + return + queueAlarm(text("--- [class] alarm detected in [A.name]!"), class) + +/mob/living/silicon/robot/alarm_cancelled(src, class, area/A, obj/origin, cleared) + if(cleared) + if(!(class in alarms_listend_for)) + return + if(origin.z != z) + return + queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0) + /mob/living/silicon/robot/ex_act(severity) switch(severity) if(1.0) @@ -697,6 +756,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(allowed(W)) locked = !locked to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") + to_chat(src, "[user] [ locked ? "locked" : "unlocked"] your interface.") update_icons() else to_chat(user, "Access denied.") @@ -713,7 +773,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(!user.drop_item()) return if(U.action(src)) - to_chat(user, "You apply the upgrade to [src].") + user.visible_message("[user] applied [U] to [src].", "You apply [U] to [src].") U.forceMove(src) else to_chat(user, "Upgrade error.") @@ -795,7 +855,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( opened = FALSE update_icons() return - else if(wiresexposed && wires.IsAllCut()) + else if(wiresexposed && wires.is_all_cut()) //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. if(!mmi) to_chat(user, "[src] has no brain to remove.") @@ -909,16 +969,24 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( update_icons() return -/mob/living/silicon/robot/verb/unlock_own_cover() +/mob/living/silicon/robot/verb/toggle_own_cover() set category = "Robot Commands" - set name = "Unlock Cover" - set desc = "Unlocks your own cover if it is locked. You can not lock it again. A human will have to lock it for you." - if(locked) - switch(alert("You can not lock your cover again, are you sure?\n (You can still ask for a human to lock it)", "Unlock Own Cover", "Yes", "No")) - if("Yes") - locked = 0 - update_icons() - to_chat(usr, "You unlock your cover.") + set name = "Toggle Cover" + set desc = "Toggles the lock on your cover." + + if(can_lock_cover) + if(alert("Are you sure?", locked ? "Unlock Cover" : "Lock Cover", "Yes", "No") == "Yes") + locked = !locked + update_icons() + to_chat(usr, "You [locked ? "lock" : "unlock"] your cover.") + return + if(!locked) + to_chat(usr, "You cannot lock your cover yourself. Find a robotocist.") + return + if(alert("You cannnot lock your own cover again. Are you sure?\n You will need a robotocist to re-lock you.", "Unlock Own Cover", "Yes", "No") == "Yes") + locked = !locked + update_icons() + to_chat(usr, "You unlock your cover.") /mob/living/silicon/robot/attack_ghost(mob/user) if(wiresexposed) @@ -1023,10 +1091,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( src << browse(null, t1) return 1 - if(href_list["showalerts"]) - subsystem_alarm_monitor() - return 1 - if(href_list["mod"]) var/obj/item/O = locate(href_list["mod"]) if(istype(O) && (O.loc == src)) @@ -1041,6 +1105,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( activate_module(O) installed_modules() + //Show alerts window if user clicked on "Show alerts" in chat + if(href_list["showalerts"]) + robot_alerts() + return TRUE + if(href_list["deact"]) var/obj/item/O = locate(href_list["deact"]) if(activated(O)) @@ -1063,7 +1132,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( return 1 /mob/living/silicon/robot/proc/radio_menu() - radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code + radio.interact(src) /mob/living/silicon/robot/proc/control_headlamp() if(stat || lamp_recharging || low_power_mode) @@ -1233,7 +1302,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/proc/SetLockdown(var/state = 1) // They stay locked down if their wire is cut. - if(wires.LockedCut()) + if(wires.is_cut(WIRE_BORG_LOCKED)) state = 1 if(state) throw_alert("locked", /obj/screen/alert/locked) @@ -1343,14 +1412,14 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( eye_protection = 2 // Immunity to flashes and the visual part of flashbangs ear_protection = 1 // Immunity to the audio part of flashbangs damage_protection = 10 // Reduce all incoming damage by this number - xeno_disarm_chance = 20 allow_rename = FALSE modtype = "Commando" faction = list("nanotrasen") is_emaggable = FALSE + can_lock_cover = TRUE default_cell_type = /obj/item/stock_parts/cell/bluespace -/mob/living/silicon/robot/deathsquad/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null) +/mob/living/silicon/robot/deathsquad/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null) laws = new /datum/ai_laws/deathsquad module = new /obj/item/robot_module/deathsquad(src) aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) @@ -1376,11 +1445,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( static_radio_channels = 1 allow_rename = FALSE weapons_unlock = TRUE + can_lock_cover = TRUE default_cell_type = /obj/item/stock_parts/cell/super var/eprefix = "Amber" -/mob/living/silicon/robot/ert/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null) +/mob/living/silicon/robot/ert/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null) laws = new /datum/ai_laws/ert_override radio = new /obj/item/radio/borg/ert(src) radio.recalculateChannels() @@ -1413,7 +1483,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( damage_protection = 5 // Reduce all incoming damage by this number eprefix = "Gamma" magpulse = 1 - xeno_disarm_chance = 40 /mob/living/silicon/robot/destroyer @@ -1433,11 +1502,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( ear_protection = 1 // Immunity to the audio part of flashbangs emp_protection = TRUE // Immunity to EMP, due to heavy shielding damage_protection = 20 // Reduce all incoming damage by this number. Very high in the case of /destroyer borgs, since it is an admin-only borg. - xeno_disarm_chance = 10 + can_lock_cover = TRUE default_cell_type = /obj/item/stock_parts/cell/bluespace -/mob/living/silicon/robot/destroyer/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null) - ..() +/mob/living/silicon/robot/destroyer/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null) + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + additional_law_channels["Binary"] = ":b " + laws = new /datum/ai_laws/deathsquad module = new /obj/item/robot_module/destroyer(src) module.add_languages(src) module.add_subsystems_and_actions(src) @@ -1446,6 +1517,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( qdel(radio) radio = new /obj/item/radio/borg/ert/specops(src) radio.recalculateChannels() + playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) /mob/living/silicon/robot/destroyer/borg_icons() if(base_icon == "") diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index 8445a399582..84eb782833b 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -2,19 +2,17 @@ if(M.a_intent == INTENT_DISARM) if(!lying) M.do_attack_animation(src, ATTACK_EFFECT_DISARM) - if(prob(xeno_disarm_chance)) - Stun(7) - step(src, get_dir(M,src)) - spawn(5) - step(src, get_dir(M,src)) - add_attack_logs(M, src, "Alien pushed over") - playsound(loc, 'sound/weapons/pierce.ogg', 50, 1, -1) - visible_message("[M] has forced back [src]!",\ - "[M] has forced back [src]!") + var/obj/item/I = get_active_hand() + if(I) + uneq_active() + visible_message("[M] disarmed [src]!", "[M] has disabled [src]'s active module!") + add_attack_logs(M, src, "alien disarmed") else - playsound(loc, 'sound/weapons/slashmiss.ogg', 25, 1, -1) - visible_message("[M] took a swipe at [src]!",\ - "[M] took a swipe at [src]!") + Stun(2) + step(src, get_dir(M,src)) + add_attack_logs(M, src, "Alien pushed over") + visible_message("[M] forces back [src]!", "[M] forces back [src]!") + playsound(loc, 'sound/weapons/pierce.ogg', 50, TRUE, -1) else ..() return diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 8e8c67693c5..25d9a0decb8 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -74,7 +74,7 @@ var/list/temp_list = modules modules = list() for(var/obj/O in temp_list) - if(O) + if(!QDELETED(O)) //so items getting deleted don't stay in module list and haunt you modules += O /obj/item/robot_module/proc/add_languages(mob/living/silicon/robot/R) @@ -114,7 +114,7 @@ /obj/item/robot_module/proc/handle_custom_removal(component_id, mob/living/user, obj/item/W) return FALSE -/obj/item/robot_module/proc/handle_death(gibbed) +/obj/item/robot_module/proc/handle_death(mob/living/silicon/robot/R, gibbed) return /obj/item/robot_module/standard @@ -255,7 +255,7 @@ fix_modules() -/obj/item/robot_module/engineering/handle_death() +/obj/item/robot_module/engineering/handle_death(mob/living/silicon/robot/R, gibbed) var/obj/item/gripper/G = locate(/obj/item/gripper) in modules if(G) G.drop_gripped_item(silent = TRUE) @@ -368,6 +368,11 @@ R.add_language("Clownish",1) R.add_language("Neo-Russkiya", 1) +/obj/item/robot_module/butler/handle_death(mob/living/silicon/robot/R, gibbed) + var/obj/item/storage/bag/tray/cyborg/T = locate(/obj/item/storage/bag/tray/cyborg) in modules + if(istype(T)) + T.drop_inventory(R) + /obj/item/robot_module/miner name = "miner robot module" @@ -623,7 +628,7 @@ ..() -/obj/item/robot_module/drone/handle_death() +/obj/item/robot_module/drone/handle_death(mob/living/silicon/robot/R, gibbed) var/obj/item/gripper/G = locate(/obj/item/gripper) in modules if(G) G.drop_gripped_item(silent = TRUE) diff --git a/code/modules/mob/living/silicon/robot/syndicate.dm b/code/modules/mob/living/silicon/robot/syndicate.dm index f5b58696059..cfc6e1bd59d 100644 --- a/code/modules/mob/living/silicon/robot/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/syndicate.dm @@ -11,6 +11,7 @@ modtype = "Syndicate" req_access = list(ACCESS_SYNDICATE) ionpulse = 1 + can_lock_cover = TRUE lawchannel = "State" var/playstyle_string = "You are a Syndicate assault cyborg!
    \ You are armed with powerful offensive tools to aid you in your mission: help the operatives secure the nuclear authentication disk. \ diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index c923e0912b7..7f9c0849d9c 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -11,9 +11,11 @@ var/list/stating_laws = list()// Channels laws are currently being stated on var/list/alarms_to_show = list() var/list/alarms_to_clear = list() + var/list/alarm_types_show = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0) + var/list/alarm_types_clear = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0) + var/list/alarms_listend_for = list("Motion", "Fire", "Atmosphere", "Power", "Camera") //var/list/hud_list[10] var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer - var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to var/designation = "" var/obj/item/camera/siliconcam/aiCamera = null //photography //Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented. @@ -25,9 +27,6 @@ //var/sensor_mode = 0 //Determines the current HUD. - var/next_alarm_notice - var/list/datum/alarm/queued_alarms = new() - hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD) @@ -46,6 +45,8 @@ diag_hud_set_health() add_language("Galactic Common") init_subsystems() + RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered) + RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled) /mob/living/silicon/med_hud_set_health() return //we use a different hud @@ -55,10 +56,93 @@ /mob/living/silicon/Destroy() GLOB.silicon_mob_list -= src - for(var/datum/alarm_handler/AH in alarm_handlers) - AH.unregister(src) return ..() +/mob/living/silicon/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource) + return + +/mob/living/silicon/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared) + return + +/mob/living/silicon/proc/queueAlarm(message, type, incoming = TRUE) + var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0) + if(incoming) + alarms_to_show += message + alarm_types_show[type] += 1 + else + alarms_to_clear += message + alarm_types_clear[type] += 1 + + if(in_cooldown) + return + + addtimer(CALLBACK(src, .proc/show_alarms), 3 SECONDS) + +/mob/living/silicon/proc/show_alarms() + if(alarms_to_show.len < 5) + for(var/msg in alarms_to_show) + to_chat(src, msg) + else if(length(alarms_to_show)) + + var/list/msg = list("--- ") + + if(alarm_types_show["Burglar"]) + msg += "BURGLAR: [alarm_types_show["Burglar"]] alarms detected. - " + + if(alarm_types_show["Motion"]) + msg += "MOTION: [alarm_types_show["Motion"]] alarms detected. - " + + if(alarm_types_show["Fire"]) + msg += "FIRE: [alarm_types_show["Fire"]] alarms detected. - " + + if(alarm_types_show["Atmosphere"]) + msg += "ATMOSPHERE: [alarm_types_show["Atmosphere"]] alarms detected. - " + + if(alarm_types_show["Power"]) + msg += "POWER: [alarm_types_show["Power"]] alarms detected. - " + + if(alarm_types_show["Camera"]) + msg += "CAMERA: [alarm_types_show["Camera"]] alarms detected. - " + + msg += "\[Show Alerts\]" + var/msg_text = msg.Join("") + to_chat(src, msg_text) + + if(alarms_to_clear.len < 3) + for(var/msg in alarms_to_clear) + to_chat(src, msg) + + else if(alarms_to_clear.len) + var/list/msg = list("--- ") + + if(alarm_types_clear["Motion"]) + msg += "MOTION: [alarm_types_clear["Motion"]] alarms cleared. - " + + if(alarm_types_clear["Fire"]) + msg += "FIRE: [alarm_types_clear["Fire"]] alarms cleared. - " + + if(alarm_types_clear["Atmosphere"]) + msg += "ATMOSPHERE: [alarm_types_clear["Atmosphere"]] alarms cleared. - " + + if(alarm_types_clear["Power"]) + msg += "POWER: [alarm_types_clear["Power"]] alarms cleared. - " + + if(alarm_types_show["Camera"]) + msg += "CAMERA: [alarm_types_clear["Camera"]] alarms cleared. - " + + msg += "\[Show Alerts\]" + + var/msg_text = msg.Join("") + to_chat(src, msg_text) + + + alarms_to_show.Cut() + alarms_to_clear.Cut() + for(var/key in alarm_types_show) + alarm_types_show[key] = 0 + for(var/key in alarm_types_clear) + alarm_types_clear[key] = 0 + /mob/living/silicon/rename_character(oldname, newname) // we actually don't want it changing minds and stuff if(!newname) @@ -283,63 +367,6 @@ if("Disable") to_chat(src, "Sensor augmentations disabled.") -/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised) - if(!next_alarm_notice) - next_alarm_notice = world.time + 10 SECONDS - - var/list/alarms = queued_alarms[alarm_handler] - if(was_raised) - // Raised alarms are always set - alarms[alarm] = 1 - else - // Alarms that were raised but then cleared before the next notice are instead removed - if(alarm in alarms) - alarms -= alarm - // And alarms that have only been cleared thus far are set as such - else - alarms[alarm] = -1 - -/mob/living/silicon/proc/process_queued_alarms() - if(next_alarm_notice && (world.time > next_alarm_notice)) - next_alarm_notice = 0 - - var/alarm_raised = 0 - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - var/reported = 0 - for(var/datum/alarm/A in alarms) - if(alarms[A] == 1) - if(!reported) - reported = 1 - to_chat(src, "--- [AH.category] Detected ---") - raised_alarm(A) - - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - var/reported = 0 - for(var/datum/alarm/A in alarms) - if(alarms[A] == -1) - if(!reported) - reported = 1 - to_chat(src, "--- [AH.category] Cleared ---") - to_chat(src, "\The [A.alarm_name()].") - - if(alarm_raised) - to_chat(src, "\[Show Alerts\]") - - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - alarms.Cut() - -/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A) - to_chat(src, "[A.alarm_name()]!") - -/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A) - var/cameratext = "" - for(var/obj/machinery/camera/C in A.cameras()) - cameratext += "[(cameratext == "")? "" : "|"][C.c_tag]" - to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])") - /mob/living/silicon/adjustToxLoss(var/amount) return STATUS_UPDATE_NONE diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index e720400abd5..36d11b0f2a2 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -3,11 +3,10 @@ /mob/living/silicon/attack_alien(mob/living/carbon/alien/humanoid/M) if(..()) //if harm or disarm intent - var/damage = rand(10, 20) + var/damage = 20 if(prob(90)) playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1) - visible_message("[M] has slashed at [src]!", \ - "[M] has slashed at [src]!") + visible_message("[M] has slashed at [src]!", "[M] has slashed at [src]!") if(prob(8)) flash_eyes(affect_silicon = 1) add_attack_logs(M, src, "Alien attacked") @@ -59,11 +58,12 @@ if(INTENT_HELP) M.visible_message("[M] pets [src]!", \ "You pet [src]!") + playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) if("grab") grabbedby(M) else M.do_attack_animation(src, ATTACK_EFFECT_PUNCH) playsound(loc, 'sound/effects/bang.ogg', 10, 1) - visible_message("[M] punches [src], but doesn't leave a dent.", \ - "[M] punches [src], but doesn't leave a dent.!") + visible_message("[M] punches [src], but doesn't leave a dent.", \ + "[M] punches [src], but doesn't leave a dent.") return FALSE diff --git a/code/modules/mob/living/silicon/subsystems.dm b/code/modules/mob/living/silicon/subsystems.dm index d18d41a40cf..fc3b775f229 100644 --- a/code/modules/mob/living/silicon/subsystems.dm +++ b/code/modules/mob/living/silicon/subsystems.dm @@ -4,17 +4,15 @@ var/datum/nano_module/atmos_control/atmos_control var/datum/tgui_module/crew_monitor/crew_monitor var/datum/nano_module/law_manager/law_manager - var/datum/nano_module/power_monitor/silicon/power_monitor + var/datum/tgui_module/power_monitor/digital/power_monitor /mob/living/silicon var/list/silicon_subsystems = list( - /mob/living/silicon/proc/subsystem_alarm_monitor, /mob/living/silicon/proc/subsystem_law_manager ) /mob/living/silicon/ai silicon_subsystems = list( - /mob/living/silicon/proc/subsystem_alarm_monitor, /mob/living/silicon/proc/subsystem_atmos_control, /mob/living/silicon/proc/subsystem_crew_monitor, /mob/living/silicon/proc/subsystem_law_manager, @@ -23,7 +21,6 @@ /mob/living/silicon/robot/drone silicon_subsystems = list( - /mob/living/silicon/proc/subsystem_alarm_monitor, /mob/living/silicon/proc/subsystem_law_manager, /mob/living/silicon/proc/subsystem_power_monitor ) @@ -32,30 +29,11 @@ register_alarms = 0 /mob/living/silicon/proc/init_subsystems() - alarm_monitor = new(src) atmos_control = new(src) crew_monitor = new(src) law_manager = new(src) power_monitor = new(src) - if(!register_alarms) - return - - var/list/register_to = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm) - for(var/datum/alarm_handler/AH in register_to) - AH.register(src, /mob/living/silicon/proc/receive_alarm) - queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order - alarm_handlers |= AH - -/******************** -* Alarm Monitor * -********************/ -/mob/living/silicon/proc/subsystem_alarm_monitor() - set name = "Alarm Monitor" - set category = "Subsystems" - - alarm_monitor.ui_interact(usr, state = GLOB.self_state) - /******************** * Atmos Control * ********************/ @@ -89,5 +67,5 @@ set category = "Subsystems" set name = "Power Monitor" - power_monitor.ui_interact(usr, state = GLOB.self_state) + power_monitor.tgui_interact(usr, state = GLOB.self_state) diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm index b42f3275997..28fff50a124 100644 --- a/code/modules/mob/living/simple_animal/animal_defense.dm +++ b/code/modules/mob/living/simple_animal/animal_defense.dm @@ -42,13 +42,18 @@ /mob/living/simple_animal/attack_alien(mob/living/carbon/alien/humanoid/M) if(..()) //if harm or disarm intent. - var/damage = rand(15, 30) - visible_message("[M] has slashed at [src]!", \ - "[M] has slashed at [src]!") - playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) - add_attack_logs(M, src, "Alien attacked") - attack_threshold_check(damage) - return + if(M.a_intent == INTENT_DISARM) + playsound(loc, 'sound/weapons/pierce.ogg', 25, TRUE, -1) + visible_message("[M] [response_disarm] [name]!", "[M] [response_disarm] you!") + add_attack_logs(M, src, "Alien disarmed") + else + var/damage = rand(15, 30) + visible_message("[M] has slashed at [src]!", \ + "[M] has slashed at [src]!") + playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) + add_attack_logs(M, src, "Alien attacked") + attack_threshold_check(damage) + return TRUE /mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L) if(..()) //successful larva bite diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index cb45fd98e8b..97fc3ac469c 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -94,7 +94,6 @@ hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_PATH_HUD = HUD_LIST_LIST)//Diagnostic HUD views /obj/item/radio/headset/bot - subspace_transmission = 1 requires_tcomms = FALSE canhear_range = 0 @@ -756,7 +755,7 @@ Pass a positive integer as an argument to override a bot's default speed. // send a radio signal with multiple data key/values /mob/living/simple_animal/bot/proc/post_signal_multiple(var/freq, var/list/keyval) - if(z != 1) //Bot control will only work on station. + if(!is_station_level(z)) //Bot control will only work on station. return var/datum/radio_frequency/frequency = SSradio.return_frequency(freq) if(!frequency) diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm index 024e2b5f304..e18672ffa20 100644 --- a/code/modules/mob/living/simple_animal/bot/construction.dm +++ b/code/modules/mob/living/simple_animal/bot/construction.dm @@ -474,8 +474,7 @@ return build_step++ to_chat(user, "You complete the Securitron! Beep boop.") - var/mob/living/simple_animal/bot/secbot/S = new /mob/living/simple_animal/bot/secbot - S.forceMove(get_turf(src)) + var/mob/living/simple_animal/bot/secbot/S = new /mob/living/simple_animal/bot/secbot(get_turf(src)) S.name = created_name S.robot_arm = robot_arm qdel(I) @@ -512,7 +511,7 @@ //General Griefsky else if((istype(I, /obj/item/wrench)) && (build_step == 3)) - var/obj/item/griefsky_assembly/A = new /obj/item/griefsky_assembly + var/obj/item/griefsky_assembly/A = new /obj/item/griefsky_assembly(get_turf(src)) user.put_in_hands(A) to_chat(user, "You adjust the arm slots for extra weapons!.") user.unEquip(src, 1) @@ -540,8 +539,7 @@ if(!user.unEquip(I)) return to_chat(user, "You complete General Griefsky!.") - var/mob/living/simple_animal/bot/secbot/griefsky/S = new /mob/living/simple_animal/bot/secbot/griefsky - S.forceMove(get_turf(src)) + new /mob/living/simple_animal/bot/secbot/griefsky(get_turf(src)) qdel(I) qdel(src) @@ -556,8 +554,7 @@ if(!user.unEquip(I)) return to_chat(user, "You complete Genewul Giftskee!.") - var/mob/living/simple_animal/bot/secbot/griefsky/toy/S = new /mob/living/simple_animal/bot/secbot/griefsky/toy - S.forceMove(get_turf(src)) + new /mob/living/simple_animal/bot/secbot/griefsky/toy(get_turf(src)) qdel(I) qdel(src) diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index f1278918898..ccc6e338144 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -66,6 +66,7 @@ RegisterSignal(src, COMSIG_CROSSED_MOVABLE, .proc/human_squish_check) /mob/living/simple_animal/bot/mulebot/Destroy() + SStgui.close_uis(wires) unload(0) QDEL_NULL(wires) QDEL_NULL(cell) @@ -142,7 +143,7 @@ if(open) icon_state="mulebot-hatch" else - icon_state = "mulebot[!wires.MobAvoid()]" + icon_state = "mulebot[wires.is_cut(WIRE_MOB_AVOIDANCE)]" overlays.Cut() if(load && !ismob(load))//buckling handles the mob offsets load.pixel_y = initial(load.pixel_y) + 9 @@ -158,9 +159,9 @@ qdel(src) if(2) for(var/i = 1; i < 3; i++) - wires.RandomCut() + wires.cut_random() if(3) - wires.RandomCut() + wires.cut_random() return /mob/living/simple_animal/bot/mulebot/bullet_act(obj/item/projectile/Proj) @@ -169,7 +170,7 @@ unload(0) if(prob(25)) visible_message("Something shorts out inside [src]!") - wires.RandomCut() + wires.cut_random() /mob/living/simple_animal/bot/mulebot/Topic(href, list/href_list) if(..()) @@ -318,7 +319,7 @@ // returns true if the bot has power /mob/living/simple_animal/bot/mulebot/proc/has_power() - return !open && cell && cell.charge > 0 && wires.HasPower() + return !open && cell && cell.charge > 0 && !wires.is_cut(WIRE_MAIN_POWER1) && !wires.is_cut(WIRE_MAIN_POWER2) /mob/living/simple_animal/bot/mulebot/proc/buzz(type) switch(type) @@ -362,7 +363,7 @@ if(istype(AM,/obj/structure/closet/crate)) CRATE = AM else - if(wires.LoadCheck()) + if(!wires.is_cut(WIRE_LOADCHECK)) buzz(SIGH) return // if not hacked, only allow crates to be loaded @@ -459,8 +460,7 @@ on = 0 return if(on) - var/speed = (wires.Motor1() ? 1 : 0) + (wires.Motor2() ? 2 : 0) -// to_chat(world, "speed: [speed]") + var/speed = (!wires.is_cut(WIRE_MOTOR1) ? 1 : 0) + (!wires.is_cut(WIRE_MOTOR2) ? 2 : 0) var/num_steps = 0 switch(speed) if(0) @@ -624,7 +624,7 @@ // not loaded if(auto_pickup) // find a crate var/atom/movable/AM - if(wires.LoadCheck()) // if hacked, load first unanchored thing we find + if(wires.is_cut(WIRE_LOADCHECK)) // if hacked, load first unanchored thing we find for(var/atom/movable/A in get_step(loc, loaddir)) if(!A.anchored) AM = A @@ -672,7 +672,7 @@ // called when bot bumps into anything /mob/living/simple_animal/bot/mulebot/Bump(atom/obs) - if(!wires.MobAvoid()) // usually just bumps, but if avoidance disabled knock over mobs + if(wires.is_cut(WIRE_MOB_AVOIDANCE)) // usually just bumps, but if avoidance disabled knock over mobs var/mob/M = obs if(ismob(M)) if(istype(M,/mob/living/silicon/robot)) @@ -736,8 +736,8 @@ ..() /mob/living/simple_animal/bot/mulebot/receive_signal(datum/signal/signal) - if(!wires.RemoteRX() || ..()) - return 1 + if(wires.is_cut(WIRE_REMOTE_RX) || ..()) + return TRUE var/recv = signal.data["command"] @@ -772,7 +772,7 @@ // send a radio signal with multiple data key/values /mob/living/simple_animal/bot/mulebot/post_signal_multiple(var/freq, var/list/keyval) - if(!wires.RemoteTX()) + if(wires.is_cut(WIRE_REMOTE_TX)) return ..() @@ -803,7 +803,7 @@ //Update navigation data. Called when commanded to deliver, return home, or a route update is needed... /mob/living/simple_animal/bot/mulebot/proc/get_nav() - if(!on || !wires.BeaconRX()) + if(!on || wires.is_cut(WIRE_BEACON_RX)) return for(var/obj/machinery/navbeacon/NB in GLOB.deliverybeacons) diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index f150005116e..4c2c2332924 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -17,7 +17,7 @@ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 faction = list("cult") - flying = 1 + flying = TRUE pressure_resistance = 100 universal_speak = 1 AIStatus = AI_OFF //normal constructs don't have AI diff --git a/code/modules/mob/living/simple_animal/friendly/butterfly.dm b/code/modules/mob/living/simple_animal/friendly/butterfly.dm index 9aaffc6ef8f..ebf068ff130 100644 --- a/code/modules/mob/living/simple_animal/friendly/butterfly.dm +++ b/code/modules/mob/living/simple_animal/friendly/butterfly.dm @@ -15,6 +15,7 @@ harm_intent_damage = 1 friendly = "nudges" density = 0 + flying = TRUE pass_flags = PASSTABLE | PASSGRILLE | PASSMOB ventcrawler = 2 mob_size = MOB_SIZE_TINY diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 24818d07652..f44244b7eae 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -123,6 +123,7 @@ for(var/mob/living/simple_animal/mouse/M in view(1, src)) if(!M.stat && Adjacent(M)) custom_emote(1, "splats \the [M]!") + M.death() M.splat() movement_target = null stop_automated_movement = 0 diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 07a21af8a0b..915bf9ae2aa 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -9,7 +9,7 @@ response_disarm = "bops" response_harm = "kicks" speak = list("YAP", "Woof!", "Bark!", "AUUUUUU") - speak_emote = list("barks", "woofs") + speak_emote = list("barks.", "woofs.") emote_hear = list("barks!", "woofs!", "yaps.","pants.") emote_see = list("shakes its head.", "chases its tail.","shivers.") faction = list("neutral") diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index ea8e949f1c7..58e3ad54089 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -126,6 +126,7 @@ gold_core_spawnable = FRIENDLY_SPAWN blood_volume = BLOOD_VOLUME_NORMAL var/obj/item/udder/udder = null + gender = FEMALE /mob/living/simple_animal/cow/Initialize() udder = new() diff --git a/code/modules/mob/living/simple_animal/friendly/lizard.dm b/code/modules/mob/living/simple_animal/friendly/lizard.dm index 96688963b73..566791786f6 100644 --- a/code/modules/mob/living/simple_animal/friendly/lizard.dm +++ b/code/modules/mob/living/simple_animal/friendly/lizard.dm @@ -23,3 +23,14 @@ butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) can_collar = 1 gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/lizard/decompile_act(obj/item/matter_decompiler/C, mob/user) + if(!istype(user, /mob/living/silicon/robot/drone)) + user.visible_message("[user] sucks [src] into its decompiler. There's a horrible crunching noise.", \ + "It's a bit of a struggle, but you manage to suck [src] into your decompiler. It makes a series of visceral crunching noises.") + new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) + C.stored_comms["wood"] += 2 + C.stored_comms["glass"] += 2 + qdel(src) + return TRUE + return ..() diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index a48b36cb0c7..be3f985db2e 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -80,22 +80,16 @@ icon_resting = "mouse_[mouse_color]_sleep" desc = "It's a small [mouse_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." -/mob/living/simple_animal/mouse/proc/splat() - src.health = 0 - src.stat = DEAD - src.icon_dead = "mouse_[mouse_color]_splat" - src.icon_state = "mouse_[mouse_color]_splat" - layer = MOB_LAYER - if(client) - client.time_died_as_mouse = world.time - /mob/living/simple_animal/mouse/attack_hand(mob/living/carbon/human/M as mob) if(M.a_intent == INTENT_HELP) get_scooped(M) ..() -/mob/living/simple_animal/mouse/start_pulling(var/atom/movable/AM)//Prevents mouse from pulling things - to_chat(src, "You are too small to pull anything.") +/mob/living/simple_animal/mouse/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE)//Prevents mouse from pulling things + if(istype(AM, /obj/item/reagent_containers/food/snacks/cheesewedge)) + return ..() // Get dem + if(show_message) + to_chat(src, "You are too small to pull anything except cheese.") return /mob/living/simple_animal/mouse/Crossed(AM as mob|obj, oldloc) @@ -110,6 +104,10 @@ desc = "It's toast." death() +/mob/living/simple_animal/mouse/proc/splat() + icon_dead = "mouse_[mouse_color]_splat" + icon_state = "mouse_[mouse_color]_splat" + /mob/living/simple_animal/mouse/death(gibbed) // Only execute the below if we successfully died playsound(src, squeak_sound, 40, 1) @@ -234,3 +232,14 @@ gold_core_spawnable = NO_SPAWN can_collar = 0 butcher_results = list(/obj/item/stack/sheet/metal = 1) + +/mob/living/simple_animal/mouse/decompile_act(obj/item/matter_decompiler/C, mob/user) + if(!(istype(user, /mob/living/silicon/robot/drone))) + user.visible_message("[user] sucks [src] into its decompiler. There's a horrible crunching noise.", \ + "It's a bit of a struggle, but you manage to suck [src] into your decompiler. It makes a series of visceral crunching noises.") + new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) + C.stored_comms["wood"] += 2 + C.stored_comms["glass"] += 2 + qdel(src) + return TRUE + return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm index be04fc217e9..b47b0eb1f23 100644 --- a/code/modules/mob/living/simple_animal/hostile/bat.dm +++ b/code/modules/mob/living/simple_animal/hostile/bat.dm @@ -16,6 +16,7 @@ maxHealth = 20 health = 20 mob_size = MOB_SIZE_TINY + flying = TRUE harm_intent_damage = 8 melee_damage_lower = 10 melee_damage_upper = 10 diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm index 3276416fe7f..3419279ffe5 100644 --- a/code/modules/mob/living/simple_animal/hostile/bees.dm +++ b/code/modules/mob/living/simple_animal/hostile/bees.dm @@ -95,6 +95,10 @@ for(var/mob/A in searched_for) . += A +// All bee sprites are made up of overlays. They do not have any special sprite overlays for items placed on them, such as collars, so this proc is unneeded. +/mob/living/simple_animal/hostile/poison/bees/regenerate_icons() + return + /mob/living/simple_animal/hostile/poison/bees/proc/generate_bee_visuals() overlays.Cut() diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index 614e814fc25..9211a43ae8c 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -47,6 +47,13 @@ if(C.can_inject(null, FALSE, inject_target, FALSE)) C.reagents.add_reagent("spidertoxin", venom_per_bite) +/mob/living/simple_animal/hostile/poison/giant_spider/get_spacemove_backup() + . = ..() + // If we don't find any normal thing to use, attempt to use any nearby spider structure instead. + if(!.) + for(var/obj/structure/spider/S in range(1, get_turf(src))) + return S + //nursemaids - these create webs and eggs /mob/living/simple_animal/hostile/poison/giant_spider/nurse desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes." @@ -164,15 +171,27 @@ if(!cocoon_target) var/list/choices = list() - for(var/mob/living/L in view(1,src)) + for(var/mob/living/L in view(1, src)) if(L == src) continue + if(L.stat != DEAD) + continue + if(istype(L, /mob/living/simple_animal/hostile/poison/giant_spider)) + continue if(Adjacent(L)) choices += L - for(var/obj/O in src.loc) + for(var/obj/O in get_turf(src)) + if(O.anchored) + continue + if(!(isitem(O) || isstructure(O) || ismachinery(O))) + continue if(Adjacent(O)) choices += O - cocoon_target = input(src,"What do you wish to cocoon?") in null|choices + if(length(choices)) + cocoon_target = input(src,"What do you wish to cocoon?") in null|choices + else + to_chat(src, "No suitable dead prey or wrappable objects found nearby.") + return if(cocoon_target && busy != SPINNING_COCOON) busy = SPINNING_COCOON @@ -199,6 +218,8 @@ for(var/mob/living/L in C.loc) if(istype(L, /mob/living/simple_animal/hostile/poison/giant_spider)) continue + if(L.stat != DEAD) + continue large_cocoon = 1 L.loc = C C.pixel_x = L.pixel_x diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm index c6f3ac845e3..2b9358214b1 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm @@ -71,9 +71,6 @@ var/spawn_delay = 600 var/turn_on = 0 var/auto_spawn = 1 - proc - warpbots() - /mob/living/simple_animal/hostile/hivebot/tele/New() ..() @@ -83,7 +80,7 @@ visible_message("The [src] warps in!") playsound(src.loc, 'sound/effects/empulse.ogg', 25, 1) -/mob/living/simple_animal/hostile/hivebot/tele/warpbots() +/mob/living/simple_animal/hostile/hivebot/tele/proc/warpbots() icon_state = "def_radar" visible_message("The [src] turns on!") while(bot_amt > 0) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index b8c72d7cf84..dd0316f4177 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -122,8 +122,8 @@ Difficulty: Very Hard /mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/L) if(ishuman(L)) var/mob/living/carbon/human/H = L - if(H.martial_art && prob(H.martial_art.deflection_chance)) - . = TRUE + if(H.mind && H.mind.martial_art && prob(H.mind.martial_art.deflection_chance)) + return TRUE /mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots() ranged_cooldown = world.time + 40 @@ -241,6 +241,14 @@ Difficulty: Very Hard AT.pixel_y += random_y return ..() +/mob/living/simple_animal/hostile/megafauna/colossus/float(on) //we don't want this guy to float, messes up his animations + if(throwing) + return + if(on && !floating) + floating = TRUE + else if(!on && floating) + floating = FALSE + /obj/item/projectile/colossus name ="death bolt" icon_state= "chronobolt" diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 50531f976e9..14d2e933605 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -524,7 +524,7 @@ Difficulty: Medium else animate(src, pixel_x = -16, pixel_z = 0, time = 5) -obj/effect/temp_visual/fireball +/obj/effect/temp_visual/fireball icon = 'icons/obj/wizard.dmi' icon_state = "fireball" name = "fireball" diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm index bd3314d1453..b6189d56eac 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm @@ -1,6 +1,6 @@ //malfunctioning combat drones -/mob/living/simple_animal/hostile/retaliate/malf_drone +/mob/living/simple_animal/hostile/malf_drone name = "combat drone" desc = "An automated combat drone armed with state of the art weaponry and shielding." icon_state = "drone3" @@ -8,254 +8,175 @@ icon_dead = "drone_dead" ranged = 1 rapid = 3 + retreat_distance = 3 + minimum_distance = 3 speak_chance = 5 turns_per_move = 3 response_help = "pokes the" response_disarm = "gently pushes aside the" response_harm = "hits the" - speak = list("ALERT.","Hostile-ile-ile entities dee-twhoooo-wected.","Threat parameterszzzz- szzet.","Bring sub-sub-sub-systems uuuup to combat alert alpha-a-a.") - emote_see = list("beeps menacingly","whirrs threateningly","scans its immediate vicinity") + speak = list("ALERT.", "Hostile-ile-ile entities dee-twhoooo-wected.", "Threat parameterszzzz- szzet.", "Bring sub-sub-sub-systems uuuup to combat alert alpha-a-a.") + emote_see = list("beeps menacingly.", "whirrs threateningly.", "scans for targets.") a_intent = INTENT_HARM - stop_automated_movement_when_pulled = 0 - health = 300 - maxHealth = 300 + stop_automated_movement_when_pulled = FALSE + health = 200 + maxHealth = 200 speed = 8 - projectiletype = /obj/item/projectile/beam/drone + projectiletype = /obj/item/projectile/beam/immolator/weak projectilesound = 'sound/weapons/laser3.ogg' - var/datum/effect_system/trail_follow/ion/ion_trail - - //the drone randomly switches between these states because it's malfunctioning - var/hostile_drone = 0 - //0 - retaliate, only attack enemies that attack it - //1 - hostile, attack everything that comes near - - var/turf/patrol_target - var/explode_chance = 1 - var/disabled = 0 - var/exploding = 0 - - //Drones aren't affected by atmos. atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 - - var/has_loot = 1 faction = list("malf_drone") deathmessage = "suddenly breaks apart." del_on_death = 1 + var/datum/effect_system/trail_follow/ion/ion_trail + var/passive_mode = TRUE // if true, don't target anything. -/mob/living/simple_animal/hostile/retaliate/malf_drone/New() - ..() - if(prob(5)) - projectiletype = /obj/item/projectile/beam/pulse/drone - projectilesound = 'sound/weapons/pulse2.ogg' +/mob/living/simple_animal/hostile/malf_drone/Initialize(mapload) + . = ..() ion_trail = new ion_trail.set_up(src) ion_trail.start() + update_icons() -/mob/living/simple_animal/hostile/retaliate/malf_drone/Process_Spacemove(var/check_drift = 0) +/mob/living/simple_animal/hostile/malf_drone/Process_Spacemove(check_drift = 0) return 1 -/mob/living/simple_animal/hostile/retaliate/malf_drone/ListTargets() - if(hostile_drone) - return view(src, 10) - else - return ..() - -//self repair systems have a chance to bring the drone back to life -/mob/living/simple_animal/hostile/retaliate/malf_drone/Life(seconds, times_fired) - - //emps and lots of damage can temporarily shut us down - if(disabled > 0) - stat = UNCONSCIOUS - icon_state = "drone_dead" - disabled-- - wander = 0 - speak_chance = 0 - if(disabled <= 0) - stat = CONSCIOUS - icon_state = "drone0" - wander = 1 - speak_chance = 5 - - //repair a bit of damage - if(prob(1)) - src.visible_message("[bicon(src)] [src] shudders and shakes as some of it's damaged systems come back online.") - do_sparks(3, 1, src) - health += rand(25,100) - - //spark for no reason - if(prob(5)) - do_sparks(3, 1, src) - - //sometimes our targetting sensors malfunction, and we attack anyone nearby - if(prob(disabled ? 0 : 1)) - if(hostile_drone) - src.visible_message("[bicon(src)] [src] retracts several targetting vanes, and dulls it's running lights.") - hostile_drone = 0 - else - src.visible_message("[bicon(src)] [src] suddenly lights up, and additional targetting vanes slide into place.") - hostile_drone = 1 - - if(health / maxHealth > 0.9) - icon_state = "drone3" - explode_chance = 0 - else if(health / maxHealth > 0.7) - icon_state = "drone2" - explode_chance = 0 - else if(health / maxHealth > 0.5) - icon_state = "drone1" - explode_chance = 0.5 - else if(health / maxHealth > 0.3) - icon_state = "drone0" - explode_chance = 5 - else if(health > 0) - //if health gets too low, shut down - icon_state = "drone_dead" - exploding = 0 - if(!disabled) - if(prob(50)) - src.visible_message("[bicon(src)] [src] suddenly shuts down!") - else - src.visible_message("[bicon(src)] [src] suddenly lies still and quiet.") - disabled = rand(150, 600) - walk(src,0) - - if(exploding && prob(20)) - if(prob(50)) - src.visible_message("[bicon(src)] [src] begins to spark and shake violenty!") - else - src.visible_message("[bicon(src)] [src] sparks and shakes like it's about to explode!") - do_sparks(3, 1, src) - - if(!exploding && !disabled && prob(explode_chance)) - exploding = 1 - stat = UNCONSCIOUS - wander = 1 - walk(src,0) - spawn(rand(50,150)) - if(!disabled && exploding) - explosion(get_turf(src), 0, 1, 4, 7) - //proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1) - ..() - -//ion rifle! -/mob/living/simple_animal/hostile/retaliate/malf_drone/emp_act(severity) - health -= rand(3,15) * (severity + 1) - disabled = rand(150, 600) - hostile_drone = 0 - walk(src,0) - -/mob/living/simple_animal/hostile/retaliate/malf_drone/Destroy() //Seriously, what the actual hell. - //some random debris left behind - if(has_loot) - do_sparks(3, 1, src) - var/obj/O - - //shards - O = new /obj/item/shard(loc) - step_to(O, get_turf(pick(view(7, src)))) - if(prob(75)) - O = new /obj/item/shard(loc) - step_to(O, get_turf(pick(view(7, src)))) - if(prob(50)) - O = new /obj/item/shard(loc) - step_to(O, get_turf(pick(view(7, src)))) - if(prob(25)) - O = new /obj/item/shard(loc) - step_to(O, get_turf(pick(view(7, src)))) - - //rods - O = new /obj/item/stack/rods(src.loc) - step_to(O, get_turf(pick(view(7, src)))) - if(prob(75)) - O = new /obj/item/stack/rods(src.loc) - step_to(O, get_turf(pick(view(7, src)))) - if(prob(50)) - O = new /obj/item/stack/rods(src.loc) - step_to(O, get_turf(pick(view(7, src)))) - if(prob(25)) - O = new /obj/item/stack/rods(src.loc) - step_to(O, get_turf(pick(view(7, src)))) - - //plasteel - O = new /obj/item/stack/sheet/plasteel(src.loc) - step_to(O, get_turf(pick(view(7, src)))) - if(prob(75)) - O = new /obj/item/stack/sheet/plasteel(src.loc) - step_to(O, get_turf(pick(view(7, src)))) - if(prob(50)) - O = new /obj/item/stack/sheet/plasteel(src.loc) - step_to(O, get_turf(pick(view(7, src)))) - if(prob(25)) - O = new /obj/item/stack/sheet/plasteel(src.loc) - step_to(O, get_turf(pick(view(7, src)))) - - //also drop dummy circuit boards deconstructable for research (loot) - var/obj/item/circuitboard/C - - //spawn 1-4 boards of a random type - var/spawnees = 0 - var/num_boards = rand(1,4) - var/list/options = list(1,2,4,8,16,32,64,128,256, 512) - for(var/i=0, i 0.9) + icon_state = "drone3" + else if(health / maxHealth > 0.7) + icon_state = "drone2" + else if(health / maxHealth > 0.5) + icon_state = "drone1" + else + icon_state = "drone0" + +/mob/living/simple_animal/hostile/malf_drone/adjustHealth(damage, updating_health) + do_sparks(3, 1, src) + passive_mode = FALSE + update_icons() + . = ..() // this will handle finding a target if there is a valid one nearby + +/mob/living/simple_animal/hostile/malf_drone/Life(seconds, times_fired) + . = ..() + if(.) // mob is alive. We check this just in case Life() can fire for qdel'ed mobs. + if(times_fired % 15 == 0) // every 15 cycles, aka 30 seconds, 50% chance to switch between modes + scramble_settings() + +/mob/living/simple_animal/hostile/malf_drone/proc/scramble_settings() + if(prob(50)) + do_sparks(3, 1, src) + passive_mode = !passive_mode + if(passive_mode) + visible_message("[src] retracts several targetting vanes.") + if(target) + LoseTarget() + else + visible_message("[src] suddenly lights up, and additional targetting vanes slide into place.") + update_icons() + +/mob/living/simple_animal/hostile/malf_drone/emp_act(severity) + adjustHealth(100 / severity) // takes the same damage as a mining drone from emp + +/mob/living/simple_animal/hostile/malf_drone/drop_loot() + do_sparks(3, 1, src) + + var/turf/T = get_turf(src) + + //shards + var/obj/O = new /obj/item/shard(T) + step_to(O, get_turf(pick(view(7, src)))) + if(prob(75)) + O = new /obj/item/shard(T) + step_to(O, get_turf(pick(view(7, src)))) + if(prob(50)) + O = new /obj/item/shard(T) + step_to(O, get_turf(pick(view(7, src)))) + if(prob(25)) + O = new /obj/item/shard(T) + step_to(O, get_turf(pick(view(7, src)))) + + //rods + var/obj/item/stack/K = new /obj/item/stack/rods(T) + step_to(K, get_turf(pick(view(7, src)))) + K.amount = pick(1, 2, 3, 4) + K.update_icon() + + //plasteel + K = new /obj/item/stack/sheet/plasteel(T) + step_to(K, get_turf(pick(view(7, src)))) + K.amount = pick(1, 2, 3, 4) + K.update_icon() + + //also drop dummy circuit boards deconstructable for research (loot) + var/obj/item/circuitboard/C + + //spawn 1-4 boards of a random type + var/spawnees = 0 + var/num_boards = rand(1, 4) + var/list/options = list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512) + for(var/i=0, iERROR: attempt to use evolve queen ability on a non-princess") - return - var/feedings_left = user.feedings_to_evolve - user.fed - if(feedings_left > 0) - to_chat(user, "You must wrap [feedings_left] more humanoid prey before you can do this!") - return - for(var/mob/living/simple_animal/hostile/poison/terror_spider/queen/Q in GLOB.ts_spiderlist) - if(Q.spider_awaymission == user.spider_awaymission) - to_chat(user, "The presence of another Queen in the area is preventing you from maturing.") - return - user.evolve_to_queen() // ---------- QUEEN ACTIONS @@ -93,19 +72,11 @@ var/mob/living/simple_animal/hostile/poison/terror_spider/queen/user = owner user.LayQueenEggs() -/datum/action/innate/terrorspider/queen/queenfakelings - name = "Fake Spiderlings" - icon_icon = 'icons/effects/effects.dmi' - button_icon_state = "spiderling" - -/datum/action/innate/terrorspider/queen/queenfakelings/Activate() - var/mob/living/simple_animal/hostile/poison/terror_spider/queen/user = owner - user.QueenFakeLings() // ---------- EMPRESS /datum/action/innate/terrorspider/queen/empress/empresserase - name = "Erase Brood" + name = "Empress Erase Brood" icon_icon = 'icons/effects/blood.dmi' button_icon_state = "mgibbl1" @@ -113,6 +84,16 @@ var/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/user = owner user.EraseBrood() +/datum/action/innate/terrorspider/queen/empress/empresslings + name = "Empresss Spiderlings" + icon_icon = 'icons/effects/effects.dmi' + button_icon_state = "spiderling" + +/datum/action/innate/terrorspider/queen/empress/empresslings/Activate() + var/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/user = owner + user.EmpressLings() + + // ---------- WEB /mob/living/simple_animal/hostile/poison/terror_spider/proc/Web() @@ -186,13 +167,26 @@ // ---------- WRAP +/mob/living/simple_animal/hostile/poison/terror_spider/proc/mobIsWrappable(mob/living/M) + if(!istype(M)) + return FALSE + if(M.stat != DEAD) + return FALSE + if(M.anchored) + return FALSE + if(!Adjacent(M)) + return FALSE + if(isterrorspider(M)) + return FALSE + return TRUE + /mob/living/simple_animal/hostile/poison/terror_spider/proc/FindWrapTarget() if(!cocoon_target) var/list/choices = list() for(var/mob/living/L in oview(1,src)) - if(Adjacent(L) && !L.anchored) - if(L.stat == DEAD) - choices += L + if(!mobIsWrappable(L)) + continue + choices += L for(var/obj/O in oview(1,src)) if(Adjacent(O) && !O.anchored) if(!istype(O, /obj/structure/spider/terrorweb) && !istype(O, /obj/structure/spider/cocoon) && !istype(O, /obj/structure/spider/spiderling/terror_spiderling)) @@ -226,9 +220,7 @@ O.loc = C large_cocoon = 1 for(var/mob/living/L in C.loc) - if(istype(L, /mob/living/simple_animal/hostile/poison/terror_spider)) - continue - if(L.stat != DEAD) + if(!mobIsWrappable(L)) continue if(iscarbon(L)) regen_points += regen_points_per_kill diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/chem.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/chem.dm index 60e11de670a..b685fdd1959 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/chem.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/chem.dm @@ -22,7 +22,7 @@ else if(volume < 90) // bitten thrice, die quickly, severe muscle cramps make movement very difficult. Even calling for help probably won't save you. // total damage: 4, human health 150 until crit, = 37.5 ticks, = 75s = 1m15s until death - update_flags |= M.adjustToxLoss(4, FALSE) // a bit worse than coiine + update_flags |= M.adjustToxLoss(4, FALSE) update_flags |= M.EyeBlurry(6, FALSE) M.Confused(6) else diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm index 137d6739466..f011becf6b8 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm @@ -19,7 +19,6 @@ ventcrawler = 1 idle_ventcrawl_chance = 0 ai_playercontrol_allowtype = 0 - rapid = 3 canlay = 1000 spider_tier = TS_TIER_5 projectiletype = /obj/item/projectile/terrorqueenspit/empress @@ -35,6 +34,8 @@ /mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/New() ..() + empresslings_action = new() + empresslings_action.Grant(src) empresserase_action = new() empresserase_action.Grant(src) @@ -44,7 +45,6 @@ /mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/NestMode() ..() queeneggs_action.button.name = "Empress Eggs" - queenfakelings_action.button.name = "Empress Lings" /mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/LayQueenEggs() var/eggtype = input("What kind of eggs?") as null|anything in list(TS_DESC_QUEEN, TS_DESC_MOTHER, TS_DESC_PRINCE, TS_DESC_PRINCESS, TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN, TS_DESC_BLACK, TS_DESC_PURPLE, TS_DESC_WHITE, TS_DESC_BROWN) @@ -70,7 +70,7 @@ if(TS_DESC_PRINCE) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/prince, numlings) if(TS_DESC_PRINCESS) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/princess, numlings) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess, numlings) if(TS_DESC_MOTHER) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/mother, numlings) if(TS_DESC_QUEEN) @@ -78,7 +78,7 @@ else to_chat(src, "Unrecognized egg type.") -/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/QueenFakeLings() +/mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/proc/EmpressLings() var/numlings = input("How many?") as null|anything in list(10, 20, 30, 40, 50) var/sbpc = input("%chance to be stillborn?") as null|anything in list(0, 25, 50, 75, 100) for(var/i=0, iThrough the hivemind, the raw power of [src] floods into your body, burning it from the inside out!") @@ -106,8 +107,6 @@ qdel(T) to_chat(src, "All Terror Spiders, except yourself, will die off shortly.") - /obj/item/projectile/terrorqueenspit/empress - damage_type = BURN - damage = 30 - bonus_tox = 0 + damage = 90 + diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm index e7daced65ff..b07f34ec368 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/green.dm @@ -39,11 +39,10 @@ to_chat(src, "You must wrap more humanoid prey before you can do this!") return var/list/eggtypes = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN) - var/num_brown = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/brown) - if(num_brown < 2) + var/list/spider_array = CountSpidersDetailed(FALSE) + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/brown] < 2) eggtypes += TS_DESC_BROWN - var/num_black = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/black) - if(num_black < 2) + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/black] < 2) eggtypes += TS_DESC_BLACK var/eggtype = pick(eggtypes) if(client) @@ -51,6 +50,10 @@ if(!(eggtype in eggtypes)) to_chat(src, "Unrecognized egg type.") return 0 + if(fed < feedings_to_lay) + // We have to check this again after the popup, to account for people spam-clicking the button, then doing all the popups at once. + to_chat(src, "You must wrap more humanoid prey before you can do this!") + return visible_message("[src] lays a cluster of eggs.") if(eggtype == TS_DESC_RED) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, 1) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm index 245ae95c5e9..407e6f3174e 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm @@ -4,7 +4,8 @@ /mob/living/simple_animal/hostile/poison/terror_spider/proc/DoHiveSense() var/hsline = "" to_chat(src, "Your Brood: ") - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing if(T.spider_awaymission != spider_awaymission) continue hsline = "* [T] in [get_area(T)], " @@ -20,21 +21,55 @@ /mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpiders() var/numspiders = 0 - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing if(T.stat != DEAD && !T.spider_placed && spider_awaymission == T.spider_awaymission) numspiders += 1 return numspiders -/mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpidersType(specific_type) - var/numspiders = 0 - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) - if(T.stat != DEAD && !T.spider_placed && spider_awaymission == T.spider_awaymission) - if(T.type == specific_type) - numspiders += 1 - for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in GLOB.ts_egg_list) - if(E.spiderling_type == specific_type && E.z == z) - numspiders += E.spiderling_number - for(var/obj/structure/spider/spiderling/terror_spiderling/L in GLOB.ts_spiderling_list) - if(!L.stillborn && L.grow_as == specific_type && L.z == z) - numspiders += 1 - return numspiders +/mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpidersDetailed(check_mine = FALSE, list/mytypes = list()) + var/list/spider_totals = list("all" = 0) + var/check_list = length(mytypes) > 0 + for(var/thistype in mytypes) + spider_totals[thistype] = 0 + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing + if(T.stat == DEAD || T.spider_placed || spider_awaymission != T.spider_awaymission) + continue + if(check_mine && T.spider_myqueen != src) + continue + if(check_list && !(T.type in mytypes)) + continue + if(T == src) + continue + if(spider_totals[T.type]) + spider_totals[T.type]++ + else + spider_totals[T.type] = 1 + spider_totals["all"]++ + for(var/thing in GLOB.ts_egg_list) + var/obj/structure/spider/eggcluster/terror_eggcluster/E = thing + if(check_mine && E.spider_myqueen != src) + continue + if(check_list && E.spiderling_type && !(E.spiderling_type in mytypes)) + continue + if(spider_totals[E.spiderling_type]) + spider_totals[E.spiderling_type] += E.spiderling_number + else + spider_totals[E.spiderling_type] = E.spiderling_number + spider_totals["all"] += E.spiderling_number + for(var/thing in GLOB.ts_spiderling_list) + var/obj/structure/spider/spiderling/terror_spiderling/L = thing + if(L.stillborn) + continue + if(check_mine && L.spider_myqueen != src) + continue + if(check_list && L.grow_as && !(L.grow_as in mytypes)) + continue + if(spider_totals[L.grow_as]) + spider_totals[L.grow_as]++ + else + spider_totals[L.grow_as] = 1 + spider_totals["all"]++ + return spider_totals + diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm index e486872abbb..7b900d8a881 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/prince.dm @@ -29,6 +29,7 @@ spider_opens_doors = 2 web_type = /obj/structure/spider/terrorweb/purple ai_spins_webs = FALSE + gender = MALE /mob/living/simple_animal/hostile/poison/terror_spider/prince/death(gibbed) if(can_die() && !hasdied && spider_uo71) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm index 047219a151b..787d89b722f 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/princess.dm @@ -2,58 +2,80 @@ // -------------------------------------------------------------------------------- // ----------------- TERROR SPIDERS: T3 PRINCESS OF TERROR -------------------------- // -------------------------------------------------------------------------------- -// -------------: ROLE: cutesy -// -------------: AI: as green, but will evolve to queen when it can -// -------------: SPECIAL: can evolve into a queen, if fed enough -// -------------: TO FIGHT IT: kill it before it evolves +// -------------: ROLE: mini-queen, maintains a smaller nest, but also more expendable +// -------------: AI: maintains a small group of spiders. Small fraction of a queen's nest. +// -------------: SPECIAL: lays eggs over time, like a queen +// -------------: TO FIGHT IT: hunt it before it lays eggs // -------------: SPRITES FROM: FoS, https://www.paradisestation.org/forum/profile/335-fos -/mob/living/simple_animal/hostile/poison/terror_spider/princess +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess name = "Princess of Terror spider" desc = "An enormous spider. It looks strangely cute and fluffy." - spider_role_summary = "Future Queen" + spider_role_summary = "Mini-Queen" ai_target_method = TS_DAMAGE_SIMPLE icon_state = "terror_princess1" icon_living = "terror_princess1" - icon_dead = "terror_princess_dead1" + icon_dead = "terror_princess1_dead" maxHealth = 150 health = 150 - regen_points_per_hp = 1 // always regens very fast - force_threshold = 18 // outright immune to anything of force under 18, same as queen - melee_damage_lower = 10 - melee_damage_upper = 20 - idle_ventcrawl_chance = 5 spider_tier = TS_TIER_3 - spider_opens_doors = 2 - web_type = /obj/structure/spider/terrorweb/queen - var/feedings_to_evolve = 3 - var/datum/action/innate/terrorspider/ventsmash/ventsmash_action - var/datum/action/innate/terrorspider/evolvequeen/evolvequeen_action -/mob/living/simple_animal/hostile/poison/terror_spider/princess/New() - ..() - ventsmash_action = new() - ventsmash_action.Grant(src) - evolvequeen_action = new() - evolvequeen_action.Grant(src) + // Unlike queens, no ranged attack. + ranged = 0 + retreat_distance = 0 + minimum_distance = 0 + projectilesound = null + projectiletype = null -/mob/living/simple_animal/hostile/poison/terror_spider/princess/proc/evolve_to_queen() - var/mob/living/simple_animal/hostile/poison/terror_spider/queen/Q = new(loc) - if(mind) - mind.transfer_to(Q) - // Calling `transfer_to()` removes our new body (the Queen's) ability to see the med hud, so we have to re-add the queen here. - var/datum/atom_hud/U = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] - U.add_hud_to(Q) - qdel(src) + canlay = 0 + hasnested = TRUE + spider_spawnfrequency = 300 // 30 seconds + var/grant_prob = 25 // 25% chance every spider_spawnfrequency seconds to gain 1 egg + var/spider_max_children = 8 -/mob/living/simple_animal/hostile/poison/terror_spider/princess/DoWrap() - . = ..() - if(fed == 0) + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/grant_queen_subtype_abilities() + // Queens start in movement mode, where they can ventcrawl but not lay eggs. Then they move to NestMode() where they can wallsmash and egglay, but not ventcrawl. + // Princesses are simpler, and can always lay eggs, always vent crawl, but never smash walls. Unlike queens, they don't have a "nesting" transformation. + queeneggs_action = new() + queeneggs_action.Grant(src) + queensense_action = new() + queensense_action.Grant(src) + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/ListAvailableEggTypes() + var/list/valid_types = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN) + + // Each princess can also have ONE black/purple/brown. If it dies, they can pick a new spider from the 3 advanced types to lay. + var/list/spider_array = CountSpidersDetailed(TRUE, list(/mob/living/simple_animal/hostile/poison/terror_spider/black, /mob/living/simple_animal/hostile/poison/terror_spider/purple, /mob/living/simple_animal/hostile/poison/terror_spider/brown)) + if(spider_array["all"] < 1) + valid_types |= TS_DESC_BLACK + valid_types |= TS_DESC_PURPLE + valid_types |= TS_DESC_BROWN + + return valid_types + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/grant_eggs() + spider_lastspawn = world.time + + if(!isturf(loc)) + to_chat(src, "You cannot generate eggs while hiding in [loc].") + return + + if(!prob(grant_prob)) + return + + var/list/spider_array = CountSpidersDetailed(TRUE) + var/brood_count = spider_array["all"] + + // Color shifts depending on how much of their brood capacity they have used. + if(brood_count == 0) icon_state = "terror_princess1" icon_living = "terror_princess1" - icon_dead = "terror_princess_dead1" + icon_dead = "terror_princess1_dead" desc = "An enormous spider. It looks strangely cute and fluffy, with soft pink fur covering most of its body." - else if(fed == 1) + else if(brood_count < (spider_max_children /2)) icon_state = "terror_princess2" icon_living = "terror_princess2" icon_dead = "terror_princess2_dead" @@ -62,13 +84,44 @@ icon_state = "terror_princess3" icon_living = "terror_princess3" icon_dead = "terror_princess3_dead" - desc = "An enormous spider. Its entire body has turned an ominous blood red color, with actual blood dripping from its jaws. It stares around, hungrily." + desc = "An enormous spider. Its entire body looks to be the color of dried blood." -/mob/living/simple_animal/hostile/poison/terror_spider/princess/spider_special_action() - if(cocoon_target) - handle_cocoon_target() - else if(fed >= feedings_to_evolve) - evolve_to_queen() - else if(world.time > (last_cocoon_object + freq_cocoon_object)) - seek_cocoon_target() + if((brood_count + canlay) >= spider_max_children) + return + canlay++ + if(canlay == 1) + to_chat(src, "You have an egg available to lay.") + else + to_chat(src, "You have [canlay] eggs available to lay.") + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/show_egg_timer() + var/average_timer = (1 / (grant_prob / 100)) * (spider_spawnfrequency / 10) + to_chat(src, "Too soon to attempt that again. You generate a new egg every [average_timer] seconds, on average.") + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/NestMode() + // Princesses don't nest. However, we still need to override this in case an AI princess calls it. + return + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/spider_special_action() + // Princess AI routine. GREATLY simplified version of queen routine. + if(!stat && !ckey) + // Utilize normal queen AI for finding a nest site (neststep=0), and activating NestMode() (neststep=1) + if(neststep != 2) + return ..() + // After that, simply lay an egg once per nestfrequency, until we have the max. + if(world.time < (lastnestsetup + nestfrequency)) + return + lastnestsetup = world.time + if(ai_nest_is_full()) + return + spider_lastspawn = world.time + DoLayTerrorEggs(pick(spider_types_standard), 1) + // Yes, this means NPC princesses won't create T2 spiders. + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess/ai_nest_is_full() + var/list/spider_array = CountSpidersDetailed(TRUE) + if(spider_array["all"] >= spider_max_children) + return TRUE + return FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm index 7e36b411bae..907e46b523e 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm @@ -47,7 +47,7 @@ /mob/living/simple_animal/hostile/poison/terror_spider/purple/Life(seconds, times_fired) . = ..() - if(.) // if mob is NOT dead + if(stat != DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive. if(!degenerate && spider_myqueen) if(dcheck_counter >= 10) dcheck_counter = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm index bf3c98bb86a..e78acd08a90 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm @@ -5,7 +5,7 @@ // -------------: ROLE: gamma-level threat to the whole station, like a blob // -------------: AI: builds a nest, lays many eggs, attempts to take over the station // -------------: SPECIAL: spins webs, breaks lights, breaks cameras, webs objects, lays eggs, commands other spiders... -// -------------: TO FIGHT IT: bring an army, and take no prisoners. Mechs and/or decloner guns are a very good idea. +// -------------: TO FIGHT IT: bring an army, and take no prisoners. Mechs are a very good idea. // -------------: SPRITES FROM: IK3I /mob/living/simple_animal/hostile/poison/terror_spider/queen @@ -43,69 +43,94 @@ var/neststep = 0 var/hasnested = FALSE var/spider_max_per_nest = 35 // above this, AI queens become stable - var/canlay = 4 // main counter for egg-laying ability! # = num uses, incremented at intervals + var/canlay = 5 // main counter for egg-laying ability! # = num uses, incremented at intervals var/eggslaid = 0 - var/spider_can_fakelings = 3 // spawns defective spiderlings that don't grow up, used to freak out crew, atmosphere var/list/spider_types_standard = list(/mob/living/simple_animal/hostile/poison/terror_spider/red, /mob/living/simple_animal/hostile/poison/terror_spider/gray, /mob/living/simple_animal/hostile/poison/terror_spider/green, /mob/living/simple_animal/hostile/poison/terror_spider/black) var/datum/action/innate/terrorspider/queen/queennest/queennest_action var/datum/action/innate/terrorspider/queen/queensense/queensense_action var/datum/action/innate/terrorspider/queen/queeneggs/queeneggs_action - var/datum/action/innate/terrorspider/queen/queenfakelings/queenfakelings_action var/datum/action/innate/terrorspider/ventsmash/ventsmash_action + /mob/living/simple_animal/hostile/poison/terror_spider/queen/New() ..() - queennest_action = new() - queennest_action.Grant(src) ventsmash_action = new() ventsmash_action.Grant(src) + grant_queen_subtype_abilities() spider_myqueen = src if(spider_awaymission) - spider_growinstantly = 1 + spider_growinstantly = TRUE spider_spawnfrequency = 150 + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/grant_queen_subtype_abilities() + queennest_action = new() + queennest_action.Grant(src) + /mob/living/simple_animal/hostile/poison/terror_spider/queen/Life(seconds, times_fired) . = ..() - if(.) // if mob is NOT dead - if(ckey && canlay < 12 && hasnested) // max 12 eggs worth stored at any one time, realistically that's tons. + if(stat != DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive. + if(ckey && hasnested) if(world.time > (spider_lastspawn + spider_spawnfrequency)) - if(eggslaid >= 20) - canlay += 3 - else if(eggslaid >= 10) - canlay += 2 - else - canlay++ - spider_lastspawn = world.time - if(canlay == 1) - to_chat(src, "You have an egg available to lay.") - else if(canlay == 12) - to_chat(src, "You have [canlay] eggs available to lay. You won't grow any more eggs until you lay some of your existing ones.") - else - to_chat(src, "You have [canlay] eggs available to lay.") + grant_eggs() + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/grant_eggs() + spider_lastspawn = world.time + canlay += getSpiderLevel() + if(canlay == 1) + to_chat(src, "You have an egg available to lay.") + else if(canlay > 1) + to_chat(src, "You have [canlay] eggs available to lay.") + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/getSpiderLevel() + return 1 + round(MinutesAlive() / 10) + + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/MinutesAlive() + return round((world.time - spider_creation_time) / 600) + /mob/living/simple_animal/hostile/poison/terror_spider/queen/death(gibbed) if(can_die() && !hasdied) if(spider_uo71) UnlockBlastDoors("UO71_Caves") - // When a queen dies, so do her player-controlled purple-type guardians. Intended as a motivator for purples to ensure they guard her. - for(var/mob/living/simple_animal/hostile/poison/terror_spider/purple/P in GLOB.ts_spiderlist) - if(ckey) - P.visible_message("\The [src] writhes in pain!") - to_chat(P,"\The [src] has died. Without her hivemind link, purple terrors like yourself cannot survive more than a few minutes!") - P.degenerate = 1 + // When a queen (or subtype!) dies, so do all of her spiderlings, and half of all her fully grown offspring + // This feature is intended to provide a way for crew to still win even if the queen has overwhelming numbers - by sniping the queen. + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing + if(!T.spider_myqueen) + continue + if(T.spider_myqueen != src) + continue + if(prob(50) || T.spider_tier >= spider_tier) + to_chat(T, "\The psychic backlash from the death of [src] crashes into your mind! Somehow... you find a way to keep going!") + continue + T.visible_message("[T] writhes in pain!") + to_chat(T, "\The psychic backlash from the death of [src] overwhelms you! You feel the life start to drain out of you...") + T.degenerate = TRUE + for(var/thing in GLOB.ts_spiderling_list) + var/obj/structure/spider/spiderling/terror_spiderling/T = thing + if(T.spider_myqueen && T.spider_myqueen == src) + qdel(T) return ..() + /mob/living/simple_animal/hostile/poison/terror_spider/queen/Retaliate() ..() - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing T.enemies |= enemies + /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/ai_nest_is_full() var/numspiders = CountSpiders() if(numspiders >= spider_max_per_nest) return TRUE return FALSE + /mob/living/simple_animal/hostile/poison/terror_spider/queen/spider_special_action() if(!stat && !ckey) switch(neststep) @@ -152,11 +177,13 @@ neststep = 2 NestMode() if(2) - // Create initial four purple nest guards. + // Create initial T2 spiders. if(world.time > (lastnestsetup + nestfrequency)) lastnestsetup = world.time spider_lastspawn = world.time - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 4) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 2) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/white, 2) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, 2) neststep = 3 if(3) // Create spiders (random types) until nest is full. @@ -183,28 +210,26 @@ neststep = 4 else spider_lastspawn = world.time - var/num_purple = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/purple) - var/num_white = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/white) - var/num_brown = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/brown) - if(num_purple < 4) + var/list/spider_array = CountSpidersDetailed(FALSE) + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/purple] < 4) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, 2) - else if(num_white < 2) + else if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/white] < 2) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/white, 2) - else if(num_brown < 4) + else if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/brown] < 4) DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, 4) else DoLayTerrorEggs(pick(spider_types_standard), 5) + /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/NestPrompt() var/confirm = alert(src, "Are you sure you want to nest? You will be able to lay eggs, and smash walls, but not ventcrawl.","Nest?","Yes","No") if(confirm == "Yes") NestMode() + /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/NestMode() queeneggs_action = new() queeneggs_action.Grant(src) - queenfakelings_action = new() - queenfakelings_action.Grant(src) queensense_action = new() queensense_action.Grant(src) queennest_action.Remove(src) @@ -213,21 +238,8 @@ ai_ventcrawls = FALSE environment_smash = ENVIRONMENT_SMASH_RWALLS DoQueenScreech(8, 100, 8, 100) - MassFlicker() to_chat(src, "You have matured to your egglaying stage. You can now smash through walls, and lay eggs, but can no longer ventcrawl.") -/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/MassFlicker() - var/list/target_lights = list() - for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.z != z) - continue - if(H.stat == DEAD) - continue - for(var/obj/machinery/light/L in orange(7, H)) - if(L.on && prob(25)) - target_lights += L - for(var/obj/machinery/light/I in target_lights) - I.flicker() /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/LayQueenEggs() if(stat == DEAD) @@ -236,48 +248,23 @@ to_chat(src, "You must nest before doing this.") return if(canlay < 1) - var/remainingtime = round(((spider_lastspawn + spider_spawnfrequency) - world.time) / 10, 1) - if(remainingtime > 0) - to_chat(src, "Too soon to attempt that again. Wait another [num2text(remainingtime)] seconds.") - else - to_chat(src, "Too soon to attempt that again. Wait just a few more seconds...") + show_egg_timer() return - var/list/eggtypes = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN, TS_DESC_BLACK, TS_DESC_PURPLE) - if(canlay >= 4) - eggtypes |= TS_DESC_BROWN - if(canlay >= 12) - eggtypes |= TS_DESC_MOTHER - eggtypes |= TS_DESC_PRINCE - var/num_purples = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/purple) - if(num_purples >= 2) - eggtypes -= TS_DESC_PURPLE - var/num_blacks = CountSpidersType(/mob/living/simple_animal/hostile/poison/terror_spider/black) - if(num_blacks >= 2) - eggtypes -= TS_DESC_BLACK + var/list/eggtypes = ListAvailableEggTypes() + var/list/eggtypes_uncapped = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN) + var/eggtype = input("What kind of eggs?") as null|anything in eggtypes + if(canlay < 1) + // this was checked before input() but we have to check again to prevent them spam-clicking the popup. + to_chat(src, "Too soon to lay another egg.") + return if(!(eggtype in eggtypes)) to_chat(src, "Unrecognized egg type.") return 0 - if(eggtype == TS_DESC_MOTHER || eggtype == TS_DESC_PRINCE) - if(canlay < 12) - to_chat(src, "Insufficient strength. It takes as much effort to lay one of those as it does to lay 12 normal eggs.") - else - if(eggtype == TS_DESC_MOTHER) - canlay -= 12 - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/mother, 1) - else if(eggtype == TS_DESC_PRINCE) - canlay -= 12 - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/prince, 1) - return - else if(eggtype == TS_DESC_BROWN) - if(canlay < 4) - to_chat(src, "Insufficient strength. It takes as much effort to lay one of those as it does to lay 4 normal eggs.") - else - canlay -= 4 - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, 1) - return + + // Multiple of eggtypes_uncapped can be laid at once. Other types must be laid one at a time (to prevent exploits) var/numlings = 1 - if(eggtype != TS_DESC_PURPLE) + if(eggtype in eggtypes_uncapped) if(canlay >= 5) numlings = input("How many in the batch?") as null|anything in list(1, 2, 3, 4, 5) else if(canlay >= 3) @@ -287,23 +274,61 @@ if(eggtype == null || numlings == null) to_chat(src, "Cancelled.") return + // Actually lay the eggs. + if(canlay < numlings) + // We have to check this again after the popups, to account for people spam-clicking the button, then doing all the popups at once. + to_chat(src, "Too soon to do this again!") + return canlay -= numlings eggslaid += numlings - if(eggtype == TS_DESC_RED) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, numlings) - else if(eggtype == TS_DESC_GRAY) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/gray, numlings) - else if(eggtype == TS_DESC_GREEN) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/green, numlings) - else if(eggtype == TS_DESC_BLACK) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/black, numlings) - else if(eggtype == TS_DESC_PURPLE) - DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, numlings) + switch(eggtype) + if(TS_DESC_RED) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/red, numlings) + if(TS_DESC_GRAY) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/gray, numlings) + if(TS_DESC_GREEN) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/green, numlings) + if(TS_DESC_BLACK) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/black, numlings) + if(TS_DESC_PURPLE) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/purple, numlings) + if(TS_DESC_BROWN) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/brown, numlings) + if(TS_DESC_MOTHER) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/mother, numlings) + if(TS_DESC_PRINCE) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/prince, numlings) + if(TS_DESC_PRINCESS) + DoLayTerrorEggs(/mob/living/simple_animal/hostile/poison/terror_spider/queen/princess, numlings) + else + to_chat(src, "Unrecognized egg type.") + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/show_egg_timer() + var/remainingtime = round(((spider_lastspawn + spider_spawnfrequency) - world.time) / 10, 1) + if(remainingtime > 0) + to_chat(src, "Too soon to attempt that again. Wait another [num2text(remainingtime)] seconds.") else - to_chat(src, "Unrecognized egg type.") + to_chat(src, "Too soon to attempt that again. Wait just a few more seconds...") + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/ListAvailableEggTypes() + if(MinutesAlive() >= 20) + var/list/spider_array = CountSpidersDetailed(TRUE, list(/mob/living/simple_animal/hostile/poison/terror_spider/mother, /mob/living/simple_animal/hostile/poison/terror_spider/prince, /mob/living/simple_animal/hostile/poison/terror_spider/queen/princess)) + if(spider_array["all"] == 0) + return list(TS_DESC_PRINCE, TS_DESC_PRINCESS) // Mother will be added to this list.... AFTER mothers are reworked. + + var/list/valid_types = list(TS_DESC_RED, TS_DESC_GRAY, TS_DESC_GREEN) + var/list/spider_array = CountSpidersDetailed(FALSE, list(/mob/living/simple_animal/hostile/poison/terror_spider/brown, /mob/living/simple_animal/hostile/poison/terror_spider/purple, /mob/living/simple_animal/hostile/poison/terror_spider/black)) + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/brown] < 2) + valid_types += TS_DESC_BROWN + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/purple] < 2) + valid_types += TS_DESC_PURPLE + if(spider_array[/mob/living/simple_animal/hostile/poison/terror_spider/black] < 2) + valid_types += TS_DESC_BLACK + return valid_types + /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/DoQueenScreech(light_range, light_chance, camera_range, camera_chance) - visible_message("\The [src] emits a bone-chilling shriek!") + visible_message("[src] emits a bone-chilling shriek!") for(var/obj/machinery/light/L in orange(light_range, src)) if(L.on && prob(light_chance)) L.break_light_tube() @@ -311,46 +336,37 @@ if(C.status && prob(camera_chance)) C.toggle_cam(src, 0) -/mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/QueenFakeLings() - if(eggslaid < 10) - to_chat(src, "You must lay at least 10 eggs before doing this.") + +/mob/living/simple_animal/hostile/poison/terror_spider/queen/examine(mob/user) + . = ..() + if(!key || stat == DEAD) return - if(spider_can_fakelings) - spider_can_fakelings-- - var/numlings = 25 - for(var/i in 1 to numlings) - var/obj/structure/spider/spiderling/terror_spiderling/S = new /obj/structure/spider/spiderling/terror_spiderling(get_turf(src)) - S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/red - S.stillborn = 1 - S.spider_mymother = src - if(!spider_can_fakelings) - queenfakelings_action.Remove(src) - else - to_chat(src, "You have run out of uses of this ability.") + if(!isobserver(user) && !isterrorspider(user)) + return + . += "[p_they(TRUE)] has laid [eggslaid] egg[eggslaid != 1 ? "s" : ""]." + . += "[p_they(TRUE)] has lived for [MinutesAlive()] minutes." + /obj/item/projectile/terrorqueenspit - name = "poisonous spit" - damage = 0 + name = "acid spit" + damage = 40 icon_state = "toxin" - damage_type = TOX - var/bonus_tox = 30 + damage_type = BURN -/obj/item/projectile/terrorqueenspit/on_hit(mob/living/carbon/target, blocked = 0, hit_zone) - if(ismob(target) && blocked < 100) - var/mob/living/L = target - if(L.reagents) - if(L.can_inject(null, FALSE, "chest", FALSE)) - L.Hallucinate(400) - if(!isterrorspider(L)) - L.adjustToxLoss(bonus_tox) /obj/structure/spider/terrorweb/queen - name = "shimmering web" - desc = "This web seems to shimmer all different colors in the light." + name = "airtight web" + desc = "This multi-layered web seems to be able to resist air pressure." -/obj/structure/spider/terrorweb/queen/web_special_ability(mob/living/carbon/C) - if(istype(C)) - var/inject_target = pick("chest","head") - if(C.can_inject(null, FALSE, inject_target, FALSE)) - C.Hallucinate(400) - C.adjustToxLoss(30) + +/obj/structure/spider/terrorweb/queen/New() + . = ..() + air_update_turf(TRUE) + +/obj/structure/spider/terrorweb/queen/CanAtmosPass(turf/T) + return FALSE + +/obj/structure/spider/terrorweb/queen/Destroy() + var/turf/T = get_turf(src) + . = ..() + T.air_update_turf(TRUE) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm index a889bc6d328..8961fcdddb5 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm @@ -188,7 +188,7 @@ C.enemies = enemies if(spider_growinstantly) C.amount_grown = 250 - C.spider_growinstantly = 1 + C.spider_growinstantly = TRUE spawn(10) stop_automated_movement = 0 @@ -196,7 +196,7 @@ name = "terror egg cluster" desc = "A cluster of tiny spider eggs. They pulse with a strong inner life, and appear to have sharp thorns on the sides." icon_state = "eggs" - var/spider_growinstantly = 0 + var/spider_growinstantly = FALSE var/spider_myqueen = null var/spider_mymother = null var/spiderling_type = null diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm index 11e4cac3e4a..c3e0c9a0511 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm @@ -126,7 +126,7 @@ spider_steps_taken++ CreatePath(entry_vent) step_to(src,entry_vent) - if(spider_debug > 0) + if(spider_debug) visible_message("[src] moves towards the vent [entry_vent].") else path_to_vent = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index 520143d71d0..66f6141c797 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -17,6 +17,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) // Name / Description name = "terror spider" desc = "The generic parent of all other terror spider types. If you see this in-game, it is a bug." + gender = FEMALE // Icons icon = 'icons/mob/terrorspider.dmi' @@ -141,29 +142,33 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) var/mylocation = null var/chasecycles = 0 var/web_infects = 0 + var/spider_creation_time = 0 var/datum/action/innate/terrorspider/web/web_action var/web_type = /obj/structure/spider/terrorweb var/datum/action/innate/terrorspider/wrap/wrap_action - // Breathing - require some oxygen, and no toxins, but take little damage from this requirement not being met (they can hold their breath) + // Breathing - require some oxygen, and no toxins atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - unsuitable_atmos_damage = 1 - // Temperature - can freeze in space and cook in plasma, but it takes extreme temperatures to do this. - minbodytemp = 100 - maxbodytemp = 500 - heat_damage_per_tick = 3 + // Temperature + heat_damage_per_tick = 5 // Takes 250% normal damage from being in a hot environment ("kill it with fire!") // DEBUG OPTIONS & COMMANDS - var/spider_growinstantly = 0 // DEBUG OPTION, DO NOT ENABLE THIS ON LIVE. IT IS USED TO TEST NEST GROWTH/SETUP AI. - var/spider_debug = 0 + var/spider_growinstantly = FALSE // DEBUG OPTION, DO NOT ENABLE THIS ON LIVE. IT IS USED TO TEST NEST GROWTH/SETUP AI. + var/spider_debug = FALSE // -------------------------------------------------------------------------------- // --------------------- TERROR SPIDERS: SHARED ATTACK CODE ----------------------- // -------------------------------------------------------------------------------- +/mob/living/simple_animal/hostile/poison/terror_spider/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect) + // Forces terrors to use the 'bite' graphic when attacking something. Same as code/modules/mob/living/carbon/alien/larva/larva_defense.dm#L34 + if(!no_effect && !visual_effect_icon) + visual_effect_icon = ATTACK_EFFECT_BITE + ..() + /mob/living/simple_animal/hostile/poison/terror_spider/AttackingTarget() if(isterrorspider(target)) if(target in enemies) @@ -187,14 +192,13 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) if(F.welded) to_chat(src, "The fire door is welded shut.") else - visible_message("\The [src] pries open the firedoor!") + visible_message("[src] pries open the firedoor!") F.open() else to_chat(src, "Closing fire doors does not help.") else if(istype(target, /obj/machinery/door/airlock)) var/obj/machinery/door/airlock/A = target - if(A.density) - try_open_airlock(A) + try_open_airlock(A) else if(isliving(target) && (!client || a_intent == INTENT_HARM)) var/mob/living/G = target if(issilicon(G)) @@ -221,27 +225,23 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) /mob/living/simple_animal/hostile/poison/terror_spider/examine(mob/user) . = ..() - var/list/msgs = list() - if(stat == DEAD) - msgs += "It appears to be dead.\n" - else + if(stat != DEAD) if(key) - msgs += "Its eyes regard you with a curious intelligence." + . += "[p_they(TRUE)] regards [p_their()] surroundings with a curious intelligence." if(health > (maxHealth*0.95)) - msgs += "It is in excellent health." + . += "[p_they(TRUE)] is in excellent health." else if(health > (maxHealth*0.75)) - msgs += "It has a few injuries." + . += "[p_they(TRUE)] has a few injuries." else if(health > (maxHealth*0.55)) - msgs += "It has many injuries." + . += "[p_they(TRUE)] has many injuries." else if(health > (maxHealth*0.25)) - msgs += "It is barely clinging on to life!" + . += "[p_they(TRUE)] is barely clinging on to life!" if(degenerate) - msgs += "It appears to be dying." + . += "[p_they(TRUE)] appears to be dying." else if(health < maxHealth && regen_points > regen_points_per_kill) - msgs += "It appears to be regenerating quickly." + . += "[p_they(TRUE)] appears to be regenerating quickly." if(killcount >= 1) - msgs += "It has blood dribbling from its mouth." - . += msgs.Join("
    ") + . += "[p_they(TRUE)] has blood dribbling from [p_their()] mouth." /mob/living/simple_animal/hostile/poison/terror_spider/New() ..() @@ -254,9 +254,10 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) if(web_type) web_action = new() web_action.Grant(src) - wrap_action = new() - wrap_action.Grant(src) - + if(regen_points_per_tick < regen_points_per_hp) + // Only grant the Wrap action button to spiders who need to use it to regenerate their health + wrap_action = new() + wrap_action.Grant(src) name += " ([rand(1, 1000)])" real_name = name msg_terrorspiders("[src] has grown in [get_area(src)].") @@ -278,6 +279,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) addtimer(CALLBACK(src, .proc/announcetoghosts), 30) var/datum/atom_hud/U = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] U.add_hud_to(src) + spider_creation_time = world.time /mob/living/simple_animal/hostile/poison/terror_spider/proc/announcetoghosts() if(spider_awaymission) @@ -285,8 +287,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) if(stat == DEAD) return if(ckey) - var/image/alert_overlay = image('icons/mob/terrorspider.dmi', icon_state) - notify_ghosts("[src] has appeared in [get_area(src)]. (already player-controlled)", source = src, alert_overlay = alert_overlay) + notify_ghosts("[src] (player controlled) has appeared in [get_area(src)].") else if(ai_playercontrol_allowtype) var/image/alert_overlay = image('icons/mob/terrorspider.dmi', icon_state) notify_ghosts("[src] has appeared in [get_area(src)].", enter_link = "(Click to control)", source = src, alert_overlay = alert_overlay, action = NOTIFY_ATTACK) @@ -298,7 +299,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) /mob/living/simple_animal/hostile/poison/terror_spider/Life(seconds, times_fired) . = ..() - if(!.) // if mob is dead + if(stat == DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive. if(prob(2)) // 2% chance every cycle to decompose visible_message("\The dead body of the [src] decomposes!") @@ -342,7 +343,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) /mob/living/simple_animal/hostile/poison/terror_spider/ObjBump(obj/O) if(istype(O, /obj/machinery/door/airlock)) var/obj/machinery/door/airlock/L = O - if(L.density) + if(L.density) // must check density here, to avoid rapid bumping of an airlock that is in the process of opening, instantly forcing it closed return try_open_airlock(L) if(istype(O, /obj/machinery/door/firedoor)) var/obj/machinery/door/firedoor/F = O @@ -352,7 +353,8 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) . = ..() /mob/living/simple_animal/hostile/poison/terror_spider/proc/msg_terrorspiders(msgtext) - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) + for(var/thing in GLOB.ts_spiderlist) + var/mob/living/simple_animal/hostile/poison/terror_spider/T = thing if(T.stat != DEAD) to_chat(T, "TerrorSense: [msgtext]") @@ -365,21 +367,61 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) /mob/living/simple_animal/hostile/poison/terror_spider/proc/try_open_airlock(obj/machinery/door/airlock/D) if(D.operating) return - if(!D.density) - to_chat(src, "Closing doors does not help us.") - else if(D.welded) - to_chat(src, "The door is welded shut.") + if(D.welded) + to_chat(src, "The door is welded.") else if(D.locked) - to_chat(src, "The door is bolted shut.") + to_chat(src, "The door is bolted.") else if(D.allowed(src)) - D.open(1) - return 1 + if(D.density) + D.open(TRUE) + else + D.close(TRUE) + return TRUE else if(D.arePowerSystemsOn() && (spider_opens_doors != 2)) to_chat(src, "The door's motors resist your efforts to force it.") else if(!spider_opens_doors) to_chat(src, "Your type of spider is not strong enough to force open doors.") else - visible_message("[src] pries open the door!") + visible_message("[src] forces the door!") playsound(src.loc, "sparks", 100, 1) - D.open(1) - return 1 + if(D.density) + D.open(TRUE) + else + D.close(TRUE) + return TRUE + + +/mob/living/simple_animal/hostile/poison/terror_spider/get_spacemove_backup() + . = ..() + // If we don't find any normal thing to use, attempt to use any nearby spider structure instead. + if(!.) + for(var/obj/structure/spider/S in range(1, get_turf(src))) + return S + +/mob/living/simple_animal/hostile/poison/terror_spider/Stat() + ..() + // Determines what shows in the "Status" tab for player-controlled spiders. Used to help players understand spider health regeneration mechanics. + // Uses because the status panel does NOT accept . + if(statpanel("Status") && ckey && stat == CONSCIOUS) + if(degenerate) + stat(null, "Hivemind Connection Severed! Dying...") // color=red + return + if(health != maxHealth) + var/hp_points_per_second = 0 + var/ltext = "FAST" + var/lcolor = "#fcba03" // orange + var/secs_per_tick = (SSmobs.wait / 10) // This uses SSmobs.wait because it must use the same frequency as mobs are processed + if(regen_points < (regen_points_per_hp * 2)) + // Slow regen speed: using regen_points as we get them. Figure out regen_points/sec, then convert that to hp/sec. + var/regen_points_per_second = (regen_points_per_tick / secs_per_tick) + hp_points_per_second = (regen_points_per_second / regen_points_per_hp) + ltext = "SLOW (HUNGRY!)" + lcolor = "#eb4034" // red + else + // Fast regen speed: healing at full 1 hp / tick rate. Just divide 1hp/tick by seconds/tick to get healing/sec. + hp_points_per_second = 1 / secs_per_tick + if(hp_points_per_second > 0) + var/pc_of_max_per_second = round(((hp_points_per_second / maxHealth) * 100), 0.1) + stat(null, "Regeneration: [ltext]: [num2text(pc_of_max_per_second)]% of health per second") + + diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 41786017ea2..8d6db8ade3c 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -86,6 +86,7 @@ //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. var/obj/item/held_item = null + flying = TRUE gold_core_spawnable = FRIENDLY_SPAWN @@ -710,7 +711,7 @@ ears.talk_into(src, message_pieces, message_mode, verb) used_radios += ears -/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) +/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) if(speaker != src && prob(50)) parrot_hear(html_decode(multilingual_to_message(message_pieces))) ..() diff --git a/code/modules/mob/living/simple_animal/posessed_object.dm b/code/modules/mob/living/simple_animal/posessed_object.dm index c3cb61d5970..729790d098c 100644 --- a/code/modules/mob/living/simple_animal/posessed_object.dm +++ b/code/modules/mob/living/simple_animal/posessed_object.dm @@ -32,8 +32,9 @@ animate_ghostly_presence(src, -1, 20, 1) // Restart the floating animation after the attack animation, as it will be cancelled. -/mob/living/simple_animal/possessed_object/start_pulling(var/atom/movable/AM) // Silly motherfuckers think they can pull things. - to_chat(src, "You are unable to pull [AM]!") +/mob/living/simple_animal/possessed_object/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) // Silly motherfuckers think they can pull things. + if(show_message) + to_chat(src, "You are unable to pull [AM]!") /mob/living/simple_animal/possessed_object/ghost() // Ghosting will return the object to normal, and will not disqualify the ghoster from various mid-round antag positions. diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index 663838d3a45..1e3c723b278 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -24,6 +24,7 @@ status_flags = 0 faction = list("cult") status_flags = CANPUSH + flying = TRUE loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) del_on_death = 1 deathmessage = "lets out a contented sigh as their form unwinds." diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 7d31b5d523b..4bff2c100bf 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -319,10 +319,9 @@ return verb /mob/living/simple_animal/movement_delay() - . = ..() - . = speed - + if(forced_look) + . += 3 . += config.animal_delay /mob/living/simple_animal/Stat() @@ -615,3 +614,8 @@ if(pcollar && collar_type) add_overlay("[collar_type]collar") add_overlay("[collar_type]tag") + +/mob/living/simple_animal/Login() + ..() + walk(src, 0) // if mob is moving under ai control, then stop AI movement + diff --git a/code/modules/mob/living/simple_animal/slime/say.dm b/code/modules/mob/living/simple_animal/slime/say.dm index 74241dc676c..8679d64a699 100644 --- a/code/modules/mob/living/simple_animal/slime/say.dm +++ b/code/modules/mob/living/simple_animal/slime/say.dm @@ -9,7 +9,7 @@ return verb -/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) +/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) if(speaker != src && !stat) if(speaker in Friends) speech_buffer = list() diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 15f483bef76..1b67b53119a 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -265,7 +265,7 @@ /mob/living/simple_animal/slime/unEquip(obj/item/I, force) return -/mob/living/simple_animal/slime/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) +/mob/living/simple_animal/slime/start_pulling(atom/movable/AM, state, force = pull_force, show_message = FALSE) return /mob/living/simple_animal/slime/attack_ui(slot) diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 7cd2b4414f7..ce266ba5b8f 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -141,18 +141,6 @@ if(updating && val_change) update_canmove() -/mob/living/proc/StartFlying() - var/val_change = !flying - flying = TRUE - if(val_change) - update_animations() - -/mob/living/proc/StopFlying() - var/val_change = !!flying - flying = FALSE - if(val_change) - update_animations() - // SCALAR STATUS EFFECTS diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 5345bda5b94..639f3da7aaf 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -20,14 +20,15 @@ spawn() alert("You have logged in already with another key this round, please log out of this one NOW or risk being banned!") if(matches) if(M.client) - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)].", 1) + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)].", 1) log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") else - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ", 1) + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ", 1) log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") /mob/Login() GLOB.player_list |= src + last_known_ckey = ckey update_Login_details() world.update_status() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 2d9d49f3fdc..ff68f7f7825 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -19,7 +19,6 @@ for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) AA.viewers -= src viewing_alternate_appearances = null - logs.Cut() LAssailant = null return ..() @@ -479,6 +478,41 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ return 0 //Unsupported slot //END HUMAN +/mob/proc/get_visible_mobs() + var/list/seen_mobs = list() + for(var/mob/M in view(src)) + seen_mobs += M + + return seen_mobs + +/** + * Returns an assoc list which contains the mobs in range and their "visible" name. + * Mobs out of view but in range will be listed as unknown. Else they will have their visible name +*/ +/mob/proc/get_telepathic_targets() + var/list/validtargets = new /list() + var/turf/T = get_turf(src) + var/list/mobs_in_view = get_visible_mobs() + + for(var/mob/living/M in range(14, T)) + if(M && M.mind) + if(M == src) + continue + var/mob_name + if(M in mobs_in_view) + mob_name = M.name + else + mob_name = "Unknown entity" + var/i = 0 + var/result_name + do + result_name = mob_name + if(i++) + result_name += " ([i])" // Avoid dupes + while(validtargets[result_name]) + validtargets[result_name] = M + return validtargets + // If you're looking for `reset_perspective`, that's a synonym for this proc. /mob/proc/reset_perspective(atom/A) if(client) @@ -578,6 +612,8 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ changeNext_move(CLICK_CD_POINT) var/obj/P = new /obj/effect/temp_visual/point(tile) P.invisibility = invisibility + P.pixel_x = A.pixel_x + P.pixel_y = A.pixel_y return 1 /mob/proc/ret_grab(obj/effect/list_container/mobl/L as obj, flag) @@ -880,6 +916,9 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ return if(!Adjacent(usr)) return + if(IsFrozen(src) && !is_admin(usr)) + to_chat(usr, "Interacting with admin-frozen players is not permitted.") + return if(isLivingSSD(src) && M.client && M.client.send_ssd_warning(src)) return show_inv(usr) @@ -1228,10 +1267,13 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ create_log_in_list(debug_log, text, collapse, world.timeofday) /mob/proc/create_log(log_type, what, target = null, turf/where = get_turf(src)) - LAZYINITLIST(logs[log_type]) - var/list/log_list = logs[log_type] + if(!ckey) + return + var/real_ckey = ckey + if(ckey[1] == "@") // Admin aghosting will do this + real_ckey = copytext(ckey, 2) var/datum/log_record/record = new(log_type, src, what, target, where, world.time) - log_list.Add(record) + GLOB.logging.add_log(real_ckey, record) /proc/create_log_in_list(list/target, text, collapse = TRUE, last_log)//forgive me code gods for this shitcode proc //this proc enables lovely stuff like an attack log that looks like this: "[18:20:29-18:20:45]21x John Smith attacked Andrew Jackson with a crowbar." diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 1477be47d89..80404460b50 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -36,7 +36,7 @@ var/list/attack_log_old = list( ) var/list/debug_log = null - var/list/logs = list() // Logs for each log type defined in __DEFINES/logs.dm + var/last_known_ckey = null // Used in logging var/last_log = 0 var/obj/machinery/machine = null diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 5fb5bcb06c8..6acfb92a515 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -113,7 +113,7 @@ var/question = "Do you want to play as [M.real_name ? M.real_name : M.name][M.job ? " ([M.job])" : ""]" if(alert("Do you want to show the antag status?","Show antag status","Yes","No") == "Yes") question += ", [M.mind?.special_role ? M.mind?.special_role : "No special role"]" - var/list/mob/dead/observer/candidates = pollCandidates("[question]?", poll_time = 100, min_hours = minhours) + var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("[question]?", poll_time = 10 SECONDS, min_hours = minhours, source = M) var/mob/dead/observer/theghost = null if(LAZYLEN(candidates)) @@ -288,7 +288,7 @@ S.message = Gibberish(S.message, p) -proc/muffledspeech(phrase) +/proc/muffledspeech(phrase) phrase = html_decode(phrase) var/leng=length(phrase) var/counter=length(phrase) @@ -675,4 +675,3 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM return FALSE //This is the only case someone should actually be completely blocked from antag rolling as well return TRUE -#define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider)) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 4797c685fe1..0325d13faf0 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -369,8 +369,9 @@ step(pulling, get_dir(pulling.loc, A)) return -/mob/proc/update_gravity() +/mob/proc/update_gravity(has_gravity) return + /client/proc/check_has_body_select() return mob && mob.hud_used && mob.hud_used.zone_select && istype(mob.hud_used.zone_select, /obj/screen/zone_sel) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index f53daba6626..2bb2466ce02 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -77,7 +77,10 @@ else output += "

    Global Antag Candidacy" output += "
    You are [client.skip_antag ? "ineligible" : "eligible"] for all antag roles.

    " - output += "

    Observe

    " + if(!SSticker || SSticker.current_state == GAME_STATE_STARTUP) + output += "

    Observe (Please wait...)

    " + else + output += "

    Observe

    " if(GLOB.join_tos) output += "

    Terms of Service

    " @@ -103,7 +106,7 @@ output += "" - var/datum/browser/popup = new(src, "playersetup", "
    New Player Options
    ", 220, 290) + var/datum/browser/popup = new(src, "playersetup", "
    New Player Options
    ", 240, 330) popup.set_window_options("can_close=0") popup.set_content(output) popup.open(0) @@ -144,7 +147,8 @@ /mob/new_player/Topic(href, href_list[]) - if(!client) return 0 + if(!client) + return FALSE if(href_list["consent_signed"]) var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") @@ -162,12 +166,12 @@ if(href_list["show_preferences"]) client.prefs.ShowChoices(src) - return 1 + return TRUE if(href_list["ready"]) if(!tos_consent) to_chat(usr, "You must consent to the terms of service before you can join!") - return 0 + return FALSE ready = !ready new_player_panel_proc() @@ -182,7 +186,11 @@ if(href_list["observe"]) if(!tos_consent) to_chat(usr, "You must consent to the terms of service before you can join!") - return 0 + return FALSE + + if(!SSticker || SSticker.current_state == GAME_STATE_STARTUP) + to_chat(usr, "You must wait for the server to finish starting before you can join!") + return FALSE if(alert(src,"Are you sure you wish to observe? You cannot normally join the round after doing this!","Player Setup","Yes","No") == "Yes") if(!client) @@ -216,12 +224,12 @@ return 1 if(href_list["tos"]) privacy_consent() - return 0 + return FALSE if(href_list["late_join"]) if(!tos_consent) to_chat(usr, "You must consent to the terms of service before you can join!") - return 0 + return FALSE if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING) to_chat(usr, "The round is either not ready, or has already finished...") return @@ -229,7 +237,7 @@ if(!is_alien_whitelisted(src, client.prefs.species) && config.usealienwhitelist) to_chat(src, alert("You are currently not whitelisted to play [client.prefs.species].")) - return 0 + return FALSE LateChoices() @@ -248,7 +256,7 @@ if(client.prefs.species in GLOB.whitelisted_species) if(!is_alien_whitelisted(src, client.prefs.species) && config.usealienwhitelist) to_chat(src, alert("You are currently not whitelisted to play [client.prefs.species].")) - return 0 + return FALSE AttemptLateSpawn(href_list["SelectedJob"],client.prefs.spawnpoint) return diff --git a/code/modules/mob/new_player/poll.dm b/code/modules/mob/new_player/poll.dm index 19893819e0c..2e109d36d34 100644 --- a/code/modules/mob/new_player/poll.dm +++ b/code/modules/mob/new_player/poll.dm @@ -90,7 +90,7 @@ if(adminonly) question = "(Admin only poll) " + question - var output = "" + var/output = "" if(polltype == POLLTYPE_MULTI || polltype == POLLTYPE_OPTION) select_query = GLOB.dbcon.NewQuery("SELECT text, percentagecalc, (SELECT COUNT(optionid) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id GROUP BY optionid) AS votecount FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") select_query.Execute() diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index 77f8cc40da9..d51a58662ef 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -513,16 +513,16 @@ if(4) clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) if(JOB_MINER) - clothes_s = new /icon(uniform_dmi, "miner_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s = new /icon(uniform_dmi, "explorer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "explorer"), ICON_UNDERLAY) clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) if(prob(1)) clothes_s.Blend(new /icon('icons/mob/head.dmi', "bearpelt"), ICON_OVERLAY) switch(backbag) if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "explorerpack"), ICON_OVERLAY) if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-explorer"), ICON_OVERLAY) if(4) clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) if(JOB_LAWYER) diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 0417e28f546..7a26de718b6 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -44,17 +44,27 @@ usr.emote(message) -/mob/proc/say_dead(var/message) - if(!(client && client.holder)) - if(!config.dsay_allowed) - to_chat(src, "Deadchat is globally muted.") +/mob/proc/say_dead(message) + if(client) + if(!client.holder) + if(!config.dsay_allowed) + to_chat(src, "Deadchat is globally muted.") + return + + if(client.prefs.muted & MUTE_DEADCHAT) + to_chat(src, "You cannot talk in deadchat (muted).") return - if(client && !(client.prefs.toggles & CHAT_DEAD)) - to_chat(usr, "You have deadchat muted.") - return + if(!(client.prefs.toggles & CHAT_DEAD)) + to_chat(src, "You have deadchat muted.") + return + + if(client.handle_spam_prevention(message, MUTE_DEADCHAT)) + return say_dead_direct("[pick("complains", "moans", "whines", "laments", "blubbers", "salts")], \"[message]\"", src) + create_log(DEADCHAT_LOG, message) + log_ghostsay(message, src) /mob/proc/say_understands(var/mob/other, var/datum/language/speaking = null) if(stat == DEAD) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 2ee4a9d2a9c..212bb41720d 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -38,32 +38,42 @@ O.rename_self("AI",1) - spawn() - qdel(src) + INVOKE_ASYNC(GLOBAL_PROC, .proc/qdel, src) // To prevent the proc from returning null. return O -//human -> robot -/mob/living/carbon/human/proc/Robotize() +/** + For transforming humans into robots (cyborgs). + + Arguments: + * cell_type: A type path of the cell the new borg should receive. + * connect_to_default_AI: TRUE if you want /robot/New() to handle connecting the borg to the AI with the least borgs. + * AI: A reference to the AI we want to connect to. +*/ +/mob/living/carbon/human/proc/Robotize(cell_type = null, connect_to_default_AI = TRUE, mob/living/silicon/ai/AI = null) if(notransform) return for(var/obj/item/W in src) unEquip(W) - regenerate_icons() + notransform = 1 canmove = 0 icon = null invisibility = 101 - for(var/t in bodyparts) - qdel(t) - for(var/i in internal_organs) - qdel(i) - var/mob/living/silicon/robot/O = new /mob/living/silicon/robot( loc ) + // Creating a new borg here will connect them to a default AI and notify that AI, if `connect_to_default_AI` is TRUE. + var/mob/living/silicon/robot/O = new /mob/living/silicon/robot(loc, connect_to_AI = connect_to_default_AI) - // cyborgs produced by Robotize get an automatic power cell - O.cell = new /obj/item/stock_parts/cell/high(O) + // If `AI` is passed in, we want to connect to that AI specifically. + if(AI) + O.lawupdate = TRUE + O.connect_to_ai(AI) + + if(!cell_type) + O.cell = new /obj/item/stock_parts/cell/high(O) + else + O.cell = new cell_type(O) O.gender = gender O.invisibility = 0 @@ -77,26 +87,21 @@ else O.key = key - O.loc = loc + O.forceMove(loc) O.job = "Cyborg" - O.notify_ai(1) if(O.mind && O.mind.assigned_role == "Cyborg") - if(O.mind.role_alt_title == "Android") + if(O.mind.role_alt_title == "Robot") O.mmi = new /obj/item/mmi/robotic_brain(O) - else if(O.mind.role_alt_title == "Robot") - O.mmi = null //Robots do not have removable brains. else O.mmi = new /obj/item/mmi(O) - - if(O.mmi) O.mmi.transfer_identity(src) //Does not transfer key/client. + O.mmi.transfer_identity(src) //Does not transfer key/client. O.update_pipe_vision() O.Namepick() - spawn(0)//To prevent the proc from returning null. - qdel(src) + INVOKE_ASYNC(GLOBAL_PROC, .proc/qdel, src) // To prevent the proc from returning null. return O //human -> alien @@ -128,9 +133,7 @@ to_chat(new_xeno, "You are now an alien.") new_xeno.update_pipe_vision() - spawn(0)//To prevent the proc from returning null. - qdel(src) - return + qdel(src) /mob/living/carbon/human/proc/slimeize(reproduce as num) if(notransform) @@ -183,9 +186,7 @@ to_chat(new_corgi, "You are now a Corgi. Yap Yap!") new_corgi.update_pipe_vision() - spawn(0)//To prevent the proc from returning null. - qdel(src) - return + qdel(src) /mob/living/carbon/human/Animalize() @@ -214,9 +215,7 @@ to_chat(new_mob, "You suddenly feel more... animalistic.") new_mob.update_pipe_vision() - spawn() - qdel(src) - return + qdel(src) /mob/proc/Animalize() @@ -257,9 +256,7 @@ to_chat(pai, "You have become a pAI! Your name is [pai.name].") pai.update_pipe_vision() - spawn(0)//To prevent the proc from returning null. - qdel(src) - return + qdel(src) /mob/proc/safe_respawn(var/MP) if(!MP) diff --git a/code/modules/mob/update_status.dm b/code/modules/mob/update_status.dm index 1708bcfb7a2..b603fc5974e 100644 --- a/code/modules/mob/update_status.dm +++ b/code/modules/mob/update_status.dm @@ -55,12 +55,6 @@ // Procs that update other things about the mob -// Does various animations - Jitter, Flying, Spinning -/mob/proc/update_animations() - if(flying) - animate(src, pixel_y = pixel_y + 5 , time = 10, loop = 1, easing = SINE_EASING) - animate(pixel_y = pixel_y - 5, time = 10, loop = 1, easing = SINE_EASING) - /mob/proc/update_stat() return diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm deleted file mode 100644 index 948824fa32e..00000000000 --- a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm +++ /dev/null @@ -1,70 +0,0 @@ -GLOBAL_VAR_INIT(ntnrc_uid, 0) - -/datum/ntnet_conversation - var/id = null - var/title = "Untitled Conversation" - var/datum/computer_file/program/chatclient/operator // "Administrator" of this channel. Creator starts as channel's operator, - var/list/messages = list() - var/list/clients = list() - var/password - -/datum/ntnet_conversation/New() - id = GLOB.ntnrc_uid - GLOB.ntnrc_uid++ - if(GLOB.ntnet_global) - GLOB.ntnet_global.chat_channels.Add(src) - ..() - -/datum/ntnet_conversation/Destroy() - if(GLOB.ntnet_global) - GLOB.ntnet_global.chat_channels.Remove(src) - return ..() - -/datum/ntnet_conversation/proc/add_message(message, username) - message = "[station_time_timestamp()] [username]: [message]" - messages.Add(message) - trim_message_list() - -/datum/ntnet_conversation/proc/add_status_message(message) - messages.Add("[station_time_timestamp()] -!- [message]") - trim_message_list() - -/datum/ntnet_conversation/proc/trim_message_list() - if(messages.len <= 50) - return - messages = messages.Copy(messages.len-50 ,0) - -/datum/ntnet_conversation/proc/add_client(datum/computer_file/program/chatclient/C) - if(!istype(C)) - return - clients.Add(C) - add_status_message("[C.username] has joined the channel.") - // No operator, so we assume the channel was empty. Assign this user as operator. - if(!operator) - changeop(C) - -/datum/ntnet_conversation/proc/remove_client(datum/computer_file/program/chatclient/C) - if(!istype(C) || !(C in clients)) - return - clients.Remove(C) - add_status_message("[C.username] has left the channel.") - - // Channel operator left, pick new operator - if(C == operator) - operator = null - if(clients.len) - var/datum/computer_file/program/chatclient/newop = pick(clients) - changeop(newop) - - -/datum/ntnet_conversation/proc/changeop(datum/computer_file/program/chatclient/newop) - if(istype(newop)) - operator = newop - add_status_message("Channel operator status transferred to [newop.username].") - -/datum/ntnet_conversation/proc/change_title(newtitle, datum/computer_file/program/chatclient/client) - if(operator != client) - return 0 // Not Authorised - - add_status_message("[client.username] has changed channel title from [title] to [newtitle]") - title = newtitle diff --git a/code/modules/modular_computers/NTNet/NTNet.dm b/code/modules/modular_computers/NTNet/NTNet.dm deleted file mode 100644 index 2f9aa3f2e7d..00000000000 --- a/code/modules/modular_computers/NTNet/NTNet.dm +++ /dev/null @@ -1,145 +0,0 @@ -GLOBAL_DATUM_INIT(ntnet_global, /datum/ntnet, new()) - - -// This is the NTNet datum. There can be only one NTNet datum in game at once. Modular computers read data from this. -/datum/ntnet - var/list/relays = list() - var/list/logs = list() - var/list/available_station_software = list() - var/list/available_antag_software = list() - var/list/chat_channels = list() - var/list/fileservers = list() - // Amount of logs the system tries to keep in memory. Keep below 999 to prevent byond from acting weirdly. - // High values make displaying logs much laggier. - var/setting_maxlogcount = 100 - - // These only affect wireless. LAN (consoles) are unaffected since it would be possible to create scenario where someone turns off NTNet, and is unable to turn it back on since it refuses connections - var/setting_softwaredownload = 1 - var/setting_peertopeer = 1 - var/setting_communication = 1 - var/setting_systemcontrol = 1 - var/setting_disabled = 0 // Setting to 1 will disable all wireless, independently on relays status. - - var/intrusion_detection_enabled = 1 // Whether the IDS warning system is enabled - var/intrusion_detection_alarm = 0 // Set when there is an IDS warning due to malicious (antag) software. - - -// If new NTNet datum is spawned, it replaces the old one. -/datum/ntnet/New() - if(GLOB.ntnet_global && (GLOB.ntnet_global != src)) - GLOB.ntnet_global = src // There can be only one. - for(var/obj/machinery/ntnet_relay/R in GLOB.machines) - relays.Add(R) - R.NTNet = src - build_software_lists() - add_log("NTNet logging system activated.") - -// Simplified logging: Adds a log. log_string is mandatory parameter, source is optional. -/datum/ntnet/proc/add_log(log_string, obj/item/computer_hardware/network_card/source = null) - var/log_text = "[station_time_timestamp()] - " - if(source) - log_text += "[source.get_network_tag()] - " - else - log_text += "*SYSTEM* - " - log_text += log_string - logs.Add(log_text) - - - // We have too many logs, remove the oldest entries until we get into the limit - if(logs.len > setting_maxlogcount) - logs = logs.Copy(logs.len-setting_maxlogcount,0) - - -// Checks whether NTNet operates. If parameter is passed checks whether specific function is enabled. -/datum/ntnet/proc/check_function(specific_action = 0) - if(!relays || !relays.len) // No relays found. NTNet is down - return 0 - - var/operating = 0 - - // Check all relays. If we have at least one working relay, network is up. - for(var/M in relays) - var/obj/machinery/ntnet_relay/R = M - if(R.operable()) - operating = 1 - break - - if(setting_disabled) - return 0 - - switch(specific_action) - if(NTNET_SOFTWAREDOWNLOAD) - return (operating && setting_softwaredownload) - if(NTNET_PEERTOPEER) - return (operating && setting_peertopeer) - if(NTNET_COMMUNICATION) - return (operating && setting_communication) - if(NTNET_SYSTEMCONTROL) - return (operating && setting_systemcontrol) - return operating - -// Builds lists that contain downloadable software. -/datum/ntnet/proc/build_software_lists() - available_station_software = list() - available_antag_software = list() - for(var/F in typesof(/datum/computer_file/program)) - var/datum/computer_file/program/prog = new F - // Invalid type (shouldn't be possible but just in case), invalid filetype (not executable program) or invalid filename (unset program) - if(!prog || prog.filename == "UnknownProgram" || prog.filetype != "PRG") - continue - // Check whether the program should be available for station/antag download, if yes, add it to lists. - if(prog.available_on_ntnet) - available_station_software.Add(prog) - if(prog.available_on_syndinet) - available_antag_software.Add(prog) - -// Attempts to find a downloadable file according to filename var -/datum/ntnet/proc/find_ntnet_file_by_name(filename) - for(var/N in available_station_software) - var/datum/computer_file/program/P = N - if(filename == P.filename) - return P - for(var/N in available_antag_software) - var/datum/computer_file/program/P = N - if(filename == P.filename) - return P - -// Resets the IDS alarm -/datum/ntnet/proc/resetIDS() - intrusion_detection_alarm = 0 - -/datum/ntnet/proc/toggleIDS() - resetIDS() - intrusion_detection_enabled = !intrusion_detection_enabled - -// Removes all logs -/datum/ntnet/proc/purge_logs() - logs = list() - add_log("-!- LOGS DELETED BY SYSTEM OPERATOR -!-") - -// Updates maximal amount of stored logs. Use this instead of setting the number, it performs required checks. -/datum/ntnet/proc/update_max_log_count(lognumber) - if(!lognumber) - return 0 - // Trim the value if necessary - lognumber = max(MIN_NTNET_LOGS, min(lognumber, MAX_NTNET_LOGS)) - setting_maxlogcount = lognumber - add_log("Configuration Updated. Now keeping [setting_maxlogcount] logs in system memory.") - -/datum/ntnet/proc/toggle_function(function) - if(!function) - return - function = text2num(function) - switch(function) - if(NTNET_SOFTWAREDOWNLOAD) - setting_softwaredownload = !setting_softwaredownload - add_log("Configuration Updated. Wireless network firewall now [setting_softwaredownload ? "allows" : "disallows"] connection to software repositories.") - if(NTNET_PEERTOPEER) - setting_peertopeer = !setting_peertopeer - add_log("Configuration Updated. Wireless network firewall now [setting_peertopeer ? "allows" : "disallows"] peer to peer network traffic.") - if(NTNET_COMMUNICATION) - setting_communication = !setting_communication - add_log("Configuration Updated. Wireless network firewall now [setting_communication ? "allows" : "disallows"] instant messaging and similar communication services.") - if(NTNET_SYSTEMCONTROL) - setting_systemcontrol = !setting_systemcontrol - add_log("Configuration Updated. Wireless network firewall now [setting_systemcontrol ? "allows" : "disallows"] remote control of station's systems.") diff --git a/code/modules/modular_computers/NTNet/NTNet_relay.dm b/code/modules/modular_computers/NTNet/NTNet_relay.dm deleted file mode 100644 index 97ddcfb69c0..00000000000 --- a/code/modules/modular_computers/NTNet/NTNet_relay.dm +++ /dev/null @@ -1,125 +0,0 @@ -// Relays don't handle any actual communication. Global NTNet datum does that, relays only tell the datum if it should or shouldn't work. -/obj/machinery/ntnet_relay - name = "NTNet Quantum Relay" - desc = "A very complex router and transmitter capable of connecting electronic devices together. Looks fragile." - use_power = ACTIVE_POWER_USE - active_power_usage = 100 // 100 watts. Yes I know this is a high power machine but this fucking obliterates an unpowered smes (AI sat) - idle_power_usage = 100 - icon = 'icons/obj/stationobjs.dmi' - icon_state = "bus" - anchored = 1 - density = 1 - var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame. - var/enabled = 1 // Set to 0 if the relay was turned off - var/dos_failure = 0 // Set to 1 if the relay failed due to (D)DoS attack - var/list/dos_sources = list() // Backwards reference for qdel() stuff - - // Denial of Service attack variables - var/dos_overload = 0 // Amount of DoS "packets" in this relay's buffer - var/dos_capacity = 500 // Amount of DoS "packets" in buffer required to crash the relay - var/dos_dissipate = 1 // Amount of DoS "packets" dissipated over time. - - -// TODO: Implement more logic here. For now it's only a placeholder. -/obj/machinery/ntnet_relay/inoperable(additional_flags = 0) - if(stat & (BROKEN | NOPOWER | EMPED | additional_flags)) - return 1 - if(dos_failure) - return 1 - if(!enabled) - return 1 - return 0 - -/obj/machinery/ntnet_relay/update_icon() - if(operable()) - icon_state = "bus" - else - icon_state = "bus_off" - -/obj/machinery/ntnet_relay/process() - if(operable()) - use_power = ACTIVE_POWER_USE - else - use_power = IDLE_POWER_USE - - update_icon() - - if(dos_overload) - dos_overload = max(0, dos_overload - dos_dissipate) - - // If DoS traffic exceeded capacity, crash. - if((dos_overload > dos_capacity) && !dos_failure) - dos_failure = 1 - update_icon() - GLOB.ntnet_global.add_log("Quantum relay switched from normal operation mode to overload recovery mode.") - // If the DoS buffer reaches 0 again, restart. - if((dos_overload == 0) && dos_failure) - dos_failure = 0 - update_icon() - GLOB.ntnet_global.add_log("Quantum relay switched from overload recovery mode to normal operation mode.") - ..() - -/obj/machinery/ntnet_relay/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "ntnet_relay.tmpl", "NTNet Quantum Relay", 500, 300) - ui.open() - -/obj/machinery/ntnet_relay/ui_data(mob/user) - var/list/data = list() - data["enabled"] = enabled - data["dos_capacity"] = dos_capacity - data["dos_overload"] = dos_overload - data["dos_crashed"] = dos_failure - return data - - -/obj/machinery/ntnet_relay/Topic(href, list/href_list) - if(..()) - return - switch(href_list["action"]) - if("restart") - dos_overload = 0 - dos_failure = 0 - update_icon() - GLOB.ntnet_global.add_log("Quantum relay manually restarted from overload recovery mode to normal operation mode.") - if("toggle") - enabled = !enabled - GLOB.ntnet_global.add_log("Quantum relay manually [enabled ? "enabled" : "disabled"].") - update_icon() - return 1 - - -/obj/machinery/ntnet_relay/attack_hand(mob/living/user) - ui_interact(user) - -/obj/machinery/ntnet_relay/New() - uid = gl_uid - gl_uid++ - component_parts = list() - component_parts += new /obj/item/circuitboard/machine/ntnet_relay(null) - component_parts += new /obj/item/stack/cable_coil(null, 2) - - if(GLOB.ntnet_global) - GLOB.ntnet_global.relays.Add(src) - NTNet = GLOB.ntnet_global - GLOB.ntnet_global.add_log("New quantum relay activated. Current amount of linked relays: [NTNet.relays.len]") - ..() - -/obj/machinery/ntnet_relay/Destroy() - if(GLOB.ntnet_global) - GLOB.ntnet_global.relays.Remove(src) - GLOB.ntnet_global.add_log("Quantum relay connection severed. Current amount of linked relays: [NTNet.relays.len]") - NTNet = null - - for(var/datum/computer_file/program/ntnet_dos/D in dos_sources) - D.target = null - D.error = "Connection to quantum relay severed" - - return ..() - -/obj/item/circuitboard/machine/ntnet_relay - name = "NTNet Relay (Machine Board)" - build_path = /obj/machinery/ntnet_relay - origin_tech = "programming=3;bluespace=3;magnets=2" - req_components = list(/obj/item/stack/cable_coil = 2) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm deleted file mode 100644 index 1dd45a33603..00000000000 --- a/code/modules/modular_computers/computers/item/computer.dm +++ /dev/null @@ -1,369 +0,0 @@ -// This is the base type that does all the hardware stuff. -// Other types expand it - tablets use a direct subtypes, and -// consoles and laptops use "procssor" item that is held inside machinery piece -/obj/item/modular_computer - name = "modular microcomputer" - desc = "A small portable microcomputer." - - var/enabled = 0 // Whether the computer is turned on. - var/screen_on = 1 // Whether the computer is active/opened/it's screen is on. - var/datum/computer_file/program/active_program = null // A currently active program running on the computer. - var/hardware_flag = 0 // A flag that describes this device type - var/last_power_usage = 0 - var/last_battery_percent = 0 // Used for deciding if battery percentage has chandged - var/last_world_time = "00:00" - var/list/last_header_icons - var/emagged = 0 // Whether the computer is emagged. - - var/base_active_power_usage = 50 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too. - var/base_idle_power_usage = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops) - - // Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..) - // must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently - // If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example. - - icon = 'icons/obj/computer.dmi' - icon_state = "laptop-open" - var/icon_state_unpowered = null // Icon state when the computer is turned off. - var/icon_state_powered = null // Icon state when the computer is turned on. - var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen. - var/max_hardware_size = 0 // Maximal hardware w_class. Tablets/PDAs have 1, laptops 2, consoles 4. - var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer. - - integrity_failure = 50 - max_integrity = 100 - armor = list("melee" = 0, "bullet" = 20, "laser" = 20, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 0, "acid" = 0) - - // Important hardware (must be installed for computer to work) - - // Optional hardware (improves functionality, but is not critical for computer to work) - - var/list/all_components // List of "connection ports" in this computer and the components with which they are plugged - - var/list/idle_threads // Idle programs on background. They still receive process calls but can't be interacted with. - var/obj/physical = null // Object that represents our computer. It's used for Adjacent() and UI visibility checks. - - - -/obj/item/modular_computer/New() - update_icon() - if(!physical) - physical = src - ..() - START_PROCESSING(SSobj, src) - all_components = list() - idle_threads = list() - -/obj/item/modular_computer/Destroy() - kill_program(forced = TRUE) - for(var/H in all_components) - var/obj/item/computer_hardware/CH = all_components[H] - if(CH.holder == src) - CH.on_remove(src) - CH.holder = null - all_components.Remove(CH.device_type) - qdel(CH) - physical = null - STOP_PROCESSING(SSobj, src) - return ..() - - -/obj/item/modular_computer/proc/add_verb(var/path) - switch(path) - if(MC_CARD) - verbs += /obj/item/modular_computer/proc/eject_id - if(MC_SDD) - verbs += /obj/item/modular_computer/proc/eject_disk - if(MC_AI) - verbs += /obj/item/modular_computer/proc/eject_card - -/obj/item/modular_computer/proc/remove_verb(path) - switch(path) - if(MC_CARD) - verbs -= /obj/item/modular_computer/proc/eject_id - if(MC_SDD) - verbs -= /obj/item/modular_computer/proc/eject_disk - if(MC_AI) - verbs -= /obj/item/modular_computer/proc/eject_card - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/item/modular_computer/proc/eject_id() - set name = "Eject ID" - set category = "Object" - set src in view(1) - - if(issilicon(usr)) - return - var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - if(!usr.incapacitated()) - card_slot.try_eject(null, usr) - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/item/modular_computer/proc/eject_card() - set name = "Eject Intellicard" - set category = "Object" - set src in view(1) - - if(issilicon(usr)) - return - var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI] - if(!usr.incapacitated()) - ai_slot.try_eject(null, usr, 1) - - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/item/modular_computer/proc/eject_disk() - set name = "Eject Data Disk" - set category = "Object" - set src in view(1) - - if(issilicon(usr)) - return - - if(!usr.incapacitated()) - var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD] - if(uninstall_component(portable_drive, usr)) - portable_drive.verb_pickup() - -/obj/item/modular_computer/AltClick(mob/user) - ..() - if(issilicon(user)) - return - - if(!user.incapacitated() && Adjacent(user)) - var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - var/obj/item/computer_hardware/ai_slot/ai_slot = all_components[MC_AI] - var/obj/item/computer_hardware/hard_drive/portable/portable_drive = all_components[MC_SDD] - if(portable_drive) - if(uninstall_component(portable_drive, user)) - portable_drive.verb_pickup() - else - if(card_slot && card_slot.try_eject(null, user)) - return - if(ai_slot) - ai_slot.try_eject(null, user) - - -// Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs. -/obj/item/modular_computer/GetAccess() - var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - if(card_slot) - return card_slot.GetAccess() - return ..() - -/obj/item/modular_computer/GetID() - var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD] - if(card_slot) - return card_slot.GetID() - return ..() - -/obj/item/modular_computer/attack_ai(mob/user) - return attack_self(user) - -/obj/item/modular_computer/attack_ghost(mob/dead/observer/user) - if(enabled) - ui_interact(user) - else if(user.can_admin_interact()) - var/response = alert(user, "This computer is turned off. Would you like to turn it on?", "Admin Override", "Yes", "No") - if(response == "Yes") - turn_on(user) - -/obj/item/modular_computer/emag_act(mob/user) - if(emagged) - to_chat(user, "\The [src] was already emagged.") - return 0 - else - emagged = 1 - to_chat(user, "You emag \the [src]. It's screen briefly shows a \"OVERRIDE ACCEPTED: New software downloads available.\" message.") - return 1 - -/obj/item/modular_computer/examine(mob/user) - . = ..() - if(obj_integrity <= integrity_failure) - . += "It is heavily damaged!" - else if(obj_integrity < max_integrity) - . += "It is damaged." - -/obj/item/modular_computer/update_icon() - overlays.Cut() - if(!enabled) - icon_state = icon_state_unpowered - else - icon_state = icon_state_powered - if(active_program && active_program.program_state != PROGRAM_STATE_KILLED) - overlays += active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu - else - overlays += icon_state_menu - - if(obj_integrity <= integrity_failure) - overlays += "bsod" - overlays += "broken" - - -// On-click handling. Turns on the computer if it's off and opens the GUI. -/obj/item/modular_computer/attack_self(mob/user) - if(enabled) - ui_interact(user) - else - turn_on(user) - -/obj/item/modular_computer/proc/turn_on(mob/user) - var/issynth = issilicon(user) // Robots and AIs get different activation messages. - if(obj_integrity <= integrity_failure) - if(issynth) - to_chat(user, "You send an activation signal to \the [src], but it responds with an error code. It must be damaged.") - else - to_chat(user, "You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again.") - return - - // If we have a recharger, enable it automatically. Lets computer without a battery work. - var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE] - if(recharger) - recharger.enabled = 1 - - if(all_components[MC_CPU] && use_power()) // use_power() checks if the PC is powered - if(issynth) - to_chat(user, "You send an activation signal to \the [src], turning it on.") - else - to_chat(user, "You press the power button and start up \the [src].") - enabled = 1 - update_icon() - ui_interact(user) - else // Unpowered - if(issynth) - to_chat(user, "You send an activation signal to \the [src] but it does not respond.") - else - to_chat(user, "You press the power button but \the [src] does not respond.") - -// Process currently calls handle_power(), may be expanded in future if more things are added. -/obj/item/modular_computer/process() - if(!enabled) // The computer is turned off - last_power_usage = 0 - return 0 - - if(obj_integrity <= integrity_failure) - shutdown_computer() - return 0 - - if(active_program && active_program.requires_ntnet && !get_ntnet_status(active_program.requires_ntnet_feature)) - active_program.event_networkfailure(0) // Active program requires NTNet to run but we've just lost connection. Crash. - - if(active_program) - if(active_program.program_state != PROGRAM_STATE_KILLED) - active_program.process_tick() - active_program.ntnet_status = get_ntnet_status() - else - active_program = null - - for(var/I in idle_threads) - var/datum/computer_file/program/P = I - if(P.program_state != PROGRAM_STATE_KILLED) - if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) - P.event_networkfailure(1) - P.process_tick() - P.ntnet_status = get_ntnet_status() - else - idle_threads.Remove(P) - - handle_power() // Handles all computer power interaction - -// Relays kill program request to currently active program. Use this to quit current program. -/obj/item/modular_computer/proc/kill_program(forced = FALSE) - if(active_program && active_program.program_state != PROGRAM_STATE_KILLED) - active_program.kill_program(forced) - active_program = null - var/mob/user = usr - if(user && istype(user)) - ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. - update_icon() - -// Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on) -/obj/item/modular_computer/proc/get_ntnet_status(specific_action = 0) - var/obj/item/computer_hardware/network_card/network_card = all_components[MC_NET] - if(network_card) - return network_card.get_signal(specific_action) - else - return 0 - -/obj/item/modular_computer/proc/add_log(text) - if(!get_ntnet_status()) - return FALSE - var/obj/item/computer_hardware/network_card/network_card = all_components[MC_NET] - return GLOB.ntnet_global.add_log(text, network_card) - -/obj/item/modular_computer/proc/shutdown_computer(loud = 1) - if(enabled) - kill_program(forced = TRUE) - for(var/datum/computer_file/program/P in idle_threads) - P.kill_program(forced = TRUE) - idle_threads.Remove(P) - if(loud) - physical.visible_message("\The [src] shuts down.") - enabled = 0 - update_icon() - - -/obj/item/modular_computer/attackby(obj/item/W, mob/user) - // Insert items into the components - for(var/h in all_components) - var/obj/item/computer_hardware/H = all_components[h] - if(H.try_insert(W, user)) - return - - // Insert new hardware - if(istype(W, /obj/item/computer_hardware)) - if(install_component(W, user)) - return - - if(istype(W, /obj/item/wrench)) - if(all_components.len) - to_chat(user, "Remove all components from \the [src] before disassembling it.") - return - new /obj/item/stack/sheet/metal(get_turf(loc), steel_sheet_cost) - physical.visible_message("\The [src] has been disassembled by [user].") - relay_qdel() - qdel(src) - return - - if(istype(W, /obj/item/screwdriver)) - if(!all_components.len) - to_chat(user, "This device doesn't have any components installed.") - return - var/list/component_names = list() - for(var/h in all_components) - var/obj/item/computer_hardware/H = all_components[h] - component_names.Add(H.name) - - var/choice = input(user, "Which component do you want to uninstall?", "Computer maintenance", null) as null|anything in component_names - - if(!choice) - return - - if(!Adjacent(user)) - return - - var/obj/item/computer_hardware/H = find_hardware_by_name(choice) - - if(!H) - return - - uninstall_component(H, user) - return - - ..() - -/obj/item/modular_computer/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - default_welder_repair(user, I) - -// Used by processor to relay qdel() to machinery type. -/obj/item/modular_computer/proc/relay_qdel() - return - -// Perform adjacency checks on our physical counterpart, if any. -/obj/item/modular_computer/Adjacent(atom/neighbor) - if(physical && physical != src) - return physical.Adjacent(neighbor) - return ..() diff --git a/code/modules/modular_computers/computers/item/computer_components.dm b/code/modules/modular_computers/computers/item/computer_components.dm deleted file mode 100644 index fb6d41d989f..00000000000 --- a/code/modules/modular_computers/computers/item/computer_components.dm +++ /dev/null @@ -1,58 +0,0 @@ -/obj/item/modular_computer/proc/can_install_component(obj/item/computer_hardware/H, mob/living/user = null) - if(!H.can_install(src, user)) - return FALSE - - if(H.w_class > max_hardware_size) - if(user) - to_chat(user, "This component is too large for \the [src]!") - return FALSE - - if(all_components[H.device_type]) - if(user) - to_chat(user, "This computer's hardware slot is already occupied by \the [all_components[H.device_type]].") - return FALSE - return TRUE - - -// Installs component. -/obj/item/modular_computer/proc/install_component(obj/item/computer_hardware/H, mob/living/user = null) - if(!can_install_component(H, user)) - return FALSE - - if(user && !user.unEquip(H)) - return FALSE - H.forceMove(src) - - all_components[H.device_type] = H - - if(user) - to_chat(user, "You install \the [H] into \the [src].") - H.holder = src - H.on_install(src, user) - - -// Uninstalls component. -/obj/item/modular_computer/proc/uninstall_component(obj/item/computer_hardware/H, mob/living/user = null) - if(H.holder != src) // Not our component at all. - return FALSE - - all_components.Remove(H.device_type) - - if(user) - to_chat(user, "You remove \the [H] from \the [src].") - - H.forceMove(get_turf(src)) - H.holder = null - H.on_remove(src, user) - if(enabled && !use_power()) - shutdown_computer() - update_icon() - - -// Checks all hardware pieces to determine if name matches, if yes, returns the hardware piece, otherwise returns null -/obj/item/modular_computer/proc/find_hardware_by_name(name) - for(var/i in all_components) - var/obj/O = all_components[i] - if(O.name == name) - return O - return null diff --git a/code/modules/modular_computers/computers/item/computer_damage.dm b/code/modules/modular_computers/computers/item/computer_damage.dm deleted file mode 100644 index 08a8ca6aaf4..00000000000 --- a/code/modules/modular_computers/computers/item/computer_damage.dm +++ /dev/null @@ -1,30 +0,0 @@ -/obj/item/modular_computer/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) - . = ..() - var/component_probability = min(50, max(damage_amount*0.1, 1 - obj_integrity/max_integrity)) - switch(damage_flag) - if("bullet") - component_probability = damage_amount * 0.5 - if("laser") - component_probability = damage_amount * 0.66 - if(component_probability) - for(var/I in all_components) - var/obj/item/computer_hardware/H = all_components[I] - if(prob(component_probability)) - H.take_damage(round(damage_amount*0.5), damage_type, damage_flag, 0) - -/obj/item/modular_computer/deconstruct(disassembled = TRUE) - break_apart() - -/obj/item/modular_computer/proc/break_apart() - if(!(flags & NODECONSTRUCT)) - physical.visible_message("\The [src] breaks apart!") - var/turf/newloc = get_turf(src) - new /obj/item/stack/sheet/metal(newloc, round(steel_sheet_cost/2)) - for(var/C in all_components) - var/obj/item/computer_hardware/H = all_components[C] - uninstall_component(H) - H.forceMove(newloc) - if(prob(25)) - H.take_damage(rand(10,30), BRUTE, 0, 0) - relay_qdel() - qdel(src) diff --git a/code/modules/modular_computers/computers/item/computer_power.dm b/code/modules/modular_computers/computers/item/computer_power.dm deleted file mode 100644 index 27c81d48190..00000000000 --- a/code/modules/modular_computers/computers/item/computer_power.dm +++ /dev/null @@ -1,77 +0,0 @@ -// Tries to draw power from charger or, if no operational charger is present, from power cell. -/obj/item/modular_computer/proc/use_power(amount = 0) - if(check_power_override()) - return TRUE - - var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE] - - if(recharger && recharger.check_functionality()) - if(recharger.use_power(amount)) - return TRUE - - var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL] - - if(battery_module && battery_module.battery && battery_module.battery.charge) - var/obj/item/stock_parts/cell/cell = battery_module.battery - if(cell.use(amount * GLOB.CELLRATE)) - return TRUE - else // Discharge the cell anyway. - cell.use(min(amount*GLOB.CELLRATE, cell.charge)) - return FALSE - return FALSE - -/obj/item/modular_computer/proc/give_power(amount) - var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL] - if(battery_module && battery_module.battery) - return battery_module.battery.give(amount) - return 0 - -/obj/item/modular_computer/get_cell() - var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL] - if(battery_module && battery_module.battery) - return battery_module.battery - -// Used in following function to reduce copypaste -/obj/item/modular_computer/proc/power_failure() - if(enabled) // Shut down the computer - if(active_program) - active_program.event_powerfailure(0) - for(var/I in idle_threads) - var/datum/computer_file/program/PRG = I - PRG.event_powerfailure(1) - shutdown_computer(0) - -// Handles power-related things, such as battery interaction, recharging, shutdown when it's discharged -/obj/item/modular_computer/proc/handle_power() - var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE] - if(recharger) - recharger.process() - - var/power_usage = screen_on ? base_active_power_usage : base_idle_power_usage - - for(var/obj/item/computer_hardware/H in all_components) - if(H.enabled) - power_usage += H.power_usage - - if(use_power(power_usage)) - last_power_usage = power_usage - return TRUE - else - power_failure() - return FALSE - -/obj/item/modular_computer/proc/get_power_eta() - var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL] - if(battery_module && battery_module.battery) - var/hours = 0 - var/minutes = (battery_module.battery.charge) / (last_power_usage / 20) // 20 = obj processing interval - if(minutes > 60) - hours = minutes/60 - minutes = (hours - round(hours)) * 60 - var/seconds = (minutes - round(minutes)) * 60 - return list("hours" = hours, "minutes" = minutes, "seconds" = seconds) - return list() - -// Used by child types if they have other power source than battery or recharger -/obj/item/modular_computer/proc/check_power_override() - return FALSE diff --git a/code/modules/modular_computers/computers/item/computer_ui.dm b/code/modules/modular_computers/computers/item/computer_ui.dm deleted file mode 100644 index 36df0243f54..00000000000 --- a/code/modules/modular_computers/computers/item/computer_ui.dm +++ /dev/null @@ -1,194 +0,0 @@ -// Operates NanoUI -/obj/item/modular_computer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(!enabled) - if(ui) - ui.close() - return 0 - if(!use_power()) - if(ui) - ui.close() - return 0 - - // Robots don't really need to see the screen, their wireless connection works as long as computer is on. - if(!screen_on && !issilicon(user)) - if(ui) - ui.close() - return 0 - - // If we have an active program switch to it now. - if(active_program) - if(ui) // This is the main laptop screen. Since we are switching to program's UI close it for now. - ui.close() - active_program.ui_interact(user) - return - - // We are still here, that means there is no program loaded. Load the BIOS/ROM/OS/whatever you want to call it. - // This screen simply lists available programs and user may select them. - var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] - if(!hard_drive || !hard_drive.stored_files || !hard_drive.stored_files.len) - to_chat(user, "\The [src] beeps three times, it's screen displaying a \"DISK ERROR\" warning.") - return // No HDD, No HDD files list or no stored files. Something is very broken. - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "computer_main.tmpl", "NTOS Main menu", 400, 500) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - - -/obj/item/modular_computer/ui_data(mob/user) - var/list/data = get_header_data() - data["programs"] = list() - var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] - for(var/datum/computer_file/program/P in hard_drive.stored_files) - var/running = 0 - if(P in idle_threads) - running = 1 - - data["programs"] += list(list("name" = P.filename, "desc" = P.filedesc, "running" = running)) - - return data - -// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_" -/obj/item/modular_computer/proc/get_header_data() - var/list/data = list() - - var/obj/item/computer_hardware/battery/battery_module = all_components[MC_CELL] - var/obj/item/computer_hardware/recharger/recharger = all_components[MC_CHARGE] - - if(battery_module && battery_module.battery) - switch(battery_module.battery.percent()) - if(80 to 200) // 100 should be maximal but just in case.. - data["PC_batteryicon"] = "batt_100.gif" - if(60 to 80) - data["PC_batteryicon"] = "batt_80.gif" - if(40 to 60) - data["PC_batteryicon"] = "batt_60.gif" - if(20 to 40) - data["PC_batteryicon"] = "batt_40.gif" - if(5 to 20) - data["PC_batteryicon"] = "batt_20.gif" - else - data["PC_batteryicon"] = "batt_5.gif" - data["PC_batterypercent"] = "[round(battery_module.battery.percent())] %" - data["PC_showbatteryicon"] = 1 - else - data["PC_batteryicon"] = "batt_5.gif" - data["PC_batterypercent"] = "N/C" - data["PC_showbatteryicon"] = battery_module ? 1 : 0 - - if(recharger && recharger.enabled && recharger.check_functionality() && recharger.use_power(0)) - data["PC_apclinkicon"] = "charging.gif" - - switch(get_ntnet_status()) - if(0) - data["PC_ntneticon"] = "sig_none.gif" - if(1) - data["PC_ntneticon"] = "sig_low.gif" - if(2) - data["PC_ntneticon"] = "sig_high.gif" - if(3) - data["PC_ntneticon"] = "sig_lan.gif" - - if(idle_threads.len) - var/list/program_headers = list() - for(var/I in idle_threads) - var/datum/computer_file/program/P = I - if(!P.ui_header) - continue - program_headers.Add(list(list( - "icon" = P.ui_header - ))) - - data["PC_programheaders"] = program_headers - - data["PC_stationtime"] = station_time_timestamp() - data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen - return data - - -// Handles user's GUI input -/obj/item/modular_computer/Topic(href, list/href_list) - if(..()) - return - var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] - switch(href_list["action"]) - if("PC_exit") - kill_program() - return 1 - if("PC_shutdown") - shutdown_computer() - return 1 - if("PC_minimize") - var/mob/user = usr - if(!active_program || !all_components[MC_CPU]) - return 1 - - idle_threads.Add(active_program) - active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs - - active_program = null - update_icon() - if(user && istype(user)) - ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. - - if("PC_killprogram") - var/prog = href_list["name"] - var/datum/computer_file/program/P = null - var/mob/user = usr - if(hard_drive) - P = hard_drive.find_file_by_name(prog) - - if(!istype(P) || P.program_state == PROGRAM_STATE_KILLED) - return 1 - - P.kill_program(forced = TRUE) - to_chat(user, "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed.") - - if("PC_runprogram") - var/prog = href_list["name"] - var/datum/computer_file/program/P = null - var/mob/user = usr - if(hard_drive) - P = hard_drive.find_file_by_name(prog) - - if(!P || !istype(P)) // Program not found or it's not executable program. - to_chat(user, "\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning.") - return 1 - - P.computer = src - - if(!P.is_supported_by_hardware(hardware_flag, 1, user)) - return 1 - - // The program is already running. Resume it. - if(P in idle_threads) - P.program_state = PROGRAM_STATE_ACTIVE - active_program = P - idle_threads.Remove(P) - update_icon() - return - - var/obj/item/computer_hardware/processor_unit/PU = all_components[MC_CPU] - - if(idle_threads.len > PU.max_idle_programs) - to_chat(user, "\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error.") - return 1 - - if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet. - to_chat(user, "\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning.") - return 1 - if(P.run_program(user)) - active_program = P - update_icon() - return 1 - else - return - -/obj/item/modular_computer/nano_host() - if(physical) - return physical - return src diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm deleted file mode 100644 index 223627bd69e..00000000000 --- a/code/modules/modular_computers/computers/item/laptop.dm +++ /dev/null @@ -1,109 +0,0 @@ -/obj/item/modular_computer/laptop - name = "laptop" - desc = "A portable laptop computer." - - icon = 'icons/obj/modular_laptop.dmi' - icon_state = "laptop-closed" - icon_state_powered = "laptop" - icon_state_unpowered = "laptop-off" - icon_state_menu = "menu" - - hardware_flag = PROGRAM_LAPTOP - max_hardware_size = 2 - w_class = WEIGHT_CLASS_NORMAL - - flags = HANDSLOW // No running around with open laptops in hands. - - screen_on = 0 // Starts closed - var/start_open = 1 // unless this var is set to 1 - var/icon_state_closed = "laptop-closed" - var/w_class_open = WEIGHT_CLASS_BULKY - var/slowdown_open = 1 - -/obj/item/modular_computer/laptop/New() - ..() - if(start_open && !screen_on) - toggle_open() - -/obj/item/modular_computer/laptop/update_icon() - if(screen_on) - ..() - else - overlays.Cut() - icon_state = icon_state_closed - -/obj/item/modular_computer/laptop/attack_self(mob/user) - if(!screen_on) - try_toggle_open(user) - else - return ..() - -/obj/item/modular_computer/laptop/verb/open_computer() - set name = "Toggle Open" - set category = "Object" - set src in view(1) - - try_toggle_open(usr) - -/obj/item/modular_computer/laptop/MouseDrop(obj/over_object, src_location, over_location) - if(over_object == usr || over_object == src) - try_toggle_open(usr) - if(ishuman(usr)) - if(!isturf(loc) || !Adjacent(usr)) - return - if(over_object && !usr.incapacitated()) - if(!usr.canUnEquip(src)) - to_chat(usr, "[src] appears stuck on you!") - return - switch(over_object.name) - if("r_hand") - usr.unEquip(src) - usr.put_in_r_hand(src) - if("l_hand") - usr.unEquip(src) - usr.put_in_l_hand(src) - -/obj/item/modular_computer/laptop/attack_hand(mob/user) - if(screen_on && isturf(loc)) - return attack_self(user) - - return ..() - - -/obj/item/modular_computer/laptop/proc/try_toggle_open(mob/living/user) - if(issilicon(user)) - return - if(!isturf(loc) && !ismob(loc)) // No opening it in backpack. - return - if(user.incapacitated() || !Adjacent(user)) - return - - toggle_open(user) - - -/obj/item/modular_computer/laptop/AltClick(mob/user) - if(screen_on) // Close it. - try_toggle_open(user) - else - return ..() - -/obj/item/modular_computer/laptop/proc/toggle_open(mob/living/user=null) - if(screen_on) - if(user) - to_chat(user, "You close \the [src].") - slowdown = initial(slowdown) - w_class = initial(w_class) - else - if(user) - to_chat(user, "You open \the [src].") - slowdown = slowdown_open - w_class = w_class_open - - screen_on = !screen_on - update_icon() - - - -// Laptop frame, starts empty and closed. -/obj/item/modular_computer/laptop/buildable - start_open = 0 diff --git a/code/modules/modular_computers/computers/item/laptop_presets.dm b/code/modules/modular_computers/computers/item/laptop_presets.dm deleted file mode 100644 index 43720dfa2fc..00000000000 --- a/code/modules/modular_computers/computers/item/laptop_presets.dm +++ /dev/null @@ -1,21 +0,0 @@ -/obj/item/modular_computer/laptop/preset/New() - . = ..() - install_component(new /obj/item/computer_hardware/processor_unit/small) - install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer)) - install_component(new /obj/item/computer_hardware/hard_drive) - install_component(new /obj/item/computer_hardware/network_card) - install_programs() - - -/obj/item/modular_computer/laptop/preset/proc/install_programs() - return - - -/obj/item/modular_computer/laptop/preset/civillian - desc = "A low-end laptop often used for personal recreation." - - -/obj/item/modular_computer/laptop/preset/civillian/install_programs() - var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] - hard_drive.store_file(new/datum/computer_file/program/chatclient()) - hard_drive.store_file(new/datum/computer_file/program/nttransfer()) diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm deleted file mode 100644 index 3095e0af876..00000000000 --- a/code/modules/modular_computers/computers/item/processor.dm +++ /dev/null @@ -1,73 +0,0 @@ -// Held by /obj/machinery/modular_computer to reduce amount of copy-pasted code. -/obj/item/modular_computer/processor - name = "processing unit" - desc = "You shouldn't see this. If you do, report it." - icon = null - icon_state = null - icon_state_unpowered = null - icon_state_menu = null - hardware_flag = 0 - - var/obj/machinery/modular_computer/machinery_computer = null - -/obj/item/modular_computer/processor/Destroy() - . = ..() - if(machinery_computer && (machinery_computer.cpu == src)) - machinery_computer.cpu = null - machinery_computer = null - -/obj/item/modular_computer/processor/New(comp) - if(!comp || !istype(comp, /obj/machinery/modular_computer)) - CRASH("Inapropriate type passed to obj/item/modular_computer/processor/New()! Aborting.") - // Obtain reference to machinery computer - all_components = list() - idle_threads = list() - machinery_computer = comp - machinery_computer.cpu = src - hardware_flag = machinery_computer.hardware_flag - max_hardware_size = machinery_computer.max_hardware_size - steel_sheet_cost = machinery_computer.steel_sheet_cost - obj_integrity = machinery_computer.obj_integrity - max_integrity = machinery_computer.max_integrity - integrity_failure = machinery_computer.integrity_failure - base_active_power_usage = machinery_computer.base_active_power_usage - base_idle_power_usage = machinery_computer.base_idle_power_usage - -/obj/item/modular_computer/processor/relay_qdel() - qdel(machinery_computer) - -/obj/item/modular_computer/processor/update_icon() - if(machinery_computer) - return machinery_computer.update_icon() - -// This thing is not meant to be used on it's own, get topic data from our machinery owner. -//obj/item/modular_computer/processor/canUseTopic(user, state) -// if(!machinery_computer) -// return 0 - -// return machinery_computer.canUseTopic(user, state) - -/obj/item/modular_computer/processor/shutdown_computer() - if(!machinery_computer) - return - ..() - machinery_computer.update_icon() - return - -/obj/item/modular_computer/processor/add_verb(path) - switch(path) - if(MC_CARD) - machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_id - if(MC_SDD) - machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_disk - if(MC_AI) - machinery_computer.verbs += /obj/machinery/modular_computer/proc/eject_card - -/obj/item/modular_computer/processor/remove_verb(path) - switch(path) - if(MC_CARD) - machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_id - if(MC_SDD) - machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_disk - if(MC_AI) - machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_card diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm deleted file mode 100644 index f0a044b1d36..00000000000 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ /dev/null @@ -1,12 +0,0 @@ -/obj/item/modular_computer/tablet //Its called tablet for theme of 90ies but actually its a "big smartphone" sized - name = "tablet computer" - icon = 'icons/obj/modular_tablet.dmi' - icon_state = "tablet" - icon_state_unpowered = "tablet" - icon_state_powered = "tablet" - icon_state_menu = "menu" - hardware_flag = PROGRAM_TABLET - max_hardware_size = 1 - w_class = WEIGHT_CLASS_SMALL - steel_sheet_cost = 1 - slot_flags = SLOT_ID | SLOT_BELT diff --git a/code/modules/modular_computers/computers/item/tablet_presets.dm b/code/modules/modular_computers/computers/item/tablet_presets.dm deleted file mode 100644 index 3ac32e30ca9..00000000000 --- a/code/modules/modular_computers/computers/item/tablet_presets.dm +++ /dev/null @@ -1,29 +0,0 @@ - -// This is literally the worst possible cheap tablet -/obj/item/modular_computer/tablet/preset/cheap - desc = "A low-end tablet often seen among low ranked station personnel." - -/obj/item/modular_computer/tablet/preset/cheap/New() - . = ..() - install_component(new /obj/item/computer_hardware/processor_unit/small) - install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer/micro)) - install_component(new /obj/item/computer_hardware/hard_drive/small) - install_component(new /obj/item/computer_hardware/network_card) - -// Alternative version, an average one, for higher ranked positions mostly -/obj/item/modular_computer/tablet/preset/advanced/New() - . = ..() - install_component(new /obj/item/computer_hardware/processor_unit/small) - install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer)) - install_component(new /obj/item/computer_hardware/hard_drive/small) - install_component(new /obj/item/computer_hardware/network_card) - install_component(new /obj/item/computer_hardware/card_slot) - install_component(new /obj/item/computer_hardware/printer/mini) - -/obj/item/modular_computer/tablet/preset/cargo/New() - . = ..() - install_component(new /obj/item/computer_hardware/processor_unit/small) - install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer)) - install_component(new /obj/item/computer_hardware/hard_drive/small) - install_component(new /obj/item/computer_hardware/network_card) - install_component(new /obj/item/computer_hardware/printer/mini) diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm deleted file mode 100644 index 4f1bd269337..00000000000 --- a/code/modules/modular_computers/computers/machinery/console_presets.dm +++ /dev/null @@ -1,76 +0,0 @@ -/obj/machinery/modular_computer/console/preset - // Can be changed to give devices specific hardware - var/_has_id_slot = 0 - var/_has_printer = 0 - var/_has_battery = 0 - var/_has_ai = 0 - -/obj/machinery/modular_computer/console/preset/New() - . = ..() - if(!cpu) - return - cpu.install_component(new /obj/item/computer_hardware/processor_unit) - - if(_has_id_slot) - cpu.install_component(new /obj/item/computer_hardware/card_slot) - if(_has_printer) - cpu.install_component(new /obj/item/computer_hardware/printer) - if(_has_battery) - cpu.install_component(new /obj/item/computer_hardware/battery(cpu, /obj/item/stock_parts/cell/computer/super)) - if(_has_ai) - cpu.install_component(new /obj/item/computer_hardware/ai_slot) - install_programs() - -// Override in child types to install preset-specific programs. -/obj/machinery/modular_computer/console/preset/proc/install_programs() - return - - - -// ===== ENGINEERING CONSOLE ===== -/obj/machinery/modular_computer/console/preset/engineering - console_department = "Engineering" - desc = "A stationary computer. This one comes preloaded with engineering programs." - -/obj/machinery/modular_computer/console/preset/engineering/install_programs() - var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] - hard_drive.store_file(new/datum/computer_file/program/power_monitor()) - hard_drive.store_file(new/datum/computer_file/program/alarm_monitor()) - hard_drive.store_file(new/datum/computer_file/program/supermatter_monitor()) - -// ===== RESEARCH CONSOLE ===== -/obj/machinery/modular_computer/console/preset/research - console_department = "Research" - desc = "A stationary computer. This one comes preloaded with research programs." - _has_ai = 1 - -/obj/machinery/modular_computer/console/preset/research/install_programs() - var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] - hard_drive.store_file(new/datum/computer_file/program/ntnetmonitor()) - hard_drive.store_file(new/datum/computer_file/program/nttransfer()) - hard_drive.store_file(new/datum/computer_file/program/chatclient()) - hard_drive.store_file(new/datum/computer_file/program/aidiag()) - - -// ===== COMMAND CONSOLE ===== -/obj/machinery/modular_computer/console/preset/command - console_department = "Command" - desc = "A stationary computer. This one comes preloaded with command programs." - _has_id_slot = 1 - _has_printer = 1 - -/obj/machinery/modular_computer/console/preset/command/install_programs() - var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] - hard_drive.store_file(new/datum/computer_file/program/chatclient()) - hard_drive.store_file(new/datum/computer_file/program/card_mod()) - hard_drive.store_file(new/datum/computer_file/program/comm()) - -// ===== CIVILIAN CONSOLE ===== -/obj/machinery/modular_computer/console/preset/civilian - console_department = "Civilian" - desc = "A stationary computer. This one comes preloaded with generic programs." - -/obj/machinery/modular_computer/console/preset/civilian/install_programs() - var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] - hard_drive.store_file(new/datum/computer_file/program/chatclient()) - hard_drive.store_file(new/datum/computer_file/program/nttransfer()) diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm deleted file mode 100644 index 391396a7d75..00000000000 --- a/code/modules/modular_computers/computers/machinery/modular_computer.dm +++ /dev/null @@ -1,154 +0,0 @@ -// Global var to track modular computers -GLOBAL_LIST_EMPTY(global_modular_computers) - -// Modular Computer - device that runs various programs and operates with hardware -// DO NOT SPAWN THIS TYPE. Use /laptop/ or /console/ instead. -/obj/machinery/modular_computer - name = "modular computer" - desc = "An advanced computer." - - use_power = IDLE_POWER_USE - idle_power_usage = 5 - var/hardware_flag = 0 // A flag that describes this device type - var/last_power_usage = 0 // Power usage during last tick - - // Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..) - // must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently - // If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example. - - icon = null - icon_state = null - var/icon_state_unpowered = null // Icon state when the computer is turned off. - var/icon_state_powered = null // Icon state when the computer is turned on. - var/screen_icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen. - var/screen_icon_screensaver = "standby" // Icon state overlay when the computer is powered, but not 'switched on'. - var/max_hardware_size = 0 // Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed. - var/steel_sheet_cost = 10 // Amount of steel sheets refunded when disassembling an empty frame of this computer. - var/light_strength = 0 // Light luminosity when turned on - var/base_active_power_usage = 100 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too. - var/base_idle_power_usage = 10 // Power usage when the computer is idle and screen is off (currently only applies to laptops) - - var/obj/item/modular_computer/processor/cpu = null // CPU that handles most logic while this type only handles power and other specific things. - -/obj/machinery/modular_computer/New() - ..() - cpu = new(src) - cpu.physical = src - GLOB.global_modular_computers.Add(src) - -/obj/machinery/modular_computer/Destroy() - QDEL_NULL(cpu) - return ..() - -/obj/machinery/modular_computer/attack_ghost(mob/dead/observer/user) - if(cpu) - cpu.attack_ghost(user) - -/obj/machinery/modular_computer/emag_act(mob/user) - return cpu ? cpu.emag_act(user) : 1 - -/obj/machinery/modular_computer/update_icon() - overlays.Cut() - icon_state = icon_state_powered - - if(!cpu || !cpu.enabled) - if (!(stat & NOPOWER) && (cpu && cpu.use_power())) - overlays += screen_icon_screensaver - else - icon_state = icon_state_unpowered - set_light(0) - else - set_light(light_strength) - if(cpu.active_program) - overlays += cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu - else - overlays += screen_icon_state_menu - - if(cpu && cpu.obj_integrity <= cpu.integrity_failure) - overlays += "bsod" - overlays += "broken" - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/machinery/modular_computer/proc/eject_id() - set name = "Eject ID" - set category = "Object" - - if(cpu) - cpu.eject_id() - -// Eject ID card from computer, if it has ID slot with card inside. -/obj/machinery/modular_computer/proc/eject_disk() - set name = "Eject Data Disk" - set category = "Object" - - if(cpu) - cpu.eject_disk() - -/obj/machinery/modular_computer/proc/eject_card() - set name = "Eject Intellicard" - set category = "Object" - set src in view(1) - - if(cpu) - cpu.eject_card() - -/obj/machinery/modular_computer/AltClick(mob/user) - if(cpu) - cpu.AltClick(user) - -// On-click handling. Turns on the computer if it's off and opens the GUI. -/obj/machinery/modular_computer/attack_hand(mob/user) - if(cpu) - cpu.attack_self(user) // CPU is an item, that's why we route attack_hand to attack_self - -// Process currently calls handle_power(), may be expanded in future if more things are added. -/obj/machinery/modular_computer/process() - if(cpu) - // Keep names in sync. - cpu.name = src.name - cpu.process() - -// Used in following function to reduce copypaste -/obj/machinery/modular_computer/proc/power_failure(malfunction = 0) - var/obj/item/computer_hardware/battery/battery_module = cpu.all_components[MC_CELL] - if(cpu && cpu.enabled) // Shut down the computer - visible_message("\The [src]'s screen flickers [battery_module ? "\"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly.") - if(cpu) - cpu.shutdown_computer(0) - stat |= NOPOWER - update_icon() - - -// Modular computers can have battery in them, we handle power in previous proc, so prevent this from messing it up for us. -/obj/machinery/modular_computer/power_change() - if(cpu && cpu.use_power()) // If MC_CPU still has a power source, PC wouldn't go offline. - stat &= ~NOPOWER - update_icon() - return - ..() - update_icon() - -/obj/machinery/modular_computer/attackby(obj/item/W, mob/user) - if(cpu) - return cpu.attackby(W, user) - return ..() - - -// Stronger explosions cause serious damage to internal components -// Minor explosions are mostly mitigitated by casing. -/obj/machinery/modular_computer/ex_act(severity) - if(cpu) - cpu.ex_act(severity) - ..() - -// EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components -/obj/machinery/modular_computer/emp_act(severity) - if(cpu) - cpu.emp_act(severity) - -// "Stun" weapons can cause minor damage to components (short-circuits?) -// "Burn" damage is equally strong against internal components and exterior casing -// "Brute" damage mostly damages the casing. -/obj/machinery/modular_computer/bullet_act(obj/item/projectile/Proj) - if(cpu) - cpu.bullet_act(Proj) diff --git a/code/modules/modular_computers/computers/machinery/modular_console.dm b/code/modules/modular_computers/computers/machinery/modular_console.dm deleted file mode 100644 index e16ce82702c..00000000000 --- a/code/modules/modular_computers/computers/machinery/modular_console.dm +++ /dev/null @@ -1,53 +0,0 @@ -/obj/machinery/modular_computer/console - name = "console" - desc = "A stationary computer." - - icon = 'icons/obj/modular_console.dmi' - icon_state = "console" - icon_state_powered = "console" - icon_state_unpowered = "console-off" - screen_icon_state_menu = "menu" - hardware_flag = PROGRAM_CONSOLE - anchored = 1 - density = 1 - base_idle_power_usage = 100 - base_active_power_usage = 500 - max_hardware_size = 4 - steel_sheet_cost = 10 - light_strength = 2 - max_integrity = 300 - integrity_failure = 150 - - var/console_department = "" // Used in New() to set network tag according to our area. - var/components = TRUE // Install basic components? - -/obj/machinery/modular_computer/console/buildable - components = FALSE - -/obj/machinery/modular_computer/console/New() - ..() - var/obj/item/computer_hardware/battery/battery_module = cpu.all_components[MC_CELL] - if(battery_module) - qdel(battery_module) - - if(components) - var/obj/item/computer_hardware/network_card/wired/network_card = new() - - cpu.install_component(network_card) - cpu.install_component(new /obj/item/computer_hardware/recharger/APC) - cpu.install_component(new /obj/item/computer_hardware/hard_drive/super) // Consoles generally have better HDDs due to lower space limitations - - var/area/A = get_area(src) - // Attempts to set this console's tag according to our area. Since some areas have stuff like "XX - YY" in their names we try to remove that too. - if(A && console_department) - network_card.identification_string = replacetext(replacetext(replacetext("[A.name] [console_department] Console", " ", "_"), "-", ""), "__", "_") // Replace spaces with "_" - else if(A) - network_card.identification_string = replacetext(replacetext(replacetext("[A.name] Console", " ", "_"), "-", ""), "__", "_") - else if(console_department) - network_card.identification_string = replacetext(replacetext(replacetext("[console_department] Console", " ", "_"), "-", ""), "__", "_") - else - network_card.identification_string = "Unknown Console" - - if(cpu) - cpu.screen_on = 1 - update_icon() diff --git a/code/modules/modular_computers/documentation.md b/code/modules/modular_computers/documentation.md deleted file mode 100644 index 9caae69aade..00000000000 --- a/code/modules/modular_computers/documentation.md +++ /dev/null @@ -1,45 +0,0 @@ - - -#Modular computer programs -Ok. so a quick rundown on how to make a program. This is kind of a shitty documentation, but oh well I was asked to. - - -## Base setup -This is how the base program is setup. the rest is mostly tgui stuff. I'll use the ntnetmonitor as a base - - -```DM -/datum/computer_file/program/ntnetmonitor - filename = "ntmonitor" //This is obviously the name of the file itself. not much to be said - filedesc = "NTNet Diagnostics and Monitoring" // This is sort of the official name. it's what shows up on the main menu - program_icon_state = "comm_monitor" // This is what the screen will look like when the program is active - extended_desc = "This program is a dummy. // This is a sort of a description, visible when looking on the ntnet - size = 12 // size of the program. Big programs need more hard drive space. Don't make it too big though. - requires_ntnet = 1 // If this is set, the program will not run without an ntnet connection, and will close if the connection is lost. Mainly for primarily online programs. - required_access = ACCESS_NETWORK //This is access required to run the program itself. ONLY SET THIS FOR SUPER SECURE SHIT. This also acts as transfer_access as well. - transfer_access = ACCESS_CHANGE_IDS // This is the access needed to download from ntnet or host on the ptp program. This is what you want to use most of the time. - available_on_ntnet = 1 //If it's available to download on ntnet. pretty self explanatory. - available_on_syndinet = 0 // ditto but on emagged syndie net. Use this for antag programs - usage_flags = PROGRAM_ALL // Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL - //^^- The comment above sorta explains it. Use this to limit what kind of machines can run the program. For example, comms program should be limited to consoles and laptops. - var/ui_header = "downloader_finished.gif" //This one is kinda cool. If you have the program minimized, this will show up in the header of the computer screen. - //you can even have the program change what the header is based on the situation! see alarm.dm for an example. -``` - -##Preinstalls -Now. for pre-installing stuff. - -Primarily done for consoles, there's an install_programs() proc in the console presets file in the machines folder. - -for example, the command console one. - -```DM -/obj/machinery/modular_computer/console/preset/command/install_programs() - cpu.hard_drive.store_file(new/datum/computer_file/program/chatclient()) - cpu.hard_drive.store_file(new/datum/computer_file/program/card_mod()) -``` -Basically, you want to do cpu.hard_drive.store_file(new/*program path here*()) and put it in the subtype's install_programs(). -Probably pretty self explanatory, but just in case. - -Will probably be expanded when new features come around or I get asked to mention something. - diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm deleted file mode 100644 index b3f8064b72a..00000000000 --- a/code/modules/modular_computers/file_system/computer_file.dm +++ /dev/null @@ -1,50 +0,0 @@ -GLOBAL_VAR_INIT(file_uid, 0) - -/datum/computer_file - var/filename = "NewFile" // Placeholder. No spacebars - var/filetype = "XXX" // File full names are [filename].[filetype] so like NewFile.XXX in this case - var/size = 1 // File size in GQ. Integers only! - var/obj/item/computer_hardware/hard_drive/holder // Holder that contains this file. - var/unsendable = 0 // Whether the file may be sent to someone via NTNet transfer or other means. - var/undeletable = 0 // Whether the file may be deleted. Setting to 1 prevents deletion/renaming/etc. - var/uid // UID of this file - var/password = "" // Placeholder for password. - -/datum/computer_file/New() - ..() - uid = GLOB.file_uid - GLOB.file_uid++ - -/datum/computer_file/Destroy() - if(!holder) - return ..() - - holder.remove_file(src) - // holder.holder is the computer that has drive installed. If we are Destroy()ing program that's currently running kill it. - if(holder.holder && holder.holder.active_program == src) - holder.holder.kill_program(forced = TRUE) - holder = null - return ..() - -// Returns independent copy of this file. -/datum/computer_file/proc/clone(rename = 0) - var/datum/computer_file/temp = new type - temp.unsendable = unsendable - temp.undeletable = undeletable - temp.size = size - if(rename) - temp.filename = filename + "(Copy)" - else - temp.filename = filename - temp.filetype = filetype - temp.password = password - return temp - -/datum/computer_file/proc/can_access_file(mob/user, input_password = "") - if(!password) - return TRUE - if(!input_password) - input_password = sanitize(input(user, "Please enter a password to access file '[filename]':")) - if(input_password == password) - return TRUE - return FALSE diff --git a/code/modules/modular_computers/file_system/data.dm b/code/modules/modular_computers/file_system/data.dm deleted file mode 100644 index 32ef6f53dd1..00000000000 --- a/code/modules/modular_computers/file_system/data.dm +++ /dev/null @@ -1,20 +0,0 @@ -// /data/ files store data in string format. -// They don't contain other logic for now. -/datum/computer_file/data - var/stored_data = "" // Stored data in string format. - filetype = "DAT" - var/block_size = 250 - var/do_not_edit = 0 // Whether the user will be reminded that the file probably shouldn't be edited. - -/datum/computer_file/data/clone() - var/datum/computer_file/data/temp = ..() - temp.stored_data = stored_data - return temp - -// Calculates file size from amount of characters in saved string -/datum/computer_file/data/proc/calculate_size() - size = max(1, round(length(stored_data) / block_size)) - -/datum/computer_file/data/logfile - filetype = "LOG" - diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm deleted file mode 100644 index 6f779312c15..00000000000 --- a/code/modules/modular_computers/file_system/program.dm +++ /dev/null @@ -1,200 +0,0 @@ -// /program/ files are executable programs that do things. -/datum/computer_file/program - filetype = "PRG" - filename = "UnknownProgram" // File name. FILE NAME MUST BE UNIQUE IF YOU WANT THE PROGRAM TO BE DOWNLOADABLE FROM NTNET! - var/required_access = null // List of required accesses to *run* the program. - var/transfer_access = null // List of required access to download or file host the program - var/program_state = PROGRAM_STATE_KILLED// PROGRAM_STATE_KILLED or PROGRAM_STATE_BACKGROUND or PROGRAM_STATE_ACTIVE - specifies whether this program is running. - var/obj/item/modular_computer/computer // Device that runs this program. - var/filedesc = "Unknown Program" // User-friendly name of this program. - var/extended_desc = "N/A" // Short description of this program's function. - var/program_icon_state = null // Program-specific screen icon state - var/requires_ntnet = 0 // Set to 1 for program to require nonstop NTNet connection to run. If NTNet connection is lost program crashes. - var/requires_ntnet_feature = 0 // Optional, if above is set to 1 checks for specific function of NTNet (currently NTNET_SOFTWAREDOWNLOAD, NTNET_PEERTOPEER, NTNET_SYSTEMCONTROL and NTNET_COMMUNICATION) - var/ntnet_status = 1 // NTNet status, updated every tick by computer running this program. Don't use this for checks if NTNet works, computers do that. Use this for calculations, etc. - var/usage_flags = PROGRAM_ALL // Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL - var/network_destination = null // Optional string that describes what NTNet server/system this program connects to. Used in default logging. - var/available_on_ntnet = 1 // Whether the program can be downloaded from NTNet. Set to 0 to disable. - var/available_on_syndinet = 0 // Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable. - var/ui_header = null // Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /icons/program_icons. Be careful not to use too large images! - -/datum/computer_file/program/New(obj/item/modular_computer/comp = null) - ..() - if(comp && istype(comp)) - computer = comp - -/datum/computer_file/program/Destroy() - computer = null - . = ..() - -/datum/computer_file/program/clone() - var/datum/computer_file/program/temp = ..() - temp.required_access = required_access - temp.filedesc = filedesc - temp.program_icon_state = program_icon_state - temp.requires_ntnet = requires_ntnet - temp.requires_ntnet_feature = requires_ntnet_feature - temp.usage_flags = usage_flags - return temp - -// Relays icon update to the computer. -/datum/computer_file/program/proc/update_computer_icon() - if(computer) - computer.update_icon() - -// Attempts to create a log in global ntnet datum. Returns 1 on success, 0 on fail. -/datum/computer_file/program/proc/generate_network_log(text) - if(computer) - return computer.add_log(text) - return 0 - -/datum/computer_file/program/proc/is_supported_by_hardware(hardware_flag = 0, loud = 0, mob/user = null) - if(!(hardware_flag & usage_flags)) - if(loud && computer && user) - to_chat(user, "\The [computer] flashes an \"Hardware Error - Incompatible software\" warning.") - return 0 - return 1 - -/datum/computer_file/program/proc/get_signal(specific_action = 0) - if(computer) - return computer.get_ntnet_status(specific_action) - return 0 - -// Called by Process() on device that runs us, once every tick. -/datum/computer_file/program/proc/process_tick() - return 1 - -// Check if the user can run program. Only humans can operate computer. Automatically called in run_program() -// User has to wear their ID for ID Scan to work. -// Can also be called manually, with optional parameter being access_to_check to scan the user's ID -/datum/computer_file/program/proc/can_run(mob/user, loud = 0, access_to_check, transfer = 0) - // Defaults to required_access - if(!access_to_check) - if(transfer && transfer_access) - access_to_check = transfer_access - else - access_to_check = required_access - if(!access_to_check) // No required_access, allow it. - return 1 - - if(!transfer && computer && computer.emagged) //emags can bypass the execution locks but not the download ones. - return 1 - - if(user.can_admin_interact()) - return 1 - - if(issilicon(user)) - return 1 - - if(ishuman(user)) - var/obj/item/card/id/D - var/obj/item/computer_hardware/card_slot/card_slot - if(computer && card_slot) - card_slot = computer.all_components[MC_CARD] - D = card_slot.GetID() - var/mob/living/carbon/human/h = user - var/obj/item/card/id/I = h.get_idcard() - var/obj/item/card/id/C = h.get_active_hand() - if(C) - C = C.GetID() - if(!(C && istype(C))) - C = null - - if(!I && !C && !D) - if(loud) - to_chat(user, "\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning.") - return 0 - - if(I) - if(access_to_check in I.GetAccess()) - return 1 - else if(C) - if(access_to_check in C.GetAccess()) - return 1 - else if(D) - if(access_to_check in D.GetAccess()) - return 1 - if(loud) - to_chat(user, "\The [computer] flashes an \"Access Denied\" warning.") - return 0 - -// This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones -// always include the device here in this proc. This proc basically relays the request to whatever is running the program. -/datum/computer_file/program/proc/get_header_data() - if(computer) - return computer.get_header_data() - return list() - -// This is performed on program startup. May be overriden to add extra logic. Remember to include ..() call. Return 1 on success, 0 on failure. -// When implementing new program based device, use this to run the program. -/datum/computer_file/program/proc/run_program(mob/living/user) - if(can_run(user, 1)) - if(requires_ntnet && network_destination) - generate_network_log("Connection opened to [network_destination].") - program_state = PROGRAM_STATE_ACTIVE - return 1 - return 0 - -// Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client. -/datum/computer_file/program/proc/kill_program(forced = FALSE) - program_state = PROGRAM_STATE_KILLED - computer.update_icon() - if(network_destination) - generate_network_log("Connection to [network_destination] closed.") - return 1 - -// This is called every tick when the program is enabled. Ensure you do parent call if you override it. If parent returns 1 continue with UI initialisation. - -/datum/computer_file/program/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(program_state != PROGRAM_STATE_ACTIVE) // Our program was closed. Close the ui if it exists. - return computer.ui_interact(user) - return 1 - - -// CONVENTIONS, READ THIS WHEN CREATING NEW PROGRAM AND OVERRIDING THIS PROC: -// Topic calls are automagically forwarded from NanoModule this program contains. -// Calls beginning with "PRG_" are reserved for programs handling. -// Calls beginning with "PC_" are reserved for computer handling (by whatever runs the program) -// ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE. -/datum/computer_file/program/Topic(href, list/href_list) - if(..()) - return 1 - - var/datum/nanoui/ui = SSnanoui.get_open_ui(usr, src, "main") - - if(computer) - switch(href_list["action"]) - if("PC_exit") - computer.kill_program() - ui.close() - return 1 - if("PC_shutdown") - computer.shutdown_computer() - ui.close() - return 1 - if("PC_minimize") - var/mob/user = usr - if(!computer.active_program || !computer.all_components[MC_CPU]) - return - - computer.idle_threads.Add(computer.active_program) - program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs - - computer.active_program = null - computer.update_icon() - ui.close() - - if(user && istype(user)) - computer.ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. - - -/datum/computer_file/program/nano_host() - if(computer.physical) - return computer.physical - else - return computer - -/datum/computer_file/program/CanUseTopic(mob/user) - if(program_state != PROGRAM_STATE_ACTIVE) // Our program was closed. Close the ui if it exists. - return STATUS_CLOSE - return ..() diff --git a/code/modules/modular_computers/file_system/program_events.dm b/code/modules/modular_computers/file_system/program_events.dm deleted file mode 100644 index 279d646cfd7..00000000000 --- a/code/modules/modular_computers/file_system/program_events.dm +++ /dev/null @@ -1,18 +0,0 @@ -// Events are sent to the program by the computer. -// Always include a parent call when overriding an event. - -// Called when the ID card is removed from computer. ID is removed AFTER this proc. -/datum/computer_file/program/proc/event_idremoved(background, slot) - return - -// Called when the computer fails due to power loss. Override when program wants to specifically react to power loss. -/datum/computer_file/program/proc/event_powerfailure(background) - kill_program(forced = TRUE) - -// Called when the network connectivity fails. Computer does necessary checks and only calls this when requires_ntnet_feature and similar variables are not met. -/datum/computer_file/program/proc/event_networkfailure(background) - kill_program(forced = TRUE) - if(background) - computer.visible_message("\The [computer]'s screen displays an \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error") - else - computer.visible_message("\The [computer]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error.") diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm deleted file mode 100644 index f6bc293db7d..00000000000 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ /dev/null @@ -1,103 +0,0 @@ -/datum/computer_file/program/ntnet_dos - filename = "ntn_dos" - filedesc = "DoS Traffic Generator" - program_icon_state = "hostile" - extended_desc = "This advanced script can perform denial of service attacks against NTNet quantum relays. The system administrator will probably notice this. Multiple devices can run this program together against same relay for increased effect" - size = 20 - requires_ntnet = 1 - available_on_ntnet = 0 - available_on_syndinet = 1 - var/obj/machinery/ntnet_relay/target = null - var/dos_speed = 0 - var/error = "" - var/executed = 0 - -/datum/computer_file/program/ntnet_dos/process_tick() - dos_speed = 0 - switch(ntnet_status) - if(1) - dos_speed = NTNETSPEED_LOWSIGNAL * 10 - if(2) - dos_speed = NTNETSPEED_HIGHSIGNAL * 10 - if(3) - dos_speed = NTNETSPEED_ETHERNET * 10 - if(target && executed) - target.dos_overload += dos_speed - if(target.inoperable()) - target.dos_sources.Remove(src) - target = null - error = "Connection to destination relay lost." - -/datum/computer_file/program/ntnet_dos/kill_program(forced = FALSE) - if(target) - target.dos_sources.Remove(src) - target = null - executed = 0 - ..() - -/datum/computer_file/program/ntnet_dos/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "ntnet_dos.tmpl", "DoS Traffic Generator", 575, 250) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - - -/datum/computer_file/program/ntnet_dos/Topic(href, list/href_list) - if(..()) - return 1 - switch(href_list["action"]) - if("PRG_target_relay") - for(var/obj/machinery/ntnet_relay/R in GLOB.ntnet_global.relays) - if("[R.uid]" == href_list["targid"]) - target = R - return 1 - if("PRG_reset") - if(target) - target.dos_sources.Remove(src) - target = null - executed = 0 - error = "" - return 1 - if("PRG_execute") - if(target) - executed = 1 - target.dos_sources.Add(src) - if(GLOB.ntnet_global.intrusion_detection_enabled) - var/obj/item/computer_hardware/network_card/network_card = computer.all_components[MC_NET] - GLOB.ntnet_global.add_log("IDS WARNING - Excess traffic flood targeting relay [target.uid] detected from device: [network_card.get_network_tag()]") - GLOB.ntnet_global.intrusion_detection_alarm = 1 - return 1 - -/datum/computer_file/program/ntnet_dos/ui_data(mob/user) - if(!GLOB.ntnet_global) - return - - var/list/data = get_header_data() - - if(error) - data["error"] = error - else if(target && executed) - data["target"] = 1 - data["speed"] = dos_speed - - // This is mostly visual, generate some strings of 1s and 0s - // Probability of 1 is equal of completion percentage of DoS attack on this relay. - // Combined with UI updates this adds quite nice effect to the UI - var/percentage = target.dos_overload * 100 / target.dos_capacity - data["dos_strings"] = list() - for(var/j, j<10, j++) - var/string = "" - for(var/i, i<20, i++) - string = "[string][prob(percentage)]" - data["dos_strings"] += list(list("nums" = string)) - else - data["relays"] = list() - for(var/obj/machinery/ntnet_relay/R in GLOB.ntnet_global.relays) - data["relays"] += list(list("id" = R.uid)) - data["focus"] = target ? target.uid : null - - return data diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm deleted file mode 100644 index 42f722af799..00000000000 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ /dev/null @@ -1,74 +0,0 @@ -/datum/computer_file/program/revelation - filename = "revelation" - filedesc = "Revelation" - program_icon_state = "hostile" - extended_desc = "This virus can destroy hard drive of system it is executed on. It may be obfuscated to look like another non-malicious program. Once armed, it will destroy the system upon next execution." - size = 13 - requires_ntnet = 0 - available_on_ntnet = 0 - available_on_syndinet = 1 - var/armed = 0 - -/datum/computer_file/program/revelation/run_program(var/mob/living/user) - . = ..(user) - if(armed) - activate() - -/datum/computer_file/program/revelation/proc/activate() - if(computer) - computer.visible_message("\The [computer]'s screen brightly flashes and loud electrical buzzing is heard.") - computer.enabled = 0 - computer.update_icon() - var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - var/obj/item/computer_hardware/battery/battery_module = computer.all_components[MC_CELL] - var/obj/item/computer_hardware/recharger/recharger = computer.all_components[MC_CHARGE] - qdel(hard_drive) - computer.take_damage(25, BRUTE, 0, 0) - if(battery_module && prob(25)) - qdel(battery_module) - computer.visible_message("\The [computer]'s battery explodes in rain of sparks.") - do_sparks(5, 0, computer.loc) - - if(recharger && prob(50)) - qdel(recharger) - computer.visible_message("\The [computer]'s recharger explodes in rain of sparks.") - do_sparks(5, 0, computer.loc) - - -/datum/computer_file/program/revelation/Topic(href, list/href_list) - if(..()) - return 1 - switch(href_list["action"]) - if("PRG_arm") - armed = !armed - if("PRG_activate") - activate() - if("PRG_obfuscate") - var/mob/living/user = usr - var/newname = sanitize(input(user, "Enter new program name: ")) - if(!newname) - return - filedesc = newname - - -/datum/computer_file/program/revelation/clone() - var/datum/computer_file/program/revelation/temp = ..() - temp.armed = armed - return temp - -/datum/computer_file/program/revelation/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "revelation.tmpl", "Revelation Virus", 575, 250) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/revelation/ui_data(mob/user) - var/list/data = get_header_data() - - data["armed"] = armed - - return data diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm deleted file mode 100644 index b85fb710e38..00000000000 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ /dev/null @@ -1,485 +0,0 @@ -/datum/computer_file/program/card_mod - filename = "cardmod" - filedesc = "ID card modification program" - program_icon_state = "id" - extended_desc = "Program for programming employee ID cards to access parts of the station." - transfer_access = ACCESS_CHANGE_IDS - requires_ntnet = 0 - size = 8 - var/is_centcom = 0 - var/mode = 0 - var/printing = 0 - - //Cooldown for closing positions in seconds - //if set to -1: No cooldown... probably a bad idea - //if set to 0: Not able to close "original" positions. You can only close positions that you have opened before - var/change_position_cooldown = 60 - //Jobs you cannot open new positions for - var/list/blacklisted = list( - /datum/job/ai, - /datum/job/cyborg, - /datum/job/captain, - /datum/job/hop, - /datum/job/hos, - /datum/job/chief_engineer, - /datum/job/rd, - /datum/job/cmo, - /datum/job/judge, - /datum/job/blueshield, - /datum/job/nanotrasenrep, - /datum/job/pilot, - /datum/job/brigdoc, - /datum/job/mechanic, - /datum/job/barber, - /datum/job/chaplain, - /datum/job/ntnavyofficer, - /datum/job/ntspecops, - /datum/job/civilian, - /datum/job/syndicateofficer, - /datum/job/explorer // blacklisted so that HOPs don't try prioritizing it, then wonder why that doesn't work - ) - - //The scaling factor of max total positions in relation to the total amount of people on board the station in % - var/max_relative_positions = 30 //30%: Seems reasonable, limit of 6 @ 20 players - - //This is used to keep track of opened positions for jobs to allow instant closing - //Assoc array: "JobName" = (int) - var/list/opened_positions = list() - -/datum/computer_file/program/card_mod/proc/is_authenticated(var/mob/user) - if(user.can_admin_interact()) - return 1 - if(computer) - var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] - if(card_slot) - var/obj/item/card/id/auth_card = card_slot.stored_card2 - if(auth_card) - return check_access(auth_card) - return 0 - -/datum/computer_file/program/card_mod/proc/check_access(obj/item/I) - if(ACCESS_CHANGE_IDS in I.GetAccess()) - return 1 - return 0 - -/datum/computer_file/program/card_mod/proc/get_target_rank() - if(computer) - var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] - if(card_slot) - var/obj/item/card/id/id_card = card_slot.stored_card - if(id_card && id_card.assignment) - return id_card.assignment - return "Unassigned" - -/datum/computer_file/program/card_mod/proc/format_job_slots() - var/list/formatted = list() - for(var/datum/job/job in SSjobs.occupations) - if(job_blacklisted(job)) - continue - formatted.Add(list(list( - "title" = job.title, - "current_positions" = job.current_positions, - "total_positions" = job.total_positions, - "can_open" = can_open_job(job), - "can_close" = can_close_job(job), - "can_prioritize" = can_prioritize_job(job) - ))) - - return formatted - -/datum/computer_file/program/card_mod/proc/format_card_skins(list/card_skins) - var/list/formatted = list() - for(var/skin in card_skins) - formatted.Add(list(list( - "display_name" = get_skin_desc(skin), - "skin" = skin))) - - return formatted - - -/datum/computer_file/program/card_mod/proc/job_blacklisted(datum/job/job) - return (job.type in blacklisted) - - -//Logic check for if you can open the job -/datum/computer_file/program/card_mod/proc/can_open_job(datum/job/job) - if(job) - if(!job_blacklisted(job)) - if((job.total_positions <= GLOB.player_list.len * (max_relative_positions / 100))) - var/delta = (world.time / 10) - GLOB.time_last_changed_position - if((change_position_cooldown < delta) || (opened_positions[job.title] < 0)) - return 1 - return -2 - return -1 - return 0 - -//Logic check for if you can close the job -/datum/computer_file/program/card_mod/proc/can_close_job(datum/job/job) - if(job) - if(!job_blacklisted(job)) - if(job.total_positions > job.current_positions) - var/delta = (world.time / 10) - GLOB.time_last_changed_position - if((change_position_cooldown < delta) || (opened_positions[job.title] > 0)) - return 1 - return -2 - return -1 - return 0 - -/datum/computer_file/program/card_mod/proc/can_prioritize_job(datum/job/job) - if(job) - if(!job_blacklisted(job)) - if(job in SSjobs.prioritized_jobs) - return 2 - else - if(SSjobs.prioritized_jobs.len >= 3) - return 0 - if(job.total_positions <= job.current_positions) - return 0 - return 1 - return -1 - -/datum/computer_file/program/card_mod/proc/format_jobs(list/jobs, targetrank, list/jobformats) - var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD] - if(!card_slot || !card_slot.stored_card) - return null - var/list/formatted = list() - for(var/job in jobs) - formatted.Add(list(list( - "display_name" = replacetext(job, " ", " "), - "target_rank" = targetrank, - "job" = job, - "jlinkformat" = jobformats[job] ? jobformats[job] : null))) - return formatted - - -/datum/computer_file/program/card_mod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "card_prog.tmpl", "ID card modification program", 775, 700) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/card_mod/Topic(href, href_list) - if(..()) - return 1 - - var/obj/item/computer_hardware/card_slot/card_slot - var/obj/item/computer_hardware/printer/printer - if(computer) - card_slot = computer.all_components[MC_CARD] - printer = computer.all_components[MC_PRINT] - if(!card_slot) - return - - var/mob/user = usr - - var/obj/item/card/id/modify = card_slot.stored_card - var/obj/item/card/id/scan = card_slot.stored_card2 - - switch(href_list["action"]) - if("PRG_modify") - if(modify) - GLOB.data_core.manifest_modify(modify.registered_name, modify.assignment) - modify.name = "[modify.registered_name]'s ID Card ([modify.assignment])" - card_slot.try_eject(1, user) - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - if(!usr.drop_item()) - return - I.forceMove(computer) - card_slot.stored_card = I - - if("PRG_scan") - if(scan) - card_slot.try_eject(2, user) - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - if(!usr.drop_item()) - return - I.forceMove(computer) - card_slot.stored_card2 = I - - if("PRG_access") - if(href_list["allowed"]) - if(is_authenticated(usr)) - var/access_type = text2num(href_list["access_target"]) - var/access_allowed = text2num(href_list["allowed"]) - if(access_type in (is_centcom ? get_all_centcom_access() : get_all_accesses())) - modify.access -= access_type - if(!access_allowed) - modify.access += access_type - - if("PRG_skin") - var/skin = href_list["skin_target"] - if(is_authenticated(usr) && modify && ((skin in get_station_card_skins()) || ((skin in get_centcom_card_skins()) && is_centcom))) - modify.icon_state = href_list["skin_target"] - - if("PRG_assign") - if(is_authenticated(usr) && modify) - var/t1 = href_list["assign_target"] - if(t1 == "Custom") - var/temp_t = sanitize(copytext(input("Enter a custom job assignment.","Assignment"),1,MAX_MESSAGE_LEN)) - //let custom jobs function as an impromptu alt title, mainly for sechuds - if(temp_t && modify) - SSjobs.log_job_transfer(modify.registered_name, modify.getRankAndAssignment(), temp_t, scan.registered_name) - modify.assignment = temp_t - log_game("[key_name(usr)] has given \"[modify.registered_name]\" the custom job title \"[temp_t]\".") - else - var/list/access = list() - if(is_centcom && islist(get_centcom_access(t1))) - access = get_centcom_access(t1) - else - var/datum/job/jobdatum - for(var/jobtype in typesof(/datum/job)) - var/datum/job/J = new jobtype - if(ckey(J.title) == ckey(t1)) - jobdatum = J - break - if(!jobdatum) - to_chat(usr, "No log exists for this job: [t1]") - return - - access = jobdatum.get_access() - - var/jobnamedata = modify.getRankAndAssignment() - log_game("[key_name(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".") - if(t1 == "Civilian") - message_admins("[key_name_admin(usr)] has reassigned \"[modify.registered_name]\" from \"[jobnamedata]\" to \"[t1]\".") - - SSjobs.log_job_transfer(modify.registered_name, jobnamedata, t1, scan.registered_name) - SSjobs.slot_job_transfer(modify.rank, t1) - - var/mob/living/carbon/human/H = modify.getPlayer() - if(istype(H)) - if(jobban_isbanned(H, t1)) - message_admins("[ADMIN_FULLMONTY(H)] has been assigned the job [t1], in possible violation of their job ban.") - if(H.mind) - H.mind.playtime_role = t1 - - modify.access = access - modify.assignment = t1 - modify.rank = t1 - - if("PRG_reg") - if(is_authenticated(usr)) - var/temp_name = reject_bad_name(href_list["reg"]) - if(temp_name) - modify.registered_name = temp_name - else - computer.visible_message("[src] buzzes rudely.") - - if("PRG_account") - if(is_authenticated(usr)) - var/account_num = text2num(href_list["account"]) - modify.associated_account_number = account_num - - if("PRG_mode") - mode = text2num(href_list["mode_target"]) - - if("PRG_wipe_my_logs") - if(is_authenticated(usr) && is_centcom) - var/delcount = SSjobs.delete_log_records(scan.registered_name, FALSE) - if(delcount) - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - - if("PRG_wipe_all_logs") - if(is_authenticated(usr)) - var/delcount = SSjobs.delete_log_records(scan.registered_name, TRUE) - if(delcount) - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - - if("PRG_print") - if(!printing && computer) - printing = 1 - playsound(computer.loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - spawn(50) - printing = 0 - SSnanoui.update_uis(src) - var/title - var/content - if(mode == 2) - title = "crew manifest ([station_time_timestamp()])" - content = "

    Crew Manifest


    [GLOB.data_core ? GLOB.data_core.get_manifest(0) : ""]" - else if(modify && !mode) - title = "access report" - content = {"

    Access Report

    - Prepared By: [scan && scan.registered_name ? scan.registered_name : "Unknown"]
    - For: [modify.registered_name ? modify.registered_name : "Unregistered"]
    -
    - Assignment: [modify.assignment]
    - Account Number: #[modify.associated_account_number]
    - Blood Type: [modify.blood_type]

    - Access:
    "} - - var/first = 1 - for(var/A in modify.access) - content += "[first ? "" : ", "][get_access_desc(A)]" - first = 0 - content += "
    " - - if(content) - if(!printer.print_text(content, title)) - to_chat(user, "Hardware error: Printer was unable to print the file. It may be out of paper.") - return 1 - else - computer.visible_message("\The [computer] prints out paper.") - - if("PRG_terminate") - if(is_authenticated(usr)) - var/jobnamedata = modify.getRankAndAssignment() - log_game("[key_name(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".") - message_admins("[key_name_admin(usr)] has terminated the employment of \"[modify.registered_name]\" the \"[jobnamedata]\".") - SSjobs.log_job_transfer(modify.registered_name, jobnamedata, "Terminated", scan.registered_name) - modify.assignment = "Terminated" - modify.access = list() - - if("PRG_make_job_available") - // MAKE ANOTHER JOB POSITION AVAILABLE FOR LATE JOINERS - if(is_authenticated(usr)) - var/edit_job_target = href_list["job"] - var/datum/job/j = SSjobs.GetJob(edit_job_target) - if(!j) - return 1 - if(can_open_job(j) != 1) - return 1 - if(opened_positions[edit_job_target] >= 0) - GLOB.time_last_changed_position = world.time / 10 - j.total_positions++ - opened_positions[edit_job_target]++ - log_game("[key_name(usr)] has opened a job slot for job \"[j]\".") - - if("PRG_make_job_unavailable") - // MAKE JOB POSITION UNAVAILABLE FOR LATE JOINERS - if(is_authenticated(usr)) - var/edit_job_target = href_list["job"] - var/datum/job/j = SSjobs.GetJob(edit_job_target) - if(!j) - return 1 - if(can_close_job(j) != 1) - return 1 - //Allow instant closing without cooldown if a position has been opened before - if(opened_positions[edit_job_target] <= 0) - GLOB.time_last_changed_position = world.time / 10 - j.total_positions-- - opened_positions[edit_job_target]-- - log_game("[key_name(usr)] has closed a job slot for job \"[j]\".") - - - if("PRG_prioritize_job") - // TOGGLE WHETHER JOB APPEARS AS PRIORITIZED IN THE LOBBY - if(is_authenticated(usr)) - var/priority_target = href_list["job"] - var/datum/job/j = SSjobs.GetJob(priority_target) - if(!j) - return 0 - // Unlike the proper ID computer, this does not check job_in_department - var/priority = TRUE - if(j in SSjobs.prioritized_jobs) - SSjobs.prioritized_jobs -= j - priority = FALSE - else if(SSjobs.prioritized_jobs.len < 3) - SSjobs.prioritized_jobs += j - else - return 0 - log_game("[key_name(usr)] [priority ? "prioritized" : "unprioritized"] the job \"[j.title]\".") - playsound(computer.loc, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - - if(modify) - modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") - - SSnanoui.update_uis(src) - return 1 - -/datum/computer_file/program/card_mod/ui_data(mob/user) - var/list/data = get_header_data() - - var/obj/item/card/id/modify = null - var/obj/item/card/id/scan = null - - var/obj/item/computer_hardware/card_slot/card_slot - var/obj/item/computer_hardware/printer/printer - if(computer) - card_slot = computer.all_components[MC_CARD] - printer = computer.all_components[MC_PRINT] - if(card_slot) - modify = card_slot.stored_card - scan = card_slot.stored_card2 - - data["src"] = UID() - data["station_name"] = station_name() - data["mode"] = mode - data["printing"] = printing - data["printer"] = printer ? TRUE : FALSE - data["manifest"] = GLOB.data_core ? GLOB.data_core.get_manifest(0) : null - data["target_name"] = modify ? modify.name : "-----" - data["target_owner"] = modify && modify.registered_name ? modify.registered_name : "-----" - data["target_rank"] = get_target_rank() - data["scan_name"] = scan ? scan.name : "-----" - data["scan_owner"] = scan && scan.registered_name ? scan.registered_name : null - data["authenticated"] = is_authenticated(user) - data["has_modify"] = !!modify - data["account_number"] = modify ? modify.associated_account_number : null - data["centcom_access"] = is_centcom - data["all_centcom_access"] = null - data["regions"] = null - - var/list/job_formats = SSjobs.format_jobs_for_id_computer(modify) - - data["top_jobs"] = format_jobs(list("Captain", "Custom"), data["target_rank"], job_formats) - data["engineering_jobs"] = format_jobs(GLOB.engineering_positions, data["target_rank"], job_formats) - data["medical_jobs"] = format_jobs(GLOB.medical_positions, data["target_rank"], job_formats) - data["science_jobs"] = format_jobs(GLOB.science_positions, data["target_rank"], job_formats) - data["security_jobs"] = format_jobs(GLOB.security_positions, data["target_rank"], job_formats) - data["support_jobs"] = format_jobs(GLOB.support_positions, data["target_rank"], job_formats) - data["civilian_jobs"] = format_jobs(GLOB.civilian_positions, data["target_rank"], job_formats) - data["special_jobs"] = format_jobs(GLOB.whitelisted_positions, data["target_rank"], job_formats) - data["centcom_jobs"] = format_jobs(get_all_centcom_jobs(), data["target_rank"], job_formats) - data["card_skins"] = format_card_skins(get_station_card_skins()) - - data["job_slots"] = format_job_slots() - - var/time_to_wait = round(change_position_cooldown - ((world.time / 10) - GLOB.time_last_changed_position), 1) - var/mins = round(time_to_wait / 60) - var/seconds = time_to_wait - (60*mins) - data["cooldown_mins"] = mins - data["cooldown_secs"] = (seconds < 10) ? "0[seconds]" : seconds - - if(mode == 3 && is_authenticated(user)) - data["id_change_html"] = SSjobs.fetch_transfer_record_html(is_centcom) - - if(modify) - data["current_skin"] = modify.icon_state - - if(modify && is_centcom) - var/list/all_centcom_access = list() - for(var/access in get_all_centcom_access()) - all_centcom_access.Add(list(list( - "desc" = replacetext(get_centcom_access_desc(access), " ", " "), - "ref" = access, - "allowed" = (access in modify.access) ? 1 : 0))) - - data["all_centcom_access"] = all_centcom_access - data["all_centcom_skins"] = format_card_skins(get_centcom_card_skins()) - - else if(modify) - var/list/regions = list() - for(var/i = 1; i <= 7; i++) - var/list/accesses = list() - for(var/access in get_region_accesses(i)) - if(get_access_desc(access)) - accesses.Add(list(list( - "desc" = replacetext(get_access_desc(access), " ", " "), - "ref" = access, - "allowed" = (access in modify.access) ? 1 : 0))) - - regions.Add(list(list( - "name" = get_region_accesses_name(i), - "accesses" = accesses))) - - data["regions"] = regions - - return data diff --git a/code/modules/modular_computers/file_system/programs/command/comms.dm b/code/modules/modular_computers/file_system/programs/command/comms.dm deleted file mode 100644 index c23f0c9e3c7..00000000000 --- a/code/modules/modular_computers/file_system/programs/command/comms.dm +++ /dev/null @@ -1,385 +0,0 @@ -/datum/computer_file/program/comm - filename = "comm" - filedesc = "Command and communications" - program_icon_state = "comm" - extended_desc = "Used to command and control the station. Can relay long-range communications. This program can not be run on tablet computers." - required_access = ACCESS_HEADS - requires_ntnet = 1 - size = 12 - usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP - network_destination = "station long-range communication array" - - var/authenticated = COMM_AUTHENTICATION_NONE - var/list/messagetitle = list() - var/list/messagetext = list() - var/currmsg = 0 - var/aicurrmsg = 0 - var/menu_state = COMM_SCREEN_MAIN - var/ai_menu_state = COMM_SCREEN_MAIN - var/message_cooldown = 0 - var/centcomm_message_cooldown = 0 - var/tmp_alertlevel = 0 - - var/stat_msg1 - var/stat_msg2 - var/display_type="blank" - - var/datum/announcement/priority/crew_announcement = new - -/datum/computer_file/program/comm/New() - GLOB.shuttle_caller_list += src - ..() - crew_announcement.newscast = 0 - -/datum/computer_file/program/comm/Destroy() - GLOB.shuttle_caller_list -= src - SSshuttle.autoEvac() - return ..() - -/datum/computer_file/program/comm/proc/is_authenticated(mob/user, loud = 1) - if(authenticated == COMM_AUTHENTICATION_MAX) - return COMM_AUTHENTICATION_MAX - else if(user.can_admin_interact()) - return COMM_AUTHENTICATION_MAX - else if(authenticated) - return COMM_AUTHENTICATION_MIN - else - if(loud) - to_chat(user, "Access denied.") - return COMM_AUTHENTICATION_NONE - -/datum/computer_file/program/comm/proc/change_security_level(mob/user, new_level) - tmp_alertlevel = new_level - var/old_level = GLOB.security_level - if(!tmp_alertlevel) - tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel < SEC_LEVEL_GREEN) - tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel > SEC_LEVEL_BLUE) - tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this - set_security_level(tmp_alertlevel) - if(GLOB.security_level != old_level) - log_game("[key_name(user)] has changed the security level to [get_security_level()].") - message_admins("[key_name_admin(user)] has changed the security level to [get_security_level()].") - switch(GLOB.security_level) - if(SEC_LEVEL_GREEN) - feedback_inc("alert_comms_green", 1) - if(SEC_LEVEL_BLUE) - feedback_inc("alert_comms_blue", 1) - tmp_alertlevel = 0 - -/datum/computer_file/program/comm/proc/setCurrentMessage(mob/user, value) - if(isAI(user) || isrobot(user)) - aicurrmsg = value - else - currmsg = value - -/datum/computer_file/program/comm/proc/getCurrentMessage(mob/user) - if(isAI(user) || isrobot(user)) - return aicurrmsg - else - return currmsg - -/datum/computer_file/program/comm/proc/setMenuState(mob/user, value) - if(isAI(user) || isrobot(user)) - ai_menu_state=value - else - menu_state=value - -/datum/computer_file/program/comm/proc/getMenuState(mob/user) - if(isAI(user) || isrobot(user)) - return ai_menu_state - else - return menu_state - -/datum/computer_file/program/comm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "comm_program.tmpl", "Command and communications program", 575, 500) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/comm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/list/data = get_header_data() - data["is_ai"] = isAI(user) || isrobot(user) - data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state - data["emagged"] = computer ? computer.emagged : null - data["authenticated"] = is_authenticated(user, 0) - data["screen"] = getMenuState(usr) - - data["stat_display"] = list( - "type" = display_type, - "line_1" = (stat_msg1 ? stat_msg1 : "-----"), - "line_2" = (stat_msg2 ? stat_msg2 : "-----"), - - "presets" = list( - list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"), - list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."), - list("name" = "message", "label" = "Message", "desc" = "A custom message.") - ), - - "alerts"=list( - list("alert" = "default", "label" = "Nanotrasen", "desc" = "Oh god."), - list("alert" = "redalert", "label" = "Red Alert", "desc" = "Nothing to do with communists."), - list("alert" = "lockdown", "label" = "Lockdown", "desc" = "Let everyone know they're on lockdown."), - list("alert" = "biohazard", "label" = "Biohazard", "desc" = "Great for virus outbreaks and parties."), - ) - ) - - data["security_level"] = GLOB.security_level - data["str_security_level"] = capitalize(get_security_level()) - data["levels"] = list( - list("id" = SEC_LEVEL_GREEN, "name" = "Green"), - list("id" = SEC_LEVEL_BLUE, "name" = "Blue"), - ) - - var/list/msg_data = list() - for(var/i = 1; i <= messagetext.len; i++) - msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i))) - - data["messages"] = msg_data - if((data["is_ai"] && aicurrmsg) || (!data["is_ai"] && currmsg)) - data["current_message"] = data["is_ai"] ? messagetext[aicurrmsg] : messagetext[currmsg] - data["current_message_title"] = data["is_ai"] ? messagetitle[aicurrmsg] : messagetitle[currmsg] - - data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null - - var/shuttle[0] - switch(SSshuttle.emergency.mode) - if(SHUTTLE_IDLE, SHUTTLE_RECALL) - shuttle["callStatus"] = 2 //#define - else - shuttle["callStatus"] = 1 - if(SSshuttle.emergency.mode == SHUTTLE_CALL) - var/timeleft = SSshuttle.emergency.timeLeft() - shuttle["eta"] = "[timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" - - data["shuttle"] = shuttle - - return data - -/datum/computer_file/program/comm/Topic(href, href_list) - if(..()) - return 1 - - var/turf/T = get_turf(computer) - if(!is_secure_level(T.z)) - to_chat(usr, "Unable to establish a connection: You're too far away from the station!") - return 1 - - if(href_list["PRG_login"]) - if(!ishuman(usr)) - to_chat(usr, "Access denied.") - return - - var/list/access = usr.get_access() - if(ACCESS_HEADS in access) - authenticated = COMM_AUTHENTICATION_MIN - - if(ACCESS_CAPTAIN in access) - authenticated = COMM_AUTHENTICATION_MAX - var/mob/living/carbon/human/H = usr - var/obj/item/card/id = H.get_idcard(TRUE) - if(istype(id)) - crew_announcement.announcer = GetNameAndAssignmentFromId(id) - - SSnanoui.update_uis(src) - return 1 - - if(href_list["PRG_logout"]) - authenticated = COMM_AUTHENTICATION_NONE - crew_announcement.announcer = "" - setMenuState(usr, COMM_SCREEN_MAIN) - SSnanoui.update_uis(src) - return 1 - - if(is_authenticated(usr)) - switch(href_list["PRG_operation"]) - if("main") - setMenuState(usr, COMM_SCREEN_MAIN) - - if("changeseclevel") - setMenuState(usr,COMM_SCREEN_SECLEVEL) - - if("newalertlevel") - if(isAI(usr) || isrobot(usr)) - to_chat(usr, "Firewalls prevent you from changing the alert level.") - return 1 - else if(usr.can_admin_interact()) - change_security_level(usr, text2num(href_list["level"])) - return 1 - else if(!ishuman(usr)) - to_chat(usr, "Security measures prevent you from changing the alert level.") - return 1 - - var/mob/living/carbon/human/L = usr - var/obj/item/card = L.get_active_hand() - var/obj/item/card/id/I = (card && card.GetID()) || L.wear_id || L.wear_pda - if(istype(I, /obj/item/pda)) - var/obj/item/pda/pda = I - I = pda.id - if(I && istype(I)) - if(ACCESS_CAPTAIN in I.access) - change_security_level(usr, text2num(href_list["level"])) - else - to_chat(usr, "You are not authorized to do this.") - setMenuState(usr, COMM_SCREEN_MAIN) - else - to_chat(usr, "You need to swipe your ID.") - - if("announce") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(message_cooldown) - to_chat(usr, "Please allow at least one minute to pass between announcements.") - SSnanoui.update_uis(src) - return 1 - var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") - if(!input || message_cooldown || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return 1 - crew_announcement.Announce(input) - message_cooldown = 1 - spawn(600)//One minute cooldown - message_cooldown = 0 - - if("callshuttle") - var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") - if(!input || ..() || !is_authenticated(usr)) - SSnanoui.update_uis(src) - return 1 - - call_shuttle_proc(usr, input) - if(SSshuttle.emergency.timer) - post_status("shuttle") - setMenuState(usr, COMM_SCREEN_MAIN) - - if("cancelshuttle") - if(isAI(usr) || isrobot(usr)) - to_chat(usr, "Firewalls prevent you from recalling the shuttle.") - SSnanoui.update_uis(src) - return 1 - var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No") - if(response == "Yes") - cancel_call_proc(usr) - if(SSshuttle.emergency.timer) - post_status("shuttle") - setMenuState(usr, COMM_SCREEN_MAIN) - - if("messagelist") - currmsg = 0 - if(href_list["msgid"]) - setCurrentMessage(usr, text2num(href_list["msgid"])) - setMenuState(usr,COMM_SCREEN_MESSAGES) - - if("delmessage") - if(href_list["msgid"]) - currmsg = text2num(href_list["msgid"]) - var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No") - if(response == "Yes") - if(currmsg) - var/id = getCurrentMessage() - var/title = messagetitle[id] - var/text = messagetext[id] - messagetitle.Remove(title) - messagetext.Remove(text) - if(currmsg == id) - currmsg = 0 - if(aicurrmsg == id) - aicurrmsg = 0 - setMenuState(usr,COMM_SCREEN_MESSAGES) - - if("status") - setMenuState(usr,COMM_SCREEN_STAT) - - // Status display stuff - if("setstat") - display_type=href_list["statdisp"] - switch(display_type) - if("message") - post_status("message", stat_msg1, stat_msg2, usr) - if("alert") - post_status("alert", href_list["alert"], user = usr) - else - post_status(href_list["statdisp"], user = usr) - setMenuState(usr, COMM_SCREEN_STAT) - - if("setmsg1") - stat_msg1 = clean_input("Line 1", "Enter Message Text", stat_msg1) - setMenuState(usr, COMM_SCREEN_STAT) - - if("setmsg2") - stat_msg2 = clean_input("Line 2", "Enter Message Text", stat_msg2) - setMenuState(usr, COMM_SCREEN_STAT) - - if("nukerequest") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return 1 - var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return 1 - Nuke_request(input, usr) - to_chat(usr, "Request sent.") - log_game("[key_name(usr)] has requested the nuclear codes from Centcomm") - GLOB.priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg') - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - if("MessageCentcomm") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return 1 - var/input = stripped_input(usr, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return 1 - Centcomm_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made a Centcomm announcement: [input]") - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - // OMG SYNDICATE ...LETTERHEAD - if("MessageSyndicate") - if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (computer && computer.emagged)) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return 1 - var/input = stripped_input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return 1 - Syndicate_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made a Syndicate announcement: [input]") - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - if("RestartNanoMob") - if(SSmob_hunt) - if(SSmob_hunt.manual_reboot()) - var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat", - "Capturing all of them", "Fixing minor text issues", "Being the very best", - "Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game") - to_chat(usr, "Restarting Nano-Mob Hunter GO! game server. [loading_msg]...") - else - to_chat(usr, "Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.") - else - to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") - - SSnanoui.update_uis(src) - return 1 diff --git a/code/modules/modular_computers/file_system/programs/engineering/alarm.dm b/code/modules/modular_computers/file_system/programs/engineering/alarm.dm deleted file mode 100644 index 8cb47337a6d..00000000000 --- a/code/modules/modular_computers/file_system/programs/engineering/alarm.dm +++ /dev/null @@ -1,71 +0,0 @@ -/datum/computer_file/program/alarm_monitor - filename = "alarmmonitor" - filedesc = "Alarm Monitoring" - ui_header = "alarm_green.gif" - program_icon_state = "alert-green" - extended_desc = "This program provides visual interface for station's alarm system." - requires_ntnet = 1 - network_destination = "alarm monitoring network" - size = 5 - var/list/datum/alarm_handler/alarm_handlers - -/datum/computer_file/program/alarm_monitor/New() - ..() - alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.fire_alarm, SSalarms.power_alarm) - for(var/datum/alarm_handler/AH in alarm_handlers) - AH.register(src, /datum/computer_file/program/alarm_monitor/proc/update_icon) - -/datum/computer_file/program/alarm_monitor/Destroy() - for(var/datum/alarm_handler/AH in alarm_handlers) - AH.unregister(src) - QDEL_NULL(alarm_handlers) - return ..() - -/datum/computer_file/program/alarm_monitor/proc/update_icon() - for(var/datum/alarm_handler/AH in alarm_handlers) - if(AH.has_major_alarms()) - program_icon_state = "alert-red" - ui_header = "alarm_red.gif" - update_computer_icon() - return 1 - program_icon_state = "alert-green" - ui_header = "alarm_green.gif" - update_computer_icon() - return 0 - -/datum/computer_file/program/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring", 575, 700) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/alarm_monitor/ui_data(mob/user) - var/list/data = get_header_data() - - var/categories[0] - for(var/datum/alarm_handler/AH in alarm_handlers) - categories[++categories.len] = list("category" = AH.category, "alarms" = list()) - for(var/datum/alarm/A in AH.major_alarms()) - var/cameras[0] - var/lost_sources[0] - - if(isAI(user)) - for(var/obj/machinery/camera/C in A.cameras()) - cameras[++cameras.len] = C.nano_structure() - for(var/datum/alarm_source/AS in A.sources) - if(!AS.source) - lost_sources[++lost_sources.len] = AS.source_name - - categories[categories.len]["alarms"] += list(list( - "name" = sanitize(A.alarm_name()), - "origin_lost" = A.origin == null, - "has_cameras" = cameras.len, - "cameras" = cameras, - "lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : "")) - data["categories"] = categories - - return data diff --git a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm deleted file mode 100644 index 0763d527345..00000000000 --- a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm +++ /dev/null @@ -1,48 +0,0 @@ -/datum/computer_file/program/power_monitor - filename = "powermonitor" - filedesc = "Power Monitoring" - program_icon_state = "power_monitor" - extended_desc = "This program connects to sensors around the station to provide information about electrical systems" - ui_header = "power_norm.gif" - transfer_access = ACCESS_ENGINE - usage_flags = PROGRAM_CONSOLE - requires_ntnet = 0 - network_destination = "power monitoring system" - size = 9 - var/obj/structure/cable/attached - -/datum/computer_file/program/power_monitor/run_program(mob/living/user) - . = ..(user) - search() - -/datum/computer_file/program/power_monitor/process_tick() - if(!attached) - search() - -/datum/computer_file/program/power_monitor/proc/search() - var/turf/T = get_turf(computer) - attached = locate() in T - -/datum/computer_file/program/power_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "power_monitor.tmpl", "Alarm Monitoring", 800, 700) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/power_monitor/ui_data() - var/list/data = get_header_data() - - data["powermonitor"] = attached ? TRUE : FALSE - - if(attached) - var/datum/powernet/powernet = attached.powernet - data["poweravail"] = powernet.avail - data["powerload"] = powernet.viewload - data["powerdemand"] = powernet.load - data["apcs"] = GLOB.apc_repository.apc_data(powernet) - - return data diff --git a/code/modules/modular_computers/file_system/programs/engineering/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/sm_monitor.dm deleted file mode 100644 index 0045b36f30e..00000000000 --- a/code/modules/modular_computers/file_system/programs/engineering/sm_monitor.dm +++ /dev/null @@ -1,137 +0,0 @@ -/datum/computer_file/program/supermatter_monitor - filename = "smmonitor" - filedesc = "Supermatter Monitoring" - ui_header = "smmon_0.gif" - program_icon_state = "smmon_0" - extended_desc = "This program connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines." - requires_ntnet = TRUE - transfer_access = ACCESS_CONSTRUCTION - network_destination = "supermatter monitoring system" - size = 5 - var/last_status = SUPERMATTER_INACTIVE - var/list/supermatters - var/obj/machinery/power/supermatter_shard/active // Currently selected supermatter crystal. - - -/datum/computer_file/program/supermatter_monitor/process_tick() - ..() - var/new_status = get_status() - if(last_status != new_status) - last_status = new_status - if(last_status == SUPERMATTER_ERROR) - last_status = SUPERMATTER_INACTIVE - ui_header = "smmon_[last_status].gif" - program_icon_state = "smmon_[last_status]" - if(istype(computer)) - computer.update_icon() - -/datum/computer_file/program/supermatter_monitor/run_program(mob/living/user) - . = ..(user) - refresh() - -/datum/computer_file/program/supermatter_monitor/kill_program(forced = FALSE) - active = null - supermatters = null - ..() - -// Refreshes list of active supermatter crystals -/datum/computer_file/program/supermatter_monitor/proc/refresh() - supermatters = list() - var/turf/T = get_turf(nano_host()) - if(!T) - return - for(var/obj/machinery/power/supermatter_shard/S in SSair.atmos_machinery) - // Delaminating, not within coverage, not on a tile. - if(!(is_station_level(S.z) || is_mining_level(S.z) || atoms_share_level(S, T) || !istype(S.loc, /turf/simulated/))) - continue - supermatters.Add(S) - - if(!(active in supermatters)) - active = null - -/datum/computer_file/program/supermatter_monitor/proc/get_status() - . = SUPERMATTER_INACTIVE - for(var/obj/machinery/power/supermatter_shard/S in supermatters) - . = max(., S.get_status()) - -/datum/computer_file/program/supermatter_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "supermatter_monitor.tmpl", "Supermatter Monitoring", 600, 400) - ui.set_auto_update(TRUE) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/supermatter_monitor/ui_data() - var/list/data = get_header_data() - - if(istype(active)) - var/turf/T = get_turf(active) - if(!T) - active = null - refresh() - return - var/datum/gas_mixture/air = T.return_air() - if(!air) - active = null - return - - data["active"] = TRUE - data["SM_integrity"] = active.get_integrity() - data["SM_power"] = active.power - data["SM_ambienttemp"] = air.temperature - data["SM_ambientpressure"] = air.return_pressure() - //data["SM_EPR"] = round((air.total_moles / air.group_multiplier) / 23.1, 0.01) - var/other_moles = air.total_trace_moles() - var/TM = air.total_moles() - if(TM) - data["SM_gas_O2"] = round(100*air.oxygen/TM,0.01) - data["SM_gas_CO2"] = round(100*air.carbon_dioxide/TM,0.01) - data["SM_gas_N2"] = round(100*air.nitrogen/TM,0.01) - data["SM_gas_PL"] = round(100*air.toxins/TM,0.01) - if(other_moles) - data["SM_gas_OTHER"] = round(100 * other_moles / TM, 0.01) - else - data["SM_gas_OTHER"] = 0 - else - data["SM_gas_O2"] = 0 - data["SM_gas_CO2"] = 0 - data["SM_gas_N2"] = 0 - data["SM_gas_PH"] = 0 - data["SM_gas_OTHER"] = 0 - else - var/list/SMS = list() - for(var/obj/machinery/power/supermatter_shard/S in supermatters) - var/area/A = get_area(S) - if(!A) - continue - - SMS.Add(list(list( - "area_name" = A.name, - "integrity" = S.get_integrity(), - "uid" = S.uid - ))) - - data["active"] = FALSE - data["supermatters"] = SMS - - return data - - -/datum/computer_file/program/supermatter_monitor/Topic(href, href_list) - if(..()) - return TRUE - if(href_list["clear"]) - active = null - return TRUE - if(href_list["refresh"]) - refresh() - return TRUE - if(href_list["set"]) - var/newuid = text2num(href_list["set"]) - for(var/obj/machinery/power/supermatter_shard/S in supermatters) - if(S.uid == newuid) - active = S - return TRUE diff --git a/code/modules/modular_computers/file_system/programs/generic/configurator.dm b/code/modules/modular_computers/file_system/programs/generic/configurator.dm deleted file mode 100644 index 70ce4656b9c..00000000000 --- a/code/modules/modular_computers/file_system/programs/generic/configurator.dm +++ /dev/null @@ -1,68 +0,0 @@ -// This is special hardware configuration program. -// It is to be used only with modular computers. -// It allows you to toggle components of your device. - -/datum/computer_file/program/computerconfig - filename = "compconfig" - filedesc = "Computer Configuration Tool" - extended_desc = "This program allows configuration of computer's hardware" - program_icon_state = "generic" - unsendable = 1 - undeletable = 1 - size = 4 - available_on_ntnet = 0 - requires_ntnet = 0 - - -/datum/computer_file/program/computerconfig/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "laptop_configuration.tmpl", "NTOS Configuration Utility", 575, 700) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/computerconfig/ui_data(mob/user) - var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - var/obj/item/computer_hardware/battery/battery_module = computer.all_components[MC_CELL] - - var/list/data = get_header_data() - - data["disk_size"] = hard_drive.max_capacity - data["disk_used"] = hard_drive.used_capacity - data["power_usage"] = computer.last_power_usage - data["battery_exists"] = battery_module ? 1 : 0 - if(battery_module && battery_module.battery) - data["battery_rating"] = battery_module.battery.maxcharge - data["battery_percent"] = round(battery_module.battery.percent()) - data["eta"] = computer.get_power_eta() - - if(battery_module && battery_module.battery) - data["battery"] = list("max" = battery_module.battery.maxcharge, "charge" = round(battery_module.battery.charge)) - - var/list/all_entries[0] - for(var/I in computer.all_components) - var/obj/item/computer_hardware/H = computer.all_components[I] - all_entries.Add(list(list( - "name" = H.name, - "desc" = H.desc, - "enabled" = H.enabled, - "critical" = H.critical, - "powerusage" = H.power_usage - ))) - - data["hardware"] = all_entries - return data - - -/datum/computer_file/program/computerconfig/Topic(href, list/href_list) - if(..()) - return - switch(href_list["action"]) - if("PC_toggle_component") - var/obj/item/computer_hardware/H = computer.find_hardware_by_name(href_list["name"]) - if(H && istype(H)) - H.enabled = !H.enabled - . = TRUE diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm deleted file mode 100644 index e7edc437639..00000000000 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ /dev/null @@ -1,228 +0,0 @@ -/datum/computer_file/program/filemanager - filename = "filemanager" - filedesc = "NTOS File Manager" - extended_desc = "This program allows management of files." - program_icon_state = "generic" - size = 8 - requires_ntnet = 0 - available_on_ntnet = 0 - undeletable = 1 - var/open_file - var/error - -/datum/computer_file/program/filemanager/proc/prepare_printjob(t, font = PRINTER_FONT) // Additional stuff to parse if we want to print it and make a happy Head of Personnel. Forms FTW. - t = replacetext(t, "\[field\]", "") - t = replacetext(t, "\[sign\]", "") - t = pencode_to_html(t, sign = 0, fields = 0, deffont = font) - return t - -/datum/computer_file/program/filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "file_manager.tmpl", "NTOS File Manager", 575, 700) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/filemanager/ui_data(mob/user) - var/list/data = get_header_data() - - var/obj/item/computer_hardware/hard_drive/HDD = computer.all_components[MC_HDD] - var/obj/item/computer_hardware/hard_drive/portable/RHDD = computer.all_components[MC_SDD] - if(error) - data["error"] = error - if(open_file) - var/datum/computer_file/data/file - - if(!computer || !HDD) - data["error"] = "I/O ERROR: Unable to access hard drive." - else - file = HDD.find_file_by_name(open_file) - if(!istype(file)) - data["error"] = "I/O ERROR: Unable to open file." - else - data["filedata"] = pencode_to_html(file.stored_data, format = 1, sign = 0, fields = 0) - data["filename"] = "[file.filename].[file.filetype]" - else - if(!computer || !HDD) - data["error"] = "I/O ERROR: Unable to access hard drive." - else - var/list/files[0] - for(var/datum/computer_file/F in HDD.stored_files) - files.Add(list(list( - "name" = F.filename, - "type" = F.filetype, - "size" = F.size, - "undeletable" = F.undeletable, - "encrypted" = !!F.password - ))) - data["files"] = files - if(RHDD) - data["usbconnected"] = 1 - var/list/usbfiles[0] - for(var/datum/computer_file/F in RHDD.stored_files) - usbfiles.Add(list(list( - "name" = F.filename, - "type" = F.filetype, - "size" = F.size, - "undeletable" = F.undeletable, - "encrypted" = !!F.password - ))) - data["usbfiles"] = usbfiles - - return data - -/datum/computer_file/program/filemanager/Topic(href, list/href_list) - if(..()) - return 1 - - var/obj/item/computer_hardware/hard_drive/HDD = computer.all_components[MC_HDD] - var/obj/item/computer_hardware/hard_drive/RHDD = computer.all_components[MC_SDD] - var/obj/item/computer_hardware/printer/printer = computer.all_components[MC_PRINT] - - switch(href_list["action"]) - if("PRG_openfile") - . = 1 - var/datum/computer_file/F = HDD.find_file_by_name(href_list["name"]) - if(!F.can_access_file(usr)) - return - open_file = href_list["name"] - if("PRG_newtextfile") - . = 1 - var/newname = stripped_input(usr, "Enter file name or leave blank to cancel:", "File rename", max_length=50) - if(!newname) - return 1 - if(!HDD) - return 1 - var/datum/computer_file/data/F = new/datum/computer_file/data() - F.filename = newname - F.filetype = "TXT" - HDD.store_file(F) - if("PRG_deletefile") - . = 1 - if(!HDD) - return 1 - var/datum/computer_file/file = HDD.find_file_by_name(href_list["name"]) - if(!file || file.undeletable) - return 1 - HDD.remove_file(file) - if("PRG_usbdeletefile") - . = 1 - if(!RHDD) - return 1 - var/datum/computer_file/file = RHDD.find_file_by_name(href_list["name"]) - if(!file || file.undeletable) - return 1 - RHDD.remove_file(file) - if("PRG_closefile") - . = 1 - open_file = null - error = null - if("PRG_clone") - . = 1 - if(!HDD) - return 1 - var/datum/computer_file/F = HDD.find_file_by_name(href_list["name"]) - if(!F || !istype(F)) - return 1 - var/newname = stripped_input(usr, "Enter clone file name:", "File clone", "Copy of " + F.filename, max_length=50) - if(F && newname) - var/datum/computer_file/C = F.clone(1) - C.filename = newname - if(!HDD.store_file(C)) - error = "I/O error: Unable to clone file. Hard drive is probably full." - if("PRG_rename") - . = 1 - if(!HDD) - return 1 - var/datum/computer_file/file = HDD.find_file_by_name(href_list["name"]) - if(!file || !istype(file)) - return 1 - var/newname = stripped_input(usr, "Enter new file name:", "File rename", file.filename, max_length=50) - if(file && newname) - file.filename = newname - if("PRG_edit") - . = 1 - if(!open_file) - return 1 - if(!HDD) - return 1 - var/datum/computer_file/data/F = HDD.find_file_by_name(open_file) - if(!F || !istype(F)) - return 1 - if(F.do_not_edit && (alert("WARNING: This file is not compatible with editor. Editing it may result in permanently corrupted formatting or damaged data consistency. Edit anyway?", "Incompatible File", "No", "Yes") == "No")) - return 1 - // 16384 is the limit for file length in characters. Currently, papers have value of 2048 so this is 8 times as long, since we can't edit parts of the file independently. - var/newtext = stripped_multiline_input(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", html_decode(F.stored_data), 16384, TRUE) - if(!newtext) - return - if(F) - var/datum/computer_file/data/backup = F.clone() - HDD.remove_file(F) - F = backup.clone() //When the file gets removed from the HDD, it gets queued for garbage collection. Hacky fix is to make a copy. - F.stored_data = newtext - F.calculate_size() - // We can't store the updated file, it's probably too large. Print an error and restore backed up version. - // This is mostly intended to prevent people from losing texts they spent lot of time working on due to running out of space. - // They will be able to copy-paste the text from error screen and store it in notepad or something. - if(!HDD.store_file(F)) - error = "I/O error: Unable to overwrite file. Hard drive is probably full. You may want to backup your changes before closing this window:

    [F.stored_data]

    " - HDD.store_file(backup) - if("PRG_printfile") - . = 1 - if(!open_file) - return 1 - if(!HDD) - return 1 - var/datum/computer_file/data/F = HDD.find_file_by_name(open_file) - if(!F || !istype(F)) - return 1 - if(!printer) - error = "Missing Hardware: Your computer does not have required hardware to complete this operation." - return 1 - if(!printer.print_text(prepare_printjob(F.stored_data, computer.emagged ? CRAYON_FONT : PRINTER_FONT), open_file)) - error = "Hardware error: Printer was unable to print the file. It may be out of paper." - return 1 - if("PRG_copytousb") - . = 1 - if(!HDD || !RHDD) - return 1 - var/datum/computer_file/F = HDD.find_file_by_name(href_list["name"]) - if(!F || !istype(F)) - return 1 - var/datum/computer_file/C = F.clone(0) - RHDD.store_file(C) - if("PRG_copyfromusb") - . = 1 - if(!HDD || !RHDD) - return 1 - var/datum/computer_file/F = RHDD.find_file_by_name(href_list["name"]) - if(!F || !istype(F)) - return 1 - var/datum/computer_file/C = F.clone(0) - HDD.store_file(C) - if("PRG_encrypt") - . = 1 - if(!HDD) - return 1 - var/datum/computer_file/F = HDD.find_file_by_name(href_list["name"]) - if(!F || F.undeletable) - return 1 - if(F.password) - return - var/new_password = sanitize(input(usr, "Enter an encryption key:", "Encrypt File")) - if(!new_password) - to_chat(usr, "File not encrypted.") - return - F.password=new_password - if("PRG_decrypt") - . = 1 - if(!HDD) - return 1 - var/datum/computer_file/F = HDD.find_file_by_name(href_list["name"]) - if(!F || F.undeletable) - return 1 - if(F.can_access_file(usr)) - F.password = "" diff --git a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm deleted file mode 100644 index b4e5ab4674c..00000000000 --- a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm +++ /dev/null @@ -1,178 +0,0 @@ -/datum/computer_file/program/ntnetdownload - filename = "ntndownloader" - filedesc = "NTNet Software Download Tool" - program_icon_state = "generic" - extended_desc = "This program allows downloads of software from official NT repositories" - unsendable = 1 - undeletable = 1 - size = 4 - requires_ntnet = 1 - requires_ntnet_feature = NTNET_SOFTWAREDOWNLOAD - available_on_ntnet = 0 - ui_header = "downloader_finished.gif" - var/datum/computer_file/program/downloaded_file = null - var/hacked_download = 0 - var/download_completion = 0 //GQ of downloaded data. - var/download_netspeed = 0 - var/downloaderror = "" - var/obj/item/modular_computer/my_computer = null - -/datum/computer_file/program/ntnetdownload/proc/begin_file_download(filename) - if(downloaded_file) - return 0 - - var/datum/computer_file/program/PRG = GLOB.ntnet_global.find_ntnet_file_by_name(filename) - - if(!PRG || !istype(PRG)) - return 0 - - // Attempting to download antag only program, but without having emagged computer. No. - if(PRG.available_on_syndinet && !computer.emagged) - return 0 - - var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - - if(!computer || !hard_drive || !hard_drive.can_store_file(PRG)) - return 0 - - ui_header = "downloader_running.gif" - - if(PRG in GLOB.ntnet_global.available_station_software) - generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from NTNet Software Repository.") - hacked_download = 0 - else if(PRG in GLOB.ntnet_global.available_antag_software) - generate_network_log("Began downloading file **ENCRYPTED**.[PRG.filetype] from unspecified server.") - hacked_download = 1 - else - generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from unspecified server.") - hacked_download = 0 - - downloaded_file = PRG.clone() - -/datum/computer_file/program/ntnetdownload/proc/abort_file_download() - if(!downloaded_file) - return - generate_network_log("Aborted download of file [hacked_download ? "**ENCRYPTED**" : "[downloaded_file.filename].[downloaded_file.filetype]"].") - downloaded_file = null - download_completion = 0 - ui_header = "downloader_finished.gif" - -/datum/computer_file/program/ntnetdownload/proc/complete_file_download() - if(!downloaded_file) - return - generate_network_log("Completed download of file [hacked_download ? "**ENCRYPTED**" : "[downloaded_file.filename].[downloaded_file.filetype]"].") - var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - if(!computer || !hard_drive || !hard_drive.store_file(downloaded_file)) - // The download failed - downloaderror = "I/O ERROR - Unable to save file. Check whether you have enough free space on your hard drive and whether your hard drive is properly connected. If the issue persists contact your system administrator for assistance." - downloaded_file = null - download_completion = 0 - ui_header = "downloader_finished.gif" - -/datum/computer_file/program/ntnetdownload/process_tick() - if(!downloaded_file) - return - if(download_completion >= downloaded_file.size) - complete_file_download() - // Download speed according to connectivity state. NTNet server is assumed to be on unlimited speed so we're limited by our local connectivity - download_netspeed = 0 - // Speed defines are found in misc.dm - switch(ntnet_status) - if(1) - download_netspeed = NTNETSPEED_LOWSIGNAL - if(2) - download_netspeed = NTNETSPEED_HIGHSIGNAL - if(3) - download_netspeed = NTNETSPEED_ETHERNET - download_completion += download_netspeed - -/datum/computer_file/program/ntnetdownload/Topic(href, list/href_list) - if(..()) - return 1 - switch(href_list["action"]) - if("PRG_downloadfile") - if(!downloaded_file) - begin_file_download(href_list["filename"]) - return 1 - if("PRG_reseterror") - if(downloaderror) - download_completion = 0 - download_netspeed = 0 - downloaded_file = null - downloaderror = "" - return 1 - return 0 - -/datum/computer_file/program/ntnetdownload/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "ntnet_downloader.tmpl", "NTNet Download Program", 575, 700) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/ntnetdownload/ui_data(mob/user) - my_computer = computer - - if(!istype(my_computer)) - return - - var/list/data = get_header_data() - - // This IF cuts on data transferred to client, so i guess it's worth it. - if(downloaderror) // Download errored. Wait until user resets the program. - data["error"] = downloaderror - else if(downloaded_file) // Download running. Wait please.. - data["downloadname"] = downloaded_file.filename - data["downloaddesc"] = downloaded_file.filedesc - data["downloadsize"] = downloaded_file.size - data["downloadspeed"] = download_netspeed - data["downloadcompletion"] = round(min(download_completion, downloaded_file.size), 0.1) - else // No download running, pick file. - var/obj/item/computer_hardware/hard_drive/hard_drive = my_computer.all_components[MC_HDD] - data["disk_size"] = hard_drive.max_capacity - data["disk_used"] = hard_drive.used_capacity - var/list/all_entries[0] - for(var/A in GLOB.ntnet_global.available_station_software) - var/datum/computer_file/program/P = A - // Only those programs our user can run will show in the list - if(!P.can_run(user,transfer = 1)) - continue - all_entries.Add(list(list( - "filename" = P.filename, - "filedesc" = P.filedesc, - "fileinfo" = P.extended_desc, - "compatibility" = check_compatibility(P), - "size" = P.size, - "status" = !hard_drive || !hard_drive.can_store_file(P) ? "disabled" : null - ))) - data["hackedavailable"] = 0 - if(computer.emagged) // If we are running on emagged computer we have access to some "bonus" software - var/list/hacked_programs[0] - for(var/S in GLOB.ntnet_global.available_antag_software) - var/datum/computer_file/program/P = S - data["hackedavailable"] = 1 - hacked_programs.Add(list(list( - "filename" = P.filename, - "filedesc" = P.filedesc, - "fileinfo" = P.extended_desc, - "size" = P.size - ))) - data["hacked_programs"] = hacked_programs - - data["downloadable_programs"] = all_entries - - return data - -/datum/computer_file/program/ntnetdownload/proc/check_compatibility(datum/computer_file/program/P) - var/hardflag = computer.hardware_flag - - if(P && P.is_supported_by_hardware(hardflag,0)) - return "Compatible" - return "Incompatible!" - -/datum/computer_file/program/ntnetdownload/kill_program(forced) - abort_file_download() - return ..(forced) diff --git a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm deleted file mode 100644 index ca208c22b02..00000000000 --- a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm +++ /dev/null @@ -1,229 +0,0 @@ -/datum/computer_file/program/chatclient - filename = "ntnrc_client" - filedesc = "NTNet Relay Chat Client" - program_icon_state = "command" - extended_desc = "This program allows communication over NTNRC network" - size = 8 - requires_ntnet = 1 - requires_ntnet_feature = NTNET_COMMUNICATION - network_destination = "NTNRC server" - ui_header = "ntnrc_idle.gif" - available_on_ntnet = 1 - var/last_message = null // Used to generate the toolbar icon - var/username - var/datum/ntnet_conversation/channel = null - var/operator_mode = 0 // Channel operator mode - var/netadmin_mode = 0 // Administrator mode (invisible to other users + bypasses passwords) - -/datum/computer_file/program/chatclient/New() - ..() - username = "DefaultUser[rand(100, 999)]" - -/datum/computer_file/program/chatclient/process_tick() - ..() - if(program_state != PROGRAM_STATE_KILLED) - ui_header = "ntnrc_idle.gif" - if(channel) - // Remember the last message. If there is no message in the channel remember null. - last_message = channel.messages.len ? channel.messages[channel.messages.len - 1] : null - else - last_message = null - return 1 - if(channel && channel.messages && channel.messages.len) - ui_header = last_message == channel.messages[channel.messages.len - 1] ? "ntnrc_idle.gif" : "ntnrc_new.gif" - else - ui_header = "ntnrc_idle.gif" - -/datum/computer_file/program/chatclient/kill_program(forced = FALSE) - if(channel) - channel.remove_client(src) - channel = null - ..() - -/datum/computer_file/program/chatclient/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "ntnet_chat.tmpl", "NTNet Relay Chat Client", 575, 700) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - - -/datum/computer_file/program/chatclient/ui_data(mob/user) - if(!GLOB.ntnet_global || !GLOB.ntnet_global.chat_channels) - return - - var/list/data = get_header_data() - - data["adminmode"] = netadmin_mode - if(channel) - data["title"] = channel.title - var/list/messages[0] - for(var/M in channel.messages) - messages.Add(list(list( - "msg" = M - ))) - data["messages"] = messages - var/list/clients[0] - for(var/C in channel.clients) - var/datum/computer_file/program/chatclient/cl = C - clients.Add(list(list( - "name" = cl.username - ))) - data["clients"] = clients - operator_mode = (channel.operator == src) ? 1 : 0 - data["is_operator"] = operator_mode || netadmin_mode - - else // Channel selection screen - var/list/all_channels[0] - for(var/C in GLOB.ntnet_global.chat_channels) - var/datum/ntnet_conversation/conv = C - if(conv && conv.title) - all_channels.Add(list(list( - "chan" = conv.title, - "id" = conv.id - ))) - data["all_channels"] = all_channels - - return data - -/datum/computer_file/program/chatclient/Topic(href, list/href_list) - if(..()) - return 1 - - switch(href_list["action"]) - if("PRG_speak") - . = 1 - if(!channel) - return 1 - var/mob/living/user = usr - var/message = reject_bad_text(input(user, "Enter message or leave blank to cancel: ")) - if(!message || !channel) - return - channel.add_message(message, username) - log_chat("[username] sent to [channel.title]: [message]", user) - - if("PRG_joinchannel") - . = 1 - var/datum/ntnet_conversation/C - for(var/datum/ntnet_conversation/chan in GLOB.ntnet_global.chat_channels) - if(chan.id == text2num(href_list["id"])) - C = chan - break - - if(!C) - return 1 - - if(netadmin_mode) - channel = C // Bypasses normal leave/join and passwords. Technically makes the user invisible to others. - return 1 - - if(C.password) - var/mob/living/user = usr - var/password = reject_bad_text(input(user,"Access Denied. Enter password:")) - if(C && (password == C.password)) - C.add_client(src) - channel = C - return 1 - C.add_client(src) - channel = C - if("PRG_leavechannel") - . = 1 - if(channel) - channel.remove_client(src) - channel = null - if("PRG_newchannel") - . = 1 - var/mob/living/user = usr - var/channel_title = reject_bad_text(input(user,"Enter channel name or leave blank to cancel:")) - if(!channel_title) - return 1 - var/datum/ntnet_conversation/C = new/datum/ntnet_conversation() - C.add_client(src) - C.operator = src - channel = C - C.title = channel_title - if("PRG_toggleadmin") - . = 1 - if(netadmin_mode) - netadmin_mode = 0 - if(channel) - channel.remove_client(src) // We shouldn't be in channel's user list, but just in case... - channel = null - return 1 - var/mob/living/user = usr - if(can_run(user, 1, ACCESS_NETWORK)) - if(channel) - var/response = alert(user, "Really engage admin-mode? You will be disconnected from your current channel!", "NTNRC Admin mode", "Yes", "No") - if(response == "Yes") - if(channel) - channel.remove_client(src) - channel = null - else - return 1 - netadmin_mode = 1 - if("PRG_changename") - . = 1 - var/mob/living/user = usr - var/newname = sanitize(input(user,"Enter new nickname or leave blank to cancel:")) - if(!newname) - return 1 - if(channel) - channel.add_status_message("[username] is now known as [newname].") - username = newname - - if("PRG_savelog") - . = 1 - if(!channel) - return 1 - var/mob/living/user = usr - var/logname = input(user,"Enter desired logfile name (.log) or leave blank to cancel:") - if(!logname || !channel) - return 1 - var/datum/computer_file/data/logfile = new/datum/computer_file/data/logfile() - // Now we will generate HTML-compliant file that can actually be viewed/printed. - logfile.filename = logname - logfile.stored_data = "\[b\]Logfile dump from NTNRC channel [channel.title]\[/b\]\[BR\]" - for(var/logstring in channel.messages) - logfile.stored_data += "[logstring]\[BR\]" - logfile.stored_data += "\[b\]Logfile dump completed.\[/b\]" - logfile.calculate_size() - var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - if(!computer || !hard_drive || !hard_drive.store_file(logfile)) - if(!computer) - // This program shouldn't even be runnable without computer. - CRASH("Var computer is null!") - if(!hard_drive) - computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.") - else // In 99.9% cases this will mean our HDD is full - computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [logfile.size]GQ\" warning.") - if("PRG_renamechannel") - . = 1 - if(!operator_mode || !channel) - return 1 - var/mob/living/user = usr - var/newname = reject_bad_text(input(user, "Enter new channel name or leave blank to cancel:")) - if(!newname || !channel) - return 1 - channel.add_status_message("Channel renamed from [channel.title] to [newname] by operator.") - channel.title = newname - if("PRG_deletechannel") - . = 1 - if(channel && ((channel.operator == src) || netadmin_mode)) - QDEL_NULL(channel) - if("PRG_setpassword") - . = 1 - if(!channel || ((channel.operator != src) && !netadmin_mode)) - return 1 - - var/mob/living/user = usr - var/newpassword = sanitize(input(user, "Enter new password for this channel. Leave blank to cancel, enter 'nopassword' to remove password completely:")) - if(!channel || !newpassword || ((channel.operator != src) && !netadmin_mode)) - return 1 - - if(newpassword == "nopassword") - channel.password = "" - else - channel.password = newpassword diff --git a/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm deleted file mode 100644 index 1ccefa9311a..00000000000 --- a/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm +++ /dev/null @@ -1,195 +0,0 @@ -GLOBAL_VAR_INIT(nttransfer_uid, 0) - -/datum/computer_file/program/nttransfer - filename = "nttransfer" - filedesc = "NTNet P2P Transfer Client" - extended_desc = "This program allows for simple file transfer via direct peer to peer connection." - program_icon_state = "comm_logs" - size = 7 - requires_ntnet = 1 - requires_ntnet_feature = NTNET_PEERTOPEER - network_destination = "other device via P2P tunnel" - available_on_ntnet = 1 - - var/error = "" // Error screen - var/server_password = "" // Optional password to download the file. - var/datum/computer_file/provided_file = null // File which is provided to clients. - var/datum/computer_file/downloaded_file = null // File which is being downloaded - var/list/connected_clients = list() // List of connected clients. - var/datum/computer_file/program/nttransfer/remote // Client var, specifies who are we downloading from. - var/download_completion = 0 // Download progress in GQ - var/download_netspeed = 0 // Our connectivity speed in GQ/s - var/actual_netspeed = 0 // Displayed in the UI, this is the actual transfer speed. - var/unique_token // UID of this program - var/upload_menu = 0 // Whether we show the program list and upload menu - -/datum/computer_file/program/nttransfer/New() - unique_token = GLOB.nttransfer_uid - GLOB.nttransfer_uid++ - ..() - -/datum/computer_file/program/nttransfer/process_tick() - // Server mode - update_netspeed() - if(provided_file) - for(var/datum/computer_file/program/nttransfer/C in connected_clients) - // Transfer speed is limited by device which uses slower connectivity. - // We can have multiple clients downloading at same time, but let's assume we use some sort of multicast transfer - // so they can all run on same speed. - C.actual_netspeed = min(C.download_netspeed, download_netspeed) - C.download_completion += C.actual_netspeed - if(C.download_completion >= provided_file.size) - C.finish_download() - else if(downloaded_file) // Client mode - if(!remote) - crash_download("Connection to remote server lost") - -/datum/computer_file/program/nttransfer/kill_program(forced = FALSE) - if(downloaded_file) // Client mode, clean up variables for next use - finalize_download() - - if(provided_file) // Server mode, disconnect all clients - for(var/datum/computer_file/program/nttransfer/P in connected_clients) - P.crash_download("Connection terminated by remote server") - downloaded_file = null - ..(forced) - -/datum/computer_file/program/nttransfer/proc/update_netspeed() - download_netspeed = 0 - switch(ntnet_status) - if(1) - download_netspeed = NTNETSPEED_LOWSIGNAL - if(2) - download_netspeed = NTNETSPEED_HIGHSIGNAL - if(3) - download_netspeed = NTNETSPEED_ETHERNET - -// Finishes download and attempts to store the file on HDD -/datum/computer_file/program/nttransfer/proc/finish_download() - var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - if(!computer || !hard_drive || !hard_drive.store_file(downloaded_file)) - error = "I/O Error: Unable to save file. Check your hard drive and try again." - finalize_download() - -// Crashes the download and displays specific error message -/datum/computer_file/program/nttransfer/proc/crash_download(var/message) - error = message ? message : "An unknown error has occurred during download" - finalize_download() - -// Cleans up variables for next use -/datum/computer_file/program/nttransfer/proc/finalize_download() - if(remote) - remote.connected_clients.Remove(src) - downloaded_file = null - remote = null - download_completion = 0 - - -/datum/computer_file/program/nttransfer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "ntnet_transfer.tmpl", "NTNet P2P Transfer Client", 575, 700) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/nttransfer/Topic(href, list/href_list) - if(..()) - return 1 - switch(href_list["action"]) - if("PRG_downloadfile") - for(var/datum/computer_file/program/nttransfer/P in GLOB.ntnet_global.fileservers) - if("[P.unique_token]" == href_list["id"]) - remote = P - break - if(!remote || !remote.provided_file) - return - if(remote.server_password) - var/pass = reject_bad_text(input(usr, "Code 401 Unauthorized. Please enter password:", "Password required")) - if(pass != remote.server_password) - error = "Incorrect Password" - return 1 - downloaded_file = remote.provided_file.clone() - remote.connected_clients.Add(src) - return 1 - if("PRG_reset") - error = "" - upload_menu = 0 - finalize_download() - if(src in GLOB.ntnet_global.fileservers) - GLOB.ntnet_global.fileservers.Remove(src) - for(var/datum/computer_file/program/nttransfer/T in connected_clients) - T.crash_download("Remote server has forcibly closed the connection") - provided_file = null - return 1 - if("PRG_setpassword") - var/pass = reject_bad_text(input(usr, "Enter new server password. Leave blank to cancel, input 'none' to disable password.", "Server security", "none")) - if(!pass) - return - if(pass == "none") - server_password = "" - return - server_password = pass - return 1 - if("PRG_uploadfile") - var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - for(var/datum/computer_file/F in hard_drive.stored_files) - if("[F.uid]" == href_list["id"]) - if(F.unsendable) - error = "I/O Error: File locked." - return - if(istype(F, /datum/computer_file/program)) - var/datum/computer_file/program/P = F - if(!P.can_run(usr,transfer = 1)) - error = "Access Error: Insufficient rights to upload file." - provided_file = F - GLOB.ntnet_global.fileservers.Add(src) - return - error = "I/O Error: Unable to locate file on hard drive." - return 1 - if("PRG_uploadmenu") - upload_menu = 1 - - -/datum/computer_file/program/nttransfer/ui_data(mob/user) - - var/list/data = get_header_data() - - if(error) - data["error"] = error - else if(downloaded_file) - data["downloading"] = 1 - data["download_size"] = downloaded_file.size - data["download_progress"] = download_completion - data["download_netspeed"] = actual_netspeed - data["download_name"] = "[downloaded_file.filename].[downloaded_file.filetype]" - else if (provided_file) - data["uploading"] = 1 - data["upload_uid"] = unique_token - data["upload_clients"] = connected_clients.len - data["upload_haspassword"] = server_password ? 1 : 0 - data["upload_filename"] = "[provided_file.filename].[provided_file.filetype]" - else if (upload_menu) - var/list/all_files[0] - var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD] - for(var/datum/computer_file/F in hard_drive.stored_files) - all_files.Add(list(list( - "uid" = F.uid, - "filename" = "[F.filename].[F.filetype]", - "size" = F.size - ))) - data["upload_filelist"] = all_files - else - var/list/all_servers[0] - for(var/datum/computer_file/program/nttransfer/P in GLOB.ntnet_global.fileservers) - all_servers.Add(list(list( - "uid" = P.unique_token, - "filename" = "[P.provided_file.filename].[P.provided_file.filetype]", - "size" = P.provided_file.size, - "haspassword" = P.server_password ? 1 : 0 - ))) - data["servers"] = all_servers - - return data diff --git a/code/modules/modular_computers/file_system/programs/research/airestorer.dm b/code/modules/modular_computers/file_system/programs/research/airestorer.dm deleted file mode 100644 index a87a2cd87b4..00000000000 --- a/code/modules/modular_computers/file_system/programs/research/airestorer.dm +++ /dev/null @@ -1,137 +0,0 @@ -/datum/computer_file/program/aidiag - filename = "aidiag" - filedesc = "AI Maintenance Utility" - program_icon_state = "generic" - extended_desc = "This program is capable of reconstructing damaged AI systems. Requires direct AI connection via intellicard slot." - size = 12 - requires_ntnet = 0 - usage_flags = PROGRAM_CONSOLE - transfer_access = ACCESS_HEADS - available_on_ntnet = 1 - var/restoring = FALSE - -/datum/computer_file/program/aidiag/proc/get_ai(cardcheck) - - var/obj/item/computer_hardware/ai_slot/ai_slot - - if(computer) - ai_slot = computer.all_components[MC_AI] - - if(computer && ai_slot && ai_slot.check_functionality()) - if(cardcheck == 1) - return ai_slot - if(ai_slot.enabled && ai_slot.stored_card) - if(cardcheck == 2) - return ai_slot.stored_card - if(locate(/mob/living/silicon/ai) in ai_slot.stored_card) - return locate(/mob/living/silicon/ai) in ai_slot.stored_card - - return null - -/datum/computer_file/program/aidiag/Topic(href, list/href_list) - if(..()) - return TRUE - - var/mob/living/silicon/ai/A = get_ai() - if(!A) - restoring = FALSE - - switch(href_list["action"]) - if("PRG_beginReconstruction") - if(A && A.health < 100) - restoring = TRUE - return TRUE - if("PRG_eject") - if(computer.all_components[MC_AI]) - var/obj/item/computer_hardware/ai_slot/ai_slot = computer.all_components[MC_AI] - if(ai_slot && ai_slot.stored_card) - ai_slot.try_eject(0,usr) - return TRUE - -/datum/computer_file/program/aidiag/process_tick() - ..() - if(!restoring) //Put the check here so we don't check for an ai all the time - return - var/obj/item/aicard/cardhold = get_ai(2) - - var/obj/item/computer_hardware/ai_slot/ai_slot = get_ai(1) - - - var/mob/living/silicon/ai/A = get_ai() - if(!A || !cardhold) - restoring = FALSE // If the AI was removed, stop the restoration sequence. - if(ai_slot) - ai_slot.locked = FALSE - return - - if(cardhold.flush) - ai_slot.locked = FALSE - restoring = FALSE - return - ai_slot.locked = TRUE - A.adjustOxyLoss(-1) - A.adjustFireLoss(-1) - A.adjustToxLoss(-1) - A.adjustBruteLoss(-1) - A.updatehealth() - if(A.health >= 0 && A.stat == DEAD) - A.stat = CONSCIOUS - A.lying = 0 - GLOB.dead_mob_list -= A - GLOB.alive_mob_list += A - // Finished restoring - if(A.health >= 100) - ai_slot.locked = FALSE - restoring = FALSE - - return TRUE - - -/datum/computer_file/program/aidiag/ui_data(mob/user) - var/list/data = get_header_data() - var/mob/living/silicon/ai/AI - // A shortcut for getting the AI stored inside the computer. The program already does necessary checks. - AI = get_ai() - - var/obj/item/aicard/aicard = get_ai(2) - - if(!aicard) - data["nocard"] = TRUE - data["error"] = "Please insert an intelliCard." - else - if(!AI) - data["error"] = "No AI located" - else - var/obj/item/aicard/cardhold = AI.loc - if(cardhold.flush) - data["error"] = "Flush in progress" - else - data["name"] = AI.name - data["restoring"] = restoring - data["health"] = (AI.health + 100) / 2 - data["isDead"] = AI.stat == DEAD - - var/list/all_laws[0] - for(var/datum/ai_law/L in AI.laws.all_laws()) - all_laws.Add(list(list( - "index" = L.index, - "text" = L.law - ))) - - data["ai_laws"] = all_laws - - return data - -/datum/computer_file/program/aidiag/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "ai_restorer.tmpl", "Integrity Restorer", 600, 400) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/aidiag/kill_program(forced) - restoring = FALSE - return ..(forced) diff --git a/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm deleted file mode 100644 index 0a839a6d13f..00000000000 --- a/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm +++ /dev/null @@ -1,90 +0,0 @@ -/datum/computer_file/program/ntnetmonitor - filename = "ntmonitor" - filedesc = "NTNet Diagnostics and Monitoring" - program_icon_state = "comm_monitor" - extended_desc = "This program monitors stationwide NTNet network, provides access to logging systems, and allows for configuration changes" - size = 12 - requires_ntnet = 1 - required_access = ACCESS_NETWORK //Network control is a more secure program. - available_on_ntnet = 1 - -/datum/computer_file/program/ntnetmonitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers) - assets.send(user) - ui = new(user, src, ui_key, "ntnet_monitor.tmpl", "NTNet Diagnostics and Monitoring Tool", 575, 700) - ui.set_auto_update(1) - ui.set_layout_key("program") - ui.open() - -/datum/computer_file/program/ntnetmonitor/Topic(href, list/href_list) - if(..()) - return 1 - switch(href_list["action"]) - if("resetIDS") - . = 1 - if(GLOB.ntnet_global) - GLOB.ntnet_global.resetIDS() - return 1 - if("toggleIDS") - . = 1 - if(GLOB.ntnet_global) - GLOB.ntnet_global.toggleIDS() - return 1 - if("toggleWireless") - . = 1 - if(!GLOB.ntnet_global) - return 1 - - // NTNet is disabled. Enabling can be done without user prompt - if(GLOB.ntnet_global.setting_disabled) - GLOB.ntnet_global.setting_disabled = 0 - return 1 - - // NTNet is enabled and user is about to shut it down. Let's ask them if they really want to do it, as wirelessly connected computers won't connect without NTNet being enabled (which may prevent people from turning it back on) - var/mob/user = usr - if(!user) - return 1 - var/response = alert(user, "Really disable NTNet wireless? If your computer is connected wirelessly you won't be able to turn it back on! This will affect all connected wireless devices.", "NTNet shutdown", "Yes", "No") - if(response == "Yes") - GLOB.ntnet_global.setting_disabled = 1 - return 1 - if("purgelogs") - . = 1 - if(GLOB.ntnet_global) - GLOB.ntnet_global.purge_logs() - if("updatemaxlogs") - . = 1 - var/mob/user = usr - var/logcount = text2num(input(user,"Enter amount of logs to keep in memory ([MIN_NTNET_LOGS]-[MAX_NTNET_LOGS]):")) - if(GLOB.ntnet_global) - GLOB.ntnet_global.update_max_log_count(logcount) - if("toggle_function") - . = 1 - if(!GLOB.ntnet_global) - return 1 - GLOB.ntnet_global.toggle_function(text2num(href_list["id"])) - -/datum/computer_file/program/ntnetmonitor/ui_data(mob/user) - if(!GLOB.ntnet_global) - return - var/list/data = get_header_data() - - data["ntnetstatus"] = GLOB.ntnet_global.check_function() - data["ntnetrelays"] = GLOB.ntnet_global.relays.len - data["idsstatus"] = GLOB.ntnet_global.intrusion_detection_enabled - data["idsalarm"] = GLOB.ntnet_global.intrusion_detection_alarm - - data["config_softwaredownload"] = GLOB.ntnet_global.setting_softwaredownload - data["config_peertopeer"] = GLOB.ntnet_global.setting_peertopeer - data["config_communication"] = GLOB.ntnet_global.setting_communication - data["config_systemcontrol"] = GLOB.ntnet_global.setting_systemcontrol - - data["ntnetlogs"] = list() - - for(var/i in GLOB.ntnet_global.logs) - data["ntnetlogs"] += list(list("entry" = i)) - data["ntnetmaxlogs"] = GLOB.ntnet_global.setting_maxlogcount - - return data diff --git a/code/modules/modular_computers/hardware/CPU.dm b/code/modules/modular_computers/hardware/CPU.dm deleted file mode 100644 index 02e7d41d512..00000000000 --- a/code/modules/modular_computers/hardware/CPU.dm +++ /dev/null @@ -1,44 +0,0 @@ -// CPU that allows the computer to run programs. -// Better CPUs are obtainable via research and can run more programs on background. - -/obj/item/computer_hardware/processor_unit - name = "processor board" - desc = "A standard CPU board used in most computers. It can run up to three programs simultaneously." - icon_state = "cpuboard" - w_class = WEIGHT_CLASS_SMALL - power_usage = 50 - critical = 1 - malfunction_probability = 1 - origin_tech = "programming=3;engineering=2" - var/max_idle_programs = 2 // 2 idle, + 1 active = 3 as said in description. - device_type = MC_CPU - -/obj/item/computer_hardware/processor_unit/on_remove(obj/item/modular_computer/MC, mob/user) - MC.shutdown_computer() - -/obj/item/computer_hardware/processor_unit/small - name = "microprocessor" - desc = "A miniaturised CPU used in portable devices. It can run up to two programs simultaneously." - icon_state = "cpu" - w_class = WEIGHT_CLASS_TINY - power_usage = 25 - max_idle_programs = 1 - origin_tech = "programming=2;engineering=2" - -/obj/item/computer_hardware/processor_unit/photonic - name = "photonic processor board" - desc = "An advanced experimental CPU board that uses photonic core instead of regular circuitry. It can run up to five programs simultaneously, but uses a lot of power." - icon_state = "cpuboard_super" - w_class = WEIGHT_CLASS_SMALL - power_usage = 250 - max_idle_programs = 4 - origin_tech = "programming=5;engineering=4" - -/obj/item/computer_hardware/processor_unit/photonic/small - name = "photonic microprocessor" - desc = "An advanced miniaturised CPU for use in portable devices. It uses photonic core instead of regular circuitry. It can run up to three programs simultaneously." - icon_state = "cpu_super" - w_class = WEIGHT_CLASS_TINY - power_usage = 75 - max_idle_programs = 2 - origin_tech = "programming=4;engineering=3" diff --git a/code/modules/modular_computers/hardware/_hardware.dm b/code/modules/modular_computers/hardware/_hardware.dm deleted file mode 100644 index 3e82455c53b..00000000000 --- a/code/modules/modular_computers/hardware/_hardware.dm +++ /dev/null @@ -1,105 +0,0 @@ -/obj/item/computer_hardware - name = "hardware" - desc = "Unknown Hardware." - icon = 'icons/obj/module.dmi' - icon_state = "std_mod" - - w_class = WEIGHT_CLASS_TINY // w_class limits which devices can contain this component. - // 1: PDAs/Tablets, 2: Laptops, 3-4: Consoles only - var/obj/item/modular_computer/holder = null - // Computer that holds this hardware, if any. - - max_integrity = 200 - - var/power_usage = 0 // If the hardware uses extra power, change this. - var/enabled = 1 // If the hardware is turned off set this to 0. - var/critical = 0 // Prevent disabling for important component, like the CPU. - var/can_install = 1 // Prevents direct installation of removable media. - var/damage = 0 // Current damage level - var/max_damage = 100 // Maximal damage level. - var/damage_malfunction = 20 // "Malfunction" threshold. When damage exceeds this value the hardware piece will semi-randomly fail and do !!FUN!! things - var/damage_failure = 50 // "Failure" threshold. When damage exceeds this value the hardware piece will not work at all. - var/malfunction_probability = 10// Chance of malfunction when the component is damaged - var/device_type - -/obj/item/computer_hardware/New(var/obj/L) - ..() - pixel_x = rand(-8, 8) - pixel_y = rand(-8, 8) - -/obj/item/computer_hardware/Destroy() - if(holder) - holder.uninstall_component(src) - return ..() - - -/obj/item/computer_hardware/attackby(obj/item/I, mob/living/user) - // Multitool. Runs diagnostics - if(istype(I, /obj/item/multitool)) - to_chat(user, "***** DIAGNOSTICS REPORT *****") - diagnostics(user) - to_chat(user, "******************************") - return 1 - - // Cable coil. Works as repair method, but will probably require multiple applications and more cable. - if(istype(I, /obj/item/stack/cable_coil)) - var/obj/item/stack/S = I - if(obj_integrity == max_integrity) - to_chat(user, "\The [src] doesn't seem to require repairs.") - return 1 - if(S.use(1)) - to_chat(user, "You patch up \the [src] with a bit of \the [I].") - obj_integrity = min(obj_integrity + 10, max_integrity) - return 1 - - if(try_insert(I, user)) - return 1 - - return ..() - -// Called on multitool click, prints diagnostic information to the user. -/obj/item/computer_hardware/proc/diagnostics(var/mob/user) - to_chat(user, "Hardware Integrity Test... (Corruption: [damage]/[max_damage]) [damage > damage_failure ? "FAIL" : damage > damage_malfunction ? "WARN" : "PASS"]") - -// Handles damage checks -/obj/item/computer_hardware/proc/check_functionality() - if(!enabled) // Disabled. - return FALSE - - if(damage > damage_failure) // Too damaged to work at all. - return FALSE - - if(damage > damage_malfunction) // Still working. Well, sometimes... - if(prob(malfunction_probability)) - return FALSE - - return TRUE // Good to go. - -/obj/item/computer_hardware/examine(var/mob/user) - . = ..() - if(damage > damage_failure) - . += "It seems to be severely damaged!" - else if(damage > damage_malfunction) - . += "It seems to be damaged!" - else if(damage) - . += "It seems to be slightly damaged." - -// Component-side compatibility check. -/obj/item/computer_hardware/proc/can_install(obj/item/modular_computer/M, mob/living/user = null) - return can_install - -// Called when component is installed into PC. -/obj/item/computer_hardware/proc/on_install(obj/item/modular_computer/M, mob/living/user = null) - return - -// Called when component is removed from PC. -/obj/item/computer_hardware/proc/on_remove(obj/item/modular_computer/M, mob/living/user = null) - try_eject(forced = 1) - -// Called when someone tries to insert something in it - paper in printer, card in card reader, etc. -/obj/item/computer_hardware/proc/try_insert(obj/item/I, mob/living/user = null) - return FALSE - -// Called when someone tries to eject something from it - card from card reader, etc. -/obj/item/computer_hardware/proc/try_eject(slot=0, mob/living/user = null, forced = 0) - return FALSE diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm deleted file mode 100644 index 1b97ae5ede8..00000000000 --- a/code/modules/modular_computers/hardware/ai_slot.dm +++ /dev/null @@ -1,80 +0,0 @@ -/obj/item/computer_hardware/ai_slot - name = "intelliCard interface slot" - desc = "A module allowing this computer to interface with most common intelliCard modules. Necessary for some programs to run properly." - power_usage = 100 //W - icon_state = "card_mini" - w_class = WEIGHT_CLASS_SMALL - origin_tech = "programming=2" - device_type = MC_AI - - var/obj/item/aicard/stored_card = null - var/locked = FALSE - -/obj/item/computer_hardware/ai_slot/Destroy() - QDEL_NULL(stored_card) - return ..() - - -/obj/item/computer_hardware/ai_slot/examine(mob/user) - . = ..() - if(stored_card) - . += "There appears to be an intelliCard loaded. There appears to be a pinhole protecting a manual eject button. A screwdriver could probably press it" - -/obj/item/computer_hardware/ai_slot/on_install(obj/item/modular_computer/M, mob/living/user = null) - M.add_verb(device_type) - -/obj/item/computer_hardware/ai_slot/on_remove(obj/item/modular_computer/M, mob/living/user = null) - M.remove_verb(device_type) - try_eject(0, forced = 1) - -/obj/item/computer_hardware/ai_slot/try_insert(obj/item/I, mob/living/user = null) - if(!holder) - return FALSE - - if(!istype(I, /obj/item/aicard)) - return FALSE - - if(stored_card) - if(user) - to_chat(user, "You try to insert \the [I] into \the [src], but the slot is occupied.") - return FALSE - if(user && !user.unEquip(I)) - return FALSE - - I.forceMove(src) - stored_card = I - if(user) - to_chat(user, "You insert \the [I] into \the [src].") - - return TRUE - - -/obj/item/computer_hardware/ai_slot/try_eject(slot=0, mob/living/user = null, forced = 0) - if(!stored_card) - if(user) - to_chat(user, "There is no card in \the [src].") - return FALSE - - if(locked && !forced) - if(user) - to_chat(user, "Safeties prevent you from removing the card until reconstruction is complete...") - return FALSE - - if(stored_card) - stored_card.forceMove(get_turf(src)) - locked = FALSE - stored_card.verb_pickup() - stored_card = null - - if(user) - to_chat(user, "You remove the card from \the [src].") - return TRUE - return FALSE - -/obj/item/computer_hardware/ai_slot/attackby(obj/item/I, mob/living/user) - if(..()) - return - if(istype(I, /obj/item/screwdriver)) - to_chat(user, "You press down on the manual eject button with \the [I].") - try_eject(0, user, 1) - return diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm deleted file mode 100644 index 1a48110006a..00000000000 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ /dev/null @@ -1,108 +0,0 @@ -/obj/item/computer_hardware/battery - name = "power cell controller" - desc = "A charge controller for standard power cells, used in all kinds of modular computers." - icon_state = "cell_con" - critical = 1 - malfunction_probability = 1 - origin_tech = "powerstorage=1;engineering=1" - var/obj/item/stock_parts/cell/battery = null - device_type = MC_CELL - -/obj/item/computer_hardware/battery/get_cell() - return battery - -/obj/item/computer_hardware/battery/New(loc, battery_type = null) - if(battery_type) - battery = new battery_type(src) - ..() - -/obj/item/computer_hardware/battery/Destroy() - QDEL_NULL(battery) - return ..() - -/obj/item/computer_hardware/battery/on_remove(obj/item/modular_computer/M, mob/living/user = null) - try_eject(0, forced = 1) - -/obj/item/computer_hardware/battery/try_insert(obj/item/I, mob/living/user = null) - if(!holder) - return FALSE - - if(!istype(I, /obj/item/stock_parts/cell)) - return FALSE - - if(battery) - if(user) - to_chat(user, "You try to connect \the [I] to \the [src], but its connectors are occupied.") - return FALSE - - if(I.w_class > holder.max_hardware_size) - if(user) - to_chat(user, "This power cell is too large for \the [holder]!") - return FALSE - - if(user && !user.unEquip(I)) - return FALSE - - I.forceMove(src) - battery = I - if(user) - to_chat(user, "You connect \the [I] to \the [src].") - - return TRUE - - -/obj/item/computer_hardware/battery/try_eject(slot=0, mob/living/user = null, forced = 0) - if(!battery) - if(user) - to_chat(user, "There is no power cell connected to \the [src].") - return FALSE - else - battery.forceMove(get_turf(src)) - if(user) - to_chat(user, "You detach \the [battery] from \the [src].") - battery = null - - if(holder) - if(holder.enabled && !holder.use_power()) - holder.shutdown_computer() - - return TRUE - - -// Stock parts -/obj/item/stock_parts/cell/computer - name = "standard battery" - desc = "A standard power cell, commonly seen in high-end portable microcomputers or low-end laptops." - icon = 'icons/obj/module.dmi' - icon_state = "cell_mini" - origin_tech = "powerstorage=2;engineering=1" - w_class = WEIGHT_CLASS_TINY - maxcharge = 750 - -/obj/item/stock_parts/cell/computer/advanced - name = "advanced battery" - desc = "An advanced power cell, often used in most laptops. It is too large to be fitted into smaller devices." - icon_state = "cell" - origin_tech = "powerstorage=2;engineering=2" - maxcharge = 1500 - -/obj/item/stock_parts/cell/computer/super - name = "super battery" - desc = "An advanced power cell, often used in high-end laptops." - icon_state = "cell" - origin_tech = "powerstorage=3;engineering=3" - maxcharge = 2000 - -/obj/item/stock_parts/cell/computer/micro - name = "micro battery" - desc = "A small power cell, commonly seen in most portable microcomputers." - icon_state = "cell_micro" - w_class = WEIGHT_CLASS_TINY - maxcharge = 500 - -/obj/item/stock_parts/cell/computer/nano - name = "nano battery" - desc = "A tiny power cell, commonly seen in low-end portable microcomputers." - icon_state = "cell_micro" - w_class = WEIGHT_CLASS_TINY - maxcharge = 300 diff --git a/code/modules/modular_computers/hardware/card_slot.dm b/code/modules/modular_computers/hardware/card_slot.dm deleted file mode 100644 index fc1abc207a5..00000000000 --- a/code/modules/modular_computers/hardware/card_slot.dm +++ /dev/null @@ -1,112 +0,0 @@ -/obj/item/computer_hardware/card_slot - name = "identification card authentication module" // \improper breaks the find_hardware_by_name proc - desc = "A module allowing this computer to read or write data on ID cards. Necessary for some programs to run properly." - power_usage = 10 //W - icon_state = "card_mini" - w_class = WEIGHT_CLASS_TINY - origin_tech = "programming=2" - device_type = MC_CARD - - var/obj/item/card/id/stored_card = null - var/obj/item/card/id/stored_card2 = null - -/obj/item/computer_hardware/card_slot/Destroy() - QDEL_NULL(stored_card) - QDEL_NULL(stored_card2) - return ..() - -/obj/item/computer_hardware/card_slot/GetAccess() - if(stored_card && stored_card2) // Best of both worlds - return (stored_card.GetAccess() | stored_card2.GetAccess()) - else if(stored_card) - return stored_card.GetAccess() - else if(stored_card2) - return stored_card2.GetAccess() - return ..() - -/obj/item/computer_hardware/card_slot/GetID() - if(stored_card) - return stored_card - else if(stored_card2) - return stored_card2 - return ..() - -/obj/item/computer_hardware/card_slot/on_install(obj/item/modular_computer/M, mob/living/user = null) - M.add_verb(device_type) - -/obj/item/computer_hardware/card_slot/on_remove(obj/item/modular_computer/M, mob/living/user = null) - M.remove_verb(device_type) - try_eject(0, forced = 1) - -/obj/item/computer_hardware/card_slot/try_insert(obj/item/I, mob/living/user = null) - if(!holder) - return FALSE - - if(!istype(I, /obj/item/card/id)) - return FALSE - - if(stored_card && stored_card2) - if(user) - to_chat(user, "You try to insert \the [I] into \the [src], but its slots are occupied.") - return FALSE - if(user && !user.unEquip(I)) - return FALSE - - I.forceMove(src) - - if(!stored_card) - stored_card = I - else - stored_card2 = I - - if(user) - to_chat(user, "You insert \the [I] into \the [src].") - - return TRUE - - -/obj/item/computer_hardware/card_slot/try_eject(slot = 0, mob/living/user = null, forced = 0) - if(!stored_card && !stored_card2) - if(user) - to_chat(user, "There are no cards in \the [src].") - return FALSE - - var/ejected = 0 - if(stored_card && (!slot || slot == 1)) - stored_card.forceMove(get_turf(src)) - stored_card.verb_pickup() - stored_card = null - ejected++ - - if(stored_card2 && (!slot || slot == 2)) - stored_card2.forceMove(get_turf(src)) - stored_card2.verb_pickup() - stored_card2 = null - ejected++ - - if(ejected) - if(holder) - if(holder.active_program) - holder.active_program.event_idremoved(0, slot) - - for(var/I in holder.idle_threads) - var/datum/computer_file/program/P = I - P.event_idremoved(1, slot) - - if(user) - to_chat(user, "You remove the card[ejected>1 ? "s" : ""] from \the [src].") - return TRUE - return FALSE - -/obj/item/computer_hardware/card_slot/attackby(obj/item/I, mob/living/user) - if(..()) - return - if(istype(I, /obj/item/screwdriver)) - to_chat(user, "You press down on the manual eject button with \the [I].") - try_eject(0,user) - return - -/obj/item/computer_hardware/card_slot/examine(mob/user) - . = ..() - if(stored_card || stored_card2) - . += "There appears to be something loaded in the card slots." diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm deleted file mode 100644 index ce7fc997192..00000000000 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ /dev/null @@ -1,173 +0,0 @@ -/obj/item/computer_hardware/hard_drive - name = "hard disk drive" - desc = "A small HDD, for use in basic computers where power efficiency is desired." - power_usage = 25 - icon_state = "harddisk_mini" - critical = 1 - w_class = WEIGHT_CLASS_TINY - origin_tech = "programming=1;engineering=1" - device_type = MC_HDD - var/max_capacity = 128 - var/used_capacity = 0 - var/list/stored_files = list() // List of stored files on this drive. DO NOT MODIFY DIRECTLY! - -/obj/item/computer_hardware/hard_drive/on_remove(obj/item/modular_computer/MC, mob/user) - MC.shutdown_computer() - -/obj/item/computer_hardware/hard_drive/proc/install_default_programs() - store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar - store_file(new/datum/computer_file/program/ntnetdownload(src)) // NTNet Downloader Utility, allows users to download more software from NTNet repository - store_file(new/datum/computer_file/program/filemanager(src)) // File manager, allows text editor functions and basic file manipulation. - -/obj/item/computer_hardware/hard_drive/examine(user) - . = ..() - . += "It has [max_capacity] GQ of storage capacity." - -/obj/item/computer_hardware/hard_drive/diagnostics(var/mob/user) - ..() - // 999 is a byond limit that is in place. It's unlikely someone will reach that many files anyway, since you would sooner run out of space. - to_chat(user, "NT-NFS File Table Status: [stored_files.len]/999") - to_chat(user, "Storage capacity: [used_capacity]/[max_capacity]GQ") - -// Use this proc to add file to the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. -/obj/item/computer_hardware/hard_drive/proc/store_file(var/datum/computer_file/F) - if(!F || !istype(F)) - return 0 - - if(!can_store_file(F)) - return 0 - - if(!check_functionality()) - return 0 - - if(!stored_files) - return 0 - - // This file is already stored. Don't store it again. - if(F in stored_files) - return 0 - - F.holder = src - stored_files.Add(F) - recalculate_size() - return 1 - -// Use this proc to remove file from the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. -/obj/item/computer_hardware/hard_drive/proc/remove_file(var/datum/computer_file/F) - if(!F || !istype(F)) - return 0 - - if(!stored_files) - return 0 - - if(!check_functionality()) - return 0 - - if(F in stored_files) - stored_files -= F - recalculate_size() - return 1 - else - return 0 - -// Loops through all stored files and recalculates used_capacity of this drive -/obj/item/computer_hardware/hard_drive/proc/recalculate_size() - var/total_size = 0 - for(var/datum/computer_file/F in stored_files) - total_size += F.size - - used_capacity = total_size - -// Checks whether file can be stored on the hard drive. We can only store unique files, so this checks whether we wouldn't get a duplicity by adding a file. -/obj/item/computer_hardware/hard_drive/proc/can_store_file(var/datum/computer_file/F) - if(!F || !istype(F)) - return 0 - - if(F in stored_files) - return 0 - - var/name = F.filename + "." + F.filetype - for(var/datum/computer_file/file in stored_files) - if((file.filename + "." + file.filetype) == name) - return 0 - - // In the unlikely event someone manages to create that many files. - // BYOND is acting weird with numbers above 999 in loops (infinite loop prevention) - if(stored_files.len >= 999) - return 0 - if((used_capacity + F.size) > max_capacity) - return 0 - else - return 1 - - -// Tries to find the file by filename. Returns null on failure -/obj/item/computer_hardware/hard_drive/proc/find_file_by_name(var/filename) - if(!check_functionality()) - return null - - if(!filename) - return null - - if(!stored_files) - return null - - for(var/datum/computer_file/F in stored_files) - if(F.filename == filename) - return F - return null - -/obj/item/computer_hardware/hard_drive/Destroy() - stored_files = null - return ..() - -/obj/item/computer_hardware/hard_drive/New() - install_default_programs() - ..() - - -/obj/item/computer_hardware/hard_drive/advanced - name = "advanced hard disk drive" - desc = "A hybrid HDD, for use in higher grade computers where balance between power efficiency and capacity is desired." - max_capacity = 256 - origin_tech = "programming=2;engineering=2" - power_usage = 50 // Hybrid, medium capacity and medium power storage - icon_state = "harddisk_mini" - w_class = WEIGHT_CLASS_SMALL - -/obj/item/computer_hardware/hard_drive/super - name = "super hard disk drive" - desc = "A high capacity HDD, for use in cluster storage solutions where capacity is more important than power efficiency." - max_capacity = 512 - origin_tech = "programming=3;engineering=3" - power_usage = 100 // High-capacity but uses lots of power, shortening battery life. Best used with APC link. - icon_state = "harddisk_mini" - w_class = WEIGHT_CLASS_SMALL - -/obj/item/computer_hardware/hard_drive/cluster - name = "cluster hard disk drive" - desc = "A large storage cluster consisting of multiple HDDs for usage in dedicated storage systems." - power_usage = 500 - origin_tech = "programming=4;engineering=4" - max_capacity = 2048 - icon_state = "harddisk" - w_class = WEIGHT_CLASS_NORMAL - -// For tablets, etc. - highly power efficient. -/obj/item/computer_hardware/hard_drive/small - name = "solid state drive" - desc = "An efficient SSD for portable devices." - power_usage = 10 - origin_tech = "programming=2;engineering=2" - max_capacity = 64 - icon_state = "ssd_mini" - w_class = WEIGHT_CLASS_TINY - -/obj/item/computer_hardware/hard_drive/micro - name = "micro solid state drive" - desc = "A highly efficient SSD chip for portable devices." - power_usage = 2 - origin_tech = "programming=1;engineering=1" - max_capacity = 32 - icon_state = "ssd_micro" - w_class = WEIGHT_CLASS_TINY diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm deleted file mode 100644 index 3e5409b5d61..00000000000 --- a/code/modules/modular_computers/hardware/network_card.dm +++ /dev/null @@ -1,82 +0,0 @@ -GLOBAL_VAR_INIT(ntnet_card_uid, 1) - -/obj/item/computer_hardware/network_card - name = "network card" - desc = "A basic wireless network card for usage with standard NTNet frequencies." - power_usage = 50 - origin_tech = "programming=2;engineering=1" - icon_state = "radio_mini" - var/identification_id = null // Identification ID. Technically MAC address of this device. Can't be changed by user. - var/identification_string = "" // Identification string, technically nickname seen in the network. Can be set by user. - var/long_range = 0 - var/ethernet = 0 // Hard-wired, therefore always on, ignores NTNet wireless checks. - malfunction_probability = 1 - device_type = MC_NET - -/obj/item/computer_hardware/network_card/diagnostics(var/mob/user) - ..() - to_chat(user, "NIX Unique ID: [identification_id]") - to_chat(user, "NIX User Tag: [identification_string]") - to_chat(user, "Supported protocols:") - to_chat(user, "511.m SFS (Subspace) - Standard Frequency Spread") - if(long_range) - to_chat(user, "511.n WFS/HB (Subspace) - Wide Frequency Spread/High Bandiwdth") - if(ethernet) - to_chat(user, "OpenEth (Physical Connection) - Physical network connection port") - -/obj/item/computer_hardware/network_card/New(var/l) - ..(l) - identification_id = GLOB.ntnet_card_uid - GLOB.ntnet_card_uid++ - -// Returns a string identifier of this network card -/obj/item/computer_hardware/network_card/proc/get_network_tag() - return "[identification_string] (NID [identification_id])" - -// 0 - No signal, 1 - Low signal, 2 - High signal. 3 - Wired Connection -/obj/item/computer_hardware/network_card/proc/get_signal(var/specific_action = 0) - if(!holder) // Hardware is not installed in anything. No signal. How did this even get called? - return 0 - - if(!check_functionality()) - return 0 - - if(ethernet) // Computer is connected via wired connection. - return 3 - - if(!GLOB.ntnet_global || !GLOB.ntnet_global.check_function(specific_action)) // NTNet is down and we are not connected via wired connection. No signal. - return 0 - - if(holder) - - var/turf/T = get_turf(holder) - if((T && istype(T)) && is_station_contact(T.z)) - // Computer is on station. Low/High signal depending on what type of network card you have - if(long_range) - return 2 - else - return 1 - - if(long_range) // Computer is not on station, but it has upgraded network card. Low signal. - return 1 - - return 0 // Computer is not on station and does not have upgraded network card. No signal. - - -/obj/item/computer_hardware/network_card/advanced - name = "advanced network card" - desc = "An advanced network card for usage with standard NTNet frequencies. Its transmitter is strong enough to connect even off-station." - long_range = 1 - origin_tech = "programming=4;engineering=2" - power_usage = 100 // Better range but higher power usage. - icon_state = "radio" - w_class = WEIGHT_CLASS_TINY - -/obj/item/computer_hardware/network_card/wired - name = "wired network card" - desc = "An advanced network card for usage with standard NTNet frequencies. This one also supports wired connection." - ethernet = 1 - origin_tech = "programming=5;engineering=3" - power_usage = 100 // Better range but higher power usage. - icon_state = "net_wired" - w_class = WEIGHT_CLASS_NORMAL diff --git a/code/modules/modular_computers/hardware/portable_disk.dm b/code/modules/modular_computers/hardware/portable_disk.dm deleted file mode 100644 index 93c9c1f412b..00000000000 --- a/code/modules/modular_computers/hardware/portable_disk.dm +++ /dev/null @@ -1,35 +0,0 @@ -/obj/item/computer_hardware/hard_drive/portable - name = "data disk" - desc = "Removable disk used to store data." - power_usage = 10 - icon_state = "datadisk6" - w_class = WEIGHT_CLASS_TINY - critical = 0 - max_capacity = 16 - origin_tech = "programming=1" - device_type = MC_SDD - -/obj/item/computer_hardware/hard_drive/portable/on_install(obj/item/modular_computer/M, mob/living/user = null) - M.add_verb(device_type) - -/obj/item/computer_hardware/hard_drive/portable/on_remove(obj/item/modular_computer/M, mob/living/user = null) - ..() - M.remove_verb(device_type) - -/obj/item/computer_hardware/hard_drive/portable/install_default_programs() - return // Empty by default - -/obj/item/computer_hardware/hard_drive/portable/advanced - name = "advanced data disk" - power_usage = 20 - icon_state = "datadisk5" - max_capacity = 64 - origin_tech = "programming=2" - -/obj/item/computer_hardware/hard_drive/portable/super - name = "super data disk" - desc = "Removable disk used to store large amounts of data." - power_usage = 40 - icon_state = "datadisk3" - max_capacity = 256 - origin_tech = "programming=4" diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm deleted file mode 100644 index 49c5d72a6f1..00000000000 --- a/code/modules/modular_computers/hardware/printer.dm +++ /dev/null @@ -1,70 +0,0 @@ -/obj/item/computer_hardware/printer - name = "printer" - desc = "Computer-integrated printer with paper recycling module." - power_usage = 100 - origin_tech = "programming=2;engineering=2" - icon_state = "printer" - w_class = WEIGHT_CLASS_NORMAL - device_type = MC_PRINT - var/stored_paper = 20 - var/max_paper = 30 - -/obj/item/computer_hardware/printer/diagnostics(mob/living/user) - ..() - to_chat(user, "Paper level: [stored_paper]/[max_paper]") - -/obj/item/computer_hardware/printer/examine(mob/user) - . = ..() - . += "Paper level: [stored_paper]/[max_paper]" - - -/obj/item/computer_hardware/printer/proc/print_text(var/text_to_print, var/paper_title = "") - if(!stored_paper) - return FALSE - if(!check_functionality()) - return FALSE - - var/obj/item/paper/P = new/obj/item/paper(holder.drop_location()) - - // Damaged printer causes the resulting paper to be somewhat harder to read. - if(damage > damage_malfunction) - P.info = stars(text_to_print, 100-malfunction_probability) - else - P.info = text_to_print - if(paper_title) - P.name = paper_title - P.update_icon() - P.populatefields() - P.updateinfolinks() - stored_paper-- - P = null - - playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1) - - return TRUE - -/obj/item/computer_hardware/printer/try_insert(obj/item/I, mob/living/user = null) - if(istype(I, /obj/item/paper)) - if(stored_paper >= max_paper) - if(user) - to_chat(user, "You try to add \the [I] into [src], but its paper bin is full!") - return FALSE - - if(user && !user.unEquip(I)) - return FALSE - - if(user) - to_chat(user, "You insert \the [I] into [src]'s paper recycler.") - qdel(I) - stored_paper++ - return TRUE - return FALSE - -/obj/item/computer_hardware/printer/mini - name = "miniprinter" - desc = "A small printer with paper recycling module." - power_usage = 50 - icon_state = "printer_mini" - w_class = WEIGHT_CLASS_TINY - stored_paper = 5 - max_paper = 15 diff --git a/code/modules/modular_computers/hardware/recharger.dm b/code/modules/modular_computers/hardware/recharger.dm deleted file mode 100644 index 418a9736528..00000000000 --- a/code/modules/modular_computers/hardware/recharger.dm +++ /dev/null @@ -1,94 +0,0 @@ -/obj/item/computer_hardware/recharger - critical = 1 - enabled = 1 - var/charge_rate = 100 - device_type = MC_CHARGE - -/obj/item/computer_hardware/recharger/proc/use_power(amount, charging=0) - if(charging) - return 1 - return 0 - -/obj/item/computer_hardware/recharger/process() - ..() - var/obj/item/computer_hardware/battery/battery_module = holder.all_components[MC_CELL] - if(!holder || !battery_module || !battery_module.battery) - return - - var/obj/item/stock_parts/cell/cell = battery_module.battery - if(cell.charge >= cell.maxcharge) - return - - if(use_power(charge_rate, charging=1)) - holder.give_power(charge_rate * GLOB.CELLRATE) - - -/obj/item/computer_hardware/recharger/APC - name = "area power connector" - desc = "A device that wirelessly recharges connected device from nearby APC." - icon_state = "charger_APC" - w_class = WEIGHT_CLASS_SMALL // Can't be installed into tablets/PDAs - origin_tech = "programming=2;engineering=2;powerstorage=3" - -/obj/item/computer_hardware/recharger/APC/use_power(amount, charging=0) - if(istype(holder.physical, /obj/machinery)) - var/obj/machinery/M = holder.physical - if(M.powered()) - M.use_power(amount) - return 1 - - else - var/area/A = get_area(src) - if(!istype(A)) - return 0 - - if(A.powered(EQUIP)) - A.use_power(amount, EQUIP) - return 1 - return 0 - -/obj/item/computer_hardware/recharger/wired - name = "wired power connector" - desc = "A power connector that recharges connected device from nearby power wire. Incompatible with portable computers." - icon_state = "charger_wire" - w_class = WEIGHT_CLASS_NORMAL - origin_tech = "engineering=2;powerstorage=1" - -/obj/item/computer_hardware/recharger/wired/can_install(obj/item/modular_computer/M, mob/living/user = null) - if(istype(M.physical, /obj/machinery) && M.physical.anchored) - return ..() - if(user) - to_chat(user, "\The [src] is incompatible with portable computers!") - return 0 - -/obj/item/computer_hardware/recharger/wired/use_power(amount, charging=0) - if(istype(holder.physical, /obj/machinery) && holder.physical.anchored) - var/obj/machinery/M = holder.physical - var/turf/T = M.loc - if(!T || !istype(T)) - return 0 - - var/obj/structure/cable/C = T.get_cable_node() - if(!C || !C.powernet) - return 0 - - var/power_in_net = C.powernet.avail-C.powernet.load - - if(power_in_net && power_in_net > amount) - C.powernet.load += amount - return 1 - - return 0 - - - -// This is not intended to be obtainable in-game. Intended for adminbus and debugging purposes. -/obj/item/computer_hardware/recharger/lambda - name = "lambda coil" - desc = "A very complex device that draws power from its own bluespace dimension." - icon_state = "charger_lambda" - w_class = WEIGHT_CLASS_TINY - charge_rate = 100000 - -/obj/item/computer_hardware/recharger/lambda/use_power(amount, charging=0) - return 1 diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm deleted file mode 100644 index aa69b85b1df..00000000000 --- a/code/modules/modular_computers/laptop_vendor.dm +++ /dev/null @@ -1,291 +0,0 @@ -// A vendor machine for modular computer portable devices - Laptops and Tablets - -/obj/machinery/lapvend - name = "computer vendor" - desc = "A vending machine with a built-in microfabricator, capable of dispensing various NT-branded computers." - icon = 'icons/obj/vending.dmi' - icon_state = "robotics" - layer = BELOW_OBJ_LAYER - anchored = 1 - density = 1 - - // The actual laptop/tablet - var/obj/item/modular_computer/laptop/fabricated_laptop = null - var/obj/item/modular_computer/tablet/fabricated_tablet = null - - // Utility vars - var/state = 0 // 0: Select device type, 1: Select loadout, 2: Payment, 3: Thankyou screen - var/devtype = 0 // 0: None(unselected), 1: Laptop, 2: Tablet - var/total_price = 0 // Price of currently vended device. - - // Device loadout - var/dev_cpu = 1 // 1: Default, 2: Upgraded - var/dev_battery = 1 // 1: Default, 2: Upgraded, 3: Advanced - var/dev_disk = 1 // 1: Default, 2: Upgraded, 3: Advanced - var/dev_netcard = 0 // 0: None, 1: Basic, 2: Long-Range - var/dev_apc_recharger = 0 // 0: None, 1: Standard (LAPTOP ONLY) - var/dev_printer = 0 // 0: None, 1: Standard - var/dev_card = 0 // 0: None, 1: Standard - -// Removes all traces of old order and allows you to begin configuration from scratch. -/obj/machinery/lapvend/proc/reset_order() - state = 0 - devtype = 0 - QDEL_NULL(fabricated_laptop) - QDEL_NULL(fabricated_tablet) - dev_cpu = 1 - dev_battery = 1 - dev_disk = 1 - dev_netcard = 0 - dev_apc_recharger = 0 - dev_printer = 0 - dev_card = 0 - -// Recalculates the price and optionally even fabricates the device. -/obj/machinery/lapvend/proc/fabricate_and_recalc_price(fabricate = 0) - total_price = 0 - if(devtype == 1) // Laptop, generally cheaper to make it accessible for most station roles - var/obj/item/computer_hardware/battery/battery_module = null - if(fabricate) - fabricated_laptop = new /obj/item/modular_computer/laptop/buildable(src) - fabricated_laptop.install_component(new /obj/item/computer_hardware/battery) - battery_module = fabricated_laptop.all_components[MC_CELL] - total_price = 99 - switch(dev_cpu) - if(1) - if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/processor_unit/small) - if(2) - if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/processor_unit) - total_price += 299 - switch(dev_battery) - if(1) // Basic(750C) - if(fabricate) - battery_module.try_insert(new /obj/item/stock_parts/cell/computer) - if(2) // Upgraded(1100C) - if(fabricate) - battery_module.try_insert(new /obj/item/stock_parts/cell/computer/advanced) - total_price += 199 - if(3) // Advanced(1500C) - if(fabricate) - battery_module.try_insert(new /obj/item/stock_parts/cell/computer/super) - total_price += 499 - switch(dev_disk) - if(1) // Basic(128GQ) - if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/hard_drive) - if(2) // Upgraded(256GQ) - if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/hard_drive/advanced) - total_price += 99 - if(3) // Advanced(512GQ) - if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/hard_drive/super) - total_price += 299 - switch(dev_netcard) - if(1) // Basic(Short-Range) - if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/network_card) - total_price += 99 - if(2) // Advanced (Long Range) - if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/network_card/advanced) - total_price += 299 - if(dev_apc_recharger) - total_price += 399 - if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/recharger/APC) - if(dev_printer) - total_price += 99 - if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/printer/mini) - if(dev_card) - total_price += 199 - if(fabricate) - fabricated_laptop.install_component(new /obj/item/computer_hardware/card_slot) - - return total_price - else if(devtype == 2) // Tablet, more expensive, not everyone could probably afford this. - var/obj/item/computer_hardware/battery/battery_module = null - if(fabricate) - fabricated_tablet = new(src) - fabricated_tablet.install_component(new /obj/item/computer_hardware/battery) - fabricated_tablet.install_component(new /obj/item/computer_hardware/processor_unit/small) - battery_module = fabricated_tablet.all_components[MC_CELL] - total_price = 199 - switch(dev_battery) - if(1) // Basic(300C) - if(fabricate) - battery_module.try_insert(new /obj/item/stock_parts/cell/computer/nano) - if(2) // Upgraded(500C) - if(fabricate) - battery_module.try_insert(new /obj/item/stock_parts/cell/computer/micro) - total_price += 199 - if(3) // Advanced(750C) - if(fabricate) - battery_module.try_insert(new /obj/item/stock_parts/cell/computer) - total_price += 499 - switch(dev_disk) - if(1) // Basic(32GQ) - if(fabricate) - fabricated_tablet.install_component(new /obj/item/computer_hardware/hard_drive/micro) - if(2) // Upgraded(64GQ) - if(fabricate) - fabricated_tablet.install_component(new /obj/item/computer_hardware/hard_drive/small) - total_price += 99 - if(3) // Advanced(128GQ) - if(fabricate) - fabricated_tablet.install_component(new /obj/item/computer_hardware/hard_drive) - total_price += 299 - switch(dev_netcard) - if(1) // Basic(Short-Range) - if(fabricate) - fabricated_tablet.install_component(new/obj/item/computer_hardware/network_card) - total_price += 99 - if(2) // Advanced (Long Range) - if(fabricate) - fabricated_tablet.install_component(new/obj/item/computer_hardware/network_card/advanced) - total_price += 299 - if(dev_printer) - total_price += 99 - if(fabricate) - fabricated_tablet.install_component(new/obj/item/computer_hardware/printer) - if(dev_card) - total_price += 199 - if(fabricate) - fabricated_tablet.install_component(new/obj/item/computer_hardware/card_slot) - return total_price - return 0 - - -/obj/machinery/lapvend/Topic(href, href_list) - if(..()) - return 1 - - switch(href_list["action"]) - if("pick_device") - if(state) // We've already picked a device type - return 0 - devtype = text2num(href_list["pick"]) - state = 1 - fabricate_and_recalc_price(0) - return 1 - if("clean_order") - reset_order() - return 1 - if((state != 1) && devtype) // Following IFs should only be usable when in the Select Loadout mode - return 0 - switch(href_list["action"]) - if("confirm_order") - state = 2 // Wait for ID swipe for payment processing - fabricate_and_recalc_price(0) - return 1 - if("hw_cpu") - dev_cpu = text2num(href_list["cpu"]) - fabricate_and_recalc_price(0) - return 1 - if("hw_battery") - dev_battery = text2num(href_list["battery"]) - fabricate_and_recalc_price(0) - return 1 - if("hw_disk") - dev_disk = text2num(href_list["disk"]) - fabricate_and_recalc_price(0) - return 1 - if("hw_netcard") - dev_netcard = text2num(href_list["netcard"]) - fabricate_and_recalc_price(0) - return 1 - if("hw_tesla") - dev_apc_recharger = text2num(href_list["tesla"]) - fabricate_and_recalc_price(0) - return 1 - if("hw_nanoprint") - dev_printer = text2num(href_list["print"]) - fabricate_and_recalc_price(0) - return 1 - if("hw_card") - dev_card = text2num(href_list["card"]) - fabricate_and_recalc_price(0) - return 1 - return 0 - -/obj/machinery/lapvend/attack_hand(mob/user) - ui_interact(user) - -/obj/machinery/lapvend/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "computer_fabricator.tmpl", "Personal Computer Vendor", 500, 700) - ui.set_auto_update(1) - ui.open() - -/obj/machinery/lapvend/ui_data(mob/user) - var/list/data[0] - data["state"] = state - if(state == 1) - data["devtype"] = devtype - data["hw_battery"] = dev_battery - data["hw_disk"] = dev_disk - data["hw_netcard"] = dev_netcard - data["hw_tesla"] = dev_apc_recharger - data["hw_nanoprint"] = dev_printer - data["hw_card"] = dev_card - data["hw_cpu"] = dev_cpu - if(state == 1 || state == 2) - data["totalprice"] = total_price - return data - -obj/machinery/lapvend/attackby(obj/item/I, mob/user) - var/obj/item/card/id/C - if(istype(I, /obj/item/card/id)) - C = I - if(istype(I, /obj/item/pda)) - var/obj/item/pda/PDA = I - if(PDA.id) - C = PDA.id - - if(C && istype(C) && state == 2) - if(process_payment(C, I)) - fabricate_and_recalc_price(1) - if((devtype == 1) && fabricated_laptop) - fabricated_laptop.forceMove(loc) - fabricated_laptop = null - else if((devtype == 2) && fabricated_tablet) - fabricated_tablet.update_icon() - fabricated_tablet.forceMove(loc) - fabricated_tablet = null - atom_say("Enjoy your new product!") - state = 3 - return 1 - return 0 - return ..() - - -// Simplified payment processing, returns 1 on success. -/obj/machinery/lapvend/proc/process_payment(obj/item/card/id/I, obj/item/ID_container) - visible_message("\The [usr] swipes \the [ID_container] through \the [src].") - var/datum/money_account/customer_account = get_card_account(I) - if(!customer_account || customer_account.suspended) - atom_say("Connection error. Unable to connect to account.") - return 0 - - if(customer_account.security_level != 0) //If card requires pin authentication (ie seclevel 1 or 2) - var/attempt_pin = input("Enter pin code", "Vendor transaction") as num - customer_account = attempt_account_access(I.associated_account_number, attempt_pin, 2) - - if(!customer_account) - atom_say("Unable to access account: incorrect credentials.") - return 0 - - if(total_price > customer_account.money) - atom_say("Insufficient funds in account.") - return 0 - else - customer_account.charge(total_price, GLOB.vendor_account, - "Purchase of [(devtype == 1) ? "laptop computer" : "tablet microcomputer"].", - name, customer_account.owner_name, "Sale of [(devtype == 1) ? "laptop computer" : "tablet microcomputer"].", - customer_account.owner_name) - - return 1 diff --git a/code/modules/nano/modules/alarm_monitor.dm b/code/modules/nano/modules/alarm_monitor.dm deleted file mode 100644 index cee3820b102..00000000000 --- a/code/modules/nano/modules/alarm_monitor.dm +++ /dev/null @@ -1,90 +0,0 @@ -/datum/nano_module/alarm_monitor - name = "Alarm monitor" - var/list_cameras = 0 // Whether or not to list camera references. A future goal would be to merge this with the enginering/security camera console. Currently really only for AI-use. - var/list/datum/alarm_handler/alarm_handlers // The particular list of alarm handlers this alarm monitor should present to the user. - -/datum/nano_module/alarm_monitor/all/New() - ..() - alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm) - -/datum/nano_module/alarm_monitor/engineering/New() - ..() - alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.fire_alarm, SSalarms.power_alarm) - -/datum/nano_module/alarm_monitor/security/New() - ..() - alarm_handlers = list(SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.motion_alarm) - -/datum/nano_module/alarm_monitor/proc/register(var/object, var/procName) - for(var/datum/alarm_handler/AH in alarm_handlers) - AH.register(object, procName) - -/datum/nano_module/alarm_monitor/proc/unregister(var/object) - for(var/datum/alarm_handler/AH in alarm_handlers) - AH.unregister(object) - -/datum/nano_module/alarm_monitor/proc/all_alarms() - var/list/all_alarms = new() - for(var/datum/alarm_handler/AH in alarm_handlers) - all_alarms += AH.alarms - - return all_alarms - -/datum/nano_module/alarm_monitor/proc/major_alarms() - var/list/all_alarms = new() - for(var/datum/alarm_handler/AH in alarm_handlers) - all_alarms += AH.major_alarms() - - return all_alarms - -/datum/nano_module/alarm_monitor/proc/minor_alarms() - var/list/all_alarms = new() - for(var/datum/alarm_handler/AH in alarm_handlers) - all_alarms += AH.minor_alarms() - - return all_alarms - -/datum/nano_module/alarm_monitor/Topic(ref, href_list) - if(..()) - return 1 - if(href_list["switchTo"]) - var/obj/machinery/camera/C = locate(href_list["switchTo"]) in GLOB.cameranet.cameras - if(!C || !isAI(usr)) - return - - usr.switch_to_camera(C) - return 1 - -/datum/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring Console", 800, 800, state = state) - ui.open() - ui.set_auto_update(1) - -/datum/nano_module/alarm_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - var/categories[0] - for(var/datum/alarm_handler/AH in alarm_handlers) - categories[++categories.len] = list("category" = AH.category, "alarms" = list()) - for(var/datum/alarm/A in AH.major_alarms()) - var/cameras[0] - var/lost_sources[0] - - if(isAI(user)) - for(var/obj/machinery/camera/C in A.cameras()) - cameras[++cameras.len] = C.nano_structure() - for(var/datum/alarm_source/AS in A.sources) - if(!AS.source) - lost_sources[++lost_sources.len] = AS.source_name - - categories[categories.len]["alarms"] += list(list( - "name" = sanitize(A.alarm_name()), - "origin_lost" = A.origin == null, - "has_cameras" = cameras.len, - "cameras" = cameras, - "lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : "")) - data["categories"] = categories - - return data diff --git a/code/modules/nano/modules/atmos_control.dm b/code/modules/nano/modules/atmos_control.dm index 2c74a1ce844..273b0bfc137 100644 --- a/code/modules/nano/modules/atmos_control.dm +++ b/code/modules/nano/modules/atmos_control.dm @@ -34,6 +34,11 @@ ui = new(user, src, ui_key, "atmos_control.tmpl", src.name, 900, 800, state = state) ui.add_template("mapContent", "atmos_control_map_content.tmpl") ui.add_template("mapHeader", "atmos_control_map_header.tmpl") + + // Send nanomaps + var/datum/asset/nanomaps = get_asset_datum(/datum/asset/simple/nanomaps) + nanomaps.send(user) + ui.set_show_map(1) ui.open() ui.set_auto_update(1) diff --git a/code/modules/nano/modules/ert_manager.dm b/code/modules/nano/modules/ert_manager.dm deleted file mode 100644 index 98a3e19d052..00000000000 --- a/code/modules/nano/modules/ert_manager.dm +++ /dev/null @@ -1,105 +0,0 @@ -/datum/nano_module/ert_manager - name = "ERT Manager" - var/ert_type = "Code Red" - var/commander_slots = 1 - var/security_slots = 3 - var/medical_slots = 3 - var/engineering_slots = 3 - var/janitor_slots = 0 - var/paranormal_slots = 0 - var/cyborg_slots = 0 - var/autoclose = 0 - - -/datum/nano_module/ert_manager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.admin_state) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(ui && autoclose) - ui.close() - return 0 - if(!ui) - ui = new(user, src, ui_key, "ert_config.tmpl", "ERT Panel", 600, 600, state = state) - ui.open() - ui.set_auto_update(1) - -/datum/nano_module/ert_manager/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["set_code"]) - ert_type = href_list["set_code"] - - if(href_list["set_com"]) - commander_slots = text2num(href_list["set_com"]) - - if(href_list["set_sec"]) - security_slots = text2num(href_list["set_sec"]) - - if(href_list["set_med"]) - medical_slots = text2num(href_list["set_med"]) - - if(href_list["set_eng"]) - engineering_slots = text2num(href_list["set_eng"]) - - if(href_list["set_jan"]) - janitor_slots = text2num(href_list["set_jan"]) - - if(href_list["set_par"]) - paranormal_slots = text2num(href_list["set_par"]) - - if(href_list["set_cyb"]) - cyborg_slots = text2num(href_list["set_cyb"]) - - if(href_list["dispatch_ert"]) - GLOB.ert_request_answered = TRUE - var/slots_list = list() - if(commander_slots > 0) - slots_list += "commander: [commander_slots]" - if(security_slots > 0) - slots_list += "security: [security_slots]" - if(medical_slots > 0) - slots_list += "medical: [medical_slots]" - if(engineering_slots > 0) - slots_list += "engineering: [engineering_slots]" - if(janitor_slots > 0) - slots_list += "janitor: [janitor_slots]" - if(paranormal_slots > 0) - slots_list += "paranormal: [paranormal_slots]" - if(cyborg_slots > 0) - slots_list += "cyborg: [cyborg_slots]" - var/slot_text = jointext(slots_list, ", ") - notify_ghosts("An ERT is being dispatched. Open positions: [slot_text]") - message_admins("[key_name_admin(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]", 1) - log_admin("[key_name(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]") - GLOB.event_announcement.Announce("Attention, [station_name()]. We are attempting to assemble an ERT. Standby.", "ERT Protocol Activated") - autoclose = 1 - ui_interact(usr) - trigger_armed_response_team(convert_ert_string(ert_type), commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots) - return 0 - - ui_interact(usr) - - -/proc/convert_ert_string(thestring) - switch(thestring) - if("Code Amber") - return new /datum/response_team/amber - if("Code Red") - return new /datum/response_team/red - if("Code Gamma") - return new /datum/response_team/gamma - - -/datum/nano_module/ert_manager/ui_data() - var/data[0] - data["alert_level"] = get_security_level() - data["ert_type"] = ert_type - data["com"] = commander_slots - data["sec"] = security_slots - data["med"] = medical_slots - data["eng"] = engineering_slots - data["jan"] = janitor_slots - data["par"] = paranormal_slots - data["cyb"] = cyborg_slots - - return data - diff --git a/code/modules/nano/modules/power_monitor.dm b/code/modules/nano/modules/power_monitor.dm deleted file mode 100644 index 66948243035..00000000000 --- a/code/modules/nano/modules/power_monitor.dm +++ /dev/null @@ -1,53 +0,0 @@ -/datum/nano_module/power_monitor - name = "Power monitor" - var/select_monitor = 0 - var/obj/machinery/computer/monitor/powermonitor - -/datum/nano_module/power_monitor/silicon - select_monitor = 1 - -/datum/nano_module/power_monitor/New() - ..() - if(!select_monitor) - powermonitor = nano_host() - -/datum/nano_module/power_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "power_monitor.tmpl", "Power Monitoring Console", 800, 700, state = state) - ui.open() - ui.set_auto_update(1) - -/datum/nano_module/power_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - data["powermonitor"] = powermonitor - if(select_monitor) - data["select_monitor"] = 1 - data["powermonitors"] = GLOB.powermonitor_repository.powermonitor_data() - - if(powermonitor && !isnull(powermonitor.powernet)) - if(select_monitor && (powermonitor.stat & (NOPOWER|BROKEN))) - powermonitor = null - return - data["poweravail"] = powermonitor.powernet.avail - data["powerload"] = powermonitor.powernet.viewload - data["powerdemand"] = powermonitor.powernet.load - data["apcs"] = GLOB.apc_repository.apc_data(powermonitor.powernet) - - return data - -/datum/nano_module/power_monitor/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["selectmonitor"]) - if(issilicon(usr)) - powermonitor = locate(href_list["selectmonitor"]) - powermonitor.powernet = powermonitor.find_powernet() // Refresh the powernet of the monitor - return 1 - - if(href_list["return"]) - if(issilicon(usr)) - powermonitor = null - return 1 diff --git a/code/modules/ninja/suit/suit_initialisation.dm b/code/modules/ninja/suit/suit_initialisation.dm index 59303b4707c..b9200d0e771 100644 --- a/code/modules/ninja/suit/suit_initialisation.dm +++ b/code/modules/ninja/suit/suit_initialisation.dm @@ -14,30 +14,30 @@ suitBusy = 1 if(suitActive && (alert("Confirm suit systems shutdown? This cannot be halted once it has started.", "Confirm Shutdown", "Yes", "No") == "Yes")) - to_chat(usr, "Now de-initializing...") + to_chat(usr, "Now de-initializing...") sleep(15) - to_chat(usr, "Logging off, [usr.real_name]. Shutting down SpiderOS.") + to_chat(usr, "Logging off, [usr.real_name]. Shutting down SpiderOS.") sleep(10) - to_chat(usr, "Primary system status: OFFLINE.\nBackup system status: OFFLINE.") + to_chat(usr, "Primary system status: OFFLINE.\nBackup system status: OFFLINE.") sleep(5) - to_chat(usr, "VOID-shift device status: OFFLINE.\nCLOAK-tech device status: OFFLINE.") + to_chat(usr, "VOID-shift device status: OFFLINE.\nCLOAK-tech device status: OFFLINE.") //TODO: Shut down any active abilities sleep(10) - to_chat(usr, "Disconnecting neural-net interface... Success.") + to_chat(usr, "Disconnecting neural-net interface... Success.") QDEL_NULL(usr.hud_used) usr.create_mob_hud() usr.regenerate_icons() sleep(5) - to_chat(usr, "Disengaging neural-net interface... Success.") + to_chat(usr, "Disengaging neural-net interface... Success.") sleep(10) - to_chat(usr, "Unsecuring external locking mechanism...\nNeural-net abolished.\nOperation status: FINISHED.") + to_chat(usr, "Unsecuring external locking mechanism...\nNeural-net abolished.\nOperation status: FINISHED.") //TODO: Grant verbs toggle_suit_lock(usr) usr.regenerate_icons() @@ -45,24 +45,24 @@ suitActive = 0 else if(!suitActive) // Activate the suit. - to_chat(usr, "Now initializing...") + to_chat(usr, "Now initializing...") sleep(15) - to_chat(usr, "Now establishing neural-net interface...") + to_chat(usr, "Now establishing neural-net interface...") if(usr.mind.special_role != "Ninja") to_chat(usr, "FĆAL �Rr�R: ŧer nt recgnized, c-cntr-r䣧-ç äcked.") return sleep(10) - to_chat(usr, "Neural-net established. Now monitoring brainwave pattern. \nBrainwave pattern GREEN, proceeding.") + to_chat(usr, "Neural-net established. Now monitoring brainwave pattern. \nBrainwave pattern GREEN, proceeding.") sleep(10) - to_chat(usr, "Securing external locking mechanism...") + to_chat(usr, "Securing external locking mechanism...") if(!toggle_suit_lock(usr)) return sleep(5) - to_chat(usr, "Suit secured, extending neural-net interface...") + to_chat(usr, "Suit secured, extending neural-net interface...") QDEL_NULL(usr.hud_used) usr.hud_used = new /datum/hud/human(usr, 'icons/mob/screen_ninja.dmi', "#ffffff", 255) @@ -71,22 +71,22 @@ usr.regenerate_icons() sleep(10) - to_chat(usr, "VOID-shift device status: ONLINE.\nCLOAK-tech device status:ONLINE") + to_chat(usr, "VOID-shift device status: ONLINE.\nCLOAK-tech device status:ONLINE") sleep(5) - to_chat(usr, "Primary system status: ONLINE.\nBackup system status: ONLINE.") + to_chat(usr, "Primary system status: ONLINE.\nBackup system status: ONLINE.") if(suitCell) - to_chat(usr, "Current energy capacity: [suitCell.charge]/[suitCell.maxcharge].") + to_chat(usr, "Current energy capacity: [suitCell.charge]/[suitCell.maxcharge].") sleep(10) - to_chat(usr, "All systems operational. Welcome to SpiderOS, [usr.real_name].") + to_chat(usr, "All systems operational. Welcome to SpiderOS, [usr.real_name].") //TODO: Grant ninja verbs here. suitBusy = 0 suitActive = 1 else suitBusy = 0 - to_chat(usr, "NOTICE: Suit de-activation protocals aborted.") + to_chat(usr, "NOTICE: Suit de-activation protocals aborted.") else to_chat(usr, "FĆAL �Rr�R: ŧer nt recgnized, c-cntr-r䣧-ç äcked.") return diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm index a65610c7382..f495466d3f5 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -42,7 +42,7 @@ if(in_range(user, src) && toppaper) . += toppaper.examine(user) -obj/item/clipboard/proc/penPlacement(mob/user, obj/item/pen/P, placing) +/obj/item/clipboard/proc/penPlacement(mob/user, obj/item/pen/P, placing) if(placing) if(containedpen) to_chat(user, "There's already a pen in [src]!") diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm index b1fe71db72e..d94e69133a7 100644 --- a/code/modules/paperwork/contract.dm +++ b/code/modules/paperwork/contract.dm @@ -314,7 +314,7 @@ /obj/item/paper/contract/infernal/magic/FulfillContract(mob/living/carbon/human/user = target.current, blood = 0) if(!istype(user) || !user.mind) return -1 - user.mind.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null)) + user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/click/fireball/hellish(null)) user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null)) return ..() diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 09858073142..32416a8d5e7 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -12,24 +12,32 @@ GLOBAL_LIST_EMPTY(fax_blacklist) insert_anim = "faxsend" pass_flags = PASSTABLE var/fax_network = "Local Fax Network" - var/syndie_restricted = FALSE //is it a syndicate base fax restricted from contacting NT assets? + /// If true, prevents fax machine from sending messages to NT machines + var/syndie_restricted = FALSE - var/long_range_enabled = 0 // Can we send messages off the station? + /// Can we send messages off-station? + var/long_range_enabled = FALSE req_one_access = list(ACCESS_LAWYER, ACCESS_HEADS, ACCESS_ARMORY) use_power = IDLE_POWER_USE idle_power_usage = 30 active_power_usage = 200 - var/obj/item/card/id/scan = null // identification + /// ID card inserted into the machine, used to log in with + var/obj/item/card/id/scan = null - var/authenticated = 0 - var/sendcooldown = 0 // to avoid spamming fax messages + /// Whether the machine is "logged in" or not + var/authenticated = FALSE + /// Next world.time at which this fax machine can send a message to CC/syndicate + var/sendcooldown = 0 + /// After sending a message to CC/syndicate, cannot send another to them for this many deciseconds var/cooldown_time = 1800 - var/department = "Unknown" // our department + /// Our department, determines whether this machine gets faxes sent to a department + var/department = "Unknown" - var/destination = "Not Selected" // the department we're sending to + /// Target department to send outgoing faxes to + var/destination /obj/machinery/photocopier/faxmachine/New() ..() @@ -44,7 +52,7 @@ GLOBAL_LIST_EMPTY(fax_blacklist) /obj/machinery/photocopier/faxmachine/longrange name = "long range fax machine" fax_network = "Central Command Quantum Entanglement Network" - long_range_enabled = 1 + long_range_enabled = TRUE /obj/machinery/photocopier/faxmachine/longrange/syndie name = "syndicate long range fax machine" @@ -58,17 +66,17 @@ GLOBAL_LIST_EMPTY(fax_blacklist) GLOB.hidden_departments |= department /obj/machinery/photocopier/faxmachine/attack_hand(mob/user) - ui_interact(user) + tgui_interact(user) /obj/machinery/photocopier/faxmachine/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) /obj/machinery/photocopier/faxmachine/attackby(obj/item/item, mob/user, params) if(istype(item,/obj/item/card/id) && !scan) scan(item) else if(istype(item, /obj/item/paper) || istype(item, /obj/item/photo) || istype(item, /obj/item/paper_bundle)) ..() - SSnanoui.update_uis(src) + SStgui.update_uis(src) else return ..() @@ -80,42 +88,6 @@ GLOBAL_LIST_EMPTY(fax_blacklist) else to_chat(user, "You swipe the card through [src], but nothing happens.") -/obj/machinery/photocopier/faxmachine/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "faxmachine.tmpl", "Fax Machine UI", 540, 450) - ui.open() - -/obj/machinery/photocopier/faxmachine/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - var/is_authenticated = is_authenticated(user) - - if(scan) - data["scan_name"] = scan.name - else - data["scan_name"] = "-----" - data["authenticated"] = is_authenticated - if(!is_authenticated) - data["network"] = "Disconnected" - else if(!emagged) - data["network"] = fax_network - else - data["network"] = "ERR*?*%!*" - if(copyitem) - data["paper"] = copyitem.name - data["paperinserted"] = 1 - else - data["paper"] = "-----" - data["paperinserted"] = 0 - data["destination"] = destination - data["cooldown"] = sendcooldown - if((destination in GLOB.admin_departments) || (destination in GLOB.hidden_admin_departments)) - data["respectcooldown"] = 1 - else - data["respectcooldown"] = 0 - - return data - /obj/machinery/photocopier/faxmachine/proc/is_authenticated(mob/user) if(authenticated) return TRUE @@ -123,87 +95,134 @@ GLOBAL_LIST_EMPTY(fax_blacklist) return TRUE return FALSE -/obj/machinery/photocopier/faxmachine/Topic(href, href_list) - if(..()) - return 1 +/obj/machinery/photocopier/faxmachine/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "FaxMachine", name, 540, 300, master_ui, state) + ui.open() +/obj/machinery/photocopier/faxmachine/tgui_data(mob/user) + var/list/data = list() + data["authenticated"] = is_authenticated(user) + data["scan_name"] = scan ? scan.name : FALSE + if(!data["authenticated"]) + data["network"] = "Disconnected" + else if(!emagged) + data["network"] = fax_network + else + data["network"] = "ERR*?*%!*" + data["paper"] = copyitem ? copyitem.name : FALSE + data["paperinserted"] = copyitem ? TRUE : FALSE + data["destination"] = destination ? destination : FALSE + data["sendError"] = FALSE + if(stat & (BROKEN|NOPOWER)) + data["sendError"] = "No Power" + else if(!data["authenticated"]) + data["sendError"] = "Not Logged In" + else if(!data["paper"]) + data["sendError"] = "Nothing Inserted" + else if(!data["destination"]) + data["sendError"] = "Destination Not Set" + else if((destination in GLOB.admin_departments) || (destination in GLOB.hidden_admin_departments)) + var/cooldown_seconds = cooldown_seconds() + if(cooldown_seconds) + data["sendError"] = "Re-aligning in [cooldown_seconds] seconds..." + return data + + +/obj/machinery/photocopier/faxmachine/tgui_act(action, params) + if(..()) + return var/is_authenticated = is_authenticated(usr) - if(href_list["send"]) - if(copyitem && is_authenticated) + . = TRUE + switch(action) + if("scan") // insert/remove your ID card + scan() + if("auth") // log in/out + if(!is_authenticated && scan) + if(scan.registered_name in GLOB.fax_blacklist) + to_chat(usr, "Login rejected: individual is blacklisted from fax network.") + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + . = FALSE + else if(check_access(scan)) + authenticated = TRUE + else // ID doesn't have access to this machine + to_chat(usr, "Login rejected: ID card does not have required access.") + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + . = FALSE + else if(is_authenticated) + authenticated = FALSE + if("paper") // insert/eject paper/paperbundle/photo + if(copyitem) + copyitem.forceMove(get_turf(src)) + if(ishuman(usr)) + if(!usr.get_active_hand() && Adjacent(usr)) + usr.put_in_hands(copyitem) + to_chat(usr, "You eject [copyitem] from [src].") + copyitem = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/paper) || istype(I, /obj/item/photo) || istype(I, /obj/item/paper_bundle)) + usr.drop_item() + copyitem = I + I.forceMove(src) + to_chat(usr, "You insert [I] into [src].") + flick(insert_anim, src) + else + to_chat(usr, "[src] only accepts paper, paper bundles, and photos.") + . = FALSE + if("rename") // rename the item that is currently in the fax machine + if(copyitem) + var/n_name = sanitize(copytext(input(usr, "What would you like to label the fax?", "Fax Labelling", copyitem.name) as text, 1, MAX_MESSAGE_LEN)) + if((copyitem && copyitem.loc == src && usr.stat == 0)) + if(istype(copyitem, /obj/item/paper)) + copyitem.name = "[(n_name ? text("[n_name]") : initial(copyitem.name))]" + copyitem.desc = "This is a paper titled '" + copyitem.name + "'." + else if(istype(copyitem, /obj/item/photo)) + copyitem.name = "[(n_name ? text("[n_name]") : "photo")]" + else if(istype(copyitem, /obj/item/paper_bundle)) + copyitem.name = "[(n_name ? text("[n_name]") : "paper")]" + else + . = FALSE + else + . = FALSE + else + . = FALSE + if("dept") // choose which department receives the fax + if(is_authenticated) + var/lastdestination = destination + var/list/combineddepartments = GLOB.alldepartments.Copy() + if(long_range_enabled) + combineddepartments += GLOB.admin_departments.Copy() + if(emagged) + combineddepartments += GLOB.hidden_admin_departments.Copy() + combineddepartments += GLOB.hidden_departments.Copy() + if(syndie_restricted) + combineddepartments = GLOB.hidden_admin_departments.Copy() + combineddepartments += GLOB.hidden_departments.Copy() + for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) + if(F.emagged)//we can contact emagged faxes on the station + combineddepartments |= F.department + destination = input(usr, "To which department?", "Choose a department", "") as null|anything in combineddepartments + if(!destination) + destination = lastdestination + if("send") // actually send the fax + if(!copyitem || !is_authenticated || !destination) + return + if(stat & (BROKEN|NOPOWER)) + return if((destination in GLOB.admin_departments) || (destination in GLOB.hidden_admin_departments)) + var/cooldown_seconds = cooldown_seconds() + if(cooldown_seconds > 0) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + to_chat(usr, "[src] is not ready for another [cooldown_seconds] seconds.") + return send_admin_fax(usr, destination) + sendcooldown = world.time + cooldown_time else sendfax(destination, usr) - - if(sendcooldown) - spawn(sendcooldown) // cooldown time - sendcooldown = 0 - SSnanoui.update_uis(src) - - if(href_list["paper"]) - if(copyitem) - copyitem.forceMove(get_turf(src)) - if(ishuman(usr)) - if(!usr.get_active_hand() && Adjacent(usr)) - usr.put_in_hands(copyitem) - to_chat(usr, "You eject \the [copyitem] from \the [src].") - copyitem = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/paper) || istype(I, /obj/item/photo) || istype(I, /obj/item/paper_bundle)) - usr.drop_item() - copyitem = I - I.forceMove(src) - to_chat(usr, "You insert \the [I] into \the [src].") - flick(insert_anim, src) - - if(href_list["scan"]) - scan() - - if(href_list["dept"]) - if(is_authenticated) - var/lastdestination = destination - var/list/combineddepartments = GLOB.alldepartments.Copy() - if(long_range_enabled) - combineddepartments += GLOB.admin_departments.Copy() - - if(emagged) - combineddepartments += GLOB.hidden_admin_departments.Copy() - combineddepartments += GLOB.hidden_departments.Copy() - - if(syndie_restricted) - combineddepartments = GLOB.hidden_admin_departments.Copy() - combineddepartments += GLOB.hidden_departments.Copy() - for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) - if(F.emagged)//we can contact emagged faxes on the station - combineddepartments |= F.department - - destination = input(usr, "To which department?", "Choose a department", "") as null|anything in combineddepartments - if(!destination) - destination = lastdestination - - if(href_list["auth"]) - if(!is_authenticated && scan) - if(scan.registered_name in GLOB.fax_blacklist) - playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) - else if(check_access(scan)) - authenticated = 1 - else if(is_authenticated) - authenticated = 0 - - if(href_list["rename"]) - if(copyitem) - var/n_name = sanitize(copytext(input(usr, "What would you like to label the fax?", "Fax Labelling", copyitem.name) as text, 1, MAX_MESSAGE_LEN)) - if((copyitem && copyitem.loc == src && usr.stat == 0)) - if(istype(copyitem, /obj/item/paper)) - copyitem.name = "[(n_name ? text("[n_name]") : initial(copyitem.name))]" - copyitem.desc = "This is a paper titled '" + copyitem.name + "'." - else if(istype(copyitem, /obj/item/photo)) - copyitem.name = "[(n_name ? text("[n_name]") : "photo")]" - else if(istype(copyitem, /obj/item/paper_bundle)) - copyitem.name = "[(n_name ? text("[n_name]") : "paper")]" - - SSnanoui.update_uis(src) + if(.) + add_fingerprint(usr) /obj/machinery/photocopier/faxmachine/proc/scan(var/obj/item/card/id/card = null) if(scan) // Card is in machine @@ -226,7 +245,7 @@ GLOBAL_LIST_EMPTY(fax_blacklist) usr.drop_item() card.forceMove(src) scan = card - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/machinery/photocopier/faxmachine/verb/eject_id() set category = null @@ -237,25 +256,20 @@ GLOBAL_LIST_EMPTY(fax_blacklist) return if(scan) - to_chat(usr, "You remove \the [scan] from \the [src].") + to_chat(usr, "You remove [scan] from [src].") scan.forceMove(get_turf(src)) if(!usr.get_active_hand() && Adjacent(usr)) usr.put_in_hands(scan) scan = null else - to_chat(usr, "There is nothing to remove from \the [src].") + to_chat(usr, "There is nothing to remove from [src].") /obj/machinery/photocopier/faxmachine/proc/sendfax(var/destination,var/mob/sender) - if(stat & (BROKEN|NOPOWER)) - return - - use_power(200) - + use_power(active_power_usage) var/success = 0 for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) if(F.department == destination) success = F.receivefax(copyitem) - if(success) var/datum/fax/F = new /datum/fax() F.name = copyitem.name @@ -272,10 +286,10 @@ GLOBAL_LIST_EMPTY(fax_blacklist) /obj/machinery/photocopier/faxmachine/proc/receivefax(var/obj/item/incoming) if(stat & (BROKEN|NOPOWER)) - return 0 + return FALSE if(department == "Unknown") - return 0 //You can't send faxes to "Unknown" + return FALSE //You can't send faxes to "Unknown" flick("faxreceive", src) @@ -291,19 +305,13 @@ GLOBAL_LIST_EMPTY(fax_blacklist) else if(istype(incoming, /obj/item/paper_bundle)) bundlecopy(incoming) else - return 0 + return FALSE use_power(active_power_usage) - return 1 + return TRUE /obj/machinery/photocopier/faxmachine/proc/send_admin_fax(var/mob/sender, var/destination) - if(stat & (BROKEN|NOPOWER)) - return - - if(sendcooldown) - return - - use_power(200) + use_power(active_power_usage) if(!(istype(copyitem, /obj/item/paper) || istype(copyitem, /obj/item/paper_bundle) || istype(copyitem, /obj/item/photo))) visible_message("[src] beeps, \"Error transmitting message.\"") @@ -327,10 +335,12 @@ GLOBAL_LIST_EMPTY(fax_blacklist) for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) if(F.department == destination) F.receivefax(copyitem) - sendcooldown = cooldown_time - spawn(50) - visible_message("[src] beeps, \"Message transmitted successfully.\"") + visible_message("[src] beeps, \"Message transmitted successfully.\"") +/obj/machinery/photocopier/faxmachine/proc/cooldown_seconds() + if(sendcooldown < world.time) + return 0 + return round((sendcooldown - world.time) / 10) /obj/machinery/photocopier/faxmachine/proc/message_admins(var/mob/sender, var/faxname, var/faxtype, var/obj/item/sent, font_colour="#9A04D1") var/msg = "[faxname]: [key_name_admin(sender)] | REPLY: (RADIO) (FAX) ([ADMIN_SM(sender,"SM")]) | REJECT: (TEMPLATE) ([ADMIN_BSA(sender,"BSA")]) (EVILFAX) : Receiving '[sent.name]' via secure connection... view message" diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index d5cbbdc0ad6..6b9f3257096 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -59,6 +59,10 @@ if(stat & (BROKEN|NOPOWER)) return + if(emag_cooldown > world.time) + to_chat(usr, "[src] is busy, try again in a few seconds.") + return + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) for(var/i = 0, i < copies, i++) if(toner <= 0) @@ -74,9 +78,6 @@ GLOB.copier_items_printed_logged = TRUE break - if(emag_cooldown > world.time) - return - if(istype(copyitem, /obj/item/paper)) copy(copyitem) sleep(15) diff --git a/code/modules/paperwork/silicon_photography.dm b/code/modules/paperwork/silicon_photography.dm index eb8904cf3e8..17cfcacec62 100644 --- a/code/modules/paperwork/silicon_photography.dm +++ b/code/modules/paperwork/silicon_photography.dm @@ -150,7 +150,7 @@ // Explicitly only allow deletion from the local camera deletepicture(src) -obj/item/camera/siliconcam/proc/getsource() +/obj/item/camera/siliconcam/proc/getsource() if(istype(src.loc, /mob/living/silicon/ai)) return src diff --git a/code/modules/paperwork/ticketmachine.dm b/code/modules/paperwork/ticketmachine.dm index c5939c392b1..0492d3da694 100644 --- a/code/modules/paperwork/ticketmachine.dm +++ b/code/modules/paperwork/ticketmachine.dm @@ -7,6 +7,7 @@ icon_state = "ticketmachine" desc = "A marvel of bureaucratic engineering encased in an efficient plastic shell. It can be refilled with a hand labeler refill roll and linked to buttons with a multitool." density = FALSE + anchored = TRUE maptext_height = 26 maptext_width = 32 maptext_x = 7 @@ -108,7 +109,7 @@ maptext_x = 10 if(100) maptext_x = 8 - maptext = "[current_number]" //Finally, apply the maptext + maptext = "[ticket_number]" /obj/machinery/ticket_machine/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/hand_labeler_refill)) @@ -151,8 +152,8 @@ to_chat(user, "You take a ticket from [src], looks like you're ticket number #[ticket_number]...") var/obj/item/ticket_machine_ticket/theirticket = new /obj/item/ticket_machine_ticket(get_turf(src)) theirticket.name = "Ticket #[ticket_number]" - theirticket.maptext = "[ticket_number]" - theirticket.saved_maptext = "[ticket_number]" + theirticket.maptext = "[ticket_number]" + theirticket.saved_maptext = "[ticket_number]" theirticket.ticket_number = ticket_number theirticket.source = src theirticket.owner = user.UID() @@ -167,9 +168,13 @@ user.adjust_fire_stacks(1) user.IgniteMob() +// Stop AI penetrating the bureaucracy +/obj/machinery/ticket_machine/attack_ai(mob/user) + return + /obj/item/ticket_machine_ticket name = "Ticket" - desc = "A ticket which shows your place in the Head of Personnel's line. Made from Nanotrasen patented NanoPaper®. Though solid, its form seems to shimmer slightly. Feels (and burns) just like the real thing." + desc = "A ticket which shows your place in the Head of Personnel's line. Made from Nanotrasen patented NanoPaper. Though solid, its form seems to shimmer slightly. Feels (and burns) just like the real thing." icon = 'icons/obj/bureaucracy.dmi' icon_state = "ticket" maptext_x = 7 diff --git a/code/modules/pda/PDA.dm b/code/modules/pda/PDA.dm index f17666af19d..69b01b53f39 100755 --- a/code/modules/pda/PDA.dm +++ b/code/modules/pda/PDA.dm @@ -1,6 +1,9 @@ //The advanced pea-green monochrome lcd of tomorrow. +// EDIT 2020-09-21: We have had NanoUI PDAs for years, and I am now finally TGUI-ing them +// They arent pea green trash. I DEFY YOU COMMENTS!!! -aa +/// Global list of all PDAs in the world GLOBAL_LIST_EMPTY(PDAs) @@ -22,7 +25,6 @@ GLOBAL_LIST_EMPTY(PDAs) var/obj/item/cartridge/cartridge = null //current cartridge var/datum/data/pda/app/current_app = null var/datum/data/pda/app/lastapp = null - var/ui_tick = 0 //Secondary variables var/model_name = "Thinktronic 5230 Personal Data Assistant" @@ -47,7 +49,6 @@ GLOBAL_LIST_EMPTY(PDAs) new/datum/data/pda/app/messenger, new/datum/data/pda/app/manifest, new/datum/data/pda/app/atmos_scanner, - new/datum/data/pda/utility/scanmode/notes, new/datum/data/pda/utility/flashlight) var/list/shortcut_cache = list() var/list/shortcut_cat_order = list() @@ -101,92 +102,11 @@ GLOBAL_LIST_EMPTY(PDAs) if((!istype(over_object, /obj/screen)) && can_use()) return attack_self(M) -/obj/item/pda/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state) - ui_tick++ - var/datum/nanoui/old_ui = SSnanoui.get_open_ui(user, src, "main") - var/auto_update = 1 - if(!current_app) - return - - if(current_app.update == PDA_APP_NOUPDATE && current_app == lastapp) - auto_update = 0 - if(old_ui && (current_app == lastapp && ui_tick % 5 && current_app.update == PDA_APP_UPDATE_SLOW)) - return - - lastapp = current_app - - var/title = "Personal Data Assistant" - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, 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, "pda.tmpl", title, 630, 600, state = state) - ui.set_state_key("pda") - - // open the new ui window - ui.open() - - // auto update every Master Controller tick - ui.set_auto_update(auto_update) - -/obj/item/pda/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) - var/data[0] - - data["owner"] = owner // Who is your daddy... - data["ownjob"] = ownjob // ...and what does he do? - - // update list of shortcuts, only if they changed - if(!shortcut_cache.len) - shortcut_cache = list() - shortcut_cat_order = list() - var/prog_list = programs.Copy() - if(cartridge) - prog_list |= cartridge.programs - - for(var/A in prog_list) - var/datum/data/pda/P = A - - if(P.hidden) - continue - var/list/cat - if(P.category in shortcut_cache) - cat = shortcut_cache[P.category] - else - cat = list() - shortcut_cache[P.category] = cat - shortcut_cat_order += P.category - cat |= list(list(name = P.name, icon = P.icon, notify_icon = P.notify_icon, ref = "\ref[P]")) - - // force the order of a few core categories - shortcut_cat_order = list("General") \ - + sortList(shortcut_cat_order - list("General", "Scanners", "Utilities")) \ - + list("Scanners", "Utilities") - - data["idInserted"] = (id ? 1 : 0) - data["idLink"] = (id ? text("[id.registered_name], [id.assignment]") : "--------") - - data["useRetro"] = retro_mode - - data["cartridge_name"] = cartridge ? cartridge.name : "" - data["stationTime"] = station_time_timestamp() - - data["app"] = list() - current_app.update_ui(user, data) - data["app"] |= list( - "name" = current_app.title, - "icon" = current_app.icon, - "template" = current_app.template, - "has_back" = current_app.has_back) - - return data - /obj/item/pda/attack_self(mob/user as mob) user.set_machine(src) if(active_uplink_check(user)) return - ui_interact(user) //NanoUI requires this proc + tgui_interact(user) /obj/item/pda/proc/start_program(datum/data/pda/P) if(P && ((P in programs) || (cartridge && (P in cartridge.programs)))) @@ -212,79 +132,8 @@ GLOBAL_LIST_EMPTY(PDAs) var/datum/data/pda/P = A P.pda = src -/obj/item/pda/Topic(href, href_list) - . = ..() - if(.) - return - - var/mob/user = usr - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") - var/mob/living/U = usr - if(usr.stat == DEAD) - return 0 - if(!can_use()) //Why reinvent the wheel? There's a proc that does exactly that. - U.unset_machine() - if(ui) - ui.close() - return 0 - - add_fingerprint(U) - U.set_machine(src) - - if(href_list["radiomenu"] && !isnull(cartridge) && !isnull(cartridge.radio)) - cartridge.radio.Topic(href, href_list) - return 1 - - . = 1 - - switch(href_list["choice"]) - if("Home")//Go home, largely replaces the old Return - var/datum/data/pda/app/main_menu/A = find_program(/datum/data/pda/app/main_menu) - if(A) - start_program(A) - if("StartProgram") - if(href_list["program"]) - var/datum/data/pda/app/A = locate(href_list["program"]) - if(A) - start_program(A) - if("Eject")//Ejects the cart, only done from hub. - if(!isnull(cartridge)) - var/turf/T = loc - if(ismob(T)) - T = T.loc - var/obj/item/cartridge/C = cartridge - C.forceMove(T) - if(scanmode in C.programs) - scanmode = null - if(current_app in C.programs) - start_program(find_program(/datum/data/pda/app/main_menu)) - if(C.radio) - C.radio.hostpda = null - for(var/datum/data/pda/P in notifying_programs) - if(P in C.programs) - P.unnotify() - cartridge = null - update_shortcuts() - if("Authenticate")//Checks for ID - id_check(usr, 1) - if("Retro") - retro_mode = !retro_mode - if("Ringtone") - return set_ringtone() - else - if(current_app) - . = current_app.Topic(href, href_list) - -//EXTRA FUNCTIONS=================================== - if((honkamt > 0) && (prob(60)))//For clown virus. - honkamt-- - playsound(loc, 'sound/items/bikehorn.ogg', 30, 1) - - return // return 1 tells it to refresh the UI in NanoUI - /obj/item/pda/proc/close(mob/user) - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") - ui.close() + SStgui.close_uis(src) /obj/item/pda/verb/verb_reset_pda() set category = "Object" @@ -299,6 +148,7 @@ GLOBAL_LIST_EMPTY(PDAs) notifying_programs.Cut() overlays -= image('icons/obj/pda.dmi', "pda-r") to_chat(usr, "You press the reset button on \the [src].") + SStgui.update_uis(src) else to_chat(usr, "You cannot do this while restrained.") @@ -327,6 +177,7 @@ GLOBAL_LIST_EMPTY(PDAs) var/mob/M = loc M.put_in_hands(id) to_chat(user, "You remove the ID from the [name].") + SStgui.update_uis(src) else id.forceMove(get_turf(src)) overlays -= image('icons/goonstation/objects/pda_overlay.dmi', id.icon_state) @@ -403,6 +254,7 @@ GLOBAL_LIST_EMPTY(PDAs) cartridge.update_programs(src) update_shortcuts() to_chat(user, "You insert [cartridge] into [src].") + SStgui.update_uis(src) if(cartridge.radio) cartridge.radio.hostpda = src @@ -417,6 +269,7 @@ GLOBAL_LIST_EMPTY(PDAs) ownrank = idcard.rank name = "PDA-[owner] ([ownjob])" to_chat(user, "Card scanned.") + SStgui.update_uis(src) else //Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand. if(((src in user.contents) && (C in user.contents)) || (istype(loc, /turf) && in_range(src, user) && (C in user.contents)) ) @@ -424,12 +277,14 @@ GLOBAL_LIST_EMPTY(PDAs) id_check(user, 2) to_chat(user, "You put the ID into \the [src]'s slot.
    You can remove it with ALT click.
    ") overlays += image('icons/goonstation/objects/pda_overlay.dmi', C.icon_state) + SStgui.update_uis(src) else if(istype(C, /obj/item/paicard) && !src.pai) user.drop_item() C.forceMove(src) pai = C to_chat(user, "You slot \the [C] into [src].") + SStgui.update_uis(src) else if(istype(C, /obj/item/pen)) var/obj/item/pen/O = locate() in src if(O) diff --git a/code/modules/pda/app.dm b/code/modules/pda/app.dm index 655bb891de5..82047a073c4 100644 --- a/code/modules/pda/app.dm +++ b/code/modules/pda/app.dm @@ -69,7 +69,8 @@ pda.current_app = src return 1 -/datum/data/pda/app/proc/update_ui(mob/user as mob, list/data) +/datum/data/pda/app/proc/update_ui(mob/user, list/data) + return // Utilities just have a button on the home screen, but custom code when clicked @@ -99,8 +100,10 @@ name = "Disable [base_name]" pda.update_shortcuts() - return 1 + return TRUE -/datum/data/pda/utility/scanmode/proc/scan_mob(mob/living/C as mob, mob/living/user as mob) +/datum/data/pda/utility/scanmode/proc/scan_mob(mob/living/C, mob/living/user) + return -/datum/data/pda/utility/scanmode/proc/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) +/datum/data/pda/utility/scanmode/proc/scan_atom(atom/A, mob/user) + return diff --git a/code/modules/pda/cart.dm b/code/modules/pda/cart.dm index 6339f5375d7..d4845926786 100644 --- a/code/modules/pda/cart.dm +++ b/code/modules/pda/cart.dm @@ -230,7 +230,6 @@ new/datum/data/pda/app/janitor, new/datum/data/pda/app/supply, - new/datum/data/pda/app/mule_control, new/datum/data/pda/app/status_display) @@ -267,7 +266,6 @@ new/datum/data/pda/app/janitor, new/datum/data/pda/app/supply, - new/datum/data/pda/app/mule_control, new/datum/data/pda/app/status_display) diff --git a/code/modules/pda/cart_apps.dm b/code/modules/pda/cart_apps.dm index d47f4184daf..59168c58871 100644 --- a/code/modules/pda/cart_apps.dm +++ b/code/modules/pda/cart_apps.dm @@ -12,20 +12,24 @@ "message1" = message1 ? message1 : "(none)", "message2" = message2 ? message2 : "(none)") -/datum/data/pda/app/status_display/Topic(href, list/href_list) - switch(href_list["choice"]) +/datum/data/pda/app/status_display/tgui_act(action, list/params) + if(..()) + return + + . = TRUE + switch(action) if("Status") - switch(href_list["statdisp"]) + switch(params["statdisp"]) if("message") post_status("message", message1, message2) if("alert") - post_status("alert", href_list["alert"]) + post_status("alert", params["alert"]) if("setmsg1") message1 = clean_input("Line 1", "Enter Message Text", message1) if("setmsg2") message2 = clean_input("Line 2", "Enter Message Text", message2) else - post_status(href_list["statdisp"]) + post_status(params["statdisp"]) /datum/data/pda/app/status_display/proc/post_status(var/command, var/data1, var/data2) var/datum/radio_frequency/frequency = SSradio.return_frequency(DISPLAY_FREQ) @@ -57,75 +61,64 @@ /datum/data/pda/app/signaller name = "Signaler System" icon = "rss" - template = "pda_signaller" + template = "pda_signaler" category = "Utilities" /datum/data/pda/app/signaller/update_ui(mob/user as mob, list/data) if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/signal)) var/obj/item/integrated_radio/signal/R = pda.cartridge.radio - data["signal_freq"] = format_frequency(R.frequency) - data["signal_code"] = R.code + data["frequency"] = R.frequency + data["code"] = R.code + data["minFrequency"] = PUBLIC_LOW_FREQ + data["maxFrequency"] = PUBLIC_HIGH_FREQ + +/datum/data/pda/app/signaller/tgui_act(action, list/params) + if(..()) + return + + . = TRUE -/datum/data/pda/app/signaller/Topic(href, list/href_list) if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/signal)) var/obj/item/integrated_radio/signal/R = pda.cartridge.radio - switch(href_list["choice"]) - if("Send Signal") + switch(action) + if("signal") spawn(0) R.send_signal("ACTIVATE") - if("Signal Frequency") - var/new_frequency = sanitize_frequency(R.frequency + text2num(href_list["sfreq"])) + if("freq") + var/new_frequency = sanitize_frequency(text2num(params["freq"]) * 10) R.set_frequency(new_frequency) - if("Signal Code") - R.code += text2num(href_list["scode"]) - R.code = round(R.code) - R.code = min(100, R.code) - R.code = max(1, R.code) + if("code") + R.code = clamp(text2num(params["code"]), 1, 100) /datum/data/pda/app/power name = "Power Monitor" - icon = "exclamation-triangle" + icon = "bolt" template = "pda_power" category = "Engineering" update = PDA_APP_UPDATE_SLOW - var/obj/machinery/computer/monitor/powmonitor = null + var/datum/tgui_module/power_monitor/digital/pm = new /datum/data/pda/app/power/update_ui(mob/user as mob, list/data) - update = PDA_APP_UPDATE_SLOW + data.Add(pm.tgui_data()) - if(powmonitor && !isnull(powmonitor.powernet)) - data["records"] = list( - "powerconnected" = 1, - "poweravail" = powmonitor.powernet.avail, - "powerload" = num2text(powmonitor.powernet.viewload, 10), - "powerdemand" = powmonitor.powernet.load, - "apcs" = GLOB.apc_repository.apc_data(powmonitor.powernet)) - has_back = 1 - else - data["records"] = list( - "powerconnected" = 0, - "powermonitors" = GLOB.powermonitor_repository.powermonitor_data()) - has_back = 0 +// All 4 args are important here because proxying matters +/datum/data/pda/app/power/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return -/datum/data/pda/app/power/Topic(href, list/href_list) - switch(href_list["choice"]) - if("Power Select") - var/pref = href_list["target"] - powmonitor = locate(pref) - update = PDA_APP_UPDATE - if("Back") - powmonitor = null - update = PDA_APP_UPDATE + . = TRUE + // Observe + pm.tgui_act(action, params, ui, state) /datum/data/pda/app/crew_records var/datum/data/record/general_records = null /datum/data/pda/app/crew_records/update_ui(mob/user as mob, list/data) - var/list/records[0] + var/list/records = list() if(general_records && (general_records in GLOB.data_core.general)) data["records"] = records @@ -135,23 +128,31 @@ for(var/A in sortRecord(GLOB.data_core.general)) var/datum/data/record/R = A if(R) - records += list(list(Name = R.fields["name"], "ref" = "\ref[R]")) + records += list(list(Name = R.fields["name"], "uid" = "[R.UID()]")) data["recordsList"] = records + data["records"] = null return null -/datum/data/pda/app/crew_records/Topic(href, list/href_list) - switch(href_list["choice"]) +/datum/data/pda/app/crew_records/tgui_act(action, list/params) + if(..()) + return + + . = TRUE + + switch(action) if("Records") - var/datum/data/record/R = locate(href_list["target"]) + var/datum/data/record/R = locateUID(params["target"]) if(R && (R in GLOB.data_core.general)) load_records(R) + return if("Back") general_records = null - has_back = 0 + has_back = FALSE + return /datum/data/pda/app/crew_records/proc/load_records(datum/data/record/R) general_records = R - has_back = 1 + has_back = TRUE /datum/data/pda/app/crew_records/medical name = "Medical Records" @@ -181,7 +182,7 @@ /datum/data/pda/app/crew_records/security name = "Security Records" - icon = "tags" + icon = "id-badge" template = "pda_security" category = "Security" @@ -212,8 +213,8 @@ category = "Security" /datum/data/pda/app/secbot_control/update_ui(mob/user as mob, list/data) - var/botsData[0] - var/beepskyData[0] + var/list/botsData = list() + var/list/beepskyData = list() if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/beepsky)) var/obj/item/integrated_radio/beepsky/SC = pda.cartridge.radio beepskyData["active"] = SC.active ? sanitize(SC.active.name) : null @@ -229,17 +230,17 @@ for(var/mob/living/simple_animal/bot/B in SC.botlist) botsCount++ if(B.loc) - botsData[++botsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "ref" = "\ref[B]") + botsData[++botsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "uid" = "[B.UID()]") if(!botsData.len) - botsData[++botsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) + botsData[++botsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "uid"= null) beepskyData["bots"] = botsData beepskyData["count"] = botsCount else beepskyData["active"] = 0 - botsData[++botsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) + botsData[++botsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "uid"= null) beepskyData["botstatus"] = list("loca" = null, "mode" = null) beepskyData["bots"] = botsData beepskyData["count"] = 0 @@ -247,11 +248,42 @@ data["beepsky"] = beepskyData -/datum/data/pda/app/secbot_control/Topic(href, list/href_list) - switch(href_list["choice"]) +/datum/data/pda/app/secbot_control/tgui_act(action, list/params) + if(..()) + return + + . = TRUE + + // Aight listen up. Its time for a comment rant again. + // The old way of doing this was to proxy things directly from the NanoUI into the PDA's cartridge's radio Topic() function directly + // It was AWFUL and took me 30 minutes to even understand + // This is in no way a good solution, but it works atleast + // Why do we rely on this whole "magical radio system" anyways + // Hell, I would rather take GLOBs with direct interactions over this + // WHYYYYYYYYYYYYYYY -aa07 + + switch(action) if("Back") if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/beepsky)) - pda.cartridge.radio.Topic(null, list(radiomenu = "1", op = "botlist")) + pda.cartridge.radio.Topic(null, list(op = "botlist")) + if("Rescan") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/beepsky)) + pda.cartridge.radio.Topic(null, list(op = "scanbots")) + if("AccessBot") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/beepsky)) + pda.cartridge.radio.Topic(null, list(op = "control", bot = params["uid"])) + if("Stop") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/beepsky)) + pda.cartridge.radio.Topic(null, list(op = "stop")) + if("Go") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/beepsky)) + pda.cartridge.radio.Topic(null, list(op = "go")) + if("Home") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/beepsky)) + pda.cartridge.radio.Topic(null, list(op = "home")) + if("Summon") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/beepsky)) + pda.cartridge.radio.Topic(null, list(op = "summon")) /datum/data/pda/app/mule_control name = "Delivery Bot Control" @@ -260,8 +292,8 @@ category = "Quartermaster" /datum/data/pda/app/mule_control/update_ui(mob/user as mob, list/data) - var/muleData[0] - var/mulebotsData[0] + var/list/muleData = list() + var/list/mulebotsData = list() if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/mule)) var/obj/item/integrated_radio/mule/QC = pda.cartridge.radio muleData["active"] = QC.active ? sanitize(QC.active.name) : null @@ -279,10 +311,10 @@ for(var/mob/living/simple_animal/bot/B in QC.botlist) mulebotsCount++ if(B.loc) - mulebotsData[++mulebotsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "ref" = "\ref[B]") + mulebotsData[++mulebotsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "uid" = "[B.UID()]") if(!mulebotsData.len) - mulebotsData[++mulebotsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) + mulebotsData[++mulebotsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "uid"= null) muleData["bots"] = mulebotsData muleData["count"] = mulebotsCount @@ -290,28 +322,63 @@ else muleData["botstatus"] = list("loca" = null, "mode" = -1,"home"=null,"powr" = null,"retn" =null, "pick"=null, "load" = null, "dest" = null) muleData["active"] = 0 - mulebotsData[++mulebotsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) + mulebotsData[++mulebotsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "uid"= null) muleData["bots"] = mulebotsData muleData["count"] = 0 has_back = 0 data["mulebot"] = muleData -/datum/data/pda/app/mule_control/Topic(href, list/href_list) - switch(href_list["choice"]) +/datum/data/pda/app/mule_control/tgui_act(action, list/params) + if(..()) + return + + . = TRUE + + // Heres the exact same shit as before, but worse + // See L257 to L263 for explanation + + switch(action) if("Back") if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/mule)) - pda.cartridge.radio.Topic(null, list(radiomenu = "1", op = "botlist")) + pda.cartridge.radio.Topic(null, list(op = "botlist")) + if("Rescan") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/mule)) + pda.cartridge.radio.Topic(null, list(op = "scanbots")) + if("AccessBot") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/mule)) + pda.cartridge.radio.Topic(null, list(op = "control", bot = params["uid"])) + if("Unload") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/mule)) + pda.cartridge.radio.Topic(null, list(op = "unload")) + if("SetDest") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/mule)) + pda.cartridge.radio.Topic(null, list(op = "setdest")) + if("SetAutoReturn") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/mule)) + pda.cartridge.radio.Topic(null, list(op = params["autoReturnType"])) // "retoff" or "reton" + if("SetAutoPickup") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/mule)) + pda.cartridge.radio.Topic(null, list(op = params["autoPickupType"])) // "pickoff" or "pickon" + if("Stop") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/mule)) + pda.cartridge.radio.Topic(null, list(op = "stop")) + if("Start") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/mule)) + pda.cartridge.radio.Topic(null, list(op = "start")) + if("ReturnHome") + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/integrated_radio/mule)) + pda.cartridge.radio.Topic(null, list(op = "home")) /datum/data/pda/app/supply name = "Supply Records" - icon = "file-text-o" - template = "pda_supply" + icon = "archive" + template = "pda_supplyrecords" category = "Quartermaster" update = PDA_APP_UPDATE_SLOW /datum/data/pda/app/supply/update_ui(mob/user as mob, list/data) - var/supplyData[0] + var/list/supplyData = list() if(SSshuttle.supply.mode == SHUTTLE_CALL) supplyData["shuttle_moving"] = 1 @@ -324,7 +391,7 @@ supplyData["shuttle_time"] = "([SSshuttle.supply.timeLeft(600)] Mins)" var/supplyOrderCount = 0 - var/supplyOrderData[0] + var/list/supplyOrderData = list() for(var/S in SSshuttle.shoppinglist) var/datum/supply_order/SO = S supplyOrderCount++ @@ -337,7 +404,7 @@ supplyData["approved_count"] = supplyOrderCount var/requestCount = 0 - var/requestData[0] + var/list/requestData = list() for(var/S in SSshuttle.requestlist) var/datum/supply_order/SO = S requestCount++ @@ -353,13 +420,13 @@ /datum/data/pda/app/janitor name = "Custodial Locator" - icon = "trash-o" + icon = "trash" template = "pda_janitor" category = "Utilities" update = PDA_APP_UPDATE_SLOW /datum/data/pda/app/janitor/update_ui(mob/user as mob, list/data) - var/JaniData[0] + var/list/JaniData = list() var/turf/cl = get_turf(pda) if(cl) @@ -367,7 +434,7 @@ else JaniData["user_loc"] = list("x" = 0, "y" = 0) - var/MopData[0] + var/list/MopData = list() for(var/obj/item/mop/M in GLOB.janitorial_equipment) var/turf/ml = get_turf(M) if(ml) @@ -376,7 +443,7 @@ var/direction = get_dir(pda, M) MopData[++MopData.len] = list ("x" = ml.x, "y" = ml.y, "dir" = uppertext(dir2text(direction)), "status" = M.reagents.total_volume ? "Wet" : "Dry") - var/BucketData[0] + var/list/BucketData = list() for(var/obj/structure/mopbucket/B in GLOB.janitorial_equipment) var/turf/bl = get_turf(B) if(bl) @@ -385,7 +452,7 @@ var/direction = get_dir(pda,B) BucketData[++BucketData.len] = list ("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "volume" = B.reagents.total_volume, "max_volume" = B.reagents.maximum_volume) - var/CbotData[0] + var/list/CbotData = list() for(var/mob/living/simple_animal/bot/cleanbot/B in GLOB.bots_list) var/turf/bl = get_turf(B) if(bl) @@ -394,7 +461,7 @@ var/direction = get_dir(pda,B) CbotData[++CbotData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.on ? "Online" : "Offline") - var/CartData[0] + var/list/CartData = list() for(var/obj/structure/janitorialcart/B in GLOB.janitorial_equipment) var/turf/bl = get_turf(B) if(bl) diff --git a/code/modules/pda/core_apps.dm b/code/modules/pda/core_apps.dm index 41f9f994420..1b4be7e8274 100644 --- a/code/modules/pda/core_apps.dm +++ b/code/modules/pda/core_apps.dm @@ -6,19 +6,24 @@ /datum/data/pda/app/main_menu/update_ui(mob/user as mob, list/data) title = pda.name - data["app"]["is_home"] = 1 + data["app"]["is_home"] = TRUE data["apps"] = pda.shortcut_cache data["categories"] = pda.shortcut_cat_order data["pai"] = !isnull(pda.pai) // pAI inserted? - var/list/notifying[0] - for(var/P in pda.notifying_programs) - notifying["\ref[P]"] = 1 + var/list/notifying = list() + for(var/datum/data/pda/P in pda.notifying_programs) + notifying["[P.UID()]"] = TRUE data["notifying"] = notifying -/datum/data/pda/app/main_menu/Topic(href, list/href_list) - switch(href_list["choice"]) +/datum/data/pda/app/main_menu/tgui_act(action, list/params) + if(..()) + return + + . = TRUE + + switch(action) if("UpdateInfo") pda.ownjob = pda.id.assignment pda.ownrank = pda.id.rank @@ -28,10 +33,10 @@ if(pda.pai.loc != pda) pda.pai = null else - switch(href_list["option"]) - if("1") // Configure pAI device + switch(text2num(params["option"])) + if(1) // Configure pAI device pda.pai.attack_self(usr) - if("2") // Eject pAI device + if(2) // Eject pAI device var/turf/T = get_turf_or_move(pda.loc) if(T) pda.pai.loc = T @@ -40,10 +45,9 @@ /datum/data/pda/app/notekeeper name = "Notekeeper" icon = "sticky-note-o" - template = "pda_notekeeper" + template = "pda_notes" - var/note = null - var/notehtml = "" + var/note /datum/data/pda/app/notekeeper/start() . = ..() @@ -51,16 +55,22 @@ note = "Congratulations, your station has chosen the [pda.model_name]!" /datum/data/pda/app/notekeeper/update_ui(mob/user as mob, list/data) - data["note"] = note // current pda notes + data["note"] = note // current pda notes -/datum/data/pda/app/notekeeper/Topic(href, list/href_list) - switch(href_list["choice"]) +/datum/data/pda/app/notekeeper/tgui_act(action, params) + if(..()) + return + + . = TRUE + + switch(action) if("Edit") - var/n = input("Please enter message", name, notehtml) as message + var/n = input("Please enter message", name, note) as message if(pda.loc == usr) + // TGUI will auto-reject supplied HTML + // However, the admin var-edit window will not + // SANITISATION IS IMPORTANT. DO NOT NEGLECT. note = adminscrub(n) - notehtml = html_decode(note) - note = replacetext(note, "\n", "
    ") else pda.close(usr) @@ -74,8 +84,6 @@ GLOB.data_core.get_manifest_json() data["manifest"] = GLOB.PDA_Manifest -/datum/data/pda/app/manifest/Topic(href, list/href_list) - /datum/data/pda/app/atmos_scanner name = "Atmospheric Scan" icon = "fire" @@ -84,6 +92,7 @@ update = PDA_APP_UPDATE_SLOW /datum/data/pda/app/atmos_scanner/update_ui(mob/user as mob, list/data) + var/list/results = list() var/turf/T = get_turf(user.loc) if(!isnull(T)) var/datum/gas_mixture/environment = T.return_air() @@ -97,15 +106,17 @@ var/co2_level = environment.carbon_dioxide/total_moles var/plasma_level = environment.toxins/total_moles var/unknown_level = 1-(o2_level+n2_level+co2_level+plasma_level) - data["aircontents"] = list( - "pressure" = pressure, - "nitrogen" = n2_level*100, - "oxygen" = o2_level*100, - "carbon_dioxide" = co2_level*100, - "plasma" = plasma_level*100, - "other" = unknown_level, - "temp" = environment.temperature-T0C, - "reading" = 1 - ) - if(isnull(data["aircontents"])) - data["aircontents"] = list("reading" = 0) + results = list( + list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), + list("entry" = "Temperature", "units" = "C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), + list("entry" = "Oxygen", "units" = "%", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), + list("entry" = "Nitrogen", "units" = "%", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), + list("entry" = "Carbon Dioxide", "units" = "%", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Plasma", "units" = "%", "val" = "[round(plasma_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Other", "units" = "%", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) + ) + + if(isnull(results)) + results = list(list("entry" = "pressure", "units" = "%", "val" = "0", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80)) + + data["aircontents"] = results diff --git a/code/modules/pda/messenger.dm b/code/modules/pda/messenger.dm index 5ccef4dee41..4f17cbf041e 100644 --- a/code/modules/pda/messenger.dm +++ b/code/modules/pda/messenger.dm @@ -6,24 +6,22 @@ template = "pda_messenger" var/toff = 0 //If 1, messenger disabled - var/list/tnote[0] //Current Texts + var/list/tnote = list() //Current Texts var/last_text //No text spamming var/m_hidden = 0 // Is the PDA hidden from the PDA list? var/active_conversation = null // New variable that allows us to only view a single conversation. var/list/conversations = list() // For keeping up with who we have PDA messsages from. - var/latest_post = 0 - var/auto_scroll = 1 /datum/data/pda/app/messenger/start() . = ..() unnotify() - latest_post = 0 /datum/data/pda/app/messenger/update_ui(mob/user as mob, list/data) data["silent"] = notify_silent // does the pda make noise when it receives a message? data["toff"] = toff // is the messenger function turned off? - data["active_conversation"] = active_conversation // Which conversation are we following right now? + // Yes I know convo is awful, but it lets me stay inside the 80 char TGUI line limit + data["active_convo"] = active_conversation // Which conversation are we following right now? has_back = active_conversation if(active_conversation) @@ -33,22 +31,19 @@ data["convo_name"] = sanitize(c["owner"]) data["convo_job"] = sanitize(c["job"]) break - data["auto_scroll"] = auto_scroll - data["latest_post"] = latest_post - latest_post = tnote.len else - var/convopdas[0] - var/pdas[0] + var/list/convopdas = list() + var/list/pdas = list() for(var/A in GLOB.PDAs) var/obj/item/pda/P = A var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) if(!P.owner || PM.toff || P == pda || PM.m_hidden) continue - if(conversations.Find("\ref[P]")) - convopdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "1"))) + if(conversations.Find("[P.UID()]")) + convopdas.Add(list(list("Name" = "[P]", "uid" = "[P.UID()]", "Detonate" = "[P.detonate]", "inconvo" = "1"))) else - pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0"))) + pdas.Add(list(list("Name" = "[P]", "uid" = "[P.UID()]", "Detonate" = "[P.detonate]", "inconvo" = "0"))) data["convopdas"] = convopdas data["pdas"] = pdas @@ -57,28 +52,30 @@ if(pda.cartridge) for(var/A in pda.cartridge.messenger_plugins) var/datum/data/pda/messenger_plugin/P = A - plugins += list(list(name = P.name, icon = P.icon, ref = "\ref[P]")) + plugins += list(list(name = P.name, icon = P.icon, uid = "[P.UID()]")) data["plugins"] = plugins if(pda.cartridge) data["charges"] = pda.cartridge.charges ? pda.cartridge.charges : 0 -/datum/data/pda/app/messenger/Topic(href, list/href_list) - if(!pda.can_use()) +/datum/data/pda/app/messenger/tgui_act(action, list/params) + if(..()) return + unnotify() - switch(href_list["choice"]) + . = TRUE + switch(action) if("Toggle Messenger") toff = !toff if("Toggle Ringer")//If viewing texts then erase them, if not then toggle silent status notify_silent = !notify_silent if("Clear")//Clears messages - if(href_list["option"] == "All") + if(params["option"] == "All") tnote.Cut() conversations.Cut() - if(href_list["option"] == "Convo") - var/new_tnote[0] + if(params["option"] == "Convo") + var/list/new_tnote = list() for(var/i in tnote) if(i["target"] != active_conversation) new_tnote[++new_tnote.len] = i @@ -86,36 +83,30 @@ conversations.Remove(active_conversation) active_conversation = null - latest_post = 0 if("Message") - var/obj/item/pda/P = locate(href_list["target"]) + var/obj/item/pda/P = locateUID(params["target"]) create_message(usr, P) - if(href_list["target"] in conversations) // Need to make sure the message went through, if not welp. - active_conversation = href_list["target"] - latest_post = 0 + if(params["target"] in conversations) // Need to make sure the message went through, if not welp. + active_conversation = params["target"] if("Select Conversation") - var/P = href_list["convo"] + var/P = params["target"] for(var/n in conversations) if(P == n) active_conversation = P - latest_post = 0 if("Messenger Plugin") - if(!href_list["target"] || !href_list["plugin"]) + if(!params["target"] || !params["plugin"]) return - var/obj/item/pda/P = locate(href_list["target"]) + var/obj/item/pda/P = locateUID(params["target"]) if(!P) to_chat(usr, "PDA not found.") - var/datum/data/pda/messenger_plugin/plugin = locate(href_list["plugin"]) + var/datum/data/pda/messenger_plugin/plugin = locateUID(params["plugin"]) if(plugin && (plugin in pda.cartridge.messenger_plugins)) plugin.messenger = src plugin.user_act(usr, P) if("Back") active_conversation = null - latest_post = 0 - if("Autoscroll") - auto_scroll = !auto_scroll /datum/data/pda/app/messenger/proc/create_message(var/mob/living/U, var/obj/item/pda/P) var/t = input(U, "Please enter message", name, null) as text|null @@ -180,8 +171,8 @@ useMS.send_pda_message("[P.owner]","[pda.owner]","[t]") - tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]"))) - PM.tnote.Add(list(list("sent" = 0, "owner" = "[pda.owner]", "job" = "[pda.ownjob]", "message" = "[t]", "target" = "\ref[pda]"))) + tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "[P.UID()]"))) + PM.tnote.Add(list(list("sent" = 0, "owner" = "[pda.owner]", "job" = "[pda.ownjob]", "message" = "[t]", "target" = "[pda.UID()]"))) pda.investigate_log("PDA Message - [U.key] - [pda.owner] -> [P.owner]: [t]", "pda") // Show it to ghosts @@ -190,13 +181,13 @@ var/ghost_message = "[pda.owner] ([ghost_follow_link(pda, ghost=M)]) PDA Message --> [P.owner] ([ghost_follow_link(P, ghost=M)]): [t]" to_chat(M, "[ghost_message]") - if(!conversations.Find("\ref[P]")) - conversations.Add("\ref[P]") - if(!PM.conversations.Find("\ref[pda]")) - PM.conversations.Add("\ref[pda]") + if(!conversations.Find("[P.UID()]")) + conversations.Add("[P.UID()]") + if(!PM.conversations.Find("[pda.UID()]")) + PM.conversations.Add("[pda.UID()]") - SSnanoui.update_user_uis(U, P) // Update the sending user's PDA UI so that they can see the new message - PM.notify("Message from [pda.owner] ([pda.ownjob]), \"[t]\" (Reply)") + SStgui.update_uis(src) + PM.notify("Message from [pda.owner] ([pda.ownjob]), \"[t]\" (Reply)") log_pda("(PDA: [src.name]) sent \"[t]\" to [P.name]", usr) else to_chat(U, "ERROR: Messaging server is not responding.") @@ -230,3 +221,15 @@ /datum/data/pda/app/messenger/proc/can_receive() return pda.owner && !toff && !hidden + +// Handler for the in-chat reply button +/datum/data/pda/app/messenger/Topic(href, href_list) + if(!pda.can_use()) + return + unnotify() + switch(href_list["choice"]) + if("Message") + var/obj/item/pda/P = locateUID(href_list["target"]) + create_message(usr, P) + if(href_list["target"] in conversations) // Need to make sure the message went through, if not welp. + active_conversation = href_list["target"] diff --git a/code/modules/pda/mob_hunt_game_app.dm b/code/modules/pda/mob_hunt_game_app.dm index c72aa6d0d41..256ec0f91d9 100644 --- a/code/modules/pda/mob_hunt_game_app.dm +++ b/code/modules/pda/mob_hunt_game_app.dm @@ -152,8 +152,12 @@ bait = bait.type new bait(1, get_turf(pda)) -/datum/data/pda/app/mob_hunter_game/Topic(href, list/href_list) - switch(href_list["choice"]) +/datum/data/pda/app/mob_hunter_game/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return + + . = TRUE + switch(action) if("Rename") assign_nickname() if("Release") diff --git a/code/modules/pda/pda_tgui.dm b/code/modules/pda/pda_tgui.dm new file mode 100644 index 00000000000..5f4a5699c7a --- /dev/null +++ b/code/modules/pda/pda_tgui.dm @@ -0,0 +1,106 @@ +// All the TGUI interactions are in their own file to keep things simpler + +/obj/item/pda/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_inventory_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "PDA", name, 600, 650, master_ui, state) + ui.open() + + +/obj/item/pda/tgui_data(mob/user) + var/list/data = list() + + data["owner"] = owner + data["ownjob"] = ownjob + + // update list of shortcuts, only if they changed + if(!length(shortcut_cache)) + shortcut_cache = list() + shortcut_cat_order = list() + var/list/prog_list = programs.Copy() + if(cartridge) + prog_list |= cartridge.programs + + for(var/A in prog_list) + var/datum/data/pda/P = A + + if(P.hidden) + continue + var/list/cat + if(P.category in shortcut_cache) + cat = shortcut_cache[P.category] + else + cat = list() + shortcut_cache[P.category] = cat + shortcut_cat_order += P.category + cat |= list(list(name = P.name, icon = P.icon, notify_icon = P.notify_icon, uid = "[P.UID()]")) + + // force the order of a few core categories + shortcut_cat_order = list("General") \ + + sortList(shortcut_cat_order - list("General", "Scanners", "Utilities")) \ + + list("Scanners", "Utilities") + + data["idInserted"] = (id ? TRUE : FALSE) + data["idLink"] = (id ? "[id.registered_name], [id.assignment]" : "--------") + + data["cartridge_name"] = cartridge ? cartridge.name : "" + data["stationTime"] = station_time_timestamp() + + data["app"] = list() + data["app"] |= list( + "name" = current_app.title, + "icon" = current_app.icon, + "template" = current_app.template, + "has_back" = current_app.has_back) + + current_app.update_ui(user, data) + + return data + +// Yes the stupid amount of args here is important, see L102 +/obj/item/pda/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return + + add_fingerprint(usr) + + . = TRUE + switch(action) + if("Home") //Go home, largely replaces the old Return + var/datum/data/pda/app/main_menu/A = find_program(/datum/data/pda/app/main_menu) + if(A) + start_program(A) + if("StartProgram") + if(params["program"]) + var/datum/data/pda/app/A = locateUID(params["program"]) + if(A) + start_program(A) + if("Eject")//Ejects the cart, only done from hub. + if(!isnull(cartridge)) + var/turf/T = loc + if(ismob(T)) + T = T.loc + var/obj/item/cartridge/C = cartridge + C.forceMove(T) + if(scanmode in C.programs) + scanmode = null + if(current_app in C.programs) + start_program(find_program(/datum/data/pda/app/main_menu)) + if(C.radio) + C.radio.hostpda = null + for(var/datum/data/pda/P in notifying_programs) + if(P in C.programs) + P.unnotify() + cartridge = null + update_shortcuts() + if("Authenticate")//Checks for ID + id_check(usr, 1) + if("Ringtone") + return set_ringtone() + else + if(current_app) + . = current_app.tgui_act(action, params, ui, state) // It needs proxying through down here so apps actually have their interacts called + + if((honkamt > 0) && (prob(60)))//For clown virus. + honkamt-- + playsound(loc, 'sound/items/bikehorn.ogg', 30, 1) diff --git a/code/modules/pda/radio.dm b/code/modules/pda/radio.dm index f214e705268..48d35e9d033 100644 --- a/code/modules/pda/radio.dm +++ b/code/modules/pda/radio.dm @@ -69,27 +69,23 @@ ..() switch(href_list["op"]) if("control") - active = locate(href_list["bot"]) - spawn(0) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + active = locateUID(href_list["bot"]) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) if("scanbots") // find all bots botlist = null - spawn(0) - post_signal(control_freq, "command", "bot_status", s_filter = bot_filter) + post_signal(control_freq, "command", "bot_status", s_filter = bot_filter) if("botlist") active = null if("stop", "go", "home") - spawn(0) - post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = bot_filter) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = bot_filter) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) if("summon") - spawn(0) - post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(hostpda), "useraccess", hostpda.GetAccess(), "user", usr, s_filter = bot_filter) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(hostpda), "useraccess", hostpda.GetAccess(), "user", usr, s_filter = bot_filter) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) /obj/item/integrated_radio/proc/add_to_radio(bot_filter) //Master filter control for bots. Must be placed in the bot's local New() to support map spawned bots. if(SSradio) @@ -123,38 +119,30 @@ ..() switch(href_list["op"]) if("start") - spawn(0) - post_signal(control_freq, "command", "start", "active", active, s_filter = RADIO_MULEBOT) + post_signal(control_freq, "command", "start", "active", active, s_filter = RADIO_MULEBOT) if("unload") - spawn(0) - post_signal(control_freq, "command", "unload", "active", active, s_filter = RADIO_MULEBOT) + post_signal(control_freq, "command", "unload", "active", active, s_filter = RADIO_MULEBOT) if("setdest") if(GLOB.deliverybeacons) var/dest = input("Select Bot Destination", "Mulebot [active.suffix] Interlink", active.destination) as null|anything in GLOB.deliverybeacontags if(dest) - spawn(0) - post_signal(control_freq, "command", "target", "active", active, "destination", dest, s_filter = RADIO_MULEBOT) + post_signal(control_freq, "command", "target", "active", active, "destination", dest, s_filter = RADIO_MULEBOT) if("retoff") - spawn(0) - post_signal(control_freq, "command", "autoret", "active", active, "value", 0, s_filter = RADIO_MULEBOT) + post_signal(control_freq, "command", "autoret", "active", active, "value", 0, s_filter = RADIO_MULEBOT) if("reton") - spawn(0) - post_signal(control_freq, "command", "autoret", "active", active, "value", 1, s_filter = RADIO_MULEBOT) + post_signal(control_freq, "command", "autoret", "active", active, "value", 1, s_filter = RADIO_MULEBOT) if("pickoff") - spawn(0) - post_signal(control_freq, "command", "autopick", "active", active, "value", 0, s_filter = RADIO_MULEBOT) + post_signal(control_freq, "command", "autopick", "active", active, "value", 0, s_filter = RADIO_MULEBOT) if("pickon") - spawn(0) - post_signal(control_freq, "command", "autopick", "active", active, "value", 1, s_filter = RADIO_MULEBOT) + post_signal(control_freq, "command", "autopick", "active", active, "value", 1, s_filter = RADIO_MULEBOT) - spawn(10) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_MULEBOT) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_MULEBOT) diff --git a/code/modules/pda/utilities.dm b/code/modules/pda/utilities.dm index 1dfb819c826..81a2271db04 100644 --- a/code/modules/pda/utilities.dm +++ b/code/modules/pda/utilities.dm @@ -10,6 +10,10 @@ name = fon ? "Disable Flashlight" : "Enable Flashlight" pda.update_shortcuts() pda.set_light(fon ? f_lum : 0) + if(fon) + pda.overlays += image('icons/obj/pda.dmi', "pda-light") + else + pda.overlays -= image('icons/obj/pda.dmi', "pda-light") /datum/data/pda/utility/honk name = "Honk Synthesizer" @@ -25,7 +29,7 @@ /datum/data/pda/utility/toggle_door name = "Toggle Door" - icon = "external-link" + icon = "external-link-alt" var/remote_door_id = "" /datum/data/pda/utility/toggle_door/start() @@ -107,7 +111,7 @@ /datum/data/pda/utility/scanmode/gas base_name = "Gas Scanner" - icon = "tachometer" + icon = "tachometer-alt" /datum/data/pda/utility/scanmode/gas/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) if(istype(A, /obj/item/tank)) @@ -134,56 +138,3 @@ var/obj/machinery/atmospherics/unary/tank/T = A pda.atmosanalyzer_scan(T.air_contents, user, T) -/datum/data/pda/utility/scanmode/notes - base_name = "Note Scanner" - icon = "clipboard" - var/datum/data/pda/app/notekeeper/notes - -/datum/data/pda/utility/scanmode/notes/start() - . = ..() - notes = pda.find_program(/datum/data/pda/app/notekeeper) - -/datum/data/pda/utility/scanmode/notes/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) - if(notes && istype(A, /obj/item/paper)) - var/obj/item/paper/P = A - var/list/brlist = list("p", "/p", "br", "hr", "h1", "h2", "h3", "h4", "/h1", "/h2", "/h3", "/h4") - - // JMO 20140705: Makes scanned document show up properly in the notes. Not pretty for formatted documents, - // as this will clobber the HTML, but at least it lets you scan a document. You can restore the original - // notes by editing the note again. (Was going to allow you to edit, but scanned documents are too long.) - var/raw_scan = sanitize_simple(P.info, list("\t" = "", "ÿ" = "")) - var/formatted_scan = "" - // Scrub out the tags (replacing a few formatting ones along the way) - // Find the beginning and end of the first tag. - var/tag_start = findtext(raw_scan, "<") - var/tag_stop = findtext(raw_scan, ">") - // Until we run out of complete tags... - while(tag_start && tag_stop) - var/pre = copytext(raw_scan, 1, tag_start) // Get the stuff that comes before the tag - var/tag = lowertext(copytext(raw_scan, tag_start + 1, tag_stop)) // Get the tag so we can do intellegent replacement - var/tagend = findtext(tag, " ") // Find the first space in the tag if there is one. - // Anything that's before the tag can just be added as is. - formatted_scan = formatted_scan + pre - // If we have a space after the tag (and presumably attributes) just crop that off. - if(tagend) - tag = copytext(tag, 1, tagend) - if(tag in brlist) // Check if it's I vertical space tag. - formatted_scan = formatted_scan + "
    " // If so, add some padding in. - raw_scan = copytext(raw_scan, tag_stop + 1) // continue on with the stuff after the tag - // Look for the next tag in what's left - tag_start = findtext(raw_scan, "<") - tag_stop = findtext(raw_scan, ">") - // Anything that is left in the page. just tack it on to the end as is - formatted_scan = formatted_scan + raw_scan - // If there is something in there already, pad it out. - if(length(notes.note) > 0) - notes.note += "

    " - // Store the scanned document to the notes - notes.note += "Scanned Document. Edit to restore previous notes/delete scan.
    ----------
    " + formatted_scan + "
    " - // notehtml ISN'T set to allow user to get their old notes back. A better implementation would add a "scanned documents" - // feature to the PDA, which would better convey the availability of the feature, but this will work for now. - // Inform the user - to_chat(user, "Paper scanned and OCRed to notekeeper.")//concept of scanning paper copyright brainoblivion 2009 - - else - to_chat(user, "Error scanning [A].") diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 56e38d31935..c4d3fb6cb91 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -25,6 +25,11 @@ #define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds +// main_status var +#define APC_EXTERNAL_POWER_NOTCONNECTED 0 +#define APC_EXTERNAL_POWER_NOENERGY 1 +#define APC_EXTERNAL_POWER_GOOD 2 + // APC malf status #define APC_MALF_NOT_HACKED 1 #define APC_MALF_HACKED 2 // APC hacked by user, and user is in its core. @@ -75,7 +80,7 @@ var/lastused_equip = 0 var/lastused_environ = 0 var/lastused_total = 0 - var/main_status = 0 + var/main_status = APC_EXTERNAL_POWER_NOTCONNECTED powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( var/malfhack = 0 //New var for my changes to AI malf. --NeoFite var/mob/living/silicon/ai/malfai = null //See above --NeoFite @@ -170,6 +175,7 @@ addtimer(CALLBACK(src, .proc/update), 5) /obj/machinery/power/apc/Destroy() + SStgui.close_uis(wires) GLOB.apcs -= src if(malfai && operating) malfai.malf_picker.processing_time = clamp(malfai.malf_picker.processing_time - 10,0,1000) @@ -212,12 +218,11 @@ if(isarea(A)) area = A // no-op, keep the name - else if(isarea(A) && src.areastring == null) + else if(isarea(A) && !areastring) area = A name = "\improper [area.name] APC" else - area = get_area_name(areastring) - name = "\improper [area.name] APC" + name = "\improper [get_area_name(area, TRUE)] APC" area.apc |= src update_icon() @@ -429,8 +434,8 @@ //attack with an item - open/close cover, insert cell, or (un)lock interface /obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params) - if(issilicon(user) && get_dist(src,user)>1) - return src.attack_hand(user) + if(issilicon(user) && get_dist(src, user) > 1) + return attack_hand(user) else if (istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside if(cell) @@ -445,7 +450,7 @@ W.forceMove(src) cell = W user.visible_message(\ - "[user.name] has inserted the power cell to [src.name]!",\ + "[user.name] has inserted the power cell to [name]!",\ "You insert the power cell.") chargecount = 0 update_icon() @@ -474,7 +479,7 @@ return user.visible_message("[user.name] adds cables to the APC frame.", \ "You start adding cables to the APC frame...") - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) if(do_after(user, 20, target = src)) if(C.get_amount() < 10 || !C) return @@ -499,10 +504,10 @@ user.visible_message("[user.name] inserts the power control board into [src].", \ "You start to insert the power control board into the frame...") - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) if(do_after(user, 10, target = src)) - if(has_electronics==0) - has_electronics = 1 + if(!has_electronics) + has_electronics = TRUE locked = FALSE to_chat(user, "You place the power control board inside the frame.") qdel(W) @@ -549,11 +554,11 @@ return to_chat(user, "You are trying to remove the power control board..." ) if(I.use_tool(src, user, 50, volume = I.tool_volume)) - if(has_electronics==1) - has_electronics = 0 + if(has_electronics) + has_electronics = FALSE if(stat & BROKEN) user.visible_message(\ - "[user.name] has broken the power control board inside [src.name]!", + "[user.name] has broken the power control board inside [name]!", "You break the charred power control board and remove the remains.", "You hear a crack.") return @@ -561,19 +566,19 @@ else if(emagged) // We emag board, not APC's frame emagged = FALSE user.visible_message( - "[user.name] has discarded emaged power control board from [src.name]!", - "You discarded shorten board.") + "[user.name] has discarded the shorted power control board from [name]!", + "You discarded the shorted board.") return else if(malfhack) // AI hacks board, not APC's frame user.visible_message(\ - "[user.name] has discarded strangely programmed power control board from [src.name]!", - "You discarded strangely programmed board.") + "[user.name] has discarded strangely the programmed power control board from [name]!", + "You discarded the strangely programmed board.") malfai = null malfhack = 0 return else user.visible_message(\ - "[user.name] has removed the power control board from [src.name]!", + "[user.name] has removed the power control board from [name]!", "You remove the power control board.") new /obj/item/apc_electronics(loc) return @@ -648,7 +653,7 @@ else if(stat & (BROKEN|MAINT)) to_chat(user, "Nothing happens!") else - if(allowed(usr) && !isWireCut(APC_WIRE_IDSCAN) && !malfhack) + if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN) && !malfhack) locked = !locked to_chat(user, "You [ locked ? "lock" : "unlock"] the APC interface.") update_icon() @@ -711,36 +716,29 @@ // attack with hand - remove cell (if cover open) or interact with the APC /obj/machinery/power/apc/attack_hand(mob/user) -// if(!can_use(user)) This already gets called in interact() and in topic() -// return if(!user) return - src.add_fingerprint(user) + add_fingerprint(user) - if(usr == user && opened && (!issilicon(user))) + if(usr == user && opened && !issilicon(user)) if(cell) - if(issilicon(user)) - cell.loc=src.loc // Drop it, whoops. - else - user.put_in_hands(cell) + user.put_in_hands(cell) cell.add_fingerprint(user) cell.update_icon() - - src.cell = null - user.visible_message("[user.name] removes the power cell from [src.name]!", "You remove the power cell.") -// to_chat(user, "You remove the power cell.") - charging = 0 - src.update_icon() + cell = null + user.visible_message("[user.name] removes [cell] from [src]!", "You remove the [cell].") + charging = FALSE + update_icon() return if(stat & (BROKEN|MAINT)) return - src.interact(user) + interact(user) /obj/machinery/power/apc/attack_ghost(mob/user) if(panel_open) wires.Interact(user) - return ui_interact(user) + return tgui_interact(user) /obj/machinery/power/apc/interact(mob/user) if(!user) @@ -749,7 +747,7 @@ if(panel_open) wires.Interact(user) - return ui_interact(user) + return tgui_interact(user) /obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf) @@ -770,22 +768,16 @@ else return APC_MALF_NOT_HACKED -/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(!user) - return - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/power/apc/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - // the ui does not exist, so we'll create a new one - ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 510, issilicon(user) ? 535 : 460) + ui = new(user, src, ui_key, "APC", name, 510, 460, master_ui, state) ui.open() - // Auto update every Master Controller tick - ui.set_auto_update(1) -/obj/machinery/power/apc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/power/apc/tgui_data(mob/user) + var/list/data = list() data["locked"] = is_locked(user) + data["normallyLocked"] = locked data["isOperating"] = operating data["externalPower"] = main_status data["powerCellStatus"] = cell ? cell.percent() : null @@ -853,10 +845,6 @@ // to_chat(world, "[area.power_equip]") area.power_change() -/obj/machinery/power/apc/proc/isWireCut(var/wireIndex) - return wires.IsIndexCut(wireIndex) - - /obj/machinery/power/apc/proc/can_use(var/mob/user, var/loud = 0) //used by attack_hand() and Topic() if(user.can_admin_interact()) return 1 @@ -866,7 +854,7 @@ var/mob/living/silicon/ai/AI = user var/mob/living/silicon/robot/robot = user if( \ - src.aidisabled || \ + aidisabled || \ malfhack && istype(malfai) && \ ( \ (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \ @@ -877,21 +865,21 @@ to_chat(user, "\The [src] has AI control disabled!") user << browse(null, "window=apc") user.unset_machine() - return 0 + return FALSE else - if((!in_range(src, user) || !istype(src.loc, /turf))) - return 0 + if((!in_range(src, user) || !istype(loc, /turf))) + return FALSE var/mob/living/carbon/human/H = user if(istype(H)) if(H.getBrainLoss() >= 60) for(var/mob/M in viewers(src, null)) to_chat(M, "[H] stares cluelessly at [src] and drools.") - return 0 + return FALSE else if(prob(H.getBrainLoss())) to_chat(user, "You momentarily forget how to use [src].") - return 0 - return 1 + return FALSE + return TRUE /obj/machinery/power/apc/proc/is_authenticated(mob/user as mob) if(user.can_admin_interact()) @@ -909,104 +897,59 @@ else return locked -/obj/machinery/power/apc/Topic(href, href_list, var/usingUI = 1) - if(..()) - return 1 - - if(!can_use(usr, 1)) - return 1 - - if(href_list["lock"]) - if(!is_authenticated(usr)) - return - - coverlocked = !coverlocked - - else if(href_list["breaker"]) - if(!is_authenticated(usr)) - return - - toggle_breaker() - - else if(href_list["toggle_nightshift"]) - if(!is_authenticated(usr)) - return - - if(last_nightshift_switch > world.time + 100) // don't spam... - to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") - return - last_nightshift_switch = world.time - set_nightshift(!nightshift_lights) - - else if(href_list["cmode"]) - if(!is_authenticated(usr)) - return - - chargemode = !chargemode - if(!chargemode) - charging = 0 - update_icon() - - else if(href_list["eqp"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["eqp"]) - equipment = setsubsystem(val) - update_icon() - update() - - else if(href_list["lgt"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["lgt"]) - lighting = setsubsystem(val) - update_icon() - update() - - else if(href_list["env"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["env"]) - environ = setsubsystem(val) - update_icon() - update() - else if( href_list["close"] ) - SSnanoui.close_user_uis(usr, src) - - return 0 - else if(href_list["close2"]) - usr << browse(null, "window=apcwires") - - return 0 - - else if(href_list["overload"]) - if(issilicon(usr) && !aidisabled) - overload_lighting() - - else if(href_list["malfhack"]) - if(get_malf_status(usr)) - malfhack(usr) - - else if(href_list["occupyapc"]) - if(get_malf_status(usr)) - malfoccupy(usr) - - else if(href_list["deoccupyapc"]) - if(get_malf_status(usr)) - malfvacate() - - else if(href_list["toggleaccess"]) - if(istype(usr, /mob/living/silicon)) - if(emagged || aidisabled || (stat & (BROKEN|MAINT))) - to_chat(usr, "The APC does not respond to the command.") +/obj/machinery/power/apc/tgui_act(action, params) + if(..() || !can_use(usr, TRUE) || (locked && !usr.has_unlimited_silicon_privilege && (action != "toggle_nightshift") && !usr.can_admin_interact())) + return + . = TRUE + switch(action) + if("lock") + if(usr.has_unlimited_silicon_privilege) + if(emagged || stat & BROKEN) + to_chat(usr, "The APC does not respond to the command!") + return FALSE + else + locked = !locked + update_icon() else - locked = !locked + to_chat(usr, "Access Denied!") + return FALSE + if("cover") + coverlocked = !coverlocked + if("breaker") + toggle_breaker(usr) + if("toggle_nightshift") + if(last_nightshift_switch > world.time + 100) // don't spam... + to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") + return FALSE + last_nightshift_switch = world.time + set_nightshift(!nightshift_lights) + if("charge") + chargemode = !chargemode + if("channel") + if(params["eqp"]) + equipment = setsubsystem(text2num(params["eqp"])) update_icon() - - return 0 + update() + else if(params["lgt"]) + lighting = setsubsystem(text2num(params["lgt"])) + update_icon() + update() + else if(params["env"]) + environ = setsubsystem(text2num(params["env"])) + update_icon() + update() + if("overload") + if(usr.has_unlimited_silicon_privilege) + INVOKE_ASYNC(src, /obj/machinery/power/apc.proc/overload_lighting) + if("hack") + if(get_malf_status(usr)) + malfhack(usr) + if("occupy") + if(get_malf_status(usr)) + malfoccupy(usr) + if("deoccupy") + if(get_malf_status(usr)) + malfvacate() /obj/machinery/power/apc/proc/toggle_breaker() operating = !operating @@ -1037,7 +980,7 @@ if(!malf.can_shunt) to_chat(malf, "You cannot shunt!") return - if(!is_station_level(src.z)) + if(!is_station_level(z)) return occupier = new /mob/living/silicon/ai(src,malf.laws,null,1) occupier.adjustOxyLoss(malf.getOxyLoss()) @@ -1052,7 +995,8 @@ occupier.eyeobj.name = "[occupier.name] (AI Eye)" if(malf.parent) qdel(malf) - occupier.verbs += /mob/living/silicon/ai/proc/corereturn + var/datum/action/innate/ai/return_to_core/R = new + R.Grant(occupier) occupier.cancel_camera() if((seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) && malf.nuking) for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) @@ -1083,21 +1027,21 @@ /obj/machinery/power/apc/proc/ion_act() //intended to be exactly the same as an AI malf attack - if(!src.malfhack && is_station_level(src.z)) + if(!malfhack && is_station_level(z)) if(prob(3)) - src.locked = 1 - if(src.cell.charge > 0) - src.cell.charge = 0 + locked = TRUE + if(cell.charge > 0) + cell.charge = 0 cell.corrupt() - src.malfhack = 1 + malfhack = TRUE update_icon() var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(3, 0, src.loc) + smoke.set_up(3, 0, loc) smoke.attach(src) smoke.start() do_sparks(3, 1, src) for(var/mob/M in viewers(src)) - M.show_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "You hear sizzling electronics.", 2) + M.show_message("The [name] suddenly lets out a blast of smoke and some sparks!", 3, "You hear sizzling electronics.", 2) /obj/machinery/power/apc/surplus() @@ -1140,12 +1084,12 @@ var/excess = surplus() - if(!src.avail()) - main_status = 0 + if(!avail()) + main_status = APC_EXTERNAL_POWER_NOTCONNECTED else if(excess < 0) - main_status = 1 + main_status = APC_EXTERNAL_POWER_NOENERGY else - main_status = 2 + main_status = APC_EXTERNAL_POWER_GOOD if(debug) log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]") @@ -1191,31 +1135,31 @@ lighting = autoset(lighting, 1) environ = autoset(environ, 1) autoflag = 3 - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.clearAlarm(loc, src) + if(report_power_alarm) + area.poweralert(TRUE, src) else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment if(autoflag != 2) equipment = autoset(equipment, 2) lighting = autoset(lighting, 1) environ = autoset(environ, 1) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) + if(report_power_alarm) + area.poweralert(FALSE, src) autoflag = 2 else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0)) equipment = autoset(equipment, 2) lighting = autoset(lighting, 2) environ = autoset(environ, 1) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) + if(report_power_alarm) + area.poweralert(FALSE, src) autoflag = 1 else if(cell.charge <= 0) // zero charge, turn all off if(autoflag != 0) equipment = autoset(equipment, 0) lighting = autoset(lighting, 0) environ = autoset(environ, 0) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) + if(report_power_alarm) + area.poweralert(FALSE, src) autoflag = 0 // now trickle-charge the cell @@ -1260,7 +1204,7 @@ if(shock_mobs.len) var/mob/living/L = pick(shock_mobs) L.electrocute_act(rand(5, 25), "electrical arc") - playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, 1) + playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, TRUE) Beam(L, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5) else // no cell, switch everything off @@ -1270,8 +1214,8 @@ equipment = autoset(equipment, 0) lighting = autoset(lighting, 0) environ = autoset(environ, 0) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) + if(report_power_alarm) + area.poweralert(FALSE, src) autoflag = 0 // update icon & area power if anything changed @@ -1343,11 +1287,10 @@ return if(cell && cell.charge >= 20) cell.use(20) - spawn(0) - for(var/obj/machinery/light/L in area) - if(prob(chance)) - L.break_light_tube(0, 1) - stoplag() + for(var/obj/machinery/light/L in area) + if(prob(chance)) + L.break_light_tube(0, 1) + stoplag() /obj/machinery/power/apc/proc/null_charge() for(var/obj/machinery/light/L in area) @@ -1371,4 +1314,22 @@ L.update(FALSE) CHECK_TICK +/obj/machinery/power/apc/proc/relock_callback() + locked = TRUE + updateDialog() + +/obj/machinery/power/apc/proc/check_main_power_callback() + if(!wires.is_cut(WIRE_MAIN_POWER1) && !wires.is_cut(WIRE_MAIN_POWER2)) + shorted = FALSE + updateDialog() + +/obj/machinery/power/apc/proc/check_ai_control_callback() + if(!wires.is_cut(WIRE_AI_CONTROL)) + aidisabled = FALSE + updateDialog() + #undef APC_UPDATE_ICON_COOLDOWN + +#undef APC_EXTERNAL_POWER_NOTCONNECTED +#undef APC_EXTERNAL_POWER_NOENERGY +#undef APC_EXTERNAL_POWER_GOOD diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index b6c65d979cf..40969d7e677 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -229,14 +229,15 @@ By design, d1 is the smallest direction and d2 is the highest if(current_size >= STAGE_FIVE) deconstruct() -obj/structure/cable/proc/cable_color(var/colorC) - if(colorC) - if(colorC == "rainbow") - color = color_rainbow() - else - color = colorC +/obj/structure/cable/proc/cable_color(colorC) + if(!colorC) + color = COLOR_RED + else if(colorC == "rainbow") + color = color_rainbow() + else if(colorC == "orange") //byond only knows 16 colors by name, and orange isn't one of them + color = COLOR_ORANGE else - color = "#DD0000" + color = colorC /obj/structure/cable/proc/color_rainbow() color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) @@ -845,13 +846,15 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) ..() -/obj/item/stack/cable_coil/proc/cable_color(var/colorC) - if(colorC) - if(colorC == "rainbow") - colorC = color_rainbow() - color = colorC - else +/obj/item/stack/cable_coil/proc/cable_color(colorC) + if(!colorC) color = COLOR_RED + else if(colorC == "rainbow") + color = color_rainbow() + else if(colorC == "orange") //byond only knows 16 colors by name, and orange isn't one of them + color = COLOR_ORANGE + else + color = colorC /obj/item/stack/cable_coil/proc/color_rainbow() color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 0a31d882488..9305faf6637 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -177,6 +177,8 @@ var/nightshift_light_power = 0.45 var/nightshift_light_color = "#FFDDCC" + var/bulb_emergency_colour = "#FF3232" // determines the colour of the light while it's in emergency mode + // the smaller bulb light fixture /obj/machinery/light/small @@ -238,7 +240,11 @@ switch(status) // set icon_states if(LIGHT_OK) - icon_state = "[base_state][on]" + var/area/A = get_area(src) + if(A && A.fire) + icon_state = "[base_state]_emergency" + else + icon_state = "[base_state][on]" if(LIGHT_EMPTY) icon_state = "[base_state]-empty" on = FALSE @@ -260,10 +266,20 @@ on = FALSE update_icon() if(on) - var/BR = nightshift_enabled ? nightshift_light_range : brightness_range - var/PO = nightshift_enabled ? nightshift_light_power : brightness_power - var/CO = nightshift_enabled ? nightshift_light_color : brightness_color - var/matching = light_range == BR && light_power == PO && light_color == CO + var/BR = brightness_range + var/PO = brightness_power + var/CO = brightness_color + if(color) + CO = color + var/area/A = get_area(src) + if(A && A.fire) + CO = bulb_emergency_colour + else if(nightshift_enabled) + BR = nightshift_light_range + PO = nightshift_light_power + if(!color) + CO = nightshift_light_color + var/matching = light && BR == light.light_range && PO == light.light_power && CO == light.light_color if(!matching) switchcount++ if(rigged) @@ -627,13 +643,19 @@ /obj/item/light/Crossed(mob/living/L) if(istype(L) && has_gravity(loc)) - if(L.incorporeal_move || L.flying) + if(L.incorporeal_move || L.flying || L.floating) return playsound(loc, 'sound/effects/glass_step.ogg', 50, TRUE) if(status == LIGHT_BURNED || status == LIGHT_OK) shatter() return ..() +/obj/item/light/decompile_act(obj/item/matter_decompiler/C, mob/user) + C.stored_comms["glass"] += 1 + C.stored_comms["metal"] += 1 + qdel(src) + return TRUE + /obj/item/light/tube name = "light tube" desc = "A replacement light tube." diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index b534fbbfda4..a0e10c73be8 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -1,3 +1,5 @@ +#define SHEET_VOLUME 1000 //cm3 + //Baseline portable generator. Has all the default handling. Not intended to be used on it's own (since it generates unlimited power). /obj/machinery/power/port_gen name = "Placeholder Generator" //seriously, don't use this. It can't be anchored without VV magic. @@ -239,7 +241,7 @@ var/temp_loss = (temperature - cooling_temperature)/TEMPERATURE_DIVISOR temp_loss = between(2, round(temp_loss, 1), TEMPERATURE_CHANGE_MAX) temperature = max(temperature - temp_loss, cooling_temperature) - SSnanoui.update_uis(src) + SStgui.update_uis(src) if(overheating) overheating-- @@ -280,7 +282,7 @@ to_chat(user, "You add [amount] sheet\s to the [src.name].") sheets += amount addstack.use(amount) - SSnanoui.update_uis(src) + SStgui.update_uis(src) return else if(!active) if(istype(O, /obj/item/wrench)) @@ -312,78 +314,69 @@ /obj/machinery/power/port_gen/pacman/attack_hand(mob/user as mob) ..() - if(!anchored) - return - ui_interact(user) + tgui_interact(user) /obj/machinery/power/port_gen/pacman/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) + add_hiddenprint(user) + return attack_hand(user) /obj/machinery/power/port_gen/pacman/attack_ghost(var/mob/user) - return src.attack_hand(user) + return attack_hand(user) -/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(IsBroken()) - return - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/power/port_gen/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "pacman.tmpl", src.name, 500, 560) + ui = new(user, src, ui_key, "Pacman", name, 500, 260) ui.open() - ui.set_auto_update(1) -/obj/machinery/power/port_gen/pacman/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/power/port_gen/pacman/tgui_data(mob/user) + var/list/data = list() data["active"] = active if(istype(user, /mob/living/silicon/ai)) - data["is_ai"] = 1 + data["is_ai"] = TRUE else if(istype(user, /mob/living/silicon/robot) && !Adjacent(user)) - data["is_ai"] = 1 + data["is_ai"] = TRUE else - data["is_ai"] = 0 + data["is_ai"] = FALSE + data["anchored"] = anchored + data["broken"] = IsBroken() data["output_set"] = power_output data["output_max"] = max_power_output data["output_safe"] = max_safe_output - data["output_watts"] = power_output * power_gen - data["temperature_current"] = src.temperature - data["temperature_max"] = src.max_temperature - data["temperature_overheat"] = overheating - // 1 sheet = 1000cm3? - data["fuel_stored"] = round((sheets * 1000) + (sheet_left * 1000)) - data["fuel_capacity"] = round(max_sheets * 1000, 0.1) - data["fuel_usage"] = active ? round((power_output / time_per_sheet) * 1000) : 0 + data["power_gen"] = power_gen + data["tmp_current"] = temperature + data["tmp_max"] = max_temperature + data["tmp_overheat"] = overheating + data["fuel_stored"] = round((sheets * SHEET_VOLUME) + (sheet_left * SHEET_VOLUME)) + data["fuel_cap"] = round(max_sheets * SHEET_VOLUME, 0.1) + data["fuel_usage"] = active ? round((power_output / time_per_sheet) * SHEET_VOLUME) : 0 data["fuel_type"] = sheet_name + data["has_fuel"] = HasFuel() return data -/obj/machinery/power/port_gen/pacman/Topic(href, href_list) +/obj/machinery/power/port_gen/pacman/tgui_act(action, params) if(..()) return - src.add_fingerprint(usr) - if(href_list["action"]) - if(href_list["action"] == "enable") - if(!active && HasFuel() && !IsBroken()) - active = 1 - update_icon() - if(href_list["action"] == "disable") - if(active) - active = 0 - update_icon() - if(href_list["action"] == "eject") - if(!active) - DropFuel() - if(href_list["action"] == "lower_power") - if(power_output > 1) - power_output-- - if(href_list["action"] == "higher_power") - if(power_output < max_power_output || (emagged && power_output < round(max_power_output*2.5))) - power_output++ + add_fingerprint(usr) - SSnanoui.update_uis(src) + . = TRUE + + switch(action) + if("toggle_power") + if(!powernet) //only a warning, process will disable + atom_say("Not connected to powernet.") + active = !active + update_icon() + if("eject_fuel") + DropFuel() + if("change_power") + var/newPower = text2num(params["change_power"]) + if(newPower) + power_output = clamp(newPower, 1, max_power_output) /obj/machinery/power/port_gen/pacman/super name = "S.U.P.E.R.P.A.C.M.A.N.-type Portable Generator" diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 046cf68a543..4d3eb5f51f0 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -144,7 +144,7 @@ field_generator power level display ..() /obj/machinery/field/generator/bullet_act(obj/item/projectile/Proj) - if(Proj.flag != "bullet") + if(Proj.flag != "bullet" && !Proj.nodamage) power = min(power + Proj.damage, field_generator_max_power) check_power_level() return 0 @@ -310,16 +310,25 @@ field_generator power level display //This is here to help fight the "hurr durr, release singulo cos nobody will notice before the //singulo eats the evidence". It's not fool-proof but better than nothing. //I want to avoid using global variables. - spawn(1) - var/temp = 1 //stops spam - for(var/thing in GLOB.singularities) - var/obj/singularity/O = thing - if(O.last_warning && temp) - if((world.time - O.last_warning) > 50) //to stop message-spam - temp = 0 - message_admins("A singulo exists and a containment field has failed. Location: [get_area(src)] (JMP)",1) - investigate_log("has failed whilst a singulo exists.","singulo") - O.last_warning = world.time + INVOKE_ASYNC(src, .proc/admin_alert) + +/obj/machinery/field/generator/proc/admin_alert() + var/temp = TRUE //stops spam + for(var/thing in GLOB.singularities) + var/obj/singularity/O = thing + if(O.last_warning && temp && atoms_share_level(O, src)) + if((world.time - O.last_warning) > 50) //to stop message-spam + temp = FALSE + // To the person who asks "Hey affected, why are you using this massive operator when you can use AREACOORD?" Well, ill tell you + // get_area_name is fucking broken and uses a for(x in world) search + // It doesnt even work, is expensive, and returns 0 + // Im not refactoring one thing which could risk breaking all admin location logs + // Fight me + // [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)] works much better and actually works at all + // Oh and yes, this exact comment was pasted from the exact same thing I did to tcomms code. Dont at me. + message_admins("A singularity exists and a containment field has failed on the same Z-Level. Singulo location: [O ? "[get_location_name(O, TRUE)] [COORD(O)]" : "nonexistent location"] [ADMIN_JMP(O)] | Field generator location: [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)]") + investigate_log("has failed whilst a singulo exists.","singulo") + O.last_warning = world.time /obj/machinery/field/generator/shock_field(mob/living/user) if(fields.len) diff --git a/code/modules/power/singularity/investigate.dm b/code/modules/power/singularity/investigate.dm index 43e8c9f8a8b..4835e09411c 100644 --- a/code/modules/power/singularity/investigate.dm +++ b/code/modules/power/singularity/investigate.dm @@ -1,4 +1,4 @@ -/area/engine/engineering/power_alert(var/alarming) - if(alarming) - investigate_log("has a power alarm!","singulo") +/area/engine/engineering/poweralert(state, source) + if(state != poweralm) + investigate_log("has a power alarm!", "singulo") ..() diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 80b10ab3a8b..bead9c935ef 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -127,6 +127,8 @@ /obj/singularity/narsie/proc/acquire(var/mob/food) if(food == target) return + if(!target) + return to_chat(target, "[uppertext(SSticker.cultdat.entity_name)] HAS LOST INTEREST IN YOU") target = food if(ishuman(target)) diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index b8ba53ae432..d1c7d3433fe 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -27,6 +27,7 @@ use_log = list() /obj/machinery/particle_accelerator/control_box/Destroy() + SStgui.close_uis(wires) if(active) toggle_power() QDEL_NULL(wires) @@ -41,6 +42,11 @@ else if(construction_state == 2) // Wires exposed wires.Interact(user) +/obj/machinery/particle_accelerator/control_box/multitool_act(mob/living/user, obj/item/I) + if(construction_state == 2) // Wires exposed + wires.Interact(user) + return TRUE + /obj/machinery/particle_accelerator/control_box/update_state() if(construction_state < 3) use_power = NO_POWER_USE @@ -93,18 +99,18 @@ usr.unset_machine() return if(href_list["togglep"]) - if(!wires.IsIndexCut(PARTICLE_TOGGLE_WIRE)) + if(!wires.is_cut(WIRE_PARTICLE_POWER)) toggle_power() else if(href_list["scan"]) part_scan() else if(href_list["strengthup"]) - if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE)) + if(!wires.is_cut(WIRE_PARTICLE_STRENGTH)) add_strength() else if(href_list["strengthdown"]) - if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE)) + if(!wires.is_cut(WIRE_PARTICLE_STRENGTH)) remove_strength() updateDialog() @@ -229,6 +235,7 @@ investigate_log("turned [active?"ON":"OFF"] by [usr ? usr.key : "outside forces"]","singulo") if(active) msg_admin_attack("PA Control Computer turned ON by [key_name_admin(usr)]", ATKLOG_FEW) + usr.create_log(MISC_LOG, "PA Control Computer turned ON", src) log_game("PA Control Computer turned ON by [key_name(usr)] in ([x],[y],[z])") use_log += text("\[[time_stamp()]\] [key_name(usr)] has turned on the PA Control Computer.") if(active) diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 59ce091b170..2fa2c4c1573 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -227,8 +227,8 @@ if(terminal) terminal.master = null terminal = null - return 1 - return 0 + return TRUE + return FALSE /obj/machinery/power/smes/proc/make_terminal(user, tempDir, tempLoc) // create a terminal object at the same position as original turf loc @@ -339,89 +339,91 @@ /obj/machinery/power/smes/attack_ai(mob/user) add_hiddenprint(user) - ui_interact(user) + tgui_interact(user) /obj/machinery/power/smes/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) /obj/machinery/power/smes/attack_hand(mob/user) add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/power/smes/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) if(stat & BROKEN) return - - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, "smes.tmpl", "SMES Power Storage Unit", 540, 380) - // open the new ui window + ui = new(user, src, ui_key, "Smes", name, 340, 350, master_ui, state) ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/power/smes/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - data["nameTag"] = name_tag - data["storedCapacity"] = round(100.0*charge/capacity, 0.1) - data["charging"] = inputting - data["chargeMode"] = input_attempt - data["chargeLevel"] = input_level - data["chargeMax"] = input_level_max - data["outputOnline"] = output_attempt - data["outputLevel"] = output_level - data["outputMax"] = output_level_max - data["outputLoad"] = round(output_used) - - if(outputting) - data["outputting"] = 2 // smes is outputting - else if(!outputting && output_attempt) - data["outputting"] = 1 // smes is online but not outputting because it's charge level is too low - else - data["outputting"] = 0 // smes is not outputting +/obj/machinery/power/smes/tgui_data(mob/user) + var/list/data = list( + "capacity" = capacity, + "capacityPercent" = round(100*charge/capacity, 0.1), + "charge" = charge, + "inputAttempt" = input_attempt, + "inputting" = inputting, + "inputLevel" = input_level, + "inputLevel_text" = DisplayPower(input_level), + "inputLevelMax" = input_level_max, + "inputAvailable" = input_available, + "outputAttempt" = output_attempt, + "outputting" = outputting, + "outputLevel" = output_level, + "outputLevel_text" = DisplayPower(output_level), + "outputLevelMax" = output_level_max, + "outputUsed" = round(output_used), + ) return data -/obj/machinery/power/smes/Topic(href, href_list) +/obj/machinery/power/smes/tgui_act(action, params) if(..()) - return 1 + return + . = TRUE + switch(action) + if("tryinput") + inputting(!input_attempt) + update_icon() + if("tryoutput") + outputting(!output_attempt) + update_icon() + if("input") + var/target = params["target"] + var/adjust = text2num(params["adjust"]) + if(target == "min") + target = 0 + else if(target == "max") + target = input_level_max + else if(adjust) + target = input_level + adjust + else if(text2num(target) != null) + target = text2num(target) + else + . = FALSE + if(.) + input_level = clamp(target, 0, input_level_max) + if("output") + var/target = params["target"] + var/adjust = text2num(params["adjust"]) + if(target == "min") + target = 0 + else if(target == "max") + target = output_level_max + else if(adjust) + target = output_level + adjust + else if(text2num(target) != null) + target = text2num(target) + else + . = FALSE + if(.) + output_level = clamp(target, 0, output_level_max) + else + . = FALSE + if(.) + log_smes(usr) - if( href_list["cmode"] ) - inputting(!input_attempt) - update_icon() - - else if( href_list["online"] ) - outputting(!output_attempt) - update_icon() - - else if( href_list["input"] ) - switch( href_list["input"] ) - if("min") - input_level = 0 - if("max") - input_level = input_level_max - if("set") - input_level = input(usr, "Enter new input level (0-[input_level_max])", "SMES Input Power Control", input_level) as num - input_level = max(0, min(input_level_max, input_level)) // clamp to range - - else if( href_list["output"] ) - switch( href_list["output"] ) - if("min") - output_level = 0 - if("max") - output_level = output_level_max - if("set") - output_level = input(usr, "Enter new output level (0-[output_level_max])", "SMES Output Power Control", output_level) as num - output_level = max(0, min(output_level_max, output_level)) // clamp to range - - investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Output-mode: [output_attempt?"on":"off"] | Input-mode: [input_attempt?"auto":"off"] by [usr.key]","singulo") - - return 1 +/obj/machinery/power/smes/proc/log_smes(mob/user) + investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Charge: [charge] | Output-mode: [output_attempt?"on":"off"] | Input-mode: [input_attempt?"auto":"off"] by [user ? key_name(user) : "outside forces"]", "singulo") /obj/machinery/power/smes/proc/ion_act() if(is_station_level(src.z)) @@ -448,6 +450,7 @@ smoke.attach(src) smoke.start() + /obj/machinery/power/smes/proc/inputting(var/do_input) input_attempt = do_input if(!input_attempt) @@ -459,14 +462,15 @@ outputting = 0 /obj/machinery/power/smes/emp_act(severity) - inputting(rand(0,1)) - outputting(rand(0,1)) + inputting(rand(0, 1)) + outputting(rand(0, 1)) output_level = rand(0, output_level_max) input_level = rand(0, input_level_max) charge -= 1e6/severity if(charge < 0) charge = 0 update_icon() + log_smes() ..() /obj/machinery/power/smes/engineering diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index d99df1de7bb..7bb2f16b64a 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -254,13 +254,17 @@ // Solar Control Computer // +#define TRACKER_OFF 0 +#define TRACKER_TIMED 1 +#define TRACKER_AUTO 2 + /obj/machinery/power/solar_control name = "solar panel control" desc = "A controller for solar panel arrays." icon = 'icons/obj/computer.dmi' icon_state = "computer" - anchored = 1 - density = 1 + anchored = TRUE + density = TRUE use_power = IDLE_POWER_USE idle_power_usage = 250 max_integrity = 200 @@ -272,17 +276,17 @@ var/targetdir = 0 // target angle in manual tracking (since it updates every game minute) var/gen = 0 var/lastgen = 0 - var/track = 0 // 0= off 1=timed 2=auto (tracker) + var/track = TRACKER_OFF var/trackrate = 600 // 300-900 seconds var/nexttime = 0 // time for a panel to rotate of 1? in manual tracking - var/autostart = 0 // Automatically search for connected devices + var/autostart = FALSE // Automatically search for connected devices var/obj/machinery/power/tracker/connected_tracker = null var/list/connected_panels = list() // Used for mapping in solar array which automatically starts itself (telecomms, for example) /obj/machinery/power/solar_control/autostart - track = 2 // Auto tracking mode - autostart = 1 // Automatically start + track = TRACKER_AUTO + autostart = TRUE // Automatically search for connected devices /obj/machinery/power/solar_control/Initialize() SSsun.solars |= src @@ -294,7 +298,7 @@ set_panels(cdir) if(autostart) search_for_connected() - if(connected_tracker && track == 2) + if(connected_tracker && track == TRACKER_AUTO) connected_tracker.set_angle(SSsun.angle) set_panels(cdir) @@ -334,15 +338,9 @@ if(stat & (NOPOWER | BROKEN)) return - switch(track) - if(1) - if(trackrate) //we're manual tracking. If we set a rotation speed... - cdir = targetdir //...the current direction is the targetted one (and rotates panels to it) - if(2) // auto-tracking - if(connected_tracker) - connected_tracker.set_angle(SSsun.angle) - - set_panels(cdir) + if(track == TRACKER_AUTO && connected_tracker) // auto-tracking + connected_tracker.set_angle(SSsun.angle) + set_panels(cdir) updateDialog() /obj/machinery/power/solar_control/update_icon() @@ -359,44 +357,73 @@ overlays += image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir)) /obj/machinery/power/solar_control/attack_ai(mob/user as mob) - src.add_hiddenprint(user) - ui_interact(user) + add_hiddenprint(user) + tgui_interact(user) /obj/machinery/power/solar_control/attack_ghost(mob/user as mob) - ui_interact(user) + tgui_interact(user) /obj/machinery/power/solar_control/attack_hand(mob/user) if(..(user)) - return 1 - + return TRUE if(stat & BROKEN) return + tgui_interact(user) - ui_interact(user) - -/obj/machinery/power/solar_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/power/solar_control/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "solar_control.tmpl", name, 490, 420) + ui = new(user, src, ui_key, "SolarControl", name, 490, 300) ui.open() - ui.set_auto_update(1) - -/obj/machinery/power/solar_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - data["generated"] = round(lastgen) - data["angle"] = cdir - data["direction"] = angle2text(cdir) - - data["tracking_state"] = track - data["tracking_rate"] = trackrate - data["rotating_way"] = (trackrate<0 ? "CCW" : "CW") +/obj/machinery/power/solar_control/tgui_data(mob/user) + var/list/data = list() + data["generated"] = round(lastgen) //generated power by all connected panels + data["generated_ratio"] = data["generated"] / round(max(connected_panels.len, 1) * SOLARGENRATE) //power generation ratio. Used for the power bar + data["direction"] = angle2text(cdir) //current orientation of the panels + data["cdir"] = cdir //current orientation of the of the panels in degrees + data["tracking_state"] = track //tracker status: TRACKER_OFF, TRACKER_TIMED, TRACKER_AUTO + data["tracking_rate"] = trackrate //rotation speed of tracker in degrees/h + data["rotating_direction"] = (trackrate < 0 ? "Counter clockwise" : "Clockwise") //direction of tracker data["connected_panels"] = connected_panels.len - data["connected_tracker"] = (connected_tracker ? 1 : 0) - + data["connected_tracker"] = (connected_tracker ? TRUE : FALSE) return data +/obj/machinery/power/solar_control/tgui_act(action, params) + if(..()) + return + . = TRUE + + switch(action) + if("cdir") //change panel orientation + var/newAngle = text2num(params["cdir"]) + if(!isnull(newAngle)) //0 is ok + cdir = clamp(newAngle, 0, 359) + targetdir = cdir + set_panels(cdir) + if("tdir") //change tracker rotation + var/newTrackrate = text2num(params["tdir"]) + if(!newTrackrate) + newTrackrate = 1 + trackrate = clamp(newTrackrate, -7200, 7200) + nexttime = world.time + 36000 / abs(trackrate) + if("track") //change tracker status + track = text2num(params["track"]) + if(track == TRACKER_AUTO) + if(connected_tracker) + connected_tracker.set_angle(SSsun.angle) + set_panels(cdir) + else if(track == TRACKER_TIMED) + targetdir = cdir + if(trackrate) + nexttime = world.time + 36000 / abs(trackrate) + set_panels(targetdir) + if("refresh") + search_for_connected() + if(connected_tracker && track == TRACKER_AUTO) + connected_tracker.set_angle(SSsun.angle) + set_panels(cdir) + /obj/machinery/power/solar_control/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/screwdriver)) playsound(src.loc, I.usesound, 50, 1) @@ -450,50 +477,16 @@ if(stat & (NOPOWER | BROKEN)) return - if(connected_tracker) //NOTE : handled here so that we don't add trackers to the processing list - if(connected_tracker.powernet != powernet) - connected_tracker.unset_control() + if(connected_tracker && connected_tracker.powernet != powernet) //NOTE : handled here so that we don't add trackers to the processing list + connected_tracker.unset_control() - if(track==1 && trackrate) //manual tracking and set a rotation speed - if(nexttime <= world.time) //every time we need to increase/decrease the angle by 1?... - targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it - nexttime += 36000/abs(trackrate) //reset the counter for the next 1? - -/obj/machinery/power/solar_control/Topic(href, href_list) - if(..()) - return - - if(href_list["rate_control"]) - if(href_list["cdir"]) - src.cdir = dd_range(0,359,(360+src.cdir+text2num(href_list["cdir"]))%360) - src.targetdir = src.cdir - if(track == 2) //manual update, so losing auto-tracking - track = 0 - spawn(1) - set_panels(cdir) - if(href_list["tdir"]) - src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(href_list["tdir"])) - if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) - - if(href_list["track"]) - track = text2num(href_list["track"]) - if(track == 2) - if(connected_tracker) - connected_tracker.set_angle(SSsun.angle) - set_panels(cdir) - else if(track == 1) //begin manual tracking - src.targetdir = src.cdir - if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) - set_panels(targetdir) - - if(href_list["search_connected"]) - search_for_connected() - if(connected_tracker && track == 2) - connected_tracker.set_angle(SSsun.angle) + //manual tracking and set a rotation speed + if(track == TRACKER_TIMED && trackrate && nexttime <= world.time) //every time we need to increase/decrease the angle by 1?... + targetdir = (targetdir + trackrate / abs(trackrate) + 360) % 360 //... do it + nexttime += 36000 / abs(trackrate) //reset the counter for the next 1? + cdir = targetdir set_panels(cdir) - return - //rotates the panel to the passed angle /obj/machinery/power/solar_control/proc/set_panels(var/cdir) diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index d3f5f76303c..6146cd3ac2c 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -311,15 +311,6 @@ /obj/machinery/power/supermatter_shard/attack_robot(mob/user as mob) if(Adjacent(user)) return attack_hand(user) - else - ui_interact(user) - return - -/obj/machinery/power/supermatter_shard/attack_ai(mob/user as mob) - ui_interact(user) - -/obj/machinery/power/supermatter_shard/attack_ghost(mob/user as mob) - ui_interact(user) /obj/machinery/power/supermatter_shard/attack_hand(mob/user as mob) user.visible_message("\The [user] reaches out and touches \the [src], inducing a resonance... [user.p_their(TRUE)] body starts to glow and bursts into flames before flashing into ash.",\ @@ -336,35 +327,6 @@ integrity = integrity < 0 ? 0 : integrity return integrity -// This is purely informational UI that may be accessed by AIs or robots -/obj/machinery/power/supermatter_shard/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "supermatter_crystal.tmpl", "Supermatter Crystal", 500, 300) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/power/supermatter_shard/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - - data["integrity_percentage"] = round(get_integrity()) - var/datum/gas_mixture/env = null - if(!istype(src.loc, /turf/space)) - env = src.loc.return_air() - - if(!env) - data["ambient_temp"] = 0 - data["ambient_pressure"] = 0 - else - data["ambient_temp"] = round(env.temperature) - data["ambient_pressure"] = round(env.return_pressure()) - if(damage > explosion_point) - data["detonating"] = 1 - else - data["detonating"] = 0 - - return data - /obj/machinery/power/supermatter_shard/proc/transfer_energy() for(var/obj/machinery/power/rad_collector/R in GLOB.rad_collectors) if(get_dist(R, src) <= 15) // Better than using orange() every process diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index 5f58bcb5dd4..5743bea8475 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -21,6 +21,7 @@ RefreshParts() /obj/machinery/power/tesla_coil/Destroy() + SStgui.close_uis(wires) QDEL_NULL(wires) return ..() diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index d9b20c9d075..36faa49b533 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -185,7 +185,8 @@ /obj/structure/sign, /obj/machinery/gateway, /obj/structure/grille, - /obj/machinery/the_singularitygen/tesla)) + /obj/machinery/the_singularitygen/tesla, + /mob/living/simple_animal/slime)) for(var/A in typecache_filter_multi_list_exclusion(oview(source, zap_range+2), things_to_shock, blacklisted_tesla_types)) diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm index c8964c7ec34..621c5f02747 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -29,10 +29,10 @@ //set the control of the tracker to a given computer if closer than SOLAR_MAX_DIST /obj/machinery/power/tracker/proc/set_control(obj/machinery/power/solar_control/SC) if(SC && (get_dist(src, SC) > SOLAR_MAX_DIST)) - return 0 + return FALSE control = SC SC.connected_tracker = src - return 1 + return TRUE //set the control of the tracker to null and removes it from the previous control computer if needed /obj/machinery/power/tracker/proc/unset_control() @@ -44,7 +44,7 @@ if(!S) S = new /obj/item/solar_assembly(src) S.glass_type = /obj/item/stack/sheet/glass - S.tracker = 1 + S.tracker = TRUE S.anchored = TRUE S.forceMove(src) update_icon() @@ -63,7 +63,7 @@ . = TRUE if(!I.tool_use_check(user, 0)) return - playsound(src.loc, 'sound/machines/click.ogg', 50, 1) + playsound(loc, 'sound/machines/click.ogg', 50, 1) user.visible_message("[user] begins to take the glass off the solar tracker.") if(I.use_tool(src, user, 50, volume = I.tool_volume)) user.visible_message("[user] takes the glass off the tracker.") @@ -76,7 +76,7 @@ stat |= BROKEN unset_control() -/obj/machinery/power/solar/deconstruct(disassembled = TRUE) +/obj/machinery/power/tracker/deconstruct(disassembled = TRUE) if(!(flags & NODECONSTRUCT)) if(disassembled) var/obj/item/solar_assembly/S = locate() in src @@ -85,8 +85,8 @@ S.give_glass(stat & BROKEN) else playsound(src, "shatter", 70, TRUE) - new /obj/item/shard(src.loc) - new /obj/item/shard(src.loc) + new /obj/item/shard(loc) + new /obj/item/shard(loc) qdel(src) // Tracker Electronic diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index 8536a0195e3..0938dd7af76 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -19,6 +19,15 @@ var/click_cooldown_override = 0 //Override this to make your gun have a faster fire rate, in tenths of a second. 4 is the default gun cooldown. var/harmful = TRUE //pacifism check for boolet, set to FALSE if bullet is non-lethal + /// What type of muzzle flash effect will be shown. If null then no effect and flash of light will be shown + var/muzzle_flash_effect = /obj/effect/temp_visual/target_angled/muzzle_flash + /// What color the flash has. If null then the flash won't cause lighting + var/muzzle_flash_color = LIGHT_COLOR_TUNGSTEN + /// What range the muzzle flash has + var/muzzle_flash_range = MUZZLE_FLASH_RANGE_WEAK + /// How strong the flash is + var/muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_WEAK + /obj/item/ammo_casing/New() ..() if(projectile_type) @@ -97,6 +106,7 @@ var/ammo_type = /obj/item/ammo_casing var/max_ammo = 7 var/multiple_sprites = 0 + var/icon_prefix // boxes with multiple sprites use this as their base var/caliber var/multiload = 1 var/list/initial_mats //For calculating refund values. @@ -123,6 +133,7 @@ if(keep) stored_ammo.Insert(1,b) update_mat_value() + update_icon() return b /obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0) @@ -191,11 +202,12 @@ update_icon() /obj/item/ammo_box/update_icon() + var/icon_base = initial(icon_prefix) ? initial(icon_prefix) : initial(icon_state) switch(multiple_sprites) if(1) - icon_state = "[initial(icon_state)]-[stored_ammo.len]" + icon_state = "[icon_base]-[stored_ammo.len]" if(2) - icon_state = "[initial(icon_state)]-[stored_ammo.len ? "[max_ammo]" : "0"]" + icon_state = "[icon_base]-[stored_ammo.len ? "[max_ammo]" : "0"]" desc = "[initial(desc)] There are [stored_ammo.len] shell\s left!" /obj/item/ammo_box/proc/update_mat_value() diff --git a/code/modules/projectiles/ammunition/ammo_casings.dm b/code/modules/projectiles/ammunition/ammo_casings.dm index 92ecf815104..ec2bb5e6ad3 100644 --- a/code/modules/projectiles/ammunition/ammo_casings.dm +++ b/code/modules/projectiles/ammunition/ammo_casings.dm @@ -2,6 +2,8 @@ desc = "A .357 bullet casing." caliber = "357" projectile_type = /obj/item/projectile/bullet + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG /obj/item/ammo_casing/rubber9mm desc = "A 9mm rubber bullet casing." @@ -14,6 +16,8 @@ icon_state = "762-casing" caliber = "a762" projectile_type = /obj/item/projectile/bullet + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_STRONG + muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG /obj/item/ammo_casing/a762/enchanted projectile_type = /obj/item/projectile/bullet/weakbullet3 @@ -22,15 +26,20 @@ desc = "A .50AE bullet casing." caliber = ".50" projectile_type = /obj/item/projectile/bullet + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG /obj/item/ammo_casing/c38 desc = "A .38 bullet casing." caliber = "38" icon_state = "r-casing" projectile_type = /obj/item/projectile/bullet/weakbullet2 + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL /obj/item/ammo_casing/c38/invisible projectile_type = /obj/item/projectile/bullet/weakbullet2/invisible + muzzle_flash_effect = null // invisible eh /obj/item/ammo_casing/c38/invisible/fake projectile_type = /obj/item/projectile/bullet/weakbullet2/invisible/fake @@ -39,12 +48,15 @@ desc = "A 10mm bullet casing." caliber = "10mm" projectile_type = /obj/item/projectile/bullet/midbullet3 + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL /obj/item/ammo_casing/c10mm/ap projectile_type = /obj/item/projectile/bullet/midbullet3/ap /obj/item/ammo_casing/c10mm/fire projectile_type = /obj/item/projectile/bullet/midbullet3/fire + muzzle_flash_color = LIGHT_COLOR_FIRE /obj/item/ammo_casing/c10mm/hp projectile_type = /obj/item/projectile/bullet/midbullet3/hp @@ -53,62 +65,60 @@ desc = "A 9mm bullet casing." caliber = "9mm" projectile_type = /obj/item/projectile/bullet/weakbullet3 + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_WEAK + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL -/obj/item/ammo_casing/c9mmap - desc = "A 9mm bullet casing." - caliber = "9mm" +/obj/item/ammo_casing/c9mm/ap projectile_type = /obj/item/projectile/bullet/armourpiercing -/obj/item/ammo_casing/c9mmtox - desc = "A 9mm bullet casing." - caliber = "9mm" +/obj/item/ammo_casing/c9mm/tox projectile_type = /obj/item/projectile/bullet/toxinbullet -/obj/item/ammo_casing/c9mminc - desc = "A 9mm bullet casing." - caliber = "9mm" +/obj/item/ammo_casing/c9mm/inc projectile_type = /obj/item/projectile/bullet/incendiary/firebullet + muzzle_flash_color = LIGHT_COLOR_FIRE /obj/item/ammo_casing/c46x30mm desc = "A 4.6x30mm bullet casing." caliber = "4.6x30mm" projectile_type = /obj/item/projectile/bullet/weakbullet3 + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_WEAK + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL -/obj/item/ammo_casing/c46x30mmap - desc = "A 4.6x30mm bullet casing." - caliber = "4.6x30mm" +/obj/item/ammo_casing/c46x30mm/ap projectile_type = /obj/item/projectile/bullet/armourpiercing -/obj/item/ammo_casing/c46x30mmtox - desc = "A 4.6x30mm bullet casing." - caliber = "4.6x30mm" +/obj/item/ammo_casing/c46x30mm/tox projectile_type = /obj/item/projectile/bullet/toxinbullet -/obj/item/ammo_casing/c46x30mminc - desc = "A 4.6x30mm bullet casing." - caliber = "4.6x30mm" +/obj/item/ammo_casing/c46x30mm/inc projectile_type = /obj/item/projectile/bullet/incendiary/firebullet + muzzle_flash_color = LIGHT_COLOR_FIRE /obj/item/ammo_casing/rubber45 desc = "A .45 rubber bullet casing." caliber = ".45" icon_state = "r-casing" projectile_type = /obj/item/projectile/bullet/midbullet_r + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL /obj/item/ammo_casing/c45 desc = "A .45 bullet casing." caliber = ".45" projectile_type = /obj/item/projectile/bullet/midbullet + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL -/obj/item/ammo_casing/c45nostamina - desc = "A .45 bullet casing." - caliber = ".45" +/obj/item/ammo_casing/c45/nostamina projectile_type = /obj/item/projectile/bullet/midbullet3 /obj/item/ammo_casing/n762 desc = "A 7.62x38mmR bullet casing." caliber = "n762" projectile_type = /obj/item/projectile/bullet + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG /obj/item/ammo_casing/caseless/magspear name = "magnetic spear" @@ -118,6 +128,7 @@ icon_state = "magspear" throwforce = 15 //still deadly when thrown throw_speed = 3 + muzzle_flash_color = null /obj/item/ammo_casing/shotgun name = "shotgun slug" @@ -127,6 +138,8 @@ drop_sound = 'sound/weapons/gun_interactions/shotgun_fall.ogg' projectile_type = /obj/item/projectile/bullet materials = list(MAT_METAL=4000) + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_STRONG + muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG /obj/item/ammo_casing/shotgun/buckshot @@ -153,6 +166,8 @@ icon_state = "bshell" projectile_type = /obj/item/projectile/bullet/weakbullet materials = list(MAT_METAL=250) + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL /obj/item/ammo_casing/shotgun/improvised @@ -163,6 +178,8 @@ materials = list(MAT_METAL=250) pellets = 10 variance = 25 + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL /obj/item/ammo_casing/shotgun/improvised/overload @@ -174,6 +191,8 @@ materials = list(MAT_METAL=250) pellets = 4 variance = 40 + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG /obj/item/ammo_casing/shotgun/stunslug @@ -182,6 +201,9 @@ icon_state = "stunshell" projectile_type = /obj/item/projectile/bullet/stunshot materials = list(MAT_METAL=250) + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL + muzzle_flash_color = "#FFFF00" /obj/item/ammo_casing/shotgun/meteorshot @@ -203,12 +225,14 @@ would have difficulty with." icon_state = "pshell" projectile_type = /obj/item/projectile/beam/pulse/shot + muzzle_flash_color = LIGHT_COLOR_DARKBLUE /obj/item/ammo_casing/shotgun/incendiary name = "incendiary slug" desc = "An incendiary-coated shotgun slug." icon_state = "ishell" projectile_type = /obj/item/projectile/bullet/incendiary/shell + muzzle_flash_color = LIGHT_COLOR_FIRE /obj/item/ammo_casing/shotgun/frag12 name = "FRAG-12 slug" @@ -223,6 +247,7 @@ projectile_type = /obj/item/projectile/bullet/incendiary/shell/dragonsbreath pellets = 4 variance = 35 + muzzle_flash_color = LIGHT_COLOR_FIRE /obj/item/ammo_casing/shotgun/ion name = "ion shell" @@ -232,12 +257,18 @@ projectile_type = /obj/item/projectile/ion/weak pellets = 4 variance = 35 + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL + muzzle_flash_color = LIGHT_COLOR_LIGHTBLUE /obj/item/ammo_casing/shotgun/laserslug name = "laser slug" desc = "An advanced shotgun shell that uses a micro laser to replicate the effects of a laser weapon in a ballistic package." icon_state = "lshell" projectile_type = /obj/item/projectile/beam/laser + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL + muzzle_flash_color = LIGHT_COLOR_DARKRED /obj/item/ammo_casing/shotgun/techshell name = "unloaded technological shell" @@ -251,6 +282,8 @@ icon_state = "cshell" container_type = OPENCONTAINER projectile_type = /obj/item/projectile/bullet/dart + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL /obj/item/ammo_casing/shotgun/dart/New() ..() @@ -275,18 +308,24 @@ desc = "A tranquilizer round used to subdue individuals utilizing stimulants." icon_state = "nshell" projectile_type = /obj/item/projectile/bullet/dart/syringe/tranquilizer + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL materials = list(MAT_METAL=250) /obj/item/ammo_casing/a556 desc = "A 5.56mm bullet casing." caliber = "a556" projectile_type = /obj/item/projectile/bullet/heavybullet + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL /obj/item/ammo_casing/shotgun/fakebeanbag name = "beanbag shell" desc = "A weak beanbag shell." icon_state = "bshell" projectile_type = /obj/item/projectile/bullet/weakbullet/booze + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL /obj/item/ammo_casing/rocket name = "rocket shell" @@ -294,6 +333,8 @@ icon_state = "rocketshell" projectile_type = /obj/item/missile caliber = "rocket" + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_STRONG + muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG /obj/item/ammo_casing/caseless desc = "A caseless bullet casing." @@ -309,6 +350,8 @@ desc = "A .75 bullet casing." caliber = "75" projectile_type = /obj/item/projectile/bullet/gyro + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_STRONG + muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG /obj/item/ammo_casing/a40mm name = "40mm HE shell" @@ -316,11 +359,14 @@ caliber = "40mm" icon_state = "40mmHE" projectile_type = /obj/item/projectile/bullet/a40mm + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL /obj/item/ammo_casing/caseless/foam_dart name = "foam dart" desc = "It's nerf or nothing! Ages 8 and up." projectile_type = /obj/item/projectile/bullet/reusable/foam_dart + muzzle_flash_effect = null caliber = "foam_force" icon = 'icons/obj/guns/toy.dmi' icon_state = "foamdart" @@ -403,6 +449,7 @@ name = "assassination shell" desc = "A specialist shrapnel shell that has been laced with a silencing toxin." projectile_type = /obj/item/projectile/bullet/pellet/assassination + muzzle_flash_effect = null icon_state = "gshell" pellets = 6 variance = 25 @@ -411,9 +458,15 @@ desc = "A cap for children toys." caliber = "cap" projectile_type = /obj/item/projectile/bullet/cap + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL /obj/item/ammo_casing/laser desc = "An experimental laser casing." caliber = "laser" projectile_type = /obj/item/projectile/beam/laser + muzzle_flash_effect = /obj/effect/temp_visual/target_angled/muzzle_flash/energy + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_WEAK + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL + muzzle_flash_color = LIGHT_COLOR_DARKRED icon_state = "lasercasing" diff --git a/code/modules/projectiles/ammunition/boxes.dm b/code/modules/projectiles/ammunition/boxes.dm index 4484be63c83..53004b768db 100644 --- a/code/modules/projectiles/ammunition/boxes.dm +++ b/code/modules/projectiles/ammunition/boxes.dm @@ -1,10 +1,11 @@ /obj/item/ammo_box/a357 name = "speed loader (.357)" desc = "Designed to quickly reload revolvers." - icon_state = "357" ammo_type = /obj/item/ammo_casing/a357 max_ammo = 7 - multiple_sprites = 1 + icon_state = "357-7" // DEFAULT icon, composed of prefix + "-" + max_ammo for multiple_sprites == 1 boxes + multiple_sprites = 1 // see: /obj/item/ammo_box/update_icon() + icon_prefix = "357" // icon prefix, used in above formula to generate dynamic icons /obj/item/ammo_box/c38 name = "speed loader (.38)" @@ -12,7 +13,9 @@ icon_state = "38" ammo_type = /obj/item/ammo_casing/c38 max_ammo = 6 + icon_state = "38-6" // see previous entry for explanation of these vars multiple_sprites = 1 + icon_prefix = "38" /obj/item/ammo_box/c9mm name = "ammo box (9mm)" diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm index bcadfc62e08..a809953b8fd 100644 --- a/code/modules/projectiles/ammunition/energy.dm +++ b/code/modules/projectiles/ammunition/energy.dm @@ -6,9 +6,11 @@ var/e_cost = 100 //The amount of energy a cell needs to expend to create this shot. var/select_name = "energy" fire_sound = 'sound/weapons/laser.ogg' + muzzle_flash_effect = /obj/effect/temp_visual/target_angled/muzzle_flash/energy /obj/item/ammo_casing/energy/laser projectile_type = /obj/item/projectile/beam/laser + muzzle_flash_color = LIGHT_COLOR_DARKRED select_name = "kill" /obj/item/ammo_casing/energy/laser/cyborg //to balance cyborg energy cost seperately @@ -16,6 +18,7 @@ /obj/item/ammo_casing/energy/lasergun projectile_type = /obj/item/projectile/beam/laser + muzzle_flash_color = LIGHT_COLOR_DARKRED e_cost = 83 select_name = "kill" @@ -40,6 +43,7 @@ /obj/item/ammo_casing/energy/laser/pulse projectile_type = /obj/item/projectile/beam/pulse + muzzle_flash_color = LIGHT_COLOR_DARKBLUE e_cost = 200 select_name = "DESTROY" fire_sound = 'sound/weapons/pulse.ogg' @@ -52,6 +56,7 @@ /obj/item/ammo_casing/energy/laser/bluetag projectile_type = /obj/item/projectile/beam/lasertag/bluetag + muzzle_flash_color = LIGHT_COLOR_BLUE select_name = "bluetag" harmful = FALSE @@ -62,6 +67,7 @@ /obj/item/ammo_casing/energy/xray projectile_type = /obj/item/projectile/beam/xray + muzzle_flash_color = LIGHT_COLOR_GREEN e_cost = 100 fire_sound = 'sound/weapons/laser3.ogg' @@ -92,6 +98,7 @@ /obj/item/ammo_casing/energy/electrode projectile_type = /obj/item/projectile/energy/electrode + muzzle_flash_color = "#FFFF00" select_name = "stun" fire_sound = 'sound/weapons/taser.ogg' e_cost = 200 @@ -107,11 +114,13 @@ /obj/item/ammo_casing/energy/ion projectile_type = /obj/item/projectile/ion + muzzle_flash_color = LIGHT_COLOR_LIGHTBLUE select_name = "ion" fire_sound = 'sound/weapons/ionrifle.ogg' /obj/item/ammo_casing/energy/declone projectile_type = /obj/item/projectile/energy/declone + muzzle_flash_color = LIGHT_COLOR_GREEN select_name = "declone" fire_sound = 'sound/weapons/pulse3.ogg' @@ -122,6 +131,7 @@ /obj/item/ammo_casing/energy/flora fire_sound = 'sound/effects/stealthoff.ogg' + muzzle_flash_color = LIGHT_COLOR_GREEN harmful = FALSE /obj/item/ammo_casing/energy/flora/yield @@ -146,10 +156,13 @@ /obj/item/ammo_casing/energy/meteor projectile_type = /obj/item/projectile/meteor + muzzle_flash_effect = /obj/effect/temp_visual/target_angled/muzzle_flash + muzzle_flash_color = null select_name = "goddamn meteor" /obj/item/ammo_casing/energy/disabler projectile_type = /obj/item/projectile/beam/disabler + muzzle_flash_color = LIGHT_COLOR_LIGHTBLUE select_name = "disable" e_cost = 50 fire_sound = 'sound/weapons/taser2.ogg' @@ -160,6 +173,7 @@ /obj/item/ammo_casing/energy/plasma projectile_type = /obj/item/projectile/plasma + muzzle_flash_color = LIGHT_COLOR_PURPLE select_name = "plasma burst" fire_sound = 'sound/weapons/plasma_cutter.ogg' delay = 15 @@ -172,6 +186,7 @@ /obj/item/ammo_casing/energy/wormhole projectile_type = /obj/item/projectile/beam/wormhole + muzzle_flash_color = "#33CCFF" e_cost = 0 fire_sound = 'sound/weapons/pulse3.ogg' var/obj/item/gun/energy/wormhole_projector/gun = null @@ -183,10 +198,13 @@ /obj/item/ammo_casing/energy/wormhole/orange projectile_type = /obj/item/projectile/beam/wormhole/orange + muzzle_flash_color = "#FF6600" select_name = "orange" /obj/item/ammo_casing/energy/bolt projectile_type = /obj/item/projectile/energy/bolt + muzzle_flash_color = null + muzzle_flash_effect = /obj/effect/temp_visual/target_angled/muzzle_flash select_name = "bolt" e_cost = 500 fire_sound = 'sound/weapons/genhit.ogg' @@ -203,17 +221,21 @@ /obj/item/ammo_casing/energy/instakill projectile_type = /obj/item/projectile/beam/instakill + muzzle_flash_color = LIGHT_COLOR_PURPLE e_cost = 0 select_name = "DESTROY" /obj/item/ammo_casing/energy/instakill/blue projectile_type = /obj/item/projectile/beam/instakill/blue + muzzle_flash_color = LIGHT_COLOR_DARKBLUE /obj/item/ammo_casing/energy/instakill/red projectile_type = /obj/item/projectile/beam/instakill/red + muzzle_flash_color = LIGHT_COLOR_DARKRED /obj/item/ammo_casing/energy/plasma projectile_type = /obj/item/projectile/plasma + muzzle_flash_color = LIGHT_COLOR_PURPLE select_name = "plasma burst" fire_sound = 'sound/weapons/pulse.ogg' @@ -224,26 +246,31 @@ fire_sound = 'sound/magic/lightningbolt.ogg' e_cost = 200 select_name = "lightning beam" + muzzle_flash_color = LIGHT_COLOR_FADEDPURPLE projectile_type = /obj/item/projectile/energy/shock_revolver /obj/item/ammo_casing/energy/toxplasma projectile_type = /obj/item/projectile/energy/toxplasma + muzzle_flash_color = LIGHT_COLOR_FADEDPURPLE fire_sound = 'sound/weapons/taser2.ogg' select_name = "plasma dart" /obj/item/ammo_casing/energy/clown projectile_type = /obj/item/projectile/clown + muzzle_flash_effect = null fire_sound = 'sound/weapons/gunshots/gunshot_smg.ogg' select_name = "clown" /obj/item/ammo_casing/energy/sniper projectile_type = /obj/item/projectile/beam/sniper + muzzle_flash_color = LIGHT_COLOR_PINK fire_sound = 'sound/weapons/marauder.ogg' delay = 50 select_name = "snipe" /obj/item/ammo_casing/energy/teleport projectile_type = /obj/item/projectile/energy/teleport + muzzle_flash_color = LIGHT_COLOR_LIGHTBLUE fire_sound = 'sound/weapons/wave.ogg' e_cost = 250 select_name = "teleport beam" @@ -258,6 +285,7 @@ /obj/item/ammo_casing/energy/mimic projectile_type = /obj/item/projectile/mimic + muzzle_flash_effect = null fire_sound = 'sound/weapons/bite.ogg' select_name = "gun mimic" var/mimic_type diff --git a/code/modules/projectiles/ammunition/magazines.dm b/code/modules/projectiles/ammunition/magazines.dm index 8cb48f8e36a..135f8c14718 100644 --- a/code/modules/projectiles/ammunition/magazines.dm +++ b/code/modules/projectiles/ammunition/magazines.dm @@ -133,6 +133,9 @@ /obj/item/ammo_box/magazine/internal/shot/riot/short max_ammo = 3 +/obj/item/ammo_box/magazine/internal/shot/riot/buckshot + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + /obj/item/ammo_box/magazine/internal/grenadelauncher name = "grenade launcher internal magazine" ammo_type = /obj/item/ammo_casing/a40mm @@ -268,15 +271,15 @@ /obj/item/ammo_box/magazine/wt550m9/wtap name = "wt550 magazine (Armour Piercing 4.6x30mm)" - ammo_type = /obj/item/ammo_casing/c46x30mmap + ammo_type = /obj/item/ammo_casing/c46x30mm/ap /obj/item/ammo_box/magazine/wt550m9/wttx name = "wt550 magazine (Toxin Tipped 4.6x30mm)" - ammo_type = /obj/item/ammo_casing/c46x30mmtox + ammo_type = /obj/item/ammo_casing/c46x30mm/tox /obj/item/ammo_box/magazine/wt550m9/wtic name = "wt550 magazine (Incendiary 4.6x30mm)" - ammo_type = /obj/item/ammo_casing/c46x30mminc + ammo_type = /obj/item/ammo_casing/c46x30mm/inc /obj/item/ammo_box/magazine/uzim9mm name = "uzi magazine (9mm)" @@ -299,17 +302,17 @@ /obj/item/ammo_box/magazine/smgm9mm/ap name = "SMG magazine (Armour Piercing 9mm)" - ammo_type = /obj/item/ammo_casing/c9mmap + ammo_type = /obj/item/ammo_casing/c9mm/ap materials = list(MAT_METAL = 3000) /obj/item/ammo_box/magazine/smgm9mm/toxin name = "SMG magazine (Toxin Tipped 9mm)" - ammo_type = /obj/item/ammo_casing/c9mmtox + ammo_type = /obj/item/ammo_casing/c9mm/tox materials = list(MAT_METAL = 3000) /obj/item/ammo_box/magazine/smgm9mm/fire name = "SMG Magazine (Incendiary 9mm)" - ammo_type = /obj/item/ammo_casing/c9mminc + ammo_type = /obj/item/ammo_casing/c9mm/inc materials = list(MAT_METAL = 3000) /obj/item/ammo_box/magazine/smgm9mm/update_icon() @@ -380,10 +383,7 @@ origin_tech = "combat=3;syndicate=1" caliber = "shotgun" max_ammo = 8 - -/obj/item/ammo_box/magazine/m12g/update_icon() - ..() - icon_state = "[initial(icon_state)]-[CEILING(ammo_count(0)/8, 1)*8]" + multiple_sprites = 2 /obj/item/ammo_box/magazine/m12g/buckshot name = "shotgun magazine (12g buckshot slugs)" @@ -411,6 +411,24 @@ icon_state = "m12gbc" ammo_type = /obj/item/ammo_casing/shotgun/breaching +/obj/item/ammo_box/magazine/m12g/XtrLrg + name = "\improper XL shotgun magazine (12g slugs)" + desc = "An extra large drum magazine." + icon_state = "m12gXlSl" + w_class = WEIGHT_CLASS_NORMAL + ammo_type = /obj/item/ammo_casing/shotgun + max_ammo = 16 + +/obj/item/ammo_box/magazine/m12g/XtrLrg/buckshot + name = "\improper XL shotgun magazine (12g buckshot)" + icon_state = "m12gXlBs" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + +/obj/item/ammo_box/magazine/m12g/XtrLrg/dragon + name = "\improper XL shotgun magazine (12g dragon's breath)" + icon_state = "m12gXlDb" + ammo_type = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath + /obj/item/ammo_box/magazine/toy name = "foam force META magazine" ammo_type = /obj/item/ammo_casing/caseless/foam_dart diff --git a/code/modules/projectiles/ammunition/special.dm b/code/modules/projectiles/ammunition/special.dm index 5fa53566fe5..f37690e2787 100644 --- a/code/modules/projectiles/ammunition/special.dm +++ b/code/modules/projectiles/ammunition/special.dm @@ -2,6 +2,8 @@ name = "magic casing" desc = "I didn't even know magic needed ammo..." projectile_type = /obj/item/projectile/magic + muzzle_flash_color = COLOR_BLUE_GRAY + muzzle_flash_effect = /obj/effect/temp_visual/target_angled/muzzle_flash/magic /obj/item/ammo_casing/magic/change projectile_type = /obj/item/projectile/magic/change @@ -40,13 +42,14 @@ projectile_type = pick(typesof(/obj/item/projectile/magic)) ..() -/obj/item/ammo_casing/forcebolt +/obj/item/ammo_casing/magic/forcebolt projectile_type = /obj/item/projectile/forcebolt /obj/item/ammo_casing/syringegun name = "syringe gun spring" desc = "A high-power spring that throws syringes." projectile_type = null + muzzle_flash_effect = null /obj/item/ammo_casing/energy/c3dbullet projectile_type = /obj/item/projectile/bullet/midbullet3 diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 3e41bf81271..ab21182e98f 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -109,20 +109,33 @@ to_chat(user, "*click*") playsound(user, 'sound/weapons/empty.ogg', 100, 1) -/obj/item/gun/proc/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) +/obj/item/gun/proc/shoot_live_shot(mob/living/user, atom/target, pointblank = FALSE, message = TRUE) if(recoil) shake_camera(user, recoil + 1, recoil) + var/muzzle_range = chambered.muzzle_flash_range + var/muzzle_strength = chambered.muzzle_flash_strength + var/muzzle_flash_time = 0.2 SECONDS if(suppressed) playsound(user, fire_sound, 10, 1) + muzzle_range *= 0.5 + muzzle_strength *= 0.2 + muzzle_flash_time *= 0.5 else playsound(user, fire_sound, 50, 1) - if(!message) - return - if(pointblank) - user.visible_message("[user] fires [src] point blank at [pbtarget]!", "You fire [src] point blank at [pbtarget]!", "You hear \a [fire_sound_text]!") + if(message) + if(pointblank) + user.visible_message("[user] fires [src] point blank at [target]!", "You fire [src] point blank at [target]!", "You hear \a [fire_sound_text]!") + else + user.visible_message("[user] fires [src]!", "You fire [src]!", "You hear \a [fire_sound_text]!") + if(chambered.muzzle_flash_effect) + var/obj/effect/temp_visual/target_angled/muzzle_flash/effect = new chambered.muzzle_flash_effect(get_turf(src), target, muzzle_flash_time) + effect.alpha = min(255, muzzle_strength * 255) + if(chambered.muzzle_flash_color) + effect.color = chambered.muzzle_flash_color + effect.set_light(muzzle_range, muzzle_strength, chambered.muzzle_flash_color) else - user.visible_message("[user] fires [src]!", "You fire [src]!", "You hear \a [fire_sound_text]!") + effect.color = LIGHT_COLOR_TUNGSTEN /obj/item/gun/emp_act(severity) for(var/obj/O in contents) @@ -228,9 +241,9 @@ break else if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot - shoot_live_shot(user, 1, target, message) + shoot_live_shot(user, target, TRUE, message) else - shoot_live_shot(user, 0, target, message) + shoot_live_shot(user, target, FALSE, message) else shoot_with_empty_chamber(user) break @@ -250,9 +263,9 @@ return else if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot - shoot_live_shot(user, 1, target, message) + shoot_live_shot(user, target, TRUE, message) else - shoot_live_shot(user, 0, target, message) + shoot_live_shot(user, target, FALSE, message) else shoot_with_empty_chamber(user) return diff --git a/code/modules/projectiles/guns/alien.dm b/code/modules/projectiles/guns/alien.dm index d9f149b3b17..aec047ad89b 100644 --- a/code/modules/projectiles/guns/alien.dm +++ b/code/modules/projectiles/guns/alien.dm @@ -49,6 +49,7 @@ name = "alloy spike" desc = "A broadhead spike made out of a weird silvery metal." projectile_type = /obj/item/projectile/bullet/spike + muzzle_flash_effect = null throwforce = 5 w_class = WEIGHT_CLASS_NORMAL caliber = "spike" diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index ce0abc8cc69..695ab02c43b 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -15,7 +15,6 @@ ammo_x_offset = 2 var/shaded_charge = 0 //if this gun uses a stateful charge bar for more detail var/selfcharge = 0 - var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically var/charge_tick = 0 var/charge_delay = 4 @@ -68,11 +67,6 @@ charge_tick = 0 if(!cell) return // check if we actually need to recharge - var/obj/item/ammo_casing/energy/E = ammo_type[select] - if(use_external_power) - var/obj/item/stock_parts/cell/external = get_external_cell() - if(!external || !external.use(E.e_cost)) //Take power from the borg... - return //Note, uses /10 because of shitty mods to the cell system cell.give(100) //... to recharge the shot on_recharge() update_icon() @@ -207,14 +201,3 @@ var/obj/item/ammo_casing/energy/shot = ammo_type[select] //Necessary to find cost of shot if(R.cell.use(shot.e_cost)) //Take power from the borg... cell.give(shot.e_cost) //... to recharge the shot - -/obj/item/gun/energy/proc/get_external_cell() - if(istype(loc, /obj/item/rig_module)) - var/obj/item/rig_module/module = loc - if(module.holder && module.holder.wearer) - var/mob/living/carbon/human/H = module.holder.wearer - if(istype(H) && H.back) - var/obj/item/rig/suit = H.back - if(istype(suit)) - return suit.cell - return null diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index 19316cb9540..c715ba966dd 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -88,7 +88,7 @@ if(!holds_charge) empty() -/obj/item/gun/energy/kinetic_accelerator/shoot_live_shot() +/obj/item/gun/energy/kinetic_accelerator/shoot_live_shot(mob/living/user, atom/target, pointblank = FALSE, message = TRUE) . = ..() attempt_reload() @@ -173,6 +173,7 @@ //Casing /obj/item/ammo_casing/energy/kinetic projectile_type = /obj/item/projectile/kinetic + muzzle_flash_color = null select_name = "kinetic" e_cost = 500 fire_sound = 'sound/weapons/kenetic_accel.ogg' // fine spelling there chap diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 3e9c882458e..9106d80ef98 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -95,12 +95,6 @@ ..() damage = min(damage+7, 100) -/obj/item/gun/energy/lasercannon/mounted - name = "mounted laser cannon" - selfcharge = 1 - use_external_power = 1 - charge_delay = 10 - /obj/item/gun/energy/lasercannon/cyborg /obj/item/gun/energy/lasercannon/cyborg/newshot() diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm index 2df2d877c53..3d9925da974 100644 --- a/code/modules/projectiles/guns/energy/nuclear.dm +++ b/code/modules/projectiles/guns/energy/nuclear.dm @@ -21,11 +21,6 @@ /obj/item/gun/energy/gun/cyborg/emp_act() return -/obj/item/gun/energy/gun/mounted - name = "mounted energy gun" - selfcharge = 1 - use_external_power = 1 - /obj/item/gun/energy/gun/mini name = "miniature energy gun" desc = "A small, pistol-sized energy gun with a built-in flashlight. It has two settings: disable and kill." diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 48aaff9d8b4..44e393f9762 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -112,10 +112,6 @@ max_mod_capacity = 0 empty_state = null -/obj/item/gun/energy/kinetic_accelerator/crossbow/ninja - name = "energy dart thrower" - ammo_type = list(/obj/item/ammo_casing/energy/dart) - /obj/item/gun/energy/kinetic_accelerator/crossbow/large name = "energy crossbow" desc = "A reverse engineered weapon using syndicate technology." @@ -136,7 +132,7 @@ if(!suppressed) playsound(loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) user.visible_message("[user] cocks the [name] and pretends to blow [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide!
    ") - shoot_live_shot() + shoot_live_shot(user, user, FALSE, FALSE) return OXYLOSS // Plasma Cutters // @@ -424,7 +420,7 @@ update_icon() if(istype(loc, /mob/living/carbon)) - var /mob/living/carbon/M = loc + var/mob/living/carbon/M = loc if(src == M.machine) update_dat() M << browse("Temperature Gun Configuration
    [dat]", "window=tempgun;size=510x102") diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index 70bb8be7e46..141366dd5d9 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -7,11 +7,6 @@ ammo_type = list(/obj/item/ammo_casing/energy/electrode) ammo_x_offset = 3 -/obj/item/gun/energy/taser/mounted - name = "mounted taser gun" - selfcharge = 1 - use_external_power = 1 - /obj/item/gun/energy/shock_revolver name = "tesla revolver" desc = "A high-tech revolver that fires internal, reusable shock cartridges in a revolving cylinder. The cartridges can be recharged using conventional rechargers." diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index 72d128178a6..e028d970894 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -71,7 +71,7 @@ icon = 'icons/obj/wizard.dmi' icon_state = "focus" item_state = "focus" - ammo_type = list(/obj/item/ammo_casing/forcebolt) + ammo_type = /obj/item/ammo_casing/magic/forcebolt /obj/item/gun/magic/staff/spellblade name = "spellblade" diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 25db1f10dcb..ca4499c8832 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -95,6 +95,7 @@ if(!user.unEquip(A)) return to_chat(user, "You screw [S] onto [src].") + playsound(src, 'sound/items/screwdriver.ogg', 40, 1) suppressed = A S.oldsound = fire_sound S.initial_w_class = w_class @@ -120,6 +121,7 @@ ..() return to_chat(user, "You unscrew [suppressed] from [src].") + playsound(src, 'sound/items/screwdriver.ogg', 40, 1) user.put_in_hands(suppressed) fire_sound = S.oldsound w_class = S.initial_w_class diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index 17a20f82d59..ea52a954a5b 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -275,13 +275,27 @@ if(magazine) overlays.Cut() overlays += "[magazine.icon_state]" - return + if(istype(magazine, /obj/item/ammo_box/magazine/m12g/XtrLrg)) + w_class = WEIGHT_CLASS_BULKY + else + w_class = WEIGHT_CLASS_NORMAL + else + w_class = WEIGHT_CLASS_NORMAL /obj/item/gun/projectile/automatic/shotgun/bulldog/update_icon() overlays.Cut() update_magazine() icon_state = "bulldog[chambered ? "" : "-e"]" +/obj/item/gun/projectile/automatic/shotgun/bulldog/attackby(var/obj/item/A as obj, mob/user as mob, params) + if(istype(A, /obj/item/ammo_box/magazine/m12g/XtrLrg)) + if(istype(loc, /obj/item/storage)) // To prevent inventory exploits + var/obj/item/storage/Strg = loc + if(Strg.max_w_class < WEIGHT_CLASS_BULKY) + to_chat(user, "You can't reload [src], with a XL mag, while it's in a normal bag.") + return + return ..() + /obj/item/gun/projectile/automatic/shotgun/bulldog/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) ..() empty_alarm() diff --git a/code/modules/projectiles/guns/projectile/bow.dm b/code/modules/projectiles/guns/projectile/bow.dm index 70229a7c6b0..d87b9a08d98 100644 --- a/code/modules/projectiles/guns/projectile/bow.dm +++ b/code/modules/projectiles/guns/projectile/bow.dm @@ -78,6 +78,7 @@ icon_state = "arrow" force = 10 projectile_type = /obj/item/projectile/bullet/reusable/arrow + muzzle_flash_effect = null caliber = "arrow" //quiver diff --git a/code/modules/projectiles/guns/projectile/saw.dm b/code/modules/projectiles/guns/projectile/saw.dm index 3c25bc30f93..9c55f3b23fd 100644 --- a/code/modules/projectiles/guns/projectile/saw.dm +++ b/code/modules/projectiles/guns/projectile/saw.dm @@ -87,7 +87,7 @@ damage = 7 armour_penetration = 0 -obj/item/projectile/bullet/saw/incen/Move() +/obj/item/projectile/bullet/saw/incen/Move() ..() var/turf/location = get_turf(src) if(location) @@ -142,6 +142,8 @@ obj/item/projectile/bullet/saw/incen/Move() icon_state = "762-casing" caliber = "mm55645" projectile_type = /obj/item/projectile/bullet/saw + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_STRONG + muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG /obj/item/ammo_casing/mm556x45/bleeding desc = "A 556x45mm bullet casing with specialized inner-casing, that when it makes contact with a target, release tiny shrapnel to induce internal bleeding." @@ -159,3 +161,4 @@ obj/item/projectile/bullet/saw/incen/Move() /obj/item/ammo_casing/mm556x45/incen desc = "A 556x45mm bullet casing designed with a chemical-filled capsule on the tip that when bursted, reacts with the atmosphere to produce a fireball, engulfing the target in flames. " projectile_type = /obj/item/projectile/bullet/saw/incen + muzzle_flash_color = LIGHT_COLOR_FIRE diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm index 17a55722d2c..c1824b48f7e 100644 --- a/code/modules/projectiles/guns/projectile/shotgun.dm +++ b/code/modules/projectiles/guns/projectile/shotgun.dm @@ -197,6 +197,8 @@ ..() post_sawoff() +/obj/item/gun/projectile/shotgun/riot/buckshot //comes pre-loaded with buckshot rather than rubber + mag_type = /obj/item/ammo_box/magazine/internal/shot/riot/buckshot /////////////////////// @@ -258,7 +260,7 @@ ..() guns_left = 0 -/obj/item/gun/projectile/shotgun/boltaction/enchanted/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) +/obj/item/gun/projectile/shotgun/boltaction/enchanted/shoot_live_shot(mob/living/user, atom/target, pointblank = FALSE, message = TRUE) ..() if(guns_left) var/obj/item/gun/projectile/shotgun/boltaction/enchanted/GUN = new @@ -276,7 +278,7 @@ /obj/item/gun/projectile/shotgun/automatic -/obj/item/gun/projectile/shotgun/automatic/shoot_live_shot(mob/living/user as mob|obj) +/obj/item/gun/projectile/shotgun/automatic/shoot_live_shot(mob/living/user, atom/target, pointblank = FALSE, message = TRUE) ..() pump(user) diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm index 761c15a6141..316a22a4db1 100644 --- a/code/modules/projectiles/guns/projectile/sniper.dm +++ b/code/modules/projectiles/guns/projectile/sniper.dm @@ -78,6 +78,8 @@ desc = "A .50 bullet casing." caliber = ".50" projectile_type = /obj/item/projectile/bullet/sniper + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_STRONG + muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG icon_state = ".50" /obj/item/projectile/bullet/sniper @@ -192,6 +194,8 @@ desc = "A .50 caliber compact round casing." caliber = ".50" projectile_type = /obj/item/projectile/bullet/sniper/compact + muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL + muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL icon_state = ".50" /obj/item/projectile/bullet/sniper/compact //Can't dismember, and can't break things; just deals massive damage. diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index c33c649ae77..2a7fd2e602d 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -287,7 +287,7 @@ Range() sleep(max(1, speed)) -obj/item/projectile/proc/reflect_back(atom/source, list/position_modifiers = list(0, 0, 0, 0, 0, -1, 1, -2, 2)) +/obj/item/projectile/proc/reflect_back(atom/source, list/position_modifiers = list(0, 0, 0, 0, 0, -1, 1, -2, 2)) if(starting) var/new_x = starting.x + pick(position_modifiers) var/new_y = starting.y + pick(position_modifiers) @@ -306,7 +306,7 @@ obj/item/projectile/proc/reflect_back(atom/source, list/position_modifiers = lis xo = new_x - curloc.x Angle = null // Will be calculated in fire() -obj/item/projectile/Crossed(atom/movable/AM, oldloc) //A mob moving on a tile with a projectile is hit by it. +/obj/item/projectile/Crossed(atom/movable/AM, oldloc) //A mob moving on a tile with a projectile is hit by it. ..() if(isliving(AM) && AM.density && !checkpass(PASSMOB)) Bump(AM, 1) diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 13f959be590..766975a4aad 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -11,7 +11,7 @@ impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser is_reflectable = TRUE light_range = 2 - light_color = LIGHT_COLOR_RED + light_color = LIGHT_COLOR_DARKRED ricochets_max = 50 //Honk! ricochet_chance = 80 @@ -85,6 +85,7 @@ icon_state = "omnilaser" hitsound = 'sound/weapons/tap.ogg' nodamage = 1 + damage = 0 damage_type = STAMINA flag = "laser" var/suit_types = list(/obj/item/clothing/suit/redtag, /obj/item/clothing/suit/bluetag) @@ -109,11 +110,12 @@ icon_state = "laser" suit_types = list(/obj/item/clothing/suit/bluetag) impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser - light_color = LIGHT_COLOR_RED + light_color = LIGHT_COLOR_DARKRED /obj/item/projectile/beam/lasertag/bluetag icon_state = "bluelaser" suit_types = list(/obj/item/clothing/suit/redtag) + light_color = LIGHT_COLOR_BLUE /obj/item/projectile/beam/sniper name = "sniper beam" @@ -161,7 +163,7 @@ /obj/item/projectile/beam/instakill/red icon_state = "red_laser" impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser - light_color = LIGHT_COLOR_RED + light_color = LIGHT_COLOR_DARKRED /obj/item/projectile/beam/instakill/on_hit(atom/target) . = ..() diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 2323aa06cf4..d6140d28f4e 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -31,16 +31,17 @@ M.AdjustDrowsy(10) A.volume += 5 //Because we can -/obj/item/projectile/bullet/weakbullet2 //detective revolver instastuns, but multiple shots are better for keeping punks down +/obj/item/projectile/bullet/weakbullet2 //detective revolver name = "rubber bullet" damage = 5 - weaken = 3 - stamina = 60 + stamina = 35 icon_state = "bullet-r" /obj/item/projectile/bullet/weakbullet2/invisible //finger gun bullets name = "invisible bullet" damage = 0 + weaken = 3 + stamina = 60 icon_state = null hitsound_wall = null diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 4aee2398b68..080114554bb 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -270,7 +270,6 @@ M.mind.transfer_to(new_mob) else new_mob.attack_log_old = M.attack_log_old.Copy() - new_mob.logs = M.logs.Copy() new_mob.key = M.key to_chat(new_mob, "Your form morphs into that of a [randomize].") diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index 91d5900610d..bacf2285c83 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -24,6 +24,7 @@ var/list/hacked_reagents = list("toxin") var/hack_message = "You disable the safety safeguards, enabling the \"Mad Scientist\" mode." var/unhack_message = "You re-enable the safety safeguards, enabling the \"NT Standard\" mode." + var/is_drink = FALSE /obj/machinery/chem_dispenser/get_cell() return cell @@ -120,7 +121,6 @@ var/usedpower = cell.give(recharge_amount) if(usedpower) use_power(15 * recharge_amount) - SSnanoui.update_uis(src) // update all UIs attached to src recharge_counter = 0 return recharge_counter++ @@ -131,7 +131,6 @@ else spawn(rand(0, 15)) stat |= NOPOWER - SSnanoui.update_uis(src) // update all UIs attached to src /obj/machinery/chem_dispenser/ex_act(severity) if(severity < 3) @@ -145,19 +144,17 @@ beaker = null overlays.Cut() -/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) +/obj/machinery/chem_dispenser/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, 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, "chem_dispenser.tmpl", ui_title, 390, 655) - // open the new ui window + ui = new(user, src, ui_key, "ChemDispenser", ui_title, 390, 655) ui.open() -/obj/machinery/chem_dispenser/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/chem_dispenser/tgui_data(mob/user) var/data[0] + data["glass"] = is_drink data["amount"] = amount data["energy"] = cell.charge ? cell.charge * powerefficiency : "0" //To prevent NaN in the UI. data["maxEnergy"] = cell.maxcharge * powerefficiency @@ -187,63 +184,59 @@ return data -/obj/machinery/chem_dispenser/Topic(href, href_list) +/obj/machinery/chem_dispenser/tgui_act(actions, params) if(..()) - return TRUE + return + if(stat & (NOPOWER|BROKEN)) + return - if(href_list["amount"]) - amount = round(text2num(href_list["amount"]), 1) // round to nearest 1 - if(amount < 0) // Since the user can actually type the commands himself, some sanity checking - amount = 0 - if(amount > 100) - amount = 100 - - if(href_list["dispense"]) - if(!is_operational() || QDELETED(cell)) - return - if(beaker && dispensable_reagents.Find(href_list["dispense"])) + . = TRUE + switch(actions) + if("amount") + amount = clamp(round(text2num(params["amount"]), 1), 0, 50) // round to nearest 1 and clamp to 0 - 50 + if("dispense") + if(!is_operational() || QDELETED(cell)) + return + if(!beaker || !dispensable_reagents.Find(params["reagent"])) + return var/datum/reagents/R = beaker.reagents var/free = R.maximum_volume - R.total_volume var/actual = min(amount, (cell.charge * powerefficiency) * 10, free) - if(!cell.use(actual / powerefficiency)) atom_say("Not enough energy to complete operation!") return - - R.add_reagent(href_list["dispense"], actual) + R.add_reagent(params["reagent"], actual) overlays.Cut() if(!icon_beaker) - icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position. + icon_beaker = mutable_appearance('icons/obj/chemical.dmi', "disp_beaker") //randomize beaker overlay position. icon_beaker.pixel_x = rand(-10, 5) overlays += icon_beaker - - if(href_list["remove"]) - if(beaker) - if(href_list["removeamount"]) - var/amount = text2num(href_list["removeamount"]) - if(isnum(amount) && (amount > 0)) - var/datum/reagents/R = beaker.reagents - var/id = href_list["remove"] - R.remove_reagent(id, amount) - else if(isnum(amount) && (amount == -1)) //Isolate instead - var/datum/reagents/R = beaker.reagents - var/id = href_list["remove"] - R.isolate_reagent(id) - - if(href_list["ejectBeaker"]) - if(beaker) + if("remove") + var/amount = text2num(params["amount"]) + if(!beaker || !amount) + return + var/datum/reagents/R = beaker.reagents + var/id = params["reagent"] + if(amount > 0) + R.remove_reagent(id, amount) + else if(amount == -1) //Isolate instead + R.isolate_reagent(id) + if("ejectBeaker") + if(!beaker) + return beaker.forceMove(loc) if(Adjacent(usr) && !issilicon(usr)) usr.put_in_hands(beaker) beaker = null overlays.Cut() + else + return FALSE add_fingerprint(usr) - return TRUE // update UIs attached to this object /obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params) if(exchange_parts(user, I)) - SSnanoui.update_uis(src) + SStgui.update_uis(src) return if(isrobot(user)) @@ -263,9 +256,9 @@ beaker = I I.forceMove(src) to_chat(user, "You set [I] on the machine.") - SSnanoui.update_uis(src) // update all UIs attached to src + SStgui.update_uis(src) // update all UIs attached to src if(!icon_beaker) - icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position. + icon_beaker = mutable_appearance('icons/obj/chemical.dmi', "disp_beaker") //randomize beaker overlay position. icon_beaker.pixel_x = rand(-10, 5) overlays += icon_beaker return @@ -299,8 +292,7 @@ to_chat(user, unhack_message) dispensable_reagents -= hacked_reagents hackedcheck = FALSE - SSnanoui.update_uis(src) - + SStgui.update_uis(src) /obj/machinery/chem_dispenser/screwdriver_act(mob/user, obj/item/I) if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", "[initial(icon_state)]", I)) @@ -321,14 +313,14 @@ return attack_hand(user) /obj/machinery/chem_dispenser/attack_ghost(mob/user) - if(user.can_admin_interact()) - return attack_hand(user) + if(stat & BROKEN) + return + tgui_interact(user) /obj/machinery/chem_dispenser/attack_hand(mob/user) if(stat & BROKEN) return - - ui_interact(user) + tgui_interact(user) /obj/machinery/chem_dispenser/soda icon_state = "soda_dispenser" @@ -342,6 +334,7 @@ hacked_reagents = list("thirteenloko") hack_message = "You change the mode from 'McNano' to 'Pizza King'." unhack_message = "You change the mode from 'Pizza King' to 'McNano'." + is_drink = TRUE /obj/machinery/chem_dispenser/soda/New() ..() @@ -377,6 +370,7 @@ hacked_reagents = list("goldschlager", "patron", "absinthe", "ethanol", "nothing", "sake") hack_message = "You disable the 'nanotrasen-are-cheap-bastards' lock, enabling hidden and very expensive boozes." unhack_message = "You re-enable the 'nanotrasen-are-cheap-bastards' lock, disabling hidden and very expensive boozes." + is_drink = TRUE /obj/machinery/chem_dispenser/beer/New() ..() diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index 39aa91099c0..8fddc97537f 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -1,15 +1,19 @@ /obj/machinery/chem_heater name = "chemical heater" - density = 1 - anchored = 1 + density = TRUE + anchored = TRUE icon = 'icons/obj/chemical.dmi' icon_state = "mixer0b" use_power = IDLE_POWER_USE idle_power_usage = 40 - resistance_flags = FIRE_PROOF | ACID_PROOF + resistance_flags = FIRE_PROOF|ACID_PROOF var/obj/item/reagent_containers/beaker = null var/desired_temp = T0C var/on = FALSE + /// Whether this should auto-eject the beaker once done heating/cooling. + var/auto_eject = FALSE + /// The higher this number, the faster reagents will heat/cool. + var/speed_increase = 0 /obj/machinery/chem_heater/New() ..() @@ -19,35 +23,36 @@ component_parts += new /obj/item/stack/sheet/glass(null) RefreshParts() +/obj/machinery/chem_heater/RefreshParts() + speed_increase = initial(speed_increase) + for(var/obj/item/stock_parts/micro_laser/M in component_parts) + speed_increase += 5 * (M.rating - 1) + /obj/machinery/chem_heater/process() ..() - if(stat & NOPOWER) + if(stat & (NOPOWER|BROKEN)) return - var/state_change = FALSE if(on) if(beaker) if(!beaker.reagents.total_volume) on = FALSE - SSnanoui.update_uis(src) return - beaker.reagents.temperature_reagents(desired_temp) - beaker.reagents.temperature_reagents(desired_temp) - if(abs(beaker.reagents.chem_temp - desired_temp) <= 3) + beaker.reagents.temperature_reagents(desired_temp, max(1, 35 - speed_increase)) + if(round(beaker.reagents.chem_temp) == round(desired_temp)) + playsound(loc, 'sound/machines/ding.ogg', 50, 1) on = FALSE - state_change = TRUE - - if(state_change) - SSnanoui.update_uis(src) + if(auto_eject) + eject_beaker() /obj/machinery/chem_heater/proc/eject_beaker(mob/user) if(beaker) beaker.forceMove(get_turf(src)) - if(Adjacent(user) && !issilicon(user)) + if(user && Adjacent(user) && !issilicon(user)) user.put_in_hands(beaker) beaker = null icon_state = "mixer0b" on = FALSE - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/machinery/chem_heater/power_change() if(powered()) @@ -55,7 +60,6 @@ else spawn(rand(0, 15)) stat |= NOPOWER - SSnanoui.update_uis(src) /obj/machinery/chem_heater/attackby(obj/item/I, mob/user) if(isrobot(user)) @@ -71,7 +75,7 @@ I.forceMove(src) to_chat(user, "You add the beaker to the machine!") icon_state = "mixer1b" - SSnanoui.update_uis(src) + SStgui.update_uis(src) return if(exchange_parts(user, I)) @@ -95,62 +99,62 @@ default_deconstruction_crowbar(user, I) /obj/machinery/chem_heater/attack_hand(mob/user) - ui_interact(user) + tgui_interact(user) /obj/machinery/chem_heater/attack_ghost(mob/user) - if(user.can_admin_interact()) - return attack_hand(user) + tgui_interact(user) /obj/machinery/chem_heater/attack_ai(mob/user) add_hiddenprint(user) return attack_hand(user) -/obj/machinery/chem_heater/Topic(href, href_list) +/obj/machinery/chem_heater/tgui_act(action, params) if(..()) - return FALSE + return + if(stat & (NOPOWER|BROKEN)) + return - if(href_list["toggle_on"]) - if(!beaker.reagents.total_volume) - return FALSE - on = !on - . = 1 - - if(href_list["adjust_temperature"]) - var/val = href_list["adjust_temperature"] - if(isnum(val)) - desired_temp = clamp(desired_temp+val, 0, 1000) - else if(val == "input") - var/target = input("Please input the target temperature", name) as num - desired_temp = clamp(target, 0, 1000) + . = TRUE + switch(action) + if("toggle_on") + on = !on + if("adjust_temperature") + desired_temp = clamp(text2num(params["target"]), 0, 1000) + if("eject_beaker") + eject_beaker(usr) + . = FALSE + if("toggle_autoeject") + auto_eject = !auto_eject else return FALSE - . = 1 + add_fingerprint(usr) - if(href_list["eject_beaker"]) - eject_beaker(usr) - . = 0 //updated in eject_beaker() already - -/obj/machinery/chem_heater/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null) +/obj/machinery/chem_heater/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) if(user.stat || user.restrained()) return - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "chem_heater.tmpl", "ChemHeater", 350, 270) + ui = new(user, src, ui_key, "ChemHeater", "Chemical Heater", 350, 270, master_ui, state) ui.open() -/obj/machinery/chem_heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/chem_heater/tgui_data(mob/user) var/data[0] + var/cur_temp = beaker ? beaker.reagents.chem_temp : null data["targetTemp"] = desired_temp + data["targetTempReached"] = FALSE + data["autoEject"] = auto_eject data["isActive"] = on - data["isBeakerLoaded"] = beaker ? 1 : 0 + data["isBeakerLoaded"] = beaker ? TRUE : FALSE - data["currentTemp"] = beaker ? beaker.reagents.chem_temp : null + data["currentTemp"] = cur_temp data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null data["beakerMaxVolume"] = beaker ? beaker.volume : null + if(cur_temp) + data["targetTempReached"] = round(cur_temp) == round(desired_temp) + //copy-pasted from chem dispenser var/beakerContents[0] if(beaker) diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index 4d1f577969c..f2b3247fe4b 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -1,3 +1,9 @@ +#define MAX_PILL_SPRITE 20 //max icon state of the pill sprites +#define MAX_MULTI_AMOUNT 20 // Max number of pills/patches that can be made at once +#define MAX_UNITS_PER_PILL 100 // Max amount of units in a pill +#define MAX_UNITS_PER_PATCH 30 // Max amount of units in a patch +#define MAX_CUSTOM_NAME_LEN 64 // Max length of a custom pill/condiment/whatever + /obj/machinery/chem_master name = "\improper ChemMaster 3000" density = TRUE @@ -14,10 +20,12 @@ var/useramount = 30 // Last used amount var/pillamount = 10 var/patchamount = 10 - var/bottlesprite = "bottle" - var/pillsprite = "1" + var/bottlesprite = 1 + var/pillsprite = 1 var/client/has_sprites = list() var/printing = FALSE + var/static/list/pill_bottle_wrappers + var/static/list/bottle_styles /obj/machinery/chem_master/New() ..() @@ -94,7 +102,7 @@ beaker = I I.forceMove(src) to_chat(user, "You add the beaker to the machine!") - SSnanoui.update_uis(src) + SStgui.update_uis(src) update_icon() else if(istype(I, /obj/item/storage/pill_bottle)) @@ -109,14 +117,10 @@ loaded_pill_bottle = I I.forceMove(src) to_chat(user, "You add [I] into the dispenser slot!") - SSnanoui.update_uis(src) + SStgui.update_uis(src) else return ..() - - - - /obj/machinery/chem_master/crowbar_act(mob/user, obj/item/I) if(!panel_open) return @@ -142,319 +146,127 @@ power_change() return TRUE -/obj/machinery/chem_master/Topic(href, href_list) +/obj/machinery/chem_master/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) if(..()) + return + if(stat & (NOPOWER|BROKEN)) + return + + if(tgui_act_modal(action, params, ui, state)) return TRUE add_fingerprint(usr) usr.set_machine(src) - - if(href_list["ejectp"]) - if(loaded_pill_bottle) - loaded_pill_bottle.forceMove(loc) - loaded_pill_bottle = null - else if(href_list["change_pillbottle"]) - if(loaded_pill_bottle) - var/list/wrappers = list("Default wrapper", "Red wrapper", "Green wrapper", "Pale green wrapper", "Blue wrapper", "Light blue wrapper", "Teal wrapper", "Yellow wrapper", "Orange wrapper", "Pink wrapper", "Brown wrapper") - var/chosen = input(usr, "Select a pillbottle wrapper", "Pillbottle wrapper", wrappers[1]) as null|anything in wrappers - if(!chosen) + . = TRUE + switch(action) + if("toggle") + mode = !mode + if("ejectp") + if(loaded_pill_bottle) + loaded_pill_bottle.forceMove(loc) + loaded_pill_bottle = null + if("print") + if(printing || condi) return - var/color - switch(chosen) - if("Default wrapper") - loaded_pill_bottle.cut_overlays() - return - if("Red wrapper") - color = COLOR_RED - if("Green wrapper") - color = COLOR_GREEN - if("Pink wrapper") - color = COLOR_PINK - if("Teal wrapper") - color = COLOR_TEAL - if("Blue wrapper") - color = COLOR_BLUE - if("Brown wrapper") - color = COLOR_MAROON - if("Light blue wrapper") - color = COLOR_CYAN_BLUE - if("Yellow wrapper") - color = COLOR_YELLOW - if("Pale green wrapper") - color = COLOR_PALE_BTL_GREEN - if("Orange wrapper") - color = COLOR_ORANGE - loaded_pill_bottle.wrapper_color = color - loaded_pill_bottle.apply_wrap() - else if(href_list["close"]) - usr << browse(null, "window=chem_master") - onclose(usr, "chem_master") - usr.unset_machine() - return - if(href_list["print_p"]) - if(!printing) + var/idx = text2num(params["idx"]) || 0 + var/from_beaker = text2num(params["beaker"]) || FALSE + var/reagent_list = from_beaker ? beaker.reagents.reagent_list : reagents.reagent_list + if(idx < 1 || idx > length(reagent_list)) + return + + var/datum/reagent/R = reagent_list[idx] + printing = TRUE visible_message("[src] rattles and prints out a sheet of paper.") playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
    Chemical Analysis

    " + P.info = "
    Chemical Analysis

    " P.info += "Time of analysis: [station_time_timestamp()]

    " - P.info += "Chemical name: [href_list["name"]]
    " - if(href_list["name"] == "Blood") - var/datum/reagents/R = beaker.reagents - var/datum/reagent/blood/G - for(var/datum/reagent/F in R.reagent_list) - if(F.name == href_list["name"]) - G = F - break - var/B = G.data["blood_type"] - var/C = G.data["blood_DNA"] - P.info += "Description:
    Blood Type: [B]
    DNA: [C]" + P.info += "Chemical name: [R.name]
    " + if(istype(R, /datum/reagent/blood)) + var/datum/reagent/blood/B = R + P.info += "Description: N/A
    Blood Type: [B.data["blood_type"]]
    DNA: [B.data["blood_DNA"]]" else - P.info += "Description: [href_list["desc"]]" + P.info += "Description: [R.description]" P.info += "

    Notes:
    " - P.name = "Chemical Analysis - [href_list["name"]]" - printing = FALSE + P.name = "Chemical Analysis - [R.name]" + spawn(50) + printing = FALSE + else + . = FALSE - if(beaker) - var/datum/reagents/R = beaker.reagents - if(href_list["analyze"]) - var/dat = "" - if(!condi) - if(href_list["name"] == "Blood") - var/datum/reagent/blood/G - for(var/datum/reagent/F in R.reagent_list) - if(F.name == href_list["name"]) - G = F - break - var/A = G.name - var/B = G.data["blood_type"] - var/C = G.data["blood_DNA"] - dat += "Chemmaster 3000Chemical infos:

    Name:
    [A]

    Description:
    Blood Type: [B]
    DNA: [C]" - else - dat += "Chemmaster 3000Chemical infos:

    Name:
    [href_list["name"]]

    Description:
    [href_list["desc"]]" - dat += "

    (Print Analysis)
    " - dat += "(Back)" + if(. || !beaker) + return + + . = TRUE + var/datum/reagents/R = beaker.reagents + switch(action) + if("add") + var/id = params["id"] + var/amount = text2num(params["amount"]) + if(!id || !amount) + return + R.trans_id_to(src, id, amount) + if("remove") + var/id = params["id"] + var/amount = text2num(params["amount"]) + if(!id || !amount) + return + if(mode) + reagents.trans_id_to(beaker, id, amount) else - dat += "Condimaster 3000Condiment infos:

    Name:
    [href_list["name"]]

    Description:
    [href_list["desc"]]


    (Back)" - usr << browse(dat, "window=chem_master;size=575x500") - return - - else if(href_list["add"]) - - if(href_list["amount"]) - var/id = href_list["add"] - var/amount = text2num(href_list["amount"]) - R.trans_id_to(src, id, amount) - - else if(href_list["addcustom"]) - - var/id = href_list["addcustom"] - useramount = input("Select the amount to transfer.", 30, useramount) as num - useramount = isgoodnumber(useramount) - Topic(null, list("amount" = "[useramount]", "add" = "[id]")) - - else if(href_list["remove"]) - - if(href_list["amount"]) - var/id = href_list["remove"] - var/amount = text2num(href_list["amount"]) - if(mode) - reagents.trans_id_to(beaker, id, amount) - else - reagents.remove_reagent(id, amount) - - - else if(href_list["removecustom"]) - - var/id = href_list["removecustom"] - useramount = input("Select the amount to transfer.", 30, useramount) as num - useramount = isgoodnumber(useramount) - Topic(null, list("amount" = "[useramount]", "remove" = "[id]")) - - else if(href_list["toggle"]) - mode = !mode - - else if(href_list["main"]) - attack_hand(usr) - return - else if(href_list["eject"]) - if(beaker) - beaker.forceMove(get_turf(src)) - if(Adjacent(usr) && !issilicon(usr)) - usr.put_in_hands(beaker) - beaker = null - reagents.clear_reagents() - update_icon() - else if(href_list["createpill"] || href_list["createpill_multiple"]) - if(!condi) - var/count = 1 - if(href_list["createpill_multiple"]) - count = input("Select the number of pills to make.", 10, pillamount) as num|null - if(count == null) - return - count = isgoodnumber(count) - if(count > 20) - count = 20 //Pevent people from creating huge stacks of pills easily. Maybe move the number to defines? - if(count <= 0) - return - var/amount_per_pill = reagents.total_volume / count - if(amount_per_pill > 100) - amount_per_pill = 100 - var/name = clean_input("Name:","Name your pill!","[reagents.get_master_reagent_name()] ([amount_per_pill]u)") - if(!name) - return - name = reject_bad_text(name) - while(count--) - if(reagents.total_volume <= 0) - to_chat(usr, "Not enough reagents to create these pills!") - return - var/obj/item/reagent_containers/food/pill/P = new/obj/item/reagent_containers/food/pill(loc) - if(!name) - name = reagents.get_master_reagent_name() - P.name = "[name] pill" - P.pixel_x = rand(-7, 7) //random position - P.pixel_y = rand(-7, 7) - P.icon_state = "pill"+pillsprite - reagents.trans_to(P, amount_per_pill) - if(loaded_pill_bottle && loaded_pill_bottle.type == /obj/item/storage/pill_bottle) - if(loaded_pill_bottle.contents.len < loaded_pill_bottle.storage_slots) - P.forceMove(loaded_pill_bottle) - updateUsrDialog() - else - var/name = clean_input("Name:", "Name your bag!", reagents.get_master_reagent_name()) - if(!name) - return - name = reject_bad_text(name) - var/obj/item/reagent_containers/food/condiment/pack/P = new/obj/item/reagent_containers/food/condiment/pack(loc) - if(!name) name = reagents.get_master_reagent_name() - P.originalname = name - P.name = "[name] pack" - P.desc = "A small condiment pack. The label says it contains [name]." - reagents.trans_to(P, 10) - else if(href_list["createpatch"] || href_list["createpatch_multiple"]) - if(!condi) - var/count = 1 - if(href_list["createpatch_multiple"]) - count = input("Select the number of patches to make.", 10, patchamount) as num|null - if(count == null) - return - count = isgoodnumber(count) - if(!count || count <= 0) - return - if(count > 20) - count = 20 //Pevent people from creating huge stacks of patches easily. Maybe move the number to defines? - var/amount_per_patch = reagents.total_volume/count - if(amount_per_patch > 30) - amount_per_patch = 30 - var/name = clean_input("Name:", "Name your patch!", "[reagents.get_master_reagent_name()] ([amount_per_patch]u)") - if(!name) - return - name = reject_bad_text(name) - var/is_medical_patch = chemical_safety_check(reagents) - while(count--) - var/obj/item/reagent_containers/food/pill/patch/P = new/obj/item/reagent_containers/food/pill/patch(loc) - if(!name) name = reagents.get_master_reagent_name() - P.name = "[name] patch" - P.pixel_x = rand(-7, 7) //random position - P.pixel_y = rand(-7, 7) - reagents.trans_to(P,amount_per_patch) - if(is_medical_patch) - P.instant_application = TRUE - P.icon_state = "bandaid_med" - if(loaded_pill_bottle && loaded_pill_bottle.type == /obj/item/storage/pill_bottle/patch_pack) - if(loaded_pill_bottle.contents.len < loaded_pill_bottle.storage_slots) - P.forceMove(loaded_pill_bottle) - updateUsrDialog() - - else if(href_list["createbottle"]) - if(!condi) - var/name = clean_input("Name:", "Name your bottle!", reagents.get_master_reagent_name()) - if(!name) - return - name = reject_bad_text(name) - var/obj/item/reagent_containers/glass/bottle/reagent/P = new/obj/item/reagent_containers/glass/bottle/reagent(loc) - if(!name) - name = reagents.get_master_reagent_name() - P.name = "[name] bottle" - P.pixel_x = rand(-7, 7) //random position - P.pixel_y = rand(-7, 7) - P.icon_state = bottlesprite - reagents.trans_to(P, 50) - else - var/obj/item/reagent_containers/food/condiment/P = new/obj/item/reagent_containers/food/condiment(loc) - reagents.trans_to(P, 50) - else if(href_list["change_pill"]) - #define MAX_PILL_SPRITE 20 //max icon state of the pill sprites - var/dat = "" - var/j = 0 - for(var/i = 1 to MAX_PILL_SPRITE) - j++ - if(j == 1) - dat += "" - dat += "" - if(j == 5) - dat += "" - j = 0 - dat += "
    " - usr << browse(dat, "window=chem_master_iconsel;size=225x193") - return - else if(href_list["change_bottle"]) - var/dat = "" - var/j = 0 - for(var/i in list("bottle", "small_bottle", "wide_bottle", "round_bottle", "reagent_bottle")) - j++ - if(j == 1) - dat += "" - dat += "" - if(j == 5) - dat += "" - j = 0 - dat += "
    " - usr << browse(dat, "window=chem_master_iconsel;size=225x193") - return - else if(href_list["pill_sprite"]) - pillsprite = href_list["pill_sprite"] - usr << browse(null, "window=chem_master_iconsel") - else if(href_list["bottle_sprite"]) - bottlesprite = href_list["bottle_sprite"] - usr << browse(null, "window=chem_master_iconsel") - - SSnanoui.update_uis(src) + reagents.remove_reagent(id, amount) + if("eject") + if(!beaker) + return + beaker.forceMove(get_turf(src)) + if(Adjacent(usr) && !issilicon(usr)) + usr.put_in_hands(beaker) + beaker = null + reagents.clear_reagents() + update_icon() + if("create_condi_bottle") + if(!condi || !reagents.total_volume) + return + var/obj/item/reagent_containers/food/condiment/P = new(loc) + reagents.trans_to(P, 50) + else + return FALSE /obj/machinery/chem_master/attack_ai(mob/user) return attack_hand(user) /obj/machinery/chem_master/attack_ghost(mob/user) - if(user.can_admin_interact()) - return attack_hand(user) + tgui_interact(user) /obj/machinery/chem_master/attack_hand(mob/user) if(..()) return TRUE - ui_interact(user) + tgui_interact(user) -/obj/machinery/chem_master/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = TRUE) +/obj/machinery/chem_master/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) var/datum/asset/chem_master/assets = get_asset_datum(/datum/asset/chem_master) assets.send(user) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "chem_master.tmpl", name, 575, 500) + ui = new(user, src, ui_key, "ChemMaster", name, 575, 500) ui.open() -/obj/machinery/chem_master/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/chem_master/tgui_data(mob/user) var/data[0] data["condi"] = condi - data["loaded_pill_bottle"] = (loaded_pill_bottle ? 1 : 0) + data["loaded_pill_bottle"] = loaded_pill_bottle ? TRUE : FALSE if(loaded_pill_bottle) + data["loaded_pill_bottle_name"] = loaded_pill_bottle.name data["loaded_pill_bottle_contents_len"] = loaded_pill_bottle.contents.len data["loaded_pill_bottle_storage_slots"] = loaded_pill_bottle.storage_slots - data["beaker"] = (beaker ? 1 : 0) + data["beaker"] = beaker ? TRUE : FALSE if(beaker) var/list/beaker_reagents_list = list() data["beaker_reagents"] = beaker_reagents_list @@ -464,13 +276,255 @@ data["buffer_reagents"] = buffer_reagents_list for(var/datum/reagent/R in reagents.reagent_list) buffer_reagents_list[++buffer_reagents_list.len] = list("name" = R.name, "volume" = R.volume, "id" = R.id, "description" = R.description) + else + data["beaker_reagents"] = list() + data["buffer_reagents"] = list() data["pillsprite"] = pillsprite data["bottlesprite"] = bottlesprite data["mode"] = mode + data["printing"] = printing + + // Transfer modal information if there is one + data["modal"] = tgui_modal_data(src) return data +/** + * Called in tgui_act() to process modal actions + * + * Arguments: + * * action - The action passed by tgui + * * params - The params passed by tgui + */ +/obj/machinery/chem_master/proc/tgui_act_modal(action, params, datum/tgui/ui, datum/tgui_state/state) + . = TRUE + var/id = params["id"] // The modal's ID + var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"] + switch(tgui_modal_act(src, action, params)) + if(TGUI_MODAL_OPEN) + switch(id) + if("analyze") + var/idx = text2num(arguments["idx"]) || 0 + var/from_beaker = text2num(arguments["beaker"]) || FALSE + var/reagent_list = from_beaker ? beaker.reagents.reagent_list : reagents.reagent_list + if(idx < 1 || idx > length(reagent_list)) + return + + var/datum/reagent/R = reagent_list[idx] + var/list/result = list("idx" = idx, "name" = R.name, "desc" = R.description) + if(!condi && istype(R, /datum/reagent/blood)) + var/datum/reagent/blood/B = R + result["blood_type"] = B.data["blood_type"] + result["blood_dna"] = B.data["blood_DNA"] + + arguments["analysis"] = result + tgui_modal_message(src, id, "", null, arguments) + if("change_pill_bottle_style") + if(!loaded_pill_bottle) + return + if(!pill_bottle_wrappers) + pill_bottle_wrappers = list( + "CLEAR" = "Default", + COLOR_RED = "Red", + COLOR_GREEN = "Green", + COLOR_PALE_BTL_GREEN = "Pale green", + COLOR_BLUE = "Blue", + COLOR_CYAN_BLUE = "Light blue", + COLOR_TEAL = "Teal", + COLOR_YELLOW = "Yellow", + COLOR_ORANGE = "Orange", + COLOR_PINK = "Pink", + COLOR_MAROON = "Brown" + ) + var/current = pill_bottle_wrappers[loaded_pill_bottle.wrapper_color] || "Default" + tgui_modal_choice(src, id, "Please select a pill bottle wrapper:", null, arguments, current, pill_bottle_wrappers) + if("addcustom") + if(!beaker || !beaker.reagents.total_volume) + return + tgui_modal_input(src, id, "Please enter the amount to transfer to buffer:", null, arguments, useramount) + if("removecustom") + if(!reagents.total_volume) + return + tgui_modal_input(src, id, "Please enter the amount to transfer to [mode ? "beaker" : "disposal"]:", null, arguments, useramount) + if("create_condi_pack") + if(!condi || !reagents.total_volume) + return + tgui_modal_input(src, id, "Please name your new condiment pack:", null, arguments, reagents.get_master_reagent_name(), MAX_CUSTOM_NAME_LEN) + if("create_pill") + if(condi || !reagents.total_volume) + return + var/num = round(text2num(arguments["num"] || 1)) + if(!num) + return + arguments["num"] = num + var/amount_per_pill = clamp(reagents.total_volume / num, 0, MAX_UNITS_PER_PILL) + var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_pill]u)" + var/pills_text = num == 1 ? "new pill" : "[num] new pills" + tgui_modal_input(src, id, "Please name your [pills_text]:", null, arguments, default_name, MAX_CUSTOM_NAME_LEN) + if("create_pill_multiple") + if(condi || !reagents.total_volume) + return + tgui_modal_input(src, id, "Please enter the amount of pills to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5) + if("change_pill_style") + var/list/choices = list() + for(var/i = 1 to MAX_PILL_SPRITE) + choices += "pill[i].png" + tgui_modal_bento(src, id, "Please select the new style for pills:", null, arguments, pillsprite, choices) + if("create_patch") + if(condi || !reagents.total_volume) + return + var/num = round(text2num(arguments["num"] || 1)) + if(!num) + return + arguments["num"] = num + var/amount_per_patch = clamp(reagents.total_volume / num, 0, MAX_UNITS_PER_PATCH) + var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_patch]u)" + var/patches_text = num == 1 ? "new patch" : "[num] new patches" + tgui_modal_input(src, id, "Please name your [patches_text]:", null, arguments, default_name, MAX_CUSTOM_NAME_LEN) + if("create_patch_multiple") + if(condi || !reagents.total_volume) + return + tgui_modal_input(src, id, "Please enter the amount of patches to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5) + if("create_bottle") + if(condi || !reagents.total_volume) + return + tgui_modal_input(src, id, "Please name your bottle:", null, arguments, reagents.get_master_reagent_name(), MAX_CUSTOM_NAME_LEN) + if("change_bottle_style") + if(!bottle_styles) + bottle_styles = list("bottle", "small_bottle", "wide_bottle", "round_bottle", "reagent_bottle") + var/list/bottle_styles_png = list() + for(var/style in bottle_styles) + bottle_styles_png += "[style].png" + tgui_modal_bento(src, id, "Please select the new style for bottles:", null, arguments, bottlesprite, bottle_styles_png) + else + return FALSE + if(TGUI_MODAL_ANSWER) + var/answer = params["answer"] + switch(id) + if("change_pill_bottle_style") + if(!pill_bottle_wrappers || !loaded_pill_bottle) // wat? + return + var/color = "CLEAR" + for(var/col in pill_bottle_wrappers) + var/col_name = pill_bottle_wrappers[col] + if(col_name == answer) + color = col + break + if(length(color) && color != "CLEAR") + loaded_pill_bottle.wrapper_color = color + loaded_pill_bottle.apply_wrap() + else + loaded_pill_bottle.wrapper_color = null + loaded_pill_bottle.cut_overlays() + if("addcustom") + var/amount = isgoodnumber(text2num(answer)) + if(!amount || !arguments["id"]) + return + tgui_act("add", list("id" = arguments["id"], "amount" = amount), ui, state) + if("removecustom") + var/amount = isgoodnumber(text2num(answer)) + if(!amount || !arguments["id"]) + return + tgui_act("remove", list("id" = arguments["id"], "amount" = amount), ui, state) + if("create_condi_pack") + if(!condi || !reagents.total_volume) + return + if(!length(answer)) + answer = reagents.get_master_reagent_name() + var/obj/item/reagent_containers/food/condiment/pack/P = new(loc) + P.originalname = answer + P.name = "[answer] pack" + P.desc = "A small condiment pack. The label says it contains [answer]." + reagents.trans_to(P, 10) + if("create_pill") + if(condi || !reagents.total_volume) + return + var/count = clamp(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT) + if(!count) + return + + if(!length(answer)) + answer = reagents.get_master_reagent_name() + var/amount_per_pill = clamp(reagents.total_volume / count, 0, MAX_UNITS_PER_PILL) + while(count--) + if(reagents.total_volume <= 0) + to_chat(usr, "Not enough reagents to create these pills!") + return + + var/obj/item/reagent_containers/food/pill/P = new(loc) + P.name = "[answer] pill" + P.pixel_x = rand(-7, 7) // Random position + P.pixel_y = rand(-7, 7) + P.icon_state = "pill[pillsprite]" + reagents.trans_to(P, amount_per_pill) + // Load the pills in the bottle if there's one loaded + if(istype(loaded_pill_bottle) && length(loaded_pill_bottle.contents) < loaded_pill_bottle.storage_slots) + P.forceMove(loaded_pill_bottle) + if("create_pill_multiple") + if(condi || !reagents.total_volume) + return + tgui_act("modal_open", list("id" = "create_pill", "arguments" = list("num" = answer)), ui, state) + if("change_pill_style") + var/new_style = clamp(text2num(answer) || 0, 0, MAX_PILL_SPRITE) + if(!new_style) + return + pillsprite = new_style + if("create_patch") + if(condi || !reagents.total_volume) + return + var/count = clamp(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT) + if(!count) + return + + if(!length(answer)) + answer = reagents.get_master_reagent_name() + var/amount_per_patch = clamp(reagents.total_volume / count, 0, MAX_UNITS_PER_PATCH) + var/is_medical_patch = chemical_safety_check(reagents) + while(count--) + if(reagents.total_volume <= 0) + to_chat(usr, "Not enough reagents to create these patches!") + return + + var/obj/item/reagent_containers/food/pill/patch/P = new(loc) + P.name = "[answer] patch" + P.pixel_x = rand(-7, 7) // random position + P.pixel_y = rand(-7, 7) + reagents.trans_to(P, amount_per_patch) + if(is_medical_patch) + P.instant_application = TRUE + P.icon_state = "bandaid_med" + // Load the patches in the bottle if there's one loaded + if(istype(loaded_pill_bottle, /obj/item/storage/pill_bottle/patch_pack) && length(loaded_pill_bottle.contents) < loaded_pill_bottle.storage_slots) + P.forceMove(loaded_pill_bottle) + if("create_patch_multiple") + if(condi || !reagents.total_volume) + return + tgui_act("modal_open", list("id" = "create_patch", "arguments" = list("num" = answer)), ui, state) + if("create_bottle") + if(condi || !reagents.total_volume) + return + + if(!length(answer)) + answer = reagents.get_master_reagent_name() + var/obj/item/reagent_containers/glass/bottle/reagent/P = new(loc) + P.name = "[answer] bottle" + P.pixel_x = rand(-7, 7) // random position + P.pixel_y = rand(-7, 7) + P.icon_state = length(bottle_styles) && bottle_styles[bottlesprite] || "bottle" + reagents.trans_to(P, 50) + if("change_bottle_style") + if(!bottle_styles) + return + var/new_sprite = text2num(answer) || 1 + if(new_sprite < 1 || new_sprite > length(bottle_styles)) + return + bottlesprite = new_sprite + else + return FALSE + else + return FALSE + /obj/machinery/chem_master/proc/isgoodnumber(num) if(isnum(num)) if(num > 200) @@ -481,7 +535,7 @@ num = round(num) return num else - return 0 + return FALSE /obj/machinery/chem_master/proc/chemical_safety_check(datum/reagents/R) var/all_safe = TRUE @@ -503,3 +557,9 @@ component_parts += new /obj/item/reagent_containers/glass/beaker(null) component_parts += new /obj/item/reagent_containers/glass/beaker(null) RefreshParts() + +#undef MAX_PILL_SPRITE +#undef MAX_MULTI_AMOUNT +#undef MAX_UNITS_PER_PILL +#undef MAX_UNITS_PER_PATCH +#undef MAX_CUSTOM_NAME_LEN diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index 7f0ed2c91dc..c5ef03040b4 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -144,56 +144,69 @@ return STATUS_UPDATE_NONE /datum/reagent/proc/addiction_act_stage2(mob/living/M) - if(prob(8)) - M.emote("shiver") - if(prob(8)) - M.emote("sneeze") - if(prob(4)) - to_chat(M, "You feel a dull headache.") + if(minor_addiction) + if(prob(4)) + to_chat(M, "You briefly think about getting some more [name].") + else + if(prob(8)) + M.emote("shiver") + if(prob(8)) + M.emote("sneeze") + if(prob(4)) + to_chat(M, "You feel a dull headache.") return STATUS_UPDATE_NONE /datum/reagent/proc/addiction_act_stage3(mob/living/M) - if(prob(8)) - M.emote("twitch_s") - if(prob(8)) - M.emote("shiver") - if(prob(4)) - to_chat(M, "Your head hurts.") - if(prob(4)) - to_chat(M, "You begin craving [name]!") + if(minor_addiction) + if(prob(4)) + to_chat(M, "You could really go for some [name] right now.") + else + if(prob(8)) + M.emote("twitch_s") + if(prob(8)) + M.emote("shiver") + if(prob(4)) + to_chat(M, "Your head hurts.") + if(prob(4)) + to_chat(M, "You begin craving [name]!") return STATUS_UPDATE_NONE /datum/reagent/proc/addiction_act_stage4(mob/living/M) - if(prob(8)) - M.emote("twitch") - if(prob(4)) - to_chat(M, "You have a pounding headache.") - if(prob(4)) - to_chat(M, "You have the strong urge for some [name]!") - else if(prob(4)) - to_chat(M, "You REALLY crave some [name]!") + if(minor_addiction) + if(prob(8)) + to_chat(M, "You could really go for some [name] right now.") + else + if(prob(8)) + M.emote("twitch") + if(prob(4)) + to_chat(M, "You have a pounding headache.") + if(prob(4)) + to_chat(M, "You have the strong urge for some [name]!") + else if(prob(4)) + to_chat(M, "You REALLY crave some [name]!") return STATUS_UPDATE_NONE /datum/reagent/proc/addiction_act_stage5(mob/living/M) var/update_flags = STATUS_UPDATE_NONE if(minor_addiction) - if(prob(6)) - M.AdjustSlowed(3) - to_chat(M, "You feel [pick("tired", "exhausted", "sluggish")].") + if(prob(8)) + to_chat(M, "You can't stop thinking about [name]...") + if(prob(4)) + M.emote(pick("twitch")) else if(prob(6)) to_chat(M, "Your stomach lurches painfully!") M.visible_message("[M] gags and retches!") update_flags |= M.Stun(rand(2,4), FALSE) update_flags |= M.Weaken(rand(2,4), FALSE) - if(prob(8)) - M.emote(pick("twitch", "twitch_s", "shiver")) - if(prob(4)) - to_chat(M, "Your head is killing you!") - if(prob(5)) - to_chat(M, "You feel like you can't live without [name]!") - else if(prob(5)) - to_chat(M, "You would DIE for some [name] right now!") + if(prob(8)) + M.emote(pick("twitch", "twitch_s", "shiver")) + if(prob(4)) + to_chat(M, "Your head is killing you!") + if(prob(5)) + to_chat(M, "You feel like you can't live without [name]!") + else if(prob(5)) + to_chat(M, "You would DIE for some [name] right now!") return update_flags /datum/reagent/proc/fakedeath(mob/living/M) diff --git a/code/modules/reagents/chemistry/reagents/drinks.dm b/code/modules/reagents/chemistry/reagents/drinks.dm index 4bd061c14b4..53187fc8c35 100644 --- a/code/modules/reagents/chemistry/reagents/drinks.dm +++ b/code/modules/reagents/chemistry/reagents/drinks.dm @@ -517,3 +517,15 @@ description = "Beloved of children and teetotalers." color = "#E6CDFF" taste_description = "grape soda" + +/datum/reagent/consumable/drink/coco/icecoco + name = "Iced Cocoa" + id = "icecoco" + description = "Hot cocoa and ice, refreshing and cool." + color = "#102838" // rgb: 16, 40, 56 + adj_temp_hot = 0 + adj_temp_cool = 5 + drink_icon = "icedcoffeeglass" + drink_name = "Iced Cocoa" + drink_desc = "A sweeter drink to perk you up and refresh you!" + taste_description = "refreshingly cold cocoa" diff --git a/code/modules/reagents/chemistry/reagents/food.dm b/code/modules/reagents/chemistry/reagents/food.dm index 565a0d483a8..966e8ae93eb 100644 --- a/code/modules/reagents/chemistry/reagents/food.dm +++ b/code/modules/reagents/chemistry/reagents/food.dm @@ -628,7 +628,7 @@ reagent_state = LIQUID color = "#B4B400" metabolization_rate = 0.2 - nutriment_factor = 2 + nutriment_factor = 2.5 * REAGENTS_METABOLISM taste_description = "broth" /datum/reagent/consumable/cheese diff --git a/code/modules/reagents/chemistry/reagents/medicine.dm b/code/modules/reagents/chemistry/reagents/medicine.dm index e90789f9d8b..072a22aa7ba 100644 --- a/code/modules/reagents/chemistry/reagents/medicine.dm +++ b/code/modules/reagents/chemistry/reagents/medicine.dm @@ -121,7 +121,14 @@ /datum/reagent/medicine/cryoxadone/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE - if(M.bodytemperature < TCRYO) + var/external_temp + if(istype(M.loc, /obj/machinery/atmospherics/unary/cryo_cell)) + var/obj/machinery/atmospherics/unary/cryo_cell/C = M.loc + external_temp = C.temperature_archived + else + var/turf/T = get_turf(M) + external_temp = T.temperature + if(external_temp < TCRYO) update_flags |= M.adjustCloneLoss(-4, FALSE) update_flags |= M.adjustOxyLoss(-10, FALSE) update_flags |= M.adjustToxLoss(-3, FALSE) @@ -849,7 +856,7 @@ /datum/reagent/medicine/stimulants name = "Stimulants" id = "stimulants" - description = "Increases run speed and eliminates stuns, can heal minor damage. If overdosed it will deal toxin damage and stun." + description = "An illegal compound that dramatically enhances the body's performance and healing capabilities." color = "#C8A5DC" harmless = FALSE can_synth = FALSE @@ -886,7 +893,7 @@ /datum/reagent/medicine/stimulative_agent name = "Stimulative Agent" id = "stimulative_agent" - description = "An illegal compound that dramatically enhances the body's performance and healing capabilities." + description = "Increases run speed and eliminates stuns, can heal minor damage. If overdosed it will deal toxin damage and be less effective for healing stamina." color = "#C8A5DC" metabolization_rate = 0.5 * REAGENTS_METABOLISM overdose_threshold = 60 diff --git a/code/modules/reagents/chemistry/reagents/misc.dm b/code/modules/reagents/chemistry/reagents/misc.dm index f9a6f974dcf..2b65054f21f 100644 --- a/code/modules/reagents/chemistry/reagents/misc.dm +++ b/code/modules/reagents/chemistry/reagents/misc.dm @@ -603,7 +603,7 @@ name = "Left 4 Zed" id = "left4zednutriment" description = "Unstable nutriment that makes plants mutate more often than usual." - color = "#1A1E4D" // RBG: 26, 30, 77 + color = "#2A1680" // RBG: 42, 128, 22 tox_prob = 25 taste_description = "evolution" diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm index c6ff39aaead..7156eff6482 100644 --- a/code/modules/reagents/chemistry/reagents/toxins.dm +++ b/code/modules/reagents/chemistry/reagents/toxins.dm @@ -917,6 +917,7 @@ reagent_state = LIQUID color = "#191919" metabolization_rate = 0.1 + can_synth = FALSE penetrates_skin = TRUE taste_mult = 0 @@ -1054,7 +1055,7 @@ id = "atrazine" description = "A herbicidal compound used for destroying unwanted plants." reagent_state = LIQUID - color = "#17002D" + color = "#773E73" //RGB: 47 24 45 lethality = 2 //Atrazine, however, is definitely toxic @@ -1237,6 +1238,7 @@ description = "An advanced corruptive toxin produced by something terrible." reagent_state = LIQUID color = "#5EFF3B" //RGB: 94, 255, 59 + can_synth = FALSE taste_description = "decay" /datum/reagent/gluttonytoxin/reaction_mob(mob/living/L, method=REAGENT_TOUCH, reac_volume) diff --git a/code/modules/reagents/chemistry/recipes/drinks.dm b/code/modules/reagents/chemistry/recipes/drinks.dm index 1f15c95bbf2..ab326d9d9ca 100644 --- a/code/modules/reagents/chemistry/recipes/drinks.dm +++ b/code/modules/reagents/chemistry/recipes/drinks.dm @@ -868,3 +868,11 @@ required_reagents = list("hooch" = 1, "absinthe" = 1, "manlydorf" = 1, "syndicatebomb" = 1) result_amount = 4 mix_message = "The mixture turns to a sickening froth." + +/datum/chemical_reaction/icecoco + name = "Iced Cocoa" + id = "icecoco" + result = "icecoco" + required_reagents = list("ice" = 1, "hot_coco" = 3) + result_amount = 4 + mix_sound = 'sound/goonstation/misc/drinkfizz.ogg' diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm index f2740676314..4be84e21d2c 100644 --- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm +++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm @@ -56,7 +56,6 @@ /datum/chemical_reaction/nitroglycerin name = "Nitroglycerin" id = "nitroglycerin" - result = "nitroglycerin" required_reagents = list("glycerol" = 1, "facid" = 1, "sacid" = 1) result_amount = 2 @@ -164,8 +163,7 @@ /datum/chemical_reaction/blackpowder_explosion/on_reaction(datum/reagents/holder, created_volume) var/location = get_turf(holder.my_atom) do_sparks(2, 1, location) - spawn(rand(5, 15)) - blackpowder_detonate(holder, created_volume) + addtimer(CALLBACK(null, .proc/blackpowder_detonate, holder, created_volume), rand(5, 15)) /proc/blackpowder_detonate(datum/reagents/holder, created_volume) var/turf/T = get_turf(holder.my_atom) @@ -176,8 +174,7 @@ explosion(T, ex_severe, ex_heavy,ex_light, ex_flash, 1) // If this black powder is in a decal, remove the decal, because it just exploded if(istype(holder.my_atom, /obj/effect/decal/cleanable/dirt/blackpowder)) - spawn(0) - qdel(holder.my_atom) + qdel(holder.my_atom) /datum/chemical_reaction/flash_powder name = "Flash powder" diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm index b1fb33edf85..5da09309057 100644 --- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm +++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm @@ -38,7 +38,7 @@ /datum/chemical_reaction/slimemonkey/on_reaction(datum/reagents/holder) feedback_add_details("slime_cores_used","[type]") for(var/i = 1, i <= 3, i++) - var /obj/item/reagent_containers/food/snacks/monkeycube/M = new /obj/item/reagent_containers/food/snacks/monkeycube + var/obj/item/reagent_containers/food/snacks/monkeycube/M = new /obj/item/reagent_containers/food/snacks/monkeycube M.forceMove(get_turf(holder.my_atom)) //Green @@ -146,7 +146,8 @@ /obj/item/reagent_containers/food/snacks/meat/slab, /obj/item/reagent_containers/food/snacks/grown, /obj/item/reagent_containers/food/snacks/grown/mushroom, - /obj/item/reagent_containers/food/snacks/deepfryholder + /obj/item/reagent_containers/food/snacks/deepfryholder, + /obj/item/reagent_containers/food/snacks/monstermeat ) blocked |= typesof(/obj/item/reagent_containers/food/snacks/customizable) diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 01a5e6823a5..f5bc4a3274a 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -39,7 +39,7 @@ reagents.add_reagent("blood", disease_amount, data) add_initial_reagents() -obj/item/reagent_containers/proc/add_initial_reagents() +/obj/item/reagent_containers/proc/add_initial_reagents() if(list_reagents) reagents.add_reagent_list(list_reagents) @@ -50,16 +50,25 @@ obj/item/reagent_containers/proc/add_initial_reagents() if(!QDELETED(src)) ..() + +/obj/item/reagent_containers/proc/add_lid() + if(has_lid) + container_type ^= REFILLABLE | DRAINABLE + update_icon() + +/obj/item/reagent_containers/proc/remove_lid() + if(has_lid) + container_type |= REFILLABLE | DRAINABLE + update_icon() + /obj/item/reagent_containers/attack_self(mob/user) if(has_lid) if(is_open_container()) to_chat(usr, "You put the lid on [src].") - container_type ^= REFILLABLE | DRAINABLE + add_lid() else to_chat(usr, "You take the lid off [src].") - container_type |= REFILLABLE | DRAINABLE - update_icon() - return + remove_lid() /obj/item/reagent_containers/attack(mob/M, mob/user, def_zone) if(user.a_intent == INTENT_HARM) diff --git a/code/modules/reagents/reagent_containers/applicator.dm b/code/modules/reagents/reagent_containers/applicator.dm index 73c3c3759b1..87be22d3c8f 100644 --- a/code/modules/reagents/reagent_containers/applicator.dm +++ b/code/modules/reagents/reagent_containers/applicator.dm @@ -5,6 +5,7 @@ icon_state = "mender" item_state = "mender" volume = 200 + possible_transfer_amounts = null resistance_flags = ACID_PROOF container_type = REFILLABLE | AMOUNT_VISIBLE temperature_min = 270 @@ -21,6 +22,9 @@ ignore_flags = TRUE to_chat(user, "You short out the safeties on [src].") +/obj/item/reagent_containers/applicator/set_APTFT() + set hidden = TRUE + /obj/item/reagent_containers/applicator/on_reagent_change() if(!emagged) var/found_forbidden_reagent = FALSE @@ -93,6 +97,20 @@ playsound(get_turf(src), pick('sound/goonstation/items/mender.ogg', 'sound/goonstation/items/mender2.ogg'), 50, 1) +/obj/item/reagent_containers/applicator/verb/empty() + set name = "Empty Applicator" + set category = "Object" + set src in usr + + if(usr.incapacitated()) + return + if(alert(usr, "Are you sure you want to empty [src]?", "Empty Applicator:", "Yes", "No") != "Yes") + return + if(!usr.incapacitated() && isturf(usr.loc) && loc == usr) + to_chat(usr, "You empty [src] onto the floor.") + reagents.reaction(usr.loc) + reagents.clear_reagents() + /obj/item/reagent_containers/applicator/brute name = "brute auto-mender" list_reagents = list("styptic_powder" = 200) @@ -104,3 +122,6 @@ /obj/item/reagent_containers/applicator/dual name = "dual auto-mender" list_reagents = list("synthflesh" = 200) + +/obj/item/reagent_containers/applicator/dual/syndi // It magically goes through hardsuits. Don't ask how. + ignore_flags = TRUE diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm index 6c836988e8b..4274c3114db 100644 --- a/code/modules/reagents/reagent_containers/bottle.dm +++ b/code/modules/reagents/reagent_containers/bottle.dm @@ -42,6 +42,13 @@ var/image/lid = image(icon, src, "lid_bottle") overlays += lid +/obj/item/reagent_containers/glass/bottle/decompile_act(obj/item/matter_decompiler/C, mob/user) + if(!reagents.total_volume) + C.stored_comms["glass"] += 3 + qdel(src) + return TRUE + return ..() + /obj/item/reagent_containers/glass/bottle/toxin name = "toxin bottle" desc = "A small bottle containing toxic compounds." diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm index 1e42899111b..48534c7ab73 100644 --- a/code/modules/reagents/reagent_containers/glass_containers.dm +++ b/code/modules/reagents/reagent_containers/glass_containers.dm @@ -47,6 +47,9 @@ reagents.reaction(M, REAGENT_TOUCH) reagents.clear_reagents() else + if(!iscarbon(M)) // Non-carbons can't process reagents + to_chat(user, "You cannot find a way to feed [M].") + return if(M != user) M.visible_message("[user] attempts to feed something to [M].", \ "[user] attempts to feed something to you.") @@ -176,6 +179,7 @@ to_chat(usr, "You detach [assembly] from [src]") usr.put_in_hands(assembly) assembly = null + qdel(GetComponent(/datum/component/proximity_monitor)) update_icon() else to_chat(usr, "There is no assembly to remove.") @@ -191,7 +195,9 @@ return ..() assembly = W user.drop_item() - W.loc = src + W.forceMove(src) + if(assembly.has_prox_sensors()) + AddComponent(/datum/component/proximity_monitor) overlays += "assembly" else ..() diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index f6980c03620..831a3ddc44f 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -23,13 +23,12 @@ /obj/item/reagent_containers/food/pill/attack(mob/living/carbon/M, mob/user, def_zone) if(!istype(M)) - return 0 + return FALSE bitesize = reagents.total_volume if(M.eat(src, user)) - spawn(0) - qdel(src) - return 1 - return 0 + qdel(src) + return TRUE + return FALSE /obj/item/reagent_containers/food/pill/afterattack(obj/target, mob/user, proximity) if(!proximity) diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 8826efa5eb8..5e20dbf6f35 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -44,7 +44,7 @@ return var/contents_log = reagents.reagent_list.Join(", ") - spray(A) + INVOKE_ASYNC(src, .proc/spray, A) playsound(loc, 'sound/effects/spray2.ogg', 50, 1, -6) user.changeNext_move(CLICK_CD_RANGE*2) @@ -57,24 +57,24 @@ var/attack_log_type = ATKLOG_MOST if(reagents.has_reagent("sacid") || reagents.has_reagent("facid") || reagents.has_reagent("lube")) attack_log_type = ATKLOG_FEW - msg_admin_attack("[key_name_admin(user)] used a spray bottle at [COORD(user)] - Contents: [contents_log] - Temperature: [reagents.chem_temp]K", attack_log_type) - log_game("[key_name(user)] used a spray bottle at [COORD(user)] - Contents: [contents_log] - Temperature: [reagents.chem_temp]K") + + add_attack_logs(user, A, "Used a potentially harmful spray bottle. Contents: [contents_log] - Temperature: [reagents.chem_temp]K", attack_log_type) return -/obj/item/reagent_containers/spray/proc/spray(var/atom/A) +/obj/item/reagent_containers/spray/proc/spray(atom/A) var/obj/effect/decal/chempuff/D = new /obj/effect/decal/chempuff(get_turf(src)) D.create_reagents(amount_per_transfer_from_this) reagents.trans_to(D, amount_per_transfer_from_this, 1/spray_currentrange) D.icon += mix_color_from_reagents(D.reagents.reagent_list) - spawn(0) - for(var/i=0, i
    " + scrambled its destination's coordinates, and launched it. You awoke from stasis when you landed and have been surviving - barely - ever since." outfit.uniform = /obj/item/clothing/under/color/orange outfit.shoes = /obj/item/clothing/shoes/orange outfit.back = /obj/item/storage/backpack if(3) flavour_text += "you were a doctor on one of Nanotrasen's space stations, but you left behind that damn corporation's tyranny and everything it stood for. From a metaphorical hell \ - to a literal one, you find yourself nonetheless missing the recycled air and warm floors of what you left behind... but you'd still rather be here than there." + to a literal one, you find yourself nonetheless missing the recycled air and warm floors of what you left behind... but you'd still rather be here than there." outfit.uniform = /obj/item/clothing/under/rank/medical outfit.suit = /obj/item/clothing/suit/storage/labcoat outfit.back = /obj/item/storage/backpack/medic outfit.shoes = /obj/item/clothing/shoes/black if(4) - flavour_text += "you were always joked about by your friends for \"not playing with a full deck\", as they so kindly put it. It seems that they were right when you, on a tour \ + flavour_text += "you were always joked about by your friends for \"not playing with a full deck\", as they so kindly put it. It seems that they were right when you, on a tour \ at one of Nanotrasen's state-of-the-art research facilities, were in one of the escape pods alone and saw the red button. It was big and shiny, and it caught your eye. You pressed \ - it, and after a terrifying and fast ride for days, you landed here. You've had time to wisen up since then, and you think that your old friends wouldn't be laughing now." + it, and after a terrifying and fast ride for days, you landed here. You've had time to wisen up since then, and you think that your old friends wouldn't be laughing now." outfit.uniform = /obj/item/clothing/under/color/grey/glorf outfit.shoes = /obj/item/clothing/shoes/black outfit.back = /obj/item/storage/backpack diff --git a/code/modules/ruins/lavalandruin_code/seed_vault.dm b/code/modules/ruins/lavalandruin_code/seed_vault.dm index 2c999a7ce29..1a5e863c52f 100644 --- a/code/modules/ruins/lavalandruin_code/seed_vault.dm +++ b/code/modules/ruins/lavalandruin_code/seed_vault.dm @@ -18,9 +18,10 @@ roundstart = FALSE death = FALSE mob_species = /datum/species/diona/pod - flavour_text = "You are a sentient ecosystem, an example of the mastery over life that your creators possessed. Your masters, benevolent as they were, created uncounted \ + description = "You are a diona on Lavaland with access to a full botany setup. Perfect to mess around with plants in peace." + flavour_text = "You are a sentient ecosystem, an example of the mastery over life that your creators possessed. Your masters, benevolent as they were, created uncounted \ seed vaults and spread them across the universe to every planet they could chart. You are in one such seed vault. Your goal is to cultivate and spread life wherever it will go while waiting \ - for contact from your creators. Estimated time of last contact: Deployment, 5x10^3 millennia ago." + for contact from your creators. Estimated time of last contact: Deployment, 5x10^3 millennia ago." assignedrole = "Lifebringer" /obj/effect/mob_spawn/human/seed_vault/special(mob/living/new_spawn) diff --git a/code/modules/ruins/lavalandruin_code/syndicate_base.dm b/code/modules/ruins/lavalandruin_code/syndicate_base.dm index d053e139f61..1e745e8d3ed 100644 --- a/code/modules/ruins/lavalandruin_code/syndicate_base.dm +++ b/code/modules/ruins/lavalandruin_code/syndicate_base.dm @@ -28,8 +28,10 @@ death = FALSE icon = 'icons/obj/cryogenic2.dmi' icon_state = "sleeper_s" - flavour_text = "You are a syndicate agent, employed in a top secret research facility developing biological weapons. Unfortunately, your hated enemy, Nanotrasen, has begun mining in this sector. Continue your research as best you can, and try to keep a low profile. The base is rigged with explosives, do not abandon it or let it fall into enemy hands! \ -
    You are free to attack anyone not aligned with the Syndicate in the vicinity of your base. DO NOT work against Syndicate personnel (such as traitors or nuclear operatives). You may work with or against non-Syndicate antagonists on a case-by-case basis. DO NOT leave your base without admin permission." + important_info = "Do not work against Syndicate personnel (such as traitors or nuclear operatives). You may work with or against non-Syndicate antagonists on a case-by-case basis. Do not leave your base without admin permission." + description = "Experiment with deadly chems and viruses in peace or help any visiting Syndicate Agent." + flavour_text = "You are a syndicate agent, employed in a top secret research facility developing biological weapons. Unfortunately, your hated enemy, Nanotrasen, has begun mining in this sector. Continue your research as best you can, and try to keep a low profile. The base is rigged with explosives, do not abandon it or let it fall into enemy hands!\ + It's been made clear to you that the Syndicate will make you regret it if you disappoint them." outfit = /datum/outfit/lavaland_syndicate assignedrole = "Lavaland Syndicate" del_types = list() // Necessary to prevent del_types from removing radio! @@ -59,13 +61,13 @@ /obj/effect/mob_spawn/human/lavaland_syndicate/comms name = "Syndicate Comms Agent sleeper" mob_name = "Syndicate Comms Agent" - flavour_text = "You are a syndicate agent, employed in a top secret research facility developing biological weapons. Unfortunately, your hated enemy, Nanotrasen, has begun mining in this sector. Monitor enemy activity as best you can, and try to keep a low profile. Do not abandon the base. Use the communication equipment to provide support to any field agents, and sow disinformation to throw Nanotrasen off your trail. Do not let the base fall into enemy hands! \ -
    You are free to attack anyone not aligned with the Syndicate in the vicinity of your base. DO NOT work against Syndicate personnel (such as traitors or nuclear operatives). You may work with or against non-Syndicate antagonists on a case-by-case basis. DO NOT leave your base without admin permission." + important_info = "Do not work against Syndicate personnel (such as traitors or nuclear operatives). You may work with or against non-Syndicate antagonists on a case-by-case basis. Do not leave your base without admin permission. Do not reveal the existence of yourself to NT." + description = "Monitor comms and cameras and try to assist any agents on station while keeping your existence a secret." + flavour_text = "You are a syndicate agent, employed in a top secret research facility developing biological weapons. Unfortunately, your hated enemy, Nanotrasen, has begun mining in this sector. Monitor enemy activity as best you can, and try to keep a low profile." outfit = /datum/outfit/lavaland_syndicate/comms /obj/effect/mob_spawn/human/lavaland_syndicate/comms/space - flavour_text = "You are a syndicate agent, assigned to a small listening post station situated near your hated enemy's top secret research facility: Space Station 13. Monitor enemy activity as best you can, and try to keep a low profile. Do not abandon the base. Use the communication equipment to provide support to any field agents, and sow disinformation to throw Nanotrasen off your trail. Do not let the base fall into enemy hands! \ -
    You are free to attack anyone not aligned with the Syndicate in the vicinity of your base. DO NOT work against Syndicate personnel (such as traitors or nuclear operatives). You may work with or against non-Syndicate antagonists on a case-by-case basis. DO NOT leave your base without admin permission." + flavour_text = "You are a syndicate agent, employed in a small listening outpost. You'd be bored to death if you couldn't listen in on those NT idiots mess up all the time." /obj/effect/mob_spawn/human/lavaland_syndicate/comms/space/Initialize(mapload) . = ..() diff --git a/code/modules/security_levels/keycard authentication.dm b/code/modules/security_levels/keycard authentication.dm index 87723701281..e51355e89bb 100644 --- a/code/modules/security_levels/keycard authentication.dm +++ b/code/modules/security_levels/keycard authentication.dm @@ -4,17 +4,17 @@ icon = 'icons/obj/monitors.dmi' icon_state = "auth_off" - var/active = 0 //This gets set to 1 on all devices except the one where the initial request was made. - var/event = "" - var/screen = 1 + var/active = FALSE // This gets set to TRUE on all devices except the one where the initial request was made. + var/event + var/swiping = FALSE // on swiping screen? var/list/ert_chosen = list() - var/confirmed = 0 //This variable is set by the device that confirms the request. - var/confirm_delay = 20 //(2 seconds) - var/busy = 0 //Busy when waiting for authentication or an event request has been sent from this device. + var/confirmed = FALSE // This variable is set by the device that confirms the request. + var/confirm_delay = 5 SECONDS // time allowed for a second person to confirm a swipe. + var/busy = FALSE // Busy when waiting for authentication or an event request has been sent from this device. var/obj/machinery/keycard_auth/event_source var/mob/event_triggered_by var/mob/event_confirmed_by - var/ert_reason = "Reason for ERT" + var/ert_reason anchored = 1 use_power = IDLE_POWER_USE @@ -35,16 +35,19 @@ return if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) if(check_access(W)) - if(active == 1) + if(active) //This is not the device that made the initial request. It is the device confirming the request. if(event_source) - event_source.confirmed = 1 + event_source.confirmed = TRUE event_source.event_confirmed_by = usr - else if(screen == 2) - if(event == "Emergency Response Team" && ert_reason == "Reason for ERT") - to_chat(user, "Supply a reason for calling the ERT first!") + SStgui.update_uis(event_source) + SStgui.update_uis(src) + else if(swiping) + if(event == "Emergency Response Team" && !ert_reason) + to_chat(user, "Supply a reason for calling the ERT first!") return event_triggered_by = usr + SStgui.update_uis(src) broadcast_request() //This is the device making the initial event request. It needs to broadcast to other devices else to_chat(user, "Access denied.") @@ -59,57 +62,58 @@ stat |= NOPOWER /obj/machinery/keycard_auth/attack_ghost(mob/user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/keycard_auth/attack_hand(mob/user as mob) +/obj/machinery/keycard_auth/attack_hand(mob/user) if(..()) - return 1 - ui_interact(user) + return TRUE + tgui_interact(user) -/obj/machinery/keycard_auth/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(busy) - to_chat(user, "This device is busy.") - return - - user.set_machine(src) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/keycard_auth/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "keycard_auth.tmpl", "Keycard Authentication Device UI", 540, 320) + ui = new(user, src, ui_key, "KeycardAuth", name, 540, 300, master_ui, state) ui.open() -/obj/machinery/keycard_auth/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["screen"] = screen - data["event"] = event - data["ertreason"] = ert_reason + +/obj/machinery/keycard_auth/tgui_data() + var/list/data = list() + data["redAvailable"] = GLOB.security_level == SEC_LEVEL_RED ? FALSE : TRUE + data["swiping"] = swiping + data["busy"] = busy + data["event"] = active && event_source && event_source.event ? event_source.event : event + data["ertreason"] = active && event_source && event_source.ert_reason ? event_source.ert_reason : ert_reason + data["isRemote"] = active ? TRUE : FALSE + data["hasSwiped"] = event_triggered_by ? TRUE : FALSE + data["hasConfirm"] = event_confirmed_by || (active && event_source && event_source.event_confirmed_by) ? TRUE : FALSE return data -/obj/machinery/keycard_auth/Topic(href, href_list) +/obj/machinery/keycard_auth/tgui_act(action, params) if(..()) - return 1 - - if(busy) - to_chat(usr, "This device is busy.") return + if(busy) + to_chat(usr, "This device is busy.") + return + if(!allowed(usr)) + to_chat(usr, "Access denied.") + return + . = TRUE + switch(action) + if("ert") + ert_reason = stripped_input(usr, "Reason for ERT Call:", "", "") + if("reset") + reset() + if("triggerevent") + event = params["triggerevent"] + swiping = TRUE - if(href_list["triggerevent"]) - event = href_list["triggerevent"] - screen = 2 - if(href_list["reset"]) - reset() - if(href_list["ert"]) - ert_reason = stripped_input(usr, "Reason for ERT Call:", "", "") - - SSnanoui.update_uis(src) add_fingerprint(usr) - return /obj/machinery/keycard_auth/proc/reset() - active = 0 - event = "" - screen = 1 - confirmed = 0 + active = FALSE + event = null + swiping = FALSE + confirmed = FALSE event_source = null icon_state = "auth_off" event_triggered_by = null @@ -125,7 +129,7 @@ sleep(confirm_delay) if(confirmed) - confirmed = 0 + confirmed = FALSE trigger_event(event) log_game("[key_name(event_triggered_by)] triggered and [key_name(event_confirmed_by)] confirmed event [event]") message_admins("[key_name_admin(event_triggered_by)] triggered and [key_name_admin(event_confirmed_by)] confirmed event [event]", 1) @@ -135,16 +139,17 @@ if(stat & (BROKEN|NOPOWER)) return event_source = source - busy = 1 - active = 1 + busy = TRUE + active = TRUE + SStgui.update_uis(src) icon_state = "auth_on" sleep(confirm_delay) event_source = null icon_state = "auth_off" - active = 0 - busy = 0 + active = FALSE + busy = FALSE /obj/machinery/keycard_auth/proc/trigger_event() switch(event) @@ -178,7 +183,7 @@ if(fullmin_count) GLOB.ert_request_answered = TRUE ERT_Announce(ert_reason , event_triggered_by, 0) - ert_reason = "Reason for ERT" + ert_reason = null feedback_inc("alert_keycard_auth_ert",1) spawn(3000) if(!GLOB.ert_request_answered) diff --git a/code/modules/security_levels/security levels.dm b/code/modules/security_levels/security levels.dm index 00c575e53b0..71fd1750902 100644 --- a/code/modules/security_levels/security levels.dm +++ b/code/modules/security_levels/security levels.dm @@ -123,10 +123,6 @@ GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/secur FA.overlays.Cut() FA.overlays += image('icons/obj/monitors.dmi', "overlay_delta") - if(level >= SEC_LEVEL_RED) - GLOB.atc.reroute_traffic(yes = TRUE) // Tell them fuck off we're busy. - else - GLOB.atc.reroute_traffic(yes = FALSE) SSnightshift.check_nightshift(TRUE) else diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index 99e31ef25ed..3fb173e7ac2 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -150,11 +150,6 @@ emergency_shuttle_called.Announce("The emergency shuttle has been called. [redAlert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason][SSshuttle.emergencyLastCallLoc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ]") - if(reason == "Automatic Crew Transfer" && signalOrigin == null) // Best way we have to check that it's actually a crew transfer and not just a player using the same message- any other calls to this proc should have a signalOrigin. - GLOB.atc.shift_ending() - else // Emergency shuttle call (probably) - GLOB.atc.reroute_traffic(yes = TRUE) - /obj/docking_port/mobile/emergency/cancel(area/signalOrigin) if(!canRecall) @@ -306,6 +301,7 @@ spawn(0) D.open() */ //Leaving this here incase someone decides to port -tg-'s escape shuttle stuff: + // This basically opens a big-ass row of blast doors when the shuttle arrives at centcom /obj/docking_port/mobile/pod name = "escape pod" diff --git a/code/modules/shuttle/ert.dm b/code/modules/shuttle/ert.dm index 356ca9687dc..ddb7bb343b6 100644 --- a/code/modules/shuttle/ert.dm +++ b/code/modules/shuttle/ert.dm @@ -6,13 +6,12 @@ resistance_flags = INDESTRUCTIBLE flags = NODECONSTRUCT -/obj/machinery/computer/shuttle/ert/Topic(href, href_list) - if(href_list["move"]) +/obj/machinery/computer/shuttle/ert/can_call_shuttle(mob/user, action) + if(action == "move") var/authorized_roles = list(SPECIAL_ROLE_ERT, SPECIAL_ROLE_DEATHSQUAD) - if(!((usr.mind.assigned_role in authorized_roles) || is_admin(usr))) - message_admins("Potential ERT shuttle hijack, ERT shuttle moved by unauthorized user: [key_name_admin(usr)]") - ..() - + if(!((user.mind?.assigned_role in authorized_roles) || is_admin(user))) + message_admins("Potential ERT shuttle hijack, ERT shuttle moved by unauthorized user: [key_name_admin(user)]") + return TRUE /obj/machinery/computer/camera_advanced/shuttle_docker/ert name = "specops navigation computer" diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index cfd3ab8e6f2..86cfe2a7444 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -727,13 +727,13 @@ name = "Shuttle Console" icon_screen = "shuttle" icon_keyboard = "tech_key" - req_access = list( ) + req_access = list() circuit = /obj/item/circuitboard/shuttle var/shuttleId var/possible_destinations = "" var/admin_controlled var/max_connect_range = 7 - var/docking_request = 0 + var/moved = FALSE //workaround for nukie shuttle, hope I find a better way to do this... /obj/machinery/computer/shuttle/New(location, obj/item/circuitboard/shuttle/C) ..() @@ -773,21 +773,20 @@ return connect() add_fingerprint(user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/shuttle/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/shuttle/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "shuttle_console.tmpl", M ? M.name : "shuttle", 300, 200) + ui = new(user, src, ui_key, "ShuttleConsole", name, 350, 150, master_ui, state) ui.open() -/obj/machinery/computer/shuttle/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] +/obj/machinery/computer/shuttle/tgui_data(mob/user) + var/list/data = list() var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) data["status"] = M ? M.getStatusText() : null if(M) - data["shuttle"] = 1 + data["shuttle"] = TRUE //this should just be boolean, right? var/list/docking_ports = list() data["docking_ports"] = docking_ports var/list/options = params2list(possible_destinations) @@ -799,34 +798,35 @@ docking_ports[++docking_ports.len] = list("name" = S.name, "id" = S.id) data["docking_ports_len"] = docking_ports.len data["admin_controlled"] = admin_controlled - data["docking_request"] = docking_request - return data -/obj/machinery/computer/shuttle/Topic(href, href_list) - if(..()) - return 1 - +/obj/machinery/computer/shuttle/tgui_act(action, params) + if(..()) //we can't actually interact, so no action + return TRUE if(!allowed(usr)) to_chat(usr, "Access denied.") - return - + return TRUE + if(!can_call_shuttle(usr, action)) + return TRUE var/list/options = params2list(possible_destinations) - if(href_list["move"]) - if(!options.Find(href_list["move"])) //I see you're trying Href exploits, I see you're failing, I SEE ADMIN WARNING. - // Seriously, though, NEVER trust a Topic with something like this. Ever. - // Sidenote for whoever did this last. Why did you set it to echo whatever the user entered to admin chat? You solved one exploit and created another. + if(action == "move") + var/destination = params["move"] + if(!options.Find(destination))//figure out if this translation works message_admins("EXPLOIT: [ADMIN_LOOKUPFLW(usr)] attempted to move [src] to an invalid location! [ADMIN_COORDJMP(src)]") return - switch(SSshuttle.moveShuttle(shuttleId, href_list["move"], 1, usr)) + switch(SSshuttle.moveShuttle(shuttleId, destination, TRUE, usr)) if(0) atom_say("Shuttle departing! Please stand away from the doors.") usr.create_log(MISC_LOG, "used [src] to call the [shuttleId] shuttle") + if(!moved) + moved = TRUE + add_fingerprint(usr) + return TRUE if(1) to_chat(usr, "Invalid shuttle requested.") else to_chat(usr, "Unable to comply.") - return 1 + /obj/machinery/computer/shuttle/emag_act(mob/user) if(!emagged) @@ -834,6 +834,10 @@ emagged = 1 to_chat(user, "You fried the consoles ID checking system.") +//for restricting when the computer can be used, needed for some console subtypes. +/obj/machinery/computer/shuttle/proc/can_call_shuttle(mob/user, action) + return TRUE + /obj/machinery/computer/shuttle/ferry name = "transport ferry console" circuit = /obj/item/circuitboard/ferry @@ -844,25 +848,23 @@ /obj/machinery/computer/shuttle/ferry/request name = "ferry console" circuit = /obj/item/circuitboard/ferry/request - var/cooldown //prevents spamming admins + var/next_request //to prevent spamming admins possible_destinations = "ferry_home" - admin_controlled = 1 + admin_controlled = TRUE resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF -/obj/machinery/computer/shuttle/ferry/request/Topic(href, href_list) - if(..()) - return 1 - if(href_list["request"]) - if(cooldown) +/obj/machinery/computer/shuttle/ferry/request/tgui_act(action, params) + if(..()) // Note that the parent handels normal shuttle movement on top of security checks + return + if(action == "request") + if(world.time < next_request) return - cooldown = 1 + next_request = world.time + 60 SECONDS //1 minute cooldown to_chat(usr, "Your request has been recieved by Centcom.") log_admin("[key_name(usr)] requested to move the transport ferry to Centcom.") message_admins("FERRY: [key_name_admin(usr)] (Move Ferry) is requesting to move the transport ferry to Centcom.") - . = 1 - SSnanoui.update_uis(src) - spawn(600) //One minute cooldown - cooldown = 0 + return TRUE + /obj/machinery/computer/shuttle/white_ship name = "White Ship Console" diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm index b1eeb07a6de..5d8e9a6f914 100644 --- a/code/modules/shuttle/supply.dm +++ b/code/modules/shuttle/supply.dm @@ -1,8 +1,3 @@ -#define ORDER_SCREEN_WIDTH 625 //width of order computer interaction window -#define ORDER_SCREEN_HEIGHT 580 //height of order computer interaction window -#define SUPPLY_SCREEN_WIDTH 625 //width of supply computer interaction window -#define SUPPLY_SCREEN_HEIGHT 620 //height of supply computer interaction window - /obj/item/paper/manifest name = "supply manifest" var/erroneous = 0 @@ -98,7 +93,7 @@ var/intel_count = 0 var/crate_count = 0 - var/msg = "" + var/msg = "
    ---[station_time_timestamp()]---

    " var/pointsEarned for(var/atom/movable/MA in areaInstance) @@ -231,7 +226,7 @@ msg += "+[pointsEarned]: Received [crate_count] crate(s).
    " SSshuttle.points += pointsEarned - SSshuttle.centcom_message = msg + SSshuttle.centcom_message += "[msg]
    " /proc/forbidden_atoms_check(atom/A) var/list/blacklist = list( @@ -387,156 +382,23 @@ icon_screen = "supply" req_access = list(ACCESS_CARGO) circuit = /obj/item/circuitboard/supplycomp - var/temp = null + /// Is this a public console (Confirm + Shuttle controls are not visible) + var/is_public = FALSE + /// Time of last request var/reqtime = 0 - var/hacked = 0 - var/can_order_contraband = 0 - var/last_viewed_group = "categories" - var/datum/supply_packs/content_pack + /// Can we order special supplies + var/hacked = FALSE + /// Can we order contraband + var/can_order_contraband = FALSE -/obj/machinery/computer/ordercomp +/obj/machinery/computer/supplycomp/public name = "Supply Ordering Console" desc = "Used to order supplies from cargo staff." icon = 'icons/obj/computer.dmi' icon_screen = "request" circuit = /obj/item/circuitboard/ordercomp - var/reqtime = 0 - var/last_viewed_group = "categories" - var/datum/supply_packs/content_pack - -/obj/machinery/computer/ordercomp/attack_ai(var/mob/user as mob) - return attack_hand(user) - -/obj/machinery/computer/ordercomp/attack_hand(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/computer/ordercomp/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui) - if(!ui) - ui = new(user, src, ui_key, "order_console.tmpl", name, ORDER_SCREEN_WIDTH, ORDER_SCREEN_HEIGHT) - ui.open() - -/obj/machinery/computer/ordercomp/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["last_viewed_group"] = last_viewed_group - - var/category_list[0] - for(var/category in GLOB.all_supply_groups) - category_list.Add(list(list("name" = get_supply_group_name(category), "category" = category))) - data["categories"] = category_list - var/cat = text2num(last_viewed_group) - var/packs_list[0] - for(var/set_name in SSshuttle.supply_packs) - var/datum/supply_packs/pack = SSshuttle.supply_packs[set_name] - if((!pack.contraband && !pack.hidden && !pack.special && pack.group == cat) || (!pack.contraband && !pack.hidden && (pack.special && pack.special_enabled) && pack.group == cat)) - // 0/1 after the pack name (set_name) is a boolean for ordering multiple crates - packs_list.Add(list(list("name" = pack.name, "amount" = pack.amount, "cost" = pack.cost, "command1" = list("doorder" = "[set_name]0"), "command2" = list("doorder" = "[set_name]1"), "command3" = list("contents" = set_name)))) - - data["supply_packs"] = packs_list - if(content_pack) - var/pack_name = sanitize(content_pack.name) - data["contents_name"] = pack_name - data["contents"] = content_pack.manifest - data["contents_access"] = content_pack.access ? get_access_desc(content_pack.access) : "None" - - var/requests_list[0] - for(var/set_name in SSshuttle.requestlist) - var/datum/supply_order/SO = set_name - if(SO) - // Check if the user owns the request, so they can cancel requests - var/obj/item/card/id/I = user.get_id_card() - var/owned = 0 - if(I && SO.orderedby == I.registered_name) - owned = 1 - requests_list.Add(list(list("ordernum" = SO.ordernum, "supply_type" = SO.object.name, "orderedby" = SO.orderedby, "owned" = owned, "command1" = list("rreq" = SO.ordernum)))) - data["requests"] = requests_list - var/orders_list[0] - for(var/set_name in SSshuttle.shoppinglist) - var/datum/supply_order/SO = set_name - if(SO) - orders_list.Add(list(list("ordernum" = SO.ordernum, "supply_type" = SO.object.name, "orderedby" = SO.orderedby))) - data["orders"] = orders_list - - data["points"] = round(SSshuttle.points) - data["send"] = list("send" = 1) - - data["moving"] = SSshuttle.supply.mode != SHUTTLE_IDLE - data["at_station"] = SSshuttle.supply.getDockedId() == "supply_home" - data["timeleft"] = SSshuttle.supply.timeLeft(600) - return data - -/obj/machinery/computer/ordercomp/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["doorder"]) - if(world.time < reqtime) - visible_message("[src]'s monitor flashes, \"[world.time - reqtime] seconds remaining until another requisition form may be printed.\"") - SSnanoui.update_uis(src) - return 1 - - var/index = copytext(href_list["doorder"], 1, length(href_list["doorder"])) //text2num(copytext(href_list["doorder"], 1)) - var/multi = text2num(copytext(href_list["doorder"], -1)) - if(!isnum(multi)) - return 1 - var/datum/supply_packs/P = SSshuttle.supply_packs[index] - if(!istype(P)) - return 1 - var/crates = 1 - if(multi) - var/num_input = input(usr, "Amount:", "How many crates?") as null|num - if(!num_input || ..()) - return 1 - crates = clamp(round(num_input), 1, 20) - - var/timeout = world.time + 600 - var/reason = input(usr,"Reason:","Why do you require this item?","") as null|text - if(world.time > timeout || !reason || ..()) - return 1 - reason = sanitize(copytext(reason, 1, MAX_MESSAGE_LEN)) - - var/idname = "*None Provided*" - var/idrank = "*None Provided*" - if(ishuman(usr)) - var/mob/living/carbon/human/H = usr - idname = H.get_authentification_name() - idrank = H.get_assignment() - else if(issilicon(usr)) - idname = usr.real_name - - reqtime = (world.time + 5) % 1e5 - - //make our supply_order datums - for(var/i = 1; i <= crates; i++) - var/datum/supply_order/O = SSshuttle.generateSupplyOrder(index, idname, idrank, reason, crates) - if(!O) return - if(i == 1) - O.generateRequisition(loc) - - else if(href_list["rreq"]) - var/ordernum = text2num(href_list["rreq"]) - var/obj/item/card/id/I = usr.get_id_card() - for(var/i=1, i<=SSshuttle.requestlist.len, i++) - var/datum/supply_order/SO = SSshuttle.requestlist[i] - if(SO.ordernum == ordernum && (I && SO.orderedby == I.registered_name)) - SSshuttle.requestlist.Cut(i,i+1) - break - - else if(href_list["last_viewed_group"]) - content_pack = null - last_viewed_group = text2num(href_list["last_viewed_group"]) - - else if(href_list["contents"]) - var/topic = href_list["contents"] - if(topic == 1) - content_pack = null - else - var/datum/supply_packs/P = SSshuttle.supply_packs[topic] - content_pack = P - - add_fingerprint(usr) - SSnanoui.update_uis(src) - return 1 + req_access = list() + is_public = TRUE /obj/machinery/computer/supplycomp/attack_ai(var/mob/user as mob) return attack_hand(user) @@ -547,47 +409,25 @@ return 1 post_signal("supply") - ui_interact(user) + tgui_interact(user) return /obj/machinery/computer/supplycomp/emag_act(user as mob) if(!hacked) to_chat(user, "Special supplies unlocked.") - hacked = 1 + hacked = TRUE return -/obj/machinery/computer/supplycomp/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui) +/obj/machinery/computer/supplycomp/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "supply_console.tmpl", name, SUPPLY_SCREEN_WIDTH, SUPPLY_SCREEN_HEIGHT) + ui = new(user, src, ui_key, "CargoConsole", name, 900, 800, master_ui, state) ui.open() -/obj/machinery/computer/supplycomp/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) - var/data[0] - data["last_viewed_group"] = last_viewed_group +/obj/machinery/computer/supplycomp/tgui_data(mob/user) + var/list/data = list() - var/category_list[0] - for(var/category in GLOB.all_supply_groups) - category_list.Add(list(list("name" = get_supply_group_name(category), "category" = category))) - data["categories"] = category_list - - var/cat = text2num(last_viewed_group) - var/packs_list[0] - for(var/set_name in SSshuttle.supply_packs) - var/datum/supply_packs/pack = SSshuttle.supply_packs[set_name] - if((pack.hidden && hacked) || (pack.contraband && can_order_contraband) || (pack.special && pack.special_enabled) || (!pack.contraband && !pack.hidden && !pack.special)) - if(pack.group == cat) - // 0/1 after the pack name (set_name) is a boolean for ordering multiple crates - packs_list.Add(list(list("name" = pack.name, "amount" = pack.amount, "cost" = pack.cost, "command1" = list("doorder" = "[set_name]0"), "command2" = list("doorder" = "[set_name]1"), "command3" = list("contents" = set_name)))) - - data["supply_packs"] = packs_list - if(content_pack) - var/pack_name = sanitize(content_pack.name) - data["contents_name"] = pack_name - data["contents"] = content_pack.manifest - data["contents_access"] = content_pack.access ? get_access_desc(content_pack.access) : "None" - - var/requests_list[0] + var/list/requests_list = list() for(var/set_name in SSshuttle.requestlist) var/datum/supply_order/SO = set_name if(SO) @@ -596,146 +436,177 @@ requests_list.Add(list(list("ordernum" = SO.ordernum, "supply_type" = SO.object.name, "orderedby" = SO.orderedby, "comment" = SO.comment, "command1" = list("confirmorder" = SO.ordernum), "command2" = list("rreq" = SO.ordernum)))) data["requests"] = requests_list - var/orders_list[0] + var/list/orders_list = list() for(var/set_name in SSshuttle.shoppinglist) var/datum/supply_order/SO = set_name if(SO) orders_list.Add(list(list("ordernum" = SO.ordernum, "supply_type" = SO.object.name, "orderedby" = SO.orderedby, "comment" = SO.comment))) data["orders"] = orders_list - data["canapprove"] = (SSshuttle.supply.getDockedId() == "supply_away") && !(SSshuttle.supply.mode != SHUTTLE_IDLE) + data["is_public"] = is_public + + data["canapprove"] = (SSshuttle.supply.getDockedId() == "supply_away") && !(SSshuttle.supply.mode != SHUTTLE_IDLE) && !is_public data["points"] = round(SSshuttle.points) - data["send"] = list("send" = 1) - data["message"] = SSshuttle.centcom_message ? SSshuttle.centcom_message : "Remember to stamp and send back the supply manifests." data["moving"] = SSshuttle.supply.mode != SHUTTLE_IDLE data["at_station"] = SSshuttle.supply.getDockedId() == "supply_home" data["timeleft"] = SSshuttle.supply.timeLeft(600) data["can_launch"] = !SSshuttle.supply.canMove() + + return data + +/obj/machinery/computer/supplycomp/tgui_static_data(mob/user) + var/list/data = list() + var/list/packs_list = list() + + for(var/set_name in SSshuttle.supply_packs) + var/datum/supply_packs/pack = SSshuttle.supply_packs[set_name] + if((pack.hidden && hacked) || (pack.contraband && can_order_contraband) || (pack.special && pack.special_enabled) || (!pack.contraband && !pack.hidden && !pack.special)) + packs_list.Add(list(list("name" = pack.name, "cost" = pack.cost, "ref" = "[pack.UID()]", "contents" = pack.tgui_manifest, "cat" = pack.group))) + + data["supply_packs"] = packs_list + + var/list/categories = list() // meow + for(var/category in GLOB.all_supply_groups) + categories.Add(list(list("name" = get_supply_group_name(category), "category" = category))) + data["categories"] = categories + return data /obj/machinery/computer/supplycomp/proc/is_authorized(mob/user) if(allowed(user)) - return 1 + return TRUE if(user.can_admin_interact()) - return 1 + return TRUE - return 0 + return FALSE -/obj/machinery/computer/supplycomp/Topic(href, href_list) +/obj/machinery/computer/supplycomp/tgui_act(action, list/params) if(..()) - return 1 + return - if(!is_authorized(usr)) - return 1 + // If its not a public console, and they aint authed, dont let them use this + if(!is_public && !is_authorized(usr)) + return if(!SSshuttle) - log_runtime(EXCEPTION("The SSshuttle controller datum is missing somehow."), src) - return 1 - - if(href_list["send"]) - if(SSshuttle.supply.canMove()) - to_chat(usr, "For safety reasons the automated supply shuttle cannot transport live organisms, classified nuclear weaponry or homing beacons.") - else if(SSshuttle.supply.getDockedId() == "supply_home") - SSshuttle.toggleShuttle("supply", "supply_home", "supply_away", 1) - investigate_log("[key_name(usr)] has sent the supply shuttle away. Remaining points: [SSshuttle.points]. Shuttle contents: [SSshuttle.sold_atoms]", "cargo") - else if(!SSshuttle.supply.request(SSshuttle.getDock("supply_home"))) - post_signal("supply") - if(LAZYLEN(SSshuttle.shoppinglist) && prob(10)) - var/datum/supply_order/O = new /datum/supply_order() - O.ordernum = SSshuttle.ordernum - O.object = SSshuttle.supply_packs[pick(SSshuttle.supply_packs)] - O.orderedby = random_name(pick(MALE,FEMALE), species = "Human") - SSshuttle.shoppinglist += O - investigate_log("Random [O.object] crate added to supply shuttle") - - else if(href_list["doorder"]) - if(world.time < reqtime) - visible_message("[src]'s monitor flashes, \"[world.time - reqtime] seconds remaining until another requisition form may be printed.\"") - SSnanoui.update_uis(src) - return 1 - - var/index = copytext(href_list["doorder"], 1, length(href_list["doorder"])) //text2num(copytext(href_list["doorder"], 1)) - var/multi = text2num(copytext(href_list["doorder"], -1)) - if(!isnum(multi)) - return 1 - var/datum/supply_packs/P = SSshuttle.supply_packs[index] - if(!istype(P)) - return 1 - var/crates = 1 - if(multi) - var/num_input = input(usr, "Amount:", "How many crates?") as null|num - if(!num_input || !is_authorized(usr) || ..()) - return 1 - crates = clamp(round(num_input), 1, 20) - - var/timeout = world.time + 600 - var/reason = input(usr,"Reason:","Why do you require this item?","") as null|text - if(world.time > timeout || !reason || !is_authorized(usr) || ..()) - return 1 - reason = sanitize(copytext(reason, 1, MAX_MESSAGE_LEN)) - - var/idname = "*None Provided*" - var/idrank = "*None Provided*" - - if(ishuman(usr)) - var/mob/living/carbon/human/H = usr - idname = H.get_authentification_name() - idrank = H.get_assignment() - else if(issilicon(usr)) - idname = usr.real_name - - //make our supply_order datums - for(var/i = 1; i <= crates; i++) - var/datum/supply_order/O = SSshuttle.generateSupplyOrder(index, idname, idrank, reason, crates) - if(!O) return 1 - if(i == 1) - O.generateRequisition(loc) - - else if(href_list["confirmorder"]) - if(SSshuttle.supply.getDockedId() != "supply_away" || SSshuttle.supply.mode != SHUTTLE_IDLE) - return 1 - var/ordernum = text2num(href_list["confirmorder"]) - var/datum/supply_order/O - var/datum/supply_packs/P - for(var/i=1, i<=SSshuttle.requestlist.len, i++) - var/datum/supply_order/SO = SSshuttle.requestlist[i] - if(SO.ordernum == ordernum) - O = SO - P = O.object - if(SSshuttle.points >= P.cost) - SSshuttle.requestlist.Cut(i,i+1) - SSshuttle.points -= P.cost - SSshuttle.shoppinglist += O - investigate_log("[key_name(usr)] has authorized an order for [P.name]. Remaining points: [SSshuttle.points].", "cargo") - else - to_chat(usr, "There are insufficient supply points for this request.") - break - - else if(href_list["rreq"]) - var/ordernum = text2num(href_list["rreq"]) - for(var/i=1, i<=SSshuttle.requestlist.len, i++) - var/datum/supply_order/SO = SSshuttle.requestlist[i] - if(SO.ordernum == ordernum) - SSshuttle.requestlist.Cut(i,i+1) - break - - else if(href_list["last_viewed_group"]) - content_pack = null - last_viewed_group = text2num(href_list["last_viewed_group"]) - - else if(href_list["contents"]) - var/topic = href_list["contents"] - if(topic == 1) - content_pack = null - else - var/datum/supply_packs/P = SSshuttle.supply_packs[topic] - content_pack = P + stack_trace("The SSshuttle controller datum is missing somehow.") + return + . = TRUE add_fingerprint(usr) - SSnanoui.update_uis(src) - return 1 + + switch(action) + if("moveShuttle") + // Public consoles cant move the shuttle. Dont allow exploiters. + if(is_public) + return + if(SSshuttle.supply.canMove()) + to_chat(usr, "For safety reasons the automated supply shuttle cannot transport live organisms, classified nuclear weaponry or homing beacons.") + else if(SSshuttle.supply.getDockedId() == "supply_home") + SSshuttle.toggleShuttle("supply", "supply_home", "supply_away", 1) + investigate_log("[key_name(usr)] has sent the supply shuttle away. Remaining points: [SSshuttle.points]. Shuttle contents: [SSshuttle.sold_atoms]", "cargo") + else if(!SSshuttle.supply.request(SSshuttle.getDock("supply_home"))) + post_signal("supply") + if(LAZYLEN(SSshuttle.shoppinglist) && prob(10)) + var/datum/supply_order/O = new /datum/supply_order() + O.ordernum = SSshuttle.ordernum + O.object = SSshuttle.supply_packs[pick(SSshuttle.supply_packs)] + O.orderedby = random_name(pick(MALE,FEMALE), species = "Human") + SSshuttle.shoppinglist += O + investigate_log("Random [O.object] crate added to supply shuttle") + + if("order") + if(world.time < reqtime) + visible_message("[src]'s monitor flashes, \"[world.time - reqtime] seconds remaining until another requisition form may be printed.\"") + return + + var/amount = 1 + if(params["multiple"] == "1") // 1 is a string here. DO NOT MAKE THIS A BOOLEAN YOU DORK + var/num_input = input(usr, "Amount", "How many crates? (20 Max)") as null|num + if(!num_input || (!is_public && !is_authorized(usr)) || ..()) // Make sure they dont walk away + return + amount = clamp(round(num_input), 1, 20) + + + var/datum/supply_packs/P = locateUID(params["crate"]) + if(!istype(P)) + return + + var/timeout = world.time + 600 // If you dont type the reason within a minute, theres bigger problems here + var/reason = input(usr, "Reason", "Why do you require this item?","") as null|text + if(world.time > timeout || !reason || (!is_public && !is_authorized(usr)) || ..()) + // Cancel if they take too long, they dont give a reason, they aint authed, or if they walked away + return + reason = sanitize(copytext(reason, 1, MAX_MESSAGE_LEN)) + + var/idname = "*None Provided*" + var/idrank = "*None Provided*" + + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + idname = H.get_authentification_name() + idrank = H.get_assignment() + else if(issilicon(usr)) + idname = usr.real_name + + //make our supply_order datums + for(var/i = 1; i <= amount; i++) + var/datum/supply_order/O = SSshuttle.generateSupplyOrder(params["crate"], idname, idrank, reason, amount) + if(!O) + return + if(i == 1) + O.generateRequisition(loc) + + if("approve") + // Public consoles cant approve stuff + if(is_public) + return + if(SSshuttle.supply.getDockedId() != "supply_away" || SSshuttle.supply.mode != SHUTTLE_IDLE) + return + + var/ordernum = text2num(params["ordernum"]) + var/datum/supply_order/O + var/datum/supply_packs/P + for(var/i=1, i<=SSshuttle.requestlist.len, i++) + var/datum/supply_order/SO = SSshuttle.requestlist[i] + if(SO.ordernum == ordernum) + O = SO + P = O.object + if(SSshuttle.points >= P.cost) + SSshuttle.requestlist.Cut(i,i+1) + SSshuttle.points -= P.cost + SSshuttle.shoppinglist += O + investigate_log("[key_name(usr)] has authorized an order for [P.name]. Remaining points: [SSshuttle.points].", "cargo") + else + to_chat(usr, "There are insufficient supply points for this request.") + break + + if("deny") + var/ordernum = text2num(params["ordernum"]) + for(var/i=1, i<=SSshuttle.requestlist.len, i++) + var/datum/supply_order/SO = SSshuttle.requestlist[i] + if(SO.ordernum == ordernum) + // If we are on a public console, only allow cancelling of our own orders + if(is_public) + var/obj/item/card/id/I = usr.get_id_card() + if(I && SO.orderedby == I.registered_name) + SSshuttle.requestlist.Cut(i,i+1) + break + // If we arent public, were cargo access. CANCELLATIONS FOR EVERYONE + else + SSshuttle.requestlist.Cut(i,i+1) + break + + // Popup to show CC message logs. Its easier this way to avoid box-spam in TGUI + if("showMessages") + // Public consoles cant view messages + if(is_public) + return + var/datum/browser/ccmsg_browser = new(usr, "ccmsg", "Central Command Cargo Message Log", 800, 600) + ccmsg_browser.set_content(SSshuttle.centcom_message) + ccmsg_browser.open() /obj/machinery/computer/supplycomp/proc/post_signal(var/command) var/datum/radio_frequency/frequency = SSradio.return_frequency(DISPLAY_FREQ) @@ -748,9 +619,3 @@ status_signal.data["command"] = command frequency.post_signal(src, status_signal) - - -#undef ORDER_SCREEN_WIDTH -#undef ORDER_SCREEN_HEIGHT -#undef SUPPLY_SCREEN_WIDTH -#undef SUPPLY_SCREEN_HEIGHT diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/syndicate.dm index f9ec738b167..1ff2959ff08 100644 --- a/code/modules/shuttle/syndicate.dm +++ b/code/modules/shuttle/syndicate.dm @@ -11,20 +11,18 @@ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF flags = NODECONSTRUCT var/challenge = FALSE - var/moved = FALSE /obj/machinery/computer/shuttle/syndicate/recall name = "syndicate shuttle recall terminal" circuit = /obj/item/circuitboard/shuttle/syndicate/recall possible_destinations = "syndicate_away" -/obj/machinery/computer/shuttle/syndicate/Topic(href, href_list) - if(href_list["move"]) +/obj/machinery/computer/shuttle/syndicate/can_call_shuttle(user, action) + if(action == "move") if(challenge && world.time < SYNDICATE_CHALLENGE_TIMER) - to_chat(usr, "You've issued a combat challenge to the station! You've got to give them at least [round(((SYNDICATE_CHALLENGE_TIMER - world.time) / 10) / 60)] more minutes to allow them to prepare.") - return 0 - moved = TRUE - ..() + to_chat(user, "You've issued a combat challenge to the station! You've got to give them at least [round(((SYNDICATE_CHALLENGE_TIMER - world.time) / 10) / 60)] more minutes to allow them to prepare.") + return FALSE + return TRUE /obj/machinery/computer/shuttle/syndicate/drop_pod name = "syndicate assault pod control" @@ -35,11 +33,11 @@ shuttleId = "steel_rain" possible_destinations = null -/obj/machinery/computer/shuttle/syndicate/drop_pod/Topic(href, href_list) - if(href_list["move"]) +/obj/machinery/computer/shuttle/syndicate/drop_pod/can_call_shuttle(user, action) + if(action == "move") if(z != level_name_to_num(CENTCOMM)) - to_chat(usr, "Pods are one way!") - return 0 + to_chat(user, "Pods are one way!") + return FALSE ..() /obj/machinery/computer/shuttle/sst diff --git a/code/modules/space_management/level_traits.dm b/code/modules/space_management/level_traits.dm index 1d88f70454f..6aa3fa802a7 100644 --- a/code/modules/space_management/level_traits.dm +++ b/code/modules/space_management/level_traits.dm @@ -61,3 +61,16 @@ GLOBAL_LIST_INIT(default_map_traits, MAP_TRANSITION_CONFIG) /proc/level_name_to_num(name) var/datum/space_level/S = GLOB.space_manager.get_zlev_by_name(name) return S.zpos + +/** + * Proc to get a list of all the linked-together Z-Levels + * + * Returns a list of zlevel numbers which can be accessed from travelling space naturally + */ +/proc/get_all_linked_levels_zpos() + var/list/znums = list() + for(var/i in GLOB.space_manager.z_list) + var/datum/space_level/SL = GLOB.space_manager.z_list[i] + if(SL.linkage == CROSSLINKED) + znums |= SL.zpos + return znums diff --git a/code/modules/spacepods/construction.dm b/code/modules/spacepods/construction.dm index be2998fc0a3..d24758c3b19 100644 --- a/code/modules/spacepods/construction.dm +++ b/code/modules/spacepods/construction.dm @@ -222,7 +222,7 @@ // EOF ) - spawn_result(mob/user as mob) - ..() - feedback_inc("spacepod_created",1) - return +/datum/construction/reversible2/pod/spawn_result(mob/user as mob) + ..() + feedback_inc("spacepod_created",1) + return diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index 56e7f8f96ba..6c8da04608d 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -204,6 +204,7 @@ deal_damage(30) /obj/spacepod/attack_animal(mob/living/simple_animal/user) + user.changeNext_move(CLICK_CD_MELEE) if((user.a_intent == INTENT_HELP && user.ckey) || user.melee_damage_upper == 0) user.custom_emote(1, "[user.friendly] [src].") return FALSE @@ -296,7 +297,7 @@ return var/sound/S = sound(mysound) S.wait = 0 //No queue - S.channel = open_sound_channel() + S.channel = SSsounds.random_available_channel() S.volume = 50 for(var/mob/M in passengers | pilot) M << S diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm index 426af9a97ac..4e9b5e57630 100644 --- a/code/modules/station_goals/bsa.dm +++ b/code/modules/station_goals/bsa.dm @@ -91,7 +91,7 @@ /obj/machinery/bsa/middle/proc/check_completion() if(!front || !back) - return "No linked parts detected!" + return "No multitool-linked parts detected!" if(!front.anchored || !back.anchored || !anchored) return "Linked parts unwrenched!" if(front.y != y || back.y != y || !(front.x > x && back.x < x || front.x < x && back.x > x) || front.z != z || back.z != z) @@ -305,51 +305,45 @@ /obj/machinery/computer/bsa_control/attack_hand(mob/user) if(..()) return 1 - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/bsa_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) +/obj/machinery/computer/bsa_control/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "bsa.tmpl", name, 400, 305) + ui = new(user, src, ui_key, "BlueSpaceArtilleryControl", name, 400, 155, master_ui, state) ui.open() - ui.set_auto_update(1) -/obj/machinery/computer/bsa_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) +/obj/machinery/computer/bsa_control/tgui_data(mob/user) var/list/data = list() data["connected"] = cannon data["notice"] = notice if(target) data["target"] = get_target_name() - if(cannon) var/reload_cooldown = cannon.reload_cooldown var/last_fire_time = cannon.last_fire_time var/time_to_wait = max(0, round(reload_cooldown - ((world.time / 10) - last_fire_time))) var/minutes = max(0, round(time_to_wait / 60)) var/seconds = max(0, time_to_wait - (60 * minutes)) - - data["reloadtime_mins"] = minutes - data["reloadtime_secs"] = (seconds < 10) ? "0[seconds]" : seconds + var/seconds2 = (seconds < 10) ? "0[seconds]" : seconds + data["reloadtime_text"] = "[minutes]:[seconds2]" data["ready"] = minutes == 0 && seconds == 0 else data["ready"] = FALSE - return data -/obj/machinery/computer/bsa_control/Topic(href, href_list) +/obj/machinery/computer/bsa_control/tgui_act(action, params) if(..()) - return 1 - - if(href_list["build"]) - cannon = deploy() - . = TRUE - else if(href_list["fire"]) - fire(usr) - . = TRUE - else if(href_list["recalibrate"]) - calibrate(usr) - . = TRUE + return + switch(action) + if("build") + cannon = deploy() + if("fire") + fire(usr) + if("recalibrate") + calibrate(usr) update_icon() + return TRUE /obj/machinery/computer/bsa_control/proc/calibrate(mob/user) var/list/gps_locators = list() diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm index db801fa5d0f..033cd9fdf94 100644 --- a/code/modules/station_goals/dna_vault.dm +++ b/code/modules/station_goals/dna_vault.dm @@ -7,7 +7,7 @@ #define VAULT_NOBREATH "Lung Enhancement" #define VAULT_FIREPROOF "Thermal Regulation" #define VAULT_STUNTIME "Neural Repathing" -#define VAULT_ARMOUR "Bone Reinforcement" +#define VAULT_ARMOUR "Hardened Skin" #define VAULT_SPEED "Leg Muscle Stimulus" #define VAULT_QUICK "Arm Muscle Stimulus" diff --git a/code/modules/station_goals/shield.dm b/code/modules/station_goals/shield.dm index 42bd3b2a10a..4bf18263310 100644 --- a/code/modules/station_goals/shield.dm +++ b/code/modules/station_goals/shield.dm @@ -178,7 +178,7 @@ for(var/datum/event_container/container in SSevents.event_containers) for(var/datum/event_meta/M in container.available_events) if(M.event_type == /datum/event/meteor_wave) - M.weight *= mod + M.weight_mod *= mod /obj/machinery/satellite/meteor_shield/Destroy() . = ..() diff --git a/code/modules/station_goals/station_goal.dm b/code/modules/station_goals/station_goal.dm index 47f65ea2553..7f13651168b 100644 --- a/code/modules/station_goals/station_goal.dm +++ b/code/modules/station_goals/station_goal.dm @@ -13,7 +13,7 @@ /datum/station_goal/proc/send_report() GLOB.priority_announcement.Announce("Priority Nanotrasen directive received. Project \"[name]\" details inbound.", "Incoming Priority Message", 'sound/AI/commandreport.ogg') - print_command_report(get_report(), "Nanotrasen Directive [pick(GLOB.phonetic_alphabet)] \Roman[rand(1,50)]") + print_command_report(get_report(), "Nanotrasen Directive [pick(GLOB.phonetic_alphabet)] \Roman[rand(1,50)]", FALSE) on_report() /datum/station_goal/proc/on_report() diff --git a/code/modules/store/items.dm b/code/modules/store/items.dm index 4c1610474ba..0a0f19fb0f7 100644 --- a/code/modules/store/items.dm +++ b/code/modules/store/items.dm @@ -111,6 +111,12 @@ typepath = /obj/item/instrument/eguitar cost = 500 +/datum/storeitem/banjo + name = "Banjo" + desc = "It's pretty much just a drum with a neck and strings." + typepath = /obj/item/instrument/banjo + cost = 500 + /datum/storeitem/piano_synth name = "Piano Synthesizer" desc = "An advanced electronic synthesizer that can emulate various instruments." diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm index 19ff7fa37d8..8e007f60f89 100644 --- a/code/modules/surgery/organs/augments_internal.dm +++ b/code/modules/surgery/organs/augments_internal.dm @@ -1,5 +1,3 @@ -#define STUN_SET_AMOUNT 2 - /obj/item/organ/internal/cyberimp name = "cybernetic implant" desc = "a state-of-the-art implant that improves a baseline's functionality" @@ -128,17 +126,27 @@ implant_color = "#FFFF00" slot = "brain_antistun" origin_tech = "materials=5;programming=4;biotech=5" + var/stun_max_amount = 2 + +/obj/item/organ/internal/cyberimp/brain/anti_stun/hardened + name = "Hardened CNS Rebooter implant" + emp_proof = TRUE + +/obj/item/organ/internal/cyberimp/brain/anti_stun/hardened/Initialize(mapload) + . = ..() + desc += " The implant has been hardened. It is invulnerable to EMPs." /obj/item/organ/internal/cyberimp/brain/anti_stun/on_life() ..() if(crit_fail) return - if(owner.stunned > STUN_SET_AMOUNT) - owner.SetStunned(STUN_SET_AMOUNT) - if(owner.weakened > STUN_SET_AMOUNT) - owner.SetWeakened(STUN_SET_AMOUNT) + if(owner.stunned > stun_max_amount) + owner.SetStunned(stun_max_amount) + if(owner.weakened > stun_max_amount) + owner.SetWeakened(stun_max_amount) /obj/item/organ/internal/cyberimp/brain/anti_stun/emp_act(severity) + ..() if(crit_fail || emp_proof) return crit_fail = TRUE @@ -147,6 +155,12 @@ /obj/item/organ/internal/cyberimp/brain/anti_stun/proc/reboot() crit_fail = FALSE +/obj/item/organ/internal/cyberimp/brain/anti_stun/hardened + name = "Hardened CNS Rebooter implant" + desc = "A military-grade version of the standard implant, for NT's more elite forces." + origin_tech = "materials=6;programming=5;biotech=5" + emp_proof = TRUE + /obj/item/organ/internal/cyberimp/brain/clown_voice name = "Comical implant" desc = "Uh oh." diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index b0403d8ae49..837c6e0d056 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -54,7 +54,7 @@ var/obj/item/organ/external/BP = X var/brutedamage = BP.brute_dam - if(BP.is_robotic() && !ismachineperson(src)) + if(BP.is_robotic()) continue //We want an accurate reading of .len diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index 1b205f957ce..59385ff85a3 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -8,7 +8,7 @@ var/eye_colour = "#000000" // Should never be null var/list/colourmatrix = null var/list/colourblind_matrix = MATRIX_GREYSCALE //Special colourblindness parameters. By default, it's black-and-white. - var/list/replace_colours = LIST_GREYSCALE_REPLACE + var/list/replace_colours = GREYSCALE_COLOR_REPLACE var/dependent_disabilities = list() //Gets set by eye-dependent disabilities such as colourblindness so the eyes can transfer the disability during transplantation. var/weld_proof = null //If set, the eyes will not take damage during welding. eg. IPC optical sensors do not take damage when they weld things while all other eyes will. diff --git a/code/modules/surgery/organs/helpers.dm b/code/modules/surgery/organs/helpers.dm index ca2698f2ad4..4102319a3fe 100644 --- a/code/modules/surgery/organs/helpers.dm +++ b/code/modules/surgery/organs/helpers.dm @@ -104,7 +104,7 @@ /mob/living/carbon/human/get_leg_ignore() - if(flying == 1) + if(flying || floating) return TRUE var/obj/item/tank/jetpack/J diff --git a/code/modules/surgery/organs/organ.dm b/code/modules/surgery/organs/organ.dm index 3dab4065f7d..e85466740e8 100644 --- a/code/modules/surgery/organs/organ.dm +++ b/code/modules/surgery/organs/organ.dm @@ -172,12 +172,6 @@ if(parent.germ_level < germ_level && ( parent.germ_level < INFECTION_LEVEL_ONE * 2 || prob(30))) parent.germ_level++ -/obj/item/organ/internal/handle_germs() - ..() - if(germ_level >= INFECTION_LEVEL_TWO) - if(prob(3)) //about once every 30 seconds - receive_damage(1, silent = prob(30)) - /obj/item/organ/proc/rejuvenate() damage = 0 germ_level = 0 @@ -236,35 +230,6 @@ status &= ~ORGAN_SPLINTED status |= ORGAN_ROBOT -/obj/item/organ/external/emp_act(severity) - if(!is_robotic() || emp_proof) - return - if(tough) - switch(severity) - if(1) - receive_damage(0, 5.5) - if(owner) - owner.Stun(10) - if(2) - receive_damage(0, 2.8) - if(owner) - owner.Stun(5) - else - switch(severity) - if(1) - receive_damage(0, 20) - if(2) - receive_damage(0, 7) - -/obj/item/organ/internal/emp_act(severity) - if(!is_robotic() || emp_proof) - return - switch(severity) - if(1) - receive_damage(20, 1) - if(2) - receive_damage(7, 1) - /obj/item/organ/proc/shock_organ(intensity) return diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm index 143bf1d1ed6..be3d00faf8e 100644 --- a/code/modules/surgery/organs/organ_external.dm +++ b/code/modules/surgery/organs/organ_external.dm @@ -41,6 +41,9 @@ var/list/obj/item/organ/external/children var/list/convertable_children = list() + // Does the organ take reduce damage from EMPs? IPC limbs get this by default + var/emp_resistant = FALSE + // Internal organs of this body part var/list/internal_organs = list() @@ -256,6 +259,34 @@ return update_icon() +/obj/item/organ/external/emp_act(severity) + if(!is_robotic() || emp_proof) + return + if(tough) // Augmented limbs + switch(severity) + if(1) + receive_damage(0, 5.5) + if(owner) + owner.Stun(10) + if(2) + receive_damage(0, 2.8) + if(owner) + owner.Stun(5) + else if(emp_resistant) // IPC limbs + switch(severity) + if(1) + // 5.28 (9 * 0.66 burn_mod) burn damage, 65.34 damage with 11 limbs. + receive_damage(0, 9) + if(2) + // 3.63 (5 * 0.66 burn_mod) burn damage, 39.93 damage with 11 limbs. + receive_damage(0, 5.5) + else // Basic prosthetic limbs + switch(severity) + if(1) + receive_damage(0, 20) + if(2) + receive_damage(0, 7) + /* This function completely restores a damaged organ to perfect condition. */ @@ -777,3 +808,23 @@ Note that amputating the affected organ does in fact remove the infection from t var/obj/item/organ/external/L = X for(var/obj/item/I in L.embedded_objects) return 1 + +/obj/item/organ/external/emp_act(severity) + if(!is_robotic() || emp_proof) + return + if(tough) + switch(severity) + if(1) + receive_damage(0, 5.5) + if(owner) + owner.Stun(10) + if(2) + receive_damage(0, 2.8) + if(owner) + owner.Stun(5) + else + switch(severity) + if(1) + receive_damage(0, 20) + if(2) + receive_damage(0, 7) diff --git a/code/modules/surgery/organs/organ_icon.dm b/code/modules/surgery/organs/organ_icon.dm index 2a3b94ca249..b41ef824508 100644 --- a/code/modules/surgery/organs/organ_icon.dm +++ b/code/modules/surgery/organs/organ_icon.dm @@ -107,7 +107,10 @@ GLOBAL_LIST_EMPTY(limb_icon_cache) add_overlay(eyes_icon) if(owner.lip_style && (LIPS in dna.species.species_traits)) - add_overlay(mutable_appearance('icons/mob/human_face.dmi', "lips_[owner.lip_style]_s")) //Hefty icon not necessary. + var/icon/lips_icon = new('icons/mob/human_face.dmi', "lips_[owner.lip_style]_s") + lips_icon.Blend(owner.lip_color, ICON_MULTIPLY) + mob_icon.Blend(lips_icon, ICON_OVERLAY) + add_overlay(lips_icon) var/head_marking = owner.m_styles["head"] if(head_marking) diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 53283ada95e..78fa06122f5 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -36,13 +36,13 @@ log_runtime(EXCEPTION("[src] attempted to insert into a [parent_organ], but [parent_organ] wasn't an organ! [atom_loc_line(M)]"), src) else parent.internal_organs |= src - //M.internal_bodyparts_by_name[src] |= src(H,1) loc = null for(var/X in actions) var/datum/action/A = X A.Grant(M) if(vital) M.update_stat("Vital organ inserted") + STOP_PROCESSING(SSobj, src) // Removes the given organ from its owner. // Returns the removed object, which is usually just itself @@ -71,8 +71,18 @@ for(var/X in actions) var/datum/action/A = X A.Remove(M) + START_PROCESSING(SSobj, src) return src +/obj/item/organ/internal/emp_act(severity) + if(!is_robotic() || emp_proof) + return + switch(severity) + if(1) + receive_damage(20, 1) + if(2) + receive_damage(7, 1) + /obj/item/organ/internal/replaced(var/mob/living/carbon/human/target) insert(target) @@ -335,3 +345,18 @@ head_organ.f_style = "Very Long Beard" head_organ.facial_colour = "#D8C078" H.update_fhair() + +/obj/item/organ/internal/emp_act(severity) + if(!is_robotic() || emp_proof) + return + switch(severity) + if(1) + receive_damage(20, 1) + if(2) + receive_damage(7, 1) + +/obj/item/organ/internal/handle_germs() + ..() + if(germ_level >= INFECTION_LEVEL_TWO) + if(prob(3)) //about once every 30 seconds + receive_damage(1, silent = prob(30)) diff --git a/code/modules/surgery/organs/pain.dm b/code/modules/surgery/organs/pain.dm index 91e4a2f6781..70f3ff43fa1 100644 --- a/code/modules/surgery/organs/pain.dm +++ b/code/modules/surgery/organs/pain.dm @@ -30,7 +30,7 @@ // message is the custom message to be displayed -mob/living/carbon/human/proc/custom_pain(message) +/mob/living/carbon/human/proc/custom_pain(message) if(stat >= UNCONSCIOUS) return @@ -49,7 +49,7 @@ mob/living/carbon/human/proc/custom_pain(message) to_chat(src, msg) next_pain_time = world.time + 100 -mob/living/carbon/human/proc/handle_pain() +/mob/living/carbon/human/proc/handle_pain() // not when sleeping if(stat >= UNCONSCIOUS) diff --git a/code/modules/surgery/organs/parasites.dm b/code/modules/surgery/organs/parasites.dm index b61a7c429bc..b9afa20b7d2 100644 --- a/code/modules/surgery/organs/parasites.dm +++ b/code/modules/surgery/organs/parasites.dm @@ -54,8 +54,6 @@ var/eggs_hatched = 0 // num of hatch events completed var/awaymission_checked = FALSE var/awaymission_infection = FALSE // TRUE if infection occurred inside gateway - var/list/types_basic = list(/mob/living/simple_animal/hostile/poison/terror_spider/red, /mob/living/simple_animal/hostile/poison/terror_spider/gray) - var/list/types_adv = list(/mob/living/simple_animal/hostile/poison/terror_spider/red, /mob/living/simple_animal/hostile/poison/terror_spider/gray, /mob/living/simple_animal/hostile/poison/terror_spider/green) /obj/item/organ/internal/body_egg/terror_eggs/on_life() @@ -104,12 +102,16 @@ var/infection_completed = FALSE var/obj/structure/spider/spiderling/terror_spiderling/S = new(get_turf(owner)) switch(eggs_hatched) - if(0) // First spiderling - S.grow_as = pick(types_basic) - if(1) // Second - S.grow_as = pick(types_adv) - if(2) // Last - S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/princess + if(0) // 1st spiderling + S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/gray + if(1) // 2nd + S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/red + if(2) // 3rd + S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/brown + if(3) // 4th + S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/green + if(4) // 5th + S.grow_as = /mob/living/simple_animal/hostile/poison/terror_spider/green infection_completed = TRUE S.immediate_ventcrawl = TRUE eggs_hatched++ diff --git a/code/modules/surgery/organs/subtypes/machine.dm b/code/modules/surgery/organs/subtypes/machine.dm index 52cad684e11..415cdb7875d 100644 --- a/code/modules/surgery/organs/subtypes/machine.dm +++ b/code/modules/surgery/organs/subtypes/machine.dm @@ -5,6 +5,7 @@ min_broken_damage = 30 encased = null status = ORGAN_ROBOT + emp_resistant = TRUE /obj/item/organ/external/head/ipc/New(mob/living/carbon/holder, datum/species/species_override = null) ..(holder, /datum/species/machine) // IPC heads need to be explicitly set to this since you can print them @@ -13,6 +14,7 @@ /obj/item/organ/external/chest/ipc encased = null status = ORGAN_ROBOT + emp_resistant = TRUE /obj/item/organ/external/chest/ipc/New() ..() @@ -21,6 +23,7 @@ /obj/item/organ/external/groin/ipc encased = null status = ORGAN_ROBOT + emp_resistant = TRUE /obj/item/organ/external/groin/ipc/New() ..() @@ -29,6 +32,7 @@ /obj/item/organ/external/arm/ipc encased = null status = ORGAN_ROBOT + emp_resistant = TRUE /obj/item/organ/external/arm/ipc/New() ..() @@ -37,6 +41,7 @@ /obj/item/organ/external/arm/right/ipc encased = null status = ORGAN_ROBOT + emp_resistant = TRUE /obj/item/organ/external/arm/right/ipc/New() ..() @@ -45,6 +50,7 @@ /obj/item/organ/external/leg/ipc encased = null status = ORGAN_ROBOT + emp_resistant = TRUE /obj/item/organ/external/leg/ipc/New() ..() @@ -53,6 +59,7 @@ /obj/item/organ/external/leg/right/ipc encased = null status = ORGAN_ROBOT + emp_resistant = TRUE /obj/item/organ/external/leg/right/ipc/New() ..() @@ -61,6 +68,7 @@ /obj/item/organ/external/foot/ipc encased = null status = ORGAN_ROBOT + emp_resistant = TRUE /obj/item/organ/external/foot/ipc/New() ..() @@ -69,6 +77,7 @@ /obj/item/organ/external/foot/right/ipc encased = null status = ORGAN_ROBOT + emp_resistant = TRUE /obj/item/organ/external/foot/right/ipc/New() ..() @@ -77,6 +86,7 @@ /obj/item/organ/external/hand/ipc encased = null status = ORGAN_ROBOT + emp_resistant = TRUE /obj/item/organ/external/hand/ipc/New() ..() @@ -85,6 +95,7 @@ /obj/item/organ/external/hand/right/ipc encased = null status = ORGAN_ROBOT + emp_resistant = TRUE /obj/item/organ/external/hand/right/ipc/New() ..() diff --git a/code/modules/surgery/organs/subtypes/tajaran.dm b/code/modules/surgery/organs/subtypes/tajaran.dm index e8d87b55b89..226bc82dd2d 100644 --- a/code/modules/surgery/organs/subtypes/tajaran.dm +++ b/code/modules/surgery/organs/subtypes/tajaran.dm @@ -7,14 +7,14 @@ icon = 'icons/obj/species_organs/tajaran.dmi' name = "tajaran eyeballs" colourblind_matrix = MATRIX_TAJ_CBLIND //The colour matrix and darksight parameters that the mob will recieve when they get the disability. - replace_colours = LIST_TAJ_REPLACE + replace_colours = TRITANOPIA_COLOR_REPLACE see_in_dark = 8 /obj/item/organ/internal/eyes/tajaran/farwa //Being the lesser form of Tajara, Farwas have an utterly incurable version of their colourblindness. name = "farwa eyeballs" colourmatrix = MATRIX_TAJ_CBLIND see_in_dark = 8 - replace_colours = LIST_TAJ_REPLACE + replace_colours = TRITANOPIA_COLOR_REPLACE /obj/item/organ/internal/heart/tajaran name = "tajaran heart" diff --git a/code/modules/surgery/organs/subtypes/vulpkanin.dm b/code/modules/surgery/organs/subtypes/vulpkanin.dm index a2815d32dc6..bd56532ebd9 100644 --- a/code/modules/surgery/organs/subtypes/vulpkanin.dm +++ b/code/modules/surgery/organs/subtypes/vulpkanin.dm @@ -7,14 +7,14 @@ name = "vulpkanin eyeballs" icon = 'icons/obj/species_organs/vulpkanin.dmi' colourblind_matrix = MATRIX_VULP_CBLIND //The colour matrix and darksight parameters that the mob will recieve when they get the disability. - replace_colours = LIST_VULP_REPLACE + replace_colours = PROTANOPIA_COLOR_REPLACE see_in_dark = 8 /obj/item/organ/internal/eyes/vulpkanin/wolpin //Being the lesser form of Vulpkanin, Wolpins have an utterly incurable version of their colourblindness. name = "wolpin eyeballs" colourmatrix = MATRIX_VULP_CBLIND see_in_dark = 8 - replace_colours = LIST_VULP_REPLACE + replace_colours = PROTANOPIA_COLOR_REPLACE /obj/item/organ/internal/heart/vulpkanin name = "vulpkanin heart" diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index c9b26f61228..dd73cf0f02a 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -157,7 +157,7 @@ GLOBAL_DATUM_INIT(multispin_words, /regex, regex("like a record baby")) if(L.can_hear() && !L.null_rod_check() && L != owner && L.stat != DEAD) if(ishuman(L)) var/mob/living/carbon/human/H = L - if(istype(H.l_ear, /obj/item/clothing/ears/earmuffs) || istype(H.r_ear, /obj/item/clothing/ears/earmuffs)) + if(H.check_ear_prot() >= HEARING_PROTECTION_TOTAL) continue listeners += L diff --git a/code/modules/surgery/rig_removal.dm b/code/modules/surgery/rig_removal.dm deleted file mode 100644 index de15eacf8f3..00000000000 --- a/code/modules/surgery/rig_removal.dm +++ /dev/null @@ -1,59 +0,0 @@ -//Procedures in this file: Unsealing a Rig. - -/datum/surgery/rigsuit - name = "Rig Unsealing" - steps = list(/datum/surgery_step/rigsuit) - possible_locs = list("chest") - -/datum/surgery/rigsuit/can_start(mob/user, mob/living/carbon/target) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/backitem = H.get_item_by_slot(slot_back) - if(istype(backitem,/obj/item/rig)) //Check if we have a rig to operate on - if(backitem.flags&NODROP) //Check if the rig is sealed, if not, we don't need to operate - return 1 - return 0 - -//Bay12 removal -/datum/surgery_step/rigsuit - name="Cut Seals" - allowed_tools = list( - /obj/item/weldingtool = 80, - /obj/item/circular_saw = 60, - /obj/item/gun/energy/plasmacutter = 100 - ) - - can_infect = 0 - blood_level = 0 - - time = 50 - -/datum/surgery_step/hardsuit/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - if(!istype(target)) - return 0 - if(tool.tool_behaviour == TOOL_WELDER) - if(!tool.tool_use_check(user, 0)) - return - if(!tool.use(1)) - return - return (target_zone == "chest") && istype(target.back, /obj/item/rig) && (target.back.flags&NODROP) - -/datum/surgery_step/rigsuit/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user] starts cutting through the support systems of [target]'s [target.back] with \the [tool]." , \ - "You start cutting through the support systems of [target]'s [target.back] with \the [tool].") - ..() - -/datum/surgery_step/rigsuit/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - - var/obj/item/rig/rig = target.back - if(!istype(rig)) - return - rig.reset() - user.visible_message("[user] has cut through the support systems of [target]'s [rig] with \the [tool].", \ - "You have cut through the support systems of [target]'s [rig] with \the [tool].") - return 1 - -/datum/surgery_step/rigsuit/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) - user.visible_message("[user]'s [tool] can't quite seem to get through the metal...", \ - "Your [tool] can't quite seem to get through the metal. It's weakening, though - try again.") - return 0 diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index 0b13d18f9da..294e98d38d0 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -111,7 +111,7 @@ prob_chance *= get_location_modifier(target) - if(!ispath(surgery.steps[surgery.status], /datum/surgery_step/robotics) && !ispath(surgery.steps[surgery.status], /datum/surgery_step/rigsuit))//Repairing robotic limbs doesn't hurt, and neither does cutting someone out of a rig + if(!ispath(surgery.steps[surgery.status], /datum/surgery_step/robotics))//Repairing robotic limbs doesn't hurt, and neither does cutting someone out of a rig if(ishuman(target)) var/mob/living/carbon/human/H = target //typecast to human prob_chance *= get_pain_modifier(H)//operating on conscious people is hard. @@ -196,7 +196,7 @@ if(AStar(E.loc, M.loc, /turf/proc/Distance, 2, simulated_only = 0)) germs++ - if(tool.blood_DNA && tool.blood_DNA.len) //germs from blood-stained tools + if(tool && tool.blood_DNA && tool.blood_DNA.len) //germs from blood-stained tools germs += 30 if(E.internal_organs.len) diff --git a/code/modules/telesci/gps.dm b/code/modules/telesci/gps.dm index 9b7ba72209b..0d9acc07207 100644 --- a/code/modules/telesci/gps.dm +++ b/code/modules/telesci/gps.dm @@ -16,12 +16,14 @@ GLOBAL_LIST_EMPTY(GPS_list) /obj/item/gps/New() ..() GLOB.GPS_list.Add(src) + GLOB.poi_list.Add(src) if(name == "default gps") //use default naming scheme name = "global positioning system ([gpstag])" overlays += "working" /obj/item/gps/Destroy() GLOB.GPS_list.Remove(src) + GLOB.poi_list.Remove(src) return ..() /obj/item/gps/emp_act(severity) diff --git a/code/modules/tgui/modules/crew_monitor.dm b/code/modules/tgui/modules/crew_monitor.dm index 783498542e4..a33a1d141f9 100644 --- a/code/modules/tgui/modules/crew_monitor.dm +++ b/code/modules/tgui/modules/crew_monitor.dm @@ -14,18 +14,21 @@ if("track") if(isAI(usr)) var/mob/living/silicon/ai/AI = usr - var/mob/living/carbon/human/H = locate(params["track"]) in GLOB.mob_list + var/mob/living/carbon/human/H = locate(params["track"]) in GLOB.human_list if(hassensorlevel(H, SUIT_SENSOR_TRACKING)) AI.ai_actual_track(H) return TRUE -/datum/tgui_module/crew_monitor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) +/datum/tgui_module/crew_monitor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - // The 557 may seem random, but its the perfectsize for margins on the nanomap - ui = new(user, src, ui_key, "CrewMonitor", name, 1400, 557, master_ui, state) - ui.autoupdate = TRUE + ui = new(user, src, ui_key, "CrewMonitor", name, 800, 600, master_ui, state) + + // Send nanomaps + var/datum/asset/nanomaps = get_asset_datum(/datum/asset/simple/nanomaps) + nanomaps.send(user) + ui.open() diff --git a/code/modules/tgui/modules/ert_manager.dm b/code/modules/tgui/modules/ert_manager.dm new file mode 100644 index 00000000000..582d03f5a54 --- /dev/null +++ b/code/modules/tgui/modules/ert_manager.dm @@ -0,0 +1,100 @@ +/datum/tgui_module/ert_manager + name = "ERT Manager" + var/ert_type = "Red" + var/commander_slots = 1 // defaults for open slots + var/security_slots = 4 + var/medical_slots = 0 + var/engineering_slots = 0 + var/janitor_slots = 0 + var/paranormal_slots = 0 + var/cyborg_slots = 0 + +/datum/tgui_module/ert_manager/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_admin_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "ERTManager", name, 350, 430, master_ui, state) + ui.autoupdate = TRUE + ui.open() + +/datum/tgui_module/ert_manager/tgui_data(mob/user) + var/list/data = list() + data["str_security_level"] = capitalize(get_security_level()) + switch(GLOB.security_level) + if(SEC_LEVEL_GREEN) + data["security_level_color"] = "green" + if(SEC_LEVEL_BLUE) + data["security_level_color"] = "blue" + if(SEC_LEVEL_RED) + data["security_level_color"] = "red" + else + data["security_level_color"] = "purple" + data["ert_type"] = ert_type + data["com"] = commander_slots + data["sec"] = security_slots + data["med"] = medical_slots + data["eng"] = engineering_slots + data["jan"] = janitor_slots + data["par"] = paranormal_slots + data["cyb"] = cyborg_slots + data["total"] = commander_slots + security_slots + medical_slots + engineering_slots + janitor_slots + paranormal_slots + cyborg_slots + data["spawnpoints"] = GLOB.emergencyresponseteamspawn.len + return data + +/datum/tgui_module/ert_manager/tgui_act(action, params) + if(..()) + return + . = TRUE + switch(action) + if("ert_type") + ert_type = params["ert_type"] + if("toggle_com") + commander_slots = commander_slots ? 0 : 1 + if("set_sec") + security_slots = text2num(params["set_sec"]) + if("set_med") + medical_slots = text2num(params["set_med"]) + if("set_eng") + engineering_slots = text2num(params["set_eng"]) + if("set_jan") + janitor_slots = text2num(params["set_jan"]) + if("set_par") + paranormal_slots = text2num(params["set_par"]) + if("set_cyb") + cyborg_slots = text2num(params["set_cyb"]) + if("dispatch_ert") + var/datum/response_team/D + switch(ert_type) + if("Amber") + D = new /datum/response_team/amber + if("Red") + D = new /datum/response_team/red + if("Gamma") + D = new /datum/response_team/gamma + else + to_chat(usr, "Invalid ERT type.") + return + GLOB.ert_request_answered = TRUE + var/slots_list = list() + if(commander_slots > 0) + slots_list += "commander: [commander_slots]" + if(security_slots > 0) + slots_list += "security: [security_slots]" + if(medical_slots > 0) + slots_list += "medical: [medical_slots]" + if(engineering_slots > 0) + slots_list += "engineering: [engineering_slots]" + if(janitor_slots > 0) + slots_list += "janitor: [janitor_slots]" + if(paranormal_slots > 0) + slots_list += "paranormal: [paranormal_slots]" + if(cyborg_slots > 0) + slots_list += "cyborg: [cyborg_slots]" + var/slot_text = english_list(slots_list) + notify_ghosts("An ERT is being dispatched. Open positions: [slot_text]") + message_admins("[key_name_admin(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]", 1) + log_admin("[key_name(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]") + GLOB.event_announcement.Announce("Attention, [station_name()]. We are attempting to assemble an ERT. Standby.", "ERT Protocol Activated") + trigger_armed_response_team(D, commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots) + else + return FALSE + diff --git a/code/modules/tgui/modules/power_monitor.dm b/code/modules/tgui/modules/power_monitor.dm new file mode 100644 index 00000000000..71e12836d78 --- /dev/null +++ b/code/modules/tgui/modules/power_monitor.dm @@ -0,0 +1,54 @@ +/datum/tgui_module/power_monitor + name = "Power monitor" + var/select_monitor = FALSE + var/obj/machinery/computer/monitor/powermonitor + +/datum/tgui_module/power_monitor/digital + select_monitor = TRUE + +/datum/tgui_module/power_monitor/New() + ..() + if(!select_monitor) + powermonitor = tgui_host() + +/datum/tgui_module/power_monitor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "PowerMonitor", name, 600, 650, master_ui, state) + ui.open() + +/datum/tgui_module/power_monitor/tgui_data(mob/user) + var/list/data = list() + + data["powermonitor"] = powermonitor + if(select_monitor) + data["select_monitor"] = TRUE + data["powermonitors"] = GLOB.powermonitor_repository.powermonitor_data() + + if(powermonitor && !isnull(powermonitor.powernet)) + if(select_monitor && (powermonitor.stat & (NOPOWER|BROKEN))) + powermonitor = null + return + data["poweravail"] = DisplayPower(powermonitor.powernet.viewavail) + data["powerdemand"] = DisplayPower(powermonitor.powernet.viewload) + data["history"] = powermonitor.history + data["apcs"] = GLOB.apc_repository.apc_data(powermonitor.powernet) + + return data + +/datum/tgui_module/power_monitor/tgui_act(action, list/params) + if(..()) + return + + // Dont allow people to break regular ones + if(!select_monitor) + return + + . = TRUE + switch(action) + if("selectmonitor") + powermonitor = locateUID(params["selectmonitor"]) + powermonitor.powernet = powermonitor.find_powernet() // Refresh the powernet of the monitor + + if("return") + powermonitor = null diff --git a/code/modules/tgui/plugins/login.dm b/code/modules/tgui/plugins/login.dm new file mode 100644 index 00000000000..ae0ac9c028e --- /dev/null +++ b/code/modules/tgui/plugins/login.dm @@ -0,0 +1,213 @@ +/** + * tgui login + * + * Allows the handling of logins using IDs within tgui. + * + * Two key procs: + * * [/obj/proc/tgui_login_act] - Call in your tgui_act() proc to catch any login actions and handle them. + * * [/obj/proc/tgui_login_data] - Call in your tgui_data() proc to pass login info. + * + * How to use (DM side): + * 1. Call [/obj/proc/tgui_login_act] at the start of your tgui_act() proc + * 2. Call [/obj/proc/tgui_login_data] in your tgui_data() proc while passing the data list + * 3. In your object, call [/obj/proc/tgui_login_get] to get the current login state. + * 4. Optional: call [/obj/proc/tgui_login_attackby] in your attackby() to make the login process easier. + * + * How to use (JS side): Use the and interfaces. + */ + +GLOBAL_LIST(tgui_logins) + +/** + * Call this from a proc that is called in tgui_act() to process login actions + * + * Arguments: + * * action - The called action + * * params - The params to the action + */ +/obj/proc/tgui_login_act(action = "", params) + . = null + switch(action) + if("login_insert") + var/datum/tgui_login/state = tgui_login_get() + if(state.id) // An ID is already inserted, eject it + return tgui_login_eject(state = state) + else // No ID, attempt to insert one in + return tgui_login_insert(usr.get_active_hand(), state = state) + if("login_eject") + return tgui_login_eject() + if("login_login") + return tgui_login_login(text2num(params["login_type"])) + if("login_logout") + return tgui_login_logout() + +/** + * Appends login state data. + * + * Arguments: + * * data - The data list to be returned + * * user - The user calling tgui_data() + * * state - The current login state + */ +/obj/proc/tgui_login_data(list/data, mob/user, datum/tgui_login/state = tgui_login_get()) + data["loginState"] = list( + "id" = state.id ? state.id.name : null, + "name" = state.name, + "rank" = state.rank, + "logged_in" = state.logged_in, + ) + data["isAI"] = isAI(user) + data["isRobot"] = isrobot(user) + data["isAdmin"] = user.can_admin_interact() + +/** + * Convenience function to perform login actions when + * the source object is hit by specific items. + * + * Arguments: + * * O - The object + * * user - The user + */ +/obj/proc/tgui_login_attackby(obj/item/O, mob/user) + if(istype(O, /obj/item/card/id) && tgui_login_insert(O)) + tgui_interact(user) + return TRUE + +/** + * Attempts to insert an object as an ID. + * + * Arguments: + * * O - The object to try inserting + * * state - The current login state + */ +/obj/proc/tgui_login_insert(obj/item/O, datum/tgui_login/state = tgui_login_get()) + if(state.id) + return + + if(istype(O, /obj/item/card/id)) + // Move the ID inside + usr.drop_item() + O.forceMove(src) + + // Update the state + state.id = O + + return TRUE + +/** + * Attempts to eject the inserted ID. + * + * Arguments: + * * state - The current login state + */ +/obj/proc/tgui_login_eject(datum/tgui_login/state = tgui_login_get()) + if(!state.id) + return + + // Drop the ID + state.id.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(state.id) + + // Update the state + state.id = null + + return TRUE + +/** + * Attempts to log in with the given login type. + * + * Arguments: + * * login_type - The login type: LOGIN_TYPE_NORMAL (checks for inserted ID), LOGIN_TYPE_AI, LOGIN_TYPE_ROBOT and LOGIN_TYPE_ADMIN + * * state - The current login state + */ +/obj/proc/tgui_login_login(login_type = LOGIN_TYPE_NORMAL, datum/tgui_login/state = tgui_login_get()) + if(state.logged_in) + return + + if(state.id && login_type == LOGIN_TYPE_NORMAL) + if(check_access(state.id)) + state.name = state.id.registered_name + state.rank = state.id.assignment + state.access = state.id.access + else if(login_type == LOGIN_TYPE_AI && isAI(usr)) + state.name = usr.name + state.rank = "AI" + else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr)) + var/mob/living/silicon/robot/R = usr + state.name = usr.name + state.rank = "[R.modtype] [R.braintype]" + else if(login_type == LOGIN_TYPE_ADMIN && usr.can_admin_interact()) + state.name = "*CONFIDENTIAL*" + state.rank = "CentComm Secure Connection" + state.access = get_all_accesses() + get_all_centcom_access() + + state.logged_in = TRUE + tgui_login_on_login(state = state) + + return TRUE + +/** + * Attempts to log out. + * + * Arguments: + * * state - The current login state + */ +/obj/proc/tgui_login_logout(datum/tgui_login/state = tgui_login_get()) + if(!state.logged_in) + return + + state.name = "" + state.rank = "" + state.access = null + state.logged_in = FALSE + tgui_login_on_logout(state = state) + + return TRUE + +/** + * Returns (or creates) the login state for the source object. + * + * Arguments: + * * state - The current login state + */ +/obj/proc/tgui_login_get() + RETURN_TYPE(/datum/tgui_login) + . = LAZYACCESS(GLOB.tgui_logins, UID()) + if(!.) + LAZYINITLIST(GLOB.tgui_logins) + . = GLOB.tgui_logins[UID()] = new /datum/tgui_login + +/** + * Called on successful login. + * + * Arguments: + * * state - The current login state + */ +/obj/proc/tgui_login_on_login(datum/tgui_login/state = tgui_login_get()) + return + +/** + * Called on successful logout. + * + * Arguments: + * * state - The current login state + */ +/obj/proc/tgui_login_on_logout(datum/tgui_login/state = tgui_login_get()) + return + +/** + * Login state (there should be only one for one datum) + */ +/datum/tgui_login + var/obj/item/card/id/id = null + var/name = "" + var/rank = "" + var/list/access = null + var/logged_in = FALSE + +/datum/tgui_login/New(id, name, rank, access) + src.id = id + src.name = name + src.rank = rank + src.access = access diff --git a/code/modules/tgui/plugins/modal.dm b/code/modules/tgui/plugins/modal.dm new file mode 100644 index 00000000000..f09f54f627c --- /dev/null +++ b/code/modules/tgui/plugins/modal.dm @@ -0,0 +1,370 @@ +/** + * tgui modals + * + * Allows creation of modals within tgui. + */ + +GLOBAL_LIST(tgui_modals) + +/** + * Call this from a proc that is called in tgui_act() to process modal actions + * + * Example: /obj/machinery/chem_master/proc/tgui_act_modal + * You can then switch based on the return value and show different + * modals depending on the answer. + * Arguments: + * * source - The source datum + * * action - The called action + * * params - The params to the action + */ +/datum/proc/tgui_modal_act(datum/source = src, action = "", params) + ASSERT(istype(source)) + + . = null + switch(action) + if("modal_open") // Params: id, arguments + return TGUI_MODAL_OPEN + if("modal_answer") // Params: id, answer, arguments + params["answer"] = tgui_modal_preprocess_answer(source, params["answer"]) + if(tgui_modal_answer(source, params["id"], params["answer"])) // If there's a current modal with a delegate that returned TRUE, no need to continue + . = TGUI_MODAL_DELEGATE + else + . = TGUI_MODAL_ANSWER + tgui_modal_clear(source) + if("modal_close") // Params: id + tgui_modal_clear(source) + return TGUI_MODAL_CLOSE + +/** + * Call this from tgui_data() to return modal information if needed + + * Arguments: + * * source - The source datum + */ +/datum/proc/tgui_modal_data(datum/source = src) + ASSERT(istype(source)) + + var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID()) + if(!current) + return null + + return current.to_data() + +/** + * Clears the current modal for a given datum + * + * Arguments: + * * source - The source datum + */ +/datum/proc/tgui_modal_clear(datum/source = src) + ASSERT(istype(source)) + + LAZYINITLIST(GLOB.tgui_modals) + var/datum/tgui_modal/previous = GLOB.tgui_modals[source.UID()] + if(!previous) + return FALSE + + for(var/i in 1 to length(GLOB.tgui_modals)) + var/key = GLOB.tgui_modals[i] + if(previous == GLOB.tgui_modals[key]) + GLOB.tgui_modals.Cut(i, i + 1) + break + + SStgui.update_uis(source) + return TRUE + +/** + * Opens a message TGUI modal + * + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * text - The text to display above the answers + * * delegate - The proc to call when closed + * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals) + */ +/datum/proc/tgui_modal_message(datum/source = src, id, text = "Default modal message", delegate, arguments) + ASSERT(length(id)) + + var/datum/tgui_modal/modal = new(id, text, delegate, arguments) + return tgui_modal_new(source, modal) + +/** + * Opens a text input TGUI modal + * + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * text - The text to display above the answers + * * delegate - The proc to call when submitted + * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals) + * * value - The default value of the input + * * max_length - The maximum char length of the input + */ +/datum/proc/tgui_modal_input(datum/source = src, id, text = "Default modal message", delegate, arguments, value = "", max_length = TGUI_MODAL_INPUT_MAX_LENGTH) + ASSERT(length(id)) + ASSERT(max_length > 0) + + var/datum/tgui_modal/input/modal = new(id, text, delegate, arguments, value, max_length) + return tgui_modal_new(source, modal) + +/** + * Opens a dropdown input TGUI modal + * + * Internally checks if the answer is in the list of choices. + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * text - The text to display above the answers + * * delegate - The proc to call when submitted + * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals) + * * value - The default value of the dropdown + * * choices - The list of available choices in the dropdown + */ +/datum/proc/tgui_modal_choice(datum/source = src, id, text = "Default modal message", delegate, arguments, value = "", choices) + ASSERT(length(id)) + + var/datum/tgui_modal/input/choice/modal = new(id, text, delegate, arguments, value, choices) + return tgui_modal_new(source, modal) + +/** + * Opens a bento input TGUI modal + * + * Internally checks if the answer is in the list of choices. + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * text - The text to display above the answers + * * delegate - The proc to call when submitted + * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals) + * * value - The default value of the bento + * * choices - The list of available choices in the bento + */ +/datum/proc/tgui_modal_bento(datum/source = src, id, text = "Default modal message", delegate, arguments, value, choices) + ASSERT(length(id)) + + var/datum/tgui_modal/input/bento/modal = new(id, text, delegate, arguments, value, choices) + return tgui_modal_new(source, modal) + +/** + * Opens a yes/no TGUI modal + * + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * text - The text to display above the answers + * * delegate - The proc to call when "Yes" is pressed + * * delegate_no - The proc to call when "No" is pressed + * * arguments - List of arguments passed to and from JS (mostly useful for chaining modals) + * * yes_text - The text to show in the "Yes" button + * * no_text - The text to show in the "No" button + */ +/datum/proc/tgui_modal_boolean(datum/source = src, id, text = "Default modal message", delegate, delegate_no, arguments, yes_text = "Yes", no_text = "No") + ASSERT(length(id)) + + var/datum/tgui_modal/boolean/modal = new(id, text, delegate, delegate_no, arguments, yes_text, no_text) + return tgui_modal_new(source, modal) + +/** + * Registers a given modal to a source. Private. + * + * Arguments: + * * source - The source datum + * * modal - The datum/tgui_modal to register + * * replace_previous - Whether any modal currently assigned to source should be replaced + * * instant_update - Whether the changes should reflect immediately + */ +/datum/proc/tgui_modal_new(datum/source = src, datum/tgui_modal/modal = null, replace_previous = TRUE, instant_update = TRUE) + ASSERT(istype(source)) + ASSERT(istype(modal)) + + var/datum/tgui_modal/previous = LAZYACCESS(GLOB.tgui_modals, source.UID()) + if(previous && !replace_previous) + return FALSE + + modal.owning_source = source + + // Previous one should get GC'd + LAZYSET(GLOB.tgui_modals, source.UID(), modal) + if(instant_update) + SStgui.update_uis(source) + return TRUE + +/** + * Calls the source's currently assigned modal's (if there is one) on_answer() proc. Private. + * + * Arguments: + * * source - The source datum + * * id - The ID of the modal + * * answer - The provided answer + */ +/datum/proc/tgui_modal_answer(datum/source = src, id, answer = "") + ASSERT(istype(source)) + + var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID()) + if(!current) + return FALSE + + return current.on_answer(answer) + +/** + * Passes an answer from JS through the modal's proc. + * + * Used namely for cutting the text short if it's longer + * than an input modal's max_length. + * Arguments: + * * source - The source datum + * * answer - The provided answer + */ +/datum/proc/tgui_modal_preprocess_answer(datum/source = src, answer = "") + ASSERT(istype(source)) + + var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID()) + if(!current) + return answer + + return current.preprocess_answer(answer) + +/** + * Modal datum (contains base information for a modal) + */ +/datum/tgui_modal + var/datum/owning_source + var/id + var/text + var/delegate + var/list/arguments + var/modal_type = "message" + +/datum/tgui_modal/New(id, text, delegate, list/arguments) + src.id = id + src.text = text + src.delegate = delegate + src.arguments = arguments + +/** + * Called when it's time to pre-process the answer before using it + * + * Arguments: + * * answer - The answer, a nullable text + */ +/datum/tgui_modal/proc/preprocess_answer(answer) + return reject_bad_text(answer, TGUI_MODAL_INPUT_MAX_LENGTH) // bleh + +/** + * Called when a modal receives an answer + * + * Arguments: + * * answer - The answer, a nullable text + */ +/datum/tgui_modal/proc/on_answer(answer) + if(delegate) + return call(owning_source, delegate)(answer, arguments) + return FALSE + +/** + * Creates a list that describes a modal visually to be passed to JS + */ +/datum/tgui_modal/proc/to_data() + . = list() + .["id"] = id + .["text"] = text + .["args"] = arguments || list() + .["type"] = modal_type + +/** + * Input modal - has a text entry that can be used to enter an answer + */ +/datum/tgui_modal/input + modal_type = "input" + var/value + var/max_length + +/datum/tgui_modal/input/New(id, text, delegate, list/arguments, value, max_length) + ..(id, text, delegate, arguments) + src.value = value + src.max_length = max_length + +/datum/tgui_modal/input/preprocess_answer(answer) + . = ..(answer) + if(length(answer) > max_length) + . = copytext(., 1, max_length + 1) + +/datum/tgui_modal/input/to_data() + . = ..() + .["value"] = value + +/** + * Choice modal - has a dropdown menu that can be used to select an answer + */ +/datum/tgui_modal/input/choice + modal_type = "choice" + var/choices + +/datum/tgui_modal/input/choice/New(id, text, delegate, list/arguments, value, choices) + ..(id, text, delegate, arguments, value, TGUI_MODAL_INPUT_MAX_LENGTH) // Max length doesn't really matter in dropdowns, but whatever + src.choices = choices + +/datum/tgui_modal/input/choice/on_answer(answer) + if(answer in choices) // Make sure the answer is actually in our choices! + return ..(answer, arguments) + return FALSE + +/datum/tgui_modal/input/choice/to_data() + . = ..() + .["choices"] = choices + +/** + * Bento modal - Similar to choice, it displays the choices in a grid of images + * + * The returned answer is the index of the choice. + */ +/datum/tgui_modal/input/bento + modal_type = "bento" + var/choices + +/datum/tgui_modal/input/bento/New(id, text, delegate, list/arguments, value, choices) + ..(id, text, delegate, arguments, text2num(value), TGUI_MODAL_INPUT_MAX_LENGTH) // Max length doesn't really matter in here, but whatever + src.choices = choices + +/datum/tgui_modal/input/bento/preprocess_answer(answer) + return text2num(answer) || 0 + +/datum/tgui_modal/input/bento/on_answer(answer) + if(answer >= 1 && answer <= length(choices)) // Make sure the answer index is actually in our indexes! + return ..(answer, arguments) + return FALSE + +/datum/tgui_modal/input/bento/to_data() + . = ..() + .["choices"] = choices + +/** + * Boolean modal - has yes/no buttons that do different actions depending on which is pressed + */ +/datum/tgui_modal/boolean + modal_type = "boolean" + var/delegate_no + var/yes_text + var/no_text + +/datum/tgui_modal/boolean/New(id, text, delegate, delegate_no, list/arguments, yes_text, no_text) + ..(id, text, delegate, arguments) + src.delegate_no = delegate_no + src.yes_text = yes_text + src.no_text = no_text + +/datum/tgui_modal/boolean/preprocess_answer(answer) + return text2num(answer) || FALSE + +/datum/tgui_modal/boolean/on_answer(answer) + if(answer) + return ..(answer, arguments) + else if(delegate_no) + return call(owning_source, delegate_no)(arguments) + return FALSE + +/datum/tgui_modal/boolean/to_data() + . = ..() + .["yes_text"] = yes_text + .["no_text"] = no_text diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 5b1409af4e7..80467d19846 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -3,6 +3,7 @@ #ifdef UNIT_TESTS #include "component_tests.dm" +#include "map_templates.dm" #include "reagent_id_typos.dm" #include "spawn_humans.dm" #include "sql.dm" diff --git a/code/modules/unit_tests/map_templates.dm b/code/modules/unit_tests/map_templates.dm new file mode 100644 index 00000000000..b590526a46b --- /dev/null +++ b/code/modules/unit_tests/map_templates.dm @@ -0,0 +1,7 @@ +/datum/unit_test/map_templates/Run() + var/list/datum/map_template/templates = subtypesof(/datum/map_template) + for(var/I in templates) + var/datum/map_template/MT = new I // The new is important here to ensure stuff gets set properly + // Check if it even has a path and if so, does it exist + if(MT.mappath && !fexists(MT.mappath)) + Fail("The map file for [MT.type] does not exist!") diff --git a/code/modules/vehicle/vehicle.dm b/code/modules/vehicle/vehicle.dm index cdbb8580da6..308967cdf82 100644 --- a/code/modules/vehicle/vehicle.dm +++ b/code/modules/vehicle/vehicle.dm @@ -33,6 +33,13 @@ QDEL_NULL(inserted_key) return ..() +// So that beepsky can't push the janicart +/obj/vehicle/CanPass(atom/movable/mover, turf/target, height) + if(istype(mover) && mover.checkpass(PASSMOB)) + return TRUE + else + return ..() + /obj/vehicle/examine(mob/user) . = ..() if(key_type) diff --git a/config/example/config.txt b/config/example/config.txt index 5934ea733b5..3a56570a03d 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -277,9 +277,6 @@ TICKLAG 0.5 ## Whether the server will talk to other processes through socket_talk SOCKET_TALK 0 -## Uncomment this to ban use of ToR -#TOR_BAN - ## Comment this out to disable automuting #AUTOMUTE_ON diff --git a/config/names/dreams.txt b/config/names/dreams.txt index 89f559dae88..4bd2c2b984e 100644 --- a/config/names/dreams.txt +++ b/config/names/dreams.txt @@ -105,7 +105,7 @@ a grey a kidan a diona a drask -the ai core +the AI core the mining station the research station a beaker of strange liquid diff --git a/config/names/nightmares.txt b/config/names/nightmares.txt index 71df2e90cff..022a0494edd 100644 --- a/config/names/nightmares.txt +++ b/config/names/nightmares.txt @@ -10,7 +10,7 @@ a dead grey a dead kidan a dead diona a dead drask -the malf ai core +the malf AI core bLoOd has been called a horrible sense of dread comes over you diff --git a/goon/browserassets/css/browserOutput-dark.css b/goon/browserassets/css/browserOutput-dark.css index e7b79557619..f043b16c0ad 100644 --- a/goon/browserassets/css/browserOutput-dark.css +++ b/goon/browserassets/css/browserOutput-dark.css @@ -38,7 +38,7 @@ a.popt {text-decoration: none;} * CUSTOM FONTS * ******************************************/ -@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); } +@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); } /***************************************** * @@ -250,6 +250,7 @@ em {font-style: normal; font-weight: bold;} {color: #638500; text-decoration: underline;} .motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover {color: #638500;} +.darkmblue {color: #6685f5;} .prefix { font-weight: bold;} .ooc { font-weight: bold;} .looc {color: #6699CC;} @@ -261,32 +262,32 @@ em {font-style: normal; font-weight: bold;} .mentorhelp {color: #0077bb; font-weight: bold;} .adminhelp {color: #aa0000; font-weight: bold;} .playerreply {color: #8800bb; font-weight: bold;} -.pmsend {color: #0000ff;} +.pmsend {color: #6685f5;} .name { font-weight: bold;} .say {} .yell { font-weight: bold;} .siliconsay {font-family: 'Courier New', Courier, monospace;} -.deadsay {color: #cc00c6;} +.deadsay {color: #B800B1;} .radio {color: #408010;} .deptradio {color: #993399;} -.comradio {color: #2040ff;} +.comradio {color: #526aff;} .syndradio {color: #993F40;} .dsquadradio {color: #998599;} .resteamradio {color: #18BC46;} -.airadio {color: #FF00FF;} -.centradio {color: #5C5C7C;} +.airadio {color: #FF94FF;} +.centradio {color: #78789B;} .secradio {color: #CF0000;} .engradio {color: #A66300;} .medradio {color: #009190;} .sciradio {color: #993399;} .supradio {color: #9F8545;} .srvradio {color: #80A000;} -.admin_channel {color: #9A04D1; font-weight: bold;} +.admin_channel {color: #fcba03; font-weight: bold;} .mentor_channel {color: #775BFF; font-weight: bold;} .mentor_channel_admin {color: #A35CFF; font-weight: bold;} .djradio {color: #996600;} .binaryradio {color: #1B00FB; font-family: 'Courier New', Courier, monospace;} -.mommiradio {color: #1B00AB;} +.mommiradio {color: #6685f5;} .alert {color: #ff0000;} h1.alert, h2.alert {color: #FFF;} .ghostalert {color: #cc00c6; font-style: italic; font-weight: bold;} @@ -304,10 +305,9 @@ h1.alert, h2.alert {color: #FFF;} .userdanger {color: #ff0000; font-weight: bold; font-size: 120%;} .biggerdanger {color: #ff0000; font-weight: bold; font-size: 150%;} -.info {color: #0044DD;} -.notice {color: #0044DD;} -.bnotice {color: #0044DD; font-weight: bold;} -.boldnotice {color: #0044DD; font-weight: bold;} +.info {color: #6685f5;} +.notice {color: #6685f5;} +.boldnotice {color: #6685f5; font-weight: bold;} .suicide {color: #ff5050; font-style: italic;} .green {color: #03bb39;} .announce {color: #228b22; font-weight: bold;} @@ -327,7 +327,7 @@ h1.alert, h2.alert {color: #FFF;} .sans {font-family: 'Comic Sans MS', cursive, sans-serif;} .wingdings {font-family: Wingdings, Webdings;} .robot {font-family: 'PxPlus IBM MDA'; font-size: 1.15em;} -.ancient {color: #008B8B; font-stye: italic;} +.ancient {color: #008B8B; font-style: italic;} .newscaster {color: #CC0000;} .mod {color: #735638; font-weight: bold;} .modooc {color: #184880; font-weight: bold;} @@ -358,7 +358,7 @@ h1.alert, h2.alert {color: #FFF;} .cultlarge {color: #A60000; font-weight: bold; font-size: 120%;} .narsie {color: #A60000; font-weight: bold; font-size: 300%;} .narsiesmall {color: #A60000; font-weight: bold; font-size: 200%;} -.interface {color: #330033;} +.interface {color: #9031C4;} .big {font-size: 150%;} .reallybig {font-size: 175%;} .greentext {color: #00FF00; font-size: 150%;} @@ -366,7 +366,7 @@ h1.alert, h2.alert {color: #FFF;} .bold {font-weight: bold;} .center {text-align: center;} .red {color: #FF0000;} -.purple {color: #5e2d79;} +.purple {color: #9031C4;} .skeleton {color: #C8C8C8; font-weight: bold; font-style: italic;} .gutter {color: #7092BE; font-family: "Trebuchet MS", cursive, sans-serif;} .orange {color: #ffa500;} @@ -374,14 +374,14 @@ h1.alert, h2.alert {color: #FFF;} .orangeb {color: #ffa500; font-weight: bold;} .resonate {color: #298F85;} -.revennotice {color: #1d29C3;} -.revenboldnotice {color: #1d29C3; font-weight: bold;} -.revenbignotice {color: #1d29C3; font-weight: bold; font-size: 120%;} +.revennotice {color: #6685F5;} +.revenboldnotice {color: #6685F5; font-weight: bold;} +.revenbignotice {color: #6685F5; font-weight: bold; font-size: 120%;} .revenminor {color: #823abb} .revenwarning {color: #760fbb; font-style: italic;} .revendanger {color: #760fbb; font-weight: bold; font-size: 120%;} -.specialnotice {color: #36525e; font-weight: bold; font-size: 120%;} +.specialnotice {color: #4A6F82; font-weight: bold; font-size: 120%;} /* /vg/ */ .good {color: green;} @@ -398,7 +398,11 @@ h1.alert, h2.alert {color: #FFF;} .connectionClosed, .fatalError {background: red; color: white; padding: 5px;} .connectionClosed.restored {background: green;} -.internal.boldnshit {color: blue; font-weight: bold;} +.internal.boldnshit {color: #6685f5; font-weight: bold;} + +.rebooting {background: #2979AF; color: white; padding: 5px;} +.rebooting a {color: white !important; text-decoration-color: white !important;} +#reconnectTimer {font-weight: bold;} /* HELPER CLASSES */ .text-normal {font-weight: normal; font-style: normal;} diff --git a/goon/browserassets/css/browserOutput.css b/goon/browserassets/css/browserOutput.css index 962ce7bb8e7..1ca6700520a 100644 --- a/goon/browserassets/css/browserOutput.css +++ b/goon/browserassets/css/browserOutput.css @@ -37,7 +37,7 @@ a.popt {text-decoration: none;} * CUSTOM FONTS * ******************************************/ -@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); } +@font-face { font-family: PxPlus IBM MDA; src: url('PxPlus_IBM_MDA.ttf'); } /***************************************** * @@ -247,6 +247,7 @@ em {font-style: normal; font-weight: bold;} {color: #638500; text-decoration: underline;} .motd a, .motd a:link, .motd a:visited, .motd a:active, .motd a:hover {color: #638500;} +.darkmblue {color: #0000ff;} .prefix { font-weight: bold;} .ooc { font-weight: bold;} .looc {color: #6699CC;} @@ -303,7 +304,6 @@ h1.alert, h2.alert {color: #000000;} .info {color: #0000CC;} .notice {color: #000099;} -.bnotice {color: #000099; font-weight: bold;} .boldnotice {color: #000099; font-weight: bold;} .suicide {color: #ff5050; font-style: italic;} .green {color: #03bb39;} @@ -324,7 +324,7 @@ h1.alert, h2.alert {color: #000000;} .sans {font-family: 'Comic Sans MS', cursive, sans-serif;} .wingdings {font-family: Wingdings, Webdings;} .robot {font-family: 'PxPlus IBM MDA'; font-size: 1.15em;} -.ancient {color: #008B8B; font-stye: italic;} +.ancient {color: #008B8B; font-style: italic;} .newscaster {color: #800000;} .mod {color: #735638; font-weight: bold;} .modooc {color: #184880; font-weight: bold;} @@ -399,6 +399,10 @@ h1.alert, h2.alert {color: #000000;} .connectionClosed.restored {background: green;} .internal.boldnshit {color: blue; font-weight: bold;} +.rebooting {background: #2979AF; color: white; padding: 5px;} +.rebooting a {color: white !important; text-decoration-color: white !important;} +#reconnectTimer {font-weight: bold;} + /* HELPER CLASSES */ .text-normal {font-weight: normal; font-style: normal;} .hidden {display: none; visibility: hidden;} diff --git a/goon/browserassets/js/browserOutput.js b/goon/browserassets/js/browserOutput.js index 40a69245188..414a5d644b6 100644 --- a/goon/browserassets/js/browserOutput.js +++ b/goon/browserassets/js/browserOutput.js @@ -69,10 +69,13 @@ var opts = { 'macros': {}, // Emoji toggle - 'enableEmoji': true + 'enableEmoji': true, + + // Reboot message stuff + 'rebootIntervalHandler': null }; -var regexHasError = false; //variable to check if regex has excepted +var regexHasError = false; //variable to check if regex has excepted function outerHTML(el) { var wrap = document.createElement('div'); @@ -97,10 +100,10 @@ if (typeof String.prototype.trim !== 'function') { if (!String.prototype.includes) { String.prototype.includes = function(search, start) { 'use strict'; - + if (search instanceof RegExp) { throw TypeError('first argument must not be a RegExp'); - } + } if (start === undefined) { start = 0; } return this.indexOf(search, start) !== -1; }; @@ -124,7 +127,7 @@ function byondDecode(message) { // The replace for + is because FOR SOME REASON, BYOND replaces spaces with a + instead of %20, and a plus with %2b. // Marvelous. message = message.replace(/\+/g, "%20"); - try { + try { // This is a workaround for the above not always working when BYOND's shitty url encoding breaks. // Basically, sometimes BYOND's double encoding trick just arbitrarily produces something that makes decodeURIComponent // throw an "Invalid Encoding URI" URIError... the simplest way to work around this is to just ignore it and use unescape instead @@ -166,30 +169,79 @@ function emojiparse(el) { } } -// Colorizes the highlight spans -function setHighlightColor(match) { - match.style.background = opts.highlightColor +// Recolorizes the highlight spans +function setHighlightColor() { + var highlightspans = document.getElementsByClassName("highlight") + for(var i in highlightspans){ + highlightspans[i].setAttribute("style","background-color:"+opts.highlightColor) + } +} + +function escapeRegexCharacters(input){ //escapes any characters that could be interpreted as regex patterns, potentially causing patterns to break if not escaped + return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } //Highlights words based on user settings function highlightTerms(el) { - if(regexHasError) return; //just stop right there ig the regex is gonna except + + if (regexHasError) return; //just stop right there ig the regex is gonna except + + function highlightRecursor(element, term){ //recursor function to do the highlighting proper + var regex = new RegExp(term, "gi"); + + function replace(str) { + return str.replace(regex, '$&'); + } + + var s = ''; + var work = element.innerHTML; + var ind = 0; + + while(ind < work.length) { + + var next_term = work.substring(ind).search(regex); + if(next_term != -1) next_term += ind; + var next_tag = work.indexOf('<', ind); + if(next_tag == -1) { + s+=replace(work.substring(ind)); + break; + } + else if(next_term==-1) { + s += work.substring(ind); + break; + } + else if(next_tag < next_term) { + var temp = work.indexOf('>', next_tag); + s += work.substring(ind,temp+1); + ind = temp+1; + } + else { + s += replace(work.substring(ind, next_tag)); + ind = next_tag; + } + } + + element.innerHTML = s; + } + for (var i = 0; i < opts.highlightTerms.length; i++) { //Each highlight term if(opts.highlightTerms[i]) { if(!opts.highlightRegexEnable){ - if(el.innerText.toString().toLowerCase().includes(opts.highlightTerms[i].toLowerCase())) //match normally - el.innerHTML = ''+el.innerHTML+'' //encloseincludes - continue; + var innerTerms = opts.highlightTerms[i].split(" ") + for(var a in innerTerms){ + highlightRecursor(el, escapeRegexCharacters(innerTerms[a])) + } } - var rexp; - try{ - rexp = new RegExp(opts.highlightTerms[i],"gmi") - } catch(e){ - el.innerHTML+='
    Your highlight regex - '+opts.highlightTerms[i]+' - is malformed. Thrown exception: '+e+'' - regexHasError = true; - return; + else { + try{ + new RegExp(opts.highlightTerms[i], "gmi"); // check to make sure the pattern wont cause issues + } catch(e){ + el.innerHTML += '
    Your highlight regex pattern -- ' + opts.highlightTerms[i] + ' -- is malformed.
    Your highlights have been disabled until they are next edited
    Thrown exception: '+e+'
    '; + regexHasError = true; + return; + } + highlightRecursor(el, opts.highlightTerms[i]); } - el.innerHTML = el.innerHTML.replace(rexp,"$0") //i cant figure out a proper, non snowflakey way to let people select the group that gets highlighted } } } @@ -524,6 +576,34 @@ function toggleWasd(state) { opts.wasd = (state == 'on' ? true : false); } +function reboot(timeRaw) { + var timeLeftSecs = parseInt(timeRaw); + const intervalSecs = 1; // tick every 1 second + + rebootFinished(); + internalOutput('
    The server is restarting. Reconnect (' + timeLeftSecs + ')
    ', 'internal'); + + opts.rebootIntervalHandler = setInterval(function() { + timeLeftSecs -= intervalSecs; + if (timeLeftSecs <= 0) { + $("#reconnectTimer").text('Reconnecting...'); + window.location.href = 'byond://winset?command=.reconnect'; + clearInterval(opts.rebootIntervalHandler) + opts.rebootIntervalHandler = null; + } else { + $("#reconnectTimer").text('Reconnect (' + timeLeftSecs + ')'); + } + }, intervalSecs * 1000); +} + +function rebootFinished() { + if (opts.rebootIntervalHandler != null) { + clearInterval(opts.rebootIntervalHandler) + } + $(" Reconnected automatically!").insertBefore("#reconnectTimer"); + $("#reconnectTimer").remove(); +} + /***************************************** * * MAKE MACRO DICTIONARY @@ -603,7 +683,7 @@ $(function() { 'shideSpam': getCookie('hidespam'), 'darkChat': getCookie('darkChat'), }; - + if (savedConfig.sfontSize) { $messages.css('font-size', savedConfig.sfontSize); internalOutput('Loaded font size setting of: '+savedConfig.sfontSize+'', 'internal'); @@ -961,18 +1041,18 @@ $(function() { } else { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } - + // synchronous requests are depricated in modern browsers - xmlHttp.open('GET', 'browserOutput.css', true); + xmlHttp.open('GET', 'browserOutput.css', true); xmlHttp.onload = function (e) { if (xmlHttp.status === 200) { // request successful - + // Generate Log var saved = ''; saved += $messages.html(); saved = saved.replace(/&/g, '&'); saved = saved.replace(/Highlights have been updated.',"internal") // simplest way to test if pattern works, why reinvent the wheel? var $popup = $('#highlightPopup').closest('.popup'); $popup.remove(); diff --git a/goon/code/datums/browserOutput.dm b/goon/code/datums/browserOutput.dm index 74c9d844728..b073bece409 100644 --- a/goon/code/datums/browserOutput.dm +++ b/goon/code/datums/browserOutput.dm @@ -109,6 +109,7 @@ var/list/chatResources = list( loaded = TRUE winset(owner, "browseroutput", "is-disabled=false") + owner << output(null, "browseroutput:rebootFinished") if(owner.holder) loadAdmin() for(var/message in messageQueue) @@ -245,8 +246,8 @@ var/list/chatResources = list( var/to_chat_filename var/to_chat_line var/to_chat_src -// Call using macro: to_chat(target, message, flag) -/proc/to_chat_immediate(target, message, flag) + +/proc/to_chat(target, message, flag) if(!is_valid_tochat_message(message) || !is_valid_tochat_target(target)) target << message @@ -304,17 +305,4 @@ var/to_chat_src target << output(output_message, "browseroutput:output") -/proc/to_chat(target, message, flag) - /* - If any of the following conditions are met, do NOT use SSchat. These conditions include: - - Is the MC still initializing? - - Has SSchat initialized? - - Has SSchat been offlined due to MC crashes? - If any of these are met, use the old chat system, otherwise people wont see messages - */ - if(Master.current_runlevel == RUNLEVEL_INIT || !SSchat?.initialized || SSchat?.flags & SS_NO_FIRE) - to_chat_immediate(target, message, flag) - return - SSchat.queue(target, message, flag) - #undef MAX_COOKIE_LENGTH diff --git a/html/browser/marked-paradise.js b/html/browser/marked-paradise.js index 03519139409..a92cadb1cfe 100644 --- a/html/browser/marked-paradise.js +++ b/html/browser/marked-paradise.js @@ -4,24 +4,22 @@ var $ = document.querySelector.bind(document); function parse(node) { - for (var i = 0; i < node.childNodes.length; i++) { - parse(node.childNodes[i]); - } - - if (!node.innerHTML) { - return; - } - - node.innerHTML = marked(node.innerHTML.replace(/
    /gi, '\n').replace(/\t/gi, ''), { breaks: false, gfm: false }) - // marked.js wraps content into

    tags, which is looks atrocious when we call it recursively. - // The following line unwraps it. - if (node.children.length == 1) { - node.innerHTML = node.children[0].innerHTML; - } + for (var i = 0; i < node.childNodes.length; i++) + parse(node.childNodes[i]); + + if (!node.innerHTML) + return; + + if (node.children.length == 0) { + node.innerHTML = marked(node.innerHTML.replace(/
    /gi, '\n').replace(/\t/gi, ''), { breaks: false, gfm: false }); + // marked.js wraps content into

    tags, which is looks atrocious when we call it recursively. + // The following line unwraps it. + if (node.children.length == 1 && node.children[0].tagName == "P") + node.innerHTML = node.children[0].innerHTML; + } } - + window.onload = function() { - if ($('#markdown')) { - parse($('#markdown')); - } + if ($('#markdown')) + parse($('#markdown')); } diff --git a/icons/_nanomaps/Cyberiad_nanomap_z1.png b/icons/_nanomaps/Cyberiad_nanomap_z1.png new file mode 100644 index 00000000000..fb87632863e Binary files /dev/null and b/icons/_nanomaps/Cyberiad_nanomap_z1.png differ diff --git a/icons/_nanomaps/Delta_nanomap_z1.png b/icons/_nanomaps/Delta_nanomap_z1.png new file mode 100644 index 00000000000..23a31d17241 Binary files /dev/null and b/icons/_nanomaps/Delta_nanomap_z1.png differ diff --git a/icons/_nanomaps/MetaStation_nanomap_z1.png b/icons/_nanomaps/MetaStation_nanomap_z1.png new file mode 100644 index 00000000000..77a34cd046a Binary files /dev/null and b/icons/_nanomaps/MetaStation_nanomap_z1.png differ diff --git a/icons/atmos/omni_devices.dmi b/icons/atmos/omni_devices.dmi deleted file mode 100644 index 3e7325b0556..00000000000 Binary files a/icons/atmos/omni_devices.dmi and /dev/null differ diff --git a/icons/atmos/vent_scrubber.dmi b/icons/atmos/vent_scrubber.dmi index c6b6d6ccfe4..628a17e8565 100644 Binary files a/icons/atmos/vent_scrubber.dmi and b/icons/atmos/vent_scrubber.dmi differ diff --git a/icons/effects/lasers2.dmi b/icons/effects/lasers2.dmi deleted file mode 100644 index f774fb9430b..00000000000 Binary files a/icons/effects/lasers2.dmi and /dev/null differ diff --git a/icons/effects/projectile.dmi b/icons/effects/projectile.dmi new file mode 100644 index 00000000000..48e25c6fe8d Binary files /dev/null and b/icons/effects/projectile.dmi differ diff --git a/icons/goonstation/objects/pda_overlay.dmi b/icons/goonstation/objects/pda_overlay.dmi index 4c7cb24a7f4..74be20fc981 100644 Binary files a/icons/goonstation/objects/pda_overlay.dmi and b/icons/goonstation/objects/pda_overlay.dmi differ diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi index 4ea648dea13..011baf1e4ac 100644 Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi index e55b06fe8f1..d3253d3e1fb 100644 Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ diff --git a/icons/mob/belt.dmi b/icons/mob/belt.dmi index f9c59529b28..f0579eae59b 100644 Binary files a/icons/mob/belt.dmi and b/icons/mob/belt.dmi differ diff --git a/icons/mob/corgi_head.dmi b/icons/mob/corgi_head.dmi index df2f3a66d84..7492b40833d 100644 Binary files a/icons/mob/corgi_head.dmi and b/icons/mob/corgi_head.dmi differ diff --git a/icons/mob/feet.dmi b/icons/mob/feet.dmi index e6f132a73a5..8257a4ffe01 100644 Binary files a/icons/mob/feet.dmi and b/icons/mob/feet.dmi differ diff --git a/icons/mob/guardian.dmi b/icons/mob/guardian.dmi index a10e1e0b0ec..e10a5c618ea 100644 Binary files a/icons/mob/guardian.dmi and b/icons/mob/guardian.dmi differ diff --git a/icons/mob/hands.dmi b/icons/mob/hands.dmi index 6d6165509c8..66d1474d61a 100644 Binary files a/icons/mob/hands.dmi and b/icons/mob/hands.dmi differ diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index 88b28aff5a9..5221aec971f 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi index df6b8f4de1f..07593d09c0c 100644 Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi index ff471760fe8..9b6d9468b9d 100644 Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ diff --git a/icons/mob/human_races/r_skeleton.dmi b/icons/mob/human_races/r_skeleton.dmi index a350c3f4c4d..6f98807254b 100644 Binary files a/icons/mob/human_races/r_skeleton.dmi and b/icons/mob/human_races/r_skeleton.dmi differ diff --git a/icons/mob/inhands/clothing_lefthand.dmi b/icons/mob/inhands/clothing_lefthand.dmi index 589135b8701..9fce742e0bf 100644 Binary files a/icons/mob/inhands/clothing_lefthand.dmi and b/icons/mob/inhands/clothing_lefthand.dmi differ diff --git a/icons/mob/inhands/clothing_righthand.dmi b/icons/mob/inhands/clothing_righthand.dmi index 8fd5f7d71b8..772f8af73e4 100644 Binary files a/icons/mob/inhands/clothing_righthand.dmi and b/icons/mob/inhands/clothing_righthand.dmi differ diff --git a/icons/mob/inhands/equipment/belt_lefthand.dmi b/icons/mob/inhands/equipment/belt_lefthand.dmi index 366493eebde..febcdb854b8 100644 Binary files a/icons/mob/inhands/equipment/belt_lefthand.dmi and b/icons/mob/inhands/equipment/belt_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/belt_righthand.dmi b/icons/mob/inhands/equipment/belt_righthand.dmi index 81b075f706e..d7b394cc09b 100644 Binary files a/icons/mob/inhands/equipment/belt_righthand.dmi and b/icons/mob/inhands/equipment/belt_righthand.dmi differ diff --git a/icons/mob/inhands/equipment/instruments_lefthand.dmi b/icons/mob/inhands/equipment/instruments_lefthand.dmi index 225f1768d99..aa3608fec28 100644 Binary files a/icons/mob/inhands/equipment/instruments_lefthand.dmi and b/icons/mob/inhands/equipment/instruments_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/instruments_righthand.dmi b/icons/mob/inhands/equipment/instruments_righthand.dmi index cc1df97183a..28085ff746d 100644 Binary files a/icons/mob/inhands/equipment/instruments_righthand.dmi and b/icons/mob/inhands/equipment/instruments_righthand.dmi differ diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index a378d2e5b04..240558ebb80 100644 Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index 0a8847d7e4a..a3743efa33b 100644 Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ diff --git a/icons/mob/map_backgrounds.dmi b/icons/mob/map_backgrounds.dmi new file mode 100644 index 00000000000..dc6e3e46b16 Binary files /dev/null and b/icons/mob/map_backgrounds.dmi differ diff --git a/icons/mob/rig_back.dmi b/icons/mob/rig_back.dmi deleted file mode 100644 index 923939d323e..00000000000 Binary files a/icons/mob/rig_back.dmi and /dev/null differ diff --git a/icons/mob/rig_modules.dmi b/icons/mob/rig_modules.dmi deleted file mode 100644 index 3d18b5435ce..00000000000 Binary files a/icons/mob/rig_modules.dmi and /dev/null differ diff --git a/icons/mob/robots.dmi b/icons/mob/robots.dmi index dcbce35fb27..738fb1e0933 100644 Binary files a/icons/mob/robots.dmi and b/icons/mob/robots.dmi differ diff --git a/icons/mob/screen_midnight.dmi b/icons/mob/screen_midnight.dmi index a5f9caa6a90..8d1f1648cf5 100644 Binary files a/icons/mob/screen_midnight.dmi and b/icons/mob/screen_midnight.dmi differ diff --git a/icons/mob/screen_ninja.dmi b/icons/mob/screen_ninja.dmi index 0f527241e0d..df30d63fe76 100644 Binary files a/icons/mob/screen_ninja.dmi and b/icons/mob/screen_ninja.dmi differ diff --git a/icons/mob/screen_operative.dmi b/icons/mob/screen_operative.dmi index 0bb1a0b9d44..4abe226fa84 100644 Binary files a/icons/mob/screen_operative.dmi and b/icons/mob/screen_operative.dmi differ diff --git a/icons/mob/screen_plasmafire.dmi b/icons/mob/screen_plasmafire.dmi index 81453caddef..a9f2a25bd1a 100644 Binary files a/icons/mob/screen_plasmafire.dmi and b/icons/mob/screen_plasmafire.dmi differ diff --git a/icons/mob/screen_retro.dmi b/icons/mob/screen_retro.dmi index 1568e701222..0f2d9ed91e0 100644 Binary files a/icons/mob/screen_retro.dmi and b/icons/mob/screen_retro.dmi differ diff --git a/icons/mob/screen_slimecore.dmi b/icons/mob/screen_slimecore.dmi index 3b2392cb8fc..2cbc53e7ba1 100644 Binary files a/icons/mob/screen_slimecore.dmi and b/icons/mob/screen_slimecore.dmi differ diff --git a/icons/mob/screen_white.dmi b/icons/mob/screen_white.dmi index 361611cf4e0..ab65366bb52 100644 Binary files a/icons/mob/screen_white.dmi and b/icons/mob/screen_white.dmi differ diff --git a/icons/mob/species/drask/head.dmi b/icons/mob/species/drask/head.dmi index b9f0604c960..ca15dea1cb4 100644 Binary files a/icons/mob/species/drask/head.dmi and b/icons/mob/species/drask/head.dmi differ diff --git a/icons/mob/species/drask/shoes.dmi b/icons/mob/species/drask/shoes.dmi index e51947fdd35..53664dd3d70 100644 Binary files a/icons/mob/species/drask/shoes.dmi and b/icons/mob/species/drask/shoes.dmi differ diff --git a/icons/mob/species/drask/suit.dmi b/icons/mob/species/drask/suit.dmi index d75a28f2bee..cef6dbdf836 100644 Binary files a/icons/mob/species/drask/suit.dmi and b/icons/mob/species/drask/suit.dmi differ diff --git a/icons/mob/species/grey/head.dmi b/icons/mob/species/grey/head.dmi index 4ff783bbb74..c059a3bff9d 100644 Binary files a/icons/mob/species/grey/head.dmi and b/icons/mob/species/grey/head.dmi differ diff --git a/icons/mob/species/grey/helmet.dmi b/icons/mob/species/grey/helmet.dmi index cf244cec6b2..3e592369adc 100644 Binary files a/icons/mob/species/grey/helmet.dmi and b/icons/mob/species/grey/helmet.dmi differ diff --git a/icons/mob/species/grey/mask.dmi b/icons/mob/species/grey/mask.dmi index cbbd722ae69..348a39c0f03 100644 Binary files a/icons/mob/species/grey/mask.dmi and b/icons/mob/species/grey/mask.dmi differ diff --git a/icons/mob/species/grey/suit.dmi b/icons/mob/species/grey/suit.dmi index 3070f6627ea..c22c9daf3dd 100644 Binary files a/icons/mob/species/grey/suit.dmi and b/icons/mob/species/grey/suit.dmi differ diff --git a/icons/mob/species/grey/uniform.dmi b/icons/mob/species/grey/uniform.dmi index c956187ffa1..7a74f930598 100644 Binary files a/icons/mob/species/grey/uniform.dmi and b/icons/mob/species/grey/uniform.dmi differ diff --git a/icons/mob/species/kidan/head.dmi b/icons/mob/species/kidan/head.dmi new file mode 100644 index 00000000000..83818995f69 Binary files /dev/null and b/icons/mob/species/kidan/head.dmi differ diff --git a/icons/mob/species/skrell/head.dmi b/icons/mob/species/skrell/head.dmi index ec5e8dd62e4..5ffe836ffc2 100644 Binary files a/icons/mob/species/skrell/head.dmi and b/icons/mob/species/skrell/head.dmi differ diff --git a/icons/mob/species/skrell/helmet.dmi b/icons/mob/species/skrell/helmet.dmi index e17fa4174c5..e54cdb52d0f 100644 Binary files a/icons/mob/species/skrell/helmet.dmi and b/icons/mob/species/skrell/helmet.dmi differ diff --git a/icons/mob/species/tajaran/helmet.dmi b/icons/mob/species/tajaran/helmet.dmi index 298ff823ea4..00c3f1d2bdc 100644 Binary files a/icons/mob/species/tajaran/helmet.dmi and b/icons/mob/species/tajaran/helmet.dmi differ diff --git a/icons/mob/species/tajaran/suit.dmi b/icons/mob/species/tajaran/suit.dmi index 2f7d8f34907..61929dcceda 100644 Binary files a/icons/mob/species/tajaran/suit.dmi and b/icons/mob/species/tajaran/suit.dmi differ diff --git a/icons/mob/species/unathi/helmet.dmi b/icons/mob/species/unathi/helmet.dmi index 1fc21dd1611..68f6171bb19 100644 Binary files a/icons/mob/species/unathi/helmet.dmi and b/icons/mob/species/unathi/helmet.dmi differ diff --git a/icons/mob/species/unathi/suit.dmi b/icons/mob/species/unathi/suit.dmi index 14d1eaaa73a..eabd0f94672 100644 Binary files a/icons/mob/species/unathi/suit.dmi and b/icons/mob/species/unathi/suit.dmi differ diff --git a/icons/mob/species/vox/head.dmi b/icons/mob/species/vox/head.dmi index 6c4b2b43f6a..fb5d153d30d 100644 Binary files a/icons/mob/species/vox/head.dmi and b/icons/mob/species/vox/head.dmi differ diff --git a/icons/mob/species/vox/shoes.dmi b/icons/mob/species/vox/shoes.dmi index 99eac81e3a9..f04249274b3 100644 Binary files a/icons/mob/species/vox/shoes.dmi and b/icons/mob/species/vox/shoes.dmi differ diff --git a/icons/mob/species/vox/suit.dmi b/icons/mob/species/vox/suit.dmi index 730149860d5..4161227ac79 100644 Binary files a/icons/mob/species/vox/suit.dmi and b/icons/mob/species/vox/suit.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index d71fb9b1d21..c7df6f275aa 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/obj/ammo.dmi b/icons/obj/ammo.dmi index 4a250a1fffc..b1876a7d063 100644 Binary files a/icons/obj/ammo.dmi and b/icons/obj/ammo.dmi differ diff --git a/icons/obj/atmos.dmi b/icons/obj/atmos.dmi index 2209f3bd769..b2668980aec 100644 Binary files a/icons/obj/atmos.dmi and b/icons/obj/atmos.dmi differ diff --git a/icons/obj/atmospherics/blue_pipe_tank.dmi b/icons/obj/atmospherics/blue_pipe_tank.dmi deleted file mode 100644 index 3d2ee4c9d69..00000000000 Binary files a/icons/obj/atmospherics/blue_pipe_tank.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/digital_valve.dmi b/icons/obj/atmospherics/digital_valve.dmi deleted file mode 100644 index 136523b7223..00000000000 Binary files a/icons/obj/atmospherics/digital_valve.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/dp_vent_pump.dmi b/icons/obj/atmospherics/dp_vent_pump.dmi deleted file mode 100644 index 86dae2cf435..00000000000 Binary files a/icons/obj/atmospherics/dp_vent_pump.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/filter.dmi b/icons/obj/atmospherics/filter.dmi deleted file mode 100644 index 99afb55d22b..00000000000 Binary files a/icons/obj/atmospherics/filter.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/mainspipe.dmi b/icons/obj/atmospherics/mainspipe.dmi deleted file mode 100644 index df6c2bc0d3a..00000000000 Binary files a/icons/obj/atmospherics/mainspipe.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/n2o_pipe_tank.dmi b/icons/obj/atmospherics/n2o_pipe_tank.dmi deleted file mode 100644 index 40efbd9f15d..00000000000 Binary files a/icons/obj/atmospherics/n2o_pipe_tank.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/omni_devices.dmi b/icons/obj/atmospherics/omni_devices.dmi deleted file mode 100644 index 44497310026..00000000000 Binary files a/icons/obj/atmospherics/omni_devices.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/orange_pipe_tank.dmi b/icons/obj/atmospherics/orange_pipe_tank.dmi deleted file mode 100644 index cc9442b8033..00000000000 Binary files a/icons/obj/atmospherics/orange_pipe_tank.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/passive_gate.dmi b/icons/obj/atmospherics/passive_gate.dmi deleted file mode 100644 index 42e8c9dc74f..00000000000 Binary files a/icons/obj/atmospherics/passive_gate.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/pipe_manifold.dmi b/icons/obj/atmospherics/pipe_manifold.dmi deleted file mode 100644 index 1e6f5750d1b..00000000000 Binary files a/icons/obj/atmospherics/pipe_manifold.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/pipe_tank.dmi b/icons/obj/atmospherics/pipe_tank.dmi deleted file mode 100644 index 72310a2657d..00000000000 Binary files a/icons/obj/atmospherics/pipe_tank.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/pipe_vent.dmi b/icons/obj/atmospherics/pipe_vent.dmi deleted file mode 100644 index e40f5946cf8..00000000000 Binary files a/icons/obj/atmospherics/pipe_vent.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/portables_connector.dmi b/icons/obj/atmospherics/portables_connector.dmi deleted file mode 100644 index c651b959990..00000000000 Binary files a/icons/obj/atmospherics/portables_connector.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/pump.dmi b/icons/obj/atmospherics/pump.dmi deleted file mode 100644 index e44a21991ba..00000000000 Binary files a/icons/obj/atmospherics/pump.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/red_orange_pipe_tank.dmi b/icons/obj/atmospherics/red_orange_pipe_tank.dmi deleted file mode 100644 index 1770c46312a..00000000000 Binary files a/icons/obj/atmospherics/red_orange_pipe_tank.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/red_pipe_tank.dmi b/icons/obj/atmospherics/red_pipe_tank.dmi deleted file mode 100644 index 3fedc659e03..00000000000 Binary files a/icons/obj/atmospherics/red_pipe_tank.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/relief_valve.dmi b/icons/obj/atmospherics/relief_valve.dmi deleted file mode 100644 index 485bf792b19..00000000000 Binary files a/icons/obj/atmospherics/relief_valve.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/valve.dmi b/icons/obj/atmospherics/valve.dmi deleted file mode 100644 index 9ce45199bd7..00000000000 Binary files a/icons/obj/atmospherics/valve.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/vent_pump.dmi b/icons/obj/atmospherics/vent_pump.dmi deleted file mode 100644 index 6b1f57baa44..00000000000 Binary files a/icons/obj/atmospherics/vent_pump.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/vent_scrubber.dmi b/icons/obj/atmospherics/vent_scrubber.dmi deleted file mode 100644 index 7133072cd79..00000000000 Binary files a/icons/obj/atmospherics/vent_scrubber.dmi and /dev/null differ diff --git a/icons/obj/atmospherics/volume_pump.dmi b/icons/obj/atmospherics/volume_pump.dmi deleted file mode 100644 index e3ff3cf9854..00000000000 Binary files a/icons/obj/atmospherics/volume_pump.dmi and /dev/null differ diff --git a/icons/obj/bureaucracy.dmi b/icons/obj/bureaucracy.dmi index 1847e985d89..3f66fa90cd6 100644 Binary files a/icons/obj/bureaucracy.dmi and b/icons/obj/bureaucracy.dmi differ diff --git a/icons/obj/card.dmi b/icons/obj/card.dmi index 0dbab70dd32..48f2080ea98 100644 Binary files a/icons/obj/card.dmi and b/icons/obj/card.dmi differ diff --git a/icons/obj/cardboard_cutout.dmi b/icons/obj/cardboard_cutout.dmi index da5f58d7f7c..63f65c7d07f 100644 Binary files a/icons/obj/cardboard_cutout.dmi and b/icons/obj/cardboard_cutout.dmi differ diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi index f007687d568..05d04330c38 100644 Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ diff --git a/icons/obj/cigarettes.dmi b/icons/obj/cigarettes.dmi index a0133e5b383..97710ac4805 100644 Binary files a/icons/obj/cigarettes.dmi and b/icons/obj/cigarettes.dmi differ diff --git a/icons/obj/clothing/belts.dmi b/icons/obj/clothing/belts.dmi index 2521d36697d..000b6b7f1c3 100644 Binary files a/icons/obj/clothing/belts.dmi and b/icons/obj/clothing/belts.dmi differ diff --git a/icons/obj/clothing/glasses.dmi b/icons/obj/clothing/glasses.dmi index c0501f9bb36..fb4063c1bf4 100644 Binary files a/icons/obj/clothing/glasses.dmi and b/icons/obj/clothing/glasses.dmi differ diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi index f7f54357fdb..06bb96f43a5 100644 Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index f942f9fd8cd..2418b096381 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/clothing/shoes.dmi b/icons/obj/clothing/shoes.dmi index 30cdfae56ef..4c0b649f615 100644 Binary files a/icons/obj/clothing/shoes.dmi and b/icons/obj/clothing/shoes.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index 7400d5d63ef..771b784f6cd 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/clothing/uniforms.dmi b/icons/obj/clothing/uniforms.dmi index bb7a1ce9188..39789326c49 100644 Binary files a/icons/obj/clothing/uniforms.dmi and b/icons/obj/clothing/uniforms.dmi differ diff --git a/icons/obj/computer.dmi b/icons/obj/computer.dmi index ac416274ebe..042b5fe7f38 100644 Binary files a/icons/obj/computer.dmi and b/icons/obj/computer.dmi differ diff --git a/icons/obj/contraband.dmi b/icons/obj/contraband.dmi index 97a11d05cad..444863abd71 100644 Binary files a/icons/obj/contraband.dmi and b/icons/obj/contraband.dmi differ diff --git a/icons/obj/custom_items.dmi b/icons/obj/custom_items.dmi index 2b1c7edfa58..0cf67d92ef9 100644 Binary files a/icons/obj/custom_items.dmi and b/icons/obj/custom_items.dmi differ diff --git a/icons/obj/decals.dmi b/icons/obj/decals.dmi index 7351c8c7483..35ce076db6c 100644 Binary files a/icons/obj/decals.dmi and b/icons/obj/decals.dmi differ diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 75838e00075..668d53ea644 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/icons/obj/food/containers.dmi b/icons/obj/food/containers.dmi index ff25b77ab45..ad31ac04998 100644 Binary files a/icons/obj/food/containers.dmi and b/icons/obj/food/containers.dmi differ diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi index 303ae139574..ba64299a052 100644 Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi index f1d09247bbd..1e61cebb799 100644 Binary files a/icons/obj/guns/projectile.dmi and b/icons/obj/guns/projectile.dmi differ diff --git a/icons/obj/hydroponics/equipment.dmi b/icons/obj/hydroponics/equipment.dmi index 9ad33b40e9d..3cb9e63cb38 100644 Binary files a/icons/obj/hydroponics/equipment.dmi and b/icons/obj/hydroponics/equipment.dmi differ diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi index bc7e927aab2..e30f3e44392 100644 Binary files a/icons/obj/items.dmi and b/icons/obj/items.dmi differ diff --git a/icons/obj/janitor.dmi b/icons/obj/janitor.dmi index 730431590ae..ebc1cca56da 100644 Binary files a/icons/obj/janitor.dmi and b/icons/obj/janitor.dmi differ diff --git a/icons/obj/lighting.dmi b/icons/obj/lighting.dmi index f8943c2c39b..49b594c1656 100644 Binary files a/icons/obj/lighting.dmi and b/icons/obj/lighting.dmi differ diff --git a/icons/obj/modular_console.dmi b/icons/obj/modular_console.dmi deleted file mode 100644 index fba8ad59431..00000000000 Binary files a/icons/obj/modular_console.dmi and /dev/null differ diff --git a/icons/obj/modular_laptop.dmi b/icons/obj/modular_laptop.dmi deleted file mode 100644 index d04e68c2041..00000000000 Binary files a/icons/obj/modular_laptop.dmi and /dev/null differ diff --git a/icons/obj/modular_tablet.dmi b/icons/obj/modular_tablet.dmi deleted file mode 100644 index a6e3223a8d0..00000000000 Binary files a/icons/obj/modular_tablet.dmi and /dev/null differ diff --git a/icons/obj/musician.dmi b/icons/obj/musician.dmi index 21078c58969..d55ecb7ebc0 100644 Binary files a/icons/obj/musician.dmi and b/icons/obj/musician.dmi differ diff --git a/icons/obj/paper.dmi b/icons/obj/paper.dmi deleted file mode 100644 index 55781076833..00000000000 Binary files a/icons/obj/paper.dmi and /dev/null differ diff --git a/icons/obj/pda.dmi b/icons/obj/pda.dmi index b4956c33a9e..5cf13a408d5 100644 Binary files a/icons/obj/pda.dmi and b/icons/obj/pda.dmi differ diff --git a/icons/obj/reagentfillings.dmi b/icons/obj/reagentfillings.dmi index 60eb897b2b1..c36fbf01876 100644 Binary files a/icons/obj/reagentfillings.dmi and b/icons/obj/reagentfillings.dmi differ diff --git a/icons/obj/recycling.dmi b/icons/obj/recycling.dmi index 2a47e6817bb..c30eb425dda 100644 Binary files a/icons/obj/recycling.dmi and b/icons/obj/recycling.dmi differ diff --git a/icons/obj/rig_modules.dmi b/icons/obj/rig_modules.dmi deleted file mode 100644 index 90b1873d58e..00000000000 Binary files a/icons/obj/rig_modules.dmi and /dev/null differ diff --git a/icons/obj/toy.dmi b/icons/obj/toy.dmi index 1ef7016307d..c51d2903b14 100644 Binary files a/icons/obj/toy.dmi and b/icons/obj/toy.dmi differ diff --git a/icons/obj/vending.dmi b/icons/obj/vending.dmi index b39384b9b5b..5be90269399 100755 Binary files a/icons/obj/vending.dmi and b/icons/obj/vending.dmi differ diff --git a/icons/pda_icons/pda_alert.png b/icons/pda_icons/pda_alert.png deleted file mode 100644 index ed35e47800f..00000000000 Binary files a/icons/pda_icons/pda_alert.png and /dev/null differ diff --git a/icons/pda_icons/pda_atmos.png b/icons/pda_icons/pda_atmos.png deleted file mode 100644 index 89a55a0a6cb..00000000000 Binary files a/icons/pda_icons/pda_atmos.png and /dev/null differ diff --git a/icons/pda_icons/pda_back.png b/icons/pda_icons/pda_back.png deleted file mode 100644 index 4708824853f..00000000000 Binary files a/icons/pda_icons/pda_back.png and /dev/null differ diff --git a/icons/pda_icons/pda_bell.png b/icons/pda_icons/pda_bell.png deleted file mode 100644 index 1e989c27473..00000000000 Binary files a/icons/pda_icons/pda_bell.png and /dev/null differ diff --git a/icons/pda_icons/pda_blank.png b/icons/pda_icons/pda_blank.png deleted file mode 100644 index 665861d3c74..00000000000 Binary files a/icons/pda_icons/pda_blank.png and /dev/null differ diff --git a/icons/pda_icons/pda_boom.png b/icons/pda_icons/pda_boom.png deleted file mode 100644 index 70e473c3c4c..00000000000 Binary files a/icons/pda_icons/pda_boom.png and /dev/null differ diff --git a/icons/pda_icons/pda_bucket.png b/icons/pda_icons/pda_bucket.png deleted file mode 100644 index ee030e3a37d..00000000000 Binary files a/icons/pda_icons/pda_bucket.png and /dev/null differ diff --git a/icons/pda_icons/pda_chatroom.png b/icons/pda_icons/pda_chatroom.png deleted file mode 100644 index a00221c4e0d..00000000000 Binary files a/icons/pda_icons/pda_chatroom.png and /dev/null differ diff --git a/icons/pda_icons/pda_clock.PNG b/icons/pda_icons/pda_clock.PNG deleted file mode 100644 index 9abad6c0f3c..00000000000 Binary files a/icons/pda_icons/pda_clock.PNG and /dev/null differ diff --git a/icons/pda_icons/pda_crate.png b/icons/pda_icons/pda_crate.png deleted file mode 100644 index e1e076e2790..00000000000 Binary files a/icons/pda_icons/pda_crate.png and /dev/null differ diff --git a/icons/pda_icons/pda_cuffs.png b/icons/pda_icons/pda_cuffs.png deleted file mode 100644 index 71958c8abc4..00000000000 Binary files a/icons/pda_icons/pda_cuffs.png and /dev/null differ diff --git a/icons/pda_icons/pda_egg.png b/icons/pda_icons/pda_egg.png deleted file mode 100644 index b72bfd97375..00000000000 Binary files a/icons/pda_icons/pda_egg.png and /dev/null differ diff --git a/icons/pda_icons/pda_eject.png b/icons/pda_icons/pda_eject.png deleted file mode 100644 index 4168be03f6a..00000000000 Binary files a/icons/pda_icons/pda_eject.png and /dev/null differ diff --git a/icons/pda_icons/pda_exit.png b/icons/pda_icons/pda_exit.png deleted file mode 100644 index cd983a4a9ab..00000000000 Binary files a/icons/pda_icons/pda_exit.png and /dev/null differ diff --git a/icons/pda_icons/pda_flashlight.png b/icons/pda_icons/pda_flashlight.png deleted file mode 100644 index 3476727930b..00000000000 Binary files a/icons/pda_icons/pda_flashlight.png and /dev/null differ diff --git a/icons/pda_icons/pda_game.png b/icons/pda_icons/pda_game.png deleted file mode 100644 index 3c0ddb8eb0d..00000000000 Binary files a/icons/pda_icons/pda_game.png and /dev/null differ diff --git a/icons/pda_icons/pda_honk.png b/icons/pda_icons/pda_honk.png deleted file mode 100644 index 55632bf40bc..00000000000 Binary files a/icons/pda_icons/pda_honk.png and /dev/null differ diff --git a/icons/pda_icons/pda_locked.PNG b/icons/pda_icons/pda_locked.PNG deleted file mode 100644 index 79fe5829162..00000000000 Binary files a/icons/pda_icons/pda_locked.PNG and /dev/null differ diff --git a/icons/pda_icons/pda_mail.png b/icons/pda_icons/pda_mail.png deleted file mode 100644 index 6bfb1e8cd7b..00000000000 Binary files a/icons/pda_icons/pda_mail.png and /dev/null differ diff --git a/icons/pda_icons/pda_medical.png b/icons/pda_icons/pda_medical.png deleted file mode 100644 index 448063ecf52..00000000000 Binary files a/icons/pda_icons/pda_medical.png and /dev/null differ diff --git a/icons/pda_icons/pda_menu.png b/icons/pda_icons/pda_menu.png deleted file mode 100644 index abd6ccb2257..00000000000 Binary files a/icons/pda_icons/pda_menu.png and /dev/null differ diff --git a/icons/pda_icons/pda_mule.png b/icons/pda_icons/pda_mule.png deleted file mode 100644 index b8c1b636f5e..00000000000 Binary files a/icons/pda_icons/pda_mule.png and /dev/null differ diff --git a/icons/pda_icons/pda_notes.png b/icons/pda_icons/pda_notes.png deleted file mode 100644 index eb076d3ca33..00000000000 Binary files a/icons/pda_icons/pda_notes.png and /dev/null differ diff --git a/icons/pda_icons/pda_power.png b/icons/pda_icons/pda_power.png deleted file mode 100644 index 04175e7c835..00000000000 Binary files a/icons/pda_icons/pda_power.png and /dev/null differ diff --git a/icons/pda_icons/pda_rdoor.png b/icons/pda_icons/pda_rdoor.png deleted file mode 100644 index 6eab5a8817d..00000000000 Binary files a/icons/pda_icons/pda_rdoor.png and /dev/null differ diff --git a/icons/pda_icons/pda_reagent.png b/icons/pda_icons/pda_reagent.png deleted file mode 100644 index b900af5ae66..00000000000 Binary files a/icons/pda_icons/pda_reagent.png and /dev/null differ diff --git a/icons/pda_icons/pda_refresh.png b/icons/pda_icons/pda_refresh.png deleted file mode 100644 index 08439c6bca3..00000000000 Binary files a/icons/pda_icons/pda_refresh.png and /dev/null differ diff --git a/icons/pda_icons/pda_scanner.png b/icons/pda_icons/pda_scanner.png deleted file mode 100644 index eabdc511cde..00000000000 Binary files a/icons/pda_icons/pda_scanner.png and /dev/null differ diff --git a/icons/pda_icons/pda_signaler.png b/icons/pda_icons/pda_signaler.png deleted file mode 100644 index f71398f70d2..00000000000 Binary files a/icons/pda_icons/pda_signaler.png and /dev/null differ diff --git a/icons/pda_icons/pda_status.png b/icons/pda_icons/pda_status.png deleted file mode 100644 index fe955e66a61..00000000000 Binary files a/icons/pda_icons/pda_status.png and /dev/null differ diff --git a/nano/images/Cyberiad_nanomap_z1.png b/nano/images/Cyberiad_nanomap_z1.png deleted file mode 100644 index 126f9021dac..00000000000 Binary files a/nano/images/Cyberiad_nanomap_z1.png and /dev/null differ diff --git a/nano/images/Delta_nanomap_z1.png b/nano/images/Delta_nanomap_z1.png deleted file mode 100644 index f0f4e31bca3..00000000000 Binary files a/nano/images/Delta_nanomap_z1.png and /dev/null differ diff --git a/nano/images/MetaStation_nanomap_z1.png b/nano/images/MetaStation_nanomap_z1.png deleted file mode 100644 index 280e28a2bc3..00000000000 Binary files a/nano/images/MetaStation_nanomap_z1.png and /dev/null differ diff --git a/nano/package-lock.json b/nano/package-lock.json index b96939c5d9a..14642f304d6 100644 --- a/nano/package-lock.json +++ b/nano/package-lock.json @@ -1728,9 +1728,9 @@ "dev": true }, "elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", diff --git a/nano/templates/adv_med.tmpl b/nano/templates/adv_med.tmpl deleted file mode 100644 index a1ca2815679..00000000000 --- a/nano/templates/adv_med.tmpl +++ /dev/null @@ -1,231 +0,0 @@ - - -{{if !data.occupied}} -

    No occupant detected.

    -{{else}} -

    Occupant Data:

    -
    -
    - Name: -
    -
    - {{:data.occupant.name}} -
    -
    -
    -
    - Health: -
    - {{:helper.displayBar(data.occupant.health, -100, 100, (data.occupant.health >= 50) ? 'good' : (data.occupant.health >= 0) ? 'average' : 'bad')}} -
    - {{:helper.smoothRound(data.occupant.health)}}% -
    -
    -
    -
    - Status: -
    -
    - {{if data.occupant.stat==0}} - Alive - {{else data.occupant.stat==1}} - Critical - {{else}} - Dead - {{/if}} -
    -
    - -
    -
    - Temperature: -
    -
    - {{:helper.smoothRound(data.occupant.bodyTempC)}}°C, {{:helper.smoothRound(data.occupant.bodyTempF)}}°F -
    -
    - {{if data.occupant.hasBorer}} - - Large growth detected in frontal lobe, possibly cancerous. Surgical removal is recommended.
    - {{/if}} - {{if data.occupant.blind}} - Cataracts detected.
    - {{/if}} - {{if data.occupant.colourblind}} - Photoreceptor abnormalities detected.
    - {{/if}} - {{if data.occupant.nearsighted}} - Retinal misalignment detected.
    - {{/if}} - -
    - {{:helper.link('Print', 'print', {'print_p' : 1, 'name' : data.occupant.name})}} - {{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectify' : 1}, data.occupied ? null : 'disabled')}} -

    - - Damage: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Brute Damage: Brain Damage:
    {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth+100, (data.occupant.bruteLoss < 15) ? 'notgood' : (data.occupant.bruteLoss < 40) ? 'average' : 'bad', data.occupant.bruteLoss) }} {{:helper.displayBar(data.occupant.brainLoss, 0, data.occupant.maxHealth+100, (data.occupant.brainLoss < 15) ? 'notgood' : (data.occupant.brainLoss < 40) ? 'average' : 'bad', data.occupant.brainLoss)}}
    Resp. Damage: Radiation Level:
    {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth+100, (data.occupant.oxyLoss < 15) ? 'notgood' : (data.occupant.oxyLoss < 40) ? 'average' : 'bad', data.occupant.oxyLoss)}} {{:helper.displayBar(data.occupant.radLoss, 0, data.occupant.maxHealth+100, (data.occupant.radLoss < 15) ? 'notgood' : (data.occupant.radLoss < 40) ? 'average' : 'bad', data.occupant.radLoss)}}
    Toxin Damage: Genetic Damage:
    {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth+100, (data.occupant.toxLoss < 15) ? 'notgood' : (data.occupant.toxLoss < 40) ? 'average' : 'bad', data.occupant.toxLoss)}} {{:helper.displayBar(data.occupant.cloneLoss, 0, data.occupant.maxHealth+100, (data.occupant.cloneLoss < 15) ? 'notgood' : (data.occupant.cloneLoss < 40) ? 'average' : 'bad', data.occupant.cloneLoss)}}
    Burn Severity: Paralysis %: ({{:helper.smoothRound(data.occupant.paralysisSeconds)}} seconds left)
    {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth+100, (data.occupant.fireLoss < 15) ? 'notgood' : (data.occupant.fireLoss < 40) ? 'average' : 'bad', data.occupant.fireLoss)}} {{:helper.displayBar(data.occupant.paralysis, 0, 100, (data.occupant.paralysis < 25) ? 'notgood' : (data.occupant.paralysis < 50) ? 'average' : 'bad', data.occupant.paralysis)}}
    - {{if data.occupant.blood.hasBlood}} -
    - Blood: -
    -
    Pulse:
    {{:data.occupant.blood.pulse}} bpm
    -
    -
    -
    Blood Level:
    - {{:helper.displayBar(data.occupant.blood.bloodLevel, 0, data.occupant.blood.bloodMax, (data.occupant.blood.percent <= 60) ? 'bad' : (data.occupant.blood.percent <= 90) ? 'average' : 'good' )}} -
    {{:helper.smoothRound(data.occupant.blood.percent)}}%, {{:helper.smoothRound(data.occupant.blood.bloodLevel)}}cl
    -
    - {{/if}} -
    - {{if data.occupant.hasVirus}} - - Viral pathogen detected in blood stream. -
    - {{/if}} - {{if data.occupant.implant_len}} -
    - Implants - - - {{for data.occupant.implant}} - - {{/for}} - -
    {{:value.name}}
    -
    - {{/if}} - External Organs - - - - - - - - - - - {{for data.occupant.extOrgan}} - - - - - - - - {{/for}} - -
    Name Total damage Brute damage Burn damage Injuries
    {{:value.name}} {{:helper.displayBar(value.totalLoss, 0, value.maxHealth, (value.totalLoss < value.bruised) ? 'notgood' : (value.totalLoss < value.broken) ? 'average' : 'bad', value.totalLoss) }} {{if value.bruteLoss> 0}} {{else}} {{/if}} {{:helper.smoothRound(value.bruteLoss)}} {{if value.fireLoss> 0}} {{else}} {{/if}} {{:helper.smoothRound(value.fireLoss)}} - - {{if value.internalBleeding}} internal bleeding
    {{/if}} - {{if value.lungRuptured}} lung ruptured
    {{/if}} - {{if value.status.broken}} {{:value.status.broken}}
    {{/if}} - {{if value.germ_level > 100}} - {{if value.germ_level < 300}} - mild infection
    - {{else value.germ_level < 400}} - mild infection+
    - {{else value.germ_level < 500}} - mild infection++
    - {{else value.germ_level < 700}} - acute infection
    - {{else value.germ_level < 800}} - acute infection+
    - {{else value.germ_level < 900}} - acute infection++
    - {{else value.germ_level >= 900}} - septic
    - {{/if}} - {{/if}} - {{if value.open}} open incision
    {{/if}} -
    - {{if value.status.splinted}} splinted
    {{/if}} - {{if value.status.robotic}} robotic
    {{/if}} - {{if value.status.dead}} DEAD
    - {{/for}} - {{/if}} -
    -
    - Internal Organs - - - - - - - - - {{for data.occupant.intOrgan}} - - - - - - {{/for}} - -
    Name Brute damage Injuries
    {{:value.name}}
    {{:helper.displayBar(value.damage, 0, value.maxHealth, (value.damage < value.bruised) ? 'notgood' : (value.damage < value.broken) ? 'average' : 'bad', value.damage) }} - - {{if value.germ_level > 100}} - {{if value.germ_level < 300}} - mild infection
    - {{else value.germ_level < 400}} - mild infection+
    - {{else value.germ_level < 500}} - mild infection++
    - {{else value.germ_level < 700}} - acute Infection
    - {{else value.germ_level < 800}} - acute Infection+
    - {{else value.germ_level < 900}} - acute Infection++
    - {{else value.germ_level >= 900}} - septic
    - {{/if}} - {{/if}} -
    - {{if value.robotic == 1}} robotic
    {{/if}} - {{if value.robotic == 2}} assisted
    {{/if}} - {{if value.dead}} DEAD
    {{/if}} -
    -{{/if}} diff --git a/nano/templates/ai_fixer.tmpl b/nano/templates/ai_fixer.tmpl deleted file mode 100644 index 4be6fee598d..00000000000 --- a/nano/templates/ai_fixer.tmpl +++ /dev/null @@ -1,58 +0,0 @@ - -{{if data.occupant}} -
    -

    Stored AI: {{:data.occupant}}

    -

    Information

    -
    - Integrity -
    -
    - {{:data.integrity}}% -
    -
    - Status -
    -
    - {{if data.stat == 2}} - Non-functional - {{else}} - Functional - {{/if}} -
    -
    - Wireless Activity -
    -
    - {{:helper.link('Enabled','check',{'wireless' : 0},data.wireless == 0 ? 'selected' : '')}} - {{:helper.link('Disabled','close',{'wireless' : 1},data.wireless == 1 ? 'selected' : '')}} -
    -
    - Subspace Transceiver -
    -
    - {{:helper.link('Enabled','check',{'radio' : 0},data.radio == 0 ? 'selected' : '')}} - {{:helper.link('Disabled','close',{'radio' : 1},data.radio == 1 ? 'selected' : '')}} -
    -

    Laws

    - {{for data.laws}} - {{:value.number}}: {{:value.law}}
    - {{empty}} - No laws detected! - {{/for}} - -

    Actions

    - {{if data.active}} - Reconstruction in progress, please wait. - {{:helper.displayBar(data.integrity, 0, 100, (data.integrity >= 75) ? 'good' : (data.integrity >= 25) ? 'average' : 'bad')}} - {{:helper.smoothRound(data.integrity)}}% - {{/if}} -
    - {{:helper.link('Reconstruct', 'wrench', {'fix' : 1}, data.active || data.integrity >= 100 ? 'disabled' : '')}} -
    -
    -{{else}} -

    No artificial intelligence detected.

    -{{/if}} \ No newline at end of file diff --git a/nano/templates/aicard.tmpl b/nano/templates/aicard.tmpl deleted file mode 100644 index 8ebe6cff319..00000000000 --- a/nano/templates/aicard.tmpl +++ /dev/null @@ -1,98 +0,0 @@ - - - - - -{{if data.has_ai}} -
    -
    - Hardware Integrity: -
    -
    - {{:data.hardware_integrity}}% -
    -
    - - {{if data.has_laws}} - - - -
    - Laws: -
    - {{for data.laws}} - - {{/for}} -
    IndexLaw
    {{:value.index}}.{{:value.law}}
    - {{else}} - No laws found. - {{/if}} - - {{if data.operational}} - - - - - - - - - - - {{if data.flushing}} - - {{else}} - - - - - {{/if}} -
    Radio Subspace Transceiver{{:helper.link("Enabled", null, {'radio' : 0}, data.radio ? 'selected' : null)}}{{:helper.link("Disabled", null, {'radio' : 1}, data.radio ? null : 'redButton' )}}
    Wireless Interface{{:helper.link("Enabled", null, {'wireless' : 0}, data.wireless ? 'selected' : null)}}{{:helper.link("Disabled", null, {'wireless' : 1}, data.wireless ? null : 'redButton' )}}
    AI wipe in progress...
    Wipe AI{{:helper.link("Wipe", 'exclamation-circle', {'wipe' : 1}, null, 'redButton')}}
    - {{/if}} -{{else}} - Stored AI: No AI detected. -{{/if}} diff --git a/nano/templates/alarm_monitor.tmpl b/nano/templates/alarm_monitor.tmpl deleted file mode 100644 index 5dae051a576..00000000000 --- a/nano/templates/alarm_monitor.tmpl +++ /dev/null @@ -1,39 +0,0 @@ - - -{{for data.categories}} -

    {{:value.category}}

    - {{for value.alarms :alarmValue:alarmIndex}} - {{if alarmValue.origin_lost}} - {{:alarmValue.name}} Alarm Origin Lost
    - {{else}} - {{:alarmValue.name}}
    - {{/if}} - {{if alarmValue.has_cameras || alarmValue.lost_sources != ""}} -
    - {{if alarmValue.has_cameras}} -
    - {{for alarmValue.cameras :cameraValue:cameraIndex}} - {{if cameraValue.deact}} - {{:helper.link(cameraValue.name + " (deactivated)", '', {}, 'inactive')}} - {{else}} - {{:helper.link(cameraValue.name, '', {'switchTo' : cameraValue.camera})}} - {{/if}} - {{/for}} -
    - {{else}} - No cameras found. - {{/if}} - {{if alarmValue.lost_sources != ""}} -
    -

    Lost Alarm Sources: {{:alarmValue.lost_sources}}

    -
    - {{/if}} -
    - {{/if}} - {{empty}} - --All Systems Nominal - {{/for}} -{{/for}} \ No newline at end of file diff --git a/nano/templates/apc.tmpl b/nano/templates/apc.tmpl deleted file mode 100644 index 234074fe577..00000000000 --- a/nano/templates/apc.tmpl +++ /dev/null @@ -1,197 +0,0 @@ -
    - {{if data.siliconUser}} -
    - Interface Lock: -
    -
    - {{:helper.link('Engaged', 'lock', {'toggleaccess' : 1}, data.siliconLock ? 'selected' : null)}}{{:helper.link('Disengaged', 'unlock', {'toggleaccess' : 1}, data.malfStatus >= 2 ? 'linkOff' : (data.siliconLock ? null : 'selected'))}} -
    -
    - {{else}} - {{if data.locked}} - Swipe an ID card to unlock this interface - {{else}} - Swipe an ID card to lock this interface - {{/if}} - {{/if}} -
    - -
    - -

    Power Status

    - -
    -
    - Main Breaker: -
    -
    - {{if data.locked && !data.siliconUser}} - {{if data.isOperating}} - On - {{else}} - Off - {{/if}} - {{else}} - {{:helper.link('On', 'power-off', {'breaker' : 1}, data.isOperating ? 'selected' : null)}}{{:helper.link('Off', 'close', {'breaker' : 1}, data.isOperating ? null : 'selected')}} - {{/if}} -
    -
    - -
    -
    - External Power: -
    -
    - {{if data.externalPower == 2}} - Good - {{else data.externalPower == 1}} - Low - {{else}} - None - {{/if}} -
    -
    - -
    -
    - Power Cell: -
    - {{if data.powerCellStatus == null}} -
    - Power cell removed. -
    - {{else}} - - {{:helper.displayBar(data.powerCellStatus, 0, 100, (data.powerCellStatus >= 50) ? 'good' : (data.powerCellStatus >= 25) ? 'average' : 'bad')}} -
    - {{:helper.smoothRound(data.powerCellStatus, 1)}}% -
    - {{/if}} -
    - - {{if data.powerCellStatus != null}} -
    -
    - Charge Mode: -
    -
    - {{if data.locked && !data.siliconUser}} - {{if data.chargeMode}} - Auto - {{else}} - Off - {{/if}} - {{else}} - {{:helper.link('Auto', 'refresh', {'cmode' : 1}, data.chargeMode ? 'selected' : null)}}{{:helper.link('Off', 'close', {'cmode' : 1}, data.chargeMode ? null : 'selected')}} - {{/if}} -   - {{if data.chargingStatus > 1}} - [Fully Charged] - {{else data.chargingStatus == 1}} - [Charging] - {{else}} - [Not Charging] - {{/if}} -
    -
    - {{/if}} - - -

    Power Channels

    - - {{for data.powerChannels}} -
    -
    - {{:value.title}}: -
    -
    - {{:helper.smoothRound(value.powerLoad)}} W -
    -
    -    - {{if value.status <= 1}} - Off - {{else value.status >= 2}} - On - {{/if}} - {{if data.locked}} - {{if value.status == 1 || value.status == 3}} -   Auto - {{else}} -   Manual - {{/if}} - {{/if}} -
    - {{if !data.locked || data.siliconUser}} -
    - {{:helper.link('Auto', 'refresh', value.topicParams.auto, (value.status == 1 || value.status == 3) ? 'selected' : null)}} - {{:helper.link('On', 'power-off', value.topicParams.on, (value.status == 2) ? 'selected' : null)}} - {{:helper.link('Off', 'close', value.topicParams.off, (value.status == 0) ? 'selected' : null)}} -
    - {{/if}} -
    - {{/for}} - -
    -
    - Total Load: -
    -
    - {{:helper.smoothRound(data.totalLoad)}} W -
    -
    - -
     
    - -
    -
    - Cover Lock: -
    -
    - {{if data.locked && !data.siliconUser}} - {{if data.coverLocked}} - Engaged - {{else}} - Disengaged - {{/if}} - {{else}} - {{:helper.link('Engaged', 'lock', {'lock' : 1}, data.coverLocked ? 'selected' : null)}}{{:helper.link('Disengaged', 'unlock', {'lock' : 1}, data.coverLocked ? null : 'selected')}} - {{/if}} -
    -
    - - {{if data.siliconUser}} -

    System Overrides

    - -
    - {{:helper.link('Overload Lighting Circuit', 'lightbulb-o', {'overload' : 1})}} - {{if data.malfStatus == 1}} - {{:helper.link('Override Programming', 'file-text', {'malfhack' : 1})}} - {{else data.malfStatus == 2}} - {{:helper.link('Shunt Core Processes', 'arrow-down', {'occupyapc' : 1})}} - {{else data.malfStatus == 3}} - {{:helper.link('Return to Main Core', 'arrow-left', {'deoccupyapc' : 1})}} - {{else data.malfStatus == 4}} - {{:helper.link('Shunt Core Processes', 'arrow-down', {'occupyapc' : 1}, 'linkOff')}} - {{/if}} -
    - {{/if}} - -
    -
    - Night Shift Lighting: -
    -
    - {{if data.locked}} - {{if data.nightshiftLights}} - On - {{else}} - Off - {{/if}} - {{else}} - {{:helper.link('Enabled', 'lightbulb-o', {'toggle_nightshift' : 1}, data.nightshiftLights ? 'selected' : null)}}{{:helper.link('Disabled', 'lightbulb-o', {'toggle_nightshift' : 1}, data.nightshiftLights ? null : 'selected')}} - {{/if}} -
    -
    - -
    diff --git a/nano/templates/atmos_alert.tmpl b/nano/templates/atmos_alert.tmpl deleted file mode 100644 index bdb6c2c872b..00000000000 --- a/nano/templates/atmos_alert.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -

    Priority Alerts

    -{{for data.priority_alarms}} -
    - {{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}} -
    -{{empty}} - No priority alerts detected. -{{/for}} - -

    Minor Alerts

    -{{for data.minor_alarms}} -
    - {{:value.name}} {{:helper.link('Reset', null, {'clear_alarm' : value.ref})}} -
    -{{empty}} - No minor alerts detected. -{{/for}} - diff --git a/nano/templates/atmos_filter.tmpl b/nano/templates/atmos_filter.tmpl deleted file mode 100644 index e0715e243c5..00000000000 --- a/nano/templates/atmos_filter.tmpl +++ /dev/null @@ -1,24 +0,0 @@ -
    -
    Power:
    -
    {{:helper.link(data.on ? 'On' : 'Off', data.on ? 'power-off' : 'times', {'power' : 1}, null, data.on ? 'selected' : null)}}
    -
    -
    -
    Output Pressure:
    -
    - {{:helper.link('Set', 'pencil', {'pressure' : 'input'})}} - {{:helper.link('Max', 'plus', {'pressure' : 'max'}, data.pressure == data.max_pressure ? 'disabled' : null)}} - {{:helper.smoothRound(data.pressure)}} kPa -
    -
    -
    -
    Filter:
    -
    - {{:helper.link('Nothing', null, {'filter' : -1}, data.filter_type == -1 ? 'selected' : null)}} - {{:helper.link('Plasma', null, {'filter' : 0}, data.filter_type == 0 ? 'selected' : null)}} - {{:helper.link('O2', null, {'filter' : 1}, data.filter_type == 1 ? 'selected' : null)}} - {{:helper.link('N2', null, {'filter' : 2}, data.filter_type == 2 ? 'selected' : null)}} - {{:helper.link('CO2', null, {'filter' : 3}, data.filter_type == 3 ? 'selected' : null)}} - {{:helper.link('N2O', null, {'filter' : 4}, data.filter_type == 4 ? 'selected' : null)}} -
    -
    - diff --git a/nano/templates/atmos_mixer.tmpl b/nano/templates/atmos_mixer.tmpl deleted file mode 100644 index 3bae7f993bd..00000000000 --- a/nano/templates/atmos_mixer.tmpl +++ /dev/null @@ -1,32 +0,0 @@ -
    -
    Power:
    -
    {{:helper.link(data.on ? 'On' : 'Off', data.on ? 'power-off' : 'times', {'power' : 1}, null, data.on ? 'selected' : null)}}
    -
    -
    -
    Output Pressure:
    -
    - {{:helper.link('Set', 'pencil', {'pressure' : 'input'})}} - {{:helper.link('Max', 'plus', {'pressure' : 'max'}, data.pressure == data.max_pressure ? 'disabled' : null)}} - {{:helper.smoothRound(data.pressure)}} kPa -
    -
    -
    -
    Node 1:
    -
    - {{:helper.link('', null, {'node1' : -0.1}, data.node1_concentration == 0 ? 'disabled' : null)}} - {{:helper.link('', null, {'node1' : -0.01}, data.node1_concentration == 0 ? 'disabled' : null)}} - {{:helper.link('', null, {'node1' : 0.01}, data.node1_concentration == 100 ? 'disabled' : null)}} - {{:helper.link('', null, {'node1' : 0.1}, data.node1_concentration == 100 ? 'disabled' : null)}} - {{:helper.smoothRound(data.node1_concentration)}}% -
    -
    -
    -
    Node 2:
    -
    - {{:helper.link('', null, {'node2' : -0.1}, data.node2_concentration == 0 ? 'disabled' : null)}} - {{:helper.link('', null, {'node2' : -0.01}, data.node2_concentration == 0 ? 'disabled' : null)}} - {{:helper.link('', null, {'node2' : 0.01}, data.node2_concentration == 100 ? 'disabled' : null)}} - {{:helper.link('', null, {'node2' : 0.1}, data.node2_concentration == 100 ? 'disabled' : null)}} - {{:helper.smoothRound(data.node2_concentration)}}% -
    -
    \ No newline at end of file diff --git a/nano/templates/atmos_pump.tmpl b/nano/templates/atmos_pump.tmpl deleted file mode 100644 index a27e5b710dd..00000000000 --- a/nano/templates/atmos_pump.tmpl +++ /dev/null @@ -1,24 +0,0 @@ -
    -
    Power:
    -
    {{:helper.link(data.on ? 'On' : 'Off', data.on ? 'power-off' : 'times', {'power' : 1}, null, data.on ? 'selected' : null)}}
    -
    -{{if data.max_rate}} -
    -
    Transfer Rate:
    -
    - {{:helper.link('Set', 'pencil', {'rate' : 'input'})}} - {{:helper.link('Max', 'plus', {'rate' : 'max'}, data.rate == data.max_rate ? 'disabled' : null)}} - {{:helper.smoothRound(data.rate)}} L/s -
    -
    -{{else}} -
    -
    Output Pressure:
    -
    - {{:helper.link('Set', 'pencil', {'pressure' : 'input'})}} - {{:helper.link('Max', 'plus', {'pressure' : 'max'}, data.pressure == data.max_pressure ? 'disabled' : null)}} - {{:helper.smoothRound(data.pressure)}} kPa -
    -
    -{{/if}} - diff --git a/nano/templates/autolathe.tmpl b/nano/templates/autolathe.tmpl deleted file mode 100644 index d56ae4926c3..00000000000 --- a/nano/templates/autolathe.tmpl +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - -{{if data.screen == 2 || data.screen == 3}} -
    - {{:helper.link('Main Menu', 'reply', {'menu' : 1})}} -
    -{{/if}} - - - - -
    -
    - {{if data.screen == 1}} -

    Autolathe Menu

    -
    - - - - - - - - - -
    Total amount:{{:data.total_amount}} / {{:data.max_amount}} cm3
    Metal amount:{{:data.metal_amount}} cm3
    Glass amount:{{:data.glass_amount}} cm3
    -
    - - - -
    - -
    - - {{for data.categories}} - {{if (index & 1) == 0 && index != 0}} - - {{/if}} - - {{/for}} -
    {{:helper.link(value, 'arrow-right', {'category': value})}}
    - {{else data.screen == 2 || data.screen == 3}} - {{if data.screen == 2}} -

    Viewing Category {{:data.selected_category}}:

    - {{else data.screen == 3}} -

    Search Results for '{{:data.search}}':

    - {{/if}} -
    - - - - - - - - - -
    Total amount:{{:data.total_amount}} / {{:data.max_amount}} cm3
    Metal amount:{{:data.metal_amount}} cm3
    Glass amount:{{:data.glass_amount}} cm3
    - - {{for data.designs}} - - - - - - - - {{/for}} -
    {{:helper.link(value.name, 'print', {'make' : value.id, 'multiplier' : 1}, value.disabled)}}{{if value.max_multiplier > 10}} - {{:helper.link('x10', null, {'make' : value.id, 'multiplier' : 10}, value.disabled)}} - {{/if}}{{if value.max_multiplier > 25}} - {{:helper.link('x25', null, {'make' : value.id, 'multiplier' : 25}, value.disabled)}} - {{/if}}{{if value.max_multiplier}} - {{:helper.link('x' + value.max_multiplier, null, {'make' : value.id, 'multiplier' : value.max_multiplier}, value.disabled)}} - {{/if}} - {{for value.materials : material : i}} - {{if material.amount}} - | - {{if material.is_red}} - - {{/if}} - {{:material.amount}} {{:material.name}} - {{if material.is_red}} - - {{/if}} - {{/if}} - {{/for}} -
    - {{/if}} -
    -
    -
    -

    Queue contains:

    - {{if data.queue}} - {{if data.processing}} -
      {{:data.processing}}
    - {{/if}} -
      - {{for data.queue}} -
    1. - {{if !value.can_build}} - - {{/if}} - {{:value.name}} {{:value.multiplier > 1 ? '(' + value.multiplier + ')' : ''}} - {{if !value.can_build}} - - {{/if}} -
      - {{if index + 1 > 1}} - {{:helper.link('', 'arrow-up', {'queue_move' : -1, 'index' : index + 1})}} - {{/if}} - {{if index + 1 < data.queue_len}} - {{:helper.link('', 'arrow-down', {'queue_move' : +1, 'index' : index + 1})}} - {{/if}} - {{:helper.link('Remove', 'times', {'remove_from_queue' : index + 1})}} -
      -
    2. - {{/for}} -
    - {{:helper.link('Clear queue', 'trash', {'clear_queue' : 1})}} - {{else}} - {{if data.processing}} -
      {{:data.processing}}
    - {{else}} -
      Nothing
    - {{/if}} - {{/if}} -
    -
    \ No newline at end of file diff --git a/nano/templates/brig_cells.tmpl b/nano/templates/brig_cells.tmpl deleted file mode 100644 index 652c8926378..00000000000 --- a/nano/templates/brig_cells.tmpl +++ /dev/null @@ -1,34 +0,0 @@ - -
    - - - - - - - - - - - - - - - {{for data.cells}} - - - - - - - - - - {{/for}} - -
    CellOccupantCrimesBrigged ByTime Brigged ForTime LeftRelease
    {{:value.cell_id}}{{:value.occupant}}{{:value.crimes}}{{:value.brigged_by}}{{:value.time_set}}{{:value.time_left}}{{:helper.link('Release', null, {'release' : value.ref}, null, 'infoButton')}}
    -
    -
    \ No newline at end of file diff --git a/nano/templates/brig_timer.tmpl b/nano/templates/brig_timer.tmpl deleted file mode 100644 index 2cd1ab73a23..00000000000 --- a/nano/templates/brig_timer.tmpl +++ /dev/null @@ -1,91 +0,0 @@ - -
    -
    - Cell: -
    -
    - {{:data.cell_id}} -
    -
    -
    -
    - Occupant: -
    -
    - {{:data.occupant}} -
    -
    -
    -
    - Crimes: -
    -
    - {{:data.crimes}} -
    -
    -
    -
    - Brigged By: -
    -
    - {{:data.brigged_by}} -
    -
    -
    -
    - Time Brigged For: -
    -
    - {{:data.time_set}} -
    -
    -
    -
    - Time Left: -
    -
    - {{:data.time_left}} -
    -
    -{{if data.isAllowed}} -
    -
    - Actions: -
    -
    - {{:helper.link('Flash', 'flash', {'flash' : 1}, null, data.isAllowed ? '' : 'dsabled')}} - {{:helper.link('Release', null, {'release' : 1}, null, data.isAllowed ? '' : 'disabled')}} -
    -
    - {{if !data.timing}} -

    Inmate Information

    -
    -
    - Name: -
    -
    - {{:helper.link('Set', 'pencil', {'prisoner_name' : 'input'})}} {{:data.prisoner_name}} -
    -
    -
    -
    - Charge: -
    -
    - {{:helper.link('Set', 'pencil', {'prisoner_charge' : 'input'})}} {{:data.prisoner_charge}} -
    -
    -
    -
    - Time (in minutes): -
    -
    - {{:helper.link('Set', 'pencil', {'prisoner_time' : 'input'})}} {{:data.prisoner_time}} -
    -
    - {{:helper.link('Submit', null, {'set_timer' : 1}, null, data.isAllowed ? '' : 'disabled')}} - {{/if}} -{{/if}} \ No newline at end of file diff --git a/nano/templates/bsa.tmpl b/nano/templates/bsa.tmpl deleted file mode 100644 index a0e8a60deb5..00000000000 --- a/nano/templates/bsa.tmpl +++ /dev/null @@ -1,51 +0,0 @@ - -{{if data.notice}} -
    {{:data.notice}}
    -{{/if}} -

    Bluespace Artillery Control

    -
    -
    - Target: -
    -
    - {{:helper.link(data.target ? data.target : "None", 'crosshairs', {'recalibrate' : 1})}} -
    -
    -
    -
    - Reload Cooldown: -
    -
    - {{if data.ready}} - Ready - {{else data.reloadtime_mins || data.reloadtime_secs}} - {{:data.reloadtime_mins}}:{{:data.reloadtime_secs}} - {{else}} - No cannon connected! - {{/if}} -
    -
    -
    -
    - Controls: -
    -
    - {{:helper.link('FIRE!', 'warning', {'fire' : 1}, data.ready && data.target ? null : 'disabled')}} -
    -
    -{{if !data.connected}} -
    -
    - Maintenance: -
    -
    - {{:helper.link('Complete Deployment', 'wrench', {'build' : 1})}} -
    -
    -{{else}} -
    -Deployment of weapon authorized by Nanotrasen Naval Command. Remember, friendly fire is grounds for termination of your contract and life. -{{/if}} \ No newline at end of file diff --git a/nano/templates/canister.tmpl b/nano/templates/canister.tmpl deleted file mode 100644 index 5aaa15d4fe3..00000000000 --- a/nano/templates/canister.tmpl +++ /dev/null @@ -1,158 +0,0 @@ -{{if data.menu == 0}} -

    Tank Status

    -
    -
    - Tank Label: -
    -
    -
    {{:data.name}}
    {{:helper.link('Relabel', 'pencil', {'choice' : 'menu', 'mode_target' : 1}, data.canLabel ? null : 'disabled')}} -
    -
    - -
    -
    - Tank Pressure: -
    -
    - {{:helper.smoothRound(data.tankPressure)}} kPa -
    -
    - -
    -
    - Port Status: -
    -
    - {{:data.portConnected ? 'Connected' : 'Disconnected'}} -
    -
    - -

    Holding Tank Status

    - {{if data.hasHoldingTank}} -
    -
    - Tank Label: -
    -
    -
    {{:data.holdingTank.name}}
    {{:helper.link('Eject', 'eject', {'remove_tank' : 1})}} -
    -
    - -
    -
    - Tank Pressure: -
    -
    - {{:helper.smoothRound(data.holdingTank.tankPressure)}} kPa -
    -
    - {{else}} -
    No holding tank inserted.
    -
     
    - {{/if}} - - -

    Release Valve Status

    -
    -
    - Release Pressure: -
    -
    - {{:helper.displayBar(data.releasePressure, data.minReleasePressure, data.maxReleasePressure)}} -
    - {{:helper.link('-', null, {'pressure_adj' : -1000}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -100}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -10}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -1}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}} -
     {{:data.releasePressure}} kPa 
    - {{:helper.link('+', null, {'pressure_adj' : 1}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 10}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 100}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 1000}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} -
    -
    -
    - -
    -
    - Release Valve: -
    -
    - {{:helper.link('Open', 'unlock', {'toggle' : 1}, data.valveOpen ? 'selected' : null)}}{{:helper.link('Close', 'lock', {'toggle' : 1}, data.valveOpen ? null : 'selected')}} -
    -
    -{{else}} - {{:helper.link('Go back', 'home', {'choice' : 'menu', 'mode_target' : 0}, null)}} -

    Relabel

    -
    - Name: -
    -
    -
    {{:data.name}}
    {{:helper.link('Rename', 'pencil', {'rename' : 1}, data.canLabel ? null : 'disabled')}} -
    - - -

    Colors

    -
    -
    -
    {{:data.colorContainer.prim.name}}
    - {{for data.colorContainer.prim.options}} -
    - {{if value.icon == data.canister_color.prim}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.prim.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}} - {{else}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.prim.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}} - {{/if}} -
    - {{/for}} -
    -
    -
    {{:data.colorContainer.sec.name}}
    - {{:helper.link("None", '', {'choice' : data.colorContainer.sec.name, 'icon' : "none"}, data.canLabel ? null : 'disabled', data.colorContainer.sec.anycolor ? null : 'selected')}} - {{for data.colorContainer.sec.options}} -
    - {{if value.icon == data.canister_color.sec}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.sec.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}} - {{else}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.sec.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}} - {{/if}} -
    - {{/for}} -
    -
    -
    {{:data.colorContainer.ter.name}}
    - {{:helper.link("None", '', {'choice' : data.colorContainer.ter.name, 'icon' : "none"}, data.canLabel ? null : 'disabled', data.colorContainer.ter.anycolor ? null : 'selected')}} - {{for data.colorContainer.ter.options}} -
    - {{if value.icon == data.canister_color.ter}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.ter.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}} - {{else}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.ter.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}} - {{/if}} -
    - {{/for}} -
    -
    -
    {{:data.colorContainer.quart.name}}
    - {{:helper.link("None", '', {'choice' : data.colorContainer.quart.name, 'icon' : "none"}, data.canLabel ? null : 'disabled', data.colorContainer.quart.anycolor ? null : 'selected')}} - {{for data.colorContainer.quart.options}} -
    - {{if value.icon == data.canister_color.quart}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.quart.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}} - {{else}} - {{:helper.link(value.name, '', {'choice' : data.colorContainer.quart.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}} - {{/if}} -
    - {{/for}} -
    -
    -

    Decals

    -
    -
    Pick and choose:
    -
    - {{for data.possibleDecals}} - {{:helper.link(value.name, '', {'choice' : 'decals', 'icon' : value.icon}, data.canLabel ? null : 'disabled', value.active ? 'selected' : null)}} - {{/for}} -
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/card_prog.tmpl b/nano/templates/card_prog.tmpl deleted file mode 100644 index 7054f8bf4c2..00000000000 --- a/nano/templates/card_prog.tmpl +++ /dev/null @@ -1,309 +0,0 @@ -{{if data.printing}} -
    The computer is currently busy.
    -
    -
    Printing...
    -
    -

    - Thank you for your patience! -

    -{{else}} - {{:helper.link('Access Modification', 'home', {'action' : 'PRG_mode', 'mode_target' : 0}, !data.mode ? 'disabled' : null)}} - {{:helper.link('Job Management', 'gear', {'action' : 'PRG_mode', 'mode_target' : 1}, data.mode == 1 ? 'disabled' : null)}} - {{:helper.link('Crew Manifest', 'folder-open', {'action' : 'PRG_mode', 'mode_target' : 2}, data.mode == 2 ? 'disabled' : null)}} - {{:helper.link('Print', 'print', {'action' : 'PRG_print'}, data.printer && (data.mode == 2 || data.has_modify && !data.mode) ? null : 'disabled')}} - {{:helper.link('Records', 'file', {'action' : 'PRG_mode', 'mode_target' : 3}, (data.mode == 3) ? 'disabled' : null)}} - - {{if data.mode == 1}} -
    -

    Job Management

    -
    -
    -
    - Authorized Identity: -
    -
    - {{:helper.link(data.scan_name, 'eject', {'action' : 'PRG_scan'})}} -
    -
    - Cooldown: -
    -
    - {{if data.cooldown_mins > 0 || data.cooldown_mins == 0 && data.cooldown_secs > 0}} - Next change in: {{:data.cooldown_mins}}:{{:data.cooldown_secs}} - {{else}} - Ready - {{/if}} -
    -
    -
    -
    - {{for data.job_slots}} -
    - {{:value.title}}: {{:value.current_positions}}/{{:value.total_positions}} - {{:helper.link('-', null, {'action' : 'PRG_make_job_unavailable', 'job' : value.title}, value.can_close == 1 && data.authenticated ? null : 'disabled')}} - {{:helper.link('+', null, {'action' : 'PRG_make_job_available', 'job' : value.title}, value.can_open == 1 && data.authenticated ? null : 'disabled')}} - {{:helper.link('Pri', null, {'action' : 'PRG_prioritize_job', 'job' : value.title}, value.can_prioritize > 0 && data.authenticated ? null : 'disabled')}} {{if value.can_prioritize == 2}}Priority Job {{/if}} -
    - {{/for}} -
    - {{else data.mode == 2}} -
    -

    Crew Manifest

    -
    -
    - {{:data.manifest}} -
    - {{else data.mode == 3}} -
    -

    Records

    -
    -
    -
    - Authorized Identity: -
    -
    - {{:helper.link(data.scan_name, 'eject', {'action' : 'PRG_scan'})}} -
    -
    -
    - {{if data.authenticated}} - {{:data.id_change_html}} - {{if data.centcom_access}} - {{:helper.link('Delete Records By: ' + data.scan_owner, null, {'action' : 'PRG_wipe_my_logs'}, null, 'linkDanger')}} - {{/if}} - {{if !data.target_dept}} - {{:helper.link('Delete ALL Records', null, {'action' : 'PRG_wipe_all_logs'}, null, 'linkDanger')}} - {{/if}} - {{else}} - Please insert an authorized ID into the terminal to proceed.
    - {{/if}} -
    - {{else}} -
    -

    Access Modification

    -
    - - {{if !data.authenticated}} - Please insert the IDs into the terminal to proceed.
    - {{/if}} - -
    -
    - Target Identity: -
    -
    - {{:helper.link(data.target_name, 'eject', {'action' : 'PRG_modify'})}} -
    -
    -
    -
    - Authorized Identity: -
    -
    - {{:helper.link(data.scan_name, 'eject', {'action' : 'PRG_scan'})}} -
    -
    -
    - - {{if data.authenticated}} - - - {{if data.has_modify}} -
    -

    Details

    -
    - -
    -
    -
    - - - Registered Name: -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - Account Number: -
    -
    - - -
    -
    -
    - -
    -
    - Terminations: -
    -
    - {{if data.target_rank == "Terminated"}} - {{:data.target_owner}} has already been terminated! - {{else}} - {{:helper.link('Terminate ' + data.target_owner, 'gear', {'action' : 'PRG_terminate'}, data.target_rank == "Terminated" ? 'disabled' : null, data.target_rank == "Terminated" ? 'disabled' : 'linkDanger')}} - {{/if}} -
    -
    - -
    -

    Assignment

    -
    - - -
    - -
    - - {{if data.centcom_access}} -
    -

    Central Command

    -
    -
    - {{for data.all_centcom_access}} - {{:helper.link(value.desc, '', {'action' : 'PRG_access', 'access_target' : value.ref, 'allowed' : value.allowed}, null, value.allowed ? 'selected' : null)}} - {{/for}} -
    -
    Card Skin
    - {{for data.all_centcom_skins}} - {{:helper.link(value.display_name, '', {'action' : 'PRG_skin', 'skin_target' : value.skin}, null,(data.current_skin == value.skin) ? 'selected' : null )}} - {{/for}} -
    -
    - {{else}} -
    -

    {{:data.station_name}}

    -
    -
    - {{for data.regions}} -
    -
    {{:value.name}}
    - {{for value.accesses :accessValue:accessKey}} -
    - {{:helper.link(accessValue.desc, '', {'action' : 'PRG_access', 'access_target' : accessValue.ref, 'allowed' : accessValue.allowed}, null, accessValue.allowed ? 'selected' : null)}} -
    - {{/for}} -
    - {{/for}} -
    -
    Card Skin
    - {{for data.card_skins}} - {{:helper.link(value.display_name, '', {'action' : 'PRG_skin', 'skin_target' : value.skin}, null,(data.current_skin == value.skin) ? 'selected' : null )}} - {{/for}} -
    -
    - {{/if}} - {{/if}} - {{/if}} - {{/if}} -{{/if}} diff --git a/nano/templates/chem_dispenser.tmpl b/nano/templates/chem_dispenser.tmpl deleted file mode 100644 index 74d694530c9..00000000000 --- a/nano/templates/chem_dispenser.tmpl +++ /dev/null @@ -1,93 +0,0 @@ - -
    -
    - Energy: -
    -
    - {{:helper.displayBar(data.energy, 0, data.maxEnergy, 'good', data.energy + ' Units')}} -
    -
    - -
    -
    - Dispense: -
    -
    - {{:helper.link('1', 'gear', {'amount' : 1}, (data.amount == 1) ? 'selected' : null)}} - {{:helper.link('5', 'gear', {'amount' : 5}, (data.amount == 5) ? 'selected' : null)}} - {{:helper.link('10', 'gear', {'amount' : 10}, (data.amount == 10) ? 'selected' : null)}} - {{:helper.link('20', 'gear', {'amount' : 20}, (data.amount == 20) ? 'selected' : null)}} - {{:helper.link('30', 'gear', {'amount' : 30}, (data.amount == 30) ? 'selected' : null)}} - {{:helper.link('50', 'gear', {'amount' : 50}, (data.amount == 50) ? 'selected' : null)}} -
    -
    -
     
    -
    -
    - {{if data.glass}} - Drink Dispenser - {{else}} - Chemical Dispenser - {{/if}} -
    -
    -
    -
    - {{for data.chemicals}} - {{:helper.link(value.title, 'arrow-circle-down', value.commands, null, data.glass ? 'fixedLeftWide' : 'fixedLeft')}} - {{/for}} - -
    -
    -
     
    -
    -
    - {{if data.glass}} - Glass - {{else}} - Beaker - {{/if}} Contents -
    -
    - {{:helper.link(data.glass ? 'Eject Glass' : 'Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}} -
    -
    -
    -
    -
    - {{if data.isBeakerLoaded}} - {{if data.beakerCurrentVolume}} - Dispose: - {{/if}} - Volume: {{:data.beakerCurrentVolume}} / {{:data.beakerMaxVolume}}
    - {{for data.beakerContents}} -
    -
    - - {{:value.volume}} units of {{:value.name}} - -
    -
    - {{:helper.link('Isolate', null, {'remove' : value.id, 'removeamount' : -1})}} - {{:helper.link('1', null, {'remove' : value.id, 'removeamount' : 1})}} - {{:helper.link('5', null, {'remove' : value.id, 'removeamount' : 5})}} - {{:helper.link('10', null, {'remove' : value.id, 'removeamount' : 10})}} - {{:helper.link('ALL', null, {'remove' : value.id, 'removeamount' : value.volume})}} -
    -
    -
    - {{empty}} - {{if data.glass}}Glass{{else}}Beaker{{/if}} is empty - - {{/for}} - {{else}} - - No {{if data.glass}}Glass{{else}}Beaker{{/if}} loaded - - {{/if}} -
    -
    -
    diff --git a/nano/templates/chem_heater.tmpl b/nano/templates/chem_heater.tmpl deleted file mode 100644 index 9d9e16fa7e1..00000000000 --- a/nano/templates/chem_heater.tmpl +++ /dev/null @@ -1,47 +0,0 @@ - -
    -
    - Status: -
    -
    - {{:helper.link(data.isActive ? 'On' : 'Off', 'power-off', {'toggle_on' : 1}, data.isBeakerLoaded ? null : 'disabled')}} -
    -
    -
    -
    - Target: -
    -
    - {{:helper.link(data.targetTemp + 'K', 'gear', {'adjust_temperature' : 'input'}, null)}} -
    -
    -
    -
    - Beaker -
    -
    - {{:helper.link('Eject', 'eject', {'eject_beaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}} -
    -
    -
    -
    -
    -
    -
    - {{if data.isBeakerLoaded}} - Volume: {{:helper.smoothRound(data.beakerCurrentVolume)}} / {{:data.beakerMaxVolume}}
    - Temperature: {{:helper.smoothRound(data.currentTemp)}} Kelvin
    - {{for data.beakerContents}} - {{:value.volume}} units of {{:value.name}}
    - {{/for}} - {{else}} - No beaker loaded - {{/if}} -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/nano/templates/chem_master.tmpl b/nano/templates/chem_master.tmpl deleted file mode 100644 index e91a43d357b..00000000000 --- a/nano/templates/chem_master.tmpl +++ /dev/null @@ -1,106 +0,0 @@ -{{if data.beaker}} -
    {{:helper.link('Eject Beaker and Clear Buffer', 'eject', {'eject': 1})}}
    -
    -

    Add to buffer:

    - {{for data.beaker_reagents}} -
    -
    {{:value.name}}, {{:value.volume}} units
    - {{:helper.link('Analyze', 'search', {'analyze': 1, 'desc': value.description, 'name': value.name})}} - {{:helper.link('1', 'plus', {'add': value.id, 'amount': 1})}} - {{:helper.link('5', 'plus', {'add': value.id, 'amount': 5})}} - {{:helper.link('10', 'plus', {'add': value.id, 'amount': 10})}} - {{:helper.link('All', 'plus', {'add': value.id, 'amount': value.volume})}} - {{:helper.link('Custom', 'plus', {'addcustom': value.id})}} -
    - {{/for}} -
    -
    -

    -
    - Transfer to {{:helper.link(data.mode ? 'beaker' : 'disposal', 'arrows-h', {'toggle': 1})}} -
    -

    - {{for data.buffer_reagents}} -
    -
    {{:value.name}}, {{:value.volume}} units
    - {{:helper.link('Analyze', 'search', {'analyze': 1, 'desc': value.description, 'name': value.name})}} - {{:helper.link('1', 'plus', {'remove': value.id, 'amount': 1})}} - {{:helper.link('5', 'plus', {'remove': value.id, 'amount': 5})}} - {{:helper.link('10', 'plus', {'remove': value.id, 'amount': 10})}} - {{:helper.link('All', 'plus', {'remove': value.id, 'amount': value.volume})}} - {{:helper.link('Custom', 'plus', {'removecustom': value.id})}} -
    - {{/for}} -
    -
    -

    Containers:

    -
    - {{if data.condi}} - {{:helper.link('Create pack (10u max)', 'arrow-right', {'createpill': 1})}} - {{:helper.link('Create bottle (50u max)', 'arrow-right', {'createbottle': 1})}} - {{else}} - - - - - - - - - - - - - - - - -
    - {{:helper.link('Create pill (100u max)', 'arrow-right', {'createpill': 1})}} - - {{:helper.link('Multiple', 'arrow-right', {'createpill_multiple': 1})}} - - {{:helper.link('Style', 'pencil', {'change_pill': 1})}} - - -
    - {{:helper.link('Create patch (30u max)', 'arrow-right', {'createpatch': 1})}} - - {{:helper.link('Multiple', 'arrow-right', {'createpatch_multiple': 1})}} -
    - {{:helper.link('Create bottle (50u max)', 'arrow-right', {'createbottle': 1})}} - - {{:helper.link('Style', 'pencil', {'change_bottle': 1})}} - - -
    - {{/if}} -
    -
    -{{else}} - No beaker loaded
    -{{/if}} -
    - - {{if data.loaded_pill_bottle}} - - - - - -
    - {{:helper.link('Pill Bottle (' + data.loaded_pill_bottle_contents_len + '/' + data.loaded_pill_bottle_storage_slots + ')', 'eject', {'ejectp': 1})}} - - {{:helper.link('Change Wrapper', 'pencil', {'change_pillbottle': 1})}} -
    - {{else}} - - - - -
    - {{:helper.link('No Pill Bottle', 'eject', {}, 'disabled')}} -
    - {{/if}} -
    -
    \ No newline at end of file diff --git a/nano/templates/cloning_console.tmpl b/nano/templates/cloning_console.tmpl deleted file mode 100644 index c541ca6d6b4..00000000000 --- a/nano/templates/cloning_console.tmpl +++ /dev/null @@ -1,148 +0,0 @@ - -
    -
    -
    Menu
    -
    - {{:helper.link('Main', 'home', {'menu' : 1}, data.menu == 1 ? 'selected' : '')}}{{:helper.link('Records', 'folder-open', {'menu' : 2}, data.menu == 2 ? 'selected' : '')}} -
    -
    -
    -
    Refresh
    -
    - {{:helper.link('Refresh', 'refresh', {'refresh' : 1})}} -
    -
    -
    -
    Autoprocessing
    -
    - {{if data.autoallowed}} - {{:helper.link('Enabled', 'check', {'task' : 'autoprocess'}, data.autoprocess ? 'selected' : '')}}{{:helper.link('Disabled', 'close', {'task' : 'stopautoprocess'}, !data.autoprocess ? 'selected' : '')}} - {{else}} - {{:helper.link('Enabled', 'check', null, 'disabled')}}{{:helper.link('Disabled', 'close', null, 'selected')}} - {{/if}} -
    -
    - {{if data.disk}} -
    -
    Disk
    -
    {{:helper.link('Eject', 'eject', {'disk' : 'eject'})}}
    -
    - {{/if}} -
    -{{if data.menu == 1}} -

    Modules

    -
    -
    Scanner
    -
    - {{if data.scanner}} Found!{{else}} Error: Not detected!{{/if}} -
    -
    -
    -
    Pods
    -
    - {{if data.numberofpods}} Found {{:data.numberofpods}}!{{else}} Error: None detected!{{/if}} -
    -
    - {{if data.scanner}} -

    Scanner Functions

    - {{if data.loading}} - Scanning... - {{else}} - {{:data.scantemp}} - {{/if}} -
    -
    Scan Occupant
    -
    {{:helper.link('Scan', 'search', {'scan' : 1}, !data.occupant ? 'disabled' : '')}}
    -
    -
    -
    Scanner Lock
    -
    {{if data.occupant}}{{:helper.link('Engaged', 'lock', {'lock' : 1}, data.locked ? 'selected' : '')}}{{else}} {{:helper.link('Engaged', 'lock', null, 'disabled')}}{{/if}}{{:helper.link('Disengaged', 'unlock', {'lock' : 1}, !data.locked ? 'selected' : '')}}
    -
    - {{if data.can_brainscan}} -
    -
    Scan Mode
    -
    - {{:helper.link('Brain', null, {'toggle_mode' : 1}, !data.scan_mode ? '' : 'selected')}}{{:helper.link('Body', null, {'toggle_mode' : 1}, data.scan_mode ? '' : 'selected')}} -
    -
    - {{/if}} - {{/if}} - {{if data.numberofpods}} -

    Pods

    - {{for data.pods}} - {{:value.name}}
    -
    -
    Biomass
    -
    - {{:value.biomass}} -
    -
    - -
    -
    Select
    -
    - {{:helper.link('Select', 'check', {'selectpod' : value.pod}, value.pod == data.selected_pod ? 'selected' : '')}} -
    -
    - - {{/for}} - {{/if}} -{{/if}} -{{if data.menu == 2}} -

    Current Records

    - {{if data.temp}} -
    - {{:data.temp}} -
    - {{/if}} - {{for data.records}} -
    {{:helper.link(value.realname, 'arrow-circle-down', {'view_rec' : value.record})}}
    - {{empty}} - No records found! - {{/for}} -{{/if}} -{{if data.menu == 3}} -

    Selected Record

    - {{if data.temp}} -
    - {{:data.temp}} -
    - {{/if}} - {{if !data.activerecord}} -
    Error: Record not found!
    - {{else}} -
    - Real Name: {{:data.realname}}
    - Health: {{:data.health}} (OXY - BURN - TOX - BRUTE) -
    -
    - UI: {{:data.unidentity}}
    - SE:
    {{:data.strucenzymes}}
    -
    - {{if data.disk}} -
    -
    Disk
    -
    {{:helper.link('Load from Disk', 'floppy-o', {'disk' : 'load'})}}{{:helper.link('Save UI + UE', 'arrow-circle-up', {'save_disk' : 'ue'})}}{{:helper.link('Save UI', 'arrow-circle-up', {'save_disk' : 'ui'})}}{{:helper.link('Save SE', 'arrow-circle-up', {'save_disk' : 'se'})}}
    -
    - {{/if}} -
    -
    Actions
    -
    {{:helper.link('Clone', 'arrow-right', {'clone' : data.activerecord}, !data.podready ? 'disabled' : '')}}{{:helper.link('Delete', 'trash', {'del_rec' : 1})}}
    -
    - {{/if}} -{{/if}} -{{if data.menu == 4}} -

    Confirm Record Deletion

    - {{if data.temp}} -
    - {{:data.temp}} -
    - {{/if}} -
    - {{:helper.link('Scan Card and Confirm', 'trash', {'del_rec' : 1})}}{{:helper.link('Cancel', 'close', {'menu' : 3})}} -
    -{{/if}} - diff --git a/nano/templates/comm_console.tmpl b/nano/templates/comm_console.tmpl deleted file mode 100644 index e7224df2ecc..00000000000 --- a/nano/templates/comm_console.tmpl +++ /dev/null @@ -1,149 +0,0 @@ - - -{{if !data.authenticated}} -
    Please swipe your ID card. {{:helper.link('Log In','unlock',{'login':1},null,'fixed')}}
    -{{else}} - {{if !data.is_ai}} -
    Please remember to {{:helper.link('Log Out','lock',{'logout':1},null,'fixed')}}
    - {{/if}} - {{if data.lastCallLoc}} -
    Most recent shuttle call/recall traced to: {{:data.lastCallLoc}}
    - {{else}} -
    Unable to trace most recent shuttle call/recall signal.
    - {{/if}} -
    -

    Emergency Shuttle:

    - {{if data.shuttle.eta}} -
    ETA:
    -
    - {{>data.shuttle.eta}} -
    - {{/if}} -
    Options:
    -
    - {{if data.shuttle.callStatus == 1 && !data.is_ai}} - {{:helper.link('Cancel Shuttle','arrow-left',{'operation':'cancelshuttle'})}} - {{else data.shuttle.callStatus == 2}} - {{:helper.link('Call Shuttle','arrow-circle-down',{'operation':'callshuttle'})}} - {{/if}} -
    -
    - - {{if data.screen==1}} - -

    Menu

    -
    - {{if data.authenticated==2}} -
    - {{:helper.link('Make an Announcement','info',{'operation':'announce'})}} -
    -
    - {{if data.emagged}} - {{:helper.link('Message [UNKNOWN]','envelope',{'operation':'MessageSyndicate'})}} -
    -
    - {{:helper.link('Reset Relays','refresh',{'operation':'RestoreBackup'})}} - {{else}} - {{:helper.link('Message CentComm','envelope',{'operation':'MessageCentcomm'})}} - {{/if}} -
    -
    - {{:helper.link('Request Nuclear Authentication Codes','exclamation-triangle',{'operation':'nukerequest'})}} -
    - {{/if}} -
    - {{:helper.link('Change Alert Level','signal',{'operation':'changeseclevel'})}} -
    -
    - {{:helper.link('Change Status Displays','info',{'operation':'status'})}} -
    -
    - {{:helper.link('Message List','comment',{'operation':'messagelist'})}} -
    -
    - {{:helper.link('Restart Nano-Mob Hunter GO! Server','power-off',{'operation':'RestartNanoMob'})}} -
    -
    - {{if data.atcSquelched}} - {{:helper.link('Enable ATC Relay', 'signal', {'operation': 'ToggleATC'})}} - {{else}} - {{:helper.link('Disable ATC Relay', 'signal', {'operation': 'ToggleATC'})}} - {{/if}} -
    -
    - {{else data.screen==2}} - -

    Status Displays

    - {{:helper.link('Back','home',{'operation':'main'})}} -

    Presets

    - {{for data.stat_display.presets}} -
    -
    {{:helper.link(value.label,'info',{'operation':'setstat','statdisp':value.name},null,(name==data.stat_display.type?'linkOn':''))}}
    -
    - {{/for}} -

    Alerts

    - {{for data.stat_display.alerts}} -
    -
    {{:helper.link(value.label,'exclamation-triangle',{'operation':'setstat','statdisp':'alert','alert':value.alert},null,(value.alert==data.stat_display.type?'linkOn':''))}}
    -
    - {{/for}} -

    Messages

    -
    - {{if data.stat_display.type}} -
    -
    {{:helper.link('Line 1:','gear',{'operation':'setmsg1'})}}
    -
    {{>data.stat_display.line_1}}
    -
    -
    -
    {{:helper.link('Line 2:','gear',{'operation':'setmsg2'})}}
    -
    {{>data.stat_display.line_2}}
    -
    - {{/if}} -
    - {{else data.screen==3}} - -

    Messages

    - {{if data.current_message}} - {{:helper.link('Messages','home',{'operation':'messagelist'})}} -

    {{:data.current_message_title}}

    -
    - {{:data.current_message}} -
    - {{else}} - {{:helper.link('Back','home',{'operation':'main'})}} - {{for data.messages}} -
    - {{:value.title}}
    - {{:helper.link('Open','envelope-o',{'operation':'messagelist','msgid':value.id})}} - {{:helper.link('Delete','close',{'operation':'delmessage','msgid':value.id})}} -
    - {{/for}} - {{/if}} - {{else data.screen==4}} - -

    Security Level

    - {{:helper.link('Back','home',{'operation':'main'})}} -
    -
    -
    Security Level:
    -
    {{>data.str_security_level}}
    -
    -
    -
    Presets:
    -
    - {{for data.levels}} - {{:helper.link(value.name,'comment',{'operation':'newalertlevel','level':value.id},null,(value.id==data.security_level?'linkOn':''))}} - {{/for}} -
    -
    -
    - {{/if}} -{{/if}} \ No newline at end of file diff --git a/nano/templates/comm_program.tmpl b/nano/templates/comm_program.tmpl deleted file mode 100644 index b1c64c6f4e3..00000000000 --- a/nano/templates/comm_program.tmpl +++ /dev/null @@ -1,129 +0,0 @@ -{{if !data.authenticated}} -
    Please swipe your ID card. {{:helper.link('Log In', 'unlock', {'PRG_login' : 1}, null, 'fixed')}}
    -{{else}} - {{if !data.is_ai}} -
    Please remember to {{:helper.link('Log Out', 'lock', {'PRG_logout' : 1}, null, 'fixed')}}
    - {{/if}} - {{if data.lastCallLoc}} -
    Most recent shuttle call/recall traced to: {{:data.lastCallLoc}}
    - {{else}} -
    Unable to trace most recent shuttle call/recall signal.
    - {{/if}} -
    -

    Emergency Shuttle:

    - {{if data.shuttle.eta}} -
    ETA:
    -
    - {{>data.shuttle.eta}} -
    - {{/if}} -
    Options:
    -
    - {{if data.shuttle.callStatus == 1 && !data.is_ai}} - {{:helper.link('Cancel Shuttle', 'arrow-left', {'PRG_operation' : 'cancelshuttle'})}} - {{else data.shuttle.callStatus == 2}} - {{:helper.link('Call Shuttle', 'arrow-circle-down', {'PRG_operation' : 'callshuttle'})}} - {{/if}} -
    -
    - - {{if data.screen==1}} - -

    Menu

    -
    - {{if data.authenticated==2}} -
    - {{:helper.link('Make an Announcement', 'info', {'PRG_operation' : 'announce'})}} -
    -
    - {{if data.emagged}} - {{:helper.link('Message [UNKNOWN]', 'envelope', {'PRG_operation' : 'MessageSyndicate'})}} - {{else}} - {{:helper.link('Message CentComm', 'envelope', {'PRG_operation' : 'MessageCentcomm'})}} - {{/if}} -
    -
    - {{:helper.link('Request Nuclear Authentication Codes', 'exclamation-triangle', {'PRG_operation' : 'nukerequest'})}} -
    - {{/if}} -
    - {{:helper.link('Change Alert Level', 'signal', {'PRG_operation' : 'changeseclevel'})}} -
    -
    - {{:helper.link('Change Status Displays', 'info', {'PRG_operation' : 'status'})}} -
    -
    - {{:helper.link('Message List', 'comment', {'PRG_operation' : 'messagelist'})}} -
    -
    - {{:helper.link('Restart Nano-Mob Hunter GO! Server', 'power-off', {'PRG_operation' : 'RestartNanoMob'})}} -
    -
    - {{else data.screen==2}} - -

    Status Displays

    - {{:helper.link('Back', 'home', {'PRG_operation' : 'main'})}} -

    Presets

    - {{for data.stat_display.presets}} -
    -
    {{:helper.link(value.label, 'info', {'PRG_operation' : 'setstat', 'statdisp' : value.name}, name == data.stat_display.type ? 'selected' : null)}}
    -
    - {{/for}} -

    Alerts

    - {{for data.stat_display.alerts}} -
    -
    {{:helper.link(value.label, 'exclamation-triangle', {'PRG_operation' : 'setstat', 'statdisp' : 'alert', 'alert' : value.alert}, value.alert == data.stat_display.type ? 'selected' : null)}}
    -
    - {{/for}} -

    Messages

    -
    - {{if data.stat_display.type}} -
    -
    {{:helper.link('Line 1:', 'gear', {'PRG_operation':'setmsg1'})}}
    -
    {{>data.stat_display.line_1}}
    -
    -
    -
    {{:helper.link('Line 2:', 'gear', {'PRG_operation':'setmsg2'})}}
    -
    {{>data.stat_display.line_2}}
    -
    - {{/if}} -
    - {{else data.screen==3}} - -

    Messages

    - {{if data.current_message}} - {{:helper.link('Messages', 'home', {'PRG_operation' : 'messagelist'})}} -

    {{:data.current_message_title}}

    -
    - {{:data.current_message}} -
    - {{else}} - {{:helper.link('Back', 'home', {'PRG_operation' : 'main'})}} - {{for data.messages}} -
    - {{:value.title}}
    - {{:helper.link('Open', 'envelope-o', {'PRG_operation' : 'messagelist', 'msgid' : value.id})}} - {{:helper.link('Delete', 'close', {'PRG_operation' : 'delmessage', 'msgid' : value.id})}} -
    - {{/for}} - {{/if}} - {{else data.screen==4}} - -

    Security Level

    - {{:helper.link('Back', 'home', {'PRG_operation' : 'main'})}} -
    -
    -
    Security Level:
    -
    {{>data.str_security_level}}
    -
    -
    -
    Presets:
    -
    - {{for data.levels}} - {{:helper.link(value.name, 'comment', {'PRG_operation' : 'newalertlevel', 'level' : value.id}, value.id == data.security_level ? 'selected' : null)}} - {{/for}} -
    -
    -
    - {{/if}} -{{/if}} \ No newline at end of file diff --git a/nano/templates/computer_fabricator.tmpl b/nano/templates/computer_fabricator.tmpl deleted file mode 100644 index 268f585a3da..00000000000 --- a/nano/templates/computer_fabricator.tmpl +++ /dev/null @@ -1,84 +0,0 @@ -{{:helper.link('Clear Order', 'circle', {'action' : 'clean_order'})}}

    -Your new computer device you always dreamed of is just four steps away...
    -{{if data.state == 0}} -
    -

    Step 1: Select your device type

    - {{:helper.link('Laptop', 'laptop', {'action' : 'pick_device', 'pick' : 1})}} - {{:helper.link('Tablet', 'tablet', {'action' : 'pick_device', 'pick' : 2})}} -
    -{{else data.state == 1}} -
    -

    Step 2: Personalise your device

    - - - - - - - - - - {{if data.devtype != 2}} - - - {{/if}} - - - {{if data.devtype != 2}} - - - - - {{/if}} -
    Current Price: - {{:data.totalprice}}C -
    Battery: - {{:helper.link('Standard', 'bolt', {'action' : 'hw_battery', 'battery' : 1}, data.hw_battery == 1 ? "selected" : null)}} - {{:helper.link('Upgraded', 'bolt', {'action' : 'hw_battery', 'battery' : 2}, data.hw_battery == 2 ? "selected" : null)}} - {{:helper.link('Advanced', 'bolt', {'action' : 'hw_battery', 'battery' : 3}, data.hw_battery == 3 ? "selected" : null)}} -
    Hard Drive: - {{:helper.link('Standard', 'hdd-o', {'action' : 'hw_disk', 'disk' : 1}, data.hw_disk == 1 ? "selected" : null)}} - {{:helper.link('Upgraded', 'hdd-o', {'action' : 'hw_disk', 'disk' : 2}, data.hw_disk == 2 ? "selected" : null)}} - {{:helper.link('Advanced', 'hdd-o', {'action' : 'hw_disk', 'disk' : 3}, data.hw_disk == 3 ? "selected" : null)}} -
    Network Card: - {{:helper.link('None', 'times', {'action' : 'hw_netcard', 'netcard' : 0}, data.hw_netcard == 0 ? "selected" : null)}} - {{:helper.link('Standard', 'signal', {'action' : 'hw_netcard', 'netcard' : 1}, data.hw_netcard == 1 ? "selected" : null)}} - {{:helper.link('Advanced', 'signal', {'action' : 'hw_netcard', 'netcard' : 2}, data.hw_netcard == 2 ? "selected" : null)}} -
    Nano Printer: - {{:helper.link('None', 'times', {'action' : 'hw_nanoprint', 'print' : 0}, data.hw_nanoprint == 0 ? "selected" : null)}} - {{:helper.link('Standard', 'print', {'action' : 'hw_nanoprint', 'print' : 1}, data.hw_nanoprint == 1 ? "selected" : null)}} -
    Card Reader: - {{:helper.link('None', 'times', {'action' : 'hw_card', 'card' : 0}, data.hw_card == 0 ? "selected" : null)}} - {{:helper.link('Standard', 'credit-card', {'action' : 'hw_card', 'card' : 1}, data.hw_card == 1 ? "selected" : null)}} -
    Processor Unit: - {{:helper.link('Standard', 'tasks', {'action' : 'hw_cpu', 'cpu' : 1}, data.hw_cpu == 1 ? "selected" : null)}} - {{:helper.link('Advanced', 'tasks', {'action' : 'hw_cpu', 'cpu' : 2}, data.hw_cpu == 2 ? "selected" : null)}} -
    Tesla Relay: - {{:helper.link('None', 'times', {'action' : 'hw_tesla', 'tesla' : 0}, data.hw_tesla == 0 ? "selected" : null)}} - {{:helper.link('Standard', 'plug', {'action' : 'hw_tesla', 'tesla' : 1}, data.hw_tesla == 1 ? "selected" : null)}} -
    - - - -
    Confirm Order: - {{:helper.link('CONFIRM', 'check', {'action' : 'confirm_order'})}} -
    - -
    - Battery allows your device to operate without external utility power source. Advanced batteries increase battery life.
    - Hard Drive stores file on your device. Advanced drives can store more files, but use more power, shortening battery life.
    - Network Card allows your device to wirelessly connect to stationwide NTNet network. Basic cards are limited to on-station use, while advanced cards can operate anywhere near the station, which includes the asteroid outposts.
    - Processor Unit is critical for your device's functionality. It allows you to run programs from your hard drive. Advanced CPUs use more power, but allow you to run more programs on background at once.
    - Tesla Relay is an advanced wireless power relay that allows your device to connect to nearby area power controller to provide alternative power source. This component is currently unavailable on tablet computers due to size restrictions.
    - Nano Printer is device that allows for various paperwork manipulations, such as, scanning of documents or printing new ones. This device was certified EcoFriendlyPlus and is capable of recycling existing paper for printing purposes.
    - Card Reader adds a slot that allows you to manipulate RFID cards. Please note that this is not necessary to allow the device to read your identification, it is just necessary to manipulate other cards. -
    -{{else data.state == 2}} -

    Step 3: Payment

    - Your device is now ready for fabrication..
    - Please swipe your identification card to finish purchase.
    - Total price: {{:data.totalprice}}C -{{else data.state == 3}} -

    Step 4: Thank you for your purchase

    - Should you experience any issues with your new device, contact technical support at support@computerservice.nt -{{/if}} diff --git a/nano/templates/computer_main.tmpl b/nano/templates/computer_main.tmpl deleted file mode 100644 index 9aad3d58609..00000000000 --- a/nano/templates/computer_main.tmpl +++ /dev/null @@ -1,9 +0,0 @@ -No program loaded. Please select program from list below. -
    - - {{for data.programs}} -
    {{:helper.link(value.desc, null, {'action' : 'PC_runprogram', 'name' : value.name})}} - {{:helper.link('', null, {'action' : 'PC_killprogram', 'name' : value.name}, value.running ? null : 'disabled')}} - {{/for}} -
    -
    \ No newline at end of file diff --git a/nano/templates/cryo.tmpl b/nano/templates/cryo.tmpl deleted file mode 100644 index de949c855f2..00000000000 --- a/nano/templates/cryo.tmpl +++ /dev/null @@ -1,117 +0,0 @@ - -

    Cryo Cell Status

    - -
    - {{if !data.hasOccupant}} -
    Cell Unoccupied
    - {{else}} -
    - {{:data.occupant.name}}   - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
    - - {{if data.occupant.stat < 2}} -
    -
    Health:
    - {{if data.occupant.health >= 0}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}} - {{else}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}} - {{/if}} -
    {{:helper.smoothRound(data.occupant.health)}}
    -
    - -
    -
    Brute Damage:
    - {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.bruteLoss)}}
    -
    - -
    -
    Resp. Damage:
    - {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.oxyLoss)}}
    -
    - -
    -
    Toxin Damage:
    - {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.toxLoss)}}
    -
    - -
    -
    Burn Severity:
    - {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.fireLoss)}}
    -
    - {{/if}} - {{/if}} -
    -
    -
    -
    Cell Temperature:
    - {{:helper.smoothRound(data.cellTemperature)}} K -
    -
    -
    - -

    Cryo Cell Operation

    -
    -
    - Cryo Cell Status: -
    -
    - {{:helper.link('On', 'power-off', {'switchOn' : 1}, data.isOperating ? 'selected' : null)}}{{:helper.link('Off', 'close', {'switchOff' : 1}, data.isOperating ? null : 'selected')}} -
    -
    - {{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectOccupant' : 1}, data.hasOccupant ? null : 'disabled')}} -
    -
    -
     
    -
    -
    - Beaker: -
    -
    - {{if data.isBeakerLoaded}} - {{:data.beakerLabel ? data.beakerLabel : 'No label'}}
    - {{if data.beakerVolume}} - {{:helper.smoothRound(data.beakerVolume)}} units remaining
    - {{else}} - Beaker is empty - {{/if}} - {{else}} - No beaker loaded - {{/if}} -
    -
    - {{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}} -
    -
    - -
     
    -
    -
    - Auto-eject healthy patients: -
    -
    - {{:helper.link('On', 'power-off', {'auto_eject_healthy_on' : 1}, data.auto_eject_healthy ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_healthy_off' : 0}, data.auto_eject_healthy ? null : 'selected')}} -
    -
    -
    -
    - Auto-eject dead patients: -
    -
    - {{:helper.link('On', 'power-off', {'auto_eject_dead_on' : 1}, data.auto_eject_dead ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_dead_off' : 0}, data.auto_eject_dead ? null : 'selected')}} -
    -
    diff --git a/nano/templates/dna_modifier.tmpl b/nano/templates/dna_modifier.tmpl deleted file mode 100644 index 2789a4a7881..00000000000 --- a/nano/templates/dna_modifier.tmpl +++ /dev/null @@ -1,313 +0,0 @@ - -

    Status

    - -
    - {{if !data.hasOccupant}} -
    Cell Unoccupied
    - {{else}} -
    - {{:data.occupant.name}} =>  - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
    - - {{if !data.occupant.isViableSubject || !data.occupant.uniqueIdentity || !data.occupant.structuralEnzymes}} -
    - The occupant's DNA structure is ruined beyond recognition, please insert a subject with an intact DNA structure. -
    - {{else data.occupant.stat < 2}} -
    -
    Health:
    - {{if data.occupant.health >= 0}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}} - {{else}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}} - {{/if}} -
    {{:helper.smoothRound(data.occupant.health)}}
    -
    - -
    -
    Radiation:
    - {{:helper.displayBar(data.occupant.radiationLevel, 0, 100, 'average')}} -
    {{:helper.smoothRound(data.occupant.radiationLevel)}}
    -
    - -
    -
    Unique Enzymes:
    -
    {{:data.occupant.uniqueEnzymes ? data.occupant.uniqueEnzymes : 'Unknown'}}
    -
    - - {{/if}} - {{/if}} -
    -
    - -

    Operations

    - {{if !data.occupant.isViableSubject || !data.occupant.uniqueIdentity || !data.occupant.structuralEnzymes}} -
    - No operation possible on this subject -
    - {{else}} -
    - {{:helper.link('Modify U.I.', 'link', {'selectMenuKey' : 'ui'}, data.selectedMenuKey == 'ui' ? 'selected' : null)}} - {{:helper.link('Modify S.E.', 'link', {'selectMenuKey' : 'se'}, data.selectedMenuKey == 'se' ? 'selected' : null)}} - {{:helper.link('Transfer Buffers', 'floppy-o', {'selectMenuKey' : 'buffer'}, data.selectedMenuKey == 'buffer' ? 'selected' : null)}} - {{:helper.link('Rejuvenators', 'plus', {'selectMenuKey' : 'rejuvenators'}, data.selectedMenuKey == 'rejuvenators' ? 'selected' : null)}} -
    - -
     
    - - {{if !data.selectedMenuKey || data.selectedMenuKey == 'ui'}} -

    Modify Unique Identifier

    - {{:helper.displayDNABlocks(data.occupant.uniqueIdentity, data.selectedUIBlock, data.selectedUISubBlock, data.dnaBlockSize, 'UI')}} -
    -
    -
    - Target: -
    -
    - {{:helper.link('-', null, {'changeUITarget' : 0}, (data.selectedUITarget > 0) ? null : 'disabled')}} -
     {{:data.selectedUITargetHex}} 
    - {{:helper.link('+', null, {'changeUITarget' : 1}, (data.selectedUITarget < 15) ? null : 'disabled')}} -
    -
    -
    -
    - {{:helper.link('Irradiate Block', 'exclamation-circle', {'pulseUIRadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}} -
    -
    - {{else data.selectedMenuKey == 'se'}} -

    Modify Structural Enzymes

    - {{:helper.displayDNABlocks(data.occupant.structuralEnzymes, data.selectedSEBlock, data.selectedSESubBlock, data.dnaBlockSize, 'SE')}} -
    -
    -
    - {{:helper.link('Irradiate Block', 'exclamation-circle', {'pulseSERadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}} -
    -
    - {{else data.selectedMenuKey == 'buffer'}} -

    Transfer Buffers

    - {{for data.buffers}} -

    Buffer {{:(index + 1)}}

    -
    -
    -
    - Load Data: -
    -
    - {{:helper.link('Subject U.I.', 'link', {'bufferOption' : 'saveUI', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}} - {{:helper.link('Subject U.I. + U.E.', 'link', {'bufferOption' : 'saveUIAndUE', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}} - {{:helper.link('Subject S.E.', 'link', {'bufferOption' : 'saveSE', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}} - {{:helper.link('From Disk', 'floppy-o', {'bufferOption' : 'loadDisk', 'bufferId' : (index + 1)}, (!data.hasDisk || !data.disk.data) ? 'disabled' : null)}} -
    -
    - {{if value.data}} -
    -
    - Label: -
    -
    - {{:helper.link(value.label, 'file', {'bufferOption' : 'changeLabel', 'bufferId' : (index + 1)})}} -
    -
    -
    -
    - Subject: -
    -
    - {{:value.owner ? value.owner : 'Unknown'}} -
    -
    -
    -
    - Stored Data: -
    -
    - {{:value.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}} - {{:value.ue ? ' + Unique Enzymes' : ''}} -
    -
    - {{else}} -
    -
    - This buffer is empty. -
    -
    - {{/if}} -
    -
    - Options: -
    -
    - {{:helper.link('Clear', 'trash', {'bufferOption' : 'clear', 'bufferId' : (index + 1)}, !value.data ? 'disabled' : null)}} - {{:helper.link('Injector', data.isInjectorReady ? 'pencil' : 'clock-o', {'bufferOption' : 'createInjector', 'bufferId' : (index + 1)}, (!data.isInjectorReady || !value.data) ? 'disabled' : null)}} - {{:helper.link('Block Injector', data.isInjectorReady ? 'pencil' : 'clock-o', {'bufferOption' : 'createInjector', 'bufferId' : (index + 1), 'createBlockInjector' : 1}, (!data.isInjectorReady || !value.data) ? 'disabled' : null)}} - {{:helper.link('Transfer', 'exclamation-circle', {'bufferOption' : 'transfer', 'bufferId' : (index + 1)}, (!data.hasOccupant || !value.data) ? 'disabled' : null)}} - {{:helper.link('Save To Disk', 'floppy-o', {'bufferOption' : 'saveDisk', 'bufferId' : (index + 1)}, (!value.data || !data.hasDisk) ? 'disabled' : null)}} -
    -
    -
    - {{/for}} - -

    Data Disk

    -
    - {{if data.hasDisk}} - {{if data.disk.data}} -
    -
    - Label: -
    -
    - {{:data.disk.label ? data.disk.label : 'No Label'}} -
    -
    -
    -
    - Subject: -
    -
    - {{:data.disk.owner ? data.disk.owner : 'Unknown'}} -
    -
    -
    -
    - Stored Data: -
    -
    - {{:data.disk.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}} - {{:data.disk.ue ? ' + Unique Enzymes' : ''}} -
    -
    - {{else}} -
    -
    - Disk is blank. -
    -
    - {{/if}} - {{else}} -
    -
    - No disk inserted. -
    -
    - {{/if}} -
    -
    - Options: -
    -
    - {{:helper.link('Wipe Disk', 'trash', {'bufferOption' : 'wipeDisk'}, (!data.hasDisk || !data.disk.data) ? 'disabled' : null)}} - {{:helper.link('Eject Disk', 'eject', {'bufferOption' : 'ejectDisk'}, !data.hasDisk ? 'disabled' : null)}} -
    -
    -
    - {{else data.selectedMenuKey == 'rejuvenators'}} -

    Rejuvenators

    -
    -
    - Inject: -
    -
    - {{:helper.link('5', 'pencil', {'injectRejuvenators' : 5}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}} - {{:helper.link('10', 'pencil', {'injectRejuvenators' : 10}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}} - {{:helper.link('20', 'pencil', {'injectRejuvenators' : 20}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}} - {{:helper.link('30', 'pencil', {'injectRejuvenators' : 30}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}} - {{:helper.link('50', 'pencil', {'injectRejuvenators' : 50}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}} -
    -
    -
     
    -
    -
    - Beaker: -
    -
    - {{if data.isBeakerLoaded}} - {{:data.beakerLabel ? data.beakerLabel : 'No label'}}
    - {{if data.beakerVolume}} - {{:data.beakerVolume}} units remaining
    - {{else}} - Beaker is empty - {{/if}} - {{else}} - No beaker loaded - {{/if}} -
    -
    - {{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}} -
    -
    - {{/if}} - -
     
    - - {{if !data.selectedMenuKey || data.selectedMenuKey == 'ui' || data.selectedMenuKey == 'se'}} -

    Radiation Emitter Settings

    -
    -
    - Intensity: -
    -
    - {{:helper.link('-', null, {'radiationIntensity' : 0}, (data.radiationIntensity > 1) ? null : 'disabled')}} -
     {{:data.radiationIntensity}} 
    - {{:helper.link('+', null, {'radiationIntensity' : 1}, (data.radiationIntensity < 10) ? null : 'disabled')}} -
    -
    -
    -
    - Duration: -
    -
    - {{:helper.link('-', null, {'radiationDuration' : 0}, (data.radiationDuration > 2) ? null : 'disabled')}} -
     {{:data.radiationDuration}} 
    - {{:helper.link('+', null, {'radiationDuration' : 1}, (data.radiationDuration < 20) ? null : 'disabled')}} -
    -
    -
    -
    -   -
    -
    - {{:helper.link('Pulse Radiation', 'exclamation-circle', {'pulseRadiation' : 1}, !data.hasOccupant ? 'disabled' : null)}} -
    -
    - {{/if}} -{{/if}} -
     
    - -
    - -
    -
    - Occupant: -
    -
    - {{:helper.link('Eject Occupant', 'eject', {'ejectOccupant' : 1}, data.locked || !data.hasOccupant || data.irradiating ? 'disabled' : null)}} -
    -
    -
    -
    - Door Lock: -
    -
    - {{:helper.link('Engaged', 'lock', {'toggleLock' : 1}, data.locked ? 'selected' : !data.hasOccupant ? 'disabled' : null)}} - {{:helper.link('Disengaged', 'unlock', {'toggleLock' : 1}, !data.locked ? 'selected' : data.irradiating ? 'disabled' : null)}} -
    -
    - -{{if data.irradiating}} -
    -
    -

    Irradiating Subject

    -

    For {{:data.irradiating}} seconds.

    -
    -
    -{{/if}} diff --git a/nano/templates/door_control.tmpl b/nano/templates/door_control.tmpl deleted file mode 100644 index 3c8dc66afb7..00000000000 --- a/nano/templates/door_control.tmpl +++ /dev/null @@ -1,69 +0,0 @@ - - -
    -
    - Main power is - {{if data.main_power_loss == 0}} - online - {{else data.main_power_loss == -1}} - offline - {{else}} - offline for {{:data.main_power_loss}} second{{:data.main_power_loss == 1 ? '' : 's'}} - {{/if}}. -
    -
    - {{:helper.link(data.main_power_loss ? 'Disabled' : 'Disable', null, {'command' : 'main_power'}, data.main_power_loss == 0 ? null : 'disabled', data.main_power_loss == 0 ? 'redButton' : null)}} -
    -
    - -
    -
    - Backup power is - {{if data.backup_power_loss == 0}} - online - {{else data.backup_power_loss == -1}} - offline - {{else}} - offline for {{:data.backup_power_loss}} second{{:data.backup_power_loss == 1 ? '' : 's'}} - {{/if}}. -
    -
    - {{:helper.link(data.backup_power_loss ? 'Disabled' : 'Disable', null, {'command' : 'backup_power'}, data.backup_power_loss == 0 ? null : 'disabled', data.backup_power_loss == 0 ? 'redButton' : null)}} -
    -
    - -
    -
    - Electrified Status: -
    -
    - {{:helper.link('Offline' , null, {'command' : 'electrify_permanently', 'activate' : "0" }, data.electrified == 0 ? 'selected' : null)}} - {{:helper.link(data.electrified <= 0 ? 'Temporary (30s)' : 'Temporary (' + data.electrified +'s)', null, {'command' : 'electrify_temporary', 'activate' : "1"}, null, data.electrified > 0 ? 'redButton' : null)}} - {{:helper.link('Permanent', null, {'command' : 'electrify_permanently', 'activate' : "1"}, data.electrified == -1 ? 'redButton' : null)}} -
    -
    - -
    - - - {{for data.commands}} - - - - - - {{/for}} -
    {{:value.name}}:{{:helper.link(value.enabled, null, {'command' : value.command, 'activate' : value.act ? 1 : 0}, value.active ? 'selected' : null)}}{{:helper.link(value.disabled, null, {'command' : value.command, 'activate' : value.act ? 0 : 1}, !value.active ? (value.danger ? 'redButton' : 'selected') : null)}}
    diff --git a/nano/templates/ert_config.tmpl b/nano/templates/ert_config.tmpl deleted file mode 100644 index c8ff0645b3b..00000000000 --- a/nano/templates/ert_config.tmpl +++ /dev/null @@ -1,75 +0,0 @@ -
    - - - - - - -
    Station Alert Level{{:data.alert_level}}
    ERT Type - {{if data.ert_type == 'Code Amber'}} AMBER {{else}} {{:helper.link('Amber', null, {"set_code": 'Code Amber'})}} {{/if}} - {{if data.ert_type == 'Code Red'}} RED {{else}} {{:helper.link('Red', null, {"set_code": 'Code Red'})}} {{/if}} - {{if data.ert_type == 'Code Gamma'}} GAMMA {{else}} {{:helper.link('Gamma', null, {"set_code": 'Code Gamma'})}} {{/if}} -
    -
    -
    -
    - - - - - - - - - -
    DepartmentSlotsChange Slots
    Command{{if data.com == 0}}-{{else}}{{:helper.smoothRound(data.com)}}{{/if}} - {{:helper.link('0', null, {"set_com": 0})}} - {{:helper.link('1', null, {"set_com": 1})}}
    Security{{if data.sec == 0}}-{{else}}{{:helper.smoothRound(data.sec)}}{{/if}} - {{:helper.link('0', null, {"set_sec": 0})}} - {{:helper.link('1', null, {"set_sec": 1})}} - {{:helper.link('2', null, {"set_sec": 2})}} - {{:helper.link('3', null, {"set_sec": 3})}} - {{:helper.link('4', null, {"set_sec": 4})}} - {{:helper.link('5', null, {"set_sec": 5})}} -
    Medical{{if data.med == 0}}-{{else}}{{:helper.smoothRound(data.med)}}{{/if}} - {{:helper.link('0', null, {"set_med": 0})}} - {{:helper.link('1', null, {"set_med": 1})}} - {{:helper.link('2', null, {"set_med": 2})}} - {{:helper.link('3', null, {"set_med": 3})}} - {{:helper.link('4', null, {"set_med": 4})}} - {{:helper.link('5', null, {"set_med": 5})}} -
    Engineering{{if data.eng == 0}}-{{else}}{{:helper.smoothRound(data.eng)}}{{/if}} - {{:helper.link('0', null, {"set_eng": 0})}} - {{:helper.link('1', null, {"set_eng": 1})}} - {{:helper.link('2', null, {"set_eng": 2})}} - {{:helper.link('3', null, {"set_eng": 3})}} - {{:helper.link('4', null, {"set_eng": 4})}} - {{:helper.link('5', null, {"set_eng": 5})}} -
    Paranormal{{if data.par == 0}}-{{else}}{{:helper.smoothRound(data.par)}}{{/if}} - {{:helper.link('0', null, {"set_par": 0})}} - {{:helper.link('1', null, {"set_par": 1})}} - {{:helper.link('2', null, {"set_par": 2})}} - {{:helper.link('3', null, {"set_par": 3})}} - {{:helper.link('4', null, {"set_par": 4})}} - {{:helper.link('5', null, {"set_par": 5})}} -
    Janitor{{if data.jan == 0}}-{{else}}{{:helper.smoothRound(data.jan)}}{{/if}} - {{:helper.link('0', null, {"set_jan": 0})}} - {{:helper.link('1', null, {"set_jan": 1})}} - {{:helper.link('2', null, {"set_jan": 2})}} - {{:helper.link('3', null, {"set_jan": 3})}} - {{:helper.link('4', null, {"set_jan": 4})}} - {{:helper.link('5', null, {"set_jan": 5})}} -
    {{if data.ert_type == 'Code Gamma'}}GAMMA {{/if}}Cyborg{{if data.cyb == 0}}-{{else}}{{:helper.smoothRound(data.cyb)}}{{/if}} - {{:helper.link('0', null, {"set_cyb": 0})}} - {{:helper.link('1', null, {"set_cyb": 1})}} - {{:helper.link('2', null, {"set_cyb": 2})}} - {{:helper.link('3', null, {"set_cyb": 3})}} - {{:helper.link('4', null, {"set_cyb": 4})}} - {{:helper.link('5', null, {"set_cyb": 5})}} -
    -
    -
    -
    - {{:helper.link('Dispatch', null, {"dispatch_ert": 1})}} -
    - diff --git a/nano/templates/faxmachine.tmpl b/nano/templates/faxmachine.tmpl deleted file mode 100644 index e52c40eb927..00000000000 --- a/nano/templates/faxmachine.tmpl +++ /dev/null @@ -1,67 +0,0 @@ - -
    -

    Authorization

    -
    -
    -
    - Confirm identity: -
    -
    - {{:helper.link(data.scan_name, 'eject', {'scan' : 1})}} -
    -
    -
    -
    - Authorize: -
    -
    - {{:helper.link(data.authenticated ? 'Log Out' : 'Log In', data.authenticated ? 'unlock' : 'lock', {'auth' : 1})}} -
    -
    -
    -
    -

    Fax Menu

    -
    -
    -
    - Network: -
    - -
    - {{:data.network}} -
    -
    -
    -
    - Currently sending: -
    -
    - {{:helper.link(data.paper, 'eject', {'paper' : 1})}} - {{if data.paperinserted}} - {{:helper.link('Rename', 'pencil', {'rename' : 1})}} - {{/if}} -
    -
    -
    -
    - Sending to: -
    -
    - {{:helper.link(data.destination, 'print', {'dept' : 1}, !data.authenticated ? 'disabled' : '')}} -
    -
    -
    -
    - Action: -
    -
    - {{if data.authenticated}} - {{:helper.link(data.cooldown && data.respectcooldown ? "Realigning" : "Send", data.cooldown && data.respectcooldown ? 'clock-o' : "envelope-o", {'send' : 1}, data.cooldown && data.respectcooldown ? 'disabled' : "")}} - {{else}} - {{:helper.link(data.cooldown && data.respectcooldown ? "Realigning" : "Send", data.cooldown && data.respectcooldown ? 'clock-o' : "envelope-o", null, !data.authenticated ? 'disabled' : "")}} - {{/if}} -
    -
    \ No newline at end of file diff --git a/nano/templates/file_manager.tmpl b/nano/templates/file_manager.tmpl deleted file mode 100644 index 51d40b84475..00000000000 --- a/nano/templates/file_manager.tmpl +++ /dev/null @@ -1,76 +0,0 @@ -{{if data.error}} -

    An error has occurred and this program can not continue.

    - Additional information: {{:data.error}}
    - Please try again. If the problem persists contact your system administrator for assistance. - {{:helper.link('Restart program', null, {'action' : 'PRG_closefile'})}} -{{else}} - {{if data.filename}} -

    Viewing file {{:data.filename}}

    -
    - {{:helper.link('CLOSE', null, {'action' : 'PRG_closefile'})}} - {{:helper.link('EDIT', null, {'action' : 'PRG_edit'})}} - {{:helper.link('PRINT', null, {'action' : 'PRG_printfile'})}} -

    - {{:data.filedata}} - {{else}} -

    Available files (local):

    -
    - - - - - - - - {{for data.files}} - - - - - - - {{/for}} -
    File nameFile typeFile size (GQ)Operations
    {{:value.name}}.{{:value.type}}{{:value.size}}GQ - {{:helper.link('VIEW', 'eye', {'action' : 'PRG_openfile', 'name' : value.name})}} - {{:helper.link('DELETE', 'trash', {'action' : 'PRG_deletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} - {{:helper.link('RENAME', 'pencil', {'action' : 'PRG_rename', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} - {{:helper.link('CLONE', 'files-o', {'action' : 'PRG_clone', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} - {{if !value.encrypted}} - {{:helper.link('ENCRYPT', null, {'action' : 'PRG_encrypt', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} - {{else}} - {{:helper.link('DECRYPT', null, {'action' : 'PRG_decrypt', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} - {{/if}} - {{if data.usbconnected}} - {{:helper.link('EXPORT', 'upload', {'action' : 'PRG_copytousb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} - {{/if}} -
    -
    - {{if data.usbconnected}} -

    Available files (portable device):

    -
    - - - - - - - - {{for data.usbfiles}} - - - - - - - {{/for}} -
    File nameFile typeFile size (GQ)Operations
    {{:value.name}}.{{:value.type}}{{:value.size}}GQ - {{:helper.link('DELETE', 'trash', {'action' : 'PRG_usbdeletefile', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} - {{if data.usbconnected}} - {{:helper.link('IMPORT', 'download', {'action' : 'PRG_copyfromusb', 'name' : value.name}, value.undeletable ? 'disabled' : null)}} - {{/if}} -
    -
    - {{/if}} - {{:helper.link('NEW DATA FILE', 'file-text', {'action' : 'PRG_newtextfile'})}} - {{/if}} -{{/if}} \ No newline at end of file diff --git a/nano/templates/firealarm.tmpl b/nano/templates/firealarm.tmpl deleted file mode 100644 index 5ddaa2977f4..00000000000 --- a/nano/templates/firealarm.tmpl +++ /dev/null @@ -1,69 +0,0 @@ - -
    -
    - Fire Alarm -
    -
    - {{:helper.link('Activated', 'lightbulb-o', {'alarm' : 1}, data.fire ? 'selected' : '')}} - {{:helper.link('Deactivated', 'lightbulb-o', {'reset' : 1}, data.fire ? '' : 'selected')}} -
    -
    - -
    -
    - Security Level -
    -
    - {{if data.sec_level == "green"}} - Green - {{else data.sec_level == "blue"}} - Blue - {{else data.sec_level == "red"}} - Red - {{else data.sec_level == "gamma"}} - Gamma - {{else data.sec_level == "epsilon"}} - Contracts terminated. - {{else data.sec_level == "delta"}} - Nuclear device primed. - {{else}} - ERR: Unable to ascertain security level. Please see an authorized NT fire alarm technician. - {{/if}} -
    -
    - -
    -
    - Timer System -
    -
    - {{:helper.link('Initiate', 'play', {'time': 1}, data.timing ? 'selected' : '')}} - {{:helper.link('Stop', 'pause', {'time': 0}, data.timing ? '' : 'selected')}} -
    -
    - -
    -
    - Time Modifiers -
    -
    - {{:helper.link('30', 'minus', {'tp': -30})}} - {{:helper.link('1', 'minus', {'tp': -1 })}} - {{:helper.link('1', 'plus', {'tp': +1 })}} - {{:helper.link('30', 'plus', {'tp': +30})}} -
    -
    - -{{if data.timing}} -
    -
    - Time Left -
    -
    - {{:data.time_left}} -
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/freezer.tmpl b/nano/templates/freezer.tmpl deleted file mode 100644 index 582598e05c5..00000000000 --- a/nano/templates/freezer.tmpl +++ /dev/null @@ -1,51 +0,0 @@ -
    -
    - Status: -
    -
    - {{:helper.link('On', 'power-off', {'toggleStatus' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'close', {'toggleStatus' : 1}, data.on ? null : 'selected')}} -
    -
    - -
    -
    - Gas Pressure: -
    -
    - {{:helper.smoothRound(data.gasPressure)}} kPa -
    -
    - -

    Gas Temperature

    -
    -
    - Current: -
    -
    - {{:helper.displayBar(data.gasTemperature, data.minGasTemperature, data.maxGasTemperature, data.gasTemperatureClass)}} -
    - {{:helper.smoothRound(data.gasTemperature)}} K ({{:helper.smoothRound(data.gasTemperatureCelsius)}} °C) -
    -
    -
    - -
    -
    - Target: -
    -
    - {{:helper.displayBar(data.targetGasTemperature, data.minGasTemperature, data.maxGasTemperature)}} -
    - {{:helper.link('Min', null, {'minimum' : 'yes'}, (data.targetGasTemperature > data.minGasTemperature) ? null : 'disabled')}} - {{:helper.link('-', null, {'temp' : -100}, (data.targetGasTemperature > data.minGasTemperature) ? null : 'disabled')}} - {{:helper.link('-', null, {'temp' : -10}, (data.targetGasTemperature > data.minGasTemperature) ? null : 'disabled')}} - {{:helper.link('-', null, {'temp' : -1}, (data.targetGasTemperature > data.minGasTemperature) ? null : 'disabled')}} -
    {{:data.targetGasTemperature}} K ({{:data.targetGasTemperatureCelsius}} °C)
    - {{:helper.link('+', null, {'temp' : 1}, (data.targetGasTemperature < data.maxGasTemperature) ? null : 'disabled')}} - {{:helper.link('+', null, {'temp' : 10}, (data.targetGasTemperature < data.maxGasTemperature) ? null : 'disabled')}} - {{:helper.link('+', null, {'temp' : 100}, (data.targetGasTemperature < data.maxGasTemperature) ? null : 'disabled')}} - {{:helper.link('Max', null, {'maximum' : 'yes'}, (data.targetGasTemperature < data.maxGasTemperature) ? null : 'disabled')}} -
    -
    -
    - diff --git a/nano/templates/identification_computer.tmpl b/nano/templates/identification_computer.tmpl deleted file mode 100644 index 606af520168..00000000000 --- a/nano/templates/identification_computer.tmpl +++ /dev/null @@ -1,347 +0,0 @@ -{{if data.printing}} -
    The computer is currently busy.
    -
    -
    Printing...
    -
    -

    - Thank you for your patience! -

    -{{else}} - {{:helper.link('Access Modification', 'home', {'choice' : 'mode', 'mode_target' : 0}, !data.mode ? 'disabled' : null)}} - {{if !data.target_dept}}{{:helper.link('Job Management', 'gear', {'choice' : 'mode', 'mode_target' : 1}, data.mode == 1 ? 'disabled' : null)}}{{/if}} - {{:helper.link('Crew Manifest', 'folder-open', {'choice' : 'mode', 'mode_target' : 2}, data.mode == 2 ? 'disabled' : null)}} - {{if !data.target_dept}}{{:helper.link('Print', 'print', {'choice' : 'print'}, (data.mode == 2 || data.has_modify && !data.mode) ? null : 'disabled')}}{{/if}} - {{:helper.link('Records', 'file', {'choice' : 'mode', 'mode_target' : 3}, (data.mode == 3) ? 'disabled' : null)}} - - {{if data.mode == 1 && !data.target_dept}} -
    -

    Job Management

    -
    -
    -
    - Authorized Identity: -
    -
    - {{:helper.link(data.scan_name, 'eject', {'choice' : 'scan'})}} -
    -
    - Cooldown: -
    -
    - {{if data.cooldown_mins > 0 || data.cooldown_mins == 0 && data.cooldown_secs > 0}} - Next change in: {{:data.cooldown_mins}}:{{:data.cooldown_secs}} - {{else}} - Ready - {{/if}} -
    -
    -
    -
    - {{for data.job_slots}} -
    - {{:value.title}}: {{:value.current_positions}}/{{:value.total_positions}} - {{:helper.link('-', null, {'choice' : 'make_job_unavailable', 'job' : value.title}, value.can_close == 1 && data.authenticated ? null : 'disabled')}} - {{:helper.link('+', null, {'choice' : 'make_job_available', 'job' : value.title}, value.can_open == 1 && data.authenticated ? null : 'disabled')}} - {{:helper.link('Pri', null, {'choice' : 'prioritize_job', 'job' : value.title}, value.can_prioritize > 0 && data.authenticated ? null : 'disabled')}} {{if value.can_prioritize == 2}}Priority Job {{/if}} -
    - {{/for}} -
    - {{else data.mode == 2}} -
    -

    Crew Manifest

    -
    -
    - {{:data.manifest}} -
    - {{else data.mode == 3}} -
    -

    Records

    -
    -
    -
    - Authorized Identity: -
    -
    - {{:helper.link(data.scan_name, 'eject', {'choice' : 'scan'})}} -
    -
    -
    - {{if data.authenticated}} - {{:data.id_change_html}} - {{if data.centcom_access}} - {{:helper.link('Delete Records By: ' + data.scan_owner, null, {'choice' : 'wipe_my_logs'}, null, 'linkDanger')}} - {{/if}} - {{if !data.target_dept}} - {{:helper.link('Delete ALL Records', null, {'choice' : 'wipe_all_logs'}, null, 'linkDanger')}} - {{/if}} - {{else}} - Please insert an authorized ID into the terminal to proceed.
    - {{/if}} -
    - {{else}} -
    -

    Access Modification

    -
    - - {{if !data.authenticated}} - Please insert the IDs into the terminal to proceed.
    - {{/if}} - -
    -
    - Target Identity: -
    -
    - {{:helper.link(data.target_name, 'eject', {'choice' : 'modify'})}} -
    -
    -
    -
    - Authorized Identity: -
    -
    - {{:helper.link(data.scan_name, 'eject', {'choice' : 'scan'})}} -
    -
    -
    - - {{if data.authenticated}} - - - {{if data.has_modify}} - {{if !data.target_dept}} -
    -

    Details

    -
    - -
    -
    -
    - - - Registered Name: -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - Account Number: -
    -
    - - -
    -
    -
    - {{/if}} - {{if data.target_rank != "Terminated"}} -
    -
    - Demotions: -
    -
    - {{if data.target_rank == "Demoted"}} - {{:data.target_owner}} has already been demoted! - {{else}} - {{:helper.link('Demote ' + data.target_owner, 'gear', {'choice' : 'demote'}, (data.target_rank == "Civilian" || data.target_rank == "Unassigned") ? 'disabled' : null, (data.target_rank == "Civilian" || data.target_rank == "Unassigned") ? 'disabled' : 'linkDanger')}} - {{/if}} -
    -
    - {{/if}} - {{if !data.target_dept}} -
    -
    - Terminations: -
    -
    - {{if data.target_rank == "Terminated"}} - {{:data.target_owner}} has already been terminated! - {{else}} - {{:helper.link('Terminate ' + data.target_owner, 'gear', {'choice' : 'terminate'}, data.target_rank == "Terminated" ? 'disabled' : null, data.target_rank == "Terminated" ? 'disabled' : 'linkDanger')}} - {{/if}} -
    -
    - -
    -

    Assignment

    -
    - - {{else}} - -
    -

    Assignment for {{:data.target_owner}}

    -
    - - {{/if}} - - -
    - -
    - - {{if data.centcom_access}} -
    -

    Central Command

    -
    -
    - {{for data.all_centcom_access}} - {{:helper.link(value.desc, '', {'choice' : 'access', 'access_target' : value.ref, 'allowed' : value.allowed}, null, value.allowed ? 'selected' : null)}} - {{/for}} -
    -
    Card Skin
    - {{for data.all_centcom_skins}} - {{:helper.link(value.display_name, '', {'choice' : 'skin', 'skin_target' : value.skin}, null,(data.current_skin == value.skin) ? 'selected' : null )}} - {{/for}} -
    -
    - {{else}} - {{if !data.target_dept}} -
    -

    {{:data.station_name}}

    -
    -
    - {{for data.regions}} -
    -
    {{:value.name}}
    - {{for value.accesses :accessValue:accessKey}} -
    - {{:helper.link(accessValue.desc, '', {'choice' : 'access', 'access_target' : accessValue.ref, 'allowed' : accessValue.allowed}, null, accessValue.allowed ? 'selected' : null)}} -
    - {{/for}} -
    - {{/for}} -
    -
    Card Skin
    - {{for data.card_skins}} - {{:helper.link(value.display_name, '', {'choice' : 'skin', 'skin_target' : value.skin}, null,(data.current_skin == value.skin) ? 'selected' : null )}} - {{/for}} -
    -
    - {{/if}} - {{/if}} - {{/if}} - {{/if}} - {{/if}} -{{/if}} diff --git a/nano/templates/keycard_auth.tmpl b/nano/templates/keycard_auth.tmpl deleted file mode 100644 index 96a3f44f60a..00000000000 --- a/nano/templates/keycard_auth.tmpl +++ /dev/null @@ -1,39 +0,0 @@ - - -
    -

    Keycard Authentication Device

    - This device is used to trigger certain high security events. It requires the simultaneous swipe of two high-level ID cards. -
    - {{if data.screen == 1}} -

    Trigger Event

    - {{:helper.link('Red Alert', 'exclamation-triangle', { 'triggerevent' : 'Red Alert' })}} - {{:helper.link('Emergency Response Team', 'exclamation-triangle', { 'triggerevent' : 'Emergency Response Team' })}} - {{:helper.link('Grant Emergency Maintenance Access', 'unlock', { 'triggerevent' : 'Grant Emergency Maintenance Access' })}} - {{:helper.link('Revoke Emergency Maintenance Access', 'lock', { 'triggerevent' : 'Revoke Emergency Maintenance Access' })}} - {{:helper.link('Activate Station-Wide Emergency Access', 'unlock', { 'triggerevent' : 'Activate Station-Wide Emergency Access' })}} - {{:helper.link('Deactivate Station-Wide Emergency Access', 'lock', { 'triggerevent' : 'Deactivate Station-Wide Emergency Access' })}} - {{else data.screen == 2}} - {{if data.event == 'Emergency Response Team'}} -
    -
    Reason for ERT Call:
    -
    - {{:helper.link(data.ertreason, 'exclamation-triangle', { 'ert' : 'callreason' })}} -
    -
    - {{/if}} -

    Please swipe your card to authorize the following event:

    -
    {{:data.event}}
    - {{:helper.link('Back', 'close', { 'reset' : 1 })}} - {{/if}} -
    -
    \ No newline at end of file diff --git a/nano/templates/laptop_configuration.tmpl b/nano/templates/laptop_configuration.tmpl deleted file mode 100644 index 95dd315b90e..00000000000 --- a/nano/templates/laptop_configuration.tmpl +++ /dev/null @@ -1,71 +0,0 @@ -Welcome to computer configuration utility. Please consult your system administrator if you have any questions about your device.
    -
    -

    Power Supply

    - {{if data.battery}} -
    -
    Battery Status
    -
    Active
    -
    -
    -
    Battery Rating
    -
    {{:data.battery.max}}
    -
    -
    -
    ETA
    -
    {{:data.eta.hours ? helper.smoothRound(data.eta.hours) + "h " : ""}}{{:data.eta.minutes ? helper.smoothRound(data.eta.minutes) + "m " : ""}}{{:helper.smoothRound(data.eta.seconds)}}s
    -
    -
    -
    Battery Charge
    - {{:helper.displayBar(data.battery.charge, 0, data.battery.max, (data.battery.charge > data.battery.max/2) ? 'good' : (data.battery.charge > data.battery.max/4) ? 'average' : 'bad')}} -
    - {{:helper.smoothRound(data.battery.charge, 1)}}/{{:data.battery.max}} -
    -
    - {{else}} -
    -
    Battery Status
    -
    Not Available
    -
    - {{/if}} - -
    -
    Power Usage
    -
    {{:data.power_usage}}W
    -
    -
    - -
    -

    File System

    -
    -
    Used Capacity
    - {{:helper.displayBar(data.disk_used, 0, data.disk_size, (data.disk_used > data.disk_size*0.75) ? 'bad' : (data.disk_used > data.disk_size/2) ? 'average' : 'good')}} -
    - {{:helper.smoothRound(data.disk_used, 1)}}/{{:data.disk_size}}GQ -
    -
    -
    - -
    -

    Computer Components

    - {{for data.hardware}} -

    {{:value.name}}

    - {{:value.desc}}
    -
    -
    State
    -
    {{:value.enabled ? "Enabled" : "Disabled"}}
    -
    - -
    -
    Power Usage
    -
    {{:value.powerusage}}W
    -
    - - {{if !value.critical}} -
    -
    Toggle Component
    -
    {{:helper.link(value.enabled ? 'On' : 'Off', value.enabled ? 'power-off' : 'close', {'action' : 'PC_toggle_component', 'name' : value.name})}}
    -
    - {{/if}} -

    - {{/for}} -
    \ No newline at end of file diff --git a/nano/templates/mech_bay_console.tmpl b/nano/templates/mech_bay_console.tmpl deleted file mode 100644 index 2a4cf5d6085..00000000000 --- a/nano/templates/mech_bay_console.tmpl +++ /dev/null @@ -1,55 +0,0 @@ -

    Subsystems

    -
    -
    - Power Port: -
    -
    - {{if data.recharge_port}} - OK - {{else}} - ERROR - {{/if}} -
    -
    -
    -
    - Mecha: -
    -
    - {{if data.has_mech}} - LOCATED ({{:data.mecha_name}}) - {{else}} - NONE - {{/if}} -
    -
    -{{if data.has_mech}} -

    Charging

    -
    -
    - Charge Level: -
    -
    - {{:helper.displayBar(data.mecha_charge_percentage, 0, 100)}} -
    - {{:helper.smoothRound(data.mecha_charge_percentage)}}%
    -
    -
    -
    -
    -
    - Cell Rating: -
    -
    - {{:data.mecha_maxcharge}} -
    -
    -
    -
    - Cell Charge: -
    -
    - {{:helper.smoothRound(data.mecha_charge)}} -
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/med_data.tmpl b/nano/templates/med_data.tmpl deleted file mode 100644 index f47e13c6885..00000000000 --- a/nano/templates/med_data.tmpl +++ /dev/null @@ -1,171 +0,0 @@ - - - - -
    -
    - Confirm Identity: -
    -
    - {{:helper.link(data.scan ? data.scan : "----------", 'eject', {'scan' : 1}, null, data.scan ? 'itemContentWide' : 'fixedLeft')}} -
    -
    -
    -{{if data.authenticated}} - {{if data.screen == 1}} -

    Menu

    -
    -
    {{:helper.link('Search Records', 'search', {'search' : 1})}}
    -
    {{:helper.link('List Records', 'list', {'screen' : 2})}}
    -
    {{:helper.link('Virus Database', 'database', {'screen' : 5})}}
    -
    {{:helper.link('Medbot Tracking', 'plus-square', {'screen' : 6})}}
    -
    {{:helper.link('Record Maintenance', 'wrench', {'screen' : 3})}}
    -
    {{:helper.link('Logout', 'lock', {'logout' : 1})}}
    -
    - {{else data.screen == 2}} -

    Record List

    - {{for data.records}} -
    {{:helper.link(value.id + ': ' + value.name, 'user', {'d_rec' : value.ref})}}
    - {{/for}} -

    -
    {{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
    - {{else data.screen == 3}} -

    Records Maintenance

    -
    {{:helper.link('Backup To Disk', 'download', {'back' : 1}, 'disabled')}}
    -
    {{:helper.link('Upload From disk', 'upload', {'u_load' : 1}, 'disabled')}}
    -
    {{:helper.link('Delete All Records', 'trash', {'del_all' : 1})}}
    -
    {{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
    - {{else data.screen == 4}} -

    Medical Record

    -

    General Data

    - {{if data.general.empty}} -
    General Record Lost!
    - {{else}} -
    - - - -
    - {{for data.general.fields}} -
    -
    {{:value.field}}
    -
    {{:helper.link(value.value, value.edit ? 'pencil' : 'user-times', {'field' : value.edit}, value.edit ? null : 'disabled')}}
    -
    - {{/for}} -
    - {{if data.general.has_photos}} - {{for data.general.photos}} - {{if value.photo}} - - {{/if}} - {{/for}} - {{/if}} -
    -
    - {{/if}} - -

    Medical Data

    - {{if data.medical.empty}} -
    Medical Record Lost!
    -
    -
    {{:helper.link('New Record', 'plus', {'new' : 1})}}
    -
    -

    Menu

    - {{else}} - {{for data.medical.fields}} -
    -
    {{:value.field}}
    -
    {{:helper.link(value.value, 'pencil', {'field' : value.edit})}}
    -
    - {{if value.line_break}} -
    - {{/if}} - {{/for}} - -

    Comments/Log

    - {{for data.medical.comments}} -
    {{:value}}
    -
    {{:helper.link('Remove entry', 'trash', {'del_c' : index})}}
    -

    - {{/for}} - -
    -
    {{:helper.link('Add Entry', 'plus', {'add_c' : 1})}}
    -
    - -

    Menu

    -
    {{:helper.link('Delete Record (Medical Only)', 'trash', {'del_r' : 1})}}
    - {{/if}} - -
    {{:helper.link('Print Record', 'print', {'print_p' : 1})}}
    -
    {{:helper.link('Back', 'arrow-left', {'screen' : 2})}}
    - {{else data.screen == 5}} -

    Virus Database

    - {{for data.virus}} -
    {{:helper.link(value.name, 'arrow-right', {'vir' : value.D})}}
    - {{/for}} -

    - {{:helper.link('Back', 'arrow-left', {'screen' : 1})}} - {{else data.screen == 6}} -

    Medical Robot Monitor

    - {{for data.medbots}} -

    {{:value.name}}

    -
    -
    Location:
    -
    {{:value.x}}, {{:value.y}}
    -
    -
    -
    Status:
    -
    - {{:value.on ? "Online" : "Offline"}}. {{if value.use_beaker}}Reservoir: [{{:value.total_volume}}/{{:value.maximum_volume}}]{{else}}Using internal synthesizer.{{/if}} -
    -
    -
    - {{empty}} -

    None detected!

    - {{/for}} -
    {{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
    - {{/if}} -{{else}} -

    Menu

    - {{:helper.link('Login', 'unlock', {'login' : 1})}} -{{/if}} - -{{if data.temp}} -
    -
    - {{if data.temp.notice}} -
    {{:data.temp.text}}
    - {{else}} -
    {{:data.temp.text}}
    - {{/if}} - - - {{if data.temp.has_buttons}} -
    -
    -
    - {{for data.temp.buttons}} - {{:helper.link(value.name, value.icon, {'temp' : 1, 'temp_action' : value.href}, value.status)}} - {{/for}} -
    -
    -
    - {{/if}} - -
    -
    {{:helper.link('Clear screen', 'home', {'temp' : 1})}}
    -
    -
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/ntnet_chat.tmpl b/nano/templates/ntnet_chat.tmpl deleted file mode 100644 index c3f2573f8e2..00000000000 --- a/nano/templates/ntnet_chat.tmpl +++ /dev/null @@ -1,58 +0,0 @@ -{{if data.adminmode}} -

    ADMINISTRATIVE MODE

    -{{/if}} - -{{if data.title}} -
    -
    Current channel:
    -
    {{:data.title}}
    -
    -
    -
    Operator access:
    -
    {{if data.is_operator}}Enabled{{else}}Disabled{{/if}}
    -
    -
    -
    Controls:
    -
    - -
    {{:helper.link('Send message', 'comment-o', {'action' : 'PRG_speak'})}} -
    {{:helper.link('Change nickname', 'pencil', {'action' : 'PRG_changename'})}} -
    {{:helper.link('Toggle administration mode', 'lock', {'action' : 'PRG_toggleadmin'})}} -
    {{:helper.link('Leave channel', 'sign-out', {'action' : 'PRG_leavechannel'})}} -
    {{:helper.link('Save log to local drive', 'download', {'action' : 'PRG_savelog'})}} - {{if data.is_operator}} -
    {{:helper.link('Rename channel', 'pencil', {'action' : 'PRG_renamechannel'})}} -
    {{:helper.link('Set password', 'key', {'action' : 'PRG_setpassword'})}} -
    {{:helper.link('Delete channel', 'trash', {'action' : 'PRG_deletechannel'})}} - {{/if}} -
    -
    -
    - Chat Window -
    -
    -
    - {{for data.messages}} - {{:value.msg}}
    - {{/for}} -
    -
    -
    - Connected Users
    - {{for data.clients}} - {{:value.name}}
    - {{/for}} -{{else}} - Controls: - -
    {{:helper.link('Change nickname', 'pencil', {'action' : 'PRG_changename'})}} -
    {{:helper.link('New Channel', 'plus', {'action' : 'PRG_newchannel'})}} -
    {{:helper.link('Toggle administration mode', 'lock', {'action' : 'PRG_toggleadmin'})}} -
    - Available channels: - - {{for data.all_channels}} -
    {{:helper.link(value.chan, 'sign-in', {'action' : 'PRG_joinchannel', 'id' : value.id})}}
    - {{/for}} -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/ntnet_dos.tmpl b/nano/templates/ntnet_dos.tmpl deleted file mode 100644 index bff7bb3b9f0..00000000000 --- a/nano/templates/ntnet_dos.tmpl +++ /dev/null @@ -1,25 +0,0 @@ -{{:helper.syndicateMode()}} -{{if data.error}} - ##SYSTEM ERROR: {{:data.error}}{{:helper.link('RESET', null, {'action' : 'PRG_reset'})}} -{{else data.target}} - ##DoS traffic generator active. Tx: {{:data.speed}}GQ/s
    - {{for data.dos_strings}} - {{:value.nums}}
    - {{/for}} - {{:helper.link('ABORT', 'ban', {'action' : 'PRG_reset'})}} -{{else}} - ##DoS traffic generator ready. Select target device.
    -
    -
    Targeted device ID:
    -
    {{if data.focus}}{{:data.focus}}{{else}}None{{/if}}
    -
    -
    - {{:helper.link('EXECUTE', 'play', {'action' : 'PRG_execute'})}}
    -
    - Detected devices on network:
    - - {{for data.relays}} -
    {{:helper.link(value.id, null, {'action' : 'PRG_target_relay', 'targid' : value.id})}} - {{/for}} -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/ntnet_downloader.tmpl b/nano/templates/ntnet_downloader.tmpl deleted file mode 100644 index 35790bb3c7f..00000000000 --- a/nano/templates/ntnet_downloader.tmpl +++ /dev/null @@ -1,102 +0,0 @@ -Welcome to software download utility. Please select which software you wish to download.
    -{{if data.error}} -

    Download Error

    -
    -
    Information
    -
    {{:data.error}}
    -
    -
    -
    Reset Program
    -
    {{:helper.link('RESET', 'times', {'action' : 'PRG_reseterror'})}}
    -
    -{{else}} - {{if data.downloadname}} -

    Download Running

    -
    - Please wait... -
    -
    File name
    -
    {{:data.downloadname}}
    -
    -
    -
    File description
    -
    {{:data.downloaddesc}}
    -
    -
    -
    File size
    -
    {{:helper.smoothRound(data.downloadcompletion, 1)}}/{{:data.downloadsize}}GQ
    -
    -
    -
    Transfer Rate
    -
    {{:data.downloadspeed}} GQ/s
    -
    -
    -
    Download progress
    - {{:helper.displayBar(data.downloadcompletion, 0, data.downloadsize, (data.downloadcompletion > data.downloadsize/2) ? 'good' : (data.downloadcompletion > data.downloadsize/4) ? 'average' : 'bad')}} -
    - {{:helper.smoothRound(data.downloadcompletion, 1)}}/{{:data.downloadsize}}GQ -
    -
    -
    - {{else}} -

    Primary software repository

    -
    -
    -
    Hard drive
    - {{:helper.displayBar(data.disk_used, 0, data.disk_size, (data.disk_used > data.disk_size/2) ? 'bad' : (data.disk_used > data.disk_size/4) ? 'average' : 'good')}} -
    - {{:helper.smoothRound(data.disk_used, 1)}}/{{:data.disk_size}}GQ -
    -
    - {{for data.downloadable_programs}} -
    -
    -
    File name
    -
    {{:value.filename}} ({{:value.size}} GQ)
    -
    -
    -
    Program name
    -
    {{:value.filedesc}}
    -
    -
    -
    Description
    -
    {{:value.fileinfo}}
    -
    -
    -
    Compatibility
    -
    {{:value.compatibility}}
    -
    -
    -
    File controls
    -
    {{:helper.link('DOWNLOAD', 'download', {'action' : 'PRG_downloadfile', 'filename' : value.filename}, value.status)}}
    -
    - {{/for}} -
    - {{if data.hackedavailable}} -

    UNKNOWN software repository

    -
    - Please note that Nanotrasen does not recommend download of software from non-official servers. - {{for data.hacked_programs}} -
    -
    -
    File name
    -
    {{:value.filename}} ({{:value.size}} GQ)
    -
    -
    -
    Program name
    -
    {{:value.filedesc}}
    -
    -
    -
    Description
    -
    {{:value.fileinfo}}
    -
    -
    -
    File controls
    -
    {{:helper.link('DOWNLOAD', 'download', {'action' : 'PRG_downloadfile', 'filename' : value.filename})}}
    -
    - {{/for}} -
    - {{/if}} - {{/if}} -{{/if}} -
    NTOS v2.0.4b Copyright Nanotrasen 2557 - 2559 diff --git a/nano/templates/ntnet_monitor.tmpl b/nano/templates/ntnet_monitor.tmpl deleted file mode 100644 index 319baa872f0..00000000000 --- a/nano/templates/ntnet_monitor.tmpl +++ /dev/null @@ -1,102 +0,0 @@ -

    WIRELESS CONNECTIVITY

    -
    -
    - Active NTNet Relays: -
    -
    - {{:data.ntnetrelays}} -
    - {{if data.ntnetrelays}} -
    -
    - System status: -
    -
    - {{:data.ntnetstatus ? "ENABLED" : "DISABLED"}} -
    -
    -
    -
    - Control: -
    -
    - {{:helper.link('TOGGLE', null, {'action' : 'toggleWireless'})}} -
    -
    - Caution - Disabling wireless transmitters when using wireless device may prevent you from re-enabling them again! - {{else}} -

    Wireless coverage unavailable, no relays are connected.

    - {{/if}} -
    - -

    FIREWALL CONFIGURATION

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PROTOCOLSTATUSCONTROL
    Software Downloads
    {{:data.config_softwaredownload ? 'ENABLED' : 'DISABLED'}}
    {{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 1})}}
    Peer to Peer Traffic
    {{:data.config_peertopeer ? 'ENABLED' : 'DISABLED'}}
    {{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 2})}}
    Communication Systems
    {{:data.config_communication ? 'ENABLED' : 'DISABLED'}}
    {{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 3})}}
    Remote System Control
    {{:data.config_systemcontrol ? 'ENABLED' : 'DISABLED'}}
    {{:helper.link('TOGGLE', null, {'action' : 'toggle_function', 'id' : 4})}}
    -
    - -

    SECURITY SYSTEMS

    -{{if data.idsalarm}} -
    -
    -
    -

    NETWORK INCURSION DETECTED

    - An abnormal activity has been detected in the network. Please verify system logs for more information -
    -
    -
    -{{/if}} -
    -
    Intrusion Detection System:
    -
    {{:data.idsstatus ? 'ENABLED' : 'DISABLED'}}
    -
    -
    -
    Maximal Log Count:
    -
    {{:data.ntnetmaxlogs}}
    -
    -
    -
    Controls:
    -
    - -
    {{:helper.link('RESET IDS', null, {'action' : 'resetIDS'})}} -
    {{:helper.link('TOGGLE IDS', null, {'action' : 'toggleIDS'})}} -
    {{:helper.link('SET LOG LIMIT', null, {'action' : 'updatemaxlogs'})}} -
    {{:helper.link('PURGE LOGS', null, {'action' : 'purgelogs'})}} -
    -
    -
    -System Logs -
    -
    -
    - {{for data.ntnetlogs}} - {{:value.entry}}
    - {{/for}} -
    -
    -
    \ No newline at end of file diff --git a/nano/templates/ntnet_relay.tmpl b/nano/templates/ntnet_relay.tmpl deleted file mode 100644 index 8625f94dd14..00000000000 --- a/nano/templates/ntnet_relay.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -

    Relay

    -{{if data.dos_crashed}} -

    NETWORK BUFFERS OVERLOADED

    -

    Overload Recovery Mode

    - This system is suffering temporary outage due to overflow of traffic buffers. Until buffered traffic is processed, all further requests will be dropped. Frequent occurences of this error may indicate insufficient hardware capacity of your network. Please contact your network planning department for instructions on how to resolve this issue. -

    ADMINISTRATIVE OVERRIDE

    - CAUTION - Data loss may occur - {{:helper.link('Purge buffered traffic', 'signal', {'action' : 'restart'})}} -{{else}} -
    -
    Relay status
    -
    {{:helper.link(data.enabled ? "ENABLED" : "DISABLED", 'power-off', {'action' : 'toggle'})}}
    -
    - -
    -
    Network buffer status
    -
    {{:data.dos_overload}}/{{:data.dos_capacity}} GQ
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/ntnet_transfer.tmpl b/nano/templates/ntnet_transfer.tmpl deleted file mode 100644 index 660838b94dc..00000000000 --- a/nano/templates/ntnet_transfer.tmpl +++ /dev/null @@ -1,77 +0,0 @@ -{{if data.error}} -
    -

    An error has occurred during operation...

    - Additional information: {{:data.error}}
    - {{:helper.link('Clear', null, {'action' : 'PRG_reset'})}} -
    -{{else data.downloading}} -

    Download in progress...

    -
    -
    Downloaded file:
    -
    {{:data.download_name}}
    -
    -
    -
    Download progress:
    -
    {{:helper.smoothRound(data.download_progress)}}/{{:data.download_size}}GQ
    -
    -
    -
    Transfer speed:
    -
    {{:data.download_netspeed}}GQ/s
    -
    -
    -
    Controls:
    -
    {{:helper.link('Abort download', 'arrow-left', {'action' : 'PRG_reset'})}}
    -
    -{{else data.uploading}} -

    Server enabled

    -
    -
    Connected clients:
    -
    {{:data.upload_clients}}
    -
    -
    -
    Provided file:
    -
    {{:data.upload_filename}}
    -
    -
    -
    Server password:
    -
    {{if data.upload_haspassword}}ENABLED{{else}}DISABLED{{/if}}
    -
    -
    -
    Commands:
    -
    - {{:helper.link('Set password', 'key', {'action' : 'PRG_setpassword'})}} - {{:helper.link('Exit server', 'arrow-left', {'action' : 'PRG_reset'})}} -
    -
    -{{else data.upload_filelist}} -

    File transfer server ready. Select file to upload:

    - -
    File nameFile sizeControls - {{for data.upload_filelist}} -
    {{:value.filename}} - {{:value.size}}GQ - {{:helper.link('Select', 'upload', {'action' : 'PRG_uploadfile', 'id' : value.uid})}} - {{/for}} -
    -
    - {{:helper.link('Set password', 'key', {'action' : 'PRG_setpassword'})}} - {{:helper.link('Return', 'arrow-left', {'action' : 'PRG_reset'})}} -{{else}} -

    Available files:

    -
    Server UIDFile NameFile SizePassword ProtectionOperations - {{for data.servers}} -
    {{:value.uid}} - {{:value.filename}} - {{:value.size}}GQ - {{if value.haspassword}} - Enabled - {{else}} - Disabled - {{/if}} - - {{:helper.link('Download', 'download', {'action' : 'PRG_downloadfile', 'id' : value.uid})}} - {{/for}} -
    -
    - {{:helper.link('Send file', 'upload', {'action' : 'PRG_uploadmenu'})}} -{{/if}} \ No newline at end of file diff --git a/nano/templates/nuclear_bomb.tmpl b/nano/templates/nuclear_bomb.tmpl deleted file mode 100644 index f2a12651af6..00000000000 --- a/nano/templates/nuclear_bomb.tmpl +++ /dev/null @@ -1,145 +0,0 @@ - -{{if data.is_syndicate}} - {{:helper.syndicateMode()}} -{{/if}} -
    -
    Authorization Disk:
    -
    - {{if data.auth}} - {{:helper.link('++++++++++', 'eject', {'auth' : 1})}} - {{else}} - {{:helper.link('----------', 'floppy-o', {'auth' : 1})}} - {{/if}} -
    -
    -
    -
    -
    Status:
    -
    {{:data.authstatus}} - {{:data.safe}}
    -
    - -
    -
    Time Left:
    -
    {{:data.time}}
    -
    - -
    - {{if data.auth && data.yescode}} -
    -
    - Timer -
    -
    - {{:helper.link('On', 'play', {'timer' : 1}, data.timer ? 'selected' : '')}} - {{:helper.link('Off', 'stop', {'timer' : 0}, !data.timer ? 'selected' : '')}} -
    -
    -
    -
    - Set Time -
    -
    - {{:helper.link('--', '', {'time' : -10}, data.time <= 120 ? 'disabled' : '')}} - {{:helper.link('-', '', {'time' : -1}, data.time <= 120 ? 'disabled' : '')}} - - {{:helper.link('+', '', {'time' : 1})}} - {{:helper.link('++', '', {'time' : 10})}} -
    -
    - - {{else}} - -
    -
    - Timer -
    -
    - {{:helper.link('On', 'play', null, 'disabled')}} - {{:helper.link('Off', 'pause', null, 'disabled')}} -
    -
    -
    -
    - Set Time -
    -
    - {{:helper.link('--', '', null, 'disabled')}} - {{:helper.link('-', '', null, 'disabled')}} - - {{:helper.link('+', '', null, 'disabled')}} - {{:helper.link('++', '', null, 'disabled')}} -
    -
    - {{/if}} -
    - -
    - {{if data.auth && data.yescode}} -
    -
    - Safety -
    -
    - {{:helper.link('Engaged', 'info', {'safety' : 1}, data.safety ? 'selected' : '')}} - {{:helper.link('Disengaged', 'exclamation-triangle', {'safety' : 0}, data.safety ? '' : 'selected')}} -
    -
    -
    -
    - Anchor -
    -
    - {{:helper.link('Engaged', 'lock', {'anchor' : 1}, data.anchored ? 'selected' : '')}} - {{:helper.link('Disengaged', 'unlock', {'anchor' : 0}, data.anchored ? '' : 'selected')}} -
    -
    - - {{else}} - -
    -
    - Safety -
    -
    - {{:helper.link('Engaged', 'info', null, 'disabled')}} - {{:helper.link('Disengaged', 'exclamation-triangle', null, 'disabled')}} -
    -
    -
    -
    - Anchor -
    -
    - {{:helper.link('Engaged', 'lock', null, 'disabled')}} - {{:helper.link('Disengaged', 'unlock', null, 'disabled')}} -
    -
    - {{/if}} -
    -
    -
    -
    - >{{if data.message}} {{:data.message}}{{/if}} -
    -
    -
    - {{:helper.link('1', '', {'type' : 1})}}{{:helper.link('2', '', {'type' : 2})}}{{:helper.link('3', '', {'type' : 3})}} -
    -
    - {{:helper.link('4', '', {'type' : 4})}}{{:helper.link('5', '', {'type' : 5})}}{{:helper.link('6', '', {'type' : 6})}} -
    -
    - {{:helper.link('7', '', {'type' : 7})}}{{:helper.link('8', '', {'type' : 8})}}{{:helper.link('9', '', {'type' : 9})}} -
    -
    - {{:helper.link('R', '', {'type' : 'R'})}}{{:helper.link('0', '', {'type' : 0})}}{{:helper.link('E', '', {'type' : 'E'})}} -
    -
    -
    \ No newline at end of file diff --git a/nano/templates/omni_filter.tmpl b/nano/templates/omni_filter.tmpl deleted file mode 100644 index 8a3ffac0af7..00000000000 --- a/nano/templates/omni_filter.tmpl +++ /dev/null @@ -1,86 +0,0 @@ -
    -
    - {{:helper.link(data.power ? 'On' : 'Off', null, {'command' : 'power'}, data.config ? 'disabled' : null)}} -
    -
    - {{:helper.link('Configure', null, {'command' : 'configure'}, null, data.config ? 'selected' : null)}} -
    - - {{if data.config}} - -
    -
    -
    Port
    - {{for data.ports}} -
    {{:value.dir}} Port
    - {{/for}} -
    -
    -
    Input
    - {{for data.ports}} -
    - {{:helper.link(' ', null, {'command' : 'switch_mode', 'mode' : 'in', 'dir' : value.dir}, null, value.input ? 'selected' : null)}} -
    - {{/for}} -
    -
    -
    Output
    - {{for data.ports}} -
    - {{:helper.link(' ', null, {'command' : 'switch_mode', 'mode' : 'out', 'dir' : value.dir}, null, value.output ? 'selected' : null)}} -
    - {{/for}} -
    -
    -
    Filter
    - {{for data.ports}} -
    - {{:helper.link(value.f_type ? value.f_type : 'None', null, {'command' : 'switch_filter', 'mode' : value.f_type, 'dir' : value.dir}, value.filter ? null : 'disabled', value.f_type ? 'selected' : null)}} -
    - {{/for}} -
    -
    - -
    - Set Flow Rate Limit: {{:(data.set_flow_rate/10)}} L/s -
    -
    - {{:helper.link('Set Flow Rate Limit', null, {'command' : 'set_flow_rate'})}} -
    - - {{else}} - -
    -
    -
    Port
    - {{for data.ports}} -
    {{:value.dir}} Port
    - {{/for}} -
    -
    -
    Mode
    - {{for data.ports}} -
    - {{if value.input}} - Input - {{else value.output}} - Output - {{else value.f_type}} - {{:value.f_type}} - {{else}} - Disabled - {{/if}} -
    - {{/for}} -
    -
    - -
    - Set Flow Rate Limit: {{:(data.set_flow_rate/10)}} L/s -
    - -
    - Flow Rate: {{:(data.last_flow_rate/10)}} L/s -
    - {{/if}} -
    \ No newline at end of file diff --git a/nano/templates/omni_mixer.tmpl b/nano/templates/omni_mixer.tmpl deleted file mode 100644 index 3d637687553..00000000000 --- a/nano/templates/omni_mixer.tmpl +++ /dev/null @@ -1,101 +0,0 @@ -
    -
    - {{:helper.link(data.power ? 'On' : 'Off', null, {'command' : 'power'}, data.config ? 'disabled' : null)}} -
    -
    - {{:helper.link('Configure', null, {'command' : 'configure'}, null, data.config ? 'selected' : null)}} -
    - - {{if data.config}} - -
    -
    -
    Port
    - {{for data.ports}} -
    {{:value.dir}} Port
    - {{/for}} -
    -
    -
    Input
    - {{for data.ports}} -
    - {{:helper.link(' ', null, value.input ? {'command' : 'switch_mode', 'mode' : 'none', 'dir' : value.dir} : {'command' : 'switch_mode', 'mode' : 'in', 'dir' : value.dir}, value.output ? 'disabled' : null, value.input ? 'selected' : null)}} -
    - {{/for}} -
    -
    -
    Output
    - {{for data.ports}} -
    - {{:helper.link(' ', null, value.output ? null : {'command' : 'switch_mode', 'mode' : 'out', 'dir' : value.dir}, null, value.output ? 'selected' : null)}} -
    - {{/for}} -
    -
    -
    Concentration
    - {{for data.ports}} -
    - {{:helper.link( value.input ? helper.round(value.concentration*100)+' %' : '-', null, {'command' : 'switch_con', 'dir' : value.dir}, value.input ? null : 'disabled')}} -
    - {{/for}} -
    -
    -
    Lock
    - {{for data.ports}} -
    - {{:helper.link(' ', value.con_lock ? 'lock' : 'unlock', {'command' : 'switch_conlock', 'dir' : value.dir}, value.input ? null : 'disabled', value.con_lock ? 'selected' : null)}} -
    - {{/for}} -
    -
    - -
    - Set Flow Rate Limit: {{:(data.set_flow_rate/10)}} L/s -
    -
    - {{:helper.link('Set Flow Rate Limit', null, {'command' : 'set_flow_rate'})}} -
    - - {{else}} - -
    -
    -
    Port
    - {{for data.ports}} -
    {{:value.dir}} Port
    - {{/for}} -
    -
    -
    Mode
    - {{for data.ports}} -
    - {{if value.input}} - Input - {{else value.output}} - Output - {{else}} - Disabled - {{/if}} -
    - {{/for}} -
    -
    -
    Concentration
    - {{for data.ports}} -
    - {{if value.input}} - {{:helper.round(value.concentration*100)}} % - {{else}} - - - {{/if}} -
    - {{/for}} -
    -
    - -
    - Flow Rate: {{:(data.last_flow_rate/10)}} L/s -
    - - {{/if}} -
    \ No newline at end of file diff --git a/nano/templates/op_computer.tmpl b/nano/templates/op_computer.tmpl deleted file mode 100644 index 77894bcba16..00000000000 --- a/nano/templates/op_computer.tmpl +++ /dev/null @@ -1,215 +0,0 @@ - -

    Patient Monitor

    - -
    - {{if data.choice==0}} - {{if !data.hasOccupant}} -
    No Patient Detected
    - {{else}} -
    - {{:data.occupant.name}} =>  - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
    - -
    -
    Health:
    - {{if data.occupant.health >= data.occupant.maxHealth}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}} - {{else data.occupant.health > 0}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'average')}} - {{else data.occupant.health >= data.minhealth}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}} - {{else}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'bad alignRight')}} - {{/if}} -
    {{:helper.smoothRound(data.occupant.health)}}
    -
    - -
    -
    Brute Damage:
    - {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.bruteLoss)}}
    -
    - -
    -
    Resp. Damage:
    - {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.oxyLoss)}}
    -
    - -
    -
    Toxin Damage:
    - {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.toxLoss)}}
    -
    - -
    -
    Burn Severity:
    - {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.fireLoss)}}
    -
    - -
    -
    Patient Temperature:
    - {{if data.occupant.temperatureSuitability == -3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == -2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == -1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 0}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{/if}} -
    - {{if data.occupant.hasBlood}} -
    -
    -
    Blood Level:
    - {{if data.occupant.bloodPercent <= 60}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{else data.occupant.bloodPercent <= 90}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{else}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{/if}} -
    -
    Blood Type:
    {{:data.occupant.bloodType}}
    -
    -
    -
    -
    Pulse:
    {{:data.occupant.pulse}} bpm
    -
    - {{/if}} - {{if data.occupant.inSurgery}} -
    -
    Initiated Surgery Procedure:
    {{:data.occupant.surgeryName}}
    -
    -
    Next Step:
    {{:data.occupant.stepName}}
    - -
    - {{/if}} -
    -
    - {{/if}} -
    -
    - {{:helper.link('Options', 'gear', {'choiceOn' : 1})}} -
    -
    - {{else}} -
    -
    - Loudspeaker: -
    -
    - {{:helper.link('On', 'power-off', {'verboseOn' : 1}, data.verbose ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'verboseOff' : 1}, data.verbose ? null : 'selected')}} -
    -
    -
    -
    -
    - Health Announcer: -
    -
    - {{:helper.link('On', 'power-off', {'healthOn' : 1}, data.health ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'healthOff' : 1}, data.health ? null : 'selected')}} -
    -
    - -
    -
    -
    Health Announcer Threshold:
    - {{:helper.link('-', null, {'health_adj' : -100}, (data.healthAlarm >= 0) ? null : 'disabled')}} - {{:helper.link('-', null, {'health_adj' : -10}, (data.healthAlarm >= -90) ? null : 'disabled')}} - {{:helper.link('-', null, {'health_adj' : -1}, (data.healthAlarm > -100) ? null : 'disabled')}} - - {{if data.healthAlarm >= 100}} - {{:helper.displayBar(data.healthAlarm, 0, 100, 'good')}} - {{else data.healthAlarm > 0}} - {{:helper.displayBar(data.healthAlarm, 0, 100, 'average')}} - {{else data.healthAlarm >= -100}} - {{:helper.displayBar(data.healthAlarm, 0, -100, 'average alignRight')}} - {{else}} - {{:helper.displayBar(data.healthAlarm, 0, -100, 'bad alignRight')}} - {{/if}} - - {{:helper.link('+', null, {'health_adj' : 1}, (data.healthAlarm < 100) ? null : 'disabled')}} - {{:helper.link('+', null, {'health_adj' : 10}, (data.healthAlarm <= 90) ? null : 'disabled')}} - {{:helper.link('+', null, {'health_adj' : 100}, (data.healthAlarm <= 0) ? null : 'disabled')}} -
     {{: data.healthAlarm }}  
    -
    -
    -
    -
    -
    - Oxygen Alarm: -
    -
    - {{:helper.link('On', 'power-off', {'oxyOn' : 1}, data.oxy ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'oxyOff' : 1}, data.oxy ? null : 'selected')}} -
    -
    - -
    -
    -
    Oxygen Alert Threshold:
    - {{:helper.link('-', null, {'oxy_adj' : -100}, (data.oxyAlarm >= 100) ? null : 'disabled')}} - {{:helper.link('-', null, {'oxy_adj' : -10}, (data.oxyAlarm >= 10) ? null : 'disabled')}} - {{:helper.link('-', null, {'oxy_adj' : -1}, (data.oxyAlarm >= 0) ? null : 'disabled')}} - {{:helper.displayBar( data.oxyAlarm, 0, 200, 'good')}} - {{:helper.link('+', null, {'oxy_adj' : 1}, (data.oxyAlarm < 200) ? null : 'disabled')}} - {{:helper.link('+', null, {'oxy_adj' : 10}, (data.oxyAlarm <= 190) ? null : 'disabled')}} - {{:helper.link('+', null, {'oxy_adj' : 100}, (data.oxyAlarm <= 100) ? null : 'disabled')}} -
     {{: data.oxyAlarm }}  
    -
    -
    -
    -
    -
    - Critical Alert: -
    -
    - {{:helper.link('On', 'power-off', {'critOn' : 1}, data.crit ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'critOff' : 1}, data.crit ? null : 'selected')}} -
    -
    -
    -
    -
    - {{:helper.link('Return', 'arrow-left', {'choiceOff' : 1})}} -
    -
    - {{/if}} -
    diff --git a/nano/templates/order_console.tmpl b/nano/templates/order_console.tmpl deleted file mode 100644 index 271e35c525a..00000000000 --- a/nano/templates/order_console.tmpl +++ /dev/null @@ -1,111 +0,0 @@ - - - -
    -
    - Supply Points: -
    -
    - {{:data.points}} -
    -
    -
    -
    - Shuttle Status: -
    -
    - {{if !data.moving && !data.at_station}} - Docked off-station. - {{else !data.moving && data.at_station}} - Docked at the station. - {{else data.moving}} - Shuttle is en route (ETA: {{:data.timeleft}} minute{{if data.timeleft != 1}}s{{/if}}). - {{/if}} -
    -
    -
    -
    - Supply Crates -
    -
    -
    -
    - {{if !data.contents}} - {{for data.supply_packs}} -
    -
    -
    {{:helper.link(value.name, null, value.command1)}}
    - {{:helper.link('#', null, value.command2, null)}}{{:helper.link('C', null, value.command3, null)}}
    {{:value.cost}} points
    -
    -
    - {{/for}} - {{else}} -
    - Contents of {{:data.contents_name}} -
    - {{:data.contents}} - {{:helper.link('Return', 'arrow-left', {'contents' : 1})}} - {{/if}} -
    -
    -
    -
    -
    -
    - Categories -
    - {{for data.categories}} -
    - {{:helper.link(value.name, 'gear', {'last_viewed_group':value.category}, (data.last_viewed_group==value.category) ? 'linkOn' : '', 'category')}} -
    - {{/for}} -
    -
    -
    - Supply Orders -
    -
    -
    -
    - Requests -
    - {{for data.requests}} -
    - #{{:value.ordernum}} - {{:value.supply_type}} for {{:value.orderedby}} - {{if value.owned == 1}} - {{:helper.link('CANCEL', null, value.command1, null)}} - {{/if}} -
    - {{empty}} - No active requests. - {{/for}} -
    - Orders -
    - {{for data.orders}} -
    - #{{:value.ordernum}} - {{:value.supply_type}} for {{:value.orderedby}} -
    - {{empty}} - No confirmed orders. - {{/for}} -
    -
    -
    -
    \ No newline at end of file diff --git a/nano/templates/pacman.tmpl b/nano/templates/pacman.tmpl deleted file mode 100644 index f76d967a6af..00000000000 --- a/nano/templates/pacman.tmpl +++ /dev/null @@ -1,113 +0,0 @@ -

    Status

    -
    -
    - Generator Status: -
    -
    - {{if data.active}} - Online - {{else}} - Offline - {{/if}} -
    -
    - Generator Control: -
    -
    - {{if data.active}} - {{:helper.link('STOP', 'power-off', {'action' : "disable"})}} - {{else}} - {{:helper.link('START', 'power-off', {'action' : "enable"})}} - {{/if}} -
    -
    -

    Fuel

    -
    -
    - Fuel Type: -
    -
    - {{:data.fuel_type}} -
    -
    - Fuel Level: -
    -
    - {{if data.fuel_stored >= 5000}} - {{:helper.displayBar(data.fuel_stored, 0, data.fuel_capacity, 'good')}} -
    {{:helper.smoothRound(data.fuel_stored)}}/{{:data.fuel_capacity}} cm3 - {{else data.fuel_stored >= 1000}} - {{:helper.displayBar(data.fuel_stored, 0, data.fuel_capacity, 'average')}} -
    {{:helper.smoothRound(data.fuel_stored)}}/{{:data.fuel_capacity}} cm3 - {{else}} - {{:helper.displayBar(data.fuel_stored, 0, data.fuel_capacity, 'bad')}} -
    {{:helper.smoothRound(data.fuel_stored)}}/{{:data.fuel_capacity}} cm3 - {{/if}} -
    -
    - Fuel Usage: -
    -
    - {{:helper.smoothRound(data.fuel_usage)}} cm3/s -
    - {{if !data.is_ai}} -
    - Control: -
    -
    - {{:helper.link('EJECT FUEL', 'arrow-down', {'action' : "eject"}, data.active ? 'disabled' : null)}} -
    - {{/if}} -
    -

    Output

    -
    -
    - Power setting: -
    -
    - {{if data.output_set > data.output_safe}} - {{:data.output_set}} / {{:data.output_max}} ({{:data.output_watts}} W) - {{else}} - {{:data.output_set}} / {{:data.output_max}} ({{:data.output_watts}} W) - {{/if}} -
    -
    - Control: -
    -
    - {{:helper.link('+', null, {'action' : "higher_power"})}} - {{:helper.link('-', null, {'action' : "lower_power"})}} -
    -
    -

    Temperature

    -
    -
    - Temperature: -
    -
    - {{if data.temperature_current < (data.temperature_max * 0.8)}} - {{:helper.displayBar(data.temperature_current, 0, (data.temperature_max * 1.5), 'good')}} -
    {{:helper.smoothRound(data.temperature_current)}} C - {{else data.temperature_current < data.temperature_max}} - {{:helper.displayBar(data.temperature_current, 0, (data.temperature_max * 1.5), 'average')}} -
    {{:helper.smoothRound(data.temperature_current)}} C - {{else}} - {{:helper.displayBar(data.temperature_current, 0, (data.temperature_max * 1.5), 'bad')}} -
    {{:helper.smoothRound(data.temperature_current)}} C - {{/if}} -
    -
    - Generator Status: -
    -
    - {{if data.temperature_overheat > 50}} - DANGER: CRITICAL OVERHEAT! Deactivate generator immediately! - {{else data.temperature_overheat > 20}} - WARNING: Overheating! - {{else data.temperature_overheat > 1}} - Temperature High - {{else}} - Optimal - {{/if}} -
    -
    \ No newline at end of file diff --git a/nano/templates/pda.tmpl b/nano/templates/pda.tmpl deleted file mode 100644 index fa8776c71ad..00000000000 --- a/nano/templates/pda.tmpl +++ /dev/null @@ -1,156 +0,0 @@ - -{{if data.useRetro}} - - - -{{/if}} -{{if data.owner}} -
    -
    - {{if data.idInserted}} - {{:helper.link(data.idLink, 'eject', {'choice' : "Authenticate"}, null, 'pdalink fixedLeftWide')}} - {{/if}} - {{if data.cartridge_name}} - {{:helper.link(data.cartridge_name, 'eject', {'choice' : "Eject"}, null, 'pdalink fixedLeftWidest')}} - {{/if}} - {{:helper.link('Toggle R.E.T.R.O. mode', 'gear', {'choice': "Retro"}, null, 'pdalink fixedLeftWide')}} -
    -
    - {{:data.stationTime}} -
    -
    - - {{if data.app}} -

     {{:data.app.name}}

    -
    -
     
    - {{/if}} - -
    - {{:helper.link("", 'undo', {'choice' : "Back"}, data.app.has_back ? null : 'disabled', 'link pdaFooterButton')}} - {{:helper.link("", 'home', {'choice' : "Home"}, data.app.is_home ? 'disabled' : null, 'link pdaFooterButton')}} -
    -{{else}} -
    -






    No Owner information found, please swipe ID -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_atmos_scan.tmpl b/nano/templates/pda_atmos_scan.tmpl deleted file mode 100644 index 57f0a356a16..00000000000 --- a/nano/templates/pda_atmos_scan.tmpl +++ /dev/null @@ -1,60 +0,0 @@ - -
    -
    - {{if data.aircontents.reading == 1}} -
    - Pressure: -
    -
    - {{:helper.string('{1} kPa', data.aircontents.pressure < 80 || data.aircontents.pressure > 120 ? 'bad' : data.aircontents.pressure < 95 || data.aircontents.pressure > 110 ? 'average' : 'good', helper.smoothRound(data.aircontents.pressure, 1))}} -
    -
    - Temperature: -
    -
    - {{:helper.string('{1} °C', data.aircontents.temp < 5 || data.aircontents.temp > 35 ? 'bad' : data.aircontents.temp < 15 || data.aircontents.temp > 25 ? 'average' : 'good' , helper.smoothRound(data.aircontents.temp, 1))}} -
    -
    -
    - Oxygen: -
    -
    - {{:helper.string('{1}%', data.aircontents.oxygen < 17 ? 'bad' : data.aircontents.oxygen < 19 ? 'average' : 'good', helper.smoothRound(data.aircontents.oxygen, 1))}} -
    -
    - Nitrogen: -
    -
    - {{:helper.string('{1}%', data.aircontents.nitrogen > 82 ? 'bad' : data.aircontents.nitrogen > 80 ? 'average' : 'good', helper.smoothRound(data.aircontents.nitrogen, 1))}} -
    -
    - Carbon Dioxide: -
    -
    - {{:helper.string('{1}%', data.aircontents.carbon_dioxide > 5 ? 'bad' : 'good', helper.smoothRound(data.aircontents.carbon_dioxide, 1))}} -
    -
    - Plasma: -
    -
    - {{:helper.string('{1}%', data.aircontents.plasma > 0 ? 'bad' : 'good', helper.smoothRound(data.aircontents.plasma, 1))}} - -
    - {{if data.aircontents.other > 0}} -
    - Unknown: -
    -
    - {{:helper.smoothRound(data.aircontents.other, 1)}}% -
    - {{/if}} - {{else}} -
    - Unable to get air reading -
    - {{/if}} -
    -
    \ No newline at end of file diff --git a/nano/templates/pda_janitor.tmpl b/nano/templates/pda_janitor.tmpl deleted file mode 100644 index 864692d8a04..00000000000 --- a/nano/templates/pda_janitor.tmpl +++ /dev/null @@ -1,66 +0,0 @@ -
    -
    - Current Location: - {{if data.janitor.user_loc.x == 0}} - Unknown - {{else}} - {{:data.janitor.user_loc.x}} / {{:data.janitor.user_loc.y}} - {{/if}} -
    -
    - {{if data.janitor.mops}} - Mop Locations: -
      - {{for data.janitor.mops}} -
    • - ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Status: {{:value.status}} -
    • - {{/for}} -
    - {{else}} - Unable to locate Mops - {{/if}} -
    -
    - {{if data.janitor.buckets}} - Mop Bucket Locations: -
      - {{for data.janitor.buckets}} -
    • - ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Capacity: {{:value.volume}}/{{:value.max_volume}} -
    • - {{/for}} -
    - {{else}} - Unable to locate Mop Buckets - {{/if}} -
    -
    - {{if data.janitor.cleanbots}} - Cleanbot Locations: -
      - {{for data.janitor.cleanbots}} -
    • - ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Status: {{:value.status}} -
    • - {{/for}} -
    - {{else}} - Unable to locate Cleanbots - {{/if}} -
    -
    - {{if data.janitor.carts}} - Janitorial Cart Locations: -
      - {{for data.janitor.carts}} -
    • - ({{:value.x}} / {{:value.y}}) - {{:value.dir}} - Water Level: {{:value.volume}}/{{:value.max_volume}} -
    • - {{/for}} -
    - {{else}} - Unable to locate Janitorial Carts - {{/if}} -
    -
    diff --git a/nano/templates/pda_main_menu.tmpl b/nano/templates/pda_main_menu.tmpl deleted file mode 100644 index 692cdb7039d..00000000000 --- a/nano/templates/pda_main_menu.tmpl +++ /dev/null @@ -1,76 +0,0 @@ - - - - -
    -
    - Owner: -
    -
    - {{:data.owner}}, {{:data.ownjob}} -
    -
    -
    -
    - ID: -
    -
    - {{:helper.link('Update PDA Info', 'refresh', {'choice' : "UpdateInfo"}, data.idInserted ? null : 'disabled', 'pdalink fixedLeftWide')}} -
    -
    - -
    -

    Functions

    -
    -{{for data.categories : cat : i}} - -{{/for}} - -{{if data.pai}} -
    -
    - PAI Utilities: -
    -
    - {{:helper.link('Configuration', 'gear', {'choice' : "pai", 'option' : "1"}, null, 'pdalink fixedLeft')}} - {{:helper.link('Eject pAI', 'eject', {'choice' : "pai", 'option' : "2"}, null, 'pdalink fixedLeft')}} -
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_manifest.tmpl b/nano/templates/pda_manifest.tmpl deleted file mode 100644 index ef9e91a088f..00000000000 --- a/nano/templates/pda_manifest.tmpl +++ /dev/null @@ -1,96 +0,0 @@ - -
    -
    - {{if data.manifest.heads}} - - {{for data.manifest["heads"]}} - {{if value.rank == "Captain"}} - - {{else}} - - {{/if}} - {{/for}} - {{/if}} - {{if data.manifest.sec}} - - {{for data.manifest["sec"]}} - {{if value.rank == "Head of Security"}} - - {{else}} - - {{/if}} - {{/for}} - {{/if}} - {{if data.manifest.eng}} - - {{for data.manifest["eng"]}} - {{if value.rank == "Chief Engineer"}} - - {{else}} - - {{/if}} - {{/for}} - {{/if}} - {{if data.manifest.med}} - - {{for data.manifest["med"]}} - {{if value.rank == "Chief Medical Officer"}} - - {{else}} - - {{/if}} - {{/for}} - {{/if}} - {{if data.manifest.sci}} - - {{for data.manifest["sci"]}} - {{if value.rank == "Research Director"}} - - {{else}} - - {{/if}} - {{/for}} - {{/if}} - {{if data.manifest.ser}} - - {{for data.manifest["ser"]}} - {{if value.rank == "Head of Personnel"}} - - {{else}} - - {{/if}} - {{/for}} - {{/if}} - {{if data.manifest.sup}} - - {{for data.manifest["sup"]}} - {{if value.rank == "Head of Personnel"}} - - {{else value.rank == "Quartermaster"}} - - {{else}} - - {{/if}} - {{/for}} - {{/if}} - {{if data.manifest.civ}} - - {{for data.manifest["civ"]}} - {{if value.rank == "Head of Personnel"}} - - {{else}} - - {{/if}} - {{/for}} - {{/if}} - {{if data.manifest.misc}} - - {{for data.manifest["misc"]}} - - {{/for}} - {{/if}} -
    Command
    {{:value.name}}{{:value.rank}}{{:value.active}}
    {{:value.name}}{{:value.rank}}{{:value.active}}
    Security
    {{:value.name}}{{:value.rank}}{{:value.active}}
    {{:value.name}}{{:value.rank}}{{:value.active}}
    Engineering
    {{:value.name}}{{:value.rank}}{{:value.active}}
    {{:value.name}}{{:value.rank}}{{:value.active}}
    Medical
    {{:value.name}}{{:value.rank}}{{:value.active}}
    {{:value.name}}{{:value.rank}}{{:value.active}}
    Science
    {{:value.name}}{{:value.rank}}{{:value.active}}
    {{:value.name}}{{:value.rank}}{{:value.active}}
    Service
    {{:value.name}}{{:value.rank}}{{:value.active}}
    {{:value.name}}{{:value.rank}}{{:value.active}}
    Supply
    {{:value.name}}{{:value.rank}}{{:value.active}}
    {{:value.name}}{{:value.rank}}{{:value.active}}
    {{:value.name}}{{:value.rank}}{{:value.active}}
    Civilian
    {{:value.name}}{{:value.rank}}{{:value.active}}
    {{:value.name}}{{:value.rank}}{{:value.active}}
    Misc
    {{:value.name}}{{:value.rank}}{{:value.active}}
    -
    \ No newline at end of file diff --git a/nano/templates/pda_medical.tmpl b/nano/templates/pda_medical.tmpl deleted file mode 100644 index 777fb91f4b8..00000000000 --- a/nano/templates/pda_medical.tmpl +++ /dev/null @@ -1,54 +0,0 @@ -{{if !data.records}} -
    -
    Select a record:
    -
    - {{for data.recordsList}} -
    - {{:helper.link(value.Name, 'user', {'choice' : "Records", 'target' : value.ref}, null, 'pdalink fixedLeftWidest')}} -
    - {{empty}} -
    - No records found. -
    - {{/for}} -{{else}} -
    -
    -
    - {{if data.records.general}} - Name: {{:data.records.general.name}}
    - Sex: {{:data.records.general.sex}}
    - Species: {{:data.records.general.species}}
    - Age: {{:data.records.general.age}}
    - Rank: {{:data.records.general.rank}}
    - Fingerprint: {{:data.records.general.fingerprint}}
    - Physical Status: {{:data.records.general.p_stat}}
    - Mental Status: {{:data.records.general.m_stat}}

    - {{else}} - - General Record Lost!

    -
    - {{/if}} - {{if data.records.medical}} -
    -
    Medical Data:
    -
    - Blood Type: {{:data.records.medical.b_type}}

    - Minor Disabilities: {{:data.records.medical.mi_dis}}
    - Details: {{:data.records.medical.mi_dis_d}}

    - Major Disabilities: {{:data.records.medical.ma_dis}}
    - Details: {{:data.records.medical.ma_dis_d}}

    - Allergies: {{:data.records.medical.alg}}
    - Details: {{:data.records.medical.alg_d}}

    - Current Disease: {{:data.records.medical.cdi}}
    - Details: {{:data.records.medical.alg_d}}

    - Important Notes: {{:data.records.medical.notes}} - {{else}} - - Medical Record Lost! - - {{/if}} -
    -
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_messenger.tmpl b/nano/templates/pda_messenger.tmpl deleted file mode 100644 index 4871934e4e9..00000000000 --- a/nano/templates/pda_messenger.tmpl +++ /dev/null @@ -1,90 +0,0 @@ - -{{if data.active_conversation}} -
    -
    - Messenger Functions: -
    -
    - {{:helper.link('Delete Conversation', 'trash', {'choice' : "Clear", 'option' : "Convo"}, null, 'pdalink fixedLeftWide')}} - {{:helper.link(data.auto_scroll ? 'Autoscroll: On' : 'Autoscroll: Off', 'level-down', {'choice' : "Autoscroll"}, null, 'pdalink fixedLeftWide')}} -
    -
    -
    -
    -

    Conversation with: {{:data.convo_name}} ({{:data.convo_job}})

    -
    -
    -
    - {{for data.messages}} - {{if data.active_conversation == value.target}} - {{if value.sent==0}} - Them: {{:value.message}}
    - {{else}} - You: {{:value.message}}
    - {{/if}} - {{/if}} - {{/for}} -
    -
    -
    - {{:helper.link('Reply', 'comment', {'choice' : "Message", 'target': data.active_conversation}, null, 'pdalink fixedLeftWidest')}} - -{{else}} -
    -
    - Messenger Functions: -
    -
    - {{:helper.link(data.silent==1 ? 'Ringer: Off' : 'Ringer: On', data.silent==1 ? 'volume-off' : 'volume-up', {'choice' : "Toggle Ringer"}, null, 'pdalink fixedLeftWide')}} - {{:helper.link(data.toff==1 ? 'Messenger: Off' : 'Messenger: On',data.toff==1 ? 'close':'check', {'choice' : "Toggle Messenger"}, null, 'pdalink fixedLeftWide')}} - {{:helper.link('Set Ringtone', 'bell-o', {'choice' : "Ringtone"}, null, 'pdalink fixedLeftWide')}} - {{:helper.link('Delete all Conversations', 'trash', {'choice' : "Clear", 'option' : "All"}, null, 'pdalink fixedLeftWider')}} -
    -
    - {{if data.toff == 0}} -
    - {{if data.charges}} -
    - {{:data.charges}} charges left. -

    -
    - {{/if}} - - {{if !data.convopdas.length && !data.pdas.length}} - No other PDAS located - {{else}} -

    Current Conversations

    - {{for data.convopdas}} -
    - {{:helper.link(value.Name, 'arrow-circle-down', {'choice' : "Select Conversation", 'convo' : value.Reference } , null, 'pdalink')}} - {{if data.charges}} - {{for data.plugins : plugin : i}} - {{:helper.link(plugin.name, plugin.icon, {'choice' : "Messenger Plugin", 'plugin' : plugin.ref, 'target' : value.Reference}, null, 'pdalink fixedLeft')}} - {{/for}} - {{/if}} -
    - {{/for}} -

    Other PDAs

    - {{for data.pdas}} -
    - {{:helper.link(value.Name, 'arrow-circle-down', {'choice' : "Message", 'target' : value.Reference}, null, 'pdalink')}} - {{if data.charges}} - {{for data.plugins : plugin : i}} - {{:helper.link(plugin.name, plugin.icon, {'choice' : "Messenger Plugin", 'plugin' : plugin.ref, 'target' : value.Reference}, null, 'pdalink fixedLeft')}} - {{/for}} - {{/if}} -
    - {{/for}} - {{/if}} - {{/if}} -{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_mob_hunt.tmpl b/nano/templates/pda_mob_hunt.tmpl deleted file mode 100644 index dde4ab82a5c..00000000000 --- a/nano/templates/pda_mob_hunt.tmpl +++ /dev/null @@ -1,91 +0,0 @@ -
    -
    - - - - {{if data.connected}} - - - {{else}} - - - {{/if}} - -
    - Connection Status: - - Connected - - {{:helper.link('Disconnect', 'sign-out', {'choice': 'Disconnect'})}} - - No Connection - - {{:helper.link('Connect', 'sign-in', {'choice': 'Reconnect'})}} -
    -
    -
    -
    -
    My Collection:
    -{{if data.no_collection}} -
    -
    - Your collection is empty! Go capture some Nano-Mobs! -
    -
    -
    - Wild mobs captured: {{:data.wild_captures}} -{{else}} -
    -
    -
    - {{if data.entry}} - {{if data.entry.nickname}} - Name: {{:data.entry.nickname}}
    - {{/if}} - Species: {{:data.entry.real_name}}
    - Level: {{:data.entry.level}}
    - Primary Type: {{:data.entry.type1}}
    - {{if data.entry.type2}} - Secondary Type: {{:data.entry.type2}}
    - {{/if}} - {{if data.entry.sprite}} - - {{else}} - Mob Image Missing! - {{/if}} - {{else}} - - Mob Data Missing!

    -
    - {{/if}} -
    -
    -
    -
    - Wild mobs captured: {{:data.wild_captures}} -
    - - - - - - - - -
    - {{:helper.link('Previous Mob', 'arrow-left', {'choice': 'Prev'})}} - - {{:helper.link('Transfer Mob', 'exchange', {'choice': 'Transfer'})}} - - {{:helper.link('Rename Mob', 'pencil', {'choice': 'Rename'})}} - - {{:helper.link('Release Mob', 'tree', {'choice': 'Release'})}} - - {{:helper.link('Next Mob', 'arrow-right', {'choice': 'Next'})}} -
    - {{if data.entry.is_hacked}} -
    - {{:helper.link('Set Trap', 'bolt', {'choice': 'Set_Trap'})}} - {{/if}} -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_mule.tmpl b/nano/templates/pda_mule.tmpl deleted file mode 100644 index 369d0830aa5..00000000000 --- a/nano/templates/pda_mule.tmpl +++ /dev/null @@ -1,118 +0,0 @@ -{{if !data.mulebot.active}} - {{if data.mulebot.count == 0}} -

    No bots found.

    - {{else}} -
    - Select a MULE: -
    -
    - {{for data.mulebot.bots}} -
    - {{:helper.link(value.Name, 'gear', {'radiomenu' : "1", 'op' : "control",'bot' : value.ref}, null, 'pdalink fixedLeftWidest')}} (Location: {{:value.Location}}) -
    - {{/for}} - {{/if}} -
    - {{:helper.link('Scan for Bots','rss', {'radiomenu' : "1", 'op' : "scanbots"}, null, 'pdalink fixedLeftWidest')}} -{{else}} -

    {{:data.mulebot.active}}

    - {{if data.mulebot.botstatus.mode == -1}} -

    Waiting for response...

    - {{else}} -

    Status:

    -
    -
    - Location: -
    -
    - {{:data.mulebot.botstatus.loca}} -
    -
    -
    -
    - Mode: -
    -
    - - {{if data.mulebot.botstatus.mode == 0}} - Ready - {{else data.mulebot.botstatus.mode == 1}} - Loading/Unloading - {{else data.mulebot.botstatus.mode == 2}} - Navigating to Delivery Location - {{else data.mulebot.botstatus.mode == 3}} - Navigating to Home - {{else data.mulebot.botstatus.mode == 4}} - Waiting for Clear Path - {{else data.mulebot.botstatus.mode == 5 || data.mulebot.botstatus.mode == 6}} - Calculating navigation Path - {{else data.mulebot.botstatus.mode == 7}} - Unable to locate destination - {{/if}} - -
    -
    -
    -
    - Current Load: -
    -
    - - {{:helper.link(data.mulebot.botstatus.load == null ? 'None (Unload)' : data.mulebot.botstatus.load + ' (Unload)', 'archive', {'radiomenu' : "1", 'op' : "unload"},data.mulebot.botstatus.load == null ? 'disabled' : null, 'pdalink fixedLeftWidest')}} - -
    -
    -
    -
    - Power: -
    -
    - - {{:data.mulebot.botstatus.powr}}% - -
    -
    -
    -
    - Destination: -
    -
    - {{:helper.link(data.mulebot.botstatus.dest == null || data.mulebot.botstatus.dest == "" ? 'None (Set)' : data.mulebot.botstatus.dest + ' (Set)', 'gear', {'radiomenu' : "1", 'op' : "setdest"}, null, 'pdalink fixedLeftWidest')}} -
    -
    -
    -
    - Home: -
    -
    - {{if data.mulebot.botstatus.home == null}} None {{else}} {{:data.mulebot.botstatus.home}} {{/if}} -
    -
    -
    -
    - Auto Return: -
    -
    - {{:helper.link(data.mulebot.botstatus.retn == 1 ? 'ON' : 'OFF', 'gear', {'radiomenu' : "1", 'op' : (data.mulebot.botstatus.retn==1 ? "retoff" : "reton")}, null, 'pdalink fixedLeftWidest')}} -
    -
    -
    -
    - Auto Pickup: -
    -
    - {{:helper.link(data.mulebot.botstatus.pick==1? 'ON' : 'OFF', 'gear', {'radiomenu' : "1", 'op' : (data.mulebot.botstatus.pick==1 ? "pickoff" : "pickon")}, null, 'pdalink fixedLeftWidest')}} -
    -
    -
    -
    - Functions: -
    -
    - {{:helper.link('Stop', 'gear', {'radiomenu' : "1", 'op' : "stop"}, null, 'pdalink fixedLeft')}} - {{:helper.link('Proceed', 'gear', {'radiomenu' : "1", 'op' : "start"}, null, 'pdalink fixedLeft')}} - {{:helper.link('Return Home', 'gear', {'radiomenu' : "1", 'op' : "home"}, null, 'pdalink fixedLeft')}} -
    -
    - {{/if}} -{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_notekeeper.tmpl b/nano/templates/pda_notekeeper.tmpl deleted file mode 100644 index 53db3be6438..00000000000 --- a/nano/templates/pda_notekeeper.tmpl +++ /dev/null @@ -1,21 +0,0 @@ - -
    -
    - Notes: -
    -
    -
    -
    -
    - {{:data.note}} -
    -
    -
    -
    -
    - {{:helper.link('Edit Notes', 'pencil-square-o', {'choice': "Edit"}, null, 'pdalink')}} -
    -
    \ No newline at end of file diff --git a/nano/templates/pda_power.tmpl b/nano/templates/pda_power.tmpl deleted file mode 100644 index 7ba04188b71..00000000000 --- a/nano/templates/pda_power.tmpl +++ /dev/null @@ -1,49 +0,0 @@ -{{if !data.records.powerconnected}} -
    -
    Select a power monitor:
    -
    - {{for data.records.powermonitors}} -
    - {{:helper.link(value.Name, 'exclamation-circle', {'choice' : "Power Select", 'target' : value.ref}, null, 'pdalink fixedLeftWidest')}} -
    - {{/for}} -{{else}} -
    -
    - Total Power: -
    -
    - {{:data.records.poweravail}} W -
    -
    -
    -
    - Total Load: -
    -
    - {{:data.records.powerload}} W -
    -
    -
    -
    - Total Demand: -
    -
    - {{:data.records.powerdemand}} W -
    -
    -
    - - - {{for data.records.apcs}} - - {{:helper.string('', value.Equipment == "On" || value.Equipment == "AOn" ? '#4f7529' : '#8f1414', value.Equipment)}} - {{:helper.string('', value.Lights == "On" || value.Lights == "AOn" ? '#4f7529' : '#8f1414', value.Lights)}} - {{:helper.string('', value.Environment == "On" || value.Environment == "AOn" ? '#4f7529' : '#8f1414', value.Environment)}} - {{:helper.string('', value.CellStatus == "F" ? '#4f7529' : value.CellStatus == "C" ? '#cd6500' : '#8f1414', value.CellStatus == "M" ? 'No Cell' : value.CellPct + '%', value.CellStatus == "M" ? '' : ' (' + value.CellStatus + ')')}} - - - {{/for}} -
    AreaEquip.LightingEnviron.CellLoad
    {{:value.Name}}{1}{1}{1}{1}{2}{{:value.Load}}W
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_secbot.tmpl b/nano/templates/pda_secbot.tmpl deleted file mode 100644 index 6ff8c98e147..00000000000 --- a/nano/templates/pda_secbot.tmpl +++ /dev/null @@ -1,59 +0,0 @@ -{{if !data.beepsky.active}} - {{if data.beepsky.count == 0}} -

    No bots found.

    - {{else}} -
    - Select a bot: -
    -
    - {{for data.beepsky.bots}} -
    - {{:helper.link(value.Name, 'gear', {'radiomenu' : "1", 'op' : "control",'bot' : value.ref}, null, 'pdalink fixedLeftWidest')}} (Location: {{:value.Location}}) -
    - {{/for}} - {{/if}} -
    - {{:helper.link('Scan for Bots','rss', {'radiomenu' : "1", 'op' : "scanbots"}, null, 'pdalink fixedLeftWidest')}} -{{else}} -

    {{:data.beepsky.active}}

    - {{if data.beepsky.botstatus.mode == -1}} -

    Waiting for response...

    - {{else}} -

    Status:

    -
    -
    - Location: -
    -
    - {{:data.beepsky.botstatus.loca}} -
    -
    -
    -
    - Mode: -
    -
    - - {{if data.beepsky.botstatus.mode ==0}} - Ready - {{else data.beepsky.botstatus.mode == 1}} - Apprehending target - {{else data.beepsky.botstatus.mode ==2 || data.beepsky.botstatus.mode == 3}} - Arresting target - {{else data.beepsky.botstatus.mode ==4}} - Starting patrol - {{else data.beepsky.botstatus.mode ==5}} - On Patrol - {{else data.beepsky.botstatus.mode ==6}} - Responding to summons - {{/if}} - -
    -
    -
    - {{:helper.link('Stop Patrol', 'gear', {'radiomenu' : "1", 'op' : "stop"}, null, 'pdalink fixedLeftWide')}} - {{:helper.link('Start Patrol', 'gear', {'radiomenu' : "1", 'op' : "go"}, null, 'pdalink fixedLeftWide')}} - {{:helper.link('Summon Bot', 'gear', {'radiomenu' : "1", 'op' : "summon"}, null, 'pdalink fixedLeftWide')}} -
    - {{/if}} -{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_security.tmpl b/nano/templates/pda_security.tmpl deleted file mode 100644 index 53d40185e05..00000000000 --- a/nano/templates/pda_security.tmpl +++ /dev/null @@ -1,50 +0,0 @@ -{{if !data.records}} -
    -
    Select a record:
    -
    - {{for data.recordsList}} -
    - {{:helper.link(value.Name, 'user', {'choice' : "Records", 'target' : value.ref}, null, 'pdalink fixedLeftWidest')}} -
    - {{empty}} -
    - No records found. -
    - {{/for}} -{{else}} -
    -
    -
    - {{if data.records.general}} - Name: {{:data.records.general.name}}
    - Sex: {{:data.records.general.sex}}
    - Species: {{:data.records.general.species}}
    - Age: {{:data.records.general.age}}
    - Rank: {{:data.records.general.rank}}
    - Fingerprint: {{:data.records.general.fingerprint}}
    - Physical Status: {{:data.records.general.p_stat}}
    - Mental Status: {{:data.records.general.m_stat}}

    - {{else}} - - General Record Lost!

    -
    - {{/if}} - {{if data.records.security}} -
    -
    Security Data:
    -
    - Criminal Status: {{:data.records.security.criminal}}

    - Minor Crimes: {{:data.records.security.mi_crim}}
    - Details: {{:data.records.security.mi_crim_d}}

    - Major Crimes: {{:data.records.security.ma_crim}}
    - Details: {{:data.records.security.ma_crim_d}}

    - Important Notes: {{:data.records.security.notes}} - {{else}} - - Security Record Lost!

    -
    - {{/if}} -
    -
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/pda_signaller.tmpl b/nano/templates/pda_signaller.tmpl deleted file mode 100644 index 2b5f148b025..00000000000 --- a/nano/templates/pda_signaller.tmpl +++ /dev/null @@ -1,38 +0,0 @@ - -
    -
    - Frequency: -
    -
    - {{:data.signal_freq}} -
    -   - {{:helper.link('-1', null, {'choice' : "Signal Frequency", 'sfreq' : "-10"}, null, null)}}  - {{:helper.link('-.2', null, {'choice' : "Signal Frequency", 'sfreq' : "-2"}, null, null)}}  - - {{:helper.link('+.2', null, {'choice' : "Signal Frequency", 'sfreq' : "2"}, null, null)}}  - {{:helper.link('+1', null, {'choice' : "Signal Frequency", 'sfreq' : "10"}, null, null)}} -
    -
    -
    -
    -
    -
    - Code: -
    -
    - - {{:data.signal_code}}
    -
    - {{:helper.link('-5', null, {'choice' : "Signal Code", 'scode' : "-5"}, null, null)}} - {{:helper.link('-1', null, {'choice' : "Signal Code", 'scode' : "-1"}, null, null)}} - {{:helper.link('+1', null, {'choice' : "Signal Code", 'scode' : "1"}, null, null)}} - {{:helper.link('+5', null, {'choice' : "Signal Code", 'scode' : "5"}, null, null)}} -
    -
    -
    - {{:helper.link('Send Signal', 'exclamation-circle', {'choice' : "Send Signal"}, null, null)}} -
    \ No newline at end of file diff --git a/nano/templates/pda_status_display.tmpl b/nano/templates/pda_status_display.tmpl deleted file mode 100644 index 8b153ad0ffd..00000000000 --- a/nano/templates/pda_status_display.tmpl +++ /dev/null @@ -1,44 +0,0 @@ - -
    -
    - Code: -
    -
    - {{:helper.link('Clear', 'trash', {'choice' : "Status", 'statdisp' : "blank"}, null, 'pdalink fixedLeftWide')}} - {{:helper.link('Shuttle ETA', 'gear', {'cartmenu' : "1", 'choice' : "Status", 'statdisp' : "shuttle"}, null, 'pdalink fixedLeftWide')}} - {{:helper.link('Message', 'gear', {'choice' : "Status", 'statdisp' : "message"}, null, 'pdalink fixedLeftWide')}} -
    -
    -
    -
    -
    - Message line 1 -
    -
    - {{:helper.link(data.records.message1 + ' (set)', 'pencil', {'choice' : "Status", 'statdisp' : "setmsg1"}, null, 'pdalink fixedLeftWide')}} -
    -
    -
    -
    - Message line 2 -
    -
    - {{:helper.link(data.records.message2 + ' (set)', 'pencil', {'choice' : "Status", 'statdisp' : "setmsg2"}, null, 'pdalink fixedLeftWide')}} -
    -
    - -
    -
    -
    - ALERT!: -
    -
    - {{:helper.link('None', 'bell', {'choice' : "Status", 'statdisp' : "alert", 'alert' : "default"}, null, 'pdalink fixedLeftWide')}} - {{:helper.link('Red Alert', 'bell', {'choice' : "Status", 'statdisp' : "alert", 'alert' : "redalert"}, null, 'pdalink fixedLeftWide')}} - {{:helper.link('Lockdown', 'exclamation-circle', {'choice' : "Status", 'statdisp' : "alert", 'alert' : "lockdown"}, null, 'pdalink fixedLeftWide')}} - {{:helper.link('Biohazard', 'exclamation-circle', {'choice' : "Status", 'statdisp' : "alert", 'alert' : "biohazard"}, null, 'pdalink fixedLeftWide')}} -
    -
    \ No newline at end of file diff --git a/nano/templates/pda_supply.tmpl b/nano/templates/pda_supply.tmpl deleted file mode 100644 index 810fc193769..00000000000 --- a/nano/templates/pda_supply.tmpl +++ /dev/null @@ -1,39 +0,0 @@ -
    -
    - Location: -
    -
    - - {{if data.supply.shuttle_moving}} - Moving to {{:data.supply.shuttle_loc}} - {{else}} - Shuttle at {{:data.supply.shuttle_loc}} - {{/if}} -
    - {{:data.supply.shuttle_time}} -
    -
    -
    -
    -
    -
    - Current Approved Orders
    - {{if data.supply.approved_count == 0}} - No current approved orders

    - {{else}} - {{for data.supply.approved}} - #{{:value.Number}} - {{:value.Name}} approved by {{:value.OrderedBy}}
    {{if value.Comment != ""}} {{:value.Comment}}
    {{/if}}
    - {{/for}} - {{/if}} -

    - Current Requested Orders
    - {{if data.supply.requests_count == 0}} - No current requested orders

    - {{else}} - {{for data.supply.requests}} - #{{:value.Number}} - {{:value.Name}} requested by {{:value.OrderedBy}}
    {{if value.Comment != ""}} {{:value.Comment}}
    {{/if}}
    - {{/for}} - {{/if}} -
    -
    -
    \ No newline at end of file diff --git a/nano/templates/poolcontroller.tmpl b/nano/templates/poolcontroller.tmpl deleted file mode 100644 index 3492aa8f85f..00000000000 --- a/nano/templates/poolcontroller.tmpl +++ /dev/null @@ -1,55 +0,0 @@ - -
    -
    - Current Temperature: -
    -
    - {{:helper.capitalizeFirstLetter(data.currentTemp)}} -
    -
    - -
    -
    - Safety Status: -
    -
    - {{if data.emagged}} - WARNING: OVERRIDDEN - {{else}} - Nominal - {{/if}} -
    -
    - -
    -
    - Temperature Selection: -
    - - {{if data.emagged}} -
    - {{:helper.link('Scalding', 'arrow-circle-up', { 'temp' : 'Scalding' }, data.currentTemp == "scalding" ? 'selected' : null)}} -
    - {{/if}} - -
    - {{:helper.link('Warm', 'arrow-circle-up', { 'temp' : 'Warm' }, data.currentTemp == "warm" ? 'selected' : null)}} -
    - -
    - {{:helper.link('Normal', 'arrow-circle-right', { 'temp' : 'Normal' }, data.currentTemp == "normal" ? 'selected' : null)}} -
    - -
    - {{:helper.link('Cool', 'arrow-circle-down', { 'temp' : 'Cool' }, data.currentTemp == "cool" ? 'selected' : null)}} -
    - - {{if data.emagged}} -
    - {{:helper.link('Frigid', 'arrow-circle-down', { 'temp' : 'Frigid' }, data.currentTemp == "frigid" ? 'selected' : null)}} -
    - {{/if}} -
    \ No newline at end of file diff --git a/nano/templates/portpump.tmpl b/nano/templates/portpump.tmpl deleted file mode 100644 index 91e37f38975..00000000000 --- a/nano/templates/portpump.tmpl +++ /dev/null @@ -1,84 +0,0 @@ -

    Pump Status

    -
    -
    - Tank Pressure: -
    -
    - {{:helper.smoothRound(data.tankPressure)}} kPa -
    -
    - -
    -
    - Port Status: -
    -
    - {{:data.portConnected ? 'Connected' : 'Disconnected'}} -
    -
    - -

    Holding Tank Status

    -{{if data.hasHoldingTank}} -
    -
    - Tank Label: -
    -
    -
    {{:data.holdingTank.name}}
    {{:helper.link('Eject', 'eject', {'remove_tank' : 1})}} -
    -
    - -
    -
    - Tank Pressure: -
    -
    - {{:helper.smoothRound(data.holdingTank.tankPressure)}} kPa -
    -
    -{{else}} -
    No holding tank inserted.
    -
     
    -{{/if}} - - -

    Power Regulator Status

    -
    -
    - Target Pressure: -
    -
    - {{:helper.displayBar(data.targetpressure, data.minpressure, data.maxpressure)}} -
    - {{:helper.link('-', null, {'pressure_adj' : -1000}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -100}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -10}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} - {{:helper.link('-', null, {'pressure_adj' : -1}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} -
     {{:data.targetpressure}} kPa 
    - {{:helper.link('+', null, {'pressure_adj' : 1}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 10}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 100}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'pressure_adj' : 1000}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} -
    -
    -
    - -
    -
    - Power Switch: -
    -
    - {{:helper.link('On', 'unlock', {'power' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'lock', {'power' : 1}, data.on ? null : 'selected')}} -
    -
    - -
    -
    - Pump Direction: -
    -
    - {{:helper.link('Out', 'arrow-right', {'direction' : 1}, data.pump_dir ? 'selected' : null)}}{{:helper.link('In', 'arrow-left', {'direction' : 1}, data.pump_dir ? null : 'selected')}} -
    -
    - - diff --git a/nano/templates/portscrubber.tmpl b/nano/templates/portscrubber.tmpl deleted file mode 100644 index 855b290886e..00000000000 --- a/nano/templates/portscrubber.tmpl +++ /dev/null @@ -1,74 +0,0 @@ -

    Scrubber Status

    -
    -
    - Tank Pressure: -
    -
    - {{:helper.smoothRound(data.tankPressure)}} kPa -
    -
    - -
    -
    - Port Status: -
    -
    - {{:data.portConnected ? 'Connected' : 'Disconnected'}} -
    -
    - -

    Holding Tank Status

    -{{if data.hasHoldingTank}} -
    -
    - Tank Label: -
    -
    -
    {{:data.holdingTank.name}}
    {{:helper.link('Eject', 'eject', {'remove_tank' : 1})}} -
    -
    - -
    -
    - Tank Pressure: -
    -
    - {{:helper.smoothRound(data.holdingTank.tankPressure)}} kPa -
    -
    -{{else}} -
    No holding tank inserted.
    -
     
    -{{/if}} - - -

    Power Regulator Status

    -
    -
    - Volume Rate: -
    -
    - {{:helper.displayBar(data.rate, data.minrate, data.maxrate)}} -
    - {{:helper.link('-', null, {'volume_adj' : -1000}, (data.rate > data.minrate) ? null : 'disabled')}} - {{:helper.link('-', null, {'volume_adj' : -100}, (data.rate > data.minrate) ? null : 'disabled')}} - {{:helper.link('-', null, {'volume_adj' : -10}, (data.rate > data.minrate) ? null : 'disabled')}} - {{:helper.link('-', null, {'volume_adj' : -1}, (data.rate > data.minrate) ? null : 'disabled')}} -
     {{:data.rate}} kPa 
    - {{:helper.link('+', null, {'volume_adj' : 1}, (data.rate < data.maxrate) ? null : 'disabled')}} - {{:helper.link('+', null, {'volume_adj' : 10}, (data.rate < data.maxrate) ? null : 'disabled')}} - {{:helper.link('+', null, {'volume_adj' : 100}, (data.rate < data.maxrate) ? null : 'disabled')}} - {{:helper.link('+', null, {'volume_adj' : 1000}, (data.rate < data.maxrate) ? null : 'disabled')}} -
    -
    -
    - -
    -
    - Power Switch: -
    -
    - {{:helper.link('On', 'unlock', {'power' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'lock', {'power' : 1}, data.on ? null : 'selected')}} -
    -
    - diff --git a/nano/templates/power_monitor.tmpl b/nano/templates/power_monitor.tmpl deleted file mode 100644 index c4bb8072373..00000000000 --- a/nano/templates/power_monitor.tmpl +++ /dev/null @@ -1,67 +0,0 @@ - -{{if !data.powermonitor && data.select_monitor}} -

    Station Power Monitors

    -
    - Select a power monitor: -
    - {{for data.powermonitors}} -
    - {{:helper.link(value.Name, 'gear', { 'selectmonitor' : value.ref }, null, null)}} -
    - {{empty}} -

    Network Error

    - No power monitors found. - {{/for}} -{{/if}} -{{if data.powermonitor}} - {{if data.select_monitor}} - {{:helper.link('Return', 'gear', { 'return' : 1 }, null, null)}} - {{/if}} -

    Powernet Status

    -
    -
    - Total Power: -
    -
    - {{:helper.smoothRound(data.poweravail)}} W -
    -
    -
    -
    - Total Load: -
    -
    - {{:helper.smoothRound(data.powerload)}} W -
    -
    -
    -
    - Total Demand: -
    -
    - {{:helper.smoothRound(data.powerdemand)}} W -
    -
    -
    - - - {{for data.apcs}} - - {{:helper.string('', value.Equipment == "On" || value.Equipment == "AOn" ? '#4f7529' : '#8f1414', value.Equipment)}} - {{:helper.string('', value.Lights == "On" || value.Lights == "AOn" ? '#4f7529' : '#8f1414', value.Lights)}} - {{:helper.string('', value.Environment == "On" || value.Environment == "AOn" ? '#4f7529' : '#8f1414', value.Environment)}} - {{:helper.string('', value.CellStatus == "F" ? '#4f7529' : value.CellStatus == "C" ? '#cd6500' : '#8f1414', value.CellStatus == "M" ? 'No Cell' : value.CellPct + '%', value.CellStatus == "M" ? '' : ' (' + value.CellStatus + ')')}} - - - {{/for}} -
    AreaEquip.LightingEnviron.CellLoad
    {{:value.Name}}{1}{1}{1}{1}{2}{{:value.Load}}W
    -
    -{{else}} - {{if !data.select_monitor}} -

    Network Error

    - Power monitor not connected to power network. - {{/if}} -{{/if}} \ No newline at end of file diff --git a/nano/templates/radio_basic.tmpl b/nano/templates/radio_basic.tmpl deleted file mode 100644 index c6a59d59691..00000000000 --- a/nano/templates/radio_basic.tmpl +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - -{{if data.useSyndMode}} - {{:helper.syndicateMode()}} -{{/if}} - -
    -
    - Microphone -
    -
    - {{if data.mic_cut}} - {{:helper.link('On', null, null, 'disabled')}} - {{:helper.link('Off', null, null, 'disabled')}} - {{else}} - {{:helper.link('On', null, {'talk' : 0}, data.mic_status ? 'selected' : null)}} - {{:helper.link('Off', null, {'talk' : 1}, data.mic_status ? null : 'selected')}} - {{/if}} -
    -
    - -
    -
    - Speaker -
    -
    - {{if data.spk_cut}} - {{:helper.link('On', null, null, 'disabled')}} - {{:helper.link('Off', null, null, 'disabled')}} - {{else}} - {{:helper.link('On', null, {'listen' : 0}, data.speaker ? 'selected' : null)}} - {{:helper.link('Off', null, {'listen' : 1}, data.speaker ? null : 'selected')}} - {{/if}} -
    -
    - -{{if data.has_subspace}} -
    -
    - Subspace Transmission: -
    -
    - {{:helper.link('On', null, {'mode' : 1}, data.subspace ? 'selected' : null)}} - {{:helper.link('Off', null, {'mode' : 0}, data.subspace ? null : 'selected')}} -
    -
    -{{/if}} - -{{if data.has_loudspeaker}} -
    -
    - Loudspeaker: -
    -
    - {{:helper.link('On', null, {'shutup' : 0}, data.loudspeaker ? 'selected' : null)}} - {{:helper.link('Off', null, {'shutup' : 1}, data.loudspeaker ? null : 'selected')}} -
    -
    -{{/if}} - -
    -
    - Frequency: {{:data.freq}} -
    -
    - {{:helper.link('--', null, {'freq' : -10})}} - {{:helper.link('-', null, {'freq' : -2})}} - {{:helper.link('+', null, {'freq' : 2})}} - {{:helper.link('++', null, {'freq' : 10})}} -
    -
    - -{{if data.chan_list_len >= 1}} -

    Channels

    -
    - {{for data.chan_list}} -
    - {{:value.display_name}} -
    -
    - {{if value.secure_channel}} - {{:helper.link('On', null, {'ch_name' : value.chan, 'listen' : value.sec_channel_listen}, value.sec_channel_listen ? null : 'selected')}} - {{:helper.link('Off', null, {'ch_name' : value.chan, 'listen' : value.sec_channel_listen}, value.sec_channel_listen ? 'selected' : null)}} - {{else}} - {{:helper.link('Switch', null, {'spec_freq' : value.chan}, data.rawfreq == value.chan ? 'selected' : null)}} - {{/if}} -
    - {{/for}} -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/radio_electro.tmpl b/nano/templates/radio_electro.tmpl deleted file mode 100644 index 9fb8183ad01..00000000000 --- a/nano/templates/radio_electro.tmpl +++ /dev/null @@ -1,37 +0,0 @@ - -
    -
    - Power -
    -
    - {{:helper.link('On', null, {'power' : 1}, data.power ? 'selected' : null)}} - {{:helper.link('Off', null, {'power' : 1}, data.power ? null : 'selected')}} -
    -
    - -
    -
    - Frequency: {{:data.freq}} -
    -
    - {{:helper.link('--', null, {'freq' : -10})}} - {{:helper.link('-', null, {'freq' : -2})}} - {{:helper.link('+', null, {'freq' : 2})}} - {{:helper.link('++', null, {'freq' : 10})}} -
    -
    - -
    -
    - Code: {{:data.code}} -
    -
    - {{:helper.link('--', null, {'code' : -5})}} - {{:helper.link('-', null, {'code' : -1})}} - {{:helper.link('+', null, {'code' : 1})}} - {{:helper.link('++', null, {'code' : 5})}} -
    -
    \ No newline at end of file diff --git a/nano/templates/revelation.tmpl b/nano/templates/revelation.tmpl deleted file mode 100644 index 5412c440caa..00000000000 --- a/nano/templates/revelation.tmpl +++ /dev/null @@ -1,23 +0,0 @@ -{{:helper.syndicateMode()}} -
    -
    - Payload status: -
    -
    - {{if data.armed}} - ARMED - {{else}} - DISARMED - {{/if}} -
    -
    - Controls: -
    -
    - -
    {{:helper.link('OBFUSCATE PROGRAM NAME', 'eye-slash', {'action' : 'PRG_obfuscate'})}} -
    {{:helper.link(data.armed ? 'DISARM' : 'ARM', data.armed ? 'crosshairs' : 'info', {'action' : 'PRG_arm'})}} - {{:helper.link('ACTIVATE', 'exclamation-triangle', {'action' : 'PRG_activate'}, data.armed ? null : 'disabled', data.armed ? 'redButton' : null)}} -
    -
    -
    \ No newline at end of file diff --git a/nano/templates/robot_control.tmpl b/nano/templates/robot_control.tmpl deleted file mode 100644 index 6e97ffaf3d7..00000000000 --- a/nano/templates/robot_control.tmpl +++ /dev/null @@ -1,100 +0,0 @@ -{{if data.robots}} - {{if !data.is_ai}} -
    -
    - Emergency Self-Destruct: -
    -
    - {{if data.safety}} - {{:helper.link('ARM', 'unlock', {'arm' : 1}, !data.allowed ? 'disabled' : null)}} - {{:helper.link('DETONATE', 'exclamation-circle', {'nuke' : 1}, 'disabled')}} - {{else}} - {{:helper.link('DISARM', 'lock', {'arm' : 1}, !data.allowed ? 'disabled' : null)}} - {{:helper.link('DETONATE', 'exclamation-circle', {'nuke' : 1}, !data.allowed ? 'disabled' : null, 'redButton')}} - {{/if}} -
    -
    -
    - {{/if}} - {{for data.robots}} -
    -
    -

    {{:value.name}}

    -

    Information

    - - Status: - - - {{:value.status}} - - - System Integrity: - - - {{:helper.smoothRound(value.health)}}% - - - Location: - - - ({{:value.xpos}}, {{:value.ypos}}, {{:value.zpos}}): {{:value.area}} - - - Master AI: - - - {{:value.master_ai}} - - - Module: - - - {{:value.module}} - - - {{if value.hackable}} - - Safeties: - - - ENABLED - - {{else value.hacked}} - - Safeties: - - - DISABLED - - {{/if}} -

    Power Cell

    - {{if value.cell}} - - Rating: - - - {{:value.cell_capacity}} - - {{:helper.displayBar(value.cell_percentage, 0, 100, (value.cell_percentage >= 50) ? 'good' : (value.cell_percentage >= 25) ? 'average' : 'bad')}} - {{:helper.smoothRound(value.cell_percentage)}}% - {{else}} - Not Installed - {{:helper.displayBar(100, 0, 100, 'bad')}} - {{/if}} -

    Actions

    - {{if value.status == "Operational"}} - {{:helper.link('Lockdown', 'lock', {'lockdown' : value.name}, !data.allowed ? 'disabled' : null)}} - {{else}} - {{:helper.link('Unlock', 'unlock', {'lockdown' : value.name}, !data.allowed ? 'disabled' : null)}} - {{/if}} - {{:helper.link('Self-Destruct', 'exclamation-circle', {'detonate' : value.name}, !data.allowed ? 'disabled' : null, 'redButton')}} - {{if value.hackable}} - {{:helper.link('Hack', 'calculator', {'hack' : value.name}, !data.allowed ? 'disabled' : null, 'redButton')}} - {{/if}} -
    - {{/for}} -{{else}} -
    -

    No robots were found.

    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/sec_camera.tmpl b/nano/templates/sec_camera.tmpl deleted file mode 100644 index ebcf44c7ddb..00000000000 --- a/nano/templates/sec_camera.tmpl +++ /dev/null @@ -1,42 +0,0 @@ - -{{:helper.link('Show Map', 'thumb-tack', {'showMap' : 1})}} -{{:helper.link('Reset', 'refresh', {'reset' : 1})}} -
    -
    Current Camera:
    - {{if data.current}} -
    {{:data.current.name}}
    - {{else}} -
    None
    - {{/if}} -
    - -{{if data.networks}} -
    -

    Networks

    - Please select the networks you'd like this console to monitor. - {{if data.emagged}} -
    WARNING: Unauthorized access detected.
    - {{/if}} -
    - {{for data.networks}} - {{:helper.link(value.name, value.active ? 'minus' : 'plus', { 'activate' : value.name, 'active' : value.active }, '', value.active ? 'linkOn' : '')}} - {{/for}} -
    -
    -{{/if}} -
    -

    Cameras

    - {{for data.cameras}} - {{if data.current && value.name == data.current.name}} - {{:helper.link(value.name, '', {'switchTo' : value.camera}, 'selected')}} - {{else value.deact}} - {{:helper.link(value.name + " (deactivated)", '', {}, 'inactive')}} - {{else}} - {{:helper.link(value.name, '', {'switchTo' : value.camera})}} - {{/if}} - {{/for}} -
    - diff --git a/nano/templates/sec_camera_map_content.tmpl b/nano/templates/sec_camera_map_content.tmpl deleted file mode 100644 index 52dc8aeadd6..00000000000 --- a/nano/templates/sec_camera_map_content.tmpl +++ /dev/null @@ -1,20 +0,0 @@ - -{{for data.cameras}} - {{if value.z == 1}} -
    - {{if data.current && value.name == data.current.name}} - {{:helper.link("#", '', {'switchTo' : value.camera}, 'selected')}} - {{else value.deact}} - {{:helper.link('#', '', {}, 'inactive')}} - {{else}} - {{:helper.link("#", '', {'switchTo' : value.camera})}} - {{/if}} - -
    - {{/if}} -{{/for}} \ No newline at end of file diff --git a/nano/templates/sec_camera_map_header.tmpl b/nano/templates/sec_camera_map_header.tmpl deleted file mode 100644 index ebe90da3015..00000000000 --- a/nano/templates/sec_camera_map_header.tmpl +++ /dev/null @@ -1,23 +0,0 @@ - -{{:helper.link('Show Camera List', 'file-text', {'showMap' : 0})}} -{{:helper.link('Reset', 'refresh', {'reset' : 1})}} -
    -
    -
    Current Camera: 
    - {{if data.current}} -
    {{:data.current.name}}
    - {{else}} -
    None
    - {{/if}} -
    -
    -
    - Zoom Level:  - - - - -
    diff --git a/nano/templates/secure_data.tmpl b/nano/templates/secure_data.tmpl deleted file mode 100644 index e3e285013c5..00000000000 --- a/nano/templates/secure_data.tmpl +++ /dev/null @@ -1,234 +0,0 @@ - - - - -{{if !data.temp && data.screen == 1}} - -{{/if}} - - -
    -
    - Confirm Identity: -
    -
    - {{:helper.link(data.scan ? data.scan : "----------", 'eject', {'scan' : 1}, null, data.scan ? 'itemContentWide' : 'fixedLeft')}} -
    -
    -
    -{{if data.authenticated}} - {{if data.screen == 1}} -
    -
    {{:helper.link('New Record', 'plus', {'new_g' : 1})}}
    -
    -

    Record List

    - - - - -
    - Search: -
    -
    - - - - - - - - - - - - - {{for data.records}} - - - - - - - - {{/for}} - -
    {{:helper.link('Name', null, {'sort' : 'name'}, null, 'infoButton')}}{{:helper.link('ID', null, {'sort' : 'id'}, null, 'infoButton')}}{{:helper.link('Rank', null, {'sort' : 'rank'}, null, 'infoButton')}}{{:helper.link('Fingerprints', null, {'sort' : 'fingerprint'}, null, 'infoButton')}}{{:helper.link('Criminal Status', null, {'sort' : 'crimstat'}, null, 'infoButton')}}
    {{:helper.link(value.name, 'user', {'d_rec' : value.ref}, null, 'infoButton')}}{{:value.id}}{{:value.rank}}{{:value.fingerprint}}{{:value.crimstat}}
    -
    -
    -

    Menu

    -
    {{:helper.link('Record Maintenance', 'wrench', {'screen' : 2})}}
    -
    {{:helper.link('Print Cell Log', 'print', {'printlogs' : 1})}}
    -
    {{:helper.link('Logout', 'lock', {'logout' : 1})}}
    - {{else data.screen == 2}} -

    Records Maintenance

    -
    {{:helper.link('Backup To Disk', 'download', {'back' : 1}, 'disabled')}}
    -
    {{:helper.link('Upload From Disk', 'upload', {'u_load' : 1}, 'disabled')}}
    -
    {{:helper.link('Delete All Records', 'trash', {'del_all' : 1})}}
    -
    {{:helper.link('Delete All Cell Logs', 'trash', {'del_alllogs' : 1})}}
    -
    {{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
    - {{else data.screen == 3}} -

    Security Record

    -

    General Data

    - {{if data.general.empty}} -
    General Record Lost!
    - {{else}} -
    - - - -
    - {{for data.general.fields}} -
    -
    - {{:value.field}} -
    -
    - {{:helper.link(value.value, value.edit ? 'pencil' : 'user-times', {'field' : value.edit}, value.edit ? null : 'disabled')}} -
    -
    - {{/for}} -
    - {{if data.general.has_photos}} - - {{for data.general.photos}} - {{if value.photo}} - - {{/if}} - {{/for}} - {{/if}} -
    -
    - {{/if}} - -

    Security Data

    - {{if data.security.empty}} -
    Security Record Lost!
    -
    -
    {{:helper.link('New Record', 'plus', {'new_s' : 1})}}
    -
    -

    Menu

    - {{else}} - {{for data.security.fields}} -
    -
    - {{:value.field}} -
    -
    - {{:helper.link(value.value, 'pencil', {'field' : value.edit})}} -
    -
    - {{if value.line_break}} -
    - {{/if}} - {{/for}} - -

    Comments/Log

    - {{for data.security.comments}} -
    {{:value}}
    -
    {{:helper.link('Remove entry', 'trash', {'del_c' : index})}}
    -

    - {{/for}} - -
    -
    {{:helper.link('Add Entry', 'plus', {'add_c' : 1})}}
    -
    - -

    Menu

    -
    {{:helper.link('Delete Record (Security Only)', 'trash', {'del_r' : 1})}}
    - {{/if}} - -
    {{:helper.link('Delete Record (All)', 'trash', {'del_rg' : 1})}}
    -
    {{:helper.link('Print Record', 'print', {'print_r' : 1})}}
    -
    {{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
    - {{/if}} -{{else}} -

    Menu

    - {{:helper.link('Login', 'unlock', {'login' : 1})}} -{{/if}} - -{{if data.temp}} -
    -
    - {{if data.temp.notice}} -
    {{:data.temp.text}}
    - {{else}} -
    {{:data.temp.text}}
    - {{/if}} - - {{if data.temp.has_buttons}} -
    -
    -
    - {{for data.temp.buttons}} - {{:helper.link(value.name, value.icon, {'temp' : 1, 'temp_action' : value.href}, value.status)}} - {{/for}} -
    -
    -
    - {{/if}} - -
    -
    {{:helper.link('Clear screen', 'home', {'temp' : 1})}}
    -
    -
    -
    -{{/if}} diff --git a/nano/templates/shuttle_console.tmpl b/nano/templates/shuttle_console.tmpl deleted file mode 100644 index 8277e92eb76..00000000000 --- a/nano/templates/shuttle_console.tmpl +++ /dev/null @@ -1,37 +0,0 @@ - - -
    -
    Location:
    -
    {{:data.status ? data.status : '*Missing*'}}
    -
    -{{if data.shuttle}} - {{if data.docking_ports_len}} -
    -
    - {{for data.docking_ports}} -
    {{:helper.link('Send to ' + value.name, 'sign-out', {'move' : value.id})}}
    - {{/for}} -
    - {{else}} -
    -
    Status:
    -
    Shuttle locked - {{if data.admin_controlled}} - , authorized personnel only -
    -
    -
    {{:helper.link('Request authorization', 'share-square-o', {'request' : 1})}}
    - {{else}} -
    -
    -
    - {{/if}} - {{/if}} - - {{if data.docking_request}} -
    {{:helper.link('Request docking at the station', 'share-square-o', {'request' : 1})}}
    - {{/if}} -{{/if}} diff --git a/nano/templates/skills_data.tmpl b/nano/templates/skills_data.tmpl deleted file mode 100644 index 6f8fb1d6a0d..00000000000 --- a/nano/templates/skills_data.tmpl +++ /dev/null @@ -1,199 +0,0 @@ - - - - -{{if !data.temp && data.screen == 1}} - -{{/if}} - - -
    -
    - Confirm Identity: -
    -
    - {{:helper.link(data.scan ? data.scan : "----------", 'eject', {'scan' : 1}, null, data.scan ? 'itemContentWide' : 'fixedLeft')}} -
    -
    -
    -{{if data.authenticated}} - {{if data.screen == 1}} -
    -
    {{:helper.link('New Record', 'plus', {'new_g' : 1})}}
    -
    -

    Record List

    - - - - -
    - Search: -
    -
    - - - - - - - - - - - - {{for data.records}} - - - - - - - {{/for}} - -
    {{:helper.link('Name', null, {'sort' : 'name'}, null, 'infoButton')}}{{:helper.link('ID', null, {'sort' : 'id'}, null, 'infoButton')}}{{:helper.link('Rank', null, {'sort' : 'rank'}, null, 'infoButton')}}{{:helper.link('Fingerprints', null, {'sort' : 'fingerprint'}, null, 'infoButton')}}
    {{:helper.link(value.name, 'user', {'d_rec' : value.ref}, null, 'infoButton')}}{{:value.id}}{{:value.rank}}{{:value.fingerprint}}
    -
    -
    -

    Menu

    -
    {{:helper.link('Record Maintenance', 'wrench', {'screen' : 2})}}
    -
    {{:helper.link('Logout', 'lock', {'logout' : 1})}}
    - {{else data.screen == 2}} -

    Records Maintenance

    -
    {{:helper.link('Backup To Disk', 'download', {'back' : 1}, 'disabled')}}
    -
    {{:helper.link('Upload From disk', 'upload', {'u_load' : 1}, 'disabled')}}
    -
    {{:helper.link('Delete All Records', 'trash', {'del_all' : 1})}}
    -
    {{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
    - {{else data.screen == 3}} -

    Employment Record

    -

    General Data

    - {{if data.general.empty}} -
    General Record Lost!
    - {{else}} -
    - - - -
    - {{for data.general.fields}} -
    -
    - {{:value.field}} -
    -
    - {{:helper.link(value.value, value.name ? 'pencil' : 'user-times', {'field' : value.name}, value.name ? null : 'disabled')}} -
    -
    - {{/for}} -
    - {{if data.general.has_photos}} - {{for data.general.photos}} - {{if value.photo}} - - {{/if}} - {{/for}} - {{/if}} -
    -
    - -

    Employment Data

    -
    -
    - Employment/skills summary: -
    -
    - {{:data.general.notes ? data.general.notes : 'None'}} -
    -
    - {{/if}} -

    Menu

    -
    {{:helper.link('Delete Record (All)', 'trash', {'del_rg' : 1})}}
    -
    {{:helper.link('Print Record', 'print', {'print_r' : 1})}}
    -
    {{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
    - {{/if}} -{{else}} -

    Menu

    - {{:helper.link('Login', 'unlock', {'login' : 1})}} -{{/if}} - -{{if data.temp}} -
    -
    - {{if data.temp.notice}} -
    {{:data.temp.text}}
    - {{else}} -
    {{:data.temp.text}}
    - {{/if}} - - - {{if data.temp.has_buttons}} -
    -
    -
    - {{for data.temp.buttons}} - {{:helper.link(value.name, value.icon, {'temp' : 1, 'temp_action' : value.val}, value.status)}} - {{/for}} -
    -
    -
    - {{/if}} - -
    -
    {{:helper.link('Clear screen', 'home', {'temp' : 1})}}
    -
    -
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/sleeper.tmpl b/nano/templates/sleeper.tmpl deleted file mode 100644 index cacf6c83ff5..00000000000 --- a/nano/templates/sleeper.tmpl +++ /dev/null @@ -1,211 +0,0 @@ - -

    Sleeper Status

    - -
    - {{if !data.hasOccupant}} -
    Sleeper Unoccupied
    - {{else}} -
    - {{:data.occupant.name}} =>  - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
    - -
    -
    Health:
    - {{if data.occupant.health >= data.occupant.maxHealth}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}} - {{else data.occupant.health > 0}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'average')}} - {{else data.occupant.health >= data.minhealth}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}} - {{else}} - {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'bad alignRight')}} - {{/if}} -
    {{:helper.smoothRound(data.occupant.health)}}
    -
    - -
    -
    => Brute Damage:
    - {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.bruteLoss)}}
    -
    - -
    -
    => Resp. Damage:
    - {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.oxyLoss)}}
    -
    - -
    -
    => Toxin Damage:
    - {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.toxLoss)}}
    -
    - -
    -
    => Burn Severity:
    - {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}} -
    {{:helper.smoothRound(data.occupant.fireLoss)}}
    -
    - -
    -
    - -
    Patient Temperature:
    - {{if data.occupant.temperatureSuitability == -3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == -2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == -1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 0}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{else data.occupant.temperatureSuitability == 3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
    {{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
    - {{/if}} -
    - - - {{if data.occupant.hasBlood}} - -
    -
    -
    Pulse:
    {{:data.occupant.pulse}} bpm
    -
    -
    -
    Blood Level:
    - {{if data.occupant.bloodPercent <= 60}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{else data.occupant.bloodPercent <= 90}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{else}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}} -
    - {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
    - {{/if}} -
    - {{/if}} - {{/if}} -
    - -

    Sleeper Operation

    -
    -
    - Sleeper Status: -
    -
    - {{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectify' : 1}, data.hasOccupant ? null : 'disabled')}} -
    -
    -
     
    -
    -
    - Dialysis Beaker: -
    -
    - {{if data.isBeakerLoaded}} - {{:helper.smoothRound(data.beakerFreeSpace)}} units of space remaining
    - {{else}} - No Dialysis Output Beaker Loaded - {{/if}} -
    -
    - {{:helper.link('Eject Beaker', 'eject', {'removebeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}} - {{if data.isBeakerLoaded}} -
    -
    - {{:helper.link('On', 'power-off', {'togglefilter' : 1}, data.occupant.hasBlood ? (data.dialysis ? 'selected' : null) : 'disabled')}}{{:helper.link('Off', 'close', {'togglefilter' : 1}, data.dialysis ? null : 'selected')}} -
    -
    - {{/if}} -
    -
    -
    -
    - Chemicals: -
    - - {{for data.chemicals}} -
    {{:value.title}}
    -
    - {{if value.overdosing}} - {{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'bad')}} - {{else value.od_warning}} - {{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'average')}} - {{else}} - {{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'good')}} - {{/if}} -
    {{:helper.smoothRound(value.pretty_amount)}}/{{:data.maxchem}}
    -
    -
    -
    -
    - {{:helper.link('5', 'gear', {'chemical' : value.id, 'amount' : 5}, (!value.injectable || ((value.occ_amount + 5) > data.maxchem)) ? 'disabled' : null)}} - {{:helper.link('10', 'gear', {'chemical' : value.id, 'amount' : 10}, (!value.injectable || ((value.occ_amount + 10) > data.maxchem)) ? 'disabled' : null)}} -
    -
    - {{/for}} - -
     
    -
    -
    - Auto-eject dead patients: -
    -
    - {{:helper.link('On', 'power-off', {'auto_eject_dead_on' : 1}, data.auto_eject_dead ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_dead_off' : 0}, data.auto_eject_dead ? null : 'selected')}} -
    -
    -
    - diff --git a/nano/templates/slotmachine.tmpl b/nano/templates/slotmachine.tmpl deleted file mode 100644 index 29545450a14..00000000000 --- a/nano/templates/slotmachine.tmpl +++ /dev/null @@ -1,34 +0,0 @@ - -{{if data.money != null}} -
    - {{:data.plays}} players have tried their luck today! -
    -
    -
    Credits Remaining:
    - {{:helper.string("
    {1}
    ", data.money >= 10 ? "" : "bad", helper.smoothRound(data.money))}} -
    -
    -
    - Ten credits to play! -
    -
    - {{:helper.link('SPIN!', 'refresh', {'ops' : 1}, data.money >= 10 && !data.working ? null : 'disabled')}} -
    -
    - {{if data.result}} -
    - {{:data.result}} -
    - {{/if}} - {{if data.working}} -
    Spinning!
    - {{/if}} -{{else}} -
    - Could not scan your card or could not find account!
    - Please wear or hold your ID and try again. -
    -{{/if}} diff --git a/nano/templates/smartfridge.tmpl b/nano/templates/smartfridge.tmpl deleted file mode 100644 index 3c59892486d..00000000000 --- a/nano/templates/smartfridge.tmpl +++ /dev/null @@ -1,46 +0,0 @@ -
    - {{:helper.link('Close', 'gear', {'close' : 1}, null, 'fixedLeft')}} -
    - -
    -

    Storage

    - {{if data.secure}} - - {{:data.locked == -1 ? "Sec.re ACC_** //):securi_nt.diag=>##'or 1=1'%($..." : "Secure Access: Please have your identification ready."}} - - {{/if}} -
    -
    - {{if data.can_dry}} -
    - Drying: -
    -
    - {{:helper.link('On', 'power-off', {'dryingOn' : 1}, data.drying ? 'selected' : null)}}{{:helper.link('Off', 'close', {'dryingOff' : 1}, data.drying ? null : 'selected')}} -
    - {{/if}} -
    -
    - {{if data.contents}} - {{for data.contents}} -
    - {{:value.display_name}} ({{:value.quantity}} available) -
    Vend: 
    {{:helper.link('x1', 'arrow-circle-down', { "vend" : value.vend, "amount" : 1 }, null, 'statusValue')}} - {{if value.quantity >= 5}} - {{:helper.link('x5', 'arrow-circle-down', { "vend" : value.vend, "amount" : 5 }, null, 'statusValue')}} - {{/if}} - {{if value.quantity >= 10}} - {{:helper.link('x10', 'arrow-circle-down', { "vend" : value.vend, "amount" : 10 }, null, 'statusValue')}} - {{/if}} - {{if value.quantity >= 25}} - {{:helper.link('x25', 'arrow-circle-down', { "vend" : value.vend, "amount" : 25 }, null, 'statusValue')}} - {{/if}} - {{if value.quantity > 1}} - {{:helper.link('All', 'arrow-circle-down', { "vend" : value.vend, "amount" : value.quantity }, null, 'statusValue')}} - {{/if}} -
    - {{/for}} - {{else}} - No products loaded. - {{/if}} -
    diff --git a/nano/templates/smes.tmpl b/nano/templates/smes.tmpl deleted file mode 100644 index 2657c0f724e..00000000000 --- a/nano/templates/smes.tmpl +++ /dev/null @@ -1,89 +0,0 @@ -
    -
    - Stored Capacity: -
    -
    - {{:helper.displayBar(data.storedCapacity, 0, 100, data.charging ? 'good' : 'average')}} -
    - {{:helper.smoothRound(data.storedCapacity)}}% -
    -
    -
    - -

    Input Management

    -
    -
    - Charge Mode: -
    -
    - {{:helper.link('Auto', 'refresh', {'cmode' : 1}, data.chargeMode ? 'selected' : null)}}{{:helper.link('Off', 'close', {'cmode' : 1}, data.chargeMode ? null : 'selected')}} -   - {{if data.charging == 2}} - [Charging] - {{else data.charging == 1}} - [Partially Charging] - {{else}} - [Not Charging] - {{/if}} -
    -
    - -
    -
    - Input Level: -
    -
    - {{:helper.displayBar(data.chargeLevel, 0, data.chargeMax)}} -
    - {{:helper.link('MIN', null, {'input' : 'min'}, (data.chargeLevel > 0) ? null : 'disabled')}} - {{:helper.link('SET', null, {'input' : 'set'}, null)}} - {{:helper.link('MAX', null, {'input' : 'max'}, (data.chargeLevel < data.chargeMax) ? null : 'disabled')}} -
     {{:data.chargeLevel}} W 
    -
    -
    -
    - -

    Output Management

    -
    -
    - Output Status: -
    -
    - {{:helper.link('Online', 'power-off', {'online' : 1}, data.outputOnline ? 'selected' : null)}}{{:helper.link('Offline', 'close', {'online' : 1}, data.outputOnline ? null : 'selected')}} -   - {{if data.outputting == 2}} - [Outputting] - {{else data.outputting == 1}} - [Stored energy too low] - {{else}} - [Not Outputting] - {{/if}} -
    -
    - -
    -
    - Output Level: -
    -
    - {{:helper.displayBar(data.outputLevel, 0, data.outputMax)}} -
    - {{:helper.link('MIN', null, {'output' : 'min'}, (data.outputLevel > 0) ? null : 'disabled')}} - {{:helper.link('SET', null, {'output' : 'set'}, null)}} - {{:helper.link('MAX', null, {'output' : 'max'}, (data.outputLevel < data.outputMax) ? null : 'disabled')}} -
     {{:data.outputLevel}} W 
    -
    -
    -
    - -
    -
    - Output Load: -
    -
    - {{:helper.displayBar(data.outputLoad, 0, data.outputMax, (data.outputLoad < data.outputLevel) ? 'good' : 'average')}} -
    - {{:helper.smoothRound(data.outputLoad)}} W -
    -
    -
    \ No newline at end of file diff --git a/nano/templates/solar_control.tmpl b/nano/templates/solar_control.tmpl deleted file mode 100644 index f4fbf6ea739..00000000000 --- a/nano/templates/solar_control.tmpl +++ /dev/null @@ -1,96 +0,0 @@ - - -

    Status

    -
    -
    -
    - Generated power: -
    -
    - {{:helper.smoothRound(data.generated)}} W -
    -
    - -
    -
    - Orientation: -
    - -
    - {{:helper.smoothRound(data.angle)}}° ({{:data.direction}})  -
    - - {{:helper.link('15°', 'minus', {'rate_control' : '1', 'cdir' : '-15'})}} - {{:helper.link('1°', 'minus', {'rate_control' : '1', 'cdir' : '-1'})}} - {{:helper.link('1°', 'plus', {'rate_control' : '1', 'cdir' : '1'})}} - {{:helper.link('15°', 'plus', {'rate_control' : '1', 'cdir' : '15'})}} -
    -
    - -

    Tracking

    -
    -
    -
    - Tracker status: -
    - - {{:helper.link('Off', 'close', {'track' : '0'}, (data.tracking_state == 0) ? 'selected' : '')}} - {{:helper.link('Timed', 'clock-o', {'track' : '1'}, (data.tracking_state == 1) ? 'selected' : '')}} - {{if data.connected_tracker}} - {{:helper.link('Auto', 'signal', {'track' : '2'}, (data.tracking_state == 2) ? 'selected' : '')}} - {{else}} - {{:helper.link('Auto', 'signal', null, 'disabled')}} - {{/if}} -
    - -
    -
    - Tracking rate: -
    - -
    - {{:helper.smoothRound(data.tracking_rate)}} deg/h ({{:data.rotating_way}}) -
    -
    - -
    - {{:helper.link('180°', 'minus', {'rate_control' : '1', 'tdir' : '-180'})}} - {{:helper.link('30°', 'minus', {'rate_control' : '1', 'tdir' : '-30'})}} - {{:helper.link('1°', 'minus', {'rate_control' : '1', 'tdir' : '-1'})}} - {{:helper.link('1°', 'plus', {'rate_control' : '1', 'tdir' : '1'})}} - {{:helper.link('30°', 'plus', {'rate_control' : '1', 'tdir' : '30'})}} - {{:helper.link('180°', 'plus', {'rate_control' : '1', 'tdir' : '180'})}} -
    -
    - -

    Connected Devices

    -
    -
    -
    - Solars panels: -
    -
    - {{:data.connected_panels}} connected -
    -
    - -
    -
    - Solar tracker: -
    - -
    - {{if data.connected_tracker}} - Found - {{else}} - Not Found - {{/if}} -
    -
    -
    -
    - {{:helper.link('Search for devices', 'refresh', {'search_connected' : '1'})}} -
    diff --git a/nano/templates/song.tmpl b/nano/templates/song.tmpl deleted file mode 100644 index 28ccae0c923..00000000000 --- a/nano/templates/song.tmpl +++ /dev/null @@ -1,89 +0,0 @@ - - -
    -
    -
    Playback
    -
    - {{:helper.link('New', 'file', {'newsong': 1})}} - {{:helper.link('Import', 'folder-open', {'import': 1})}} - {{:helper.link('Play', 'play', {'play': 1}, data.lines.length > 0 ? (data.playing ? 'selected' : null) : 'disabled')}} - {{:helper.link('Stop', 'stop', {'stop': 1}, !data.playing ? 'selected' : null)}} -
    -
    -
    -
    Repeat Song:
    - {{:helper.link('-', null, {'repeat' : -10}, data.repeat > 0 && !data.playing ? null : 'disabled')}} - {{:helper.link('-', null, {'repeat' : -1}, data.repeat > 0 && !data.playing ? null : 'disabled')}} - {{:data.repeat}} - {{:helper.link('+', null, {'repeat' : 1}, data.repeat < data.maxRepeat && !data.playing ? null : 'disabled')}} - {{:helper.link('+', null, {'repeat' : 10}, data.repeat < data.maxRepeat && !data.playing ? null : 'disabled')}} -
    -
    -
    Tempo:
    - {{:helper.link('-', null, {'tempo' : 10}, data.tempo < data.maxTempo ? null : 'disabled')}} - {{:helper.link('-', null, {'tempo' : 1}, data.tempo < data.maxTempo ? null : 'disabled')}} - {{:helper.round(600 / data.tempo)}} BPM - {{:helper.link('+', null, {'tempo' : -1}, data.tempo > data.minTempo ? null : 'disabled')}} - {{:helper.link('+', null, {'tempo' : -10}, data.tempo > data.minTempo ? null : 'disabled')}} -
    -
    - -

    Editor

    -
    - {{for data.lines}} -
    -
    {{:index + 1}}: 
    -
    - {{:helper.link('Edit', 'pencil', {'modifyline': index+1}, data.playing ? 'disabled' : null)}} - {{:helper.link('Insert', 'arrow-up', {'insertline': index+1}, data.playing ? 'disabled' : null)}} - {{:helper.link('Delete', 'trash', {'deleteline': index+1}, data.playing ? 'disabled' : null)}} -
    -
    {{:value}}
    -
    - {{/for}} -
    -
    - {{:helper.link('Add Line', 'arrow-down', {'insertline': data.lines.length + 1}, data.playing ? 'disabled' : null)}} -
    -
    -
    - -
    -
    - {{:helper.link('Help', data.help ? 'close' : 'question', {'help': 1})}} -
    -
    - {{if data.help}} -

    - Lines are a series of chords, separated by commas (,), each with notes seperated by hyphens (-). -
    - Every note in a chord will play together, with the chord timed by the tempo as defined above. -

    -

    - Notes are played by the names of the note, and optionally, the accidental, and/or the octave number. -
    - By default, every note is natural and in octave 3. Defining a different state for either is remembered for each note. -

      -
    • Example: C,D,E,F,G,A,B will play a C major scale.
    • -
    • After a note has an accidental or octave placed, it will be remembered: C,C4,C#,C3 is C3,C4,C4#,C3#
    • -
    -

    -

    - Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B.
    - A pause may be denoted by an empty chord: C,E,,C,G. -
    - To make a chord be a different time, end it with /x, where the chord length will be length defined by tempo / x, eg: C,G/2,E/4. -

    -

    - Combined, an example line is: E-E4/4,F#/2,G#/8,B/8,E3-E4/4. -

      -
    • Lines may be up to 50 characters.
    • -
    • A song may only contain up to 50 lines.
    • -
    -

    - {{/if}} -
    -
    \ No newline at end of file diff --git a/nano/templates/spawners_menu.tmpl b/nano/templates/spawners_menu.tmpl deleted file mode 100644 index c12490bbb60..00000000000 --- a/nano/templates/spawners_menu.tmpl +++ /dev/null @@ -1,20 +0,0 @@ -
    -

    Mob Spawners

    - - {{for data.spawners}} -
    -
    -

    {{:value.name}}

    -

    {{:value.amount_left}} spawners left.

    -
    -
    - {{:value.desc}} -
    -
    - {{:helper.link('Jump', null, {'action': 'jump', 'uid': value.uids}, null)}} - {{:helper.link('Spawn', null, {'action': 'spawn', 'uid': value.uids}, value.amount_left > 0 ? null : 'disabled')}} -
    -
    - {{/for}} -
    -
    diff --git a/nano/templates/supermatter_crystal.tmpl b/nano/templates/supermatter_crystal.tmpl deleted file mode 100644 index 40bc6796cd7..00000000000 --- a/nano/templates/supermatter_crystal.tmpl +++ /dev/null @@ -1,25 +0,0 @@ -{{if data.detonating}} -
    -

    CRYSTAL DELAMINATING

    -

    Evacuate area immediately

    -
    -
    -{{else}} -

    Crystal Integrity

    - {{:helper.displayBar(data.integrity_percentage, 0, 100, (data.integrity_percentage >= 90) ? 'good' : (data.integrity_percentage >= 25) ? 'average' : 'bad')}} - {{:data.integrity_percentage}} % -

    Environment

    - - Temperature: - - - {{:helper.displayBar(data.ambient_temp, 0, 10000, (data.ambient_temp >= 5000) ? 'bad' : (data.ambient_temp >= 4000) ? 'average' : 'good')}} - {{:data.ambient_temp}} K - - - Pressure: - - - {{:helper.smoothRound(data.ambient_pressure)}} kPa - -{{/if}} \ No newline at end of file diff --git a/nano/templates/supermatter_monitor.tmpl b/nano/templates/supermatter_monitor.tmpl deleted file mode 100644 index 3bc87984d79..00000000000 --- a/nano/templates/supermatter_monitor.tmpl +++ /dev/null @@ -1,111 +0,0 @@ -{{if data.active}} - {{:helper.link('Back to Menu', null, {'clear' : 1})}}
    -
    -
    - Core Integrity: -
    -
    - {{:helper.displayBar(data.SM_integrity, 0, 100, (data.SM_integrity == 100) ? 'good' : (data.SM_integrity >= 50) ? 'average' : 'bad')}} {{:data.SM_integrity}}% -
    -
    - Relative EER: -
    -
    - {{if data.SM_power > 300}} - {{:data.SM_power}} MeV/cm3 - {{else data.SM_power > 150}} - {{:data.SM_power}} MeV/cm3 - {{else}} - {{:data.SM_power}} MeV/cm3 - {{/if}} -
    -
    - Temperature: -
    -
    - {{if data.SM_ambienttemp > 5000}} - {{:data.SM_ambienttemp}} K - {{else data.SM_ambienttemp > 4000}} - {{:data.SM_ambienttemp}} K - {{else}} - {{:data.SM_ambienttemp}} K - {{/if}} -
    -
    - Pressure: -
    -
    - {{if data.SM_ambientpressure > 10000}} - {{:data.SM_ambientpressure}} kPa - {{else data.SM_ambientpressure > 5000}} - {{:data.SM_ambientpressure}} kPa - {{else}} - {{:data.SM_ambientpressure}} kPa - {{/if}} -
    -
    -

    -
    - Gas Composition: -
    -
    -
    -
    - O2: -
    -
    - {{:data.SM_gas_O2}} % -
    -
    - CO2: -
    -
    - {{:data.SM_gas_CO2}} % -
    -
    - N2: -
    -
    - {{:data.SM_gas_N2}} % -
    -
    - PL: -
    -
    - {{:data.SM_gas_PL}} % -
    -
    - OTHER: -
    -
    - {{:data.SM_gas_OTHER}} % -
    -
    -
    -
    -{{else}} - {{:helper.link('Refresh', null, {'refresh' : 1})}}
    - {{for data.supermatters}} -
    -
    - Area: -
    -
    - {{:value.area_name}} -
    -
    - Integrity: -
    -
    - {{:value.integrity}} % -
    -
    - Options: -
    -
    - {{:helper.link('View Details', null, {'set' : value.uid})}} -
    -
    - {{/for}} -{{/if}} diff --git a/nano/templates/supply_console.tmpl b/nano/templates/supply_console.tmpl deleted file mode 100644 index 3402e8982b5..00000000000 --- a/nano/templates/supply_console.tmpl +++ /dev/null @@ -1,130 +0,0 @@ - - - -
    -
    - Supply Points: -
    -
    - {{:data.points}} -
    -
    -
    -
    - Central Command messages: -
    -
    - {{:data.message}} -
    -
    -
    -
    - Shuttle Status: -
    -
    - {{if !data.moving && !data.at_station}} - Docked off-station. - {{else !data.moving && data.at_station}} - Docked at the station. - {{else data.moving}} -
    Shuttle is en route (ETA: {{:data.timeleft}} minute{{if data.timeleft != 1}}s{{/if}}).
    - {{/if}} -
    -
    -
    -
    - Shuttle Control: -
    -
    - {{:helper.link('Call Shuttle', 'arrow-circle-down', data.send, data.moving || data.at_station || !data.can_launch ? 'disabled' : '')}} - {{:helper.link('Return Shuttle', 'arrow-circle-up', data.send, data.moving || !data.at_station || !data.can_launch ? 'disabled' : '')}} -
    -
    -
    -
    - Supply Crates -
    -
    -
    -
    - {{if !data.contents}} - {{for data.supply_packs}} -
    -
    -
    {{:helper.link(value.name, null, value.command1)}}
    - {{:helper.link('#', null, value.command2, null)}} - {{:helper.link('C', null, value.command3, null)}}
    {{:value.cost}} points
    -
    -
    - {{/for}} - {{else}} - {{:helper.link('Return', 'arrow-left', {'contents' : 1})}} -
    - Contents of {{:data.contents_name}} -
    - {{:data.contents}} - {{/if}} -
    -
    -
    -
    -
    -
    - Categories -
    - {{for data.categories}} -
    - {{:helper.link(value.name, 'gear', {'last_viewed_group':value.category}, (data.last_viewed_group==value.category) ? 'linkOn' : '', 'category')}} -
    - {{/for}} -
    -
    -
    - Supply Orders -
    -
    -
    -
    - Requests -
    - {{for data.requests}} -
    - #{{:value.ordernum}} - {{:value.supply_type}} for {{:value.orderedby}} -
    - {{:value.comment}} -
    - {{:helper.link('APPROVE', null, value.command1, !data.canapprove ? 'disabled' : '')}} {{:helper.link('REJECT', null, value.command2, null)}} -
    - {{empty}} - No active requests. - {{/for}} -
    - Orders -
    - {{for data.orders}} -
    - #{{:value.ordernum}} - {{:value.supply_type}} for {{:value.orderedby}} -
    - {{empty}} - No confirmed orders. - {{/for}} -
    -
    -
    -
    \ No newline at end of file diff --git a/nano/templates/tank_dispenser.tmpl b/nano/templates/tank_dispenser.tmpl deleted file mode 100644 index a634a2dde8e..00000000000 --- a/nano/templates/tank_dispenser.tmpl +++ /dev/null @@ -1,7 +0,0 @@ -
    -
    Dispense:
    -
    - {{:helper.link('Plasma (' + helper.smoothRound(data.p_tanks) + ')', data.p_tanks ? 'square' : 'square-o', {'plasma' : 1}, data.p_tanks ? null : 'disabled')}} - {{:helper.link('Oxygen (' + helper.smoothRound(data.o_tanks) + ')', data.o_tanks ? 'square' : 'square-o', {'oxygen' : 1}, data.o_tanks ? null : 'disabled')}} -
    -
    \ No newline at end of file diff --git a/nano/templates/tanks.tmpl b/nano/templates/tanks.tmpl deleted file mode 100644 index 2cf12746f01..00000000000 --- a/nano/templates/tanks.tmpl +++ /dev/null @@ -1,47 +0,0 @@ -{{if data.maskConnected}} -
    This tank is connected to a mask.
    -{{else}} -
    This tank is NOT connected to a mask.
    -{{/if}} - -
    -
    - Tank Pressure: -
    -
    - {{:helper.displayBar(data.tankPressure, 0, 1013, (data.tankPressure > 200) ? 'good' : ((data.tankPressure > 100) ? 'average' : 'bad'))}} -
    - {{:helper.smoothRound(data.tankPressure)}} kPa -
    -
    -
    - -
     
    - -
    -
    - Mask Release Pressure: -
    -
    - {{:helper.displayBar(data.releasePressure, 0, data.maxReleasePressure, (data.releasePressure >= 23) ? null : ((data.releasePressure >= 17) ? 'average' : 'bad'))}} -
    - {{:helper.link('-', null, {'dist_p' : -10}, (data.releasePressure > 0) ? null : 'disabled')}} - {{:helper.link('-', null, {'dist_p' : -1}, (data.releasePressure > 0) ? null : 'disabled')}} -
     {{:data.releasePressure}} kPa 
    - {{:helper.link('+', null, {'dist_p' : 1}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('+', null, {'dist_p' : 10}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('Max', null, {'dist_p' : 'max'}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}} - {{:helper.link('Reset', null, {'dist_p' : 'reset'}, (data.releasePressure != data.defaultReleasePressure) ? null : 'disabled')}} -
    -
    -
    - -
    -
    - Mask Release Valve: -
    -
    - {{:helper.link('Open', 'unlock', {'stat' : 1}, (!data.maskConnected) ? 'disabled' : (data.valveOpen ? 'selected' : null))}}{{:helper.link('Close', 'lock', {'stat' : 1}, data.valveOpen ? null : 'selected')}} -
    -
    - diff --git a/nano/templates/tcomms_core.tmpl b/nano/templates/tcomms_core.tmpl deleted file mode 100644 index ee7bffd0ed0..00000000000 --- a/nano/templates/tcomms_core.tmpl +++ /dev/null @@ -1,189 +0,0 @@ -{{:helper.link('Device Configuration', 'wrench', {'tab' : "CONFIG"}, data.tab == "CONFIG" ? 'selected' : '')}} -{{:helper.link('Device Links', 'link', {'tab' : "LINKS"}, data.tab == "LINKS" ? 'selected' : '')}} -{{:helper.link('User Filtering', 'user-times', {'tab' : "FILTER"}, data.tab == "FILTER" ? 'selected' : '')}} - -{{if data.ion}} -
    -ERROR: An Ionspheric overload has occured. Please wait for the machine to reboot. This cannot be manually done. -
    -{{/if}} - -{{if data.tab == "CONFIG"}} -

    Core Configuration

    -

    Status

    -
    -
    - Machine Active: -
    -
    - {{:helper.link(data.active ? 'Enabled' : 'Disabled', 'power-off', {'toggle_active' : 1}, null, data.active ? 'selected' : '')}} -
    -
    -
    -
    - Sectors with Telecommunications Signal: -
    -
    - {{:data.sectors_available}} -
    -
    -

    Settings

    -
    -
    - Job Announcements: -
    -
    - {{:helper.link(data.nttc_toggle_jobs ? 'Enabled' : 'Disabled', 'clipboard', {'nttc_toggle_jobs' : 1}, null, data.nttc_toggle_jobs ? 'selected' : null)}} -
    -
    -
    -
    - Job Departmentalisation: -
    -
    - {{:helper.link(data.nttc_toggle_job_color ? 'Enabled' : 'Disabled', 'clipboard', {'nttc_toggle_job_color' : 1}, null, data.nttc_toggle_job_color ? 'selected' : null)}} -
    -
    -
    -
    - Name Departmentalisation: -
    -
    - {{:helper.link(data.nttc_toggle_name_color ? 'Enabled' : 'Disabled', 'users', {'nttc_toggle_name_color' : 1}, null, data.nttc_toggle_name_color ? 'selected' : null)}} -
    -
    -
    -
    - Command Amplification: -
    -
    - {{:helper.link(data.nttc_toggle_command_bold ? 'Enabled' : 'Disabled', 'volume-up', {'nttc_toggle_command_bold' : 1}, null, data.nttc_toggle_command_bold ? 'selected' : null)}} -
    -
    -

    Advanced

    -
    -
    - Job Announcement Format: -
    -
    - {{:helper.link(data.nttc_job_indicator_type ? data.nttc_job_indicator_type : 'Unset', 'pencil', {'nttc_job_indicator_type' : 1}, null, data.nttc_job_indicator_type ? 'selected' : null)}} -
    -
    -
    -
    - Language Conversion: -
    -
    - {{:helper.link(data.nttc_setting_language ? data.nttc_setting_language : 'Unset', 'globe', {'nttc_setting_language' : 1}, null, data.nttc_setting_language ? 'selected' : null)}} -
    -
    -
    -
    - Network ID: -
    -
    - {{:helper.link(data.network_id ? data.network_id : 'Unset', 'server', {'network_id' : 1}, null, data.network_id ? 'selected' : null)}} -
    -
    -

    Maintenance

    - {{:helper.link('Import Configuration', 'sign-in', {'import' : 1})}} - {{:helper.link('Export Configuration', 'sign-out', {'export' : 1})}} -{{else data.tab == "LINKS"}} - -

    Connected Devices

    -
    -
    - Link Password: -
    -
    - {{:helper.link(data.link_password ? data.link_password : 'Unset', 'lock', {'change_password' : 1}, null, data.link_password ? 'selected' : null)}} -
    -
    -
    - - - - - - - - - - - - - - - - - - - {{for data.entries}} - - - - - - - - {{/for}} - -
    Network AddressNetwork IDSectorStatusUnlink
    {{:value.addr}}{{:value.net_id}}{{:value.sector}}{{:value.status ? 'Active' : 'Offline'}}{{:helper.link('Unlink', 'unlink', {'unlink' : value.addr}, null, 'infoButton')}}
    -{{else data.tab == "FILTER"}} - -

    Filtering List

    -
    -
    - {{:helper.link('Add User', 'plus', {'add_filter': 1})}} -
    -
    -
    - - - - - - - - - - - - - {{for data.filtered_users}} - - - - - {{/for}} - -
    NameRemove
    {{:value}}{{:helper.link('Remove', 'times', {'remove_filter' : value}, null, 'infoButton')}}
    -{{/if}} diff --git a/nano/templates/tcomms_relay.tmpl b/nano/templates/tcomms_relay.tmpl deleted file mode 100644 index ae549eff78f..00000000000 --- a/nano/templates/tcomms_relay.tmpl +++ /dev/null @@ -1,107 +0,0 @@ -

    Relay Configuration

    -
    -
    - Device Active: -
    -
    - {{:helper.link(data.active ? 'Enabled' : 'Disabled', 'power-off', {'toggle_active' : 1}, null, data.active ? 'selected' : '')}} -
    -
    -
    -
    - Network ID: -
    -
    - {{:helper.link(data.network_id ? data.network_id : 'Unset', 'pencil', {'network_id' : 1}, null, data.network_id ? 'selected' : null)}} -
    -
    -
    -
    - Link Status: -
    -
    - {{if data.linked}} - Linked - {{else}} - Unlinked - {{/if}} -
    -
    - -{{if data.linked}} -
    -
    - Linked Core ID: -
    -
    - {{:data.linked_core_id}} -
    -
    -
    -
    - Linked Core Address: -
    -
    - {{:data.linked_core_addr}} -
    -
    -
    -
    - Hidden Link: -
    -
    - {{:helper.link(data.hidden_link ? 'Yes' : 'No', data.hidden_link ? 'eye-slash' : 'eye', {'toggle_hidden_link' : 1}, null, data.hidden_link ? 'selected' : null)}} -
    -
    -
    -
    - Unlink: -
    -
    - {{:helper.link('Unlink', 'unlink', {'unlink' : 1}, null, 'redButton')}} -
    -
    -{{else}} - -

    Please select a Core to link to

    - - - - - - - - - - - - - - - - - {{for data.entries}} - - - - - - - {{/for}} - -
    Network AddressNetwork IDSectorLink
    {{:value.addr}}{{:value.net_id}}{{:value.sector}}{{:helper.link('Link', 'link', {'link' : value.addr}, null, 'infoButton')}}
    -{{/if}} diff --git a/nano/templates/teleporter_console.tmpl b/nano/templates/teleporter_console.tmpl deleted file mode 100644 index 026917eb8ef..00000000000 --- a/nano/templates/teleporter_console.tmpl +++ /dev/null @@ -1,36 +0,0 @@ - -

    Teleporter Status

    -{{if !data.powerstation}} -
    No power station linked.
    -{{else !data.teleporterhub}} -
    No hub linked.
    -{{else}} -
    -
    -
    Current Regime
    -
    {{:data.regime}}
    -
    -
    -
    Current Target
    -
    {{:data.target}}{{if data.target != "None"}}{{if data.regime == "Gate"}} Teleporter{{/if}}{{/if}}
    -
    -
    -
    Calibration
    -
    {{if data.calibrating}}In Progress{{else data.calibrated}}Optimal{{else data.accurate > 2}}Optimal{{else}}Sub-Optimal{{/if}}
    -
    -
    -
    -
    {{:helper.link('Change regime', 'gear', {'regimeset': 1})}}
    -
    {{:helper.link('Set target', 'gear', {'settarget': 1})}}
    -
    -
    -
    {{:helper.link('Get target from memory', 'arrow-circle-down', {'lock': 1}, data.locked ? '' : 'disabled')}}
    -
    {{:helper.link('Eject GPS device', 'eject', {'eject': 1}, data.locked ? '' : 'disabled')}}
    -
    -
    -
    {{:helper.link('Calibrate hub', 'arrows-alt', {'calibrate': 1}, data.target == 'None' ? 'disabled' : '')}}
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/transfer_valve.tmpl b/nano/templates/transfer_valve.tmpl deleted file mode 100644 index be64b5cc6f4..00000000000 --- a/nano/templates/transfer_valve.tmpl +++ /dev/null @@ -1,56 +0,0 @@ -
    -
    - Attachment One: -
    -
    - {{if data.attachmentOne}} - {{:data.attachmentOne}} - {{else}} - None - {{/if}} - {{:helper.link('Remove', 'eject', {'tankone' : 1}, data.attachmentOne ? null : 'disabled')}} -
    -
    - -
    -
    - Attachment Two: -
    -
    - {{if data.attachmentTwo}} - {{:data.attachmentTwo}} - {{else}} - None - {{/if}} - {{:helper.link('Remove', 'eject', {'tanktwo' : 1}, data.attachmentTwo ? null : 'disabled')}} -
    -
    - -
    -
    - Valve Attachment: -
    -
    - {{if data.valveAttachment}} - {{:data.valveAttachment}} - {{else}} - None - {{/if}} - {{:helper.link('Remove', 'eject', {'rem_device' : 1}, data.valveAttachment ? null : 'disabled')}} - {{if data.valveAttachment}} - {{:helper.link('View', 'wrench', {'device' : 1})}} - {{/if}} -
    -
    - -
     
    - -
    -
    - Valve Status: -
    -
    - {{:helper.link('Open', 'unlock', {'open' : 1}, (!data.attachmentOne || !data.attachmentTwo) ? 'disabled' : (data.valveOpen ? 'selected' : null))}}{{:helper.link('Close', 'lock', {'open' : 1}, (!data.attachmentOne || !data.attachmentTwo) ? 'disabled' : (data.valveOpen ? null : 'selected'))}} -
    -
    - diff --git a/nano/templates/turret_control.tmpl b/nano/templates/turret_control.tmpl deleted file mode 100644 index 50f733ea80f..00000000000 --- a/nano/templates/turret_control.tmpl +++ /dev/null @@ -1,72 +0,0 @@ -
    - Behaviour controls are {{:data.locked ? "locked" : "unlocked"}}. -
    -
    -{{if data.access}} - {{if !data.screen}} -
    -
    - Turret Status: -
    -
    - {{:helper.link('Enabled', null, {'command' : 'enable', 'value' : 1}, data.enabled ?'redButton' : null)}} - {{:helper.link('Disabled',null, {'command' : 'enable', 'value' : 0}, !data.enabled ? 'selected' : null)}} -
    -
    - {{if data.accesses}} -
    -
    - Required Access: -
    -
    - {{:helper.link('Modify', null, {'command' : 'screen', 'value' : 1})}} -
    -
    - {{/if}} - {{if data.lethal_control}} -
    -
    - Lethal Mode: -
    -
    - {{:helper.link('On', null, {'command' : 'lethal', 'value' : 1}, data.lethal ?'redButton' : null)}} - {{:helper.link('Off',null, {'command' : 'lethal', 'value' : 0}, !data.lethal ? 'selected' : null)}} -
    -
    - {{/if}} - - {{for data.settings}} -
    -
    - {{:value.category}} -
    -
    - {{:helper.link('On', null, {'command' : value.setting, 'value' : 1}, value.value ? 'selected' : null)}} - {{:helper.link('Off',null, {'command' : value.setting, 'value' : 0}, !value.value ? 'selected' : null)}} -
    -
    - {{/for}} - {{else}} -
    -
    - Turret Settings: -
    -
    - {{:helper.link('Modify', null, {'command' : 'screen', 'value' : 0})}} -
    -
    -
    -
    - Access Type: -
    -
    - {{:helper.link('All', null, {'one_access' : 0}, !data.one_access ? 'selected' : null)}} - {{:helper.link('One', null, {'one_access' : 1}, data.one_access ? 'selected' : null)}} -
    -
    -

    Select Access

    - {{for data.accesses}} - {{:helper.link(value.name, null, {'access' : value.number}, null, value.active ? 'selected' : null)}} - {{/for}} - {{/if}} -{{/if}} diff --git a/nano/templates/uplink.tmpl b/nano/templates/uplink.tmpl deleted file mode 100644 index 3058bb10bbb..00000000000 --- a/nano/templates/uplink.tmpl +++ /dev/null @@ -1,101 +0,0 @@ - - -{{:helper.syndicateMode()}} - -

    {{:data.welcome}}

    -

    Uplink Functions

    -
    -
    - {{:helper.link('Purchase Gear', 'gear', {'menu' : 0, 'category' : null}, null, 'white')}} - {{:helper.link('Exploitable Information', 'gear', {'menu' : 1}, null, 'white')}} - {{:helper.link('Lock Uplink', 'power-off', {'lock' : "1"}, null, 'white')}} -
    -
    - -{{if data.menu == 0}} -

    Purchase Gear

    - Each item costs a number of tele-crystals as indicated by the number following their name. -
    -
    - Tele-Crystals: -
    -
    - {{:data.crystals}} -
    -
    -
    - - {{for data.nano_items}} - {{if value.Category == data.category_choice}} -
    - {{:helper.link ( value.Category, 'arrow-circle-down', {'menu' : 0, 'category' : null}, null, 'white')}} -
    - {{else}} -
    - {{:helper.link ( value.Category, 'arrow-circle-right', {'menu' : 0, 'category' : value.Category}, null, 'white')}} -
    - {{/if}} - {{if value.Category == data.category_choice}} -
    - - {{for value.items :itemValue:itemIndex}} - - - - {{/for}} -
    - {{:helper.link( itemValue.Name, 'gear', {'buy_item' : itemValue.obj_path, 'cost' : itemValue.Cost}, itemValue.Cost > data.crystals ? 'disabled' : null, null)}} - {{:itemValue.Cost}} {{if itemValue.hijack_only}}(HIJACK AGENTS ONLY){{/if}} -
    -
    - {{/if}} - {{/for}} -
    -
    - {{:helper.link('Buy Random Item' , 'gear', {'buy_item' : 'random'}, data.crystals <= 0 ? 'disabled' : null, 'white')}} -
    -
    - {{:helper.link('Refund item in active hand' , 'gear', {refund : 1}, null, 'white')}} -
    - -{{else data.menu == 1}} -

    Information Record List:

    -
    -
    - Select a Record -
    -
    - {{for data.exploit_records}} -
    - {{:helper.link(value.Name, 'gear', {'menu' : 11, 'id' : value.id}, null, null)}} -
    - {{/for}} - -{{else data.menu == 11}} -

    Information Record:

    -
    -
    -
    -
    - {{if data.exploit_exists == 1}} - Name: {{:data.exploit.name}}
    - Sex: {{:data.exploit.sex}}
    - Species: {{:data.exploit.species}}
    - Age: {{:data.exploit.age}}
    - Rank: {{:data.exploit.rank}}
    - Fingerprint: {{:data.exploit.fingerprint}}
    - {{else}} - - No exploitative information acquired! -
    -
    -
    - {{/if}} -
    -
    -
    -{{/if}} diff --git a/nano/templates/vending_machine.tmpl b/nano/templates/vending_machine.tmpl deleted file mode 100644 index 1849e3e51d5..00000000000 --- a/nano/templates/vending_machine.tmpl +++ /dev/null @@ -1,68 +0,0 @@ - - -{{if data.mode == 0}} -

    Items available

    -
    - {{for data.products}} -
    -
    - {{if value.price > 0}} - {{:helper.link('Buy (' + value.price + ')', 'shopping-cart', { "vend" : value.key }, value.amount > 0 ? null : 'disabled')}} - {{else}} - {{:helper.link('Vend', 'arrow-circle-down', { "vend" : value.key }, value.amount > 0 ? null : 'disabled')}} - {{/if}} -
    -
    - {{if value.color}}{{:value.name}} - {{else}}{{:value.name}} - {{/if}} - ({{:value.amount ? value.amount : "NONE LEFT"}}) -
    -
    - {{empty}} - No items available! - {{/for}} -
    -{{if data.coin}} -

    Coin

    -
    -
    Coin deposited:
    -
    {{:helper.link(data.coin, 'eject', {'remove_coin' : 1})}}
    -
    -{{/if}} -{{if data.item_slot == 1}} -

    Item Slot

    -
    -
    Item Slot:
    - {{if data.inserted_item}} -
    {{:helper.link(data.inserted_item, 'eject', {'remove_item' : 1})}}
    - {{else}} -
    Empty
    - {{/if}} -
    -{{/if}} -{{else data.mode == 1}} -

    Item selected

    -
    -
    -
    Item selected:
    {{:data.product}}
    -
    Charge:
    {{:data.price}}
    -
    -
    - {{if data.message_err}} {{/if}} {{:data.message}} -
    -
    - {{:helper.link('Pay', 'shopping-cart', {'pay' : 1})}} - {{:helper.link('Cancel', 'arrow-left', {'cancelpurchase' : 1})}} -
    -
    -{{/if}} -{{if data.panel}} -

    Maintenance panel

    -
    -
    Speaker
    {{:helper.link(data.speaker ? 'Enabled' : 'Disabled', 'gear', {'togglevoice' : 1})}}
    -
    -{{/if}} \ No newline at end of file diff --git a/nano/templates/wires.tmpl b/nano/templates/wires.tmpl deleted file mode 100644 index 2702d97d7dc..00000000000 --- a/nano/templates/wires.tmpl +++ /dev/null @@ -1,24 +0,0 @@ -
    - - {{for data.wires}} - - - - - {{/for}} -
    {{:value.colour_name}} {{if value.index}}({{:value.index}}){{/if}} - {{:helper.link(value.cut ? 'Mend' : 'Cut', null, {'action' : 'cut', 'wire' : value.colour})}} - {{:helper.link('Pulse', null, {'action' : 'pulse', 'wire' : value.colour})}} - {{:helper.link(value.attached ? 'Detach' : 'Attach', null, {'action' : 'attach', 'wire' : value.colour})}} -
    -
    -{{if data.status_len}} -
    -
    -
    - {{for data.status}} -
    {{:value}}
    - {{/for}} -
    -
    -{{/if}} diff --git a/paradise.dme b/paradise.dme index ca3ce303ce9..b1823e23749 100644 --- a/paradise.dme +++ b/paradise.dme @@ -40,6 +40,7 @@ #include "code\__DEFINES\genetics.dm" #include "code\__DEFINES\hud.dm" #include "code\__DEFINES\hydroponics.dm" +#include "code\__DEFINES\instruments.dm" #include "code\__DEFINES\inventory.dm" #include "code\__DEFINES\is_helpers.dm" #include "code\__DEFINES\job.dm" @@ -49,6 +50,7 @@ #include "code\__DEFINES\lighting.dm" #include "code\__DEFINES\logs.dm" #include "code\__DEFINES\machines.dm" +#include "code\__DEFINES\martial_arts.dm" #include "code\__DEFINES\math.dm" #include "code\__DEFINES\MC.dm" #include "code\__DEFINES\mecha.dm" @@ -56,6 +58,7 @@ #include "code\__DEFINES\misc.dm" #include "code\__DEFINES\mobs.dm" #include "code\__DEFINES\move_force.dm" +#include "code\__DEFINES\muzzle_flash.dm" #include "code\__DEFINES\pda.dm" #include "code\__DEFINES\pipes.dm" #include "code\__DEFINES\preferences.dm" @@ -71,9 +74,11 @@ #include "code\__DEFINES\station_goals.dm" #include "code\__DEFINES\status_effects.dm" #include "code\__DEFINES\subsystems.dm" +#include "code\__DEFINES\tgui.dm" #include "code\__DEFINES\tools.dm" #include "code\__DEFINES\typeids.dm" #include "code\__DEFINES\vv.dm" +#include "code\__DEFINES\wires.dm" #include "code\__DEFINES\zlevel.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" @@ -87,6 +92,7 @@ #include "code\__HELPERS\files.dm" #include "code\__HELPERS\game.dm" #include "code\__HELPERS\global_lists.dm" +#include "code\__HELPERS\heap.dm" #include "code\__HELPERS\icon_smoothing.dm" #include "code\__HELPERS\icons.dm" #include "code\__HELPERS\lists.dm" @@ -108,8 +114,6 @@ #include "code\__HELPERS\sorts\InsertSort.dm" #include "code\__HELPERS\sorts\MergeSort.dm" #include "code\__HELPERS\sorts\TimSort.dm" -#include "code\_DATASTRUCTURES\heap.dm" -#include "code\_DATASTRUCTURES\stacks.dm" #include "code\_globalvars\configuration.dm" #include "code\_globalvars\game_modes.dm" #include "code\_globalvars\genetics.dm" @@ -136,7 +140,6 @@ #include "code\_onclick\observer.dm" #include "code\_onclick\other_mobs.dm" #include "code\_onclick\overmind.dm" -#include "code\_onclick\rig.dm" #include "code\_onclick\telekinesis.dm" #include "code\_onclick\hud\_defines.dm" #include "code\_onclick\hud\action_button.dm" @@ -153,6 +156,7 @@ #include "code\_onclick\hud\guardian.dm" #include "code\_onclick\hud\hud.dm" #include "code\_onclick\hud\human.dm" +#include "code\_onclick\hud\map_popups.dm" #include "code\_onclick\hud\movable_screen_objects.dm" #include "code\_onclick\hud\other_mobs.dm" #include "code\_onclick\hud\parallax.dm" @@ -173,10 +177,6 @@ #include "code\ATMOSPHERICS\components\binary_devices\pump.dm" #include "code\ATMOSPHERICS\components\binary_devices\valve.dm" #include "code\ATMOSPHERICS\components\binary_devices\volume_pump.dm" -#include "code\ATMOSPHERICS\components\omni_devices\_omni_extras.dm" -#include "code\ATMOSPHERICS\components\omni_devices\filter.dm" -#include "code\ATMOSPHERICS\components\omni_devices\mixer.dm" -#include "code\ATMOSPHERICS\components\omni_devices\omni_base.dm" #include "code\ATMOSPHERICS\components\trinary_devices\filter.dm" #include "code\ATMOSPHERICS\components\trinary_devices\mixer.dm" #include "code\ATMOSPHERICS\components\trinary_devices\trinary_base.dm" @@ -217,11 +217,11 @@ #include "code\controllers\subsystem\assets.dm" #include "code\controllers\subsystem\atoms.dm" #include "code\controllers\subsystem\changelog.dm" -#include "code\controllers\subsystem\chat.dm" -#include "code\controllers\subsystem\dcs.dm" +#include "code\controllers\subsystem\cleanup.dm" #include "code\controllers\subsystem\events.dm" #include "code\controllers\subsystem\fires.dm" #include "code\controllers\subsystem\garbage.dm" +#include "code\controllers\subsystem\ghost_spawns.dm" #include "code\controllers\subsystem\holiday.dm" #include "code\controllers\subsystem\icon_smooth.dm" #include "code\controllers\subsystem\idlenpcpool.dm" @@ -241,6 +241,7 @@ #include "code\controllers\subsystem\parallax.dm" #include "code\controllers\subsystem\radio.dm" #include "code\controllers\subsystem\shuttles.dm" +#include "code\controllers\subsystem\sounds.dm" #include "code\controllers\subsystem\spacedrift.dm" #include "code\controllers\subsystem\statistics.dm" #include "code\controllers\subsystem\sun.dm" @@ -251,7 +252,9 @@ #include "code\controllers\subsystem\titlescreen.dm" #include "code\controllers\subsystem\vote.dm" #include "code\controllers\subsystem\weather.dm" +#include "code\controllers\subsystem\processing\dcs.dm" #include "code\controllers\subsystem\processing\fastprocess.dm" +#include "code\controllers\subsystem\processing\instruments.dm" #include "code\controllers\subsystem\processing\obj.dm" #include "code\controllers\subsystem\processing\processing.dm" #include "code\controllers\subsystem\tickets\mentor_tickets.dm" @@ -273,6 +276,7 @@ #include "code\datums\hud.dm" #include "code\datums\log_record.dm" #include "code\datums\log_viewer.dm" +#include "code\datums\logging.dm" #include "code\datums\mind.dm" #include "code\datums\mixed.dm" #include "code\datums\mutable_appearance.dm" @@ -303,8 +307,10 @@ #include "code\datums\components\label.dm" #include "code\datums\components\material_container.dm" #include "code\datums\components\paintable.dm" +#include "code\datums\components\proximity_monitor.dm" #include "code\datums\components\slippery.dm" #include "code\datums\components\spawner.dm" +#include "code\datums\components\spooky.dm" #include "code\datums\components\squeak.dm" #include "code\datums\components\swarming.dm" #include "code\datums\diseases\_disease.dm" @@ -569,6 +575,7 @@ #include "code\game\gamemodes\miniantags\bot_swarm\swarmer.dm" #include "code\game\gamemodes\miniantags\bot_swarm\swarmer_event.dm" #include "code\game\gamemodes\miniantags\guardian\guardian.dm" +#include "code\game\gamemodes\miniantags\guardian\host_actions.dm" #include "code\game\gamemodes\miniantags\guardian\types\assassin.dm" #include "code\game\gamemodes\miniantags\guardian\types\bomb.dm" #include "code\game\gamemodes\miniantags\guardian\types\charger.dm" @@ -684,7 +691,6 @@ #include "code\game\machinery\syndicatebomb.dm" #include "code\game\machinery\teleporter.dm" #include "code\game\machinery\transformer.dm" -#include "code\game\machinery\turntable.dm" #include "code\game\machinery\turret_control.dm" #include "code\game\machinery\vending.dm" #include "code\game\machinery\washing_machine.dm" @@ -729,7 +735,7 @@ #include "code\game\machinery\computer\robot.dm" #include "code\game\machinery\computer\salvage_ship.dm" #include "code\game\machinery\computer\security.dm" -#include "code\game\machinery\computer\skills.dm" +#include "code\game\machinery\computer\sm_monitor.dm" #include "code\game\machinery\computer\specops_shuttle.dm" #include "code\game\machinery\computer\station_alert.dm" #include "code\game\machinery\computer\store.dm" @@ -848,6 +854,7 @@ #include "code\game\objects\effects\temporary_visuals\clockcult.dm" #include "code\game\objects\effects\temporary_visuals\cult.dm" #include "code\game\objects\effects\temporary_visuals\miscellaneous.dm" +#include "code\game\objects\effects\temporary_visuals\muzzle_flashes.dm" #include "code\game\objects\effects\temporary_visuals\temporary_visual.dm" #include "code\game\objects\items\ashtray.dm" #include "code\game\objects\items\blueprints.dm" @@ -879,7 +886,6 @@ #include "code\game\objects\items\devices\flashlight.dm" #include "code\game\objects\items\devices\floor_painter.dm" #include "code\game\objects\items\devices\handheld_defib.dm" -#include "code\game\objects\items\devices\instruments.dm" #include "code\game\objects\items\devices\laserpointer.dm" #include "code\game\objects\items\devices\lightreplacer.dm" #include "code\game\objects\items\devices\machineprototype.dm" @@ -940,6 +946,7 @@ #include "code\game\objects\items\tools\wrench.dm" #include "code\game\objects\items\weapons\AI_modules.dm" #include "code\game\objects\items\weapons\alien_specific.dm" +#include "code\game\objects\items\weapons\batons.dm" #include "code\game\objects\items\weapons\bee_briefcase.dm" #include "code\game\objects\items\weapons\cards_ids.dm" #include "code\game\objects\items\weapons\cash.dm" @@ -988,7 +995,6 @@ #include "code\game\objects\items\weapons\staff.dm" #include "code\game\objects\items\weapons\stock_parts.dm" #include "code\game\objects\items\weapons\stunbaton.dm" -#include "code\game\objects\items\weapons\swords_axes_etc.dm" #include "code\game\objects\items\weapons\tape.dm" #include "code\game\objects\items\weapons\teleportation.dm" #include "code\game\objects\items\weapons\teleprod.dm" @@ -1084,7 +1090,6 @@ #include "code\game\objects\structures\misc.dm" #include "code\game\objects\structures\mop_bucket.dm" #include "code\game\objects\structures\morgue.dm" -#include "code\game\objects\structures\musician.dm" #include "code\game\objects\structures\noticeboard.dm" #include "code\game\objects\structures\plasticflaps.dm" #include "code\game\objects\structures\reflector.dm" @@ -1197,7 +1202,6 @@ #include "code\modules\admin\sql_notes.dm" #include "code\modules\admin\stickyban.dm" #include "code\modules\admin\topic.dm" -#include "code\modules\admin\ToRban.dm" #include "code\modules\admin\watchlist.dm" #include "code\modules\admin\DB ban\functions.dm" #include "code\modules\admin\permissionverbs\permissionedit.dm" @@ -1207,8 +1211,8 @@ #include "code\modules\admin\verbs\adminjump.dm" #include "code\modules\admin\verbs\adminpm.dm" #include "code\modules\admin\verbs\adminsay.dm" -#include "code\modules\admin\verbs\alt_check.dm" #include "code\modules\admin\verbs\antag-ooc.dm" +#include "code\modules\admin\verbs\asays.dm" #include "code\modules\admin\verbs\atmosdebug.dm" #include "code\modules\admin\verbs\BrokenInhands.dm" #include "code\modules\admin\verbs\cinematic.dm" @@ -1236,7 +1240,6 @@ #include "code\modules\admin\verbs\randomverbs.dm" #include "code\modules\admin\verbs\serialization.dm" #include "code\modules\admin\verbs\space_transitions.dm" -#include "code\modules\admin\verbs\spawnfloorcluwne.dm" #include "code\modules\admin\verbs\striketeam.dm" #include "code\modules\admin\verbs\striketeam_syndicate.dm" #include "code\modules\admin\verbs\ticklag.dm" @@ -1246,14 +1249,6 @@ #include "code\modules\admin\verbs\SDQL2\SDQL_2.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm" #include "code\modules\admin\verbs\SDQL2\useful_procs.dm" -#include "code\modules\alarm\alarm.dm" -#include "code\modules\alarm\alarm_handler.dm" -#include "code\modules\alarm\atmosphere_alarm.dm" -#include "code\modules\alarm\burglar_alarm.dm" -#include "code\modules\alarm\camera_alarm.dm" -#include "code\modules\alarm\fire_alarm.dm" -#include "code\modules\alarm\motion_alarm.dm" -#include "code\modules\alarm\power_alarm.dm" #include "code\modules\antagonists\_common\antag_datum.dm" #include "code\modules\antagonists\_common\antag_helpers.dm" #include "code\modules\antagonists\_common\antag_hud.dm" @@ -1329,9 +1324,6 @@ #include "code\modules\buildmode\submodes\save.dm" #include "code\modules\buildmode\submodes\throwing.dm" #include "code\modules\buildmode\submodes\variable_edit.dm" -#include "code\modules\busy_space\air_traffic.dm" -#include "code\modules\busy_space\loremaster.dm" -#include "code\modules\busy_space\organizations.dm" #include "code\modules\client\asset_cache.dm" #include "code\modules\client\client defines.dm" #include "code\modules\client\client procs.dm" @@ -1381,7 +1373,6 @@ #include "code\modules\clothing\shoes\magboots.dm" #include "code\modules\clothing\shoes\miscellaneous.dm" #include "code\modules\clothing\spacesuits\alien.dm" -#include "code\modules\clothing\spacesuits\breaches.dm" #include "code\modules\clothing\spacesuits\chronosuit.dm" #include "code\modules\clothing\spacesuits\ert.dm" #include "code\modules\clothing\spacesuits\hardsuit.dm" @@ -1389,24 +1380,6 @@ #include "code\modules\clothing\spacesuits\plasmamen.dm" #include "code\modules\clothing\spacesuits\syndi.dm" #include "code\modules\clothing\spacesuits\void.dm" -#include "code\modules\clothing\spacesuits\rig\rig.dm" -#include "code\modules\clothing\spacesuits\rig\rig_armormod.dm" -#include "code\modules\clothing\spacesuits\rig\rig_attackby.dm" -#include "code\modules\clothing\spacesuits\rig\rig_pieces.dm" -#include "code\modules\clothing\spacesuits\rig\rig_verbs.dm" -#include "code\modules\clothing\spacesuits\rig\rig_wiring.dm" -#include "code\modules\clothing\spacesuits\rig\modules\combat.dm" -#include "code\modules\clothing\spacesuits\rig\modules\computer.dm" -#include "code\modules\clothing\spacesuits\rig\modules\modules.dm" -#include "code\modules\clothing\spacesuits\rig\modules\ninja.dm" -#include "code\modules\clothing\spacesuits\rig\modules\utility.dm" -#include "code\modules\clothing\spacesuits\rig\modules\vision.dm" -#include "code\modules\clothing\spacesuits\rig\suits\alien.dm" -#include "code\modules\clothing\spacesuits\rig\suits\combat.dm" -#include "code\modules\clothing\spacesuits\rig\suits\ert_suits.dm" -#include "code\modules\clothing\spacesuits\rig\suits\light.dm" -#include "code\modules\clothing\spacesuits\rig\suits\merc.dm" -#include "code\modules\clothing\spacesuits\rig\suits\station.dm" #include "code\modules\clothing\suits\alien.dm" #include "code\modules\clothing\suits\armor.dm" #include "code\modules\clothing\suits\bio.dm" @@ -1465,6 +1438,7 @@ #include "code\modules\events\abductor.dm" #include "code\modules\events\alien_infestation.dm" #include "code\modules\events\anomaly.dm" +#include "code\modules\events\anomaly_atmos.dm" #include "code\modules\events\anomaly_bluespace.dm" #include "code\modules\events\anomaly_flux.dm" #include "code\modules\events\anomaly_grav.dm" @@ -1636,6 +1610,25 @@ #include "code\modules\hydroponics\grown\tobacco.dm" #include "code\modules\hydroponics\grown\tomato.dm" #include "code\modules\hydroponics\grown\towercap.dm" +#include "code\modules\instruments\_instrument_data.dm" +#include "code\modules\instruments\_instrument_key.dm" +#include "code\modules\instruments\brass.dm" +#include "code\modules\instruments\chromatic_percussion.dm" +#include "code\modules\instruments\fun.dm" +#include "code\modules\instruments\guitar.dm" +#include "code\modules\instruments\hardcoded.dm" +#include "code\modules\instruments\organ.dm" +#include "code\modules\instruments\piano.dm" +#include "code\modules\instruments\synth_tones.dm" +#include "code\modules\instruments\objs\items\_instrument.dm" +#include "code\modules\instruments\objs\items\headphones.dm" +#include "code\modules\instruments\objs\items\instruments.dm" +#include "code\modules\instruments\objs\structures\_musician.dm" +#include "code\modules\instruments\objs\structures\piano.dm" +#include "code\modules\instruments\songs\_song.dm" +#include "code\modules\instruments\songs\_song_ui.dm" +#include "code\modules\instruments\songs\play_legacy.dm" +#include "code\modules\instruments\songs\play_synthesized.dm" #include "code\modules\karma\karma.dm" #include "code\modules\keybindings\bindings_admin.dm" #include "code\modules\keybindings\bindings_ai.dm" @@ -1677,7 +1670,28 @@ #include "code\modules\martial_arts\mimejutsu.dm" #include "code\modules\martial_arts\plasma_fist.dm" #include "code\modules\martial_arts\sleeping_carp.dm" -#include "code\modules\martial_arts\wrestleing.dm" +#include "code\modules\martial_arts\wrestling.dm" +#include "code\modules\martial_arts\combos\martial_combo.dm" +#include "code\modules\martial_arts\combos\adminfu\healing_palm.dm" +#include "code\modules\martial_arts\combos\cqc\consecutive.dm" +#include "code\modules\martial_arts\combos\cqc\kick.dm" +#include "code\modules\martial_arts\combos\cqc\pressure.dm" +#include "code\modules\martial_arts\combos\cqc\restrain.dm" +#include "code\modules\martial_arts\combos\cqc\slam.dm" +#include "code\modules\martial_arts\combos\krav_maga\leg_sweep.dm" +#include "code\modules\martial_arts\combos\krav_maga\lung_punch.dm" +#include "code\modules\martial_arts\combos\krav_maga\neck_chop.dm" +#include "code\modules\martial_arts\combos\mimejutsu\mimechucks.dm" +#include "code\modules\martial_arts\combos\mimejutsu\silent_palm.dm" +#include "code\modules\martial_arts\combos\mimejutsu\smokebomb.dm" +#include "code\modules\martial_arts\combos\plasma_fist\plasma_fist.dm" +#include "code\modules\martial_arts\combos\plasma_fist\throwback.dm" +#include "code\modules\martial_arts\combos\plasma_fist\tornado_sweep.dm" +#include "code\modules\martial_arts\combos\sleeping_carp\back_kick.dm" +#include "code\modules\martial_arts\combos\sleeping_carp\elbow_drop.dm" +#include "code\modules\martial_arts\combos\sleeping_carp\head_kick.dm" +#include "code\modules\martial_arts\combos\sleeping_carp\stomach_knee.dm" +#include "code\modules\martial_arts\combos\sleeping_carp\wrist_wrench.dm" #include "code\modules\mining\abandonedcrates.dm" #include "code\modules\mining\fulton.dm" #include "code\modules\mining\machine_processing.dm" @@ -2071,51 +2085,6 @@ #include "code\modules\mob\new_player\sprite_accessories\vulpkanin\vulpkanin_head_markings.dm" #include "code\modules\mob\new_player\sprite_accessories\vulpkanin\vulpkanin_tail_markings.dm" #include "code\modules\mob\new_player\sprite_accessories\wryn\wryn_face.dm" -#include "code\modules\modular_computers\laptop_vendor.dm" -#include "code\modules\modular_computers\computers\item\computer.dm" -#include "code\modules\modular_computers\computers\item\computer_components.dm" -#include "code\modules\modular_computers\computers\item\computer_damage.dm" -#include "code\modules\modular_computers\computers\item\computer_power.dm" -#include "code\modules\modular_computers\computers\item\computer_ui.dm" -#include "code\modules\modular_computers\computers\item\laptop.dm" -#include "code\modules\modular_computers\computers\item\laptop_presets.dm" -#include "code\modules\modular_computers\computers\item\processor.dm" -#include "code\modules\modular_computers\computers\item\tablet.dm" -#include "code\modules\modular_computers\computers\item\tablet_presets.dm" -#include "code\modules\modular_computers\computers\machinery\console_presets.dm" -#include "code\modules\modular_computers\computers\machinery\modular_computer.dm" -#include "code\modules\modular_computers\computers\machinery\modular_console.dm" -#include "code\modules\modular_computers\file_system\computer_file.dm" -#include "code\modules\modular_computers\file_system\data.dm" -#include "code\modules\modular_computers\file_system\program.dm" -#include "code\modules\modular_computers\file_system\program_events.dm" -#include "code\modules\modular_computers\file_system\programs\antagonist\dos.dm" -#include "code\modules\modular_computers\file_system\programs\antagonist\revelation.dm" -#include "code\modules\modular_computers\file_system\programs\command\card.dm" -#include "code\modules\modular_computers\file_system\programs\command\comms.dm" -#include "code\modules\modular_computers\file_system\programs\engineering\alarm.dm" -#include "code\modules\modular_computers\file_system\programs\engineering\power_monitor.dm" -#include "code\modules\modular_computers\file_system\programs\engineering\sm_monitor.dm" -#include "code\modules\modular_computers\file_system\programs\generic\configurator.dm" -#include "code\modules\modular_computers\file_system\programs\generic\file_browser.dm" -#include "code\modules\modular_computers\file_system\programs\generic\ntdownloader.dm" -#include "code\modules\modular_computers\file_system\programs\generic\ntnrc_client.dm" -#include "code\modules\modular_computers\file_system\programs\generic\nttransfer.dm" -#include "code\modules\modular_computers\file_system\programs\research\airestorer.dm" -#include "code\modules\modular_computers\file_system\programs\research\ntmonitor.dm" -#include "code\modules\modular_computers\hardware\_hardware.dm" -#include "code\modules\modular_computers\hardware\ai_slot.dm" -#include "code\modules\modular_computers\hardware\battery_module.dm" -#include "code\modules\modular_computers\hardware\card_slot.dm" -#include "code\modules\modular_computers\hardware\CPU.dm" -#include "code\modules\modular_computers\hardware\hard_drive.dm" -#include "code\modules\modular_computers\hardware\network_card.dm" -#include "code\modules\modular_computers\hardware\portable_disk.dm" -#include "code\modules\modular_computers\hardware\printer.dm" -#include "code\modules\modular_computers\hardware\recharger.dm" -#include "code\modules\modular_computers\NTNet\NTNet.dm" -#include "code\modules\modular_computers\NTNet\NTNet_relay.dm" -#include "code\modules\modular_computers\NTNet\NTNRC\conversation.dm" #include "code\modules\nano\nanoexternal.dm" #include "code\modules\nano\nanoui.dm" #include "code\modules\nano\subsystem.dm" @@ -2131,13 +2100,10 @@ #include "code\modules\nano\interaction\physical.dm" #include "code\modules\nano\interaction\self.dm" #include "code\modules\nano\interaction\zlevel.dm" -#include "code\modules\nano\modules\alarm_monitor.dm" #include "code\modules\nano\modules\atmos_control.dm" -#include "code\modules\nano\modules\ert_manager.dm" #include "code\modules\nano\modules\human_appearance.dm" #include "code\modules\nano\modules\law_manager.dm" #include "code\modules\nano\modules\nano_module.dm" -#include "code\modules\nano\modules\power_monitor.dm" #include "code\modules\ninja\energy_katana.dm" #include "code\modules\ninja\suit\gloves.dm" #include "code\modules\ninja\suit\head.dm" @@ -2173,6 +2139,7 @@ #include "code\modules\pda\messenger_plugins.dm" #include "code\modules\pda\mob_hunt_game_app.dm" #include "code\modules\pda\PDA.dm" +#include "code\modules\pda\pda_tgui.dm" #include "code\modules\pda\pdas.dm" #include "code\modules\pda\radio.dm" #include "code\modules\pda\utilities.dm" @@ -2338,7 +2305,6 @@ #include "code\modules\research\designs\biogenerator_designs.dm" #include "code\modules\research\designs\bluespace_designs.dm" #include "code\modules\research\designs\comp_board_designs.dm" -#include "code\modules\research\designs\computer_part_designs.dm" #include "code\modules\research\designs\equipment_designs.dm" #include "code\modules\research\designs\janitorial_designs.dm" #include "code\modules\research\designs\machine_designs.dm" @@ -2417,7 +2383,6 @@ #include "code\modules\surgery\other.dm" #include "code\modules\surgery\plastic_surgery.dm" #include "code\modules\surgery\remove_embedded_object.dm" -#include "code\modules\surgery\rig_removal.dm" #include "code\modules\surgery\robotics.dm" #include "code\modules\surgery\surgery.dm" #include "code\modules\surgery\tools.dm" @@ -2473,6 +2438,10 @@ #include "code\modules\tgui\tgui.dm" #include "code\modules\tgui\modules\_base.dm" #include "code\modules\tgui\modules\crew_monitor.dm" +#include "code\modules\tgui\modules\ert_manager.dm" +#include "code\modules\tgui\modules\power_monitor.dm" +#include "code\modules\tgui\plugins\login.dm" +#include "code\modules\tgui\plugins\modal.dm" #include "code\modules\tgui\states\admin.dm" #include "code\modules\tgui\states\always.dm" #include "code\modules\tgui\states\conscious.dm" diff --git a/sound/AI/apc_overload.ogg b/sound/AI/apc_overload.ogg deleted file mode 100644 index ef9357b9eb6..00000000000 Binary files a/sound/AI/apc_overload.ogg and /dev/null differ diff --git a/sound/AI/apc_short.ogg b/sound/AI/apc_short.ogg deleted file mode 100644 index 90afc50f6fb..00000000000 Binary files a/sound/AI/apc_short.ogg and /dev/null differ diff --git a/sound/instruments/banjo/Ab3.ogg b/sound/instruments/banjo/Ab3.ogg new file mode 100644 index 00000000000..66e263bd615 Binary files /dev/null and b/sound/instruments/banjo/Ab3.ogg differ diff --git a/sound/instruments/banjo/Ab4.ogg b/sound/instruments/banjo/Ab4.ogg new file mode 100644 index 00000000000..f003e03233a Binary files /dev/null and b/sound/instruments/banjo/Ab4.ogg differ diff --git a/sound/instruments/banjo/Ab5.ogg b/sound/instruments/banjo/Ab5.ogg new file mode 100644 index 00000000000..c405725208e Binary files /dev/null and b/sound/instruments/banjo/Ab5.ogg differ diff --git a/sound/instruments/banjo/An3.ogg b/sound/instruments/banjo/An3.ogg new file mode 100644 index 00000000000..1700704c9c1 Binary files /dev/null and b/sound/instruments/banjo/An3.ogg differ diff --git a/sound/instruments/banjo/An4.ogg b/sound/instruments/banjo/An4.ogg new file mode 100644 index 00000000000..eb7279f869e Binary files /dev/null and b/sound/instruments/banjo/An4.ogg differ diff --git a/sound/instruments/banjo/An5.ogg b/sound/instruments/banjo/An5.ogg new file mode 100644 index 00000000000..d9cf57c0feb Binary files /dev/null and b/sound/instruments/banjo/An5.ogg differ diff --git a/sound/instruments/banjo/Bb3.ogg b/sound/instruments/banjo/Bb3.ogg new file mode 100644 index 00000000000..d3f757c0ace Binary files /dev/null and b/sound/instruments/banjo/Bb3.ogg differ diff --git a/sound/instruments/banjo/Bb4.ogg b/sound/instruments/banjo/Bb4.ogg new file mode 100644 index 00000000000..a9d869091bf Binary files /dev/null and b/sound/instruments/banjo/Bb4.ogg differ diff --git a/sound/instruments/banjo/Bb5.ogg b/sound/instruments/banjo/Bb5.ogg new file mode 100644 index 00000000000..a56e6c25005 Binary files /dev/null and b/sound/instruments/banjo/Bb5.ogg differ diff --git a/sound/instruments/banjo/Bn2.ogg b/sound/instruments/banjo/Bn2.ogg new file mode 100644 index 00000000000..3154f974193 Binary files /dev/null and b/sound/instruments/banjo/Bn2.ogg differ diff --git a/sound/instruments/banjo/Bn3.ogg b/sound/instruments/banjo/Bn3.ogg new file mode 100644 index 00000000000..6c72ec2fd5a Binary files /dev/null and b/sound/instruments/banjo/Bn3.ogg differ diff --git a/sound/instruments/banjo/Bn4.ogg b/sound/instruments/banjo/Bn4.ogg new file mode 100644 index 00000000000..b0e9a2b3b2f Binary files /dev/null and b/sound/instruments/banjo/Bn4.ogg differ diff --git a/sound/instruments/banjo/Bn5.ogg b/sound/instruments/banjo/Bn5.ogg new file mode 100644 index 00000000000..1b002140b87 Binary files /dev/null and b/sound/instruments/banjo/Bn5.ogg differ diff --git a/sound/instruments/banjo/Cn3.ogg b/sound/instruments/banjo/Cn3.ogg new file mode 100644 index 00000000000..6ef414d9d01 Binary files /dev/null and b/sound/instruments/banjo/Cn3.ogg differ diff --git a/sound/instruments/banjo/Cn4.ogg b/sound/instruments/banjo/Cn4.ogg new file mode 100644 index 00000000000..4a26a6741db Binary files /dev/null and b/sound/instruments/banjo/Cn4.ogg differ diff --git a/sound/instruments/banjo/Cn5.ogg b/sound/instruments/banjo/Cn5.ogg new file mode 100644 index 00000000000..901ed3bc08b Binary files /dev/null and b/sound/instruments/banjo/Cn5.ogg differ diff --git a/sound/instruments/banjo/Cn6.ogg b/sound/instruments/banjo/Cn6.ogg new file mode 100644 index 00000000000..5cdbbb17cea Binary files /dev/null and b/sound/instruments/banjo/Cn6.ogg differ diff --git a/sound/instruments/banjo/Db3.ogg b/sound/instruments/banjo/Db3.ogg new file mode 100644 index 00000000000..1ebffdf5025 Binary files /dev/null and b/sound/instruments/banjo/Db3.ogg differ diff --git a/sound/instruments/banjo/Db4.ogg b/sound/instruments/banjo/Db4.ogg new file mode 100644 index 00000000000..5b939365086 Binary files /dev/null and b/sound/instruments/banjo/Db4.ogg differ diff --git a/sound/instruments/banjo/Db5.ogg b/sound/instruments/banjo/Db5.ogg new file mode 100644 index 00000000000..6ee4dde9479 Binary files /dev/null and b/sound/instruments/banjo/Db5.ogg differ diff --git a/sound/instruments/banjo/Db6.ogg b/sound/instruments/banjo/Db6.ogg new file mode 100644 index 00000000000..fd73894fda6 Binary files /dev/null and b/sound/instruments/banjo/Db6.ogg differ diff --git a/sound/instruments/banjo/Dn3.ogg b/sound/instruments/banjo/Dn3.ogg new file mode 100644 index 00000000000..77491b01b8c Binary files /dev/null and b/sound/instruments/banjo/Dn3.ogg differ diff --git a/sound/instruments/banjo/Dn4.ogg b/sound/instruments/banjo/Dn4.ogg new file mode 100644 index 00000000000..11f68b5a157 Binary files /dev/null and b/sound/instruments/banjo/Dn4.ogg differ diff --git a/sound/instruments/banjo/Dn5.ogg b/sound/instruments/banjo/Dn5.ogg new file mode 100644 index 00000000000..2e9ebe49891 Binary files /dev/null and b/sound/instruments/banjo/Dn5.ogg differ diff --git a/sound/instruments/banjo/Dn6.ogg b/sound/instruments/banjo/Dn6.ogg new file mode 100644 index 00000000000..89ae62361dc Binary files /dev/null and b/sound/instruments/banjo/Dn6.ogg differ diff --git a/sound/instruments/banjo/Eb3.ogg b/sound/instruments/banjo/Eb3.ogg new file mode 100644 index 00000000000..1d1e43049d2 Binary files /dev/null and b/sound/instruments/banjo/Eb3.ogg differ diff --git a/sound/instruments/banjo/Eb4.ogg b/sound/instruments/banjo/Eb4.ogg new file mode 100644 index 00000000000..2722655f5a3 Binary files /dev/null and b/sound/instruments/banjo/Eb4.ogg differ diff --git a/sound/instruments/banjo/Eb5.ogg b/sound/instruments/banjo/Eb5.ogg new file mode 100644 index 00000000000..7a109dfdf79 Binary files /dev/null and b/sound/instruments/banjo/Eb5.ogg differ diff --git a/sound/instruments/banjo/En3.ogg b/sound/instruments/banjo/En3.ogg new file mode 100644 index 00000000000..4610efdd4f0 Binary files /dev/null and b/sound/instruments/banjo/En3.ogg differ diff --git a/sound/instruments/banjo/En4.ogg b/sound/instruments/banjo/En4.ogg new file mode 100644 index 00000000000..64c14daf915 Binary files /dev/null and b/sound/instruments/banjo/En4.ogg differ diff --git a/sound/instruments/banjo/En5.ogg b/sound/instruments/banjo/En5.ogg new file mode 100644 index 00000000000..8e0b6c1637e Binary files /dev/null and b/sound/instruments/banjo/En5.ogg differ diff --git a/sound/instruments/banjo/Fn3.ogg b/sound/instruments/banjo/Fn3.ogg new file mode 100644 index 00000000000..5cdc4f13fb3 Binary files /dev/null and b/sound/instruments/banjo/Fn3.ogg differ diff --git a/sound/instruments/banjo/Fn4.ogg b/sound/instruments/banjo/Fn4.ogg new file mode 100644 index 00000000000..78d5454f186 Binary files /dev/null and b/sound/instruments/banjo/Fn4.ogg differ diff --git a/sound/instruments/banjo/Fn5.ogg b/sound/instruments/banjo/Fn5.ogg new file mode 100644 index 00000000000..b21559b4656 Binary files /dev/null and b/sound/instruments/banjo/Fn5.ogg differ diff --git a/sound/instruments/banjo/Gb3.ogg b/sound/instruments/banjo/Gb3.ogg new file mode 100644 index 00000000000..fd055b74717 Binary files /dev/null and b/sound/instruments/banjo/Gb3.ogg differ diff --git a/sound/instruments/banjo/Gb4.ogg b/sound/instruments/banjo/Gb4.ogg new file mode 100644 index 00000000000..f2c62510ed0 Binary files /dev/null and b/sound/instruments/banjo/Gb4.ogg differ diff --git a/sound/instruments/banjo/Gb5.ogg b/sound/instruments/banjo/Gb5.ogg new file mode 100644 index 00000000000..ab17347912b Binary files /dev/null and b/sound/instruments/banjo/Gb5.ogg differ diff --git a/sound/instruments/banjo/Gn3.ogg b/sound/instruments/banjo/Gn3.ogg new file mode 100644 index 00000000000..ad52ef85c08 Binary files /dev/null and b/sound/instruments/banjo/Gn3.ogg differ diff --git a/sound/instruments/banjo/Gn4.ogg b/sound/instruments/banjo/Gn4.ogg new file mode 100644 index 00000000000..2ddb13b86b3 Binary files /dev/null and b/sound/instruments/banjo/Gn4.ogg differ diff --git a/sound/instruments/banjo/Gn5.ogg b/sound/instruments/banjo/Gn5.ogg new file mode 100644 index 00000000000..d5a7886c4cf Binary files /dev/null and b/sound/instruments/banjo/Gn5.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg new file mode 100644 index 00000000000..aaa1e27ab89 Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg new file mode 100644 index 00000000000..ce50e76aae6 Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg new file mode 100644 index 00000000000..22f34d67592 Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg b/sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg new file mode 100644 index 00000000000..eb5bb7c295e Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C2.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C2.ogg new file mode 100644 index 00000000000..bd299e321ab Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C2.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C3.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C3.ogg new file mode 100644 index 00000000000..0519d2d20dd Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C3.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C4.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C4.ogg new file mode 100644 index 00000000000..3b969a34b1c Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trombone/C5.ogg b/sound/instruments/synthesis_samples/brass/crisis_trombone/C5.ogg new file mode 100644 index 00000000000..75f709c16fe Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trombone/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trumpet/C4.ogg b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C4.ogg new file mode 100644 index 00000000000..ba347f80034 Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/brass/crisis_trumpet/C5.ogg b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C5.ogg new file mode 100644 index 00000000000..cee89761d0d Binary files /dev/null and b/sound/instruments/synthesis_samples/brass/crisis_trumpet/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C2.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C2.ogg new file mode 100644 index 00000000000..105f7676557 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C2.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C3.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C3.ogg new file mode 100644 index 00000000000..4aa33b6cded Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C3.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C4.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C4.ogg new file mode 100644 index 00000000000..d661e8d7580 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C5.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C5.ogg new file mode 100644 index 00000000000..bf650f1a6fa Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C6.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C6.ogg new file mode 100644 index 00000000000..c00f7949b7e Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C6.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C7.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C7.ogg new file mode 100644 index 00000000000..72588e9ca4c Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C7.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C8.ogg b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C8.ogg new file mode 100644 index 00000000000..b2a0b445b92 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/fluid_celeste/C8.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg new file mode 100644 index 00000000000..ecf6778343b Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg new file mode 100644 index 00000000000..867e9ce00d0 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg new file mode 100644 index 00000000000..446d45993e8 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg b/sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg new file mode 100644 index 00000000000..54d56400c03 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg new file mode 100644 index 00000000000..f3770c1f1a0 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg new file mode 100644 index 00000000000..28954fbb47f Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg new file mode 100644 index 00000000000..1233f5314a3 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg new file mode 100644 index 00000000000..00daf331357 Binary files /dev/null and b/sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C2.ogg new file mode 100644 index 00000000000..13ad54bff00 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C2.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C3.ogg new file mode 100644 index 00000000000..17bf392c4b2 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C3.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C4.ogg new file mode 100644 index 00000000000..feda419a0ad Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_clean/C5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_clean/C5.ogg new file mode 100644 index 00000000000..bd088dd850e Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_clean/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C2.ogg new file mode 100644 index 00000000000..09cdbeec42c Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C2.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C3.ogg new file mode 100644 index 00000000000..f82c39cee5b Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C3.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C4.ogg new file mode 100644 index 00000000000..23bfd113d6c Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_muted/C5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_muted/C5.ogg new file mode 100644 index 00000000000..e5ec38d5ab8 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_muted/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg new file mode 100644 index 00000000000..42a6cdfad3c Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg new file mode 100644 index 00000000000..cd6414c0aa2 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg new file mode 100644 index 00000000000..e5366018653 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg new file mode 100644 index 00000000000..60382228374 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg new file mode 100644 index 00000000000..648549d594a Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg new file mode 100644 index 00000000000..01ba59a908c Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg new file mode 100644 index 00000000000..7cfaa8ca72b Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg b/sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg new file mode 100644 index 00000000000..b4ca49dc047 Binary files /dev/null and b/sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg new file mode 100644 index 00000000000..7c9870a7c3b Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg new file mode 100644 index 00000000000..5723c2edd27 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg new file mode 100644 index 00000000000..329f14f6feb Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg new file mode 100644 index 00000000000..5e8ac69de28 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg new file mode 100644 index 00000000000..ddc44c69c29 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg new file mode 100644 index 00000000000..28557475284 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg new file mode 100644 index 00000000000..906fff5bd8d Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg new file mode 100644 index 00000000000..96d28a7206d Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg new file mode 100644 index 00000000000..9b917b7eb53 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg new file mode 100644 index 00000000000..c68410d6f09 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg new file mode 100644 index 00000000000..df84ba99e8e Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg new file mode 100644 index 00000000000..af8c178efe8 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg new file mode 100644 index 00000000000..268b41f1fce Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg new file mode 100644 index 00000000000..04ceb54bfc2 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg new file mode 100644 index 00000000000..b321983e74f Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg new file mode 100644 index 00000000000..250a5c08e08 Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg new file mode 100644 index 00000000000..8b1c23007bb Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg new file mode 100644 index 00000000000..098587183bb Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg new file mode 100644 index 00000000000..81b60ef4c2f Binary files /dev/null and b/sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg new file mode 100644 index 00000000000..39e992fbd85 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg new file mode 100644 index 00000000000..04aa9852815 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg new file mode 100644 index 00000000000..aff97942e9e Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg new file mode 100644 index 00000000000..19fd937707a Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg new file mode 100644 index 00000000000..452e7485be1 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg new file mode 100644 index 00000000000..66c88185a73 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg new file mode 100644 index 00000000000..d93c5176ced Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg new file mode 100644 index 00000000000..fabd90d2e6a Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg new file mode 100644 index 00000000000..e4cda1487aa Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg new file mode 100644 index 00000000000..c596994b3eb Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg new file mode 100644 index 00000000000..d265514e27b Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg new file mode 100644 index 00000000000..3e17b3f99a6 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg new file mode 100644 index 00000000000..b57a8a9109a Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg new file mode 100644 index 00000000000..ce4d9535e84 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg new file mode 100644 index 00000000000..bb02363fffb Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg new file mode 100644 index 00000000000..1a532ac8d42 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg new file mode 100644 index 00000000000..16ff313baa3 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg new file mode 100644 index 00000000000..04161d2571b Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C2.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C2.ogg new file mode 100644 index 00000000000..30a3c653a1c Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C2.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C3.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C3.ogg new file mode 100644 index 00000000000..f6bc891506c Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C3.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C4.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C4.ogg new file mode 100644 index 00000000000..ab47f6940c9 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C4.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C5.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C5.ogg new file mode 100644 index 00000000000..5dfb9aa5291 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C6.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C6.ogg new file mode 100644 index 00000000000..7bc8784207e Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C6.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C7.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C7.ogg new file mode 100644 index 00000000000..185b4d3db64 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C7.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_harpsi/C8.ogg b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C8.ogg new file mode 100644 index 00000000000..f358ef0810d Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_harpsi/C8.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg new file mode 100644 index 00000000000..048f9640bfe Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg new file mode 100644 index 00000000000..f1083d7dcb2 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg new file mode 100644 index 00000000000..244ebc3d5f2 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg new file mode 100644 index 00000000000..d3c68d64e9c Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg new file mode 100644 index 00000000000..2666ee66134 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg new file mode 100644 index 00000000000..050e463c0d1 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg differ diff --git a/sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg b/sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg new file mode 100644 index 00000000000..4793c5b7fd7 Binary files /dev/null and b/sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg differ diff --git a/sound/instruments/synthesis_samples/tones/Sawtooth.ogg b/sound/instruments/synthesis_samples/tones/Sawtooth.ogg new file mode 100644 index 00000000000..10b1930a64c Binary files /dev/null and b/sound/instruments/synthesis_samples/tones/Sawtooth.ogg differ diff --git a/sound/instruments/synthesis_samples/tones/Sine.ogg b/sound/instruments/synthesis_samples/tones/Sine.ogg new file mode 100644 index 00000000000..96a09d501b5 Binary files /dev/null and b/sound/instruments/synthesis_samples/tones/Sine.ogg differ diff --git a/sound/instruments/synthesis_samples/tones/Square.ogg b/sound/instruments/synthesis_samples/tones/Square.ogg new file mode 100644 index 00000000000..71029c07f95 Binary files /dev/null and b/sound/instruments/synthesis_samples/tones/Square.ogg differ diff --git a/sound/instruments/violin/Ab1.mid b/sound/instruments/violin/Ab1.mid new file mode 100644 index 00000000000..b8253364b4e Binary files /dev/null and b/sound/instruments/violin/Ab1.mid differ diff --git a/sound/instruments/violin/Ab2.mid b/sound/instruments/violin/Ab2.mid new file mode 100644 index 00000000000..4cd7f9b55a7 Binary files /dev/null and b/sound/instruments/violin/Ab2.mid differ diff --git a/sound/instruments/violin/Ab3.mid b/sound/instruments/violin/Ab3.mid new file mode 100644 index 00000000000..e827cfc635e Binary files /dev/null and b/sound/instruments/violin/Ab3.mid differ diff --git a/sound/instruments/violin/Ab4.mid b/sound/instruments/violin/Ab4.mid new file mode 100644 index 00000000000..57e1f76c976 Binary files /dev/null and b/sound/instruments/violin/Ab4.mid differ diff --git a/sound/instruments/violin/Ab5.mid b/sound/instruments/violin/Ab5.mid new file mode 100644 index 00000000000..59e95a6d997 Binary files /dev/null and b/sound/instruments/violin/Ab5.mid differ diff --git a/sound/instruments/violin/Ab6.mid b/sound/instruments/violin/Ab6.mid new file mode 100644 index 00000000000..9bd3436287b Binary files /dev/null and b/sound/instruments/violin/Ab6.mid differ diff --git a/sound/instruments/violin/Ab7.mid b/sound/instruments/violin/Ab7.mid new file mode 100644 index 00000000000..3c90af807e2 Binary files /dev/null and b/sound/instruments/violin/Ab7.mid differ diff --git a/sound/instruments/violin/Ab8.mid b/sound/instruments/violin/Ab8.mid new file mode 100644 index 00000000000..873d771f2ae Binary files /dev/null and b/sound/instruments/violin/Ab8.mid differ diff --git a/sound/instruments/violin/An1.mid b/sound/instruments/violin/An1.mid new file mode 100644 index 00000000000..d7f8a001d93 Binary files /dev/null and b/sound/instruments/violin/An1.mid differ diff --git a/sound/instruments/violin/An2.mid b/sound/instruments/violin/An2.mid new file mode 100644 index 00000000000..2f01800a075 Binary files /dev/null and b/sound/instruments/violin/An2.mid differ diff --git a/sound/instruments/violin/An3.mid b/sound/instruments/violin/An3.mid new file mode 100644 index 00000000000..c8ed3cdfa6c Binary files /dev/null and b/sound/instruments/violin/An3.mid differ diff --git a/sound/instruments/violin/An4.mid b/sound/instruments/violin/An4.mid new file mode 100644 index 00000000000..e7984ca7e62 Binary files /dev/null and b/sound/instruments/violin/An4.mid differ diff --git a/sound/instruments/violin/An5.mid b/sound/instruments/violin/An5.mid new file mode 100644 index 00000000000..e1fd228f7a9 Binary files /dev/null and b/sound/instruments/violin/An5.mid differ diff --git a/sound/instruments/violin/An6.mid b/sound/instruments/violin/An6.mid new file mode 100644 index 00000000000..1c8df6c98e5 Binary files /dev/null and b/sound/instruments/violin/An6.mid differ diff --git a/sound/instruments/violin/An7.mid b/sound/instruments/violin/An7.mid new file mode 100644 index 00000000000..2784428daf9 Binary files /dev/null and b/sound/instruments/violin/An7.mid differ diff --git a/sound/instruments/violin/An8.mid b/sound/instruments/violin/An8.mid new file mode 100644 index 00000000000..2db2ab70a7d Binary files /dev/null and b/sound/instruments/violin/An8.mid differ diff --git a/sound/instruments/violin/Bb1.mid b/sound/instruments/violin/Bb1.mid new file mode 100644 index 00000000000..693b73f5420 Binary files /dev/null and b/sound/instruments/violin/Bb1.mid differ diff --git a/sound/instruments/violin/Bb2.mid b/sound/instruments/violin/Bb2.mid new file mode 100644 index 00000000000..40da5f3da15 Binary files /dev/null and b/sound/instruments/violin/Bb2.mid differ diff --git a/sound/instruments/violin/Bb3.mid b/sound/instruments/violin/Bb3.mid new file mode 100644 index 00000000000..5bab6ccd636 Binary files /dev/null and b/sound/instruments/violin/Bb3.mid differ diff --git a/sound/instruments/violin/Bb4.mid b/sound/instruments/violin/Bb4.mid new file mode 100644 index 00000000000..dce830448ef Binary files /dev/null and b/sound/instruments/violin/Bb4.mid differ diff --git a/sound/instruments/violin/Bb5.mid b/sound/instruments/violin/Bb5.mid new file mode 100644 index 00000000000..fda796e27b9 Binary files /dev/null and b/sound/instruments/violin/Bb5.mid differ diff --git a/sound/instruments/violin/Bb6.mid b/sound/instruments/violin/Bb6.mid new file mode 100644 index 00000000000..9e5da684f43 Binary files /dev/null and b/sound/instruments/violin/Bb6.mid differ diff --git a/sound/instruments/violin/Bb7.mid b/sound/instruments/violin/Bb7.mid new file mode 100644 index 00000000000..215c56cbe7e Binary files /dev/null and b/sound/instruments/violin/Bb7.mid differ diff --git a/sound/instruments/violin/Bb8.mid b/sound/instruments/violin/Bb8.mid new file mode 100644 index 00000000000..4b55c34691f Binary files /dev/null and b/sound/instruments/violin/Bb8.mid differ diff --git a/sound/instruments/violin/Bn1.mid b/sound/instruments/violin/Bn1.mid new file mode 100644 index 00000000000..27968b5f9e7 Binary files /dev/null and b/sound/instruments/violin/Bn1.mid differ diff --git a/sound/instruments/violin/Bn2.mid b/sound/instruments/violin/Bn2.mid new file mode 100644 index 00000000000..54c9b99d03f Binary files /dev/null and b/sound/instruments/violin/Bn2.mid differ diff --git a/sound/instruments/violin/Bn3.mid b/sound/instruments/violin/Bn3.mid new file mode 100644 index 00000000000..f73476fb7bb Binary files /dev/null and b/sound/instruments/violin/Bn3.mid differ diff --git a/sound/instruments/violin/Bn4.mid b/sound/instruments/violin/Bn4.mid new file mode 100644 index 00000000000..2aa30708a6c Binary files /dev/null and b/sound/instruments/violin/Bn4.mid differ diff --git a/sound/instruments/violin/Bn5.mid b/sound/instruments/violin/Bn5.mid new file mode 100644 index 00000000000..0ebe636b714 Binary files /dev/null and b/sound/instruments/violin/Bn5.mid differ diff --git a/sound/instruments/violin/Bn6.mid b/sound/instruments/violin/Bn6.mid new file mode 100644 index 00000000000..3b8e1c217f7 Binary files /dev/null and b/sound/instruments/violin/Bn6.mid differ diff --git a/sound/instruments/violin/Bn7.mid b/sound/instruments/violin/Bn7.mid new file mode 100644 index 00000000000..afcb1982a13 Binary files /dev/null and b/sound/instruments/violin/Bn7.mid differ diff --git a/sound/instruments/violin/Bn8.mid b/sound/instruments/violin/Bn8.mid new file mode 100644 index 00000000000..3afd469256c Binary files /dev/null and b/sound/instruments/violin/Bn8.mid differ diff --git a/sound/instruments/violin/Cn1.mid b/sound/instruments/violin/Cn1.mid new file mode 100644 index 00000000000..857120f31f4 Binary files /dev/null and b/sound/instruments/violin/Cn1.mid differ diff --git a/sound/instruments/violin/Cn2.mid b/sound/instruments/violin/Cn2.mid new file mode 100644 index 00000000000..3ccd6670e87 Binary files /dev/null and b/sound/instruments/violin/Cn2.mid differ diff --git a/sound/instruments/violin/Cn3.mid b/sound/instruments/violin/Cn3.mid new file mode 100644 index 00000000000..1851e4f8d27 Binary files /dev/null and b/sound/instruments/violin/Cn3.mid differ diff --git a/sound/instruments/violin/Cn4.mid b/sound/instruments/violin/Cn4.mid new file mode 100644 index 00000000000..65e8b0efe4e Binary files /dev/null and b/sound/instruments/violin/Cn4.mid differ diff --git a/sound/instruments/violin/Cn5.mid b/sound/instruments/violin/Cn5.mid new file mode 100644 index 00000000000..544f921e43b Binary files /dev/null and b/sound/instruments/violin/Cn5.mid differ diff --git a/sound/instruments/violin/Cn6.mid b/sound/instruments/violin/Cn6.mid new file mode 100644 index 00000000000..7c78dab2f07 Binary files /dev/null and b/sound/instruments/violin/Cn6.mid differ diff --git a/sound/instruments/violin/Cn7.mid b/sound/instruments/violin/Cn7.mid new file mode 100644 index 00000000000..3abe4cde086 Binary files /dev/null and b/sound/instruments/violin/Cn7.mid differ diff --git a/sound/instruments/violin/Cn8.mid b/sound/instruments/violin/Cn8.mid new file mode 100644 index 00000000000..06f14081b3b Binary files /dev/null and b/sound/instruments/violin/Cn8.mid differ diff --git a/sound/instruments/violin/Cn9.mid b/sound/instruments/violin/Cn9.mid new file mode 100644 index 00000000000..62f4eef045a Binary files /dev/null and b/sound/instruments/violin/Cn9.mid differ diff --git a/sound/instruments/violin/Db1.mid b/sound/instruments/violin/Db1.mid new file mode 100644 index 00000000000..88dba851452 Binary files /dev/null and b/sound/instruments/violin/Db1.mid differ diff --git a/sound/instruments/violin/Db2.mid b/sound/instruments/violin/Db2.mid new file mode 100644 index 00000000000..b510926b45f Binary files /dev/null and b/sound/instruments/violin/Db2.mid differ diff --git a/sound/instruments/violin/Db3.mid b/sound/instruments/violin/Db3.mid new file mode 100644 index 00000000000..9954bbe478a Binary files /dev/null and b/sound/instruments/violin/Db3.mid differ diff --git a/sound/instruments/violin/Db4.mid b/sound/instruments/violin/Db4.mid new file mode 100644 index 00000000000..2c5ff74db0a Binary files /dev/null and b/sound/instruments/violin/Db4.mid differ diff --git a/sound/instruments/violin/Db5.mid b/sound/instruments/violin/Db5.mid new file mode 100644 index 00000000000..e5850a3fd04 Binary files /dev/null and b/sound/instruments/violin/Db5.mid differ diff --git a/sound/instruments/violin/Db6.mid b/sound/instruments/violin/Db6.mid new file mode 100644 index 00000000000..217c0ad014c Binary files /dev/null and b/sound/instruments/violin/Db6.mid differ diff --git a/sound/instruments/violin/Db7.mid b/sound/instruments/violin/Db7.mid new file mode 100644 index 00000000000..ec32bdbf904 Binary files /dev/null and b/sound/instruments/violin/Db7.mid differ diff --git a/sound/instruments/violin/Db8.mid b/sound/instruments/violin/Db8.mid new file mode 100644 index 00000000000..555bce3db0d Binary files /dev/null and b/sound/instruments/violin/Db8.mid differ diff --git a/sound/instruments/violin/Dn1.mid b/sound/instruments/violin/Dn1.mid new file mode 100644 index 00000000000..92e4e0d9581 Binary files /dev/null and b/sound/instruments/violin/Dn1.mid differ diff --git a/sound/instruments/violin/Dn2.mid b/sound/instruments/violin/Dn2.mid new file mode 100644 index 00000000000..34eb9d1db1b Binary files /dev/null and b/sound/instruments/violin/Dn2.mid differ diff --git a/sound/instruments/violin/Dn3.mid b/sound/instruments/violin/Dn3.mid new file mode 100644 index 00000000000..fbd56085aaf Binary files /dev/null and b/sound/instruments/violin/Dn3.mid differ diff --git a/sound/instruments/violin/Dn4.mid b/sound/instruments/violin/Dn4.mid new file mode 100644 index 00000000000..e13c7448292 Binary files /dev/null and b/sound/instruments/violin/Dn4.mid differ diff --git a/sound/instruments/violin/Dn5.mid b/sound/instruments/violin/Dn5.mid new file mode 100644 index 00000000000..8fd41e5c6fe Binary files /dev/null and b/sound/instruments/violin/Dn5.mid differ diff --git a/sound/instruments/violin/Dn6.mid b/sound/instruments/violin/Dn6.mid new file mode 100644 index 00000000000..d47329e8f9e Binary files /dev/null and b/sound/instruments/violin/Dn6.mid differ diff --git a/sound/instruments/violin/Dn7.mid b/sound/instruments/violin/Dn7.mid new file mode 100644 index 00000000000..b2496603876 Binary files /dev/null and b/sound/instruments/violin/Dn7.mid differ diff --git a/sound/instruments/violin/Dn8.mid b/sound/instruments/violin/Dn8.mid new file mode 100644 index 00000000000..56667a1a86d Binary files /dev/null and b/sound/instruments/violin/Dn8.mid differ diff --git a/sound/instruments/violin/Eb1.mid b/sound/instruments/violin/Eb1.mid new file mode 100644 index 00000000000..829e6fcf185 Binary files /dev/null and b/sound/instruments/violin/Eb1.mid differ diff --git a/sound/instruments/violin/Eb2.mid b/sound/instruments/violin/Eb2.mid new file mode 100644 index 00000000000..66029b340cc Binary files /dev/null and b/sound/instruments/violin/Eb2.mid differ diff --git a/sound/instruments/violin/Eb3.mid b/sound/instruments/violin/Eb3.mid new file mode 100644 index 00000000000..c982375941e Binary files /dev/null and b/sound/instruments/violin/Eb3.mid differ diff --git a/sound/instruments/violin/Eb4.mid b/sound/instruments/violin/Eb4.mid new file mode 100644 index 00000000000..016ed4f1edf Binary files /dev/null and b/sound/instruments/violin/Eb4.mid differ diff --git a/sound/instruments/violin/Eb5.mid b/sound/instruments/violin/Eb5.mid new file mode 100644 index 00000000000..ddb511795df Binary files /dev/null and b/sound/instruments/violin/Eb5.mid differ diff --git a/sound/instruments/violin/Eb6.mid b/sound/instruments/violin/Eb6.mid new file mode 100644 index 00000000000..b7242b9ab99 Binary files /dev/null and b/sound/instruments/violin/Eb6.mid differ diff --git a/sound/instruments/violin/Eb7.mid b/sound/instruments/violin/Eb7.mid new file mode 100644 index 00000000000..773538340a5 Binary files /dev/null and b/sound/instruments/violin/Eb7.mid differ diff --git a/sound/instruments/violin/Eb8.mid b/sound/instruments/violin/Eb8.mid new file mode 100644 index 00000000000..4ad074e173b Binary files /dev/null and b/sound/instruments/violin/Eb8.mid differ diff --git a/sound/instruments/violin/En1.mid b/sound/instruments/violin/En1.mid new file mode 100644 index 00000000000..79ab68df9df Binary files /dev/null and b/sound/instruments/violin/En1.mid differ diff --git a/sound/instruments/violin/En2.mid b/sound/instruments/violin/En2.mid new file mode 100644 index 00000000000..cd61c8d0de5 Binary files /dev/null and b/sound/instruments/violin/En2.mid differ diff --git a/sound/instruments/violin/En3.mid b/sound/instruments/violin/En3.mid new file mode 100644 index 00000000000..da5b703d545 Binary files /dev/null and b/sound/instruments/violin/En3.mid differ diff --git a/sound/instruments/violin/En4.mid b/sound/instruments/violin/En4.mid new file mode 100644 index 00000000000..f7d3af024ff Binary files /dev/null and b/sound/instruments/violin/En4.mid differ diff --git a/sound/instruments/violin/En5.mid b/sound/instruments/violin/En5.mid new file mode 100644 index 00000000000..d3d353943f9 Binary files /dev/null and b/sound/instruments/violin/En5.mid differ diff --git a/sound/instruments/violin/En6.mid b/sound/instruments/violin/En6.mid new file mode 100644 index 00000000000..73eb5b0697d Binary files /dev/null and b/sound/instruments/violin/En6.mid differ diff --git a/sound/instruments/violin/En7.mid b/sound/instruments/violin/En7.mid new file mode 100644 index 00000000000..79a9462c844 Binary files /dev/null and b/sound/instruments/violin/En7.mid differ diff --git a/sound/instruments/violin/En8.mid b/sound/instruments/violin/En8.mid new file mode 100644 index 00000000000..88947fc7318 Binary files /dev/null and b/sound/instruments/violin/En8.mid differ diff --git a/sound/instruments/violin/Fn1.mid b/sound/instruments/violin/Fn1.mid new file mode 100644 index 00000000000..abe0d4e4051 Binary files /dev/null and b/sound/instruments/violin/Fn1.mid differ diff --git a/sound/instruments/violin/Fn2.mid b/sound/instruments/violin/Fn2.mid new file mode 100644 index 00000000000..d245bef3b54 Binary files /dev/null and b/sound/instruments/violin/Fn2.mid differ diff --git a/sound/instruments/violin/Fn3.mid b/sound/instruments/violin/Fn3.mid new file mode 100644 index 00000000000..e532e30dac9 Binary files /dev/null and b/sound/instruments/violin/Fn3.mid differ diff --git a/sound/instruments/violin/Fn4.mid b/sound/instruments/violin/Fn4.mid new file mode 100644 index 00000000000..47219c72fa2 Binary files /dev/null and b/sound/instruments/violin/Fn4.mid differ diff --git a/sound/instruments/violin/Fn5.mid b/sound/instruments/violin/Fn5.mid new file mode 100644 index 00000000000..630d16371d9 Binary files /dev/null and b/sound/instruments/violin/Fn5.mid differ diff --git a/sound/instruments/violin/Fn6.mid b/sound/instruments/violin/Fn6.mid new file mode 100644 index 00000000000..08cbc981bdb Binary files /dev/null and b/sound/instruments/violin/Fn6.mid differ diff --git a/sound/instruments/violin/Fn7.mid b/sound/instruments/violin/Fn7.mid new file mode 100644 index 00000000000..6c28c7d272e Binary files /dev/null and b/sound/instruments/violin/Fn7.mid differ diff --git a/sound/instruments/violin/Fn8.mid b/sound/instruments/violin/Fn8.mid new file mode 100644 index 00000000000..2d73762f269 Binary files /dev/null and b/sound/instruments/violin/Fn8.mid differ diff --git a/sound/instruments/violin/Gb1.mid b/sound/instruments/violin/Gb1.mid new file mode 100644 index 00000000000..d18668e8911 Binary files /dev/null and b/sound/instruments/violin/Gb1.mid differ diff --git a/sound/instruments/violin/Gb2.mid b/sound/instruments/violin/Gb2.mid new file mode 100644 index 00000000000..302f0c6fdc1 Binary files /dev/null and b/sound/instruments/violin/Gb2.mid differ diff --git a/sound/instruments/violin/Gb3.mid b/sound/instruments/violin/Gb3.mid new file mode 100644 index 00000000000..1f592fc9039 Binary files /dev/null and b/sound/instruments/violin/Gb3.mid differ diff --git a/sound/instruments/violin/Gb4.mid b/sound/instruments/violin/Gb4.mid new file mode 100644 index 00000000000..45854126f98 Binary files /dev/null and b/sound/instruments/violin/Gb4.mid differ diff --git a/sound/instruments/violin/Gb5.mid b/sound/instruments/violin/Gb5.mid new file mode 100644 index 00000000000..fb1e1da339a Binary files /dev/null and b/sound/instruments/violin/Gb5.mid differ diff --git a/sound/instruments/violin/Gb6.mid b/sound/instruments/violin/Gb6.mid new file mode 100644 index 00000000000..bfa896bb784 Binary files /dev/null and b/sound/instruments/violin/Gb6.mid differ diff --git a/sound/instruments/violin/Gb7.mid b/sound/instruments/violin/Gb7.mid new file mode 100644 index 00000000000..a27763c1d47 Binary files /dev/null and b/sound/instruments/violin/Gb7.mid differ diff --git a/sound/instruments/violin/Gb8.mid b/sound/instruments/violin/Gb8.mid new file mode 100644 index 00000000000..aaab80a7276 Binary files /dev/null and b/sound/instruments/violin/Gb8.mid differ diff --git a/sound/instruments/violin/Gn1.mid b/sound/instruments/violin/Gn1.mid new file mode 100644 index 00000000000..1df52ab0760 Binary files /dev/null and b/sound/instruments/violin/Gn1.mid differ diff --git a/sound/instruments/violin/Gn2.mid b/sound/instruments/violin/Gn2.mid new file mode 100644 index 00000000000..6e0ca383127 Binary files /dev/null and b/sound/instruments/violin/Gn2.mid differ diff --git a/sound/instruments/violin/Gn3.mid b/sound/instruments/violin/Gn3.mid new file mode 100644 index 00000000000..bb3e6dedcbf Binary files /dev/null and b/sound/instruments/violin/Gn3.mid differ diff --git a/sound/instruments/violin/Gn4.mid b/sound/instruments/violin/Gn4.mid new file mode 100644 index 00000000000..0c46432afee Binary files /dev/null and b/sound/instruments/violin/Gn4.mid differ diff --git a/sound/instruments/violin/Gn5.mid b/sound/instruments/violin/Gn5.mid new file mode 100644 index 00000000000..f39dcf5e2b9 Binary files /dev/null and b/sound/instruments/violin/Gn5.mid differ diff --git a/sound/instruments/violin/Gn6.mid b/sound/instruments/violin/Gn6.mid new file mode 100644 index 00000000000..0efa2259ca1 Binary files /dev/null and b/sound/instruments/violin/Gn6.mid differ diff --git a/sound/instruments/violin/Gn7.mid b/sound/instruments/violin/Gn7.mid new file mode 100644 index 00000000000..22fd1b6bcb0 Binary files /dev/null and b/sound/instruments/violin/Gn7.mid differ diff --git a/sound/instruments/violin/Gn8.mid b/sound/instruments/violin/Gn8.mid new file mode 100644 index 00000000000..16b7171d627 Binary files /dev/null and b/sound/instruments/violin/Gn8.mid differ diff --git a/sound/machines/machine_vend.ogg b/sound/machines/machine_vend.ogg new file mode 100644 index 00000000000..92867a1f3d3 Binary files /dev/null and b/sound/machines/machine_vend.ogg differ diff --git a/sound/misc/berightback.ogg b/sound/misc/berightback.ogg new file mode 100644 index 00000000000..e9a6f6c182b Binary files /dev/null and b/sound/misc/berightback.ogg differ diff --git a/sound/music/thunderdome.ogg b/sound/music/thunderdome.ogg index 26b18df5e05..82780e416d4 100644 Binary files a/sound/music/thunderdome.ogg and b/sound/music/thunderdome.ogg differ diff --git a/sound/turntable/testloop.ogg b/sound/turntable/testloop.ogg deleted file mode 100644 index 453ad568fa2..00000000000 Binary files a/sound/turntable/testloop.ogg and /dev/null differ diff --git a/sound/turntable/testloop1.ogg b/sound/turntable/testloop1.ogg deleted file mode 100644 index 5e1210bfd06..00000000000 Binary files a/sound/turntable/testloop1.ogg and /dev/null differ diff --git a/sound/turntable/testloop2.ogg b/sound/turntable/testloop2.ogg deleted file mode 100644 index 5feaa762936..00000000000 Binary files a/sound/turntable/testloop2.ogg and /dev/null differ diff --git a/sound/turntable/testloop3.ogg b/sound/turntable/testloop3.ogg deleted file mode 100644 index 9ac0e02b773..00000000000 Binary files a/sound/turntable/testloop3.ogg and /dev/null differ diff --git a/sound/weapons/banjoslap.ogg b/sound/weapons/banjoslap.ogg new file mode 100644 index 00000000000..06a86a535dd Binary files /dev/null and b/sound/weapons/banjoslap.ogg differ diff --git a/sound/weapons/guitarslam.ogg b/sound/weapons/guitarslam.ogg new file mode 100644 index 00000000000..4fa53db9404 Binary files /dev/null and b/sound/weapons/guitarslam.ogg differ diff --git a/sound/weapons/jug_empty_impact.ogg b/sound/weapons/jug_empty_impact.ogg new file mode 100644 index 00000000000..d78c1c5e554 Binary files /dev/null and b/sound/weapons/jug_empty_impact.ogg differ diff --git a/sound/weapons/jug_filled_impact.ogg b/sound/weapons/jug_filled_impact.ogg new file mode 100644 index 00000000000..05e2d364373 Binary files /dev/null and b/sound/weapons/jug_filled_impact.ogg differ diff --git a/strings/tips.txt b/strings/tips.txt index b2df1c17482..d28943913d3 100644 --- a/strings/tips.txt +++ b/strings/tips.txt @@ -1,4 +1,4 @@ -Where the space map levels connect is randomized every round, but are otherwise kept consistent within rounds. +Space map levels' connections are randomized between rounds, but are otherwise kept consistent in the same round. You can catch thrown items by toggling on your throw mode with an empty hand active. To crack the safe in the vault, use a stethoscope or thermal drill. You can climb onto a table by dragging yourself onto one. This takes some time. Clicking on a table that someone else is climbing onto will knock them down. @@ -9,8 +9,8 @@ You can change the control scheme by pressing tab. One uses WASD for movement, w Firesuits and winter coats offer mild protection from the cold, allowing you to spend longer periods of time near breaches and space than if wearing nothing at all. Glass shards can be welded to make glass, and metal rods can be welded to make metal. Ores can be welded too, but this takes a lot of fuel. If you need to drag multiple people either to safety or to space, bring a locker over and stuff them all in before hauling them off. -You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on them once more. An aggressive grab will momentarily stun someone, allow you to place them on a table by clicking on it, or throw them by toggling on throwing. -Holding alt and left clicking a tile will allow you to see its contents in the top right window pane, which is much faster than right clicking. +You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on the grab itself. An aggressive grab will allow you to place them on a table by clicking on it, or throw them by toggling on throwing. +Holding alt and left clicking a tile will allow you to see its contents in the top right window panel, which is much faster than right clicking. The resist button will allow you to resist out of handcuffs, being buckled to a chair or bed, out of locked lockers and more. Whenever you're stuck, try resisting! You can move an item out of the way by dragging it and then clicking on an adjacent tile with an empty hand. You can recolor certain items like jumpsuits and gloves in washing machines by also throwing in a crayon. @@ -21,9 +21,8 @@ When in doubt about technicial issues, clear your cache (byond launcher > cogwhe Most things have special interactions with your middle mouse button, alt, shift, and control click. Experiment! If you find yourself in a fistfight with another player, running away and calling for help is a perfectly viable option. Different weapons have different strengths. Some weapons, such as spears, floor tiles, and throwing stars, deal more damage when thrown compared to when attacked normally. -A thrown glass of water can make a slippery tile, allowing you to slow down your pursuers in a pinch. +Clicking on a tile on harm intent with a glass of water can make a slippery tile, allowing you to slow down your pursuers in a pinch. When dealing with security, you can often get your sentence negated entirely through cooperation and deception. -The P2P chat function found on tablet computers allows for a stealthy way to communicate with people. We were all new once, be patient and guide new players in the right direction. On most clothing items that go in the exosuit slot, you can put certain small items into your suit storage, such as a spraycan, your emergency oxygen tank, or a flashlight. Most job-related exosuit clothing can fit job-related items into it. For example, the atmospheric technician's hardsuit/winter coat can hold an RPD, and labcoats can hold most medicine. @@ -36,8 +35,8 @@ As the Captain, you have absolute access and control over the station, but this As the Chief Medical Officer, your hypospray is like a refillable instant injection syringe that can hold 30 units and the unlike standard hypospray, yours is able to be filled with harmful reagents and injects without telling anyone what have you exactly injected the person with. As the Chief Medical Officer, coordinate and communicate with your doctors, chemists, and geneticists during a nuclear emergency, blob infestation, or some other crisis to keep people alive and fighting. As a Medical Doctor, you can surgically implant or extract things from people's chests. This can range from putting in a bomb to pulling out an alien larva. -As a Medical Doctor, you must target the correct limb and be on help intent when trying to perform surgery on someone. Using disarm intent will intentionally fail the surgery step. -As a Medical Doctor, corpses with the "...and their soul has departed" description no longer have a ghost attached to them and aren't revivable or clonable. +As a Medical Doctor, you must target the correct limb and be on help intent when trying to perform surgery on someone. +As a Medical Doctor, corpses with the "...and their soul has departed" description no longer have a ghost attached to them and aren't revivable or clonable right now, but they might be clonable later. As a Medical Doctor, treating plasmamen is not impossible! Salbutamol stops them from suffocating and showers stop them from burning alive. You can even perform surgery on them by doing the procedure on a roller bed under a shower. As a Chemist, there are dozens of chemicals that can heal, and even more that can cause harm. Experiment! As a Chemist, some chemicals can only be synthesized by heating up the contents with a chemical heater or manually with lighters and similar tools. @@ -50,7 +49,7 @@ As the Research Director, you can take AIs out of their cores by loading them in As the Research Director, you can lock down cyborgs instead of blowing them up. Then you can have their laws reset or if that doesn't work, safely dismantled. As the Research Director, you can spy on and even forge PDA communications with the message monitor console! The key is in your office. As a Scientist, you can maximize the number of uses you get out of a slime by feeding it slime steroid, created from purple slimes, while alive. You can then apply extract enhancer, created from cerulean slimes, on each extract. -As a Scientist, you can disable anomalies by scanning them with an analyzer, then send a signal on the frequency it gives you with a remote signaling device, or if researched, hit the anomaly with an anomaly analyzer. This will leave behind an anomaly core, which can be used to construct a Phazon mech or reactive armors! +As a Scientist, you can disable anomalies by scanning them with an analyzer, then send a signal on the frequency it gives you with a remote signaling device. This will leave behind an anomaly core, which can be used to construct a Phazon mech! As a Scientist, researchable stock parts can seriously improve the efficiency and speed of machines around the station. In some cases, it can even unlock new functions. As a Roboticist, keep an ear out for anomaly announcements. If you get your hands on an anomaly core, you can build a Phazon mech! As a Roboticist, you can repair your cyborgs with a welding tool. If they have taken burn damage, you can remove their battery, expose the wiring with a screwdriver and replace their wires with a cable coil. @@ -59,24 +58,24 @@ As a Roboticist, you can augment people with cyborg limbs. Augmented limbs can e As the AI, you can click on people's names to look at them. This only works if there are cameras that can see them, they aren't wearing agent IDs, or aren't using digital camouflage as changelings. As the AI, you can quickly open and close doors by holding shift while clicking them, bolt them when holding ctrl, and even shock them while holding alt. As the AI, you can take pictures with your camera and upload them to newscasters. -As a Cyborg, choose your module carefully, as only cutting and mending your reset wire or using a cyborg reset module will let you repick it. If possible, refrain from choosing a module until a situation that requires one occurs. +As a Cyborg, choose your module carefully, as only a cyborg reset module will let you repick it. As a Cyborg, you are immune to most forms of stunning, and excel at almost everything far better than humans. However, flashes can easily stunlock you and you cannot do any precision work as you lack hands. As a Cyborg, you are impervious to fires and heat. If you are rogue, you can release plasma fires everywhere and walk through them without a care in the world! As a Cyborg, you are extremely vulnerable to EMPs as EMPs both stun you and damage you. The ion rifle in the armory or a traitor with an EMP kit can kill you in seconds. -As an Engineering Cyborg, you can attach air alarm/fire alarm/APC frames to walls by placing them on the floor and using a screwdriver on them. -As a Medical Cyborg, you can fully perform surgery and even augment people. +As an Engineering Cyborg, you can attach air alarm/fire alarm/APC frames to walls by using your magnetic gripper. +As a Medical Cyborg, you can partially perform surgery, as you cannot replace organs, but you cannot fail any surgery steps. As a Janitor Cyborg, you are the bane of all slaughter demons. Cleaning up blood stains will severely gimp them. As the Chief Engineer, you can rename areas or create entirely new ones using your station blueprints. As the Chief Engineer, your hardsuit is significantly better than everybody else's. It has the best features of both engineering and atmospherics hardsuits, boasting nigh-invulnerability to radiation and all atmospheric conditions. -As the Chief Engineer, the power flow control console in your office will show you APC infos and lets you control them remotely. +As the Chief Engineer, the power flow control console in your office will show you APC infos and lets you control them remotely. As an Engineer, you can electrify grilles by placing wire "nodes" beneath them: the big seemingly unconnected bulges from a half completed wiring job. The wire will be need to be connected to an active power source in order to function. As an Engineer, return to Engineering once in a while to check on the engine and SMES cells. It's always a good idea to make sure containment isn't compromised. As an Engineer, you can power the station solely with the solar arrays. They will provide just enough electricity to power the station, however their output is still much worse compared to the true engine. As an Engineer, you can cool a supermatter shard by spraying it with a fire extinguisher. Only for the brave! -As an Engineer, you can repair windows by using a welding tool on them while on any intent other than harm. +As an Engineer, you can repair windows by using a welding tool on them while on help intent. As an Engineer, you can lock APCs, fire alarms, emitters, and radiation collectors using your ID card to prevent others from disabling them. As an Engineer, don't underestimate the humble P.A.C.M.A.N. generators. With upgraded parts, a couple units working in tandem are sufficient to take over for an exploded engine or shattered solars. -As an Engineer, you can pry open secure storage blast doors by disabling the engine room APC's main breaker. This is obviously a bad idea if the engine is running. +As an Engineer, you can pry open secure storage blast doors by turning off the secure storage APC's enviroment power channel. As an Atmospheric Technician, look into replacing your gas pumps with volumetric gas pumps, as those move air in flat numerical amounts, rather than percentages which leave trace gases. As an Atmospheric Technician, you are better suited to fighting fires than anyone else. As such, you have access to better firesuits, backpack firefighter tanks, and a completely heat and fire proof hardsuit. As an Atmospheric Technician, your backpack firefighter tank can launch cryofrost. This resin will extinguish fires and very quickly; it's ideal for plasma fires! @@ -86,9 +85,9 @@ As the Head of Security, don't let the power go to your head. You may have high As the Warden, your duty is to be the watchdog of the brig and handler of prisoners when little is happening, and to hand out equipment and weapons to the security officers when a crisis strikes. As the Warden, keep a close eye on the armory at all times, as it is a favored strike point of nuclear operatives and cocky traitors. As the Warden, if a prisoner's crimes are heinous enough you can put them in permabrig or the gulag. Make sure to check on them once in a while! -As the Warden, you can implant criminals you suspect might re-offend with devices that will track their location and allow you to remotely inject them with disabling chemicals. +As the Warden, you can implant criminals you suspect might re-offend with devices that will track their location and allow you to remotely inject them with disabling chemicals. As a Security Officer, communicate and coordinate with your fellow officers using the security channel (:s) to avoid confusion. -As a Security Officer, your sechuds or HUDsunglasses can not only see crewmates' job assignments and criminal status, but also if they are mindshield implanted. You can tell by the flashing blue outline around their job icon. +As a Security Officer, your sechuds or HUDsunglasses let you see crewmates' job assignments, their criminal status, and whether they have a mindshield or not. A flashing green border around their job icon means they are mindshielded. As a Security Officer, mindshield implants can only prevent someone from being turned into a cultist. It will not de-cult them if they have already been converted. As a Security Officer, examining someone while wearing sechuds or HUDsunglasses will let you set their arrest level, which will cause Beepsky and other security bots to chase after them. As the Detective, keep in mind that people leave fingerprints everywhere and on everything. With the exception of white latex, gloves will hide them. All is not lost, however, as gloves leave fibers specific to their kind such as black or nitrile, pointing to a general department. @@ -98,7 +97,6 @@ As the IAA, try to negotiate with the Warden if sentences seem too high for the As the IAA, you can try to convince the Captain and Head of Security to hold trials for prisoners in the courtroom. As the Head of Personnel, you are not higher ranking than other heads of staff, even though you are expected to take the Captain's place first should he go missing. If the situation seems too rough for you, consider allowing another head to become temporary Captain. As the Head of Personnel, you are just as large a target as the Captain because of the potential power your ID and computer can hand out. -As the Head of Personnel, consider getting a laptop with an ID slot. It allows you to do your job even when out of your office. As the Mime, your invisible wall power blocks people as well as projectiles. You can use it in a pinch to delay your pursuer. As the Mime, your oath of silence is your source of power. Breaking it robs you of your powers and of your honor. As the Clown, if you lose your banana peel, you can still slip people with your PDA! Honk! @@ -111,7 +109,7 @@ As the Chaplain, you are much more likely to get a response by praying to the go As a Botanist, you can hack the MegaSeed Vendor to get access to more exotic seeds. These seeds can alternatively be ordered from cargo. As a Botanist, you can mutate the plants growing in your hydroponics trays with unstable mutagen or, as an alternative, crude radioactives from chemistry to get special variations. As a Botanist, you should look into increasing the potency of your plants. This increases the size, amount of chemicals, points gained from grinding them in the biogenerator, and lets people know you are a proficient botanist. -As a Botanist, you can combine production trait chemicals just like a Chemist. Chlorine (blumpkin) + radium and phosphorus (glowshrooms) equals unstable mutagen! +As a Botanist, you can combine production trait chemicals just like a Chemist. Chlorine and plasma (blumpkin) + radium (glowshrooms) equals unstable mutagen! As the Chef, you can create a very wide variety of food with the crafting menu. You can find it by looking for the hammer icon near your intents. As the Chef, you can rename your custom made food with a pen. As the Chef, if you are low on ingredients, consider making something that has several servings to last longer among the crew. @@ -121,8 +119,8 @@ As a Janitor, mousetraps can be used to create bombs or booby-trap containers. As the Librarian, be sure to keep the shelves stocked and the library clean for crew. As a Cargo Technician, you can hack MULEbots to make them faster, run over people in their way, and even let you ride them! As a Cargo Technician, you can order contraband items from the supply shuttle console by de-constructing it and using a multitool on the circuit board, the re-assembling it. -As a Cargo Technician, you can earn more cargo points by shipping back crates from maintenance, liquid containers, plasma sheets, rare seeds from hydroponics, and more! -As a Shaft Miner, the western side of the Asteroid has a lot more rare minerals than on the east, but is a lot more dangerous. +As a Cargo Technician, you can earn more cargo points by shipping back crates from maintenance, plasma sheets, rare seeds from hydroponics, technology disks from R&D, and more! +As a Shaft Miner, the north side of Lavaland has a lot more rare minerals than on the south, but is a lot more dangerous. As a Shaft Miner, always have a GPS on you, so a fellow miner or cyborg can come to save you if you die. As a Traitor, the cryptographic sequencer (emag) can not only open doors, but also lockers, crates, APCs and more. It can hack cyborgs, and even cause bots to go berserk. Use it on the right machines, and you can even order more traitor gear or contact the Syndicate. Experiment! As a Traitor, subverting the AI to serve you can make it an extremely powerful ally. However, be careful of the wording in the laws you give it, as it may use your poorly written laws against you! @@ -130,14 +128,14 @@ As a Traitor, the Captain and the Head of Security are two of the most difficult As a Traitor, you can manufacture and recycle revolver bullets at a hacked autolathe, making the revolver an extremely powerful tool. As a Traitor, you may sometimes be assigned to hunt other traitors, and in turn be hunted by others. As a Traitor, the syndicate encryption key is very useful for coordinating plans with your fellow traitors -- or, of course, betraying them. -As a Traitor, plasma can be injected into many things to sabotage them. Power cells, light bulbs, welding tools, cigars and e-cigs will all explode when used. +As a Traitor, plasma can be injected into many things to sabotage them. Power cells, light bulbs, cigars and e-cigs will all explode when used. As a Traitor, if you can find another Traitor and pool your TC you can buy a mega surplus crate, which costs 40TC but contains a lot of random syndicate gear. As a Nuclear Operative, communication is key! Use ; to speak to your fellow operatives and coordinate an attack plan. As a Nuclear Operative, you should look into purchasing a syndicate cyborg, as they can provide heavy fire support, full access, are immune to conventional stuns, and can easily take down the AI. As a Nuclear Operative, stick together! While your equipment is robust, your fellow operatives are much better at saving your life: they can drag you away from danger while stunned and provide cover fire. As a Nuclear Operative, you might end up in a situation where the AI has bolted you into a room. Having some spare C4 in your pocket can save your life. As a Monkey, you can still wear a few human items, such as backpacks, gas masks, and hats, and still have two free hands. -As the Malfunctioning AI, you can shunt to an APC if the situation gets bad. This disables your doomsday device if it is active. +As the Malfunctioning AI, you can shunt to an APC if the situation gets bad. As the Malfunctioning AI, you should either order your cyborgs to dismantle the robotics console or blow it up yourself in order to protect them. As an Alien, your melee prowess is unmatched, but your ranged abilities are sorely lacking. Make use of corners to force a melee confrontation! As an Alien, you take double damage from all burn attacks, such as lasers, welding tools, and fires. Furthermore, fire can destroy your resin and eggs. Expose areas to space to starve away any flamethrower fires before they can do damage! @@ -155,11 +153,10 @@ As a Changeling, the Extract DNA sting counts for your genome absorb objective, As a Changeling, you can absorb someone by strangling them and using the Absorb verb; this gives you the ability to rechoose your powers, the DNA of whoever you absorbed, the memory of the absorbed, and some samples of things the absorbed said. As a Cultist, do not cause too much chaos before your objective is completed. If the shuttle gets called too soon, you may not have enough time to win. As a Cultist, your team starts off very weak, but if necessary can quickly convert everything they have into raw power. Make sure you have the numbers and equipment to support going loud, or the cult will fall flat on its face. -As a Cultist, the Blood Boil rune will deal massive amounts of brute damage to non-cultists, and some damage to fellow cultists of Nar'Sie nearby, but will create a fire where the rune stands on use. +As a Cultist, the Blood Boil rune will deal massive amounts of brute damage to non-cultists, and some damage to fellow cultists nearby, but will create a fire where the rune stands on use. As a Cultist, you can create an army of manifested goons using a combination of the Manifest rune, which creates homunculi from ghosts, and the Blood Drain rune, which drains life from anyone standing on any blood drain rune. -As a Cultist, check the alert in the upper-right of your screen for all the details about your cult's current status and objective. You can deconvert Cultists by feeding them large amounts of holy water. -The Chaplain can bless any container with water by hitting it with their bible. Holy water has a myriad of uses against both cults and large amounts of it are a great contributor to success against them. +The Chaplain can bless any container with water by hitting it with their bible. Holy water has a myriad of uses against cults and large amounts of it are a great contributor to success against them. As a Wizard, you can turn people to stone, then animate the resulting statue with a staff of animation to create an extremely powerful minion, for all of 5 minutes at least. As a Wizard, the fireball spell performs very poorly at close range, as it can easily catch you in the blast. It is best used as a form of artillery down long hallways. As a Wizard, summoning guns will give everyone anything from a floral somatoray to a pulse rifle. Use at your own risk! @@ -170,17 +167,16 @@ As an Abductor Agent, the combat mode vest has much higher resistance to every k As an Abductor, the baton can cycle between four modes: stun, sleep, cuff and probe. As a Revenant, the Chaplain is your worst enemy, as they can damage you massively with the null rod and make large swaths of the station impassable with holy water. As a Revenant, your essence is also your health, so revealing yourself in front of humans to harvest the essence of the living is much safer if you've already stocked up on essences from poorly guarded corpses. -As a Revenant, your Defile ability removes holy water from tiles in a small radius, along with salt, allowing you to reclaim the station from the chaplain if they've been covering the station in holy water. It can also be used to open morgue trays! +As a Revenant, your Defile ability removes holy water from tiles in a small radius, allowing you to reclaim the station from the chaplain if they've been covering the station in holy water. As a Revenant, your Overload Lights ability will only shock humans with lights if the lights are still on after a brief delay. As a Revenant, your Malfunction ability in general damages machinery and mechanical objects, possibly even emagging some objects. Experiment! -As a Revenant, the illness inflicted on humans by Blight can be easily cured by lying down or with holy water, making it best used on targets that have no time to lie down, such as humans in combat. -As a Revenant, fastmos is your friend. Breaking windows to space can cause massive damage and mayhem. +As a Revenant, space is your friend. Breaking windows to space can cause massive damage and mayhem. As a Swarmer, you can deconstruct more things than you think. Try deconstructing light switches, buttons, air alarms and more. Experiment! As a Swarmer, you can teleport fellow swarmers away if you think they are in danger. As a Ghost, you can double click on just about anything to follow it. Or just warp around! As a Devil, you gain power for every three souls you control, however you also become more obvious. As a Devil, as long as you control at least one other soul, you will automatically resurrect, as long as a banishment ritual is not performed. -At which time a Devil's nameth is spake on the tongue of man, the Devil may appeareth. +At which time a Devil's nameth is spake on the tongue of man, the Devil may appeareth. As a Security Officer, remember that correlation does not equal causation. Someone may have just been at the wrong place at the wrong time! As a Security Officer, remember that you can attach a sec-lite to your taser! As a Terror Spider, leave wrapping of corpses to green spiders unless absolutely necessary. They need it to lay eggs! @@ -202,4 +198,4 @@ You can make lasertag turrets, for the ultimate lasertag tournament. Blob structures take half damage from brute damage. Use lasers. You can hide paper in vents, but you have to use a screwdriver to open it first. While the Standard Operating Procedures aren't fully rules, they are there for safety and professional reasons. -Killing the Wizard usually ends the round, unless they are a lich or Space Wizard Federation is RAGING. \ No newline at end of file +Killing the Wizard usually ends the round, unless they are a lich or Space Wizard Federation is RAGING. diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml index 67e74085c7e..595a6978f91 100644 --- a/tgui/.eslintrc.yml +++ b/tgui/.eslintrc.yml @@ -366,13 +366,13 @@ rules: ## Enforce a maximum depth that blocks can be nested # max-depth: error ## Enforce a maximum line length - max-len: [error, { - code: 80, + #max-len: [error, { + # code: 80, ## Ignore imports - ignorePattern: '^(import\s.+\sfrom\s|.*require\()', - ignoreUrls: true, - ignoreRegExpLiterals: true, - }] + #ignorePattern: '^(import\s.+\sfrom\s|.*require\()', + #ignoreUrls: true, + #ignoreRegExpLiterals: true, + #}] ## Enforce a maximum number of lines per file # max-lines: error ## Enforce a maximum number of line of code in a function diff --git a/tgui/docs/tutorial-and-examples.md b/tgui/docs/tutorial-and-examples.md index 08bb6f92321..590ea0f0ee7 100644 --- a/tgui/docs/tutorial-and-examples.md +++ b/tgui/docs/tutorial-and-examples.md @@ -37,7 +37,7 @@ powerful interactions for embedded objects or remote access. Let's start with a very basic hello world. ```dm -/obj/machinery/my_machine/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) +/obj/machinery/my_machine/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "my_machine", name, 300, 300, master_ui, state) @@ -240,7 +240,7 @@ and builds a new array based on what was returned by that function. ``` If you need more examples of what you can do with React, see the -[interface conversion guide](docs/converting-old-tgui-interfaces.md). +[interface conversion guide](docs/converting-old-nano-interfaces.md). #### Splitting UIs into smaller, modular components @@ -294,7 +294,7 @@ here's what you need (note that you'll probably be forced to clean your shit up upon code review): ```dm -/obj/copypasta/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) // Remember to use the appropriate state. +/obj/copypasta/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state) // Remember to use the appropriate state. ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "copypasta", name, 300, 300, master_ui, state) diff --git a/tgui/packages/tgui/components/Box.js b/tgui/packages/tgui/components/Box.js index 9d5e71c78a7..ada17f05d55 100644 --- a/tgui/packages/tgui/components/Box.js +++ b/tgui/packages/tgui/components/Box.js @@ -68,7 +68,10 @@ const mapColorPropTo = attrName => (style, value) => { const styleMapperByPropName = { // Direct mapping + display: mapRawPropTo('display'), position: mapRawPropTo('position'), + float: mapRawPropTo('float'), + clear: mapRawPropTo('clear'), overflow: mapRawPropTo('overflow'), overflowX: mapRawPropTo('overflow-x'), overflowY: mapRawPropTo('overflow-y'), @@ -88,6 +91,9 @@ const styleMapperByPropName = { opacity: mapRawPropTo('opacity'), textAlign: mapRawPropTo('text-align'), verticalAlign: mapRawPropTo('vertical-align'), + textTransform: mapRawPropTo('text-transform'), + wordWrap: mapRawPropTo('word-wrap'), + textOverflow: mapRawPropTo('text-overflow'), // Boolean props inline: mapBooleanPropTo('display', 'inline-block'), bold: mapBooleanPropTo('font-weight', 'bold'), @@ -125,6 +131,18 @@ const styleMapperByPropName = { color: mapColorPropTo('color'), textColor: mapColorPropTo('color'), backgroundColor: mapColorPropTo('background-color'), + // Flex props + order: mapRawPropTo('order'), + flexDirection: mapRawPropTo('flex-direction'), + flexGrow: mapRawPropTo('flex-grow'), + flexShrink: mapRawPropTo('flex-shrink'), + flexWrap: mapRawPropTo('flex-wrap'), + flexFlow: mapRawPropTo('flex-flow'), + flexBasis: mapRawPropTo('flex-basis'), + flex: mapRawPropTo('flex'), + alignItems: mapRawPropTo('align-items'), + justifyContent: mapRawPropTo('justify-content'), + alignSelf: mapRawPropTo('align-self'), // Utility props fillPositionedParent: (style, value) => { if (value) { @@ -140,6 +158,9 @@ const styleMapperByPropName = { export const computeBoxProps = props => { const computedProps = {}; const computedStyles = {}; + if (props.double) { + computedStyles["transform"] = "scale(2);"; + } // Compute props for (let propName of Object.keys(props)) { if (propName === 'style') { diff --git a/tgui/packages/tgui/components/Button.js b/tgui/packages/tgui/components/Button.js index e5172453432..06e31e5f54e 100644 --- a/tgui/packages/tgui/components/Button.js +++ b/tgui/packages/tgui/components/Button.js @@ -23,6 +23,7 @@ export const Button = props => { ellipsis, content, iconRotation, + iconColor, iconSpin, children, onclick, @@ -81,7 +82,10 @@ export const Button = props => { }} {...rest}> {icon && ( - + )} {content} {children} diff --git a/tgui/packages/tgui/components/Collapsible.js b/tgui/packages/tgui/components/Collapsible.js index 84af070fe18..080074e48ef 100644 --- a/tgui/packages/tgui/components/Collapsible.js +++ b/tgui/packages/tgui/components/Collapsible.js @@ -22,7 +22,7 @@ export class Collapsible extends Component { ...rest } = props; return ( - +
    ); } } diff --git a/tgui/packages/tgui/components/Flex.js b/tgui/packages/tgui/components/Flex.js index e63051438f3..26de30ef93c 100644 --- a/tgui/packages/tgui/components/Flex.js +++ b/tgui/packages/tgui/components/Flex.js @@ -8,9 +8,11 @@ export const computeFlexProps = props => { direction, wrap, align, + alignContent, justify, inline, spacing = 0, + spacingPrecise = 0, ...rest } = props; return { @@ -23,6 +25,7 @@ export const computeFlexProps = props => { ), inline && 'Flex--inline', spacing > 0 && 'Flex--spacing--' + spacing, + spacingPrecise > 0 && 'Flex--spacingPrecise--' + spacingPrecise, className, ]), style: { @@ -30,6 +33,7 @@ export const computeFlexProps = props => { 'flex-direction': direction, 'flex-wrap': wrap, 'align-items': align, + 'align-content': alignContent, 'justify-content': justify, }, ...rest, diff --git a/tgui/packages/tgui/components/Input.js b/tgui/packages/tgui/components/Input.js index 3c73d88a8fd..b7f2dcb1f15 100644 --- a/tgui/packages/tgui/components/Input.js +++ b/tgui/packages/tgui/components/Input.js @@ -76,6 +76,11 @@ export class Input extends Component { const input = this.inputRef.current; if (input) { input.value = toInputValue(nextValue); + if (this.props.autofocus) { + input.focus(); + input.selectionStart = 0; + input.selectionEnd = input.value.length; + } } } @@ -104,6 +109,7 @@ export class Input extends Component { value, maxLength, placeholder, + autofocus, ...boxProps } = props; // Box props diff --git a/tgui/packages/tgui/components/Knob.js b/tgui/packages/tgui/components/Knob.js index 501308aea8d..0e817111653 100644 --- a/tgui/packages/tgui/components/Knob.js +++ b/tgui/packages/tgui/components/Knob.js @@ -35,6 +35,7 @@ export const Knob = props => { size, bipolar, children, + popUpPosition, ...rest } = props; return ( @@ -102,7 +103,10 @@ export const Knob = props => {
    {dragging && ( -
    +
    {displayElement}
    )} diff --git a/tgui/packages/tgui/components/LabeledList.js b/tgui/packages/tgui/components/LabeledList.js index e2b515014a2..43dbd0d5a32 100644 --- a/tgui/packages/tgui/components/LabeledList.js +++ b/tgui/packages/tgui/components/LabeledList.js @@ -20,6 +20,7 @@ export const LabeledListItem = props => { labelColor = 'label', color, textAlign, + verticalAlign, buttons, content, children, @@ -33,6 +34,7 @@ export const LabeledListItem = props => { { as="td" color={color} textAlign={textAlign} + verticalAlign={verticalAlign} className={classes([ 'LabeledList__cell', 'LabeledList__content', diff --git a/tgui/packages/tgui/components/Modal.js b/tgui/packages/tgui/components/Modal.js index 916150de402..125593902b6 100644 --- a/tgui/packages/tgui/components/Modal.js +++ b/tgui/packages/tgui/components/Modal.js @@ -6,10 +6,20 @@ export const Modal = props => { const { className, children, + onEnter, ...rest } = props; + let handleKeyDown; + if (onEnter) { + handleKeyDown = e => { + if (e.keyCode === 13) { + onEnter(e); + } + }; + } return ( - +
    { - const { config } = useBackend(context); - const { onClick } = props; - return ( - - - - ); +const pauseEvent = e => { + if (e.stopPropagation) { e.stopPropagation(); } + if (e.preventDefault) { e.preventDefault(); } + e.cancelBubble = true; + e.returnValue = false; + return false; }; -const NanoMapMarker = props => { +export class NanoMap extends Component { + constructor(props) { + super(props); + + // Auto center based on window size + const Xcenter = (window.innerWidth / 2) - 256; + const Ycenter = (window.innerHeight / 2) - 256; + + this.state = { + offsetX: 128, + offsetY: 48, + transform: 'none', + dragging: false, + originX: null, + originY: null, + zoom: 1, + }; + + // Dragging + this.handleDragStart = e => { + this.ref = e.target; + this.setState({ + dragging: false, + originX: e.screenX, + originY: e.screenY, + }); + document.addEventListener('mousemove', this.handleDragMove); + document.addEventListener('mouseup', this.handleDragEnd); + pauseEvent(e); + }; + + this.handleDragMove = e => { + this.setState(prevState => { + const state = { ...prevState }; + const newOffsetX = e.screenX - state.originX; + const newOffsetY = e.screenY - state.originY; + if (prevState.dragging) { + state.offsetX += newOffsetX; + state.offsetY += newOffsetY; + state.originX = e.screenX; + state.originY = e.screenY; + } else { + state.dragging = true; + } + return state; + }); + pauseEvent(e); + }; + + this.handleDragEnd = e => { + this.setState({ + dragging: false, + originX: null, + originY: null, + }); + document.removeEventListener('mousemove', this.handleDragMove); + document.removeEventListener('mouseup', this.handleDragEnd); + pauseEvent(e); + }; + + this.handleZoom = (_e, value) => { + this.setState(state => { + const newZoom = Math.min(Math.max(value, 1), 8); + let zoomDiff = (newZoom - state.zoom) * 1.5; + state.zoom = newZoom; + state.offsetX = state.offsetX - 262 * zoomDiff; + state.offsetY = state.offsetY - 256 * zoomDiff; + if (props.onZoom) { + props.onZoom(state.zoom); + } + return state; + }); + }; + + } + + render() { + const { config } = useBackend(this.context); + const { dragging, offsetX, offsetY, zoom = 1 } = this.state; + const { children } = this.props; + + const mapUrl = config.map + "_nanomap_z1.png"; + const mapSize = (510 * zoom) + 'px'; + const newStyle = { + width: mapSize, + height: mapSize, + "margin-top": offsetY + "px", + "margin-left": offsetX + "px", + "overflow": "hidden", + "position": "relative", + "background-image": "url(" + mapUrl + ")", + "background-size": "cover", + "background-repeat": "no-repeat", + "text-align": "center", + "cursor": dragging ? "move" : "auto", + }; + + return ( + + + + {children} + + + + + ); + } +} + +const NanoMapMarker = (props, context) => { const { x, y, + zoom = 1, icon, tooltip, color, } = props; + const rx = ((x * 2 * zoom) - zoom) - 3; + const ry = ((y * 2 * zoom) - zoom) - 3; return ( - - - - +
    + + + + +
    ); }; NanoMap.Marker = NanoMapMarker; + +const NanoMapZoomer = (props, context) => { + return ( + + + + v + "x"} + value={props.zoom} + onDrag={(e, v) => props.onZoom(e, v)} + /> + + + + ); +}; + +NanoMap.Zoomer = NanoMapZoomer; diff --git a/tgui/packages/tgui/components/Section.js b/tgui/packages/tgui/components/Section.js index a9e60a54d98..5c0d9dd4748 100644 --- a/tgui/packages/tgui/components/Section.js +++ b/tgui/packages/tgui/components/Section.js @@ -8,7 +8,10 @@ export const Section = props => { level = 1, buttons, content, + stretchContents, + noTopPadding, children, + scrollable, ...rest } = props; const hasTitle = !isFalsy(title) || !isFalsy(buttons); @@ -18,6 +21,7 @@ export const Section = props => { className={classes([ 'Section', 'Section--level--' + level, + props.flexGrow && 'Section--flex', className, ])} {...rest}> @@ -32,10 +36,14 @@ export const Section = props => {
    )} {hasContent && ( -
    + {content} {children} -
    +
    )} ); diff --git a/tgui/packages/tgui/components/TimeDisplay.js b/tgui/packages/tgui/components/TimeDisplay.js new file mode 100644 index 00000000000..a50d8b0090c --- /dev/null +++ b/tgui/packages/tgui/components/TimeDisplay.js @@ -0,0 +1,13 @@ +// see seconds_to_clock +const secondsToClock = totalSeconds => { + if (!totalSeconds || totalSeconds < 0) { totalSeconds = 0; } + + let minutes = Math.floor(totalSeconds / 60).toString(10); + let seconds = (Math.floor(totalSeconds) % 60).toString(10); + + return [minutes, seconds].map(p => p.length < 2 ? '0' + p : p).join(':'); +}; + +export const TimeDisplay = ({ totalSeconds = 0 }) => ( + secondsToClock(totalSeconds) +); diff --git a/tgui/packages/tgui/components/index.js b/tgui/packages/tgui/components/index.js index 888c852ca5a..d51773c1943 100644 --- a/tgui/packages/tgui/components/index.js +++ b/tgui/packages/tgui/components/index.js @@ -26,3 +26,4 @@ export { Slider } from './Slider'; export { Table } from './Table'; export { Tabs } from './Tabs'; export { Tooltip } from './Tooltip'; +export { TimeDisplay } from './TimeDisplay'; diff --git a/tgui/packages/tgui/constants.js b/tgui/packages/tgui/constants.js index 20ba00cb759..0f2fc9aa6b7 100644 --- a/tgui/packages/tgui/constants.js +++ b/tgui/packages/tgui/constants.js @@ -8,14 +8,15 @@ export const UI_CLOSE = -1; export const COLORS = { // Department colors department: { - captain: '#c06616', - security: '#e74c3c', - medbay: '#3498db', - science: '#9b59b6', - engineering: '#f1c40f', - cargo: '#f39c12', - centcom: '#00c100', - other: '#c38312', + command: '#526aff', + security: '#CF0000', + medical: '#009190', + science: '#993399', + engineering: '#A66300', + supply: '#9F8545', + service: '#80A000', + centcom: '#78789B', + other: '#C38312', }, // Damage type colors damageType: { @@ -54,6 +55,11 @@ export const RADIO_CHANNELS = [ freq: 1213, color: '#a52a2a', }, + { + name: 'SyndTeam', + freq: 1244, + color: '#a52a2a', + }, { name: 'Red Team', freq: 1215, @@ -65,8 +71,13 @@ export const RADIO_CHANNELS = [ color: '#3434fd', }, { - name: 'CentCom', - freq: 1337, + name: 'Response Team', + freq: 1345, + color: '#2681a5', + }, + { + name: 'Special Ops', + freq: 1341, color: '#2681a5', }, { @@ -94,6 +105,11 @@ export const RADIO_CHANNELS = [ freq: 1355, color: '#57b8f0', }, + { + name: 'Medical(I)', + freq: 1485, + color: '#57b8f0', + }, { name: 'Engineering', freq: 1357, @@ -104,9 +120,14 @@ export const RADIO_CHANNELS = [ freq: 1359, color: '#dd3535', }, + { + name: 'Security(I)', + freq: 1475, + color: '#dd3535', + }, { name: 'AI Private', - freq: 1447, + freq: 1343, color: '#d65d95', }, { diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js index 59199f9edbe..af9d6c6d7be 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.js @@ -3,30 +3,31 @@ import 'core-js/es'; import 'core-js/web/immediate'; import 'core-js/web/queue-microtask'; import 'core-js/web/timers'; -import 'regenerator-runtime/runtime'; -import './polyfills/html5shiv'; -import './polyfills/ie8'; -import './polyfills/dom4'; -import './polyfills/css-om'; -import './polyfills/inferno'; - -// Themes -import './styles/main.scss'; -import './styles/themes/cardtable.scss'; -import './styles/themes/malfunction.scss'; -import './styles/themes/ntos.scss'; -import './styles/themes/hackerman.scss'; -import './styles/themes/retro.scss'; -import './styles/themes/syndicate.scss'; - import { loadCSS } from 'fg-loadcss'; import { render } from 'inferno'; +import 'regenerator-runtime/runtime'; import { setupHotReloading } from 'tgui-dev-server/link/client'; import { backendUpdate } from './backend'; import { IS_IE8 } from './byond'; import { setupDrag } from './drag'; import { logger } from './logging'; +import './polyfills/css-om'; +import './polyfills/dom4'; +import './polyfills/html5shiv'; +import './polyfills/ie8'; +import './polyfills/inferno'; import { createStore, StoreProvider } from './store'; +// Themes +import './styles/main.scss'; +import './styles/themes/cardtable.scss'; +import './styles/themes/hackerman.scss'; +import './styles/themes/malfunction.scss'; +import './styles/themes/ntos.scss'; +import './styles/themes/retro.scss'; +import './styles/themes/security.scss'; +import './styles/themes/syndicate.scss'; + + const enteredBundleAt = Date.now(); const store = createStore(); diff --git a/tgui/packages/tgui/interfaces/AICard.js b/tgui/packages/tgui/interfaces/AICard.js new file mode 100644 index 00000000000..8ffe9906e2c --- /dev/null +++ b/tgui/packages/tgui/interfaces/AICard.js @@ -0,0 +1,94 @@ +import { useBackend } from "../backend"; +import { Button, ProgressBar, LabeledList, Box, Section } from "../components"; +import { Window } from "../layouts"; + +export const AICard = (props, context) => { + const { act, data } = useBackend(context); + if (data.has_ai === 0) { + return ( + + +
    + +

    No AI detected.

    +
    +
    +
    +
    + ); + } else { + + let integrityColor = null; // Handles changing color of the integrity bar + if (data.integrity >= 75) { integrityColor = 'green'; } + else if (data.integrity >= 25) { integrityColor = 'yellow'; } + else { integrityColor = 'red'; } + + return ( + + +
    + +

    {data.name}

    +
    + + + + + + + + +

    {data.flushing === 1 ? "Wipe of AI in progress..." : ""}

    +
    +
    + +
    + {!!data.has_laws && ( + + {data.laws.map((value, key) => ( + + {value} + + ))} + + ) || ( // Else, no laws. + +

    No laws detected.

    +
    + )} +
    + +
    + + +
    +
    +
    + ); + } +}; diff --git a/tgui/packages/tgui/interfaces/AIFixer.js b/tgui/packages/tgui/interfaces/AIFixer.js new file mode 100644 index 00000000000..7a7d65101d5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AIFixer.js @@ -0,0 +1,110 @@ +import { useBackend } from "../backend"; +import { Button, ProgressBar, Box, LabeledList, Section } from "../components"; +import { Window } from "../layouts"; + +export const AIFixer = (props, context) => { + const { act, data } = useBackend(context); + if (data.occupant === null) { + return ( + + +
    + +

    No artificial intelligence detected.

    +
    +
    +
    +
    + ); + } else { + + let workingAI = null; // If the AI is dead (stat = 2) or isn't existent + if (data.stat === 2 || data.stat === null) { + workingAI = false; + } else { + workingAI = true; + } + + let integrityColor = null; // Handles changing color of the integrity bar + if (data.integrity >= 75) { integrityColor = 'green'; } + else if (data.integrity >= 25) { integrityColor = 'yellow'; } + else { integrityColor = 'red'; } + + let integrityFull = null; // If integrity >= 100, prevents overchar + if (data.integrity >= 100) { integrityFull = true; } + else { integrityFull = false; } + + + return ( + + +
    + +

    {data.occupant}

    +
    +
    + +
    + + + + + + {workingAI ? "Functional" : "Non-Functional"} + + +
    + +
    + {!!data.has_laws && ( + + {data.laws.map((value, key) => ( + + {value} + + ))} + + ) || ( // Else, no laws. + +

    No laws detected.

    +
    + )} +
    + +
    + + +
    +
    +
    + ); + } +}; diff --git a/tgui/packages/tgui/interfaces/APC.js b/tgui/packages/tgui/interfaces/APC.js new file mode 100644 index 00000000000..652188a3073 --- /dev/null +++ b/tgui/packages/tgui/interfaces/APC.js @@ -0,0 +1,195 @@ +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { Box, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../components'; +import { Window } from '../layouts'; +import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; + +export const APC = (props, context) => { + return ( + + + + + + ); +}; + +const powerStatusMap = { + 2: { + color: 'good', + externalPowerText: 'External Power', + chargingText: 'Fully Charged', + }, + 1: { + color: 'average', + externalPowerText: 'Low External Power', + chargingText: 'Charging', + }, + 0: { + color: 'bad', + externalPowerText: 'No External Power', + chargingText: 'Not Charging', + }, +}; + +const malfMap = { + 1: { + icon: 'terminal', + content: 'Override Programming', + action: 'hack', + }, + 2: { + icon: 'caret-square-down', + content: 'Shunt Core Process', + action: 'occupy', + }, + 3: { + icon: 'caret-square-left', + content: 'Return to Main Core', + action: 'deoccupy', + }, + 4: { + icon: 'caret-square-down', + content: 'Shunt Core Process', + action: 'occupy', + }, +}; + +const ApcContent = (props, context) => { + const { act, data } = useBackend(context); + const locked = data.locked && !data.siliconUser; + const normallyLocked = data.normallyLocked; + const externalPowerStatus = powerStatusMap[data.externalPower] + || powerStatusMap[0]; + const chargingStatus = powerStatusMap[data.chargingStatus] + || powerStatusMap[0]; + const channelArray = data.powerChannels || []; + const malfStatus = malfMap[data.malfStatus] || malfMap[0]; + const adjustedCellChange = data.powerCellStatus / 100; + + return ( + + +
    + + act('breaker')} /> + )}> + [ {externalPowerStatus.externalPowerText} ] + + + + + act('charge')} /> + )}> + [ {chargingStatus.chargingText} ] + + +
    +
    + + {channelArray.map(channel => { + const { topicParams } = channel; + return ( + + = 2 ? 'good' : 'bad'}> + {channel.status >= 2 ? 'On' : 'Off'} + +
    +
    + {!!data.malfStatus && ( +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/AiAirlock.js b/tgui/packages/tgui/interfaces/AiAirlock.js new file mode 100644 index 00000000000..ee47422d32d --- /dev/null +++ b/tgui/packages/tgui/interfaces/AiAirlock.js @@ -0,0 +1,202 @@ +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { Button, LabeledList, Section } from '../components'; +import { Window } from '../layouts'; + +const dangerMap = { + 2: { + color: 'good', + localStatusText: 'Offline', + }, + 1: { + color: 'average', + localStatusText: 'Caution', + }, + 0: { + color: 'bad', + localStatusText: 'Optimal', + }, +}; + +export const AiAirlock = (props, context) => { + const { act, data } = useBackend(context); + const statusMain = dangerMap[data.power.main] || dangerMap[0]; + const statusBackup = dangerMap[data.power.backup] || dangerMap[0]; + const statusElectrify = dangerMap[data.shock] || dangerMap[0]; + return ( + + +
    + + act('disrupt-main')} /> + )}> + {data.power.main ? 'Online' : 'Offline'} + {' '} + {(!data.wires.main_power) + && '[Wires have been cut!]' + || (data.power.main_timeleft > 0 + && `[${data.power.main_timeleft}s]`)} + + act('disrupt-backup')} /> + )}> + {data.power.backup ? 'Online' : 'Offline'} + {' '} + {(!data.wires.backup_power) + && '[Wires have been cut!]' + || (data.power.backup_timeleft > 0 + && `[${data.power.backup_timeleft}s]`)} + + +
    +
    + + act('idscan-toggle')} /> + )}> + {!data.wires.id_scanner && '[Wires have been cut!]'} + + act('emergency-toggle')} /> + )} /> + + act('bolt-toggle')} /> + )}> + {!data.wires.bolts && '[Wires have been cut!]'} + + act('light-toggle')} /> + )}> + {!data.wires.lights && '[Wires have been cut!]'} + + act('safe-toggle')} /> + )}> + {!data.wires.safe && '[Wires have been cut!]'} + + act('speed-toggle')} /> + )}> + {!data.wires.timing && '[Wires have been cut!]'} + + + act('open-close')} /> + )}> + {!!(data.locked || data.welded) && ( + + [Door is {data.locked ? 'bolted' : ''} + {(data.locked && data.welded) ? ' and ' : ''} + {data.welded ? 'welded' : ''}!] + + )} + + +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/AtmosAlertConsole.js b/tgui/packages/tgui/interfaces/AtmosAlertConsole.js new file mode 100644 index 00000000000..8db992f61b5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AtmosAlertConsole.js @@ -0,0 +1,47 @@ +import { useBackend } from '../backend'; +import { Button, Section } from '../components'; +import { Window } from '../layouts'; + +export const AtmosAlertConsole = (props, context) => { + const { act, data } = useBackend(context); + const priorityAlerts = data.priority || []; + const minorAlerts = data.minor || []; + return ( + + +
    +
      + {priorityAlerts.length === 0 && ( +
    • + No Priority Alerts +
    • + )} + {priorityAlerts.map(alert => ( +
    • +
    • + ))} + {minorAlerts.length === 0 && ( +
    • + No Minor Alerts +
    • + )} + {minorAlerts.map(alert => ( +
    • +
    • + ))} +
    +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/AtmosFilter.js b/tgui/packages/tgui/interfaces/AtmosFilter.js new file mode 100644 index 00000000000..37b99bfed7f --- /dev/null +++ b/tgui/packages/tgui/interfaces/AtmosFilter.js @@ -0,0 +1,70 @@ +import { useBackend } from "../backend"; +import { Button, Section, NumberInput, LabeledList } from "../components"; +import { Window } from "../layouts"; + +export const AtmosFilter = (props, context) => { + const { act, data } = useBackend(context); + const { + on, + pressure, + max_pressure, + filter_type, + filter_type_list, + } = data; + + return ( + + +
    + + +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/AtmosMixer.js b/tgui/packages/tgui/interfaces/AtmosMixer.js new file mode 100644 index 00000000000..66621ed8f78 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AtmosMixer.js @@ -0,0 +1,109 @@ +import { useBackend } from "../backend"; +import { Button, Section, NumberInput, LabeledList, Flex } from "../components"; +import { Window } from "../layouts"; + +export const AtmosMixer = (props, context) => { + const { act, data } = useBackend(context); + const { + on, + pressure, + max_pressure, + node1_concentration, + node2_concentration, + } = data; + + return ( + + +
    + + +
    +
    +
    + ); +}; + +const NodeControls = (props, context) => { + const { act, data } = useBackend(context); + const { + node_name, + node_ref, + } = props; + + return ( + + + {recipe.max_multiplier >= 10 && ( + + )} + {recipe.max_multiplier >= 25 && ( + + )} + {recipe.max_multiplier > 25 && ( + + )} + + + {recipe.requirements && ( + Object + .keys(recipe.requirements) + .map(mat => toTitleCase(mat) + + ": " + recipe.requirements[mat]) + .join(", ") + ) || ( + + No resources required. + + )} + + + ))} + +
    +
    +
    + + + {metalReadable} + + + {glassReadable} + + + {totalReadable} + + + {data.fill_percent}% Full + + +
    +
    + + {busyname ? busyname : "Nothing"} + +
    +
    + {buildQueueItems} +
    +
    +
    +
    + + + + ); +}; + + diff --git a/tgui/packages/tgui/interfaces/BlueSpaceArtilleryControl.js b/tgui/packages/tgui/interfaces/BlueSpaceArtilleryControl.js new file mode 100644 index 00000000000..adc5b210aab --- /dev/null +++ b/tgui/packages/tgui/interfaces/BlueSpaceArtilleryControl.js @@ -0,0 +1,66 @@ +import { useBackend } from "../backend"; +import { Button, LabeledList, Section, Box, ProgressBar } from "../components"; +import { Window } from "../layouts"; + +export const BlueSpaceArtilleryControl = (props, context) => { + const { act, data } = useBackend(context); + let alertStatus; + if (data.ready) { + alertStatus = ( + + Ready + + ); + } else if (data.reloadtime_text) { + alertStatus = ( + + {data.reloadtime_text} + + ); + } else { + alertStatus = ( + + No cannon connected! + + ); + } + return ( + + +
    + + {data.notice && ( + + {data.notice} + + )} + {alertStatus} + +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/BodyScanner.js b/tgui/packages/tgui/interfaces/BodyScanner.js new file mode 100644 index 00000000000..9e24c62900f --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner.js @@ -0,0 +1,433 @@ +import { round } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { AnimatedNumber, Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, Table, Tooltip } from "../components"; +import { Window } from "../layouts"; + +const stats = [ + ['good', 'Alive'], + ['average', 'Critical'], + ['bad', 'DEAD'], +]; + +const abnormalities = [ + ['hasBorer', 'bad', 'Large growth detected in frontal lobe,' + + ' possibly cancerous. Surgical removal is recommended.'], + ['hasVirus', 'bad', 'Viral pathogen detected in blood stream.'], + ['blind', 'average', 'Cataracts detected.'], + ['colourblind', 'average', 'Photoreceptor abnormalities detected.'], + ['nearsighted', 'average', 'Retinal misalignment detected.'], +]; + +const damages = [ + ['Respiratory', 'oxyLoss'], + ['Brain', 'brainLoss'], + ['Toxin', 'toxLoss'], + ['Radioactive', 'radLoss'], + ['Brute', 'bruteLoss'], + ['Genetic', 'cloneLoss'], + ['Burn', 'fireLoss'], + ['Paralysis', 'paralysis'], +]; + +const damageRange = { + average: [0.25, 0.5], + bad: [0.5, Infinity], +}; + +const mapTwoByTwo = (a, c) => { + let result = []; + for (let i = 0; i < a.length; i += 2) { + result.push(c(a[i], a[i + 1], i)); + } + return result; +}; + +const reduceOrganStatus = A => { + return A.length > 0 + ? A + .filter(s => !!s && s.length > 0) + .reduce((a, s) => a === null + ? [s] + : {a}
    {s}
    , null + ) + : null; +}; + +const germStatus = i => { + if (i > 100) { + if (i < 300) { return "mild infection"; } + if (i < 400) { return "mild infection+"; } + if (i < 500) { return "mild infection++"; } + if (i < 700) { return "acute infection"; } + if (i < 800) { return "acute infection+"; } + if (i < 900) { return "acute infection++"; } + if (i >= 900) { return "septic"; } + } + + return ""; +}; + +export const BodyScanner = (props, context) => { + const { data } = useBackend(context); + const { + occupied, + occupant = {}, + } = data; + const body = occupied ? ( + + ) : ( + + ); + return ( + + + {body} + + + ); +}; + +const BodyScannerMain = props => { + const { + occupant, + } = props; + return ( + + + + + + + + ); +}; + +const BodyScannerMainOccupant = (props, context) => { + const { act, data } = useBackend(context); + const { + occupant, + } = data; + return ( +
    + + + + )}> + + + {occupant.name} + + + + + + {stats[occupant.stat][1]} + + + °C,  + °F + + + {occupant.implant_len ? ( + + {occupant.implant.map(im => im.name).join(', ')} + + ) : ( + + None + + )} + + +
    + ); +}; + +const BodyScannerMainAbnormalities = props => { + const { + occupant, + } = props; + if (!(occupant.hasBorer || occupant.blind + || occupant.colourblind || occupant.nearsighted + || occupant.hasVirus)) { + return ( +
    + + No abnormalities found. + +
    + ); + } + + return ( +
    + {abnormalities.map((a, i) => { + if (occupant[a[0]]) { + return ( + + {a[2]} + + ); + } + })} +
    + ); +}; + +const BodyScannerMainDamage = props => { + const { + occupant, + } = props; + return ( +
    + + {mapTwoByTwo(damages, (d1, d2, i) => ( + + + + {d1[0]}: + + + {!!d2 && d2[0] + ":"} + + + + + + + + {!!d2 && ( + + )} + + + + ))} +
    +
    + ); +}; + +const BodyScannerMainDamageBar = props => { + return ( + + {round(props.value, 0)} + + ); +}; + +const BodyScannerMainOrgansExternal = props => { + if (props.organs.length === 0) { + return ( +
    + + N/A + +
    + ); + } + + return ( +
    + + + + Name + + + Damage + + + Injuries + + + {props.organs.map((o, i) => ( + + + {o.name} + + + 0 && "0.5rem"} + value={o.totalLoss / 100} + ranges={damageRange}> + + {!!o.bruteLoss && ( + + + {round(o.bruteLoss, 0)}  + + )} + {!!o.fireLoss && ( + + + {round(o.fireLoss, 0)} + + )} + + + {round(o.totalLoss, 0)} + + + + + + {reduceOrganStatus([ + o.internalBleeding && "Internal bleeding", + o.lungRuptured && "Ruptured lung", + !!o.status.broken && o.status.broken, + germStatus(o.germ_level), + !!o.open && "Open incision", + ])} + + + {reduceOrganStatus([ + !!o.status.splinted && "Splinted", + !!o.status.robotic && "Robotic", + !!o.status.dead && ( + + DEAD + + ), + ])} + {reduceOrganStatus(o.shrapnel.map( + s => s.known + ? s.name + : "Unknown object" + ))} + + + + ))} +
    +
    + ); +}; + +const BodyScannerMainOrgansInternal = props => { + if (props.organs.length === 0) { + return ( +
    + + N/A + +
    + ); + } + + return ( +
    + + + + Name + + + Damage + + + Injuries + + + {props.organs.map((o, i) => ( + + + {o.name} + + + 0 && "0.5rem"} + ranges={damageRange}> + {round(o.damage, 0)} + + + + + {reduceOrganStatus([ + germStatus(o.germ_level), + ])} + + + {reduceOrganStatus([ + (o.robotic === 1) && "Robotic", + (o.robotic === 2) && "Assisted", + !!o.dead && ( + + DEAD + + ), + ])} + + + + ))} +
    +
    + ); +}; + +const BodyScannerEmpty = () => { + return ( +
    + + +
    + No occupant detected. +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/BrigCells.js b/tgui/packages/tgui/interfaces/BrigCells.js new file mode 100644 index 00000000000..baa3b31d5ed --- /dev/null +++ b/tgui/packages/tgui/interfaces/BrigCells.js @@ -0,0 +1,75 @@ +import { Window } from "../layouts"; +import { TimeDisplay, Box, Button, Flex, Icon, Input, LabeledList, Section, Table, Tabs } from '../components'; +import { useBackend, useLocalState } from "../backend"; + +const BrigCellsTableRow = (properties, context) => { + const { cell } = properties; + const { act } = useBackend(context); + const { + cell_id, + occupant, + crimes, + brigged_by, + time_left_seconds, + time_set_seconds, + ref, + } = cell; + + let className = ''; + if (time_left_seconds > 0) { className += ' BrigCells__listRow--active'; } + + const release = () => { + act('release', { ref }); + }; + + return ( + + {cell_id} + {occupant} + {crimes} + {brigged_by} + + + + + + + ); +}; + +const BrigCellsTable = ({ cells }) => ( + + + Cell + Occupant + Crimes + Brigged By + Time Brigged For + Time Left + Release + + {cells.map(cell => ( + + ))} +
    +); + + +export const BrigCells = (properties, context) => { + const { act, data } = useBackend(context); + const { cells } = data; + + return ( + + + +
    + +
    +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/BrigTimer.js b/tgui/packages/tgui/interfaces/BrigTimer.js new file mode 100644 index 00000000000..5895b333c9c --- /dev/null +++ b/tgui/packages/tgui/interfaces/BrigTimer.js @@ -0,0 +1,123 @@ +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { Button, LabeledList, Dropdown, Box, Section } from '../components'; +import { Window } from '../layouts'; + +export const BrigTimer = (props, context) => { + const { act, data } = useBackend(context); + data.nameText = data.occupant; + if (data.timing) { + if (data.prisoner_hasrec) { + data.nameText = ({data.occupant}); + } else { + data.nameText = ({data.occupant}); + } + } + let nameIcon = "pencil-alt"; + if (data.prisoner_name) { + if (!data.prisoner_hasrec) { + nameIcon = "exclamation-triangle"; + } + } + let nameOptions = []; + let i = 0; + for (i = 0; i < data.spns.length; i++) { + nameOptions.push(data.spns[i]); + } + return ( + + +
    + + + {data.cell_id} + + + {data.nameText} + + + {data.crimes} + + + {data.brigged_by} + + + {data.time_set} + + + {data.time_left} + + + +
    + {!data.timing && ( +
    + + +
    + )} +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/CameraConsole.js b/tgui/packages/tgui/interfaces/CameraConsole.js new file mode 100644 index 00000000000..594de52eb2c --- /dev/null +++ b/tgui/packages/tgui/interfaces/CameraConsole.js @@ -0,0 +1,135 @@ +import { filter, sortBy } from 'common/collections'; +import { flow } from 'common/fp'; +import { classes } from 'common/react'; +import { createSearch } from 'common/string'; +import { Fragment } from 'inferno'; +import { useBackend, useLocalState } from '../backend'; +import { Button, ByondUi, Input, Section } from '../components'; +import { refocusLayout, Window } from '../layouts'; + +/** + * Returns previous and next camera names relative to the currently + * active camera. + */ +const prevNextCamera = (cameras, activeCamera) => { + if (!activeCamera) { + return []; + } + const index = cameras.findIndex(camera => ( + camera.name === activeCamera.name + )); + return [ + cameras[index - 1]?.name, + cameras[index + 1]?.name, + ]; +}; + +/** + * Camera selector. + * + * Filters cameras, applies search terms and sorts the alphabetically. + */ +const selectCameras = (cameras, searchText = '') => { + const testSearch = createSearch(searchText, camera => camera.name); + return flow([ + // Null camera filter + filter(camera => camera?.name), + // Optional search term + searchText && filter(testSearch), + // Slightly expensive, but way better than sorting in BYOND + sortBy(camera => camera.name), + ])(cameras); +}; + +export const CameraConsole = (props, context) => { + const { act, data, config } = useBackend(context); + const { mapRef, activeCamera } = data; + const cameras = selectCameras(data.cameras); + const [ + prevCameraName, + nextCameraName, + ] = prevNextCamera(cameras, activeCamera); + return ( + +
    + + + +
    +
    +
    + Camera: + {activeCamera + && activeCamera.name + || '—'} +
    +
    +
    + +
    +
    + ); +}; + +export const CameraConsoleContent = (props, context) => { + const { act, data } = useBackend(context); + const [ + searchText, + setSearchText, + ] = useLocalState(context, 'searchText', ''); + const { activeCamera } = data; + const cameras = selectCameras(data.cameras, searchText); + return ( + + setSearchText(value)} /> +
    + {cameras.map(camera => ( + // We're not using the component here because performance + // would be absolutely abysmal (50+ ms for each re-render). +
    { + refocusLayout(); + act('switch_camera', { + name: camera.name, + }); + }}> + {camera.name} +
    + ))} +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/Canister.js b/tgui/packages/tgui/interfaces/Canister.js new file mode 100644 index 00000000000..621637b4cc2 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Canister.js @@ -0,0 +1,247 @@ +import { toFixed } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { AnimatedNumber, Box, Button, Dropdown, Icon, Knob, LabeledControls, LabeledList, Section, Tooltip } from '../components'; +import { formatSiUnit } from '../format'; +import { Window } from '../layouts'; + +export const Canister = (props, context) => { + const { act, data } = useBackend(context); + const { + portConnected, + tankPressure, + releasePressure, + defaultReleasePressure, + minReleasePressure, + maxReleasePressure, + valveOpen, + name, + canLabel, + colorContainer, + color_index, + hasHoldingTank, + holdingTank, + } = data; + + + let preset_prim = ""; + if (color_index["prim"]) { + preset_prim = colorContainer.prim.options[color_index["prim"]]["name"]; + } + let preset_sec = ""; + if (color_index["sec"]) { + preset_sec = colorContainer.sec.options[color_index["sec"]]["name"]; + } + let preset_ter = ""; + if (color_index["ter"]) { + preset_ter = colorContainer.ter.options[color_index["ter"]]["name"]; + } + let preset_quart = ""; + if (color_index["quart"]) { + preset_quart + = colorContainer.quart.options[color_index["quart"]]["name"]; + } + + let array_prim = []; + let array_sec = []; + let array_ter = []; + let array_quart = []; + let i = 0; + + for (i = 0; i < colorContainer.prim.options.length; i++) { + array_prim.push(colorContainer.prim.options[i]["name"]); + } + for (i = 0; i < colorContainer.sec.options.length; i++) { + array_sec.push(colorContainer.sec.options[i]["name"]); + } + for (i = 0; i < colorContainer.ter.options.length; i++) { + array_ter.push(colorContainer.ter.options[i]["name"]); + } + for (i = 0; i < colorContainer.quart.options.length; i++) { + array_quart.push(colorContainer.quart.options[i]["name"]); + } + let paintSection = ""; + if (canLabel) { + paintSection = ( +
    + + + act('recolor', + { nc: array_prim.indexOf(value), + ctype: "prim" })} /> + + + act('recolor', + { nc: array_sec.indexOf(value), + ctype: "sec" })} /> + + + act('recolor', + { nc: array_ter.indexOf(value), + ctype: "ter" })} /> + + + act('recolor', + { nc: array_quart.indexOf(value), + ctype: "quart" })} /> + + +
    + ); + } + + return ( + + +
    act('relabel')} /> + )}> + + + { + if (value < 10000) { + return toFixed(value) + ' kPa'; + } + return formatSiUnit(value * 1000, 1, 'Pa'); + }} /> + + + + act('pressure', { + pressure: value, + })} /> +
    +
    act('eject')} /> + )}> + {!!hasHoldingTank && ( + + + {holdingTank.name} + + + kPa + + + )} + {!hasHoldingTank && ( + + No Holding Tank + + )} +
    + {paintSection} +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/CardComputer.js b/tgui/packages/tgui/interfaces/CardComputer.js new file mode 100644 index 00000000000..7e460fe1869 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CardComputer.js @@ -0,0 +1,549 @@ +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { Button, LabeledList, Box, Section, Table, Tabs } from '../components'; +import { Window } from '../layouts'; +import { AccessList } from './common/AccessList'; + +export const CardComputer = (props, context) => { + const { act, data } = useBackend(context); + + + let menuBlock = ( + + act("mode", { mode: 0 })} > + Job Transfers + + {!data.target_dept && ( + act("mode", { mode: 2 })} > + Access Modification + + )} + act("mode", { mode: 1 })}> + Job Management + + act("mode", { mode: 3 })}> + Records + + act("mode", { mode: 4 })}> + Department + + + ); + + + let authBlock = ( +
    + + +
    + ); + + let bodyBlock; + + switch (data.mode) { + case 0: // job transfer + if (!data.authenticated || !data.scan_name) { + bodyBlock = ( +
    + Not logged in. +
    + ); + } else if (!data.modify_name) { + bodyBlock = ( +
    + No card to modify. +
    + ); + } else if (data.target_dept) { + bodyBlock = ( +
    + + {!!data.modify_lastlog && ( + + {data.modify_lastlog} + + )} + + {data.jobs_dept.map(v => ( +
    + ); + } else { + bodyBlock = ( + +
    + +
    +
    + + + {data.jobs_top.map(v => ( +
    +
    + {data.card_skins.map(v => ( +
    +
    + ); + } + break; + case 1: // job slot management + if (!data.authenticated || !data.scan_name) { + bodyBlock = ( +
    + Not logged in. +
    + ); + } else { + bodyBlock = ( + +
    + Next Change Available: + {data.cooldown_time ? data.cooldown_time : "Now"} +
    +
    + + + + + Title + Used Slots + Total Slots + Free Slots + Close Slot + Open Slot + Priority + + {data.job_slots.map(slotData => ( + + + {slotData.title} + + + {slotData.current_positions} + + + {slotData.total_positions} + + + {slotData.total_positions + > slotData.current_positions && ( + + {slotData.total_positions + - slotData.current_positions} + + ) || ( + + 0 + + )} + + +
    +
    +
    + ); + } + break; + case 2: // access change + if (!data.authenticated || !data.scan_name) { + bodyBlock = ( +
    + Not logged in. +
    + ); + } else if (!data.modify_name) { + bodyBlock = ( +
    + No card to modify. +
    + ); + } else { + bodyBlock = ( + act('set', { + access: ref, + })} + grantAll={() => act('grant_all')} + denyAll={() => act('clear_all')} + grantDep={ref => act('grant_region', { + region: ref, + })} + denyDep={ref => act('deny_region', { + region: ref, + })} /> + ); + } + break; + case 3: // records + if (!data.authenticated) { + bodyBlock = ( +
    + Not logged in. +
    + ); + } else if (!data.records.length) { + bodyBlock = ( +
    + No records. +
    + ); + } else { + bodyBlock = ( +
    act('wipe_all_logs')} /> + }> + + + Crewman + Old Rank + New Rank + Authorized By + Time + Reason + {!!data.iscentcom && ( + + Deleted By + + )} + + {data.records.map(record => ( + + {record.transferee} + {record.oldvalue} + {record.newvalue} + {record.whodidit} + {record.timestamp} + {record.reason} + {!!data.iscentcom && ( + + {record.deletedby} + + )} + + ))} +
    + {!!data.iscentcom && ( + +
    + ); + } + break; + case 4: // department + if (!data.authenticated || !data.scan_name) { + bodyBlock = ( +
    + Not logged in. +
    + ); + } else { + bodyBlock = ( +
    + + + Name + Rank + Sec Status + Actions + + {data.people_dept.map(record => ( + + {record.name} + {record.title} + {record.crimstat} + +
    +
    + ); + } + break; + default: + bodyBlock = ( +
    + ERROR: Unknown Mode. +
    + ); + } + + return ( + + + {menuBlock} + {authBlock} + {bodyBlock} + + + ); + +}; diff --git a/tgui/packages/tgui/interfaces/CargoConsole.js b/tgui/packages/tgui/interfaces/CargoConsole.js new file mode 100644 index 00000000000..a9ae19b5e10 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CargoConsole.js @@ -0,0 +1,280 @@ +import { flow } from 'common/fp'; +import { Fragment } from 'inferno'; +import { filter, sortBy } from 'common/collections'; +import { useBackend, useSharedState, useLocalState } from "../backend"; +import { Button, LabeledList, Box, AnimatedNumber, Section, Dropdown, Input, Table, Modal } from "../components"; +import { Window } from "../layouts"; +import { LabeledListItem } from "../components/LabeledList"; +import { createSearch, toTitleCase } from 'common/string'; + +export const CargoConsole = (props, context) => { + return ( + + + + + + + + + ); +}; + +const ContentsModal = (_properties, context) => { + const [ + contentsModal, + setContentsModal, + ] = useLocalState(context, 'contentsModal', null); + + const [ + contentsModalTitle, + setContentsModalTitle, + ] = useLocalState(context, 'contentsModalTitle', null); + if ((contentsModal !== null) && (contentsModalTitle !== null)) { + return ( + +

    {contentsModalTitle} contents:

    + + {contentsModal.map(i => ( + // This needs keying. I hate it. + + - {i} + + ))} + + + + + + + + + ); +}; + +const ChemMasterProductionCondiment = (props, context) => { + const { act } = useBackend(context); + return ( + + + )}> + {hasOccupant ? ( + + + {occupant.name || "Unknown"} + + + 0 ? 'good' : 'average'}> + + + + + {statNames[occupant.stat][1]} + + + + {' K'} + + + {(damageTypes.map(damageType => ( + + + + + + )))} + + ) : ( + + +
    + No occupant detected. +
    +
    + )} + +
    act('ejectBeaker')} + disabled={!isBeakerLoaded}> + Eject Beaker + + )}> + + + + + + K + + + + + + + + + + + + +
    +
    + ); +}; + +const CryoBeaker = (props, context) => { + const { act, data } = useBackend(context); + const { + isBeakerLoaded, + beakerLabel, + beakerVolume, + } = data; + if (isBeakerLoaded) { + return ( + + {beakerLabel + ? beakerLabel + : ( + + No label + + )} + + {beakerVolume ? ( + Math.round(v) + " units remaining"} + /> + ) : "Beaker is empty"} + + + ); + } else { + return ( + + No beaker loaded + + ); + } +}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier.js b/tgui/packages/tgui/interfaces/DNAModifier.js new file mode 100644 index 00000000000..111aa59c8a6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier.js @@ -0,0 +1,711 @@ +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Dimmer, Flex, Icon, Knob, LabeledList, ProgressBar, Section, Tabs } from "../components"; +import { Window } from "../layouts"; +import { ComplexModal } from './common/ComplexModal'; + +const stats = [ + ['good', 'Alive'], + ['average', 'Critical'], + ['bad', 'DEAD'], +]; + +const operations = [ + ['ui', 'Modify U.I.', 'dna'], + ['se', 'Modify S.E.', 'dna'], + ['buffer', 'Transfer Buffers', 'syringe'], + ['rejuvenators', 'Rejuvenators', 'flask'], +]; + +const rejuvenatorsDoses = [5, 10, 20, 30, 50]; + +export const DNAModifier = (props, context) => { + const { act, data } = useBackend(context); + const { + irradiating, + dnaBlockSize, + occupant, + } = data; + context.dnaBlockSize = dnaBlockSize; + context.isDNAInvalid = !occupant.isViableSubject + || !occupant.uniqueIdentity + || !occupant.structuralEnzymes; + let radiatingModal; + if (irradiating) { + radiatingModal = ; + } + return ( + + + {radiatingModal} + + + + + + ); +}; + +const DNAModifierOccupant = (props, context) => { + const { act, data } = useBackend(context); + const { + locked, + hasOccupant, + occupant, + } = data; + return ( +
    + + Door Lock: + +
    + ); +}; + +const DNAModifierMain = (props, context) => { + const { act, data } = useBackend(context); + const { + selectedMenuKey, + hasOccupant, + occupant, + } = data; + if (!hasOccupant) { + return ( +
    + + +
    + No occupant in DNA modifier. +
    +
    +
    + ); + } else if (context.isDNAInvalid) { + return ( +
    + + +
    + No operation possible on this subject. +
    +
    +
    + ); + } + let body; + if (selectedMenuKey === "ui") { + body = ( + + + + + ); + } else if (selectedMenuKey === "se") { + body = ( + + + + + ); + } else if (selectedMenuKey === "buffer") { + body = ; + } else if (selectedMenuKey === "rejuvenators") { + body = ; + } + return ( +
    + + {operations.map((op, i) => ( + act('selectMenuKey', { key: op[0] })}> + + {op[1]} + + ))} + + {body} +
    + ); +}; + +const DNAModifierMainUI = (props, context) => { + const { act, data } = useBackend(context); + const { + selectedUIBlock, + selectedUISubBlock, + selectedUITarget, + occupant, + } = data; + return ( +
    + + + + value.toString(16).toUpperCase()} + ml="0" + onChange={(e, val) => act('changeUITarget', { value: val })} + /> + + +
    + ); +}; + +const DNAModifierMainSE = (props, context) => { + const { act, data } = useBackend(context); + const { + selectedSEBlock, + selectedSESubBlock, + occupant, + } = data; + return ( +
    + +
    + ); +}; + +const DNAModifierMainRadiationEmitter = (props, context) => { + const { act, data } = useBackend(context); + const { + radiationIntensity, + radiationDuration, + } = data; + return ( +
    + + + act('radiationIntensity', { value: val })} + /> + + + act('radiationDuration', { value: val })} + /> + + +
    + ); +}; + +const DNAModifierMainBuffers = (props, context) => { + const { act, data } = useBackend(context); + const { + buffers, + } = data; + let bufferElements = buffers.map((buffer, i) => ( + + )); + return ( + +
    + {bufferElements} +
    + +
    + ); +}; + +const DNAModifierMainBuffersElement = (props, context) => { + const { act, data } = useBackend(context); + const { + id, + name, + buffer, + } = props; + const isInjectorReady = data.isInjectorReady; + const realName = name + (buffer.data ? ' - ' + buffer.label : ''); + return ( + +
    + act('bufferOption', { + option: 'clear', + id: id, + })} + /> +
    +
    + ); +}; + +const DNAModifierMainBuffersDisk = (props, context) => { + const { act, data } = useBackend(context); + const { + hasDisk, + disk, + } = data; + return ( +
    + act('wipeDisk')} + /> +
    + ); +}; + +const DNAModifierMainRejuvenators = (props, context) => { + const { act, data } = useBackend(context); + const { + isBeakerLoaded, + beakerVolume, + beakerLabel, + } = data; + return ( +
    act('ejectBeaker')} + /> + }> + {isBeakerLoaded ? ( + + + {rejuvenatorsDoses.map((a, i) => ( +
    + ); +}; + +const DNAModifierIrradiating = (props, context) => { + return ( + +
    + +

    + +  Irradiating occupant  + +

    +
    + +

    + For {props.duration} second{props.duration === 1 ? "" : "s"} +

    +
    +
    + ); +}; + +const DNAModifierBlocks = (props, context) => { + const { act, data } = useBackend(context); + const { + dnaString, + selectedBlock, + selectedSubblock, + blockSize, + action, + } = props; + + const characters = dnaString.split(''); + let curBlock = 0; + let dnaBlocks = []; + for (let block = 0; block < characters.length; block += blockSize) { + const realBlock = block / blockSize + 1; + let subBlocks = []; + for (let subblock = 0; subblock < blockSize; subblock++) { + const realSubblock = subblock + 1; + subBlocks.push( + + + act("shock", { mt: beacon.uid })} /> + + }> + + + + + + {beacon.cell && ( + + ) || No Cell Installed} + + + {beacon.airtank}kPa + + + {beacon.pilot || "Unoccupied"} + + + {toTitleCase(beacon.location) || "Unknown"} + + + {beacon.active || "None"} + + {beacon.cargoMax && ( + + + + ) || null} + + + )) || ( + No mecha beacons found. + )} + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/MedicalRecords.js b/tgui/packages/tgui/interfaces/MedicalRecords.js new file mode 100644 index 00000000000..92064677c3b --- /dev/null +++ b/tgui/packages/tgui/interfaces/MedicalRecords.js @@ -0,0 +1,422 @@ +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Collapsible, Icon, Input, LabeledList, Section, Tabs } from "../components"; +import { ComplexModal, modalOpen, modalRegisterBodyOverride } from "../interfaces/common/ComplexModal"; +import { Window } from "../layouts"; +import { LoginInfo } from './common/LoginInfo'; +import { LoginScreen } from './common/LoginScreen'; +import { TemporaryNotice } from './common/TemporaryNotice'; + +const severities = { + "Minor": "good", + "Medium": "average", + "Dangerous!": "bad", + "Harmful": "bad", + "BIOHAZARD THREAT!": "bad", +}; + +const doEdit = (context, field) => { + modalOpen(context, 'edit', { + field: field.edit, + value: field.value, + }); +}; + +const virusModalBodyOverride = (modal, context) => { + const virus = modal.args; + return ( +
    + + + + {virus.max_stages} + + + {virus.spread_text} Transmission + + + {virus.cure} + + + {virus.desc} + + + {virus.severity} + + + +
    + ); +}; + +export const MedicalRecords = (_properties, context) => { + const { data } = useBackend(context); + const { + loginState, + screen, + } = data; + if (!loginState.logged_in) { + return ( + + + + + + ); + } + + let body; + if (screen === 2) { // List Records + body = ; + } else if (screen === 3) { // Record Maintenance + body = ; + } else if (screen === 4) { // View Records + body = ; + } else if (screen === 5) { // Virus Database + body = ; + } else if (screen === 6) { // Medbot Tracking + body = ; + } + + return ( + + + + + + +
    + {body} +
    +
    +
    + ); +}; + +const MedicalRecordsList = (_properties, context) => { + const { act, data } = useBackend(context); + const { + records, + } = data; + return ( + + act('search', { t1: value })} + /> + + {records.map((record, i) => ( + + + ); +}; + +export const PoolController = (properties, context) => { + const { data } = useBackend(context); + const { emagged, currentTemp } = data; + const { label: currentLabel, color: currentColor } = TEMPS[currentTemp] || TEMPS.normal; + + const visibleTempKeys = []; + for (const [tempKey, { requireEmag }] of Object.entries(TEMPS)) { + if (!requireEmag || requireEmag && emagged) { + visibleTempKeys.push(tempKey); + } + } + + return ( + + +
    + + + + {currentLabel} + + + + + {emagged ? ( + + WARNING: OVERRIDDEN + + ) : ( + + Nominal + + )} + + +
    + +
    + + {visibleTempKeys.map(tempKey => ( + + ))} + +
    + +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/PortablePump.js b/tgui/packages/tgui/interfaces/PortablePump.js new file mode 100644 index 00000000000..aa0a1385511 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PortablePump.js @@ -0,0 +1,215 @@ +import { useBackend } from "../backend"; +import { Button, Section, LabeledList, Slider, Box, ProgressBar, Flex } from "../components"; +import { Window } from "../layouts"; + +export const PortablePump = (props, context) => { + const { act, data } = useBackend(context); + const { has_holding_tank } = data; + + return ( + + + + + {has_holding_tank ? ( + + ) : ( +
    + + No Holding Tank Inserted. + +
    + )} +
    +
    + ); +}; + +const PumpSettings = (props, context) => { + const { act, data } = useBackend(context); + const { + on, + direction, + port_connected, + } = data; + + return ( +
    + + +
    + ); +}; + +const PressureSettings = (props, context) => { + const { act, data } = useBackend(context); + const { + tank_pressure, + target_pressure, + max_target_pressure, + } = data; + + const average_pressure = max_target_pressure * 0.70; + const bad_pressure = max_target_pressure * 0.25; + + return ( +
    + + + + {tank_pressure} kPa + + + + + + Target pressure: + + +
    + ); +}; + +const HoldingTank = (props, context) => { + const { act, data } = useBackend(context); + const { + holding_tank, + max_target_pressure, + } = data; + + const average_pressure = max_target_pressure * 0.70; + const bad_pressure = max_target_pressure * 0.25; + + return ( +
    act('remove_tank')} + icon="eject"> + Eject + + }> + + + Tank Label: + + + {holding_tank.name} + + + + + Tank Pressure: + + + + {holding_tank.tank_pressure} kPa + + + +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/PortableScrubber.js b/tgui/packages/tgui/interfaces/PortableScrubber.js new file mode 100644 index 00000000000..e51a1445f2b --- /dev/null +++ b/tgui/packages/tgui/interfaces/PortableScrubber.js @@ -0,0 +1,205 @@ +import { useBackend } from "../backend"; +import { Button, Section, LabeledList, Slider, Box, ProgressBar, Flex } from "../components"; +import { Window } from "../layouts"; + +export const PortableScrubber = (props, context) => { + const { act, data } = useBackend(context); + const { has_holding_tank } = data; + + return ( + + + + + {has_holding_tank ? ( + + ) : ( +
    + + No Holding Tank Inserted. + +
    + )} +
    +
    + ); +}; + +const PumpSettings = (props, context) => { + const { act, data } = useBackend(context); + const { + on, + port_connected, + } = data; + + return ( +
    + + + Power: + + +
    + ); +}; + +const PressureSettings = (props, context) => { + const { act, data } = useBackend(context); + const { + tank_pressure, + rate, + max_rate, + } = data; + + const average_pressure = max_rate * 0.70; + const bad_pressure = max_rate * 0.25; + + return ( +
    + + + + {tank_pressure} kPa + + + + + + Target pressure: + + +
    + ); +}; + +const HoldingTank = (props, context) => { + const { act, data } = useBackend(context); + const { + holding_tank, + max_rate, + } = data; + + const average_pressure = max_rate * 0.70; + const bad_pressure = max_rate * 0.25; + + return ( +
    act('remove_tank')} + icon="eject"> + Eject + + }> + + + Tank Label: + + + {holding_tank.name} + + + + + Tank Pressure: + + + + {holding_tank.tank_pressure} kPa + + + +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/PortableTurret.js b/tgui/packages/tgui/interfaces/PortableTurret.js new file mode 100644 index 00000000000..9a86bc4dbf6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PortableTurret.js @@ -0,0 +1,134 @@ +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { Button, LabeledList, NoticeBox, Section } from '../components'; +import { Window } from '../layouts'; +import { AccessList } from './common/AccessList'; + +export const PortableTurret = (props, context) => { + const { act, data } = useBackend(context); + const { + locked, + on, + lethal, + lethal_is_configurable, + targetting_is_configurable, + check_weapons, + neutralize_noaccess, + access_is_configurable, + regions, + selectedAccess, + one_access, + neutralize_norecord, + neutralize_criminals, + neutralize_all, + neutralize_unidentified, + neutralize_cyborgs, + } = data; + return ( + + + + Swipe an ID card to {locked ? 'unlock' : 'lock'} this interface. + +
    + + +
    + {!!targetting_is_configurable && ( + +
    + act('autharrest')} /> + act('authnorecord')} /> + act('authweapon')} /> + act('authaccess')} /> +
    +
    + act('authxeno')} /> + act('authborgs')} /> + act('authsynth')} /> +
    +
    + )} + {!!access_is_configurable && ( + act('set', { + access: ref, + })} + grantAll={() => act('grant_all')} + denyAll={() => act('clear_all')} + grantDep={ref => act('grant_region', { + region: ref, + })} + denyDep={ref => act('deny_region', { + region: ref, + })} /> + )} +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/PowerMonitor.js b/tgui/packages/tgui/interfaces/PowerMonitor.js new file mode 100644 index 00000000000..660c57021db --- /dev/null +++ b/tgui/packages/tgui/interfaces/PowerMonitor.js @@ -0,0 +1,297 @@ +import { map, sortBy } from 'common/collections'; +import { flow } from 'common/fp'; +import { toFixed } from 'common/math'; +import { pureComponentHooks } from 'common/react'; +import { Component, Fragment } from 'inferno'; +import { decodeHtmlEntities } from 'common/string'; +import { Section, Box, Button, Flex, LabeledList, ProgressBar, ColorBox, Chart, Table, Icon } from "../components"; +import { Window } from "../layouts"; +import { useBackend, useLocalState } from "../backend"; +const PEAK_DRAW = 600000; + +export const PowerMonitor = (props, context) => { + return ( + + + + + + ); +}; + +// This constant is so it can be thrown +// into PDAs with minimal effort +export const PowerMonitorMainContent = (props, context) => { + const { act, data } = useBackend(context); + const { + powermonitor, + select_monitor, + } = data; + return ( + + {(!powermonitor && select_monitor) && ( + + )} + {(powermonitor && ( + + ))} + + ); +}; + +const SelectionView = (props, context) => { + const { act, data } = useBackend(context); + const { + powermonitors, + } = data; + + + return ( +
    + {powermonitors.map(p => ( + +
    + ); +}; + +const DataView = (props, context) => { + const { act, data } = useBackend(context); + const { + powermonitor, + history, + apcs, + select_monitor, + } = data; + + const [ + sortByField, + setSortByField, + ] = useLocalState(context, 'sortByField', null); + const supply = history.supply[history.supply.length - 1] || 0; + const demand = history.demand[history.demand.length - 1] || 0; + const supplyData = history.supply.map((value, i) => [i, value]); + const demandData = history.demand.map((value, i) => [i, value]); + const maxValue = Math.max( + PEAK_DRAW, + ...history.supply, + ...history.demand); + // Process area data + const parsedApcs = flow([ + map((apc, i) => ({ + ...apc, + // Generate a unique id + id: apc.name + i, + })), + sortByField === 'name' && sortBy(apc => apc.Name), + sortByField === 'charge' && sortBy(apc => -apc.CellPct), + sortByField === 'draw' && sortBy(apc => -apc.Load), + ])(apcs); + + return ( +
    + {select_monitor && ( +
    + ); +}; + +const AreaCharge = props => { + const { charging, charge } = props; + return ( + + 50 + ? 'battery-half' + : 'battery-quarter' + ) + || charging === "C" && 'bolt' + || charging === "F" && 'battery-full' + )} + color={( + charging === "N" && ( + charge > 50 + ? 'yellow' + : 'red' + ) + || charging === "C" && 'yellow' + || charging === "F" && 'green' + )} /> + + {toFixed(charge) + '%'} + + + ); +}; + +AreaCharge.defaultHooks = pureComponentHooks; + + +const AreaStatusColorBox = props => { + let auto; + let active; + const { status } = props; + switch (status) { + case "AOn": + auto = true; + active = true; + break; + case "AOff": + auto = true; + active = false; + break; + case "On": + auto = false; + active = true; + break; + case "Off": + auto = false; + active = false; + break; + } + const tooltipText = (active ? 'On' : 'Off') + + ` [${auto ? 'auto' : 'manual'}]`; + return ( + + ); +}; + +AreaStatusColorBox.defaultHooks = pureComponentHooks; diff --git a/tgui/packages/tgui/interfaces/Radio.js b/tgui/packages/tgui/interfaces/Radio.js new file mode 100644 index 00000000000..2ab4fa49a60 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Radio.js @@ -0,0 +1,145 @@ +import { map } from 'common/collections'; +import { toFixed } from 'common/math'; +import { useBackend } from '../backend'; +import { Box, Button, Fragment, LabeledList, NumberInput, Section } from '../components'; +import { RADIO_CHANNELS } from '../constants'; +import { Window } from '../layouts'; + +export const Radio = (props, context) => { + const { act, data } = useBackend(context); + const { + freqlock, + frequency, + minFrequency, + maxFrequency, + canReset, + listening, + broadcasting, + loudspeaker, + has_loudspeaker, + } = data; + const tunedChannel = RADIO_CHANNELS + .find(channel => channel.freq === frequency); + let matchedChannel = (tunedChannel && tunedChannel.name) ? true : false; + let colorMap = []; + let rc = []; + let i = 0; + for (i=0; i < RADIO_CHANNELS.length; i++) { + rc = RADIO_CHANNELS[i]; + colorMap[rc["name"]] = rc["color"]; + } + const schannels = map((value, key) => ({ + name: key, + status: !!value, + }))(data.schannels); + const ichannels = map((value, key) => ({ + name: key, + freq: value, + }))(data.ichannels); + return ( + + +
    + + + {freqlock && ( + + {toFixed(frequency / 10, 1) + ' kHz'} + + ) || ( + + toFixed(value, 1)} + onChange={(e, value) => act('frequency', { + adjust: (value - frequency / 10), + })} /> +
    +
    +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/RoboticsControlConsole.js b/tgui/packages/tgui/interfaces/RoboticsControlConsole.js new file mode 100644 index 00000000000..1f342a3f730 --- /dev/null +++ b/tgui/packages/tgui/interfaces/RoboticsControlConsole.js @@ -0,0 +1,143 @@ +import { Fragment } from 'inferno'; +import { useBackend, useSharedState } from '../backend'; +import { Box, Button, LabeledList, ProgressBar, NoticeBox, Section, Tabs } from '../components'; +import { Window } from '../layouts'; + +export const RoboticsControlConsole = (props, context) => { + const { act, data } = useBackend(context); + const { + can_hack, + safety, + show_detonate_all, + cyborgs = [], + } = data; + return ( + + + {!!show_detonate_all && ( +
    +
    + )} + +
    +
    + ); +}; + +const Cyborgs = (props, context) => { + const { cyborgs, can_hack } = props; + const { act, data } = useBackend(context); + if (!cyborgs.length) { + return ( + + No cyborg units detected within access parameters. + + ); + } + return cyborgs.map(cyborg => { + return ( +
    + {!!cyborg.hackable && !cyborg.emagged && ( +
    + ); + }); +}; diff --git a/tgui/packages/tgui/interfaces/SecurityRecords.js b/tgui/packages/tgui/interfaces/SecurityRecords.js new file mode 100644 index 00000000000..b1edc242e11 --- /dev/null +++ b/tgui/packages/tgui/interfaces/SecurityRecords.js @@ -0,0 +1,436 @@ +import { createSearch, decodeHtmlEntities } from 'common/string'; +import { Fragment } from 'inferno'; +import { useBackend, useLocalState } from "../backend"; +import { Box, Button, Flex, Icon, Input, LabeledList, Section, Table, Tabs } from '../components'; +import { FlexItem } from '../components/Flex'; +import { Window } from "../layouts"; +import { ComplexModal, modalOpen } from './common/ComplexModal'; +import { LoginInfo } from './common/LoginInfo'; +import { LoginScreen } from './common/LoginScreen'; +import { TemporaryNotice } from './common/TemporaryNotice'; + +const statusStyles = { + "*Execute*": "execute", + "*Arrest*": "arrest", + "Incarcerated": "incarcerated", + "Parolled": "parolled", + "Released": "released", +}; + +const doEdit = (context, field) => { + modalOpen(context, 'edit', { + field: field.edit, + value: field.value, + }); +}; + +export const SecurityRecords = (properties, context) => { + const { act, data } = useBackend(context); + const { + loginState, + currentPage, + } = data; + + let body; + if (!loginState.logged_in) { + return ( + + + + + + ); + } else { + if (currentPage === 1) { + body = ; + } else if (currentPage === 2) { + body = ; + } else if (currentPage === 3) { + body = ; + } + } + + return ( + + + + + + +
    + {body} +
    +
    +
    + ); +}; + +const SecurityRecordsNavigation = (properties, context) => { + const { act, data } = useBackend(context); + const { + currentPage, + general, + } = data; + return ( + + act('page', { page: 1 })}> + + List Records + + act('page', { page: 2 })}> + + Record Maintenance + + {(currentPage === 3 && general && !general.empty) && ( + + + Record: {general.fields[0].value} + + )} + + ); +}; + +const SecurityRecordsPageList = (properties, context) => { + const { act, data } = useBackend(context); + const { + records, + } = data; + const [searchText, setSearchText] = useLocalState(context, "searchText", ""); + const [sortId, _setSortId] = useLocalState(context, "sortId", "name"); + const [sortOrder, _setSortOrder] = useLocalState(context, "sortOrder", true); + return ( + + +
    + + + Name + ID + Assignment + Fingerprint + Criminal Status + + {records + .filter(createSearch(searchText, record => { + return record.name + "|" + + record.id + "|" + + record.rank + "|" + + record.fingerprint + "|" + + record.status; + })) + .sort((a, b) => { + const i = sortOrder ? 1 : -1; + return a[sortId].localeCompare(b[sortId]) * i; + }) + .map(record => ( + act('view', { + uid_gen: record.uid_gen, + uid_sec: record.uid_sec, + })}> + {record.name} + {record.id} + {record.rank} + {record.fingerprint} + {record.status} + + ))} +
    +
    +
    + ); +}; + +const SortButton = (properties, context) => { + const [sortId, setSortId] = useLocalState(context, "sortId", "name"); + const [sortOrder, setSortOrder] = useLocalState(context, "sortOrder", true); + const { + id, + children, + } = properties; + return ( + + + + ); +}; + +const SecurityRecordsActions = (properties, context) => { + const { act, data } = useBackend(context); + const { + isPrinting, + } = data; + const [searchText, setSearchText] = useLocalState(context, "searchText", ""); + return ( + + + + }> + + {capacityPercent >= 100 && 'Fully Charged' + || inputting && 'Charging' + || 'Not Charging'} + + + + + + + }> + + {outputting + ? 'Sending' + : charge > 0 + ? 'Not Sending' + : 'No Charge'} + + + + + + + + + + + {/* The duplication of the dropdowns is due to the + updates of selected not affecting the state + of the dropdown */} + {regime === REGIME_TELEPORT && ( + act('settarget', + { + x: targetsTeleport[val]["x"], + y: targetsTeleport[val]["y"], + z: targetsTeleport[val]["z"], + })} /> + )} + {regime === REGIME_GATE && ( + act('settarget', + { + x: targetsTeleport[val]["x"], + y: targetsTeleport[val]["y"], + z: targetsTeleport[val]["z"], + })} /> + )} + {regime === REGIME_GPS && ( + + {target} + + )} + + + {target !== 'None' + && ( + + + {calibrating + && ( + + In Progress + + ) || (calibrated + && ( + + Optimal + + ) || ( + + Sub-Optimal + + ))} + + + + + + ))} + + ); + } else if (type === "boolean") { + modalFooter = ( + + + + + {body} + + ); +}; + +const MessengerList = (props, context) => { + const { act, data } = useBackend(context); + + const { + convopdas, + pdas, + charges, + silent, + toff, + } = data; + + return ( + + + + + + + + + + {!toff && ( + + {!!charges && ( + + + {charges} charges left. + + + )} + {!convopdas.length && !pdas.length && ( + + No current conversations + + ) || ( + + + + + )} + + ) || ( + + Messenger Offline. + + )} + + ); +}; + +const PDAList = (props, context) => { + const { act, data } = useBackend(context); + + const { + pdas, + title, + msgAct, + } = props; + + const { + charges, + plugins, + } = data; + + if (!pdas || !pdas.length) { + return ( +
    + No PDAs found. +
    + ); + } + + return ( +
    + {pdas.map(pda => ( + +
    + ); +}; diff --git a/tgui/packages/tgui/interfaces/pda/pda_mob_hunt.js b/tgui/packages/tgui/interfaces/pda/pda_mob_hunt.js new file mode 100644 index 00000000000..7f7956cd2fd --- /dev/null +++ b/tgui/packages/tgui/interfaces/pda/pda_mob_hunt.js @@ -0,0 +1,132 @@ +import { useBackend } from "../../backend"; +import { LabeledList, Box, Button, Section, Flex } from "../../components"; + +export const pda_mob_hunt = (props, context) => { + const { act, data } = useBackend(context); + + const { + connected, + wild_captures, + no_collection, + entry, + } = data; + + return ( + + + + {connected ? ( + + Connected +