Merge remote-tracking branch 'ParadiseSS13/master' into client-data-loading

This commit is contained in:
AffectedArc07
2021-08-12 13:24:13 +01:00
116 changed files with 17492 additions and 20702 deletions

View File

@@ -8,17 +8,20 @@ refer to the [Issue Report](#issues) section, as well as the
[Issue Report Template](ISSUE_TEMPLATE.md).
## Commenting
If you comment on an active pull request, or issue report, make sure your comment is
If you comment on an active pull request or issue report, make sure your comment is
concise and to the point. Comments on issue reports or pull requests should be relevant
and friendly, not attacks on the author or adages about something minimally relevant.
If you believe an issue report is not a "bug", please report it to the Maintainers, or
point out specifically and concisely your reasoning in a comment on the issue report.
If you believe an issue report is not a "bug", please point out specifically and concisely your reasoning in a comment on the issue itself.
#### Guidelines:
* Comments on Pull Requests and Issues should remain relevant to the subject in question and not derail discussions.
* Under no circumstances are users to be attacked for their ideas or contributions. All participants on a given PR or issue are expected to be civil. Failure to do so will result in disciplinary action.<br>
For more details, see the [Code of Conduct](../CODE_OF_CONDUCT.md).
## Issues
The Issues section is not a place to request features, or ask for things to be changed
because you think they should be that way; The Issues section is specifically for
reporting bugs in the code. Refer to ISSUE_TEMPLATE for the exact format that your Issue
should be in.
reporting bugs in the code.
#### Guidelines:
* Issue reports should be as detailed as possible, and if applicable, should include
@@ -57,21 +60,21 @@ actual development.
* 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.
Some examples
```
:cl:
add: The ability to change the color of wires
del: Deleted depreciated wire merging now handled in parent
fix: Moving wires now follows the user input instead of moving the stack
/:cl:
```
```
:cl: N3X15
typo: Fixes some misspelled words under Using Changelog
/:cl:
```
* The tags able to be used in the changelog are: `add/soundadd/imageadd`, `del/sounddel/imagedel`, `tweak`, `fix`, `wip`, `spellcheck`, and `experiment`.
* Without specifying a name it will default to using your GitHub name. Some examples include:
```
:cl:
add: The ability to change the color of wires
del: Deleted depreciated wire merging now handled in parent
fix: Moving wires now follows the user input instead of moving the stack
/:cl:
```
```
:cl: UsernameHere
spellcheck: Fixes some misspelled words under Using Changelog
/:cl:
```
## Specifications
@@ -113,14 +116,13 @@ 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
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 use of this format is **not** allowed in this project, 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:
```DM
/datum/datum1
var/varname1
var/varname1 = 1
var/varname2
var/static/varname3
var/static/varname4
@@ -129,6 +131,7 @@ The previous code made compliant:
code
/datum/datum1/proc/proc2()
code
/datum/datum1/datum2
varname1 = 0
/datum/datum1/datum2/proc/proc3()
@@ -139,11 +142,11 @@ The previous code made compliant:
```
### User Interfaces
All new user interfaces in the game must be created using the TGUI framework. Documentation can be found inside the `tgui/docs` folder.
All new user interfaces in the game must be created using the TGUI framework. Documentation can be found inside the [`tgui/docs`](../tgui/docs) folder, and the [`README.md`](../tgui/README.md) file.
This is to ensure all ingame UIs are snappy and respond well. An exception is made for user interfaces which are purely for OOC actions (Such as character creation, or anything admin related)
### No overriding type safety checks
The use of the : operator to override type safety checks is not allowed. You must cast the variable to the proper type.
The use of the `:` operator to override type safety checks is not allowed. You must cast the variable to the proper type.
### Type paths must begin with a /
eg: `/datum/thing`, not `datum/thing`
@@ -156,11 +159,11 @@ will still be, in actuality, `/datum/arbitrary`. Write your code to reflect this
It is rarely allowed to put type paths in a text format, as there are no compile errors if the type path no longer exists. Here is an example:
```DM
//Good
var/path_type = /obj/item/baseball_bat
//Bad
var/path_type = "/obj/item/baseball_bat"
//Good
var/path_type = /obj/item/baseball_bat
```
### Do not use `\The`.
@@ -170,17 +173,18 @@ The only exception to this rule is when dealing with items "belonging" to a mob,
ever forming.
```DM
//Bad
var/atom/A
"\The [A]"
//Good
var/atom/A
"[A]"
//Bad
"\The [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.
We have a system in [`code/__HELPERS/pronouns.dm`](../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!"`.
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.
@@ -189,14 +193,14 @@ We have a system in code/\_\_HELPERS/pronouns.dm for addressing all forms of pro
The way to avoid these problems is to use the pronoun system. Instead of `"[user] waves \his arms."`, you can do `"[user] waves [user.p_their()] arms."`
```
//Good
"[H] waves [H.p_their()] hands!"
"[user] waves [H.p_their()] [user.weapon] around, hitting [H.p_their()] opponents!"`
```DM
//Bad
"[H] waves \his hands!"
"[user] waves \his [user.weapon] around, hitting \his opponents!"
//Good
"[H] waves [H.p_their()] hands!"
"[user] waves [H.p_their()] [user.weapon] around, hitting [H.p_their()] opponents!"`
```
### Use `[A.UID()]` over `\ref[A]`
@@ -207,24 +211,30 @@ if the original datum has been deleted - BYOND recycles the references.
UID's are actually unique; they work off of a global counter and are not recycled. Each datum has one assigned to it when it's created, which can be
accessed by `[datum.UID()]`. You can use this as a snap-in replacement for `\ref` by changing any `locate(ref)` calls in your code to `locateUID(ref)`.
Usage of this system is mandatory for any /Topic( calls, and will produce errors in Dream Daemon if it's not used. `<a href='?src=[UID()];'>`, not `<a href='?src=\ref[src];'`.
Usage of this system is mandatory for any `Topic()` calls, and will produce errors in Dream Daemon if it's not used.
```DM
//Bad
"<a href='?src=\ref[src];'>Link!</a>"
### Use var/name format when declaring variables
//Good
"<a href='?src=[UID()];'>Link!</a>"
```
### Use `var/name` format when declaring variables
While DM allows other ways of declaring variables, this one should be used for consistency.
### Tabs, not spaces
You must use tabs to indent your code, NOT SPACES.
(You may use spaces to align something, but you should tab to the block level first, then add the remaining spaces)
(You may use spaces to align something, but you should tab to the block level first, then add the remaining spaces.)
### No hacky code
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.)
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, PR Reviewers, and other contributors who can help you 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
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)
The same also applies to bugfixes - 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)
### No duplicated code
Copying code from one place to another may be suitable for small, short-time projects, but Paradise is a long-term project and highly discourages this.
@@ -236,220 +246,226 @@ First, read the comments in [this BYOND thread](http://www.byond.com/forum/?post
There are two key points here:
1) Defining a list in the variable's definition calls a hidden proc - init. If you have to define a list at startup, do so in New() (or preferably Initialize()) and avoid the overhead of a second call (Init() and then New())
1) Defining a list in the variable's definition calls a hidden proc - init. If you have to define a list at startup, do so in `New()` (or preferably `Initialize()`) and avoid the overhead of a second call (`init()` and then `New()`)
2) It also consumes more memory to the point where the list is actually required, even if the object in question may never use it!
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.
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 dependent 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.
### 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.
```DM
//Bad
/obj/item/proc1(var/mob/input1, var/input2)
code
I.e.
Bad:
````
obj/item/proc1(var/input1, var/input2)
````
Good:
````
obj/item/proc1(input1, input2)
````
//Good
/obj/item/proc1(mob/input1, input2)
code
```
### 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
more clearly states what it's for. For instance:
````DM
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
//Bad
/datum/proc/do_the_thing(thing_to_do)
switch(thing_to_do)
if(1)
(...)
if(2)
(...)
````
switch(thing_to_do)
if(1)
do_stuff()
if(2)
do_other_stuff()
```
There's no indication of what "1" and "2" mean! Instead, you should do something like this:
````DM
```DM
//Good
#define DO_THE_THING_REALLY_HARD 1
#define DO_THE_THING_EFFICIENTLY 2
/datum/proc/do_the_thing(thing_to_do)
switch(thing_to_do)
if(DO_THE_THING_REALLY_HARD)
(...)
if(DO_THE_THING_EFFICIENTLY)
(...)
````
switch(thing_to_do)
if(DO_THE_THING_REALLY_HARD)
do_stuff()
if(DO_THE_THING_EFFICIENTLY)
do_other_stuff()
```
This is clearer and enhances readability of your code! Get used to doing it!
### Control statements
(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
(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.
Instead of doing
```
* All control statements must not contain code on the same line as the statement.
```DM
//Bad
if(x) return
```
You should do
```
//Good
if(x)
return
return
```
### Player Output
Due to the use of "Goonchat", Paradise requires a special syntax for outputting text messages to players. Instead of `mob/client/world << "message"`,
you must use `to_chat(mob/client/world, "message")`. Failure to do so will lead to your code not working.
Due to the use of "Goonchat", Paradise requires a special syntax for outputting text messages to players. Instead of `mob << "message"`, you must use `to_chat(mob, "message")`. Failure to do so will lead to your code not working.
### Use early return
### Use early returns
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()
if(thing1)
if(!thing2)
if(thing3 == 30)
do stuff
if(thing1)
if(!thing2)
if(thing3 == 30)
do stuff
````
This is good:
````DM
/datum/datum1/proc/proc1()
if(!thing1)
return
if(thing2)
return
if(thing3 != 30)
return
do stuff
if(!thing1)
return
if(thing2)
return
if(thing3 != 30)
return
do stuff
````
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.
### Use `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.
Though more complex, this method has greater performance. Additionally, 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.
Look for code examples on how to properly use it.
```DM
//Bad
/datum/datum1/proc/proc1(target)
spawn(5 SECONDS)
target.dothing(arg1, arg2, arg3)
This is bad:
````DM
/datum/datum1/proc/proc1()
spawn(5)
dothing(arg1, arg2, arg3)
````
This is good:
````DM
addtimer(CALLBACK(procsource, .proc/dothing, arg1, arg2, arg3), waittime, timertype)
````
//Good
/datum/datum1/proc/proc1(target)
addtimer(CALLBACK(target, .proc/dothing, arg1, arg2, arg3), 5 SECONDS)
```
This prevents nesting levels from getting deeper then they need to be.
### Operators
#### Spacing
* Operators that should be separated by spaces
* Boolean and logic operators like &&, || <, >, ==, etc (but not !)
* Bitwise AND & and OR |
* Argument separator operators like , (and ; when used in a forloop)
* Assignment operators like = or += or the like
* Math operators like +, -, /, or \*
* Operators that should not be separated by spaces
* Access operators like . and :
* Parentheses ()
* logical not !
* Operators that should be separated by spaces:
* Boolean and logic operators like `&&`, `||` `<`, `>`, `==`, etc. (But not `!`)
* Bitwise AND `&` and OR `|`.
* Argument separator operators like `,`. (and `;` when used in a forloop)
* Assignment operators like `=` or `+=` or the like.
* Math operators like `+`, `-`, `/`, or `*`.
* Operators that should NOT be separated by spaces:
* Access operators like `.` and `:`.
* Parentheses `()`.
* Logical not `!`.
#### Use
* Bitwise AND - '&'
* Should be written as ```bitfield & bitflag``` NEVER ```bitflag & bitfield```, both are valid, but the latter is confusing and nonstandard.
* Bitwise AND `&`
* Should be written as `bitfield & bitflag` NEVER `bitflag & bitfield`, both are valid, but the latter is confusing and nonstandard.
* Associated lists declarations must have their key value quoted if it's a string
* WRONG: list(a = "b")
* RIGHT: list("a" = "b")
```DM
//Bad
list(a = "b")
//Good
list("a" = "b")
```
#### Bitflags
* We prefer using bitshift operators instead of directly typing out the value. I.E.
* 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
Is preferable to:
```
#define MACRO_ONE 1
#define MACRO_TWO 2
#define MACRO_THREE 4
```
This make the code more readable and less prone to error
While it may initially look intimidating, `(1<<x)` is actually very simple and, as the name implies, shifts the bits of a given binary number over by one digit.
```
000100 (4, or (1<<2))
<<
001000 (8, or (1<<3))
```
Using this system makes the code more readable and less prone to error.
### Legacy Code
SS13 has a lot of legacy code that's never been updated. Here are some examples of common legacy trends which are no longer acceptable:
* To display messages to all mobs that can view `src`, you should use
`visible_message()`.
* Bad:
```
for(var/mob/M in viewers(src))
M.show_message("<span class='warning'>Arbitrary text</span>")
```
* Good:
```
visible_message("<span class='warning'>Arbitrary text</span>")
```
* To display messages to all mobs that can view `user`, you should use `visible_message()`.
```DM
//Bad
for(var/mob/M in viewers(user))
M.show_message("<span class='warning'>Arbitrary text</span>")
//Good
user.visible_message("<span class='warning'>Arbitrary text</span>")
```
* You should not use color macros (`\red, \blue, \green, \black`) to color text,
instead, you should use span classes. `<span class='warning'>red text</span>`,
`<span class='notice'>blue text</span>`.
* Bad:
instead, you should use span classes. `<span class='warning'>Red text</span>`, `<span class='notice'>Blue text</span>`.
```
to_chat("\red Red Text \black black text")
```
* Good:
```
to_chat("<span class='warning'>Red Text</span>black text")
//Bad
to_chat(user, "\red Red text \black Black text")
//Good
to_chat(user, "<span class='warning'>Red text</span>Black text")
```
* To use variables in strings, you should **never** use the `text()` operator, use
embedded expressions directly in the string.
* Bad:
```
to_chat(text("[] is leaking []!", src.name, src.liquid_type))
```
* Good:
```
to_chat("[src] is leaking [liquid_type]")
```
```DM
//Bad
to_chat(user, text("[] is leaking []!", name, liquid_type))
//Good
to_chat(user, "[name] is leaking [liquid_type]!")
```
* To reference a variable/proc on the src object, you should **not** use
`src.var`/`src.proc()`. The `src.` in these cases is implied, so you should just use
`var`/`proc()`.
* Bad:
```
var/user = src.interactor
src.fillReserves(user)
```
* Good:
```
var/user = interactor
fillReserves(user)
```
```DM
//Bad
var/user = src.interactor
src.fill_reserves(user)
//Good
var/user = interactor
fill_reserves(user)
```
### Develop Secure Code
* Player input must always be escaped safely, we recommend you use stripped_input in all cases where you would use input. Essentially, just always treat input from players as inherently malicious and design with that use case in mind
* Player input must always be escaped safely, we recommend you use `stripped_input()` in all cases where you would use input. Essentially, just always treat input from players as inherently malicious and design with that use case in mind.
* Calls to the database must be escaped properly - use proper parameters (values starting with a :). You can then replace these with a list of parameters, and these will be properly escaped during the query, and prevent any SQL injection.
* Good:
```dm
var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey=:target_ckey", list(
"target_ckey" = target_ckey
)) // Note the use of parameters on the above line and :target_ckey in the query
```
* Bad:
```dm
var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey='[target_ckey]'")
```
```DM
//Bad
var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey='[target_ckey]'")
//Good
var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey=:target_ckey", list(
"target_ckey" = target_ckey
)) // Note the use of parameters on the above line and :target_ckey in the query.
```
* All calls to topics must be checked for correctness. Topic href calls can be easily faked by clients, so you should ensure that the call is valid for the state the item is in. Do not rely on the UI code to provide only valid topic calls, because it won't.
@@ -467,13 +483,13 @@ SS13 has a lot of legacy code that's never been updated. Here are some examples
### SQL
* Do not use the shorthand sql insert format (where no column names are specified) because it unnecessarily breaks all queries on minor column changes and prevents using these tables for tracking outside related info such as in a connected site/forum.
* Use parameters for queries (Mentioned above in) [###Develop Secure Code](###Develop Secure Code)
* Use parameters for queries, as mentioned above in [Develop Secure Code](#develop-secure-code).
* Always check your queries for success with if(!query.warn_execute()). By using this standard format, you can ensure the correct log messages are used
* Always check your queries for success with `if(!query.warn_execute())`. By using this standard format, you can ensure the correct log messages are used.
* Always qdel() your queries after you are done with them, this cleans up the results and helps things run smoother
* Always `qdel()` your queries after you are done with them, this cleans up the results and helps things run smoother.
* 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
* All changes to the database's layout (schema) must be specified in the database changelog in SQL, as well as reflected in the schema file.
* Any time the schema is changed the `SQL_VERSION` defines must be incremented, as well as the example config, with an appropriate conversion kit placed
in the SQL/updates folder.
@@ -536,7 +552,7 @@ in the SQL/updates folder.
* External areas, or areas where depressurisation is expected and normal, should use airless turf variants to prevent additional atmospherics load.
* Edits in mapping tools should generally be possible to replicate in-game. For this reason, avoid stacking multiple structures on the same tile (i.e. placing a light and an APC on the same wall.)
### Other Notes
* Code should be modular where possible; if you are working on a new addition, then strongly consider putting it in its own file unless it makes sense to put it with similar ones (i.e. a new tool would go in the "tools.dm" file)
* Code should be modular where possible; if you are working on a new addition, then strongly consider putting it in its own file unless it makes sense to put it with similar ones (i.e. a new tool would go in the `tools.dm` file)
* Bloated code may be necessary to add a certain feature, which means there has to be a judgement over whether the feature is worth having or not. You can help make this decision easier by making sure your code is modular.
* You are expected to help maintain the code that you add, meaning that if there is a problem then you are likely to be approached in order to fix any issues, runtimes, or bugs.
@@ -546,38 +562,38 @@ in the SQL/updates folder.
* All new var/proc names should use the American English spelling of words. This is for consistency with BYOND.
### Dream Maker Quirks/Tricks
Like all languages, Dream Maker has its quirks, some of them are beneficial to us, like these
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
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
some_value-1```).
`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
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)
### `for(var/A in list)` VS `for(var/i in 1 to list.len)`
The former is faster than the latter, as shown by the following profile results:
https://file.house/zy7H.png
https://file.house/zy7H.png <br>
Code used for the test in a readable format:
https://pastebin.com/w50uERkG
#### Istypeless for loops
A name for a differing syntax for writing for-each style loops in DM. It's NOT DM's standard syntax, hence why this is considered a quirk. Take a look at this:
```DM
var/list/bag_of_items = list(sword, apple, coinpouch, sword, sword)
var/list/bag_of_items = list(sword1, apple, coinpouch, sword2, sword3)
var/obj/item/sword/best_sword
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```
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)
var/list/bag_of_swords = list(sword1, sword2, sword3, sword4)
var/obj/item/sword/best_sword
for(var/obj/item/sword/S in bag_of_swords)
if(!best_sword || S.damage > best_sword.damage)
@@ -585,10 +601,10 @@ for(var/obj/item/sword/S in bag_of_swords)
```
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
With the previous example that's perfectly fine, we only want swords, but if 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:
you can circumvent DM's filtering and automatic `istype()` checks by writing the loop as such:
```DM
var/list/bag_of_swords = list(sword, sword, sword, sword)
var/obj/item/sword/best_sword
@@ -607,7 +623,7 @@ eg:
var/mob/living/carbon/human/H = YOU_THE_READER
H.gib()
```
However, DM also has a dot variable, accessed just as `.` on its own, defaulting to a value of null. Now, what's special about the dot operator is that it is automatically returned (as in the `return` statement) at the end of a proc, provided the proc does not already manually return (`return count` for example.) Why is this special?
However, DM also has a dot *variable*, accessed just as `.` on its own, defaulting to a value of null. Now, what's special about the dot operator is that it is automatically returned (as in the `return` statement) at the end of a proc, provided the proc does not already manually return (`return count` for example.) Why is this special?
With `.` being everpresent in every proc, can we use it as a temporary variable? Of course we can! However, the `.` operator cannot replace a typecasted variable - it can hold data any other var in DM can, it just can't be accessed as one, although the `.` operator is compatible with a few operators that look weird but work perfectly fine, such as: `.++` for incrementing `.'s` value, or `.[1]` for accessing the first element of `.`, provided that it's a list.
@@ -627,7 +643,7 @@ There is also an undocumented keyword called `static` that has the same behaviou
### Global Vars
All new global vars must use the defines in code/\_\_DEFINES/\_globals.dm. Basic usage is as follows:
All new global vars must use the defines in [`code/__DEFINES/_globals.dm`](../code/__DEFINES/_globals.dm). Basic usage is as follows:
To declare a global var:
```DM
@@ -648,13 +664,13 @@ responsible for properly tagging new pull requests and issues, moderating commen
pull requests/issues, and merging/closing pull requests.
### Maintainer List
* [Fox P McCloud](https://github.com/Fox-McCloud)
* [Crazy Lemon](https://github.com/Crazylemon64)
* [Ansari](https://github.com/variableundefined)
* [AffectedArc07](https://github.com/AffectedArc07)
* [Ansari](https://github.com/variableundefined)
* [Crazy Lemon](https://github.com/Crazylemon64)
* [Fox P McCloud](https://github.com/Fox-McCloud)
### Maintainer instructions
* Do not `self-merge`; this refers to the practice of opening a pull request, then
* Do not "self-merge"; this refers to the practice of opening a pull request, then
merging it yourself. A different maintainer must review and merge your pull request, no
matter how trivial. This is to ensure quality.
* A subset of this instruction: Do not push directly to the repository, always make a

View File

@@ -2,4 +2,4 @@
"workbench.editorAssociations": {
"*.dmi": "imagePreview.previewEditor"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -40,7 +40,6 @@
#define BELOW_OPEN_DOOR_LAYER 2.6
#define BLASTDOOR_LAYER 2.65
#define OPEN_DOOR_LAYER 2.7
#define DOOR_HELPER_LAYER 2.71 //keep this above OPEN_DOOR_LAYER
#define PROJECTILE_HIT_THRESHHOLD_LAYER 2.75 //projectiles won't hit objects at or below this layer if possible
#define TABLE_LAYER 2.8
#define BELOW_OBJ_LAYER 2.9
@@ -52,6 +51,7 @@
#define SHUTTER_LAYER 3.12 // HERE BE DRAGONS
#define ABOVE_OBJ_LAYER 3.2
#define ABOVE_WINDOW_LAYER 3.3
#define DOOR_HELPER_LAYER 3.31 // Keep this above doors and windoors
#define SIGN_LAYER 3.4
#define NOT_HIGH_OBJ_LAYER 3.5
#define HIGH_OBJ_LAYER 3.6

View File

@@ -50,7 +50,7 @@ GLOBAL_PROTECT(log_end)
/proc/log_access_in(client/new_client)
if(GLOB.configuration.logging.access_logging)
var/message = "[key_name(new_client)] - IP:[new_client.address] - CID:[new_client.computer_id] - BYOND v[new_client.byond_version]"
var/message = "[key_name(new_client)] - IP:[new_client.address] - CID:[new_client.computer_id] - BYOND v[new_client.byond_version].[new_client.byond_build]"
rustg_log_write(GLOB.world_game_log, "ACCESS IN: [message][GLOB.log_end]")
/proc/log_access_out(mob/last_mob)

View File

@@ -680,6 +680,7 @@ proc/dd_sortedObjectList(list/incoming)
#define UNSETEMPTY(L) if (L && !L.len) L = null
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } }
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
#define LAZYADDOR(L, I) if(!L) { L = list(); } L |= I;
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null)
#define LAZYLEN(L) length(L) // Despite how pointless this looks, it's still needed in order to convey that the list is specificially a 'Lazy' list.
#define LAZYCLEARLIST(L) if(L) L.Cut()

View File

@@ -616,7 +616,7 @@ so as to remain in compliance with the most up-to-date laws."
if(stone)
if(alert(usr, "Do you want to be captured by [stoner]'s soul stone? This will destroy your corpse and make it \
impossible for you to get back into the game as your regular character.",, "No", "Yes") == "Yes")
stone.opt_in = TRUE
stone?.opt_in = TRUE
/obj/screen/alert/notify_soulstone/Destroy()
stone = null

View File

@@ -1,5 +1,5 @@
/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
if(!tool_attack_chain(user, target) && pre_attackby(target, user, params))
if(!tool_attack_chain(user, target) && pre_attack(target, user, params))
// Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example)
var/resolved = target.attackby(src, user, params)
if(!resolved && target && !QDELETED(src))
@@ -18,7 +18,7 @@
return
return
/obj/item/proc/pre_attackby(atom/A, mob/living/user, params) //do stuff before attackby!
/obj/item/proc/pre_attack(atom/A, mob/living/user, params) //do stuff before attackby!
if(is_hot(src) && A.reagents && !ismob(A))
to_chat(user, "<span class='notice'>You heat [A] with [src].</span>")
A.reagents.temperature_reagents(is_hot(src))

View File

@@ -14,6 +14,8 @@
var/shutdown_shell_command = null
/// 2FA backend server host
var/_2fa_auth_host = null
/// List of IP addresses which bypass world topic rate limiting
var/list/topic_ip_ratelimit_bypass = list()
/datum/configuration_section/system_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
@@ -24,3 +26,5 @@
CONFIG_LOAD_STR(medal_hub_password, data["medal_hub_password"])
CONFIG_LOAD_STR(shutdown_shell_command, data["shutdown_shell_command"])
CONFIG_LOAD_STR(_2fa_auth_host, data["_2fa_auth_host"])
CONFIG_LOAD_LIST(topic_ip_ratelimit_bypass, data["topic_ip_ratelimit_bypass"])

View File

@@ -9,6 +9,8 @@ SUBSYSTEM_DEF(mobs)
var/static/list/clients_by_zlevel[][]
var/static/list/dead_players_by_zlevel[][] = list(list()) // Needs to support zlevel 1 here, MaxZChanged only happens when CC is created and new_players can login before that.
var/static/list/cubemonkeys = list()
/// The amount of giant spiders that exist in the world. Used for mob capping.
var/giant_spiders = 0
/datum/controller/subsystem/mobs/stat_entry()
..("P:[GLOB.mob_living_list.len]")

View File

@@ -37,7 +37,6 @@ SUBSYSTEM_DEF(shuttle)
var/list/shoppinglist = list()
var/list/requestlist = list()
var/list/supply_packs = list()
var/datum/round_event/shuttle_loan/shuttle_loan
var/sold_atoms = ""
var/list/hidden_shuttle_turfs = list() //all turfs hidden from navigation computers associated with a list containing the image hiding them and the type of the turf they are pretending to be
var/list/hidden_shuttle_turf_images = list() //only the images from the above list

View File

@@ -41,7 +41,7 @@ SUBSYSTEM_DEF(vote)
CHECK_TICK
/datum/controller/subsystem/vote/proc/autotransfer()
initiate_vote("crew_transfer","the server")
initiate_vote("crew transfer", "the server")
/datum/controller/subsystem/vote/proc/reset()
initiator = null
@@ -95,7 +95,7 @@ SUBSYSTEM_DEF(vote)
choices[GLOB.master_mode] += non_voters
if(choices[GLOB.master_mode] >= greatest_votes)
greatest_votes = choices[GLOB.master_mode]
else if(mode == "crew_transfer")
else if(mode == "crew transfer")
var/factor = 0.5
switch(world.time / (10 * 60)) // minutes
if(0 to 60)
@@ -174,9 +174,9 @@ SUBSYSTEM_DEF(vote)
if(!SSticker.ticker_going)
SSticker.ticker_going = TRUE
to_chat(world, "<font color='red'><b>The round will start soon.</b></font>")
if("crew_transfer")
if("crew transfer")
if(. == "Initiate Crew Transfer")
init_shift_change(null, 1)
init_shift_change(null, TRUE)
if("map")
// Find target map.
var/datum/map/top_voted_map
@@ -222,7 +222,7 @@ SUBSYSTEM_DEF(vote)
if(SSticker.current_state >= 2)
return 0
choices.Add(GLOB.configuration.gamemode.votable_modes)
if("crew_transfer")
if("crew transfer")
if(check_rights(R_ADMIN|R_MOD))
if(SSticker.current_state <= 2)
return 0
@@ -266,16 +266,16 @@ SUBSYSTEM_DEF(vote)
<a href='?src=[UID()];vote=open'>Click here or type vote to place your vote.</a>
You have [GLOB.configuration.vote.vote_time / 10] seconds to vote.</font>"})
switch(vote_type)
if("crew_transfer", "gamemode", "custom", "map")
if("crew transfer", "gamemode", "custom", "map")
SEND_SOUND(world, sound('sound/ambience/alarm4.ogg'))
if(mode == "gamemode" && SSticker.ticker_going)
SSticker.ticker_going = FALSE
to_chat(world, "<font color='red'><b>Round start has been delayed.</b></font>")
if(mode == "crew_transfer" && GLOB.ooc_enabled)
if(mode == "crew transfer" && GLOB.ooc_enabled)
auto_muted = TRUE
GLOB.ooc_enabled = FALSE
to_chat(world, "<b>The OOC channel has been automatically disabled due to a crew transfer vote.</b>")
log_admin("OOC was toggled automatically due to crew_transfer vote.")
log_admin("OOC was toggled automatically due to crew transfer vote.")
message_admins("OOC has been toggled off automatically.")
if(mode == "gamemode" && GLOB.ooc_enabled)
auto_muted = TRUE
@@ -314,37 +314,41 @@ SUBSYSTEM_DEF(vote)
dat += "(<a href='?src=[UID()];vote=cancel'>Cancel Vote</a>) "
else
dat += "<div id='vote_div'><h2>Start a vote:</h2><hr><ul><li>"
//restart
if(admin || GLOB.configuration.vote.allow_restart_votes)
dat += "<a href='?src=[UID()];vote=restart'>Restart</a>"
else
dat += "<font color='grey'>Restart (Disallowed)</font>"
dat += "</li><li>"
// Crew transfer
if(admin || GLOB.configuration.vote.allow_restart_votes)
dat += "<a href='?src=[UID()];vote=crew_transfer'>Crew Transfer</a>"
else
dat += "<font color='grey'>Crew Transfer (Disallowed)</font>"
dat += "</li><li>"
// Restart
if(admin || GLOB.configuration.vote.allow_restart_votes)
dat += "<a href='?src=[UID()];vote=restart'>Restart</a>"
else
dat += "<font color='grey'>Restart (Disallowed)</font>"
if(admin)
dat += "\t(<a href='?src=[UID()];vote=toggle_restart'>[GLOB.configuration.vote.allow_restart_votes ? "Allowed" : "Disallowed"]</a>)"
dat += "</li><li>"
//gamemode
// Gamemode
if(admin || GLOB.configuration.vote.allow_mode_votes)
dat += "<a href='?src=[UID()];vote=gamemode'>GameMode</a>"
dat += "<a href='?src=[UID()];vote=gamemode'>Gamemode</a>"
else
dat += "<font color='grey'>GameMode (Disallowed)</font>"
dat += "<font color='grey'>Gamemode (Disallowed)</font>"
if(admin)
dat += "\t(<a href='?src=[UID()];vote=toggle_gamemode'>[GLOB.configuration.vote.allow_mode_votes ? "Allowed" : "Disallowed"]</a>)"
dat += "</li><li>"
// Map
if(admin)
dat += "<a href='?src=[UID()];vote=map'>Map</a>"
else
dat += "<font color='grey'>Map (Disallowed)</font>"
dat += "</li><li>"
dat += "</li>"
//custom
// Custom
if(admin)
dat += "<li><a href='?src=[UID()];vote=custom'>Custom</a></li>"
dat += "<a href='?src=[UID()];vote=custom'>Custom</a></li>"
dat += "</ul></div><hr>"
var/datum/browser/popup = new(C.mob, "vote", "Voting Panel", nref=src)
popup.set_content(dat)
@@ -387,14 +391,16 @@ SUBSYSTEM_DEF(vote)
var/votedesc = capitalize(mode)
if(mode == "custom")
votedesc += " ([question])"
log_and_message_admins("cancelled the running [votedesc] vote.")
log_and_message_admins("cancelled the running '[votedesc]' vote.")
reset()
if("toggle_restart")
if(admin)
GLOB.configuration.vote.allow_restart_votes = !GLOB.configuration.vote.allow_restart_votes
log_and_message_admins("has [GLOB.configuration.vote.allow_restart_votes ? "enabled" : "disabled"] public restart voting.")
if("toggle_gamemode")
if(admin)
GLOB.configuration.vote.allow_mode_votes = !GLOB.configuration.vote.allow_mode_votes
log_and_message_admins("has [GLOB.configuration.vote.allow_mode_votes ? "enabled" : "disabled"] public gamemode voting.")
if("restart")
if(GLOB.configuration.vote.allow_restart_votes || admin)
initiate_vote("restart",usr.key)
@@ -406,7 +412,7 @@ SUBSYSTEM_DEF(vote)
initiate_vote("map", usr.key)
if("crew_transfer")
if(GLOB.configuration.vote.allow_restart_votes || admin)
initiate_vote("crew_transfer",usr.key)
initiate_vote("crew transfer", usr.key)
if("custom")
if(admin)
initiate_vote("custom",usr.key)

View File

@@ -647,20 +647,20 @@
name = "Solar Federation Marine"
uniform = /obj/item/clothing/under/solgov
suit = /obj/item/clothing/suit/armor/bulletproof
back = /obj/item/storage/backpack/security
back = /obj/item/storage/backpack/ert/solgov
belt = /obj/item/storage/belt/military/assault/marines/full
head = /obj/item/clothing/head/soft/solgov/marines
glasses = /obj/item/clothing/glasses/night
gloves = /obj/item/clothing/gloves/combat
shoes = /obj/item/clothing/shoes/combat
l_ear = /obj/item/radio/headset/ert
l_ear = /obj/item/radio/headset/ert/alt/solgov
id = /obj/item/card/id
l_hand = /obj/item/gun/projectile/automatic/shotgun/bulldog
suit_store = /obj/item/gun/projectile/automatic/pistol/m1911
r_pocket = /obj/item/flashlight/seclite
pda = /obj/item/pda
box = /obj/item/storage/box/responseteam
backpack_contents = list(
/obj/item/storage/box/responseteam = 1,
/obj/item/clothing/shoes/magboots = 1,
/obj/item/whetstone = 1,
/obj/item/clothing/mask/gas/explorer/marines = 1,
@@ -702,13 +702,12 @@
head = /obj/item/clothing/head/beret/solgov/command
glasses = /obj/item/clothing/glasses/night
back = /obj/item/storage/backpack/satchel
l_ear = /obj/item/radio/headset/ert/alt/commander
l_ear = /obj/item/radio/headset/ert/alt/commander/solgov
l_hand = null
belt = /obj/item/melee/baton/loaded
suit_store = /obj/item/gun/projectile/automatic/pistol/deagle
l_pocket = /obj/item/pinpointer/advpinpointer
backpack_contents = list(
/obj/item/storage/box/responseteam = 1,
/obj/item/storage/box/handcuffs = 1,
/obj/item/clothing/shoes/magboots/advance = 1,
/obj/item/reagent_containers/hypospray/autoinjector/survival = 1,
@@ -720,14 +719,14 @@
/datum/outfit/admin/solgov/elite
name = "Solar Federation Specops Marine"
uniform = /obj/item/clothing/under/solgov/elite
head = /obj/item/clothing/head/soft/solgov/marines/elite
suit = /obj/item/clothing/suit/space/hardsuit/ert/solgov
head = null
mask = /obj/item/clothing/mask/gas/explorer/marines
belt = /obj/item/storage/belt/military/assault/marines/elite/full
l_hand = /obj/item/gun/projectile/automatic/ar
backpack_contents = list(
/obj/item/storage/box/responseteam = 1,
/obj/item/clothing/shoes/magboots/advance = 1,
/obj/item/whetstone = 1,
/obj/item/clothing/mask/gas/explorer/marines = 1,
/obj/item/reagent_containers/hypospray/autoinjector/survival = 1
)
cybernetic_implants = list(
@@ -737,22 +736,22 @@
/obj/item/organ/internal/cyberimp/arm/flash,
/obj/item/organ/internal/eyes/cybernetic/shield
)
/datum/outfit/admin/solgov/elite/lieutenant
name = "Solar Federation Specops Lieutenant"
uniform = /obj/item/clothing/under/solgov/command/elite
head = /obj/item/clothing/head/beret/solgov/command/elite
suit = /obj/item/clothing/suit/space/hardsuit/ert/solgov/command
head = null
mask = /obj/item/clothing/mask/gas/explorer/marines
glasses = /obj/item/clothing/glasses/night
back = /obj/item/storage/backpack/satchel
belt = /obj/item/melee/baton/loaded
l_hand = null
suit_store = /obj/item/gun/projectile/automatic/pistol/deagle
l_pocket = /obj/item/pinpointer/advpinpointer
l_ear = /obj/item/radio/headset/ert/alt/commander
l_ear = /obj/item/radio/headset/ert/alt/commander/solgov
backpack_contents = list(
/obj/item/storage/box/responseteam = 1,
/obj/item/storage/box/handcuffs = 1,
/obj/item/clothing/shoes/magboots/advance = 1,
/obj/item/clothing/mask/gas/explorer/marines = 1,
/obj/item/reagent_containers/hypospray/autoinjector/survival = 1,
/obj/item/ammo_box/magazine/m50 = 3
)

View File

@@ -560,7 +560,8 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell))
to_chat(user, "<span class='warning'>You shouldn't have this spell! Something's wrong.</span>")
return 0
if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
var/turf/T = get_turf(user)
if(is_admin_level(T.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel
return 0
if(!holy_area_cancast && user.holy_check())

View File

@@ -273,6 +273,12 @@
/atom/proc/HasProximity(atom/movable/AM)
return
/**
* Proc which will make the atom act accordingly to an EMP.
* This proc can sleep depending on the implementation. So assume it sleeps!
*
* severity - The severity of the EMP. Either EMP_HEAVY or EMP_LIGHT
*/
/atom/proc/emp_act(severity)
return

View File

@@ -822,9 +822,10 @@
else
M.change_gender(FEMALE)
var/new_eyes = input("Please select eye color.", "Character Generation", eyes_organ.eye_color) as null|color
if(new_eyes)
M.change_eye_color(new_eyes)
if(eyes_organ)
var/new_eyes = input("Please select eye color.", "Character Generation", eyes_organ.eye_color) as null|color
if(new_eyes)
M.change_eye_color(new_eyes)
//Alt heads.
if(head_organ.dna.species.bodyflags & HAS_ALT_HEADS)

View File

@@ -238,7 +238,7 @@
blobber.LoseTarget()
spawn()
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blobbernaut?", ROLE_BLOB, TRUE, 10 SECONDS, source = blobber)
if(candidates.len)
if(length(candidates) && !QDELETED(blobber))
var/mob/C = pick(candidates)
if(C)
blobber.key = C.key

View File

@@ -11,7 +11,7 @@ GLOBAL_LIST_INIT(possible_changeling_IDs, list("Alpha","Beta","Gamma","Delta","E
name = "changeling"
config_tag = "changeling"
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation Brigadier General")
protected_species = list("Machine")
required_players = 15
required_enemies = 1

View File

@@ -185,7 +185,7 @@
owner.visible_message("<span class='warning'>[owner]'s body flashes a bright blue!</span>", \
"<span class='cultitalic'>You speak the cursed words, channeling an electromagnetic pulse from your body.</span>")
owner.emp_act(2)
empulse(owner, 2, 5, cause = "cult")
INVOKE_ASYNC(GLOBAL_PROC, /proc/empulse, owner, 2, 5, TRUE, "cult")
owner.whisper(invocation)
charges--
if(charges <= 0)

View File

@@ -48,7 +48,7 @@ GLOBAL_LIST_EMPTY(all_cults)
/datum/game_mode/cult
name = "cult"
config_tag = "cult"
restricted_jobs = list("Chaplain", "AI", "Cyborg", "Internal Affairs Agent", "Security Officer", "Warden", "Detective", "Security Pod Pilot", "Head of Security", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative", "Magistrate", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
restricted_jobs = list("Chaplain", "AI", "Cyborg", "Internal Affairs Agent", "Security Officer", "Warden", "Detective", "Security Pod Pilot", "Head of Security", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative", "Magistrate", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation Brigadier General")
protected_jobs = list()
required_players = 30
required_enemies = 3

View File

@@ -607,7 +607,7 @@ structure_check() searches for nearby cultist structures required for the invoca
set waitfor = FALSE
to_chat(user, "<span class='cult'>[mob_to_revive] was revived, but their mind is lost! Seeking a lost soul to replace it.</span>")
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Would you like to play as a revived Cultist?", ROLE_CULTIST, TRUE, poll_time = 20 SECONDS, source = /obj/item/melee/cultblade/dagger)
if(length(candidates))
if(length(candidates) && !QDELETED(mob_to_revive))
var/mob/dead/observer/C = pick(candidates)
to_chat(mob_to_revive.mind, "<span class='biggerdanger'>Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.</span>")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(mob_to_revive)]) to replace an AFK player.")

View File

@@ -90,6 +90,8 @@
if(!length(candidates))
to_chat(owner, "<span class='danger'>There were no ghosts willing to take control of your guardian. You can try again in 5 minutes.</span>")
return
if(QDELETED(guardian)) // Just in case
return
var/mob/dead/observer/new_stand = pick(candidates)
to_chat(guardian, "<span class='danger'>Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.</span>")

View File

@@ -168,6 +168,8 @@
visible_message("<span class='warning'>[src] disappears in a flash of red light!</span>")
qdel(src)
return
if(QDELETED(src)) // Just in case
return
var/mob/M = pick(demon_candidates)
var/mob/living/simple_animal/slaughter/cult/S = src
if(!M || !M.client)

View File

@@ -4,6 +4,10 @@
#define NUKE_SEALANT_OPEN 3
#define NUKE_UNWRENCHED 4
#define NUKE_MOBILE 5
#define NUKE_CORE_EVERYTHING_FINE 6
#define NUKE_CORE_PANEL_EXPOSED 7
#define NUKE_CORE_PANEL_UNWELDED 8
#define NUKE_CORE_FULLY_EXPOSED 9
GLOBAL_VAR(bomb_set)
@@ -25,12 +29,17 @@ GLOBAL_VAR(bomb_set)
var/yes_code = FALSE
var/safety = TRUE
var/obj/item/disk/nuclear/auth = null
var/removal_stage = NUKE_INTACT
var/obj/item/nuke_core/plutonium/core = null
var/lastentered
var/is_syndicate = FALSE
use_power = NO_POWER_USE
var/previous_level = ""
var/datum/wires/nuclearbomb/wires = null
var/removal_stage = NUKE_INTACT
///The same state removal stage is, until someone opens the panel of the nuke. This way we can have someone open the front of the nuke, while keeping track of where in the world we are on the anchoring bolts.
var/anchor_stage = NUKE_INTACT
///This is so that we can check if the internal components are sealed up properly when the outer hatch is closed.
var/core_stage = NUKE_CORE_EVERYTHING_FINE
/obj/machinery/nuclearbomb/syndicate
is_syndicate = TRUE
@@ -39,16 +48,19 @@ GLOBAL_VAR(bomb_set)
extended = FALSE
anchored = FALSE
/obj/machinery/nuclearbomb/New()
..()
r_code = rand(10000, 99999.0) // Creates a random code upon object spawn.
/obj/machinery/nuclearbomb/Initialize()
. = ..()
r_code = rand(10000, 99999) // Creates a random code upon object spawn.
wires = new/datum/wires/nuclearbomb(src)
previous_level = get_security_level()
GLOB.poi_list |= src
core = new /obj/item/nuke_core/plutonium(src)
STOP_PROCESSING(SSobj, core) //Let us not irradiate the vault by default.
/obj/machinery/nuclearbomb/Destroy()
SStgui.close_uis(wires)
QDEL_NULL(wires)
QDEL_NULL(core)
GLOB.poi_list.Remove(src)
return ..()
@@ -73,16 +85,41 @@ GLOBAL_VAR(bomb_set)
else
to_chat(user, "<span class='notice'>You need to deploy [src] first.</span>")
return
if(istype(O, /obj/item/stack/sheet/mineral/titanium) && removal_stage == NUKE_CORE_FULLY_EXPOSED)
if(do_after(user, 2 SECONDS, target = src))
var/obj/item/stack/S = O
if(!loc || !S || S.get_amount() < 5)
return
S.use(5)
user.visible_message("<span class='notice'>[user] repairs [src]'s inner core plate.</span>", "<span class='notice'>You repair [src]'s inner core plate. The radiation is contained.</span>")
removal_stage = NUKE_CORE_PANEL_UNWELDED
if(core)
STOP_PROCESSING(SSobj, core)
return
if(istype(O, /obj/item/stack/sheet/metal) && removal_stage == NUKE_CORE_PANEL_EXPOSED)
var/obj/item/stack/S = O
if(do_after(user, 2 SECONDS, target = src))
if(!loc || !S || S.get_amount() < 5)
return
S.use(5)
user.visible_message("<span class='notice'>[user] repairs [src]'s outer core plate.</span>", "<span class='notice'>You repair [src]'s outer core plate.</span>")
removal_stage = NUKE_CORE_EVERYTHING_FINE
return
if(istype(O, /obj/item/nuke_core/plutonium) && removal_stage == NUKE_CORE_FULLY_EXPOSED)
if(do_after(user, 2 SECONDS, target = src))
if(!user.unEquip(O))
to_chat(user, "<span class='notice'>The [O] is stuck to your hand!</span>")
return
user.visible_message("<span class='notice'>[user] puts [O] back in [src].</span>", "<span class='notice'>You put [O] back in [src].</span>")
O.forceMove(src)
core = O
else if(istype(O, /obj/item/disk/plantgene))
to_chat(user, "<span class='warning'>You try to plant the disk, but despite rooting around, it won't fit! After you branch out to read the instructions, you find out where the problem stems from. You've been bamboo-zled, this isn't a nuclear disk at all!</span>")
return
return ..()
/obj/machinery/nuclearbomb/crowbar_act(mob/user, obj/item/I)
if(!anchored)
return
if(removal_stage != NUKE_UNWRENCHED && removal_stage != NUKE_COVER_OFF)
return
. = TRUE
if(!I.tool_use_check(user, 0))
return
@@ -92,9 +129,26 @@ GLOBAL_VAR(bomb_set)
return
user.visible_message("[user] forces open the bolt covers on [src].", "You force open the bolt covers.")
removal_stage = NUKE_COVER_OPEN
else
if(removal_stage == NUKE_CORE_EVERYTHING_FINE)
user.visible_message("<span class='notice'>[user] starts removing [src]'s outer core plate...</span>", "<span class='notice'>You start removing [src]'s outer core plate...</span>")
if(!I.use_tool(src, user, 4 SECONDS, volume = I.tool_volume) || removal_stage != NUKE_CORE_EVERYTHING_FINE)
return
user.visible_message("<span class='notice'>[user] finishes removing [src]'s outer core plate.</span>", "<span class='notice'>You finish removing [src]'s outer core plate.</span>")
new /obj/item/stack/sheet/metal(loc, 5)
removal_stage = NUKE_CORE_PANEL_EXPOSED
if(removal_stage == NUKE_CORE_PANEL_UNWELDED)
user.visible_message("<span class='notice'>[user] starts removing [src]'s inner core plate...</span>", "<span class='notice'>You start removing [src]'s inner core plate...</span>")
if(!I.use_tool(src, user, 8 SECONDS, volume = I.tool_volume) || removal_stage != NUKE_CORE_PANEL_UNWELDED)
return
user.visible_message("<span class='notice'>[user] finishes removing [src]'s inner core plate.</span>", "<span class='notice'>You remove [src]'s inner core plate. You can see the core's green glow!</span>")
removal_stage = NUKE_CORE_FULLY_EXPOSED
new /obj/item/stack/sheet/mineral/titanium(loc, 5)
if(core)
START_PROCESSING(SSobj, core)
if(removal_stage == NUKE_UNWRENCHED)
user.visible_message("[user] begins lifting [src] off of the anchors.", "You begin lifting the device off the anchors...")
if(!I.use_tool(src, user, 80, volume = I.tool_volume) || removal_stage != NUKE_UNWRENCHED)
if(!I.use_tool(src, user, 8 SECONDS, volume = I.tool_volume) || removal_stage != NUKE_UNWRENCHED)
return
user.visible_message("[user] crowbars [src] off of the anchors. It can now be moved.", "You jam the crowbar under the nuclear device and lift it off its anchors. You can now move it!")
anchored = FALSE
@@ -126,15 +180,19 @@ GLOBAL_VAR(bomb_set)
. = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return
if(auth)
if(auth || (istype(I, /obj/item/screwdriver/nuke)))
if(!panel_open)
panel_open = TRUE
overlays += image(icon, "npanel_open")
to_chat(user, "You unscrew the control panel of [src].")
anchor_stage = removal_stage
removal_stage = core_stage
else
panel_open = FALSE
overlays -= image(icon, "npanel_open")
to_chat(user, "You screw the control panel of [src] back on.")
core_stage = removal_stage
removal_stage = anchor_stage
else
if(!panel_open)
to_chat(user, "[src] emits a buzzing noise, the panel staying locked in.")
@@ -142,6 +200,8 @@ GLOBAL_VAR(bomb_set)
panel_open = FALSE
overlays -= image(icon, "npanel_open")
to_chat(user, "You screw the control panel of [src] back on.")
core_stage = removal_stage
removal_stage = anchor_stage
flick("nuclearbombc", src)
/obj/machinery/nuclearbomb/wirecutter_act(mob/user, obj/item/I)
@@ -154,8 +214,6 @@ GLOBAL_VAR(bomb_set)
/obj/machinery/nuclearbomb/welder_act(mob/user, obj/item/I)
. = TRUE
if(removal_stage != NUKE_INTACT && removal_stage != NUKE_COVER_OPEN)
return
if(!I.tool_use_check(user, 0))
return
if(removal_stage == NUKE_INTACT)
@@ -167,7 +225,20 @@ GLOBAL_VAR(bomb_set)
visible_message("<span class='notice'>[user] cuts through the bolt covers on [src].</span>",\
"<span class='notice'>You cut through the bolt cover.</span>")
removal_stage = NUKE_COVER_OFF
else if(removal_stage == NUKE_COVER_OPEN)
if(removal_stage == NUKE_CORE_PANEL_UNWELDED)
user.visible_message("<span class='notice'>[user] starts welding [src]'s inner core plate...</span>", "<span class='notice'>You start welding [src]'s inner core plate...</span>")
if(!I.use_tool(src, user, 4 SECONDS, 5, volume = I.tool_volume) || removal_stage != NUKE_CORE_PANEL_UNWELDED)
return
user.visible_message("<span class='notice'>[user] finishes welding [src]'s inner core plate...</span>", "<span class='notice'>You finish welding [src]'s inner core plate...</span>")
removal_stage = NUKE_CORE_PANEL_EXPOSED
else if(removal_stage == NUKE_CORE_PANEL_EXPOSED)
user.visible_message("<span class='notice'>[user] starts unwelding [src]'s inner core plate...</span>", "<span class='notice'>You start unwelding [src]'s inner core plate...</span>")
if(!I.use_tool(src, user, 4 SECONDS, 5, volume = I.tool_volume) || removal_stage != NUKE_CORE_PANEL_EXPOSED)
return
user.visible_message("<span class='notice'>[user] finishes unwelding [src]'s inner core plate...</span>", "<span class='notice'>You finish unwelding [src]'s inner core plate...</span>")
removal_stage = NUKE_CORE_PANEL_UNWELDED
if(removal_stage == NUKE_COVER_OPEN)
visible_message("<span class='notice'>[user] starts cutting apart the anchoring system sealant on [src].</span>",\
"<span class='notice'>You start cutting apart the anchoring system's sealant with [I]...</span>",\
"<span class='warning'>You hear welding.</span>")
@@ -181,10 +252,18 @@ GLOBAL_VAR(bomb_set)
attack_hand(user)
/obj/machinery/nuclearbomb/attack_hand(mob/user as mob)
if(panel_open)
wires.Interact(user)
else
ui_interact(user)
if(!panel_open)
return ui_interact(user)
if(removal_stage != NUKE_CORE_FULLY_EXPOSED || !core)
return wires.Interact(user)
if(timing) //removing the core is less risk then cutting wires, and doesnt take long, so we should not let crew do it while the nuke is armed. You can however get to it, without the special screwdriver, if you put the NAD in.
to_chat(user, "<span class='warning'>[core] won't budge, metal clamps keep it in!</span>")
return
user.visible_message("<span class='notice'>[user] starts to pull [core] out of [src]!</span>", "<span class='notice'>You start to pull [core] out of [src]!</span>")
if(do_after(user, 5 SECONDS, target = src))
user.visible_message("<span class='notice'>[user] pulls [core] out of [src]!</span>", "<span class='notice'>You pull [core] out of [src]! Might want to put it somewhere safe.</span>")
core.forceMove(loc)
core = null
/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
@@ -308,6 +387,9 @@ GLOBAL_VAR(bomb_set)
if(safety)
to_chat(usr, "<span class='notice'>The safety is still on.</span>")
return
if(!core)
to_chat(usr, "<span class='danger'>[src]'s screen blinks red! There is no plutonium core in [src]!</span>")
return
timing = !(timing)
if(timing)
if(!lighthack)
@@ -471,3 +553,7 @@ GLOBAL_VAR(bomb_set)
#undef NUKE_SEALANT_OPEN
#undef NUKE_UNWRENCHED
#undef NUKE_MOBILE
#undef NUKE_CORE_EVERYTHING_FINE
#undef NUKE_CORE_PANEL_EXPOSED
#undef NUKE_CORE_PANEL_UNWELDED
#undef NUKE_CORE_FULLY_EXPOSED

View File

@@ -391,6 +391,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
explanation_text = "Steal [steal_target]. One was last seen in [get_location()]. "
if(length(O.protected_jobs))
explanation_text += "It may also be in the possession of the [english_list(O.protected_jobs, and_text = " or ")]."
if(steal_target.special_equipment)
give_kit(steal_target.special_equipment)
return
explanation_text = "Free Objective."
@@ -412,6 +414,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
else
steal_target = new new_target
explanation_text = "Steal [steal_target.name]."
if(steal_target.special_equipment)
give_kit(steal_target.special_equipment)
return steal_target
/datum/objective/steal/check_completion()
@@ -429,6 +433,23 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
if(I.type in steal_target.altitems)
return steal_target.check_special_completion(I)
/datum/objective/steal/proc/give_kit(obj/item/item_path)
var/mob/living/carbon/human/mob = owner.current
var/I = new item_path
var/list/slots = list(
"backpack" = slot_in_backpack,
"left pocket" = slot_l_store,
"right pocket" = slot_r_store,
"left hand" = slot_l_hand,
"right hand" = slot_r_hand,
)
var/where = mob.equip_in_one_of_slots(I, slots)
if(where)
to_chat(mob, "<br><br><span class='info'>In your [where] is a box containing <b>items and instructions</b> to help you with your steal objective.</span><br>")
else
to_chat(mob, "<span class='userdanger'>Unfortunately, you weren't able to get a stealing kit. This is very bad and you should adminhelp immediately (press F1).</span>")
message_admins("[ADMIN_LOOKUPFLW(mob)] Failed to spawn with their [item_path] theft kit.")
qdel(I)
/datum/objective/steal/exchange
martyr_compatible = 0

View File

@@ -74,7 +74,7 @@ Made by Xhuis
required_enemies = 2
recommended_enemies = 2
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Head of Personnel", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation Brigadier General")
/datum/game_mode/shadowling/announce()
to_chat(world, "<b>The current game mode is - Shadowling!</b>")

View File

@@ -12,6 +12,8 @@
var/list/altitems = list()
var/flags = 0
var/location_override
/// Do we have a special item we give to somewhen when they get this objective?
var/special_equipment = null
/datum/theft_objective/proc/check_completion(datum/mind/owner)
if(!owner.current)
@@ -138,6 +140,19 @@
protected_jobs = list("Head Of Security", "Warden")
location_override = "the Warden's Office"
/datum/theft_objective/supermatter_sliver
name = "a supermatter sliver"
typepath = /obj/item/nuke_core/supermatter_sliver
protected_jobs = list("Chief Engineer", "Engineer", "Atmospheric Technician") //Unlike other steal objectives, all jobs in the department have easy access, and would not be noticed at all stealing this
location_override = "Engineering. You can use the box and instructions provided to harvest the sliver."
special_equipment = /obj/item/storage/box/syndie_kit/supermatter
/datum/theft_objective/plutonium_core
name = "the plutonium core from the stations nuclear device"
typepath = /obj/item/nuke_core/plutonium
location_override = "the Vault. You can use the box and instructions provided to remove the core, with some extra tools."
special_equipment = /obj/item/storage/box/syndie_kit/nuke
/datum/theft_objective/number
var/min=0
var/max=0

View File

@@ -11,7 +11,7 @@
name = "traitor"
config_tag = "traitor"
restricted_jobs = list("Cyborg")//They are part of the AI if he is traitor so are they, they use to get double chances
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Internal Affairs Agent", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Internal Affairs Agent", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation Brigadier General")
required_players = 0
required_enemies = 1
recommended_enemies = 4

View File

@@ -2,7 +2,7 @@
name = "traitor+vampire"
config_tag = "traitorvamp"
traitors_possible = 3 //hard limit on traitors if scaling is turned off
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Chaplain", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Chaplain", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Solar Federation Brigadier General")
restricted_jobs = list("Cyborg")
secondary_restricted_jobs = list("AI")
required_players = 10

View File

@@ -9,7 +9,7 @@
name = "vampire"
config_tag = "vampire"
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Chaplain", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Chaplain", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation Brigadier General")
protected_species = list("Machine")
required_players = 15
required_enemies = 1

View File

@@ -100,6 +100,8 @@
return get_all_centcom_access() + get_all_accesses()
if("Special Operations Officer")
return get_all_centcom_access() + get_all_accesses()
if("Solar Federation Brigadier General")
return get_all_centcom_access() + get_all_accesses()
if("Nanotrasen Navy Representative")
return get_all_centcom_access() + get_all_accesses()
if("Nanotrasen Navy Officer")
@@ -401,7 +403,7 @@
return list("VIP Guest","Custodian","Thunderdome Overseer","Emergency Response Team Member","Emergency Response Team Leader","Intel Officer","Medical Officer","Death Commando","Research Officer","Deathsquad Officer","Special Operations Officer","Nanotrasen Navy Representative","Nanotrasen Navy Officer","Nanotrasen Navy Captain","Supreme Commander")
/proc/get_all_solgov_jobs()
return list("Solar Federation Lieutenant","Solar Federation Specops Lieutenant","Solar Federation Marine","Solar Federation Specops Marine","Solar Federation Representative","Sol Trader")
return list("Solar Federation Lieutenant","Solar Federation Specops Lieutenant","Solar Federation Marine","Solar Federation Specops Marine","Solar Federation Representative","Sol Trader","Solar Federation Brigadier General")
//gets the actual job rank (ignoring alt titles)
//this is used solely for sechuds

View File

@@ -69,9 +69,9 @@
/datum/outfit/job/ntspecops
name = "Special Operations Officer"
jobtype = /datum/job/ntspecops
allow_backbag_choice = FALSE
uniform = /obj/item/clothing/under/rank/centcom/captain
suit = /obj/item/clothing/suit/space/deathsquad/officer
back = /obj/item/storage/backpack/ert/security
belt = /obj/item/storage/belt/military/assault
gloves = /obj/item/clothing/gloves/combat
shoes = /obj/item/clothing/shoes/combat
@@ -82,8 +82,8 @@
id = /obj/item/card/id/centcom
pda = /obj/item/pda/centcom
r_pocket = /obj/item/storage/box/matches
back = /obj/item/storage/backpack/satchel
box = /obj/item/storage/box/centcomofficer
backpack = /obj/item/storage/backpack/satchel
backpack_contents = list(
/obj/item/clothing/shoes/magboots/advance = 1,
/obj/item/storage/box/zipties = 1
@@ -104,3 +104,23 @@
if(visualsOnly)
return
H.mind.offstation_role = TRUE
/datum/job/ntspecops/solgovspecops
title = "Solar Federation Brigadier General"
outfit = /datum/outfit/job/ntspecops/solgovspecops
/datum/outfit/job/ntspecops/solgovspecops
name = "Solar Federation Brigadier General"
uniform = /obj/item/clothing/under/rank/centcom/captain/solgov
suit = /obj/item/clothing/suit/space/deathsquad/officer/solgov
head = /obj/item/clothing/head/helmet/space/deathsquad/beret/solgov
/datum/outfit/job/ntspecops/solgovspecops/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
. = ..()
if(visualsOnly)
return
var/obj/item/card/id/I = H.wear_id
if(istype(I))
apply_to_card(I, H, get_centcom_access(name), name, "lifetimeid")
H.sec_hud_set_ID()
H.mind.offstation_role = TRUE

View File

@@ -30,6 +30,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
var/list/blacklisted_full = list(
/datum/job/ntnavyofficer,
/datum/job/ntspecops,
/datum/job/ntspecops/solgovspecops,
/datum/job/civilian,
/datum/job/syndicateofficer,
/datum/job/explorer // blacklisted so that HOPs don't try prioritizing it, then wonder why that doesn't work

View File

@@ -758,8 +758,7 @@
var/mob/living/silicon/robot/R = occupant
if(!istype(R)) return ..()
R.contents -= R.mmi
qdel(R.mmi)
QDEL_NULL(R.mmi)
for(var/obj/item/I in R.module) // the tools the borg has; metal, glass, guns etc
for(var/obj/item/O in I) // the things inside the tools, if anything; mainly for janiborg trash bags
O.loc = R
@@ -771,7 +770,7 @@
return ..()
/proc/cryo_ssd(mob/living/carbon/person_to_cryo)
/proc/cryo_ssd(mob/living/person_to_cryo)
if(istype(person_to_cryo.loc, /obj/machinery/cryopod))
return 0
if(isobj(person_to_cryo.loc))
@@ -779,7 +778,9 @@
O.force_eject_occupant(person_to_cryo)
var/list/free_cryopods = list()
for(var/obj/machinery/cryopod/P in GLOB.machines)
if(!P.occupant && istype(get_area(P), /area/crew_quarters/sleep))
if(P.occupant)
continue
if((ishuman(person_to_cryo) && istype(get_area(P), /area/crew_quarters/sleep)) || istype(P, /obj/machinery/cryopod/robot))
free_cryopods += P
var/obj/machinery/cryopod/target_cryopod = null
if(free_cryopods.len)

View File

@@ -85,6 +85,13 @@
else
do_animate("deny")
/obj/machinery/door/window/unrestricted_side(mob/M)
var/mob_dir = get_dir(src, M)
if(mob_dir == 0) // If the mob is inside the tile
mob_dir = GetOppositeDir(dir) // Set it to the inside direction of the windoor
return mob_dir & unres_sides
/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0)
if(istype(mover) && mover.checkpass(PASSGLASS))
return 1

View File

@@ -51,6 +51,11 @@
"Emergency Response Team Officer" = "dsquadradio",
"Nanotrasen Navy Officer" = "dsquadradio",
"Special Operations Officer" = "dsquadradio",
"Solar Federation Brigadier General" = "dsquadradio",
"Solar Federation Specops Lieutenant" = "dsquadradio",
"Solar Federation Specops Marine" = "dsquadradio",
"Solar Federation Lieutenant" = "dsquadradio",
"Solar Federation Marine" = "dsquadradio",
// Medical
"Chemist" = "medradio",
"Chief Medical Officer" = "medradio",
@@ -119,7 +124,9 @@
/// List of ERT jobs
var/list/ert_jobs = list("Emergency Response Team Officer", "Emergency Response Team Engineer", "Emergency Response Team Medic", "Emergency Response Team Leader", "Emergency Response Team Member")
/// List of CentComm jobs
var/list/cc_jobs = list("Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer")
var/list/cc_jobs = list("Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer", "Solar Federation Brigadier General")
/// List of SolGov Marine jobs
var/list/tsf_jobs = list("Solar Federation Specops Lieutenant", "Solar Federation Specops Marine", "Solar Federation Lieutenant", "Solar Federation Marine")
// Defined so code compiles and incase someone has a non-standard job
var/job_class = "radio"
// NOW FOR ACTUAL TOGGLES
@@ -278,7 +285,7 @@
// Makes heads of staff bold
if(toggle_command_bold)
var/job = tcm.sender_job
if((job in ert_jobs) || (job in heads) || (job in cc_jobs))
if((job in ert_jobs) || (job in heads) || (job in cc_jobs) || (job in tsf_jobs))
for(var/I in 1 to length(message_pieces))
var/datum/multilingual_say_piece/S = message_pieces[I]
if(!S.message)

View File

@@ -107,10 +107,10 @@
if(R.mind && !R.client && !R.grab_ghost()) // Make sure this is an actual player first and not just a humanized monkey or something.
message_admins("[key_name_admin(R)] was just transformed by a borg factory, but they were SSD. Polling ghosts for a replacement.")
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a malfunctioning cyborg?", ROLE_TRAITOR, poll_time = 15 SECONDS)
if(!length(candidates))
if(!length(candidates) || QDELETED(R))
return
var/mob/dead/observer/O = pick(candidates)
R.key= O.key
R.key = O.key
/obj/machinery/transformer/mime
name = "Mimetech Greyscaler"

View File

@@ -287,7 +287,7 @@
A.Grant(S)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a pyroclastic anomaly slime?", ROLE_SENTIENT, FALSE, 100, source = S, role_cleanname = "pyroclastic anomaly slime")
if(LAZYLEN(candidates))
if(length(candidates) && !QDELETED(S))
var/mob/dead/observer/chosen = pick(candidates)
S.key = chosen.key
S.mind.special_role = SPECIAL_ROLE_PYROCLASTIC_SLIME

View File

@@ -67,6 +67,11 @@
/obj/effect/mapping_helpers/no_lava
icon_state = "no_lava"
/obj/effect/mapping_helpers/no_lava/New()
var/turf/T = get_turf(src)
T.flags |= NO_LAVA_GEN
..()
/obj/effect/mapping_helpers/airlock
layer = DOOR_HELPER_LAYER
@@ -78,13 +83,9 @@
if(!mapload)
log_world("### MAP WARNING, [src] spawned outside of mapload!")
return
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in src.loc
if(airlock)
airlock.unres_sides ^= dir
var/obj/machinery/door/door = locate(/obj/machinery/door) in loc
if(door)
door.unres_sides ^= dir
else
log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]")
..()
/obj/effect/mapping_helpers/no_lava/New()
var/turf/T = get_turf(src)
T.flags |= NO_LAVA_GEN
. = ..()
..()

View File

@@ -1,4 +1,6 @@
//generic procs copied from obj/effect/alien
#define SPIDER_SOFT_CAP 30
#define SPIDER_HARD_CAP 40
/obj/structure/spider
name = "web"
desc = "it's stringy and sticky"
@@ -67,7 +69,11 @@
START_PROCESSING(SSobj, src)
/obj/structure/spider/eggcluster/process()
amount_grown += rand(0,2)
if(SSmobs.giant_spiders > SPIDER_SOFT_CAP) //eggs gonna chill out until there is less spiders
return
amount_grown += rand(0, 2)
if(amount_grown >= 100)
var/num = rand(3, 12)
for(var/i in 1 to num)
@@ -75,7 +81,7 @@
S.faction = faction.Copy()
S.master_commander = master_commander
if(player_spiders)
S.player_spiders = 1
S.player_spiders = TRUE
qdel(src)
/obj/structure/spider/spiderling
@@ -168,9 +174,13 @@
if(isturf(loc))
amount_grown += rand(0,2)
if(amount_grown >= 100)
if(SSmobs.giant_spiders > SPIDER_HARD_CAP)
qdel(src)
return
if(!grow_as)
grow_as = pick(typesof(/mob/living/simple_animal/hostile/poison/giant_spider))
var/mob/living/simple_animal/hostile/poison/giant_spider/S = new grow_as(loc)
SSmobs.giant_spiders++
S.faction = faction.Copy()
S.master_commander = master_commander
if(player_spiders && !selecting_player)
@@ -178,7 +188,7 @@
spawn()
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a giant spider?", ROLE_GSPIDER, TRUE, source = S)
if(candidates.len)
if(length(candidates) && !QDELETED(S))
var/mob/C = pick(candidates)
if(C)
S.key = C.key

View File

@@ -1,3 +1,13 @@
/**
* Will cause an EMP on the given epicenter.
* This proc can sleep depending on the affected objects. So assume it sleeps!
*
* epicenter - The center of the EMP. Can be an atom, as long as the given atom is on a turf (in)directly
* heavy_range - The max distance from the epicenter where objects will be get heavy EMPed
* light_range - The max distance from the epicenter where objects will get light EMPed
* log - Whether or not this action should be logged or not. Will use the cause if provided
* cause - The cause of the EMP. Used for the logging
*/
/proc/empulse(turf/epicenter, heavy_range, light_range, log = FALSE, cause = null)
if(!epicenter) return

View File

@@ -329,5 +329,6 @@
..()
/obj/item/paicard/extinguish_light()
pai.extinguish_light()
set_light(0)
if(pai)
pai.extinguish_light()
set_light(0)

View File

@@ -1,6 +1,7 @@
/obj/item/painter
name = "modular painter"
icon = 'icons/obj/painting.dmi'
icon_state = "floor_painter"
usesound = 'sound/effects/spray2.ogg'
flags = CONDUCT | NOBLUDGEON
w_class = WEIGHT_CLASS_SMALL

View File

@@ -307,12 +307,18 @@
icon_state = "com_headset_alt"
item_state = "com_headset_alt"
/obj/item/radio/headset/ert/alt/solgov
name = "\improper Trans-Solar Federation Marine's bowman headset"
/obj/item/radio/headset/ert/alt/commander
name = "ERT commander's bowman headset"
desc = "The headset of the boss. Protects ears from flashbangs. Can transmit even if telecomms are down."
requires_tcomms = FALSE
instant = TRUE
/obj/item/radio/headset/ert/alt/commander/solgov
name = "\improper Trans-Solar Federation Lieutenant's bowman headset"
/obj/item/radio/headset/centcom
name = "centcom officer's bowman headset"
desc = "The headset of final authority. Protects ears from flashbangs. Can transmit even if telecomms are down."

View File

@@ -2,6 +2,7 @@
name = "station intercom (General)"
desc = "Talk through this."
icon_state = "intercom"
layer = ABOVE_WINDOW_LAYER
anchored = 1
w_class = WEIGHT_CLASS_BULKY
canhear_range = 2

View File

@@ -0,0 +1,282 @@
//Items for nuke theft, supermatter theft traitor objective
// STEALING THE NUKE
//the nuke core, base item
/obj/item/nuke_core
name = "plutonium core"
desc = "Extremely radioactive. Wear goggles."
icon = 'icons/obj/nuke_tools.dmi'
icon_state = "plutonium_core"
item_state = "plutoniumcore"
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
flags_2 = RAD_NO_CONTAMINATE_2 //Don't have the item itself become irradiated when it makes radiation.
var/pulse = 0
var/cooldown = 0
var/pulseicon = "plutonium_core_pulse"
/obj/item/nuke_core/Initialize()
. = ..()
START_PROCESSING(SSobj, src)
/obj/item/nuke_core/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/nuke_core/attackby(obj/item/nuke_core_container/container, mob/user)
if(istype(container))
container.load(src, user)
else
return ..()
/obj/item/nuke_core/process()
if(cooldown < world.time - 6 SECONDS)
cooldown = world.time
flick(pulseicon, src)
radiation_pulse(src, 400, 2)
/obj/item/nuke_core/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] is rubbing [src] against [user.p_them()]self! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return TOXLOSS
/obj/item/nuke_core/plutonium //The steal objective, so it doesnt mess with the SM sliver on pinpointers and objectives
//nuke core box, for carrying the core
/obj/item/nuke_core_container
name = "nuke core container"
desc = "A solid container for radioactive objects."
icon = 'icons/obj/nuke_tools.dmi'
icon_state = "core_container_empty"
item_state = "metal"
var/obj/item/nuke_core/plutonium/core
/obj/item/nuke_core_container/Destroy()
QDEL_NULL(core)
return ..()
/obj/item/nuke_core_container/proc/load(obj/item/nuke_core/plutonium/new_core, mob/user)
if(core || !istype(new_core))
return
new_core.forceMove(src)
core = new_core
icon_state = "core_container_loaded"
to_chat(user, "<span class='warning'>Container is sealing...</span>")
addtimer(CALLBACK(src, .proc/seal), 10 SECONDS)
/obj/item/nuke_core_container/proc/seal()
if(!QDELETED(core))
STOP_PROCESSING(SSobj, core)
icon_state = "core_container_sealed"
playsound(src, 'sound/items/deconstruct.ogg', 60, TRUE)
if(ismob(loc))
to_chat(loc, "<span class='warning'>[src] is permanently sealed, [core]'s radiation is contained.</span>")
/obj/item/nuke_core_container/attackby(obj/item/nuke_core/plutonium/core, mob/user)
if(!istype(core))
return ..()
if(!user.drop_item())
to_chat(user, "<span class='warning'>[core] is stuck to your hand!</span>")
return
else
load(core, user)
/obj/item/paper/guides/antag/nuke_instructions
info = "How to break into a Nanotrasen nuclear device and remove its plutonium core:<br>\
<ul>\
<li>Use a screwdriver with a very thin tip (provided) to unscrew the terminal's front panel.</li>\
<li>Dislodge and remove the front panel with a crowbar.</li>\
<li>Cut the inner metal plate with a welding tool.</li>\
<li>Pry off the inner plate with a crowbar to expose the radioactive core.</li>\
<li>Pull the core out of the nuclear device. </li>\
<li>Put the core in the provided container, which will take some time to seal. </li>\
<li>???</li>\
</ul>"
// STEALING SUPERMATTER.
/obj/item/paper/guides/antag/supermatter_sliver
info = "How to safely extract a supermatter sliver:<br>\
<ul>\
<li>Approach an active supermatter crystal with radiation shielded personal protective equipment. DO NOT MAKE PHYSICAL CONTACT.</li>\
<li>Use a supermatter scalpel (provided) to slice off a sliver of the crystal.</li>\
<li>Use supermatter extraction tongs (also provided) to safely pick up the sliver you sliced off.</li>\
<li>Physical contact of any object with the sliver will dust the object, as well as yourself.</li>\
<li>Use the tongs to place the sliver into the provided container, which will take some time to seal.</li>\
<li>Get the hell out before the crystal delaminates.</li>\
<li>???</li>\
</ul>"
/obj/item/nuke_core/supermatter_sliver
name = "supermatter sliver"
desc = "A tiny, highly volatile sliver of a supermatter crystal. Do not handle without protection!"
icon_state = "supermatter_sliver"
pulseicon = "supermatter_sliver_pulse"
/obj/item/nuke_core/supermatter_sliver/attack_tk(mob/user) // no TK dusting memes
return
/obj/item/nuke_core/supermatter_sliver/can_be_pulled(user) // no drag memes
return FALSE
/obj/item/nuke_core/supermatter_sliver/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/retractor/supermatter))
var/obj/item/retractor/supermatter/tongs = I
if(tongs.sliver)
to_chat(user, "<span class='warning'>[tongs] are already holding a supermatter sliver!</span>")
return FALSE
forceMove(tongs)
tongs.sliver = src
tongs.icon_state = "supermatter_tongs_loaded"
tongs.item_state = "supermatter_tongs_loaded"
to_chat(user, "<span class='notice'>You carefully pick up [src] with [tongs].</span>")
else if(istype(I, /obj/item/scalpel/supermatter) || istype(I, /obj/item/nuke_core_container/supermatter)) // we don't want it to dust
return
else
to_chat(user, "<span class='danger'>As it touches [src], both [src] and [I] burst into dust!</span>")
radiation_pulse(user, 100)
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE)
qdel(I)
qdel(src)
/obj/item/nuke_core/supermatter_sliver/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!isliving(hit_atom))
return ..()
var/mob/living/victim = hit_atom
if(victim.incorporeal_move || victim.status_flags & GODMODE) //try to keep this in sync with supermatter's consume fail conditions
return ..()
if(throwingdatum?.thrower)
var/mob/user = throwingdatum.thrower
add_attack_logs(user, victim, "[victim] consumed by [src] thrown by [user] ")
message_admins("[src] has consumed [key_name_admin(victim)] [ADMIN_JMP(src)], thrown by [key_name_admin(user)].")
investigate_log("has consumed [key_name(victim)], thrown by [key_name(user)]", "supermatter")
else
message_admins("[src] has consumed [key_name_admin(victim)] [ADMIN_JMP(src)] via throw impact.")
investigate_log("has consumed [key_name(victim)] via throw impact.", "supermatter")
victim.visible_message("<span class='danger'>As [victim] is hit by [src], both flash into dust and silence fills the room...</span>",
"<span class='userdanger'>You're hit by [src] and everything suddenly goes silent.\n[src] flashes into dust, and soon as you can register this, you do as well.</span>",
"<span class='hear'>Everything suddenly goes silent.</span>")
victim.dust()
radiation_pulse(src, 500, 2)
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE)
qdel(src)
/obj/item/nuke_core/supermatter_sliver/pickup(mob/living/user)
..()
if(!isliving(user) || user.status_flags & GODMODE) //try to keep this in sync with supermatter's consume fail conditions
return FALSE
user.visible_message("<span class='danger'>[user] reaches out and tries to pick up [src]. [user.p_their()] body starts to glow and bursts into flames before flashing into dust!</span>",
"<span class='userdanger'>You reach for [src] with your hands. That was dumb.</span>",
"<span class='hear'>Everything suddenly goes silent.</span>")
radiation_pulse(user, 500, 2)
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE)
user.dust()
/obj/item/nuke_core_container/supermatter
name = "supermatter bin"
desc = "A tiny receptacle that releases an inert hyper-noblium mix upon sealing, allowing a sliver of a supermatter crystal to be safely stored."
var/obj/item/nuke_core/supermatter_sliver/sliver
/obj/item/nuke_core_container/supermatter/Destroy()
QDEL_NULL(sliver)
return ..()
/obj/item/nuke_core_container/supermatter/load(obj/item/retractor/supermatter/I, mob/user)
if(!istype(I) || !I.sliver)
return
I.sliver.forceMove(src)
sliver = I.sliver
I.sliver = null
I.icon_state = "supermatter_tongs"
I.item_state = "supermatter_tongs"
icon_state = "supermatter_container_loaded"
to_chat(user, "<span class='warning'>Container is sealing...</span>")
addtimer(CALLBACK(src, .proc/seal), 10 SECONDS)
/obj/item/nuke_core_container/supermatter/seal()
if(!QDELETED(sliver))
STOP_PROCESSING(SSobj, sliver)
icon_state = "supermatter_container_sealed"
playsound(src, 'sound/items/deconstruct.ogg', 60, TRUE)
if(ismob(loc))
to_chat(loc, "<span class='warning'>[src] is permanently sealed, [sliver] is safely contained.</span>")
/obj/item/nuke_core_container/supermatter/attackby(obj/item/retractor/supermatter/tongs, mob/user)
if(istype(tongs))
//try to load shard into core
load(tongs, user)
else
return ..()
/obj/item/scalpel/supermatter
name = "supermatter scalpel"
desc = "A scalpel with a fragile tip of condensed hyper-noblium gas, searingly cold to the touch, that can safely shave a sliver off a supermatter crystal."
icon = 'icons/obj/nuke_tools.dmi'
icon_state = "supermatter_scalpel"
toolspeed = 0.5
damtype = BURN
usesound = 'sound/weapons/bladeslice.ogg'
var/uses_left
/obj/item/scalpel/supermatter/Initialize()
. = ..()
uses_left = rand(2, 4)
/obj/item/retractor/supermatter
name = "supermatter extraction tongs"
desc = "A pair of tongs made from condensed hyper-noblium gas, searingly cold to the touch, that can safely grip a supermatter sliver."
icon = 'icons/obj/nuke_tools.dmi'
icon_state = "supermatter_tongs"
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
item_state = "supermatter_tongs"
toolspeed = 0.75
damtype = BURN
var/obj/item/nuke_core/supermatter_sliver/sliver
/obj/item/retractor/supermatter/Destroy()
QDEL_NULL(sliver)
return ..()
/obj/item/retractor/supermatter/afterattack(atom/O, mob/user, proximity)
. = ..()
if(!sliver)
return
if(proximity && ismovable(O) && O != sliver)
Consume(O, user)
/obj/item/retractor/supermatter/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) // no instakill supermatter javelins
if(sliver)
sliver.forceMove(loc)
visible_message("<span class='notice'>[sliver] falls out of [src] as it hits the ground.</span>")
sliver = null
icon_state = "supermatter_tongs"
item_state = "supermatter_tongs"
return ..()
/obj/item/retractor/supermatter/proc/Consume(atom/movable/AM, mob/living/user)
if(ismob(AM))
if(!isliving(AM))
return
var/mob/living/victim = AM
if(victim.incorporeal_move || victim.status_flags & GODMODE) //try to keep this in sync with supermatter's consume fail conditions
return
victim.dust()
message_admins("[src] has consumed [key_name_admin(victim)] [ADMIN_JMP(src)].")
investigate_log("has irradiated [key_name(victim)].", "supermatter")
else if(istype(AM, /obj/singularity))
return
else
investigate_log("has consumed [AM].", "supermatter")
qdel(AM)
if(user)
add_attack_logs(user, AM, "[AM] and [user] consumed by melee attack with [src] by [user]")
user.visible_message("<span class='danger'>As [user] touches [AM] with [src], both flash into dust and silence fills the room...</span>",
"<span class='userdanger'>You touch [AM] with [src], and everything suddenly goes silent.\n[AM] and [sliver] flash into dust, and soon as you can register this, you do as well.</span>",
"<span class='hear'>Everything suddenly goes silent.</span>")
user.dust()
radiation_pulse(src, 500, 2)
playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE)
QDEL_NULL(sliver)

View File

@@ -27,6 +27,7 @@
desc = "A screwdriver with an ultra thin tip."
icon_state = "screwdriver_nuke"
toolspeed = 0.5
random_color = FALSE
/obj/item/screwdriver/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] is stabbing [src] into [user.p_their()] [pick("temple", "heart")]! It looks like [user.p_theyre()] trying to commit suicide!</span>")

View File

@@ -4,6 +4,7 @@
icon_state = "explosive"
origin_tech = "materials=2;combat=3;biotech=4;syndicate=4"
actions_types = list(/datum/action/item_action/hands_free/activate/always)
var/detonating = FALSE
var/weak = 2
var/medium = 0.8
var/heavy = 0.4
@@ -26,16 +27,20 @@
activate("death")
/obj/item/implant/explosive/activate(cause)
if(!cause || !imp_in) return 0
if(!cause || !imp_in)
return FALSE
if(cause == "action_button" && alert(imp_in, "Are you sure you want to activate your microbomb implant? This will cause you to explode!", "Microbomb Implant Confirmation", "Yes", "No") != "Yes")
return 0
return FALSE
if(detonating)
return FALSE
heavy = round(heavy)
medium = round(medium)
weak = round(weak)
to_chat(imp_in, "<span class='notice'>You activate your microbomb implant.</span>")
detonating = TRUE
to_chat(imp_in, "<span class='danger'>You activate your microbomb implant.</span>")
//If the delay is short, just blow up already jeez
if(delay <= 7)
explosion(src,heavy,medium,weak,weak, flame_range = weak)
explosion(src, heavy, medium, weak, weak, flame_range = weak)
if(imp_in)
imp_in.gib()
qdel(src)

View File

@@ -58,7 +58,7 @@
/obj/item/implant/emp/activate()
uses--
empulse(imp_in, 3, 5, 1)
INVOKE_ASYNC(GLOBAL_PROC, .proc/empulse, get_turf(imp_in), 3, 5, 1)
if(!uses)
qdel(src)

View File

@@ -61,6 +61,9 @@
return
if(istype(W, /obj/item))
var/obj/item/IW = W
if(IW.flags & (ABSTRACT | NODROP | DROPDEL))
to_chat(user, "<span class='warning'>You can't put [IW] into [src]!</span>")
return
if((loadedWeightClass + IW.w_class) > maxWeightClass)
to_chat(user, "<span class='warning'>\The [IW] won't fit into \the [src]!</span>")
return

View File

@@ -584,3 +584,9 @@
name = "emergency response team janitor backpack"
desc = "A spacious backpack with lots of pockets, worn by janitorial members of a Nanotrasen Emergency Response Team."
icon_state = "ert_janitor"
//Solgov
/obj/item/storage/backpack/ert/solgov
name = "\improper TSF marine backpack"
desc = "A spacious backpack with lots of pockets, worn by marines of the Trans-Solar Federation."
icon_state = "ert_solgov"

View File

@@ -12,9 +12,9 @@
/// No message on putting items in.
var/silent = FALSE
/// List of objects which this item can store (if set, it can't store anything else)
var/list/can_hold = new/list()
var/list/can_hold = list()
/// List of objects which this item can't store (in effect only if can_hold isn't set)
var/list/cant_hold = new/list()
var/list/cant_hold = list()
/// Max size of objects that this object can store (in effect only if can_hold isn't set)
var/max_w_class = WEIGHT_CLASS_SMALL
/// The sum of the w_classes of all the items in this storage item.
@@ -42,6 +42,9 @@
/// How much of the stack item do you get.
var/foldable_amt = 0
/// Lazy list of mobs which are currently viewing the storage inventory.
var/list/mobs_viewing
/obj/item/storage/Initialize(mapload)
. = ..()
can_hold = typecacheof(can_hold)
@@ -73,6 +76,15 @@
closer.plane = ABOVE_HUD_PLANE
orient2hud()
/obj/item/storage/Destroy()
for(var/obj/O in contents)
O.mouse_opacity = initial(O.mouse_opacity)
QDEL_NULL(boxes)
QDEL_NULL(closer)
LAZYCLEARLIST(mobs_viewing)
return ..()
/obj/item/storage/MouseDrop(obj/over_object)
if(!ismob(usr)) //so monkeys can take off their backpacks -- Urist
return
@@ -177,11 +189,13 @@
user.client.screen += closer
user.client.screen += contents
user.s_active = src
LAZYADDOR(mobs_viewing, user)
/**
* Hides the current container interface from `user`.
*/
/obj/item/storage/proc/hide_from(mob/user)
LAZYREMOVE(mobs_viewing, user) // Remove clientless mobs too
if(!user.client)
return
user.client.screen -= boxes
@@ -190,6 +204,16 @@
if(user.s_active == src)
user.s_active = null
/**
* Checks all mobs currently viewing the storage inventory, and hides it if they shouldn't be able to see it.
*/
/obj/item/storage/proc/update_viewers()
for(var/_M in mobs_viewing)
var/mob/M = _M
if(!QDELETED(M) && M.s_active == src && (M in range(1, loc)))
continue
hide_from(M)
/obj/item/storage/proc/open(mob/user)
if(use_sound)
playsound(loc, use_sound, 50, TRUE, -5)
@@ -374,6 +398,12 @@
prevent_warning = TRUE
I.forceMove(src)
I.on_enter_storage(src)
for(var/_M in mobs_viewing)
var/mob/M = _M
if((M.s_active == src) && M.client)
M.client.screen += I
if(usr)
if(usr.client && usr.s_active != src)
usr.client.screen -= I
@@ -412,7 +442,8 @@
var/obj/item/storage/fancy/F = src
F.update_icon(TRUE)
for(var/mob/M in range(1, loc))
for(var/_M in mobs_viewing)
var/mob/M = _M
if((M.s_active == src) && M.client)
M.client.screen -= I
@@ -498,6 +529,10 @@
close(M)
add_fingerprint(user)
/obj/item/storage/equipped(mob/user, slot, initial)
. = ..()
update_viewers()
/obj/item/storage/attack_ghost(mob/user)
if(isobserver(user))
// Revenants don't get to play with the toys.
@@ -539,14 +574,6 @@
/obj/item/storage/proc/populate_contents()
return // Override
/obj/item/storage/Destroy()
for(var/obj/O in contents)
O.mouse_opacity = initial(O.mouse_opacity)
QDEL_NULL(boxes)
QDEL_NULL(closer)
return ..()
/obj/item/storage/emp_act(severity)
..()
for(var/I in contents)

View File

@@ -309,3 +309,24 @@ To apply, hold the injector a short distance away from the outer thigh before ap
new /obj/item/reagent_containers/syringe/capulettium_plus(src)
new /obj/item/reagent_containers/syringe/sarin(src)
new /obj/item/reagent_containers/syringe/pancuronium(src)
/obj/item/storage/box/syndie_kit/nuke
name = "box" //Bit of stealth, since you spawn with it
desc = "It's just an ordinary box."
icon_state = "box"
/obj/item/storage/box/syndie_kit/nuke/populate_contents()
new /obj/item/screwdriver/nuke(src)
new /obj/item/nuke_core_container(src)
new /obj/item/paper/guides/antag/nuke_instructions(src)
/obj/item/storage/box/syndie_kit/supermatter
name = "box"
desc = "It's just an ordinary box."
icon_state = "box"
/obj/item/storage/box/syndie_kit/supermatter/populate_contents()
new /obj/item/scalpel/supermatter(src)
new /obj/item/retractor/supermatter(src)
new /obj/item/nuke_core_container/supermatter(src)
new /obj/item/paper/guides/antag/supermatter_sliver(src)

View File

@@ -41,6 +41,7 @@
trigger_alarm()
emagged = TRUE
toggle_lock()
/obj/structure/displaycase/examine(mob/user)
. = ..()
@@ -110,12 +111,12 @@
/obj/structure/displaycase/attackby(obj/item/I, mob/user, params)
if(I.GetID() && !broken && openable)
if(allowed(user) || emagged)
to_chat(user, "<span class='notice'>You [open ? "close":"open"] [src].</span>")
toggle_lock(user)
to_chat(user, "<span class='notice'>You [open ? "close":"open"] [src].</span>")
toggle_lock()
else
to_chat(user, "<span class='warning'>Access denied.</span>")
to_chat(user, "<span class='warning'>Access denied.</span>")
else if(open && !showpiece)
if(user.drop_item())
if(!(I.flags & (ABSTRACT | DROPDEL)) && user.drop_item())
I.forceMove(src)
showpiece = I
to_chat(user, "<span class='notice'>You put [I] on display</span>")
@@ -151,14 +152,14 @@
if(!I.use_tool(src, user, 20, volume = I.tool_volume))
return
to_chat(user, "<span class='notice'>You [open ? "close":"open"] [src].</span>")
toggle_lock(user)
toggle_lock()
/obj/structure/displaycase/welder_act(mob/user, obj/item/I)
. = TRUE
if(default_welder_repair(user, I))
broken = FALSE
/obj/structure/displaycase/proc/toggle_lock(mob/user)
/obj/structure/displaycase/proc/toggle_lock()
open = !open
update_icon()

View File

@@ -28,7 +28,7 @@
if(GIRDER_DISASSEMBLED)
. += "<span class='notice'>[src] is disassembled! You probably shouldn't be able to see this examine message.</span>"
/obj/structre/girder/detailed_examine()
/obj/structure/girder/detailed_examine()
return "Use metal sheets on this to build a normal wall. Adding plasteel instead will make a reinforced wall.<br>\
A false wall can be made by using a crowbar on this girder, and then adding metal or plasteel.<br>\
You can dismantle the girder with a wrench."

View File

@@ -46,9 +46,16 @@
/obj/structure/inflatable/CanAtmosPass(turf/T)
return !density
/obj/structure/inflatable/attack_hand(mob/user as mob)
/obj/structure/inflatable/attack_hand(mob/user)
add_fingerprint(user)
/obj/structure/inflatable/attackby(obj/item/I, mob/living/user, params)
if(I.sharp || is_type_in_typecache(I, GLOB.pointed_types))
user.do_attack_animation(src, used_item = I)
deconstruct(FALSE)
return FALSE
return ..()
/obj/structure/inflatable/AltClick()
if(usr.stat || usr.restrained())
return

View File

@@ -203,6 +203,7 @@
windoor.req_one_access = electronics.selected_accesses
else
windoor.req_access = electronics.selected_accesses
windoor.unres_sides = electronics.unres_access_from
windoor.electronics = src.electronics
electronics.forceMove(windoor)
electronics = null

View File

@@ -85,15 +85,16 @@ GLOBAL_LIST_EMPTY(world_topic_handlers)
TGS_TOPIC
log_misc("WORLD/TOPIC: \"[T]\", from:[addr], master:[master], key:[key]")
// Handle spam prevention
if(!GLOB.world_topic_spam_prevention_handlers[address])
GLOB.world_topic_spam_prevention_handlers[address] = new /datum/world_topic_spam_prevention_handler
// Handle spam prevention, if their IP isnt in the whitelist
if(!(addr in GLOB.configuration.system.topic_ip_ratelimit_bypass))
if(!GLOB.world_topic_spam_prevention_handlers[addr])
GLOB.world_topic_spam_prevention_handlers[addr] = new /datum/world_topic_spam_prevention_handler(addr)
var/datum/world_topic_spam_prevention_handler/sph = GLOB.world_topic_spam_prevention_handlers[address]
var/datum/world_topic_spam_prevention_handler/sph = GLOB.world_topic_spam_prevention_handlers[addr]
// Lock the user out and cancel their topic if needed
if(sph.check_lockout())
return
// Lock the user out and cancel their topic if needed
if(sph.check_lockout())
return
var/list/input = params2list(T)

View File

@@ -92,6 +92,7 @@ GLOBAL_VAR_INIT(nologevent, 0)
body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] "
body += "\[<A href='?_src_=holder;getplaytimewindow=[M.UID()]'>" + M.client.get_exp_type(EXP_TYPE_CREW) + " as [EXP_TYPE_CREW]</a>\]"
body += "<br>BYOND account registration date: [M.client.byondacc_date || "ERROR"] [M.client.byondacc_age <= GLOB.configuration.general.byond_account_age_threshold ? "<b>" : ""]([M.client.byondacc_age] days old)[M.client.byondacc_age <= GLOB.configuration.general.byond_account_age_threshold ? "</b>" : ""]"
body += "<br>BYOND client version: [M.client.byond_version].[M.client.byond_build]"
body += "<br>Global Ban DB Lookup: [GLOB.configuration.url.centcom_ban_db_url ? "<a href='?_src_=holder;open_ccbdb=[M.client.ckey]'>Lookup</a>" : "<i>Disabled</i>"]"
body += "<br>"

View File

@@ -2300,26 +2300,40 @@
else if(href_list["cryossd"])
if(!check_rights(R_ADMIN))
return
var/mob/living/carbon/human/H = locateUID(href_list["cryossd"])
if(!istype(H))
to_chat(usr, "<span class='warning'>This can only be used on instances of type /mob/living/carbon/human</span>")
var/mob/living/M = locateUID(href_list["cryossd"])
var/human = ishuman(M)
if(!human && !issilicon(M))
to_chat(usr, "<span class='warning'>This can only be used on humans and silicons.</span>")
return
if(!href_list["cryoafk"] && !isLivingSSD(H))
if(!href_list["cryoafk"] && !isLivingSSD(M))
to_chat(usr, "<span class='warning'>This can only be used on living, SSD players.</span>")
return
if(istype(H.loc, /obj/machinery/cryopod))
var/obj/machinery/cryopod/P = H.loc
if(isAI(M))
var/mob/living/silicon/ai/A = M
A.cryo_AI()
if(istype(M.loc, /obj/machinery/cryopod))
var/obj/machinery/cryopod/P = M.loc
P.despawn_occupant()
log_admin("[key_name(usr)] despawned [H.job] [H] in cryo.")
message_admins("[key_name_admin(usr)] despawned [H.job] [H] in cryo.")
else if(cryo_ssd(H))
log_admin("[key_name(usr)] sent [H.job] [H] to cryo.")
message_admins("[key_name_admin(usr)] sent [H.job] [H] to cryo.")
if(human)
var/mob/living/carbon/human/H = M
log_admin("[key_name(usr)] despawned [H.job] [H] in cryo.")
message_admins("[key_name_admin(usr)] despawned [H.job] [H] in cryo.")
else //robot
log_admin("[key_name(usr)] despawned [M] in cryo.")
message_admins("[key_name_admin(usr)] despawned [M] in cryo.")
else if(cryo_ssd(M))
if(human)
var/mob/living/carbon/human/H = M
log_admin("[key_name(usr)] sent [H.job] [H] to cryo.")
message_admins("[key_name_admin(usr)] sent [H.job] [H] to cryo.")
else
log_admin("[key_name(usr)] sent [M] to cryo.")
message_admins("[key_name_admin(usr)] sent [M] to cryo.")
if(href_list["cryoafk"]) // Warn them if they are send to storage and are AFK
to_chat(H, "<span class='danger'>The admins have moved you to cryo storage for being AFK. Please eject yourself (right click, eject) out of the cryostorage if you want to avoid being despawned.</span>")
SEND_SOUND(H, sound('sound/effects/adminhelp.ogg'))
if(H.client)
window_flash(H.client)
to_chat(M, "<span class='danger'>The admins have moved you to cryo storage for being AFK. Please eject yourself (right click, eject) out of the cryostorage if you want to avoid being despawned.</span>")
SEND_SOUND(M, sound('sound/effects/adminhelp.ogg'))
if(M.client)
window_flash(M.client)
else if(href_list["FaxReplyTemplate"])
if(!check_rights(R_ADMIN))
return

View File

@@ -92,7 +92,7 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]")
returnval = WrapAdminProcCall(GLOBAL_PROC, procname, lst) // Pass the lst as an argument list to the proc
to_chat(usr, "<font color='blue'>[procname] returned: [!isnull(returnval) ? returnval : "null"]</font>")
to_chat(usr, "<font color='#EB4E00'>[procname] returned: [!isnull(returnval) ? returnval : "null"]</font>")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Advanced Proc-Call") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
// All these vars are related to proc call protection

View File

@@ -528,8 +528,8 @@
if(!antnum || antnum <= 0)
return
log_admin("[key_name(owner)] tried making Vampires with One-Click-Antag")
message_admins("[key_name_admin(owner)] tried making Vampires with One-Click-Antag")
log_admin("[key_name(owner)] tried making [antnum] Vampires with One-Click-Antag")
message_admins("[key_name_admin(owner)] tried making [antnum] Vampires with One-Click-Antag")
for(var/mob/living/carbon/human/applicant in GLOB.player_list)
if(CandCheck(ROLE_VAMPIRE, applicant, temp))

View File

@@ -74,7 +74,7 @@ GLOBAL_LIST_EMPTY(antagonists)
set waitfor = FALSE
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [name]?", job_rank, TRUE, 5 SECONDS)
if(LAZYLEN(candidates))
if(length(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner.current)]) to replace a jobbaned player.")

View File

@@ -48,7 +48,7 @@
to_chat(user, "<span class='notice'>You activate [src] and wait for confirmation.</span>")
var/image/I = new('icons/mob/simple_human.dmi', "syndicate_space_sword")
var/list/nuke_candidates = SSghost_spawns.poll_candidates("Do you want to play as a [rolename]?", ROLE_OPERATIVE, TRUE, 15 SECONDS, source = I)
if(LAZYLEN(nuke_candidates))
if(length(nuke_candidates))
checking = FALSE
if(QDELETED(src) || !check_usability(user))
return

View File

@@ -34,21 +34,30 @@
"Virology",
"Waste Disposal",
// Maintenance
"Aft Maintenance",
"Aft-Port Maintenance",
"Aft-Port Secondary Maintenance",
"Aft Port Solar Maintenance",
"Aft Secondary Maintenance",
"Aft-Starboard Maintenance",
"Aft-Starboard Secondary Maintenance",
"Aft Starboard Solar Maintenance",
"Arrivals North Maintenance",
"Bar Maintenance",
"Cargo Maintenance",
"Dormitory Maintenance",
"Electrical Maintenance",
"EVA Maintenance",
"Engineering Maintenance",
"Electronics Den",
"Fore Maintenance",
"Fore-Port Maintenance",
"Fore-Port Secondary Maintenance",
"Fore Port Solar Maintenance",
"Fore Secondary Maintenance",
"Fore-Starboard Maintenance",
"Fore-Starboard Secondary Maintenance",
"Fore Starboard Solar Maintenance",
"Gambling Den",
"Genetics Maintenance",
"Locker Room Maintenance",
"Medbay Maintenance",
"Science Maintenance",
"Port Maintenance",
"Port Secondary Maintenance",
"Starboard Maintenance",
"Starboard Secondary Maintenance",
),
EXTRACTION_DIFFICULTY_MEDIUM = list(
// Rooms
@@ -89,7 +98,8 @@
"Xenobiology Lab",
// Maintenance
"Atmospherics Maintenance",
"Bridge Maintenance",
"Central Maintenance",
"Central Secondary Maintenance",
),
EXTRACTION_DIFFICULTY_HARD = list(
// No AI Chamber because I'm not that sadistic.
@@ -100,7 +110,6 @@
"AI Satellite Hallway",
"Bar",
"Cargo Office",
"Central Primary Hallway",
"Chemistry",
"Chief Engineer's office",
"Chief Medical Officer's office",

View File

@@ -198,13 +198,7 @@
apply_mode()
/obj/machinery/alarm/New(loc, direction, building = 0)
. = ..()
GLOB.air_alarms += src
GLOB.air_alarms = sortAtom(GLOB.air_alarms)
wires = new(src)
if(building)
if(building) // Do this first since the Init uses this later on. TODO refactor to just use an Init
if(loc)
src.loc = loc
@@ -214,10 +208,15 @@
buildstage = 0
wiresexposed = 1
set_pixel_offsets_from_dir(-24, 24, -24, 24)
update_icon()
return
first_run()
. = ..()
GLOB.air_alarms += src
GLOB.air_alarms = sortAtom(GLOB.air_alarms)
wires = new(src)
if(!building)
first_run()
/obj/machinery/alarm/Destroy()
SStgui.close_uis(wires)

View File

@@ -194,7 +194,7 @@
servant_mind.transfer_to(H)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as the servant of [user.real_name]?", ROLE_WIZARD, poll_time = 30 SECONDS, source = H)
if(LAZYLEN(candidates))
if(length(candidates) && !QDELETED(H))
var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant")
H.key = C.key

View File

@@ -206,11 +206,13 @@
icon_state = "solgovcberet"
item_color = "solgovc"
dog_fashion = null
armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60)
armor = list("melee" = 20, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60)
strip_delay = 80
/obj/item/clothing/head/beret/solgov/command/elite
name = "\improper Trans-Solar Federation Specops Lieutenant's beret"
desc = "A beret worn by marines of the Trans-Solar Federation Psiops division. The insignia signifies the wearer bears the rank of a Lieutenant."
desc = "A beret worn by marines of the Trans-Solar Federation Specops division. The insignia signifies the wearer bears the rank of a Lieutenant."
armor = list("melee" = 35, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 50, "fire" = 80, "acid" = 80)
icon_state = "solgovceliteberet"
item_color = "solgovcelite"
resistance_flags = FIRE_PROOF

View File

@@ -125,28 +125,32 @@
dog_fashion = null
/obj/item/clothing/head/soft/solgov/marines
armor = list("melee" = 35, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50)
strip_delay = 60
armor = list("melee" = 20, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60)
icon_state = "solgovsoft_flipped"
strip_delay = 60
flipped = TRUE
/obj/item/clothing/head/soft/solgov/marines/elite
name = "\improper Trans-Solar Federation Specops marine cap"
desc = "A soft cap worn by marines of the Trans-Solar Federation Specops division."
desc = "A cap worn by marines of the Trans-Solar Federation Specops division. You aren't quite sure how they made this bulletproof, but you are glad it is!"
armor = list("melee" = 35, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 50, "fire" = 80, "acid" = 80)
icon_state = "solgovelitesoft_flipped"
item_color = "solgovelite"
resistance_flags = FIRE_PROOF
/obj/item/clothing/head/soft/solgov/marines/command
name = "\improper Trans-Solar Federation lieutenant's cap"
desc = "A soft cap worn by marines of the Trans-Solar Federation. The insignia signifies the wearer bears the rank of a Lieutenant."
armor = list("melee" = 20, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60)
icon_state = "solgovcsoft_flipped"
item_color = "solgovc"
dog_fashion = null
armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60)
strip_delay = 80
/obj/item/clothing/head/soft/solgov/marines/command/elite
name = "\improper Trans-Solar Federation Specops Lieutenant's cap"
desc = "A soft cap worn by marines of the Trans-Solar Federation Specops division. The insignia signifies the wearer bears the rank of a Lieutenant."
desc = "A cap worn by marines of the Trans-Solar Federation Specops division. You aren't quite sure how they made this bulletproof, but you are glad it is! The insignia signifies the wearer bears the rank of a Lieutenant."
armor= list("melee" = 35, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 50, "fire" = 80, "acid" = 80)
icon_state = "solgovcelitesoft"
item_color = "solgovcelite"
resistance_flags = FIRE_PROOF

View File

@@ -259,3 +259,38 @@
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/berserker
armor = list(melee = 65, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 100, rad = 100, fire = 80, acid = 80)
slowdown = 0
// Solgov
/obj/item/clothing/head/helmet/space/hardsuit/ert/solgov
name = "\improper Trans-Solar Federation Specops Marine helmet"
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
desc = "A helmet worn by marines of the Trans-Solar Federation. Armored, space ready, and fireproof."
icon_state = "hardsuit0-solgovmarine"
item_state = "hardsuit0-solgovmarine"
item_color = "solgovmarine"
armor = list("melee" = 35, "bullet" = 60, "laser" = 15, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100)
/obj/item/clothing/suit/space/hardsuit/ert/solgov
name = "\improper Trans-Solar Federation Specops Marine hardsuit"
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
desc = "A suit worn by marines of the Trans-Solar Federation. Armored, space ready, and fireproof."
icon_state = "ert_solgov_marine"
item_state = "ert_solgov_marine"
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/solgov
slowdown = 0
armor = list("melee" = 35, "bullet" = 60, "laser" = 15, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100)
/obj/item/clothing/head/helmet/space/hardsuit/ert/solgov/command
name = "\improper Trans-Solar Federation Specops Lieutenant helmet"
desc = "A helmet worn by Lieutenants of the Trans-Solar Federation Marines. Has gold highlights to denote the wearer's rank. Armored, space ready, and fireproof."
icon_state = "hardsuit0-solgovcommand"
item_state = "hardsuit0-solgovcommand"
item_color = "solgovcommand"
/obj/item/clothing/suit/space/hardsuit/ert/solgov/command
name = "\improper Trans-Solar Federation Specops Lieutenant hardsuit"
desc = "A suit worn by Lieutenants of the Trans-Solar Federation Marines. Has gold highlights to denote the wearer's rank. Armored, space ready, and fireproof."
icon_state = "ert_solgov_command"
item_state = "ert_solgov_command"
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/solgov/command

View File

@@ -87,6 +87,11 @@
armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
flags = STOPSPRESSUREDMAGE | THICKMATERIAL
/obj/item/clothing/head/helmet/space/deathsquad/beret/solgov
name = "\improper Trans-Solar Federation commander's beret"
desc = "A camouflaged beret adorned with the star of the Trans-Solar Federation, worn by generals of the Trans-Solar Federation."
icon_state = "solgovceliteberet"
/obj/item/clothing/suit/space/deathsquad/officer
name = "officer jacket"
desc = "An armored jacket used in special operations."
@@ -99,6 +104,12 @@
resistance_flags = FIRE_PROOF | ACID_PROOF
w_class = WEIGHT_CLASS_NORMAL
/obj/item/clothing/suit/space/deathsquad/officer/solgov
name = "\improper Trans-Solar Federation commander's jacket"
icon_state = "solgovcommander"
item_state = "solgovcommander"
//Space santa outfit suit
/obj/item/clothing/head/helmet/space/santahat
name = "Santa's hat"

View File

@@ -129,6 +129,10 @@
armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30)
displays_id = 0
/obj/item/clothing/under/rank/centcom/captain/solgov
name = "\improper Trans-Solar Federation commander's uniform"
desc = "Gold trim on space-black cloth, this uniform is worn by generals of the Trans-Solar Federation. It has exotic materials for protection."
/obj/item/clothing/under/rank/centcom/blueshield
name = "formal blueshield's uniform"
desc = "Gold trim on space-black cloth, this uniform bears \"Close Protection\" on the left shoulder. It's got exotic materials for protection."

View File

@@ -1470,13 +1470,13 @@
/obj/item/clothing/head/fluff/lfbowler //Lightfire: Hyperion
name = "Classy bowler hat"
desc = "a very classy looking bowler hat"
name = "classy bowler hat"
desc = "A very classy looking bowler hat."
icon = 'icons/obj/custom_items.dmi'
icon_state = "bowler_lightfire"
/obj/item/clothing/under/fluff/lfvicsuit //Lightfire: Hyperion
name = "Classy victorian suit"
name = "classy victorian suit"
desc = "A blue and black victorian suit with silver buttons, very fancy!"
icon = 'icons/obj/custom_items.dmi'
lefthand_file = 'icons/mob/inhands/fluff_lefthand.dmi'

View File

@@ -4,46 +4,46 @@
INVOKE_ASYNC(src, .proc/make_sentient_mob)
/datum/event/sentience/proc/make_sentient_mob()
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to awaken as a sentient being?", ROLE_SENTIENT, TRUE)
var/list/potential = list()
var/sentience_type = SENTIENCE_ORGANIC
for(var/mob/living/simple_animal/L in GLOB.alive_mob_list)
var/turf/T = get_turf(L)
if (!is_station_level(T.z))
if(!is_station_level(T.z))
continue
if(!(L in GLOB.player_list) && !L.mind && (L.sentience_type == sentience_type))
if(!(L in GLOB.player_list) && !L.mind && (L.sentience_type == SENTIENCE_ORGANIC))
potential += L
if(!candidates.len || !potential.len) //if there are no players or simple animals to choose from, then end
if(!length(potential)) // If there are somehow no simple animals to choose from, then end.
return FALSE
var/mob/living/simple_animal/SA = pick(potential)
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to awaken as \a [SA]?", ROLE_SENTIENT, TRUE, source = SA)
if(!length(candidates) || QDELETED(SA)) // If there's no candidates or the mob got deleted.
return
var/mob/SG = pick(candidates)
var/sentience_report = "<font size=3><b>NAS Trurl Medium-Priority Update</b></font>"
var/data = pick("scans from our long-range sensors", "our sophisticated probabilistic models", "our omnipotence", "the communications traffic on your station", "energy emissions we detected", "\[REDACTED\]", "Steve")
var/pets = pick("animals", "pets", "simple animals", "lesser lifeforms", "\[REDACTED\]")
var/strength = pick("human", "skrell", "vox", "grey", "diona", "IPC", "tajaran", "vulpakanin", "kidan", "plasmaman", "drask",
"slime", "monkey", "moderate", "lizard", "security", "command", "clown", "mime", "low", "very low", "greytide", "catgirl", "\[REDACTED\]")
sentience_report += "<br><br>Based on [data], we believe that one of the station's [pets] has developed [strength] level intelligence, and the ability to communicate."
SA.key = SG.key
SA.universal_speak = 1
SA.universal_speak = TRUE
SA.sentience_act()
SA.can_collar = 1
SA.can_collar = TRUE
SA.maxHealth = max(SA.maxHealth, 200)
SA.health = SA.maxHealth
SA.del_on_death = FALSE
greet_sentient(SA)
var/sentience_report = "<font size=3><b>NAS Trurl Medium-Priority Update</b></font>"
var/data = pick("scans from our long-range sensors", "our sophisticated probabilistic models", "our omnipotence", "the communications traffic on your station", "energy emissions we detected", "\[REDACTED]", "Steve")
var/pets = pick("animals", "pets", "simple animals", "lesser lifeforms", "\[REDACTED]")
var/strength = pick("human", "skrell", "vox", "grey", "diona", "IPC", "tajaran", "vulpakanin", "kidan", "plasmaman", "drask",
"slime", "monkey", "moderate", "lizard", "security", "command", "clown", "mime", "low", "very low", "greytide", "catgirl", "\[REDACTED]")
sentience_report += "<br><br>Based on [data], we believe that one of the station's [pets] has developed [strength] level intelligence, and the ability to communicate."
print_command_report(sentience_report, "NAS Trurl Update", FALSE)
/datum/event/sentience/proc/greet_sentient(mob/living/carbon/human/M)
to_chat(M, "<span class='userdanger'>Hello world!</span>")
to_chat(M, "<span class='warning'>Due to freak radiation, you have gained \
human level intelligence and the ability to speak and understand \
human level intelligence and the ability to speak and understand \
human language!</span>")

View File

@@ -274,7 +274,7 @@
// Bust through windows or other stuff blocking the way
if(!target.Enter(holder))
for(var/atom/movable/AM in target)
if(istype(AM, /obj/structure/spacevine) || !AM.density)
if(!AM.density || isvineimmune(AM))
continue
AM.ex_act(severity)
target.ex_act(severity) // vine immunity handled at /mob/ex_act
@@ -697,8 +697,10 @@
. = ..()
/proc/isvineimmune(atom/A)
. = FALSE
if(isliving(A))
var/mob/living/M = A
if(("vines" in M.faction) || ("plants" in M.faction))
. = TRUE
return TRUE
else if(istype(A, /obj/structure/spacevine) || istype(A, /obj/structure/alien/resin/flower_bud_enemy))
return TRUE
return FALSE

View File

@@ -139,7 +139,7 @@
playsound(loc, pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg'), 50, 1, -1)
return BRUTELOSS
/obj/item/scythe/pre_attackby(atom/A, mob/living/user, params)
/obj/item/scythe/pre_attack(atom/A, mob/living/user, params)
if(swiping || !istype(A, /obj/structure/spacevine) || get_turf(A) == get_turf(user))
return ..()
else

View File

@@ -41,7 +41,7 @@
new /obj/effect/temp_visual/resonance(T, user, src, burst_time)
user.changeNext_move(CLICK_CD_MELEE)
/obj/item/resonator/pre_attackby(atom/target, mob/user, params)
/obj/item/resonator/pre_attack(atom/target, mob/user, params)
if(check_allowed_items(target, 1))
CreateResonance(target, user)
return TRUE

View File

@@ -247,7 +247,7 @@
to_chat(user, "<span class='notice'>You release the wisp. It begins to bob around your head.</span>")
icon_state = "lantern"
wisp.orbit(user, 20)
INVOKE_ASYNC(wisp, /atom/movable/.proc/orbit, user, 20)
set_light(0)
user.update_sight()

View File

@@ -87,7 +87,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
real_name = name
//starts ghosts off with all HUDs.
toggle_all_huds_on()
toggle_all_huds_on(body)
..()
/mob/dead/observer/Destroy()
@@ -323,10 +323,23 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
return
GLOB.ghost_hud_panel.ui_interact(src)
/mob/dead/observer/proc/toggle_all_huds_on()
/**
* Toggles on all HUDs for the ghost player.
*
* Enables antag HUD only if the ghost belongs to an admin.
*
* Arguments:
* * user - A reference to the ghost's old mob. This argument is required since `src` does not have a `client` at this point.
*/
/mob/dead/observer/proc/toggle_all_huds_on(mob/user)
show_me_the_hud(DATA_HUD_DIAGNOSTIC)
show_me_the_hud(DATA_HUD_SECURITY_ADVANCED)
show_me_the_hud(DATA_HUD_MEDICAL_ADVANCED)
if(!check_rights((R_ADMIN | R_MOD), FALSE, user))
return
antagHUD = TRUE
for(var/datum/atom_hud/antag/H in GLOB.huds)
H.add_hud_to(src)
/mob/dead/observer/verb/set_dnr()
set name = "Set DNR"

View File

@@ -486,6 +486,14 @@
flags = RESTRICTED | HIVEMIND | NOBABEL
follow = TRUE
/datum/language/terrorspider/broadcast(mob/living/speaker, message, speaker_mask)
if(isterrorspider(speaker))
var/mob/living/simple_animal/hostile/poison/terror_spider/T = speaker
if(T.loudspeaker)
..(speaker, "<font size=3><b>[message]</b></font>")
return
..(speaker, message)
/datum/language/ling
name = "Changeling"
desc = "Although they are normally wary and suspicious of each other, changelings can commune over a distance."

View File

@@ -9,8 +9,9 @@ GLOBAL_LIST_EMPTY(empty_playable_ai_cores)
if(alert("WARNING: This will immediately wipe your core and ghost you, removing your character from the round permanently (similar to cryo and robotic storage). Are you entirely sure you want to do this?",
"Wipe Core", "No", "No", "Yes") != "Yes")
return
cryo_AI()
// We warned you.
/mob/living/silicon/ai/proc/cryo_AI()
GLOB.empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(loc)
GLOB.global_announcer.autosay("[src] has been moved to intelligence storage.", "Artificial Intelligence Oversight")

View File

@@ -284,7 +284,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
else
to_chat(src, "<span class='boldannounce'>Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.</span>")
ghostize()
error("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].")
log_runtime(EXCEPTION("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey]."), src)
mmi = null
if(connected_ai)
connected_ai.connected_robots -= src

View File

@@ -129,6 +129,7 @@
status_flags = 0
construct_type = "juggernaut"
mob_size = MOB_SIZE_LARGE
move_resist = MOVE_FORCE_STRONG
construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall)
force_threshold = 11
playstyle_string = "<b>You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \

View File

@@ -115,7 +115,7 @@
icon_living = "cow"
icon_dead = "cow_dead"
icon_gib = "cow_gib"
speak = list("moo?","moo","MOOOOOO")
speak = list("Moo?","Moo","MOOOOOO")
speak_emote = list("moos","moos hauntingly")
emote_hear = list("brays")
emote_see = list("shakes its head")

View File

@@ -56,6 +56,12 @@
for(var/obj/structure/spider/S in range(1, get_turf(src)))
return S
/mob/living/simple_animal/hostile/poison/giant_spider/death(gibbed)
. = ..()
if(!.)
return FALSE
SSmobs.giant_spiders--
//nursemaids - these create webs and eggs
/mob/living/simple_animal/hostile/poison/giant_spider/nurse
desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes."

View File

@@ -408,7 +408,7 @@ Difficulty: Hard
/obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in()
return TRUE
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon)
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(!charging)
..()

View File

@@ -25,6 +25,7 @@
regen_points_per_kill = 200 // >2x normal, since they're food reprocessors
idle_ventcrawl_chance = 5
spider_tier = TS_TIER_3
loudspeaker = TRUE
spider_opens_doors = 2
web_type = /obj/structure/spider/terrorweb/mother
var/datum/action/innate/terrorspider/ventsmash/ventsmash_action

View File

@@ -26,6 +26,7 @@
environment_smash = ENVIRONMENT_SMASH_RWALLS
idle_ventcrawl_chance = 0
spider_tier = TS_TIER_3
loudspeaker = TRUE
spider_opens_doors = 2
web_type = /obj/structure/spider/terrorweb/purple
ai_spins_webs = FALSE

View File

@@ -33,6 +33,7 @@
projectilesound = 'sound/weapons/pierce.ogg'
projectiletype = /obj/item/projectile/terrorqueenspit
spider_tier = TS_TIER_4
loudspeaker = TRUE
spider_opens_doors = 2
web_type = /obj/structure/spider/terrorweb/queen
var/spider_spawnfrequency = 1200 // 120 seconds. Default for player queens and NPC queens on station. Awaymission queens have this changed in New()

View File

@@ -137,6 +137,8 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
var/killcount = 0
var/busy = 0 // leave this alone!
var/spider_tier = TS_TIER_1 // 1 for red,gray,green. 2 for purple,black,white, 3 for prince, mother. 4 for queen
/// Does this terror speak loudly on the terror hivemind?
var/loudspeaker = FALSE
var/hasdied = 0
var/list/spider_special_drops = list()
var/attackstep = 0

View File

@@ -20,6 +20,7 @@
melee_damage_lower = 5
melee_damage_upper = 15
spider_tier = TS_TIER_2
loudspeaker = TRUE
web_type = /obj/structure/spider/terrorweb/white

View File

@@ -49,6 +49,7 @@
name = "venus human trap"
desc = "Now you know how the fly feels."
icon_state = "venus_human_trap"
icon_living = "venus_human_trap"
mob_biotypes = MOB_ORGANIC | MOB_PLANT
layer = MOB_LAYER + 0.9
health = 50

View File

@@ -20,10 +20,10 @@
spawn() alert("You have logged in already with another key this round, please log out of this one NOW or risk being banned!")
if(matches)
if(M.client)
message_admins("<font color='red'><B>Notice: </B><font color='blue'><A href='?src=[usr.UID()];priv_msg=[src.client.ckey]'>[key_name_admin(src)]</A> has the same [matches] as <A href='?src=[usr.UID()];priv_msg=[M.client.ckey]'>[key_name_admin(M)]</A>.</font>", 1)
message_admins("<font color='red'><B>Notice: </B><font color='#EB4E00'><A href='?src=[usr.UID()];priv_msg=[src.client.ckey]'>[key_name_admin(src)]</A> has the same [matches] as <A href='?src=[usr.UID()];priv_msg=[M.client.ckey]'>[key_name_admin(M)]</A>.</font>", 1)
log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].")
else
message_admins("<font color='red'><B>Notice: </B><font color='blue'><A href='?src=[usr.UID()];priv_msg=[src.client.ckey]'>[key_name_admin(src)]</A> has the same [matches] as [key_name_admin(M)] (no longer logged in). </font>", 1)
message_admins("<font color='red'><B>Notice: </B><font color='#EB4E00'><A href='?src=[usr.UID()];priv_msg=[src.client.ckey]'>[key_name_admin(src)]</A> has the same [matches] as [key_name_admin(M)] (no longer logged in). </font>", 1)
log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).")
/mob/Login()

View File

@@ -116,7 +116,9 @@
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("[question]?", poll_time = 10 SECONDS, min_hours = minhours, source = M)
var/mob/dead/observer/theghost = null
if(LAZYLEN(candidates))
if(length(candidates))
if(QDELETED(M))
return
theghost = pick(candidates)
to_chat(M, "Your mob has been taken over by a ghost!")
message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)])")

View File

@@ -172,7 +172,7 @@
if(alert(src,"Are you sure you wish to observe? You cannot normally join the round after doing this!","Player Setup","Yes","No") == "Yes")
if(!client)
return 1
var/mob/dead/observer/observer = new()
var/mob/dead/observer/observer = new(src)
src << browse(null, "window=playersetup")
spawning = 1
stop_sound_channel(CHANNEL_LOBBYMUSIC)

View File

@@ -80,6 +80,7 @@
/datum/job/chaplain,
/datum/job/ntnavyofficer,
/datum/job/ntspecops,
/datum/job/ntspecops/solgovspecops,
/datum/job/civilian,
/datum/job/syndicateofficer
)

View File

@@ -730,7 +730,20 @@
return
if(moveable && default_unfasten_wrench(user, I, time = 20))
return
if(user.drop_item())
if(istype(I, /obj/item/scalpel/supermatter))
var/obj/item/scalpel/supermatter/scalpel = I
to_chat(user, "<span class='notice'>You carefully begin to scrape [src] with [I]...</span>")
if(I.use_tool(src, user, 10 SECONDS, volume = 100))
if(scalpel.uses_left)
to_chat(user, "<span class='danger'>You extract a sliver from [src], and it begins to react violently!</span>")
new /obj/item/nuke_core/supermatter_sliver(drop_location())
matter_power += 800
scalpel.uses_left--
if(!scalpel.uses_left)
to_chat(user, "<span class='boldwarning'>A tiny piece of [I] falls off, rendering it useless!</span>")
else
to_chat(user, "<span class='warning'>You fail to extract a sliver from [src]! [I] isn't sharp enough anymore.</span>")
else if(user.drop_item())
user.visible_message("<span class='danger'>As [user] touches [src] with \a [I], silence fills the room...</span>",\
"<span class='userdanger'>You touch [src] with [I], and everything suddenly goes silent.</span>\n<span class='notice'>[I] flashes into dust as you flinch away from [src].</span>",\
"<span class='italics'>Everything suddenly goes silent.</span>")

View File

@@ -78,6 +78,8 @@
for(var/atom/T in get_turf(D))
D.reagents.reaction(T)
sleep(3)
if(QDELETED(D))
return
qdel(D)

View File

@@ -153,10 +153,14 @@
resistance_flags = FLAMMABLE
var/static/list/no_wrap = list(/obj/item/smallDelivery, /obj/structure/bigDelivery, /obj/item/evidencebag, /obj/structure/closet/body_bag, /obj/item/twohanded/required)
/obj/item/stack/packageWrap/afterattack(obj/target as obj, mob/user as mob, proximity)
if(!proximity) return
if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete
/obj/item/stack/packageWrap/pre_attack(atom/A, mob/living/user, params)
. = ..()
if(!in_range(A, user))
return
if(!isobj(A))
return
var/obj/target = A
if(is_type_in_list(target, no_wrap))
return
if(target.anchored)
@@ -166,62 +170,61 @@
if(istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box) && !istype(target, /obj/item/shippingPackage)))
var/obj/item/O = target
if(use(1))
var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up!
if(!istype(O.loc, /turf))
if(user.client)
user.client.screen -= O
P.wrapped = O
O.loc = P
var/i = round(O.w_class)
if(i in list(1,2,3,4,5))
P.icon_state = "deliverycrate[i]"
P.w_class = i
P.add_fingerprint(usr)
O.add_fingerprint(usr)
add_fingerprint(usr)
else
return
if(!use(1))
return FALSE
var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up!
if(!isturf(O.loc))
if(user.client)
user.client.screen -= O
P.wrapped = O
O.loc = P
var/i = round(O.w_class)
if(i in list(1,2,3,4,5))
P.icon_state = "deliverycrate[i]"
P.w_class = i
P.add_fingerprint(user)
O.add_fingerprint(user)
add_fingerprint(user)
else if(istype(target, /obj/structure/closet/crate))
var/obj/structure/closet/crate/O = target
if(O.opened)
return
if(amount >= 3 && do_after_once(user, 15, target = target))
if(O.opened || !use(3))
return
var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc))
P.icon_state = "deliverycrate"
P.wrapped = O
O.loc = P
else
to_chat(user, "<span class='notice'>You need more paper.</span>")
return
else if(istype (target, /obj/structure/closet))
var/obj/structure/closet/O = target
if(O.opened)
return
if(amount >= 3 && do_after_once(user, 15, target = target))
if(O.opened || !use(3))
return
var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc))
P.wrapped = O
P.init_welded = O.welded
O.welded = 1
O.loc = P
else
to_chat(user, "<span class='notice'>You need more paper.</span>")
return
var/obj/structure/bigDelivery/D = wrap_closet(target, user)
if(!D)
return FALSE
D.icon_state = "deliverycrate"
else if(istype(target, /obj/structure/closet))
var/obj/structure/closet/C = target
var/obj/structure/bigDelivery/D = wrap_closet(target, user)
if(!D)
return FALSE
D.init_welded = C.welded
C.welded = TRUE
else
to_chat(user, "<span class='notice'>The object you are trying to wrap is unsuitable for the sorting machinery.</span>")
return
return FALSE
user.visible_message("<span class='notice'>[user] wraps [target].</span>")
user.create_attack_log("<font color='blue'>Has used [name] on [target]</font>")
add_attack_logs(user, target, "used [name]", ATKLOG_ALL)
if(amount <= 0 && !src.loc) //if we used our last wrapping paper, drop a cardboard tube
new /obj/item/c_tube( get_turf(user) )
return
if(amount <= 0 && QDELETED(src)) //if we used our last wrapping paper, drop a cardboard tube
var/obj/item/c_tube/T = new(get_turf(user))
user.put_in_active_hand(T)
return FALSE
// Separate proc to avoid copy pasting the code twice
/obj/item/stack/packageWrap/proc/wrap_closet(obj/structure/closet/C, mob/user)
if(C.opened)
return
if(amount < 3)
to_chat(user, "<span class='warning'>You need more paper.</span>")
return
if(!do_after_once(user, 1.5 SECONDS, target = C) || C.opened || !use(3)) // Checking these again since it's after a delay
return
var/obj/structure/bigDelivery/P = new(get_turf(C))
P.wrapped = C
C.loc = P
return P
/obj/item/destTagger
name = "destination tagger"

View File

@@ -205,7 +205,7 @@
var/ghostmsg = "Play as [SM.name], pet of [user.name]?"
var/list/candidates = SSghost_spawns.poll_candidates(ghostmsg, ROLE_SENTIENT, FALSE, 10 SECONDS, source = M)
if(!src)
if(QDELETED(src) || QDELETED(SM))
return
if(candidates.len)

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