Merge branch 'master' into client-hud-consistency

This commit is contained in:
mochi
2020-10-04 20:59:52 +02:00
1378 changed files with 40745 additions and 32097 deletions
+108
View File
@@ -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
+42 -41
View File
@@ -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.
+32
View File
@@ -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 }}
+2 -2
View File
@@ -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
+9
View File
@@ -0,0 +1,9 @@
[langserver]
dreamchecker = true
[code_standards]
disallow_relative_type_definitions = true
disallow_relative_proc_definitions = true
[dmdoc]
use_typepath_names = true
+23 -83
View File
@@ -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
@@ -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
@@ -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
@@ -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
"}
@@ -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
"}
@@ -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
"}
@@ -99,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,
@@ -922,7 +928,7 @@ b
f
f
f
c
f
f
D
m
@@ -964,7 +970,7 @@ f
f
p
Y
f
u
f
j
i
File diff suppressed because it is too large Load Diff
@@ -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,
+18 -52
View File
@@ -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
+8 -43
View File
@@ -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
File diff suppressed because it is too large Load Diff
+9 -1
View File
@@ -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())
"<span class='italics'>You hear ratchet.</span>")
investigate_log("was <span class='warning'>REMOVED</span> 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, "<span class='italics'>Your magboots cling to the floor as a great burst of wind bellows against you.</span>")
else
unsafe_pressure_release(user,internal_pressure)
deconstruct(TRUE)
else
return ..()
@@ -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 <NumberInput> 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))
@@ -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 <NumberInput> 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
@@ -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 <NumberInput> 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
@@ -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
@@ -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
@@ -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
@@ -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, "<span class='danger'>You cannot unwrench [src], it is too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
playsound(loc, W.usesound, 50, 1)
to_chat(user, "<span class='notice'>You begin to unfasten \the [src]...</span>")
if(do_after(user, 40 * W.toolspeed, target = src))
user.visible_message( \
"[user] unfastens \the [src].", \
"<span class='notice'>You have unfastened \the [src].</span>", \
"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
@@ -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
@@ -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))
@@ -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("<span class='danger'>The welded cover of [src] bursts open!</span>")
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("<span class='notice'>[user] welds [src] shut!</span>",\
user.visible_message("<span class='notice'>[user] welds [src] shut!</span>",\
"<span class='notice'>You weld [src] shut!</span>")
else
welded = FALSE
visible_message("<span class='notice'>[user] unwelds [src]!</span>",\
user.visible_message("<span class='notice'>[user] unwelds [src]!</span>",\
"<span class='notice'>You unweld [src]!</span>")
update_icon()
@@ -404,10 +404,10 @@
if(I.use_tool(src, user, 20, volume = I.tool_volume))
if(!welded)
welded = TRUE
visible_message("<span class='notice'>[user] welds [src] shut!</span>",\
user.visible_message("<span class='notice'>[user] welds [src] shut!</span>",\
"<span class='notice'>You weld [src] shut!</span>")
else
welded = FALSE
visible_message("<span class='notice'>[user] unwelds [src]!</span>",\
user.visible_message("<span class='notice'>[user] unwelds [src]!</span>",\
"<span class='notice'>You unweld [src]!</span>")
update_icon()
-17
View File
@@ -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)
+3 -3
View File
@@ -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
+2 -2
View File
@@ -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))
+6
View File
@@ -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
+4
View File
@@ -726,3 +726,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"
+29
View File
@@ -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
+1 -1
View File
@@ -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)
+6
View File
@@ -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
+16
View File
@@ -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 .
+60 -16
View File
@@ -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)
+10
View File
@@ -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
+7
View File
@@ -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
-2
View File
@@ -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
+1
View File
@@ -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
+3 -10
View File
@@ -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
+9 -9
View File
@@ -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.
+8
View File
@@ -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
+77
View File
@@ -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"
+46 -7
View File
@@ -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,8 +443,8 @@
if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE)
. = TRUE
/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)
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)
/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
@@ -454,3 +472,24 @@
if(!C || !C.prefs.windowflashing)
return
winset(C, "mainwindow", "flash=5")
/**
* Get a bounding box of a list of atoms.
*
* Arguments:
* - atoms - List of atoms. Can accept output of view() and range() procs.
*
* Returns: list(x1, y1, x2, y2)
*/
/proc/get_bbox_of_atoms(list/atoms)
var/list/list_x = list()
var/list/list_y = list()
for(var/_a in atoms)
var/atom/a = _a
list_x += a.x
list_y += a.y
return list(
min(list_x),
min(list_y),
max(list_x),
max(list_y))
-3
View File
@@ -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)
+27 -26
View File
@@ -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 <span class='warning'>EXECUTION</span> 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 <span class='warning'>DEMOTE</span> 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
@@ -541,7 +542,7 @@ GLOBAL_LIST_INIT(do_after_once_tracker, list())
to_chat(user, "Name = <b>[M.name]</b>; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = <b>[M.key]</b>;")
to_chat(user, "Location = [location_description];")
to_chat(user, "[special_role_description]")
to_chat(user, "(<a href='?src=[usr.UID()];priv_msg=[M.client ? M.client.UID(): null]'>PM</a>) ([ADMIN_PP(M,"PP")]) ([ADMIN_VV(M,"VV")]) ([ADMIN_TP(M,"TP")]) ([ADMIN_SM(M,"SM")]) ([ADMIN_FLW(M,"FLW")])")
to_chat(user, "(<a href='?src=[usr.UID()];priv_msg=[M.client?.ckey]'>PM</a>) ([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)
+1 -3
View File
@@ -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 &lt;
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
@@ -615,5 +615,3 @@ proc/checkhtml(var/t)
text = replacetext(text, "<td>", "\[cell\]")
text = replacetext(text, "<img src = ntlogo.png>", "\[logo\]")
return text
#define string2charlist(string) (splittext(string, regex("(\\x0A|.)")) - splittext(string, ""))
+2 -2
View File
@@ -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)
+39 -1
View File
@@ -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("<h1>UID Log</h1>", "<p>Current UID: [GLOB.next_unique_datum_id]</p>", "<ul>")
for(var/key in sorted)
text += "<li>[key] - [sorted[key]]</li>"
text += "</ul>"
usr << browse(text.Join(), "window=uidlog")
+44 -36
View File
@@ -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()
@@ -2022,5 +2009,26 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
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()
+1
View File
@@ -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
+4 -1
View File
@@ -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.
+3 -2
View File
@@ -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.).
+68 -66
View File
@@ -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, "<span class='warning'>The electrification wire is cut - Cannot electrify the door.</span>")
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))
+52 -37
View File
@@ -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
+1 -1
View File
@@ -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"
+4
View File
@@ -569,6 +569,10 @@ so as to remain in compliance with the most up-to-date laws."
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
+3
View File
@@ -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, "<span class='warning'>You are not eligible to become a pAI.</span>")
return
GLOB.paiController.recruitWindow(G)
/datum/hud/ghost
+3 -3
View File
@@ -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"
+170
View File
@@ -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(<mapname> = 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")
+1 -1
View File
@@ -488,7 +488,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()
+5
View File
@@ -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)
+28 -28
View File
@@ -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)
-67
View File
@@ -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 += "<br>"
//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
+44
View File
@@ -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]]!")
+4 -4
View File
@@ -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 += "<td>[EM.name]</td>"
html += "<td><A align='right' href='?src=[UID()];set_weight=\ref[EM]'>[EM.weight]</A></td>"
html += "<td>[EM.min_weight]</td>"
html += "<td>[EM.max_weight]</td>"
html += "<td>[EM.max_weight == INFINITY ? "No max" : EM.max_weight]</td>"
html += "<td><A align='right' href='?src=[UID()];toggle_oneshot=\ref[EM]'>[EM.one_shot]</A></td>"
html += "<td><A align='right' href='?src=[UID()];toggle_enabled=\ref[EM]'>[EM.enabled]</A></td>"
html += "<td><span class='alert'>[EM.get_weight(number_active_with_role())]</span></td>"
+11 -3
View File
@@ -41,9 +41,10 @@ SUBSYSTEM_DEF(ghost_spawns)
* * 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)
log_debug("Polling candidates [role ? "for [get_roletext(role)]" : "\"[question]\""] for [poll_time / 10] seconds")
/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
@@ -59,7 +60,7 @@ SUBSYSTEM_DEF(ghost_spawns)
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))
if(!is_eligible(M, role, antag_age_check, role, min_hours, check_antaghud))
continue
SEND_SOUND(M, 'sound/misc/notice2.ogg')
@@ -125,6 +126,13 @@ SUBSYSTEM_DEF(ghost_spawns)
I.plane = FLOAT_PLANE
A.overlays += I
// Chat message
var/act_jump = ""
if(isatom(source))
act_jump = "<a href='?src=[M.UID()];jump=\ref[source]'>\[Teleport]</a>"
var/act_signup = "<a href='?src=[A.UID()];signup=1'>\[Sign Up]</a>"
to_chat(M, "<big><span class='boldnotice'>Now looking for candidates [role ? "to play as \an [role_cleanname || get_roletext(role)]" : "\"[question]\""]. [act_jump] [act_signup]</span></big>")
// Start processing it so it updates visually the timer
START_PROCESSING(SSprocessing, A)
A.process()
+40 -38
View File
@@ -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)
@@ -663,45 +675,35 @@ SUBSYSTEM_DEF(jobs)
return
var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger)
if(PM && PM.can_receive())
PM.notify("<b>Automated Notification: </b>\"[antext]\" (Unable to Reply)")
PM.notify("<b>Automated Notification: </b>\"[antext]\" (Unable to Reply)", 0) // the 0 means don't make the PDA flash
/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("<b>Automated Notification: </b>\"[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 = "<TABLE border=\"1\">"
var/table_headers = list("Crewman", "Old Rank", "New Rank", "Authorized By", "Time")
var/hidden_fields = list("deletedby")
if(centcom)
table_headers += "<span class='bad'>Deleted By</span>"
record_html += "<TR>"
for(var/thisheader in table_headers)
record_html += "<TD><B>[thisheader]</B></TD>"
record_html += "</TR>"
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 += "<TR>"
var/list/newlist = list()
for(var/lkey in thisrecord)
if(lkey in hidden_fields)
if(centcom)
record_html += "<TD><span class='bad'>[thisrecord[lkey]]<span></TD>"
else
continue
else
record_html += "<TD>[thisrecord[lkey]]</TD>"
record_html += "</TR>"
visible_record_count++
newlist[lkey] = thisrecord[lkey]
formatted.Add(list(newlist))
return formatted
record_html += "</TABLE>"
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
+1 -1
View File
@@ -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)
@@ -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]"]
+15 -8
View File
@@ -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 = "<center>---[station_time_timestamp()]---</center><br>Remember to stamp and send back the supply manifests.<hr>"
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
+165
View File
@@ -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
+2
View File
@@ -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.
+2
View File
@@ -65,6 +65,8 @@ SUBSYSTEM_DEF(ticker)
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
@@ -25,4 +25,4 @@ GLOBAL_REAL(SSmentor_tickets, /datum/controller/subsystem/tickets/mentor_tickets
message_mentorTicket(msg)
/datum/controller/subsystem/tickets/mentor_tickets/create_other_system_ticket(datum/ticket/T)
SStickets.newTicket(T.clientName, T.content, T.title)
SStickets.newTicket(get_client_by_ckey(T.client_ckey), T.content, T.title)
+16 -13
View File
@@ -93,7 +93,7 @@ SUBSYSTEM_DEF(tickets)
var/datum/ticket/T = new(title, passedContent, getTicketCounterAndInc())
allTickets += T
T.clientName = C
T.client_ckey = C.ckey
T.locationSent = C.mob.loc.name
T.mobControlled = C.mob
@@ -139,14 +139,16 @@ SUBSYSTEM_DEF(tickets)
/datum/controller/subsystem/tickets/proc/convert_ticket(datum/ticket/T)
T.ticketState = TICKET_CLOSED
var/client/C = usr.client
to_chat_safe(T.clientName, list("<span class='[span_class]'>[key_name_hidden(C)] has converted your ticket to a [other_ticket_name] ticket.</span>",\
var/client/owner = get_client_by_ckey(T.client_ckey)
to_chat_safe(owner, list("<span class='[span_class]'>[key_name_hidden(C)] has converted your ticket to a [other_ticket_name] ticket.</span>",\
"<span class='[span_class]'>Be sure to use the correct type of help next time!</span>"))
message_staff("<span class='[span_class]'>[C] has converted ticket number [T.ticketNum] to a [other_ticket_name] ticket.</span>")
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)
SSmentor_tickets.newTicket(T.clientName, T.content, T.title)
var/client/C = get_client_by_ckey(T.client_ckey)
SSmentor_tickets.newTicket(C, T.content, T.title)
/datum/controller/subsystem/tickets/proc/autoRespond(N)
if(!check_rights(rights_needed))
@@ -177,7 +179,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
@@ -189,18 +191,18 @@ 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:<span class='adminticketalt'> [message_key] </span>")
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:<span class='adminticketalt'> [message_key] </span>")
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_safe(returnClient(N), "<span class='[span_class]'>[key_name_hidden(C)] is autoresponding with: <span/> <span class='adminticketalt'>[response_phrases[message_key]]</span>")//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:<span class='adminticketalt'> [message_key] </span>") //we want to use the short named keys for this instead of the full sentence which is why we just do message_key
message_staff("[C] has auto responded to [ticket_owner]\'s adminhelp with:<span class='adminticketalt'> [message_key] </span>") //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)
@@ -214,7 +216,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
@@ -222,7 +224,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
@@ -231,7 +233,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]
@@ -244,7 +246,8 @@ 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/list/content // content of the staff help
@@ -379,7 +382,7 @@ UI STUFF
dat += "<h2>Ticket #[T.ticketNum]</h2>"
dat += "<h3>[T.clientName] / [T.mobControlled] opened this [ticket_name] at [T.timeOpened] at location [T.locationSent]</h3>"
dat += "<h3>[T.client_ckey] / [T.mobControlled] opened this [ticket_name] at [T.timeOpened] at location [T.locationSent]</h3>"
dat += "<h4>Ticket Status: <font color='red'>[status]</font>"
dat += "<table style='width:950px; border: 3px solid;'>"
dat += "<tr><td>[T.title]</td></tr>"
+1 -4
View File
@@ -20,7 +20,7 @@
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"
@@ -65,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")
+24 -16
View File
@@ -174,6 +174,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"
@@ -202,9 +210,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"
@@ -244,19 +249,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.<br>If the beacon is still attached, will detach it."
@@ -269,6 +261,9 @@
return FALSE
return ..()
/datum/action/item_action/change_headphones_song
name = "Change Headphones Song"
/datum/action/item_action/toggle
/datum/action/item_action/toggle/New(Target)
@@ -475,6 +470,7 @@
/datum/action/spell_action
check_flags = 0
background_icon_state = "bg_spell"
var/recharge_text_color = "#FFFFFF"
/datum/action/spell_action/New(Target)
..()
@@ -512,6 +508,18 @@
return spell.can_cast(owner)
return FALSE
/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 = "<div style=\"font-size:6pt;color:[recharge_text_color];font:'Small Fonts';text-align:center;\" valign=\"bottom\">[round_down(progress * 100)]%</div>"
button.color = rgb(col_val_high, col_val_low, col_val_low, col_val_high)
else
button.maptext = null
/*
/datum/action/spell_action/alien
+5 -1
View File
@@ -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
+13 -1
View File
@@ -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)
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+17 -10
View File
@@ -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, "<span class='warning'>You cant reach [target_turf].</span>")
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, "<span class='notice'>You stick [I] to [target_turf].</span>")
I.pixel_x = x_offset
I.pixel_y = y_offset
+139
View File
@@ -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.
+1 -1
View File
@@ -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)
+58
View File
@@ -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, "<font color='red' size='4'><b>Your ears weren't meant for this spectral sound.</b></font>")
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, "<font color='red' size='4'><B>DOOT</B></font>")
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("<span class='warning'>[H] has given up on life as a mortal.</span>")
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, "<span class='boldwarning'>The spooky gods forgot to ship your instrument. Better luck next unlife.</span>")
to_chat(H, "<span class='boldnotice'>You are the spooky skeleton!</span>")
to_chat(H, "<span class='boldnotice'>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.</span>")
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
+8
View File
@@ -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()
+5 -5
View File
@@ -278,8 +278,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 +319,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 +518,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)
+8
View File
@@ -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."
+5 -7
View File
@@ -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
+8 -4
View File
@@ -3,8 +3,8 @@
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
@@ -12,9 +12,13 @@
who = get_subject_text(_who, _log_type)
what = _what
target = get_subject_text(_target, _log_type)
if(!_where)
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
+26 -9
View File
@@ -4,6 +4,9 @@ if(!result || result.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
@@ -23,7 +26,7 @@ if(!result || result.ckey != __ckey){\
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()
@@ -47,8 +50,8 @@ if(!result || result.ckey != __ckey){\
continue
log_records.Add(logs.Copy(start_index, end_index + 1))
if(invalid_mobs.len)
to_chat(usr, "<span class='warning'>The search criteria contained invalid mobs. They have been removed from the criteria.</span>")
if(length(invalid_mobs))
to_chat(user, "<span class='warning'>The search criteria contained invalid mobs. They have been removed from the criteria.</span>")
for(var/i in invalid_mobs)
selected_mobs -= i // Cleanup
@@ -103,13 +106,13 @@ if(!result || result.ckey != __ckey){\
return 0
/datum/log_viewer/proc/add_mobs(list/mob/mobs)
if(!mobs?.len)
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 || !user)
if(!user || !ckey)
return
selected_ckeys |= ckey
UPDATE_CKEY_MOB(ckey)
@@ -127,7 +130,7 @@ if(!result || result.ckey != __ckey){\
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
var/list/dat = list()
dat += "<head><meta http-equiv='X-UA-Compatible' content='IE=edge'><style>.adminticket{border:2px solid} td{border:1px solid grey;} th{border:1px solid grey;} span{float:left;width:150px;}</style></head>"
dat += "<div style='min-height:100px'>"
dat += "<span>Time Search Range:</span> <a href='?src=[UID()];start_time=1'>[gameTimestamp(wtime = time_from)]</a>"
@@ -184,13 +187,12 @@ if(!result || result.ckey != __ckey){\
dat +="<tr style='[trStyle]'><td style='[tdStyleTime]'>[time]</td><td style='[tdStyleType]background: [get_logtype_color(L.log_type)]'>[L.log_type]</td>\
<td style='[tdStyleWho]'>[L.who]</td><td style='background: [get_logtype_color(L.log_type)];'>[L.what]</td>\
<td style='[tdStyleWho]'>[L.target]</td><td style='[tdStyleWhere]'>[ADMIN_COORDJMP(L.where)]</td></tr>"
<td style='[tdStyleWho]'>[L.target]</td><td style='[tdStyleWhere]'>[L.where]</td></tr>"
dat += "</table>"
dat += "</div>"
var/datum/browser/popup = new(user, "Log Viewer", "Log Viewer", 1500, 600)
popup.set_content(dat)
popup.set_content(dat.Join())
popup.open()
/datum/log_viewer/Topic(href, href_list)
@@ -219,6 +221,19 @@ if(!result || result.ckey != __ckey){\
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, "<span class='warning'>Record limit reached. Limiting to [RECORD_HARD_LIMIT].</span>")
log_records.Cut(RECORD_HARD_LIMIT)
show_ui(usr)
return
if(href_list["clear_all"])
@@ -300,3 +315,5 @@ if(!result || result.ckey != __ckey){\
return get_display_name(M)
#undef UPDATE_CKEY_MOB
#undef RECORD_WARN_LIMIT
#undef RECORD_HARD_LIMIT
+1 -1
View File
@@ -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]
+11 -4
View File
@@ -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
@@ -101,6 +102,7 @@
leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it
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
@@ -111,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
@@ -914,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")
@@ -1102,8 +1109,8 @@
special_role = null
to_chat(current,"<span class='userdanger'>Your infernal link has been severed! You are no longer a devil!</span>")
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)
@@ -1501,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)
+3 -3
View File
@@ -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
)
-10
View File
@@ -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

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