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). [Issue Report Template](ISSUE_TEMPLATE.md).
## Commenting ## 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 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. 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 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.
point out specifically and concisely your reasoning in a comment on the issue report.
#### 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 ## Issues
The Issues section is not a place to request features, or ask for things to be changed 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 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 reporting bugs in the code.
should be in.
#### Guidelines: #### Guidelines:
* Issue reports should be as detailed as possible, and if applicable, should include * 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. * 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 #### Using Changelog
* Tags used in changelog include add/rscadd, del/rscdel, fix/fixes, typo/spellcheck. * 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. * Without specifying a name it will default to using your GitHub name. Some examples include:
Some examples
``` ```
:cl: :cl:
add: The ability to change the color of wires add: The ability to change the color of wires
del: Deleted depreciated wire merging now handled in parent del: Deleted depreciated wire merging now handled in parent
fix: Moving wires now follows the user input instead of moving the stack fix: Moving wires now follows the user input instead of moving the stack
/:cl: /:cl:
``` ```
``` ```
:cl: N3X15 :cl: UsernameHere
typo: Fixes some misspelled words under Using Changelog spellcheck: Fixes some misspelled words under Using Changelog
/:cl: /:cl:
``` ```
## Specifications ## Specifications
@@ -113,14 +116,13 @@ datum
code code
``` ```
The use of this is not allowed in this project *unless the majority of the file is already relatively pathed* as it makes finding definitions via full text The use of this 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.
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: The previous code made compliant:
```DM ```DM
/datum/datum1 /datum/datum1
var/varname1 var/varname1 = 1
var/varname2 var/varname2
var/static/varname3 var/static/varname3
var/static/varname4 var/static/varname4
@@ -129,6 +131,7 @@ The previous code made compliant:
code code
/datum/datum1/proc/proc2() /datum/datum1/proc/proc2()
code code
/datum/datum1/datum2 /datum/datum1/datum2
varname1 = 0 varname1 = 0
/datum/datum1/datum2/proc/proc3() /datum/datum1/datum2/proc/proc3()
@@ -139,11 +142,11 @@ The previous code made compliant:
``` ```
### User Interfaces ### 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) 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 ### 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 / ### Type paths must begin with a /
eg: `/datum/thing`, not `datum/thing` 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: 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 ```DM
//Good
var/path_type = /obj/item/baseball_bat
//Bad //Bad
var/path_type = "/obj/item/baseball_bat" var/path_type = "/obj/item/baseball_bat"
//Good
var/path_type = /obj/item/baseball_bat
``` ```
### Do not use `\The`. ### 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. ever forming.
```DM ```DM
//Bad
var/atom/A
"\The [A]"
//Good //Good
var/atom/A var/atom/A
"[A]" "[A]"
//Bad
"\The [A]"
``` ```
### Use the pronoun library instead of `\his` macros. ### 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; 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. * BYOND's `\his` macro can be unpredictable on what object it references.
Take this example: `"[user] waves \his [user.weapon] around, hitting \his opponents!"`. Take this example: `"[user] waves \his [user.weapon] around, hitting \his opponents!"`.
This will end up referencing the user's gender in the first occurence, but what about the second? This will end up referencing the user's gender in the first occurence, but what about the second?
It'll actually print the gender set on the weapon he's carrying, which is unintended - and there's no way around this. It'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."` 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."`
``` ```DM
//Good
"[H] waves [H.p_their()] hands!"
"[user] waves [H.p_their()] [user.weapon] around, hitting [H.p_their()] opponents!"`
//Bad //Bad
"[H] waves \his hands!" "[H] waves \his hands!"
"[user] waves \his [user.weapon] around, hitting \his opponents!" "[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]` ### 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 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)`. 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. While DM allows other ways of declaring variables, this one should be used for consistency.
### Tabs, not spaces ### Tabs, not spaces
You must use tabs to indent your code, 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 ### 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. ( 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.)
Protip: 'I couldn't immediately think of a proper way so thus there must be no other option' is not gonna cut it here! If you can't think of anything else, say that outright and admit that you need help with it. Maintainers exist for exactly that reason.)
You can avoid hacky code by using object-oriented methodologies, such as overriding a function (called "procs" in DM) or sectioning code into functions and You can avoid hacky code by using object-oriented methodologies, such as overriding a function (called "procs" in DM) or sectioning code into functions and
then overriding them as required. 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 ### 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. 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: 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! 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. 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 ### 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/ ### 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. 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. //Good
Bad: /obj/item/proc1(mob/input1, input2)
```` code
obj/item/proc1(var/input1, var/input2) ```
````
Good:
````
obj/item/proc1(input1, input2)
````
### No magic numbers or strings ### No magic numbers or strings
This means stuff like having a "mode" variable for an object set to "1" or "2" with no clear indicator of what that means. Make these #defines with a name that This means stuff like having a "mode" variable for an object set to "1" or "2" with no clear indicator of what that means. Make these #defines with a name that more clearly states what it's for. For instance:
more clearly states what it's for. For instance: ```DM
````DM //Bad
/datum/proc/do_the_thing(thing_to_do) /datum/proc/do_the_thing(thing_to_do)
switch(thing_to_do) switch(thing_to_do)
if(1) if(1)
(...) do_stuff()
if(2) if(2)
(...) do_other_stuff()
```` ```
There's no indication of what "1" and "2" mean! Instead, you should do something like this: 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_REALLY_HARD 1
#define DO_THE_THING_EFFICIENTLY 2 #define DO_THE_THING_EFFICIENTLY 2
/datum/proc/do_the_thing(thing_to_do) /datum/proc/do_the_thing(thing_to_do)
switch(thing_to_do) switch(thing_to_do)
if(DO_THE_THING_REALLY_HARD) if(DO_THE_THING_REALLY_HARD)
(...) do_stuff()
if(DO_THE_THING_EFFICIENTLY) if(DO_THE_THING_EFFICIENTLY)
(...) do_other_stuff()
```` ```
This is clearer and enhances readability of your code! Get used to doing it! This is clearer and enhances readability of your code! Get used to doing it!
### Control statements ### Control statements
(if, while, for, etc) (if, while, for, etc)
* All control statements must not contain code on the same line as the statement (`if(condition) return`)
* All control statements comparing a variable to a number should use the formula of `thing` `operator` `number`, not the reverse * All control statements comparing a variable to a number should use the formula of `thing` `operator` `number`, not the reverse
(eg: `if(count <= 10)` not `if(10 >= count)`) (eg: `if(count <= 10)` not `if(10 >= count)`)
* All control statements must be spaced as `if()`, with the brackets touching the keyword. * All control statements must be spaced as `if()`, with the brackets touching the keyword.
* Do not use one-line control statements. * All control statements must not contain code on the same line as the statement.
Instead of doing
``` ```DM
//Bad
if(x) return if(x) return
```
You should do //Good
```
if(x) if(x)
return return
``` ```
### Player Output ### 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"`, 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.
you must use `to_chat(mob/client/world, "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. Do not enclose a proc in an if-block when returning on a condition is more feasible.
This is bad: This is bad:
````DM ````DM
/datum/datum1/proc/proc1() /datum/datum1/proc/proc1()
if(thing1) if(thing1)
if(!thing2) if(!thing2)
if(thing3 == 30) if(thing3 == 30)
do stuff do stuff
```` ````
This is good: This is good:
````DM ````DM
/datum/datum1/proc/proc1() /datum/datum1/proc/proc1()
if(!thing1) if(!thing1)
return return
if(thing2) if(thing2)
return return
if(thing3 != 30) if(thing3 != 30)
return return
do stuff do stuff
```` ````
This prevents nesting levels from getting deeper then they need to be. This prevents nesting levels from getting deeper then they need to be.
### Uses addtimer() instead of sleep() or spawn() ### 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. 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. 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. 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: //Good
````DM /datum/datum1/proc/proc1(target)
/datum/datum1/proc/proc1() addtimer(CALLBACK(target, .proc/dothing, arg1, arg2, arg3), 5 SECONDS)
spawn(5) ```
dothing(arg1, arg2, arg3)
````
This is good:
````DM
addtimer(CALLBACK(procsource, .proc/dothing, arg1, arg2, arg3), waittime, timertype)
````
This prevents nesting levels from getting deeper then they need to be. This prevents nesting levels from getting deeper then they need to be.
### Operators ### Operators
#### Spacing #### Spacing
* Operators that should be separated by spaces * Operators that should be separated by spaces:
* Boolean and logic operators like &&, || <, >, ==, etc (but not !) * Boolean and logic operators like `&&`, `||` `<`, `>`, `==`, etc. (But not `!`)
* Bitwise AND & and OR | * Bitwise AND `&` and OR `|`.
* Argument separator operators like , (and ; when used in a forloop) * Argument separator operators like `,`. (and `;` when used in a forloop)
* Assignment operators like = or += or the like * Assignment operators like `=` or `+=` or the like.
* Math operators like +, -, /, or \* * Math operators like `+`, `-`, `/`, or `*`.
* Operators that should not be separated by spaces * Operators that should NOT be separated by spaces:
* Access operators like . and : * Access operators like `.` and `:`.
* Parentheses () * Parentheses `()`.
* logical not ! * Logical not `!`.
#### Use #### Use
* Bitwise AND - '&' * Bitwise AND `&`
* Should be written as ```bitfield & bitflag``` NEVER ```bitflag & bitfield```, both are valid, but the latter is confusing and nonstandard. * 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 * 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 #### 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_ONE (1<<0)
#define MACRO_TWO (1<<1) #define MACRO_TWO (1<<1)
#define MACRO_THREE (1<<2) #define MACRO_THREE (1<<2)
``` ```
Is preferable to Is preferable to:
``` ```
#define MACRO_ONE 1 #define MACRO_ONE 1
#define MACRO_TWO 2 #define MACRO_TWO 2
#define MACRO_THREE 4 #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 ### 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: 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 * To display messages to all mobs that can view `user`, you should use `visible_message()`.
`visible_message()`.
* Bad: ```DM
``` //Bad
for(var/mob/M in viewers(src)) for(var/mob/M in viewers(user))
M.show_message("<span class='warning'>Arbitrary text</span>") M.show_message("<span class='warning'>Arbitrary text</span>")
```
* Good: //Good
``` user.visible_message("<span class='warning'>Arbitrary text</span>")
visible_message("<span class='warning'>Arbitrary text</span>") ```
```
* You should not use color macros (`\red, \blue, \green, \black`) to color text, * 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>`, instead, you should use span classes. `<span class='warning'>Red text</span>`, `<span class='notice'>Blue text</span>`.
`<span class='notice'>blue text</span>`.
* Bad:
``` ```
to_chat("\red Red Text \black black text") //Bad
``` to_chat(user, "\red Red text \black Black text")
* Good:
``` //Good
to_chat("<span class='warning'>Red Text</span>black text") to_chat(user, "<span class='warning'>Red text</span>Black text")
``` ```
* To use variables in strings, you should **never** use the `text()` operator, use * To use variables in strings, you should **never** use the `text()` operator, use
embedded expressions directly in the string. embedded expressions directly in the string.
* Bad:
``` ```DM
to_chat(text("[] is leaking []!", src.name, src.liquid_type)) //Bad
``` to_chat(user, text("[] is leaking []!", name, liquid_type))
* Good:
``` //Good
to_chat("[src] is leaking [liquid_type]") to_chat(user, "[name] is leaking [liquid_type]!")
``` ```
* To reference a variable/proc on the src object, you should **not** use * 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 `src.var`/`src.proc()`. The `src.` in these cases is implied, so you should just use
`var`/`proc()`. `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 ### 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. * 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
```dm //Bad
var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey='[target_ckey]'") 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. * 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 ### 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. * 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 * 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. 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. * 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.) * 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 ### 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. * 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. * 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. * All new var/proc names should use the American English spelling of words. This is for consistency with BYOND.
### Dream Maker Quirks/Tricks ### 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 #### In-To for-loops
```for(var/i = 1, i <= some_value, i++)``` is a fairly standard way to write an incremental for loop in most languages (especially those in the C family), but `for(var/i = 1, i <= some_value, i++)` is a fairly standard way to write an incremental for loop in most languages (especially those in the C family), but
DM's ```for(var/i in 1 to some_value)``` syntax is oddly faster than its implementation of the former syntax; where possible, it's advised to use DM's syntax. ( DM's `for(var/i in 1 to some_value)` syntax is oddly faster than its implementation of the former syntax; where possible, it's advised to use DM's syntax. (
Note, the ```to``` keyword is inclusive, so it automatically defaults to replacing ```<=```; if you want ```<``` then you should write it as ```1 to Note, the `to` keyword is inclusive, so it automatically defaults to replacing `<=`; if you want `<` then you should write it as `1 to
some_value-1```). 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! 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: 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: Code used for the test in a readable format:
https://pastebin.com/w50uERkG https://pastebin.com/w50uERkG
#### Istypeless for loops #### 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: 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 ```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 var/obj/item/sword/best_sword
for(var/obj/item/sword/S in bag_of_items) for(var/obj/item/sword/S in bag_of_items)
if(!best_sword || S.damage > best_sword.damage) if(!best_sword || S.damage > best_sword.damage)
best_sword = S 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 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 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``` 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: contained ONLY swords, or only SUBTYPES of swords, then the above is inefficient. For example:
```DM ```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 var/obj/item/sword/best_sword
for(var/obj/item/sword/S in bag_of_swords) for(var/obj/item/sword/S in bag_of_swords)
if(!best_sword || S.damage > best_sword.damage) 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. 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 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, 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 ```DM
var/list/bag_of_swords = list(sword, sword, sword, sword) var/list/bag_of_swords = list(sword, sword, sword, sword)
var/obj/item/sword/best_sword var/obj/item/sword/best_sword
@@ -607,7 +623,7 @@ eg:
var/mob/living/carbon/human/H = YOU_THE_READER var/mob/living/carbon/human/H = YOU_THE_READER
H.gib() 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. 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 ### 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: To declare a global var:
```DM ```DM
@@ -648,13 +664,13 @@ responsible for properly tagging new pull requests and issues, moderating commen
pull requests/issues, and merging/closing pull requests. pull requests/issues, and merging/closing pull requests.
### Maintainer List ### 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) * [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 ### 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 merging it yourself. A different maintainer must review and merge your pull request, no
matter how trivial. This is to ensure quality. matter how trivial. This is to ensure quality.
* A subset of this instruction: Do not push directly to the repository, always make a * A subset of this instruction: Do not push directly to the repository, always make a

View File

@@ -2,4 +2,4 @@
"workbench.editorAssociations": { "workbench.editorAssociations": {
"*.dmi": "imagePreview.previewEditor" "*.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 BELOW_OPEN_DOOR_LAYER 2.6
#define BLASTDOOR_LAYER 2.65 #define BLASTDOOR_LAYER 2.65
#define OPEN_DOOR_LAYER 2.7 #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 PROJECTILE_HIT_THRESHHOLD_LAYER 2.75 //projectiles won't hit objects at or below this layer if possible
#define TABLE_LAYER 2.8 #define TABLE_LAYER 2.8
#define BELOW_OBJ_LAYER 2.9 #define BELOW_OBJ_LAYER 2.9
@@ -52,6 +51,7 @@
#define SHUTTER_LAYER 3.12 // HERE BE DRAGONS #define SHUTTER_LAYER 3.12 // HERE BE DRAGONS
#define ABOVE_OBJ_LAYER 3.2 #define ABOVE_OBJ_LAYER 3.2
#define ABOVE_WINDOW_LAYER 3.3 #define ABOVE_WINDOW_LAYER 3.3
#define DOOR_HELPER_LAYER 3.31 // Keep this above doors and windoors
#define SIGN_LAYER 3.4 #define SIGN_LAYER 3.4
#define NOT_HIGH_OBJ_LAYER 3.5 #define NOT_HIGH_OBJ_LAYER 3.5
#define HIGH_OBJ_LAYER 3.6 #define HIGH_OBJ_LAYER 3.6

View File

@@ -50,7 +50,7 @@ GLOBAL_PROTECT(log_end)
/proc/log_access_in(client/new_client) /proc/log_access_in(client/new_client)
if(GLOB.configuration.logging.access_logging) 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]") rustg_log_write(GLOB.world_game_log, "ACCESS IN: [message][GLOB.log_end]")
/proc/log_access_out(mob/last_mob) /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 UNSETEMPTY(L) if (L && !L.len) L = null
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!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 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 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 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() #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(stone)
if(alert(usr, "Do you want to be captured by [stoner]'s soul stone? This will destroy your corpse and make it \ 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") 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() /obj/screen/alert/notify_soulstone/Destroy()
stone = null stone = null

View File

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

View File

@@ -14,6 +14,8 @@
var/shutdown_shell_command = null var/shutdown_shell_command = null
/// 2FA backend server host /// 2FA backend server host
var/_2fa_auth_host = null 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) /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 // 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(medal_hub_password, data["medal_hub_password"])
CONFIG_LOAD_STR(shutdown_shell_command, data["shutdown_shell_command"]) CONFIG_LOAD_STR(shutdown_shell_command, data["shutdown_shell_command"])
CONFIG_LOAD_STR(_2fa_auth_host, data["_2fa_auth_host"]) 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/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/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() 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() /datum/controller/subsystem/mobs/stat_entry()
..("P:[GLOB.mob_living_list.len]") ..("P:[GLOB.mob_living_list.len]")

View File

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

View File

@@ -647,20 +647,20 @@
name = "Solar Federation Marine" name = "Solar Federation Marine"
uniform = /obj/item/clothing/under/solgov uniform = /obj/item/clothing/under/solgov
suit = /obj/item/clothing/suit/armor/bulletproof 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 belt = /obj/item/storage/belt/military/assault/marines/full
head = /obj/item/clothing/head/soft/solgov/marines head = /obj/item/clothing/head/soft/solgov/marines
glasses = /obj/item/clothing/glasses/night glasses = /obj/item/clothing/glasses/night
gloves = /obj/item/clothing/gloves/combat gloves = /obj/item/clothing/gloves/combat
shoes = /obj/item/clothing/shoes/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 id = /obj/item/card/id
l_hand = /obj/item/gun/projectile/automatic/shotgun/bulldog l_hand = /obj/item/gun/projectile/automatic/shotgun/bulldog
suit_store = /obj/item/gun/projectile/automatic/pistol/m1911 suit_store = /obj/item/gun/projectile/automatic/pistol/m1911
r_pocket = /obj/item/flashlight/seclite r_pocket = /obj/item/flashlight/seclite
pda = /obj/item/pda pda = /obj/item/pda
box = /obj/item/storage/box/responseteam
backpack_contents = list( backpack_contents = list(
/obj/item/storage/box/responseteam = 1,
/obj/item/clothing/shoes/magboots = 1, /obj/item/clothing/shoes/magboots = 1,
/obj/item/whetstone = 1, /obj/item/whetstone = 1,
/obj/item/clothing/mask/gas/explorer/marines = 1, /obj/item/clothing/mask/gas/explorer/marines = 1,
@@ -702,13 +702,12 @@
head = /obj/item/clothing/head/beret/solgov/command head = /obj/item/clothing/head/beret/solgov/command
glasses = /obj/item/clothing/glasses/night glasses = /obj/item/clothing/glasses/night
back = /obj/item/storage/backpack/satchel 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 l_hand = null
belt = /obj/item/melee/baton/loaded belt = /obj/item/melee/baton/loaded
suit_store = /obj/item/gun/projectile/automatic/pistol/deagle suit_store = /obj/item/gun/projectile/automatic/pistol/deagle
l_pocket = /obj/item/pinpointer/advpinpointer l_pocket = /obj/item/pinpointer/advpinpointer
backpack_contents = list( backpack_contents = list(
/obj/item/storage/box/responseteam = 1,
/obj/item/storage/box/handcuffs = 1, /obj/item/storage/box/handcuffs = 1,
/obj/item/clothing/shoes/magboots/advance = 1, /obj/item/clothing/shoes/magboots/advance = 1,
/obj/item/reagent_containers/hypospray/autoinjector/survival = 1, /obj/item/reagent_containers/hypospray/autoinjector/survival = 1,
@@ -720,14 +719,14 @@
/datum/outfit/admin/solgov/elite /datum/outfit/admin/solgov/elite
name = "Solar Federation Specops Marine" name = "Solar Federation Specops Marine"
uniform = /obj/item/clothing/under/solgov/elite 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 belt = /obj/item/storage/belt/military/assault/marines/elite/full
l_hand = /obj/item/gun/projectile/automatic/ar l_hand = /obj/item/gun/projectile/automatic/ar
backpack_contents = list( backpack_contents = list(
/obj/item/storage/box/responseteam = 1,
/obj/item/clothing/shoes/magboots/advance = 1, /obj/item/clothing/shoes/magboots/advance = 1,
/obj/item/whetstone = 1, /obj/item/whetstone = 1,
/obj/item/clothing/mask/gas/explorer/marines = 1,
/obj/item/reagent_containers/hypospray/autoinjector/survival = 1 /obj/item/reagent_containers/hypospray/autoinjector/survival = 1
) )
cybernetic_implants = list( cybernetic_implants = list(
@@ -737,22 +736,22 @@
/obj/item/organ/internal/cyberimp/arm/flash, /obj/item/organ/internal/cyberimp/arm/flash,
/obj/item/organ/internal/eyes/cybernetic/shield /obj/item/organ/internal/eyes/cybernetic/shield
) )
/datum/outfit/admin/solgov/elite/lieutenant /datum/outfit/admin/solgov/elite/lieutenant
name = "Solar Federation Specops Lieutenant" name = "Solar Federation Specops Lieutenant"
uniform = /obj/item/clothing/under/solgov/command/elite 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 glasses = /obj/item/clothing/glasses/night
back = /obj/item/storage/backpack/satchel
belt = /obj/item/melee/baton/loaded belt = /obj/item/melee/baton/loaded
l_hand = null l_hand = null
suit_store = /obj/item/gun/projectile/automatic/pistol/deagle suit_store = /obj/item/gun/projectile/automatic/pistol/deagle
l_pocket = /obj/item/pinpointer/advpinpointer 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( backpack_contents = list(
/obj/item/storage/box/responseteam = 1,
/obj/item/storage/box/handcuffs = 1, /obj/item/storage/box/handcuffs = 1,
/obj/item/clothing/shoes/magboots/advance = 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/reagent_containers/hypospray/autoinjector/survival = 1,
/obj/item/ammo_box/magazine/m50 = 3 /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>") to_chat(user, "<span class='warning'>You shouldn't have this spell! Something's wrong.</span>")
return 0 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 return 0
if(!holy_area_cancast && user.holy_check()) if(!holy_area_cancast && user.holy_check())

View File

@@ -273,6 +273,12 @@
/atom/proc/HasProximity(atom/movable/AM) /atom/proc/HasProximity(atom/movable/AM)
return 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) /atom/proc/emp_act(severity)
return return

View File

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

View File

@@ -238,7 +238,7 @@
blobber.LoseTarget() blobber.LoseTarget()
spawn() spawn()
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blobbernaut?", ROLE_BLOB, TRUE, 10 SECONDS, source = blobber) 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) var/mob/C = pick(candidates)
if(C) if(C)
blobber.key = C.key blobber.key = C.key

View File

@@ -11,7 +11,7 @@ GLOBAL_LIST_INIT(possible_changeling_IDs, list("Alpha","Beta","Gamma","Delta","E
name = "changeling" name = "changeling"
config_tag = "changeling" config_tag = "changeling"
restricted_jobs = list("AI", "Cyborg") 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") protected_species = list("Machine")
required_players = 15 required_players = 15
required_enemies = 1 required_enemies = 1

View File

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

View File

@@ -48,7 +48,7 @@ GLOBAL_LIST_EMPTY(all_cults)
/datum/game_mode/cult /datum/game_mode/cult
name = "cult" name = "cult"
config_tag = "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() protected_jobs = list()
required_players = 30 required_players = 30
required_enemies = 3 required_enemies = 3

View File

@@ -607,7 +607,7 @@ structure_check() searches for nearby cultist structures required for the invoca
set waitfor = FALSE 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>") 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) 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) 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>") 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.") 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)) 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>") 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 return
if(QDELETED(guardian)) // Just in case
return
var/mob/dead/observer/new_stand = pick(candidates) 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>") 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>") visible_message("<span class='warning'>[src] disappears in a flash of red light!</span>")
qdel(src) qdel(src)
return return
if(QDELETED(src)) // Just in case
return
var/mob/M = pick(demon_candidates) var/mob/M = pick(demon_candidates)
var/mob/living/simple_animal/slaughter/cult/S = src var/mob/living/simple_animal/slaughter/cult/S = src
if(!M || !M.client) if(!M || !M.client)

View File

@@ -4,6 +4,10 @@
#define NUKE_SEALANT_OPEN 3 #define NUKE_SEALANT_OPEN 3
#define NUKE_UNWRENCHED 4 #define NUKE_UNWRENCHED 4
#define NUKE_MOBILE 5 #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) GLOBAL_VAR(bomb_set)
@@ -25,12 +29,17 @@ GLOBAL_VAR(bomb_set)
var/yes_code = FALSE var/yes_code = FALSE
var/safety = TRUE var/safety = TRUE
var/obj/item/disk/nuclear/auth = null var/obj/item/disk/nuclear/auth = null
var/removal_stage = NUKE_INTACT var/obj/item/nuke_core/plutonium/core = null
var/lastentered var/lastentered
var/is_syndicate = FALSE var/is_syndicate = FALSE
use_power = NO_POWER_USE use_power = NO_POWER_USE
var/previous_level = "" var/previous_level = ""
var/datum/wires/nuclearbomb/wires = null 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 /obj/machinery/nuclearbomb/syndicate
is_syndicate = TRUE is_syndicate = TRUE
@@ -39,16 +48,19 @@ GLOBAL_VAR(bomb_set)
extended = FALSE extended = FALSE
anchored = FALSE anchored = FALSE
/obj/machinery/nuclearbomb/New() /obj/machinery/nuclearbomb/Initialize()
..() . = ..()
r_code = rand(10000, 99999.0) // Creates a random code upon object spawn. r_code = rand(10000, 99999) // Creates a random code upon object spawn.
wires = new/datum/wires/nuclearbomb(src) wires = new/datum/wires/nuclearbomb(src)
previous_level = get_security_level() previous_level = get_security_level()
GLOB.poi_list |= src 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() /obj/machinery/nuclearbomb/Destroy()
SStgui.close_uis(wires) SStgui.close_uis(wires)
QDEL_NULL(wires) QDEL_NULL(wires)
QDEL_NULL(core)
GLOB.poi_list.Remove(src) GLOB.poi_list.Remove(src)
return ..() return ..()
@@ -73,16 +85,41 @@ GLOBAL_VAR(bomb_set)
else else
to_chat(user, "<span class='notice'>You need to deploy [src] first.</span>") to_chat(user, "<span class='notice'>You need to deploy [src] first.</span>")
return 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)) 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>") 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
return ..() return ..()
/obj/machinery/nuclearbomb/crowbar_act(mob/user, obj/item/I) /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 . = TRUE
if(!I.tool_use_check(user, 0)) if(!I.tool_use_check(user, 0))
return return
@@ -92,9 +129,26 @@ GLOBAL_VAR(bomb_set)
return return
user.visible_message("[user] forces open the bolt covers on [src].", "You force open the bolt covers.") user.visible_message("[user] forces open the bolt covers on [src].", "You force open the bolt covers.")
removal_stage = NUKE_COVER_OPEN 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...") 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 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!") 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 anchored = FALSE
@@ -126,15 +180,19 @@ GLOBAL_VAR(bomb_set)
. = TRUE . = TRUE
if(!I.use_tool(src, user, 0, volume = I.tool_volume)) if(!I.use_tool(src, user, 0, volume = I.tool_volume))
return return
if(auth) if(auth || (istype(I, /obj/item/screwdriver/nuke)))
if(!panel_open) if(!panel_open)
panel_open = TRUE panel_open = TRUE
overlays += image(icon, "npanel_open") overlays += image(icon, "npanel_open")
to_chat(user, "You unscrew the control panel of [src].") to_chat(user, "You unscrew the control panel of [src].")
anchor_stage = removal_stage
removal_stage = core_stage
else else
panel_open = FALSE panel_open = FALSE
overlays -= image(icon, "npanel_open") overlays -= image(icon, "npanel_open")
to_chat(user, "You screw the control panel of [src] back on.") to_chat(user, "You screw the control panel of [src] back on.")
core_stage = removal_stage
removal_stage = anchor_stage
else else
if(!panel_open) if(!panel_open)
to_chat(user, "[src] emits a buzzing noise, the panel staying locked in.") to_chat(user, "[src] emits a buzzing noise, the panel staying locked in.")
@@ -142,6 +200,8 @@ GLOBAL_VAR(bomb_set)
panel_open = FALSE panel_open = FALSE
overlays -= image(icon, "npanel_open") overlays -= image(icon, "npanel_open")
to_chat(user, "You screw the control panel of [src] back on.") to_chat(user, "You screw the control panel of [src] back on.")
core_stage = removal_stage
removal_stage = anchor_stage
flick("nuclearbombc", src) flick("nuclearbombc", src)
/obj/machinery/nuclearbomb/wirecutter_act(mob/user, obj/item/I) /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) /obj/machinery/nuclearbomb/welder_act(mob/user, obj/item/I)
. = TRUE . = TRUE
if(removal_stage != NUKE_INTACT && removal_stage != NUKE_COVER_OPEN)
return
if(!I.tool_use_check(user, 0)) if(!I.tool_use_check(user, 0))
return return
if(removal_stage == NUKE_INTACT) 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>",\ visible_message("<span class='notice'>[user] cuts through the bolt covers on [src].</span>",\
"<span class='notice'>You cut through the bolt cover.</span>") "<span class='notice'>You cut through the bolt cover.</span>")
removal_stage = NUKE_COVER_OFF 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>",\ 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='notice'>You start cutting apart the anchoring system's sealant with [I]...</span>",\
"<span class='warning'>You hear welding.</span>") "<span class='warning'>You hear welding.</span>")
@@ -181,10 +252,18 @@ GLOBAL_VAR(bomb_set)
attack_hand(user) attack_hand(user)
/obj/machinery/nuclearbomb/attack_hand(mob/user as mob) /obj/machinery/nuclearbomb/attack_hand(mob/user as mob)
if(panel_open) if(!panel_open)
wires.Interact(user) return ui_interact(user)
else if(removal_stage != NUKE_CORE_FULLY_EXPOSED || !core)
ui_interact(user) 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) /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) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
@@ -308,6 +387,9 @@ GLOBAL_VAR(bomb_set)
if(safety) if(safety)
to_chat(usr, "<span class='notice'>The safety is still on.</span>") to_chat(usr, "<span class='notice'>The safety is still on.</span>")
return 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) timing = !(timing)
if(timing) if(timing)
if(!lighthack) if(!lighthack)
@@ -471,3 +553,7 @@ GLOBAL_VAR(bomb_set)
#undef NUKE_SEALANT_OPEN #undef NUKE_SEALANT_OPEN
#undef NUKE_UNWRENCHED #undef NUKE_UNWRENCHED
#undef NUKE_MOBILE #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()]. " explanation_text = "Steal [steal_target]. One was last seen in [get_location()]. "
if(length(O.protected_jobs)) if(length(O.protected_jobs))
explanation_text += "It may also be in the possession of the [english_list(O.protected_jobs, and_text = " or ")]." 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 return
explanation_text = "Free Objective." explanation_text = "Free Objective."
@@ -412,6 +414,8 @@ GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective)
else else
steal_target = new new_target steal_target = new new_target
explanation_text = "Steal [steal_target.name]." explanation_text = "Steal [steal_target.name]."
if(steal_target.special_equipment)
give_kit(steal_target.special_equipment)
return steal_target return steal_target
/datum/objective/steal/check_completion() /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) if(I.type in steal_target.altitems)
return steal_target.check_special_completion(I) 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 /datum/objective/steal/exchange
martyr_compatible = 0 martyr_compatible = 0

View File

@@ -74,7 +74,7 @@ Made by Xhuis
required_enemies = 2 required_enemies = 2
recommended_enemies = 2 recommended_enemies = 2
restricted_jobs = list("AI", "Cyborg") 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() /datum/game_mode/shadowling/announce()
to_chat(world, "<b>The current game mode is - Shadowling!</b>") to_chat(world, "<b>The current game mode is - Shadowling!</b>")

View File

@@ -12,6 +12,8 @@
var/list/altitems = list() var/list/altitems = list()
var/flags = 0 var/flags = 0
var/location_override 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) /datum/theft_objective/proc/check_completion(datum/mind/owner)
if(!owner.current) if(!owner.current)
@@ -138,6 +140,19 @@
protected_jobs = list("Head Of Security", "Warden") protected_jobs = list("Head Of Security", "Warden")
location_override = "the Warden's Office" 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 /datum/theft_objective/number
var/min=0 var/min=0
var/max=0 var/max=0

View File

@@ -11,7 +11,7 @@
name = "traitor" name = "traitor"
config_tag = "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 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_players = 0
required_enemies = 1 required_enemies = 1
recommended_enemies = 4 recommended_enemies = 4

View File

@@ -2,7 +2,7 @@
name = "traitor+vampire" name = "traitor+vampire"
config_tag = "traitorvamp" config_tag = "traitorvamp"
traitors_possible = 3 //hard limit on traitors if scaling is turned off 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") restricted_jobs = list("Cyborg")
secondary_restricted_jobs = list("AI") secondary_restricted_jobs = list("AI")
required_players = 10 required_players = 10

View File

@@ -9,7 +9,7 @@
name = "vampire" name = "vampire"
config_tag = "vampire" config_tag = "vampire"
restricted_jobs = list("AI", "Cyborg") 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") protected_species = list("Machine")
required_players = 15 required_players = 15
required_enemies = 1 required_enemies = 1

View File

@@ -100,6 +100,8 @@
return get_all_centcom_access() + get_all_accesses() return get_all_centcom_access() + get_all_accesses()
if("Special Operations Officer") if("Special Operations Officer")
return get_all_centcom_access() + get_all_accesses() 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") if("Nanotrasen Navy Representative")
return get_all_centcom_access() + get_all_accesses() return get_all_centcom_access() + get_all_accesses()
if("Nanotrasen Navy Officer") 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") 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() /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) //gets the actual job rank (ignoring alt titles)
//this is used solely for sechuds //this is used solely for sechuds

View File

@@ -69,9 +69,9 @@
/datum/outfit/job/ntspecops /datum/outfit/job/ntspecops
name = "Special Operations Officer" name = "Special Operations Officer"
jobtype = /datum/job/ntspecops jobtype = /datum/job/ntspecops
allow_backbag_choice = FALSE
uniform = /obj/item/clothing/under/rank/centcom/captain uniform = /obj/item/clothing/under/rank/centcom/captain
suit = /obj/item/clothing/suit/space/deathsquad/officer suit = /obj/item/clothing/suit/space/deathsquad/officer
back = /obj/item/storage/backpack/ert/security
belt = /obj/item/storage/belt/military/assault belt = /obj/item/storage/belt/military/assault
gloves = /obj/item/clothing/gloves/combat gloves = /obj/item/clothing/gloves/combat
shoes = /obj/item/clothing/shoes/combat shoes = /obj/item/clothing/shoes/combat
@@ -82,8 +82,8 @@
id = /obj/item/card/id/centcom id = /obj/item/card/id/centcom
pda = /obj/item/pda/centcom pda = /obj/item/pda/centcom
r_pocket = /obj/item/storage/box/matches r_pocket = /obj/item/storage/box/matches
back = /obj/item/storage/backpack/satchel
box = /obj/item/storage/box/centcomofficer box = /obj/item/storage/box/centcomofficer
backpack = /obj/item/storage/backpack/satchel
backpack_contents = list( backpack_contents = list(
/obj/item/clothing/shoes/magboots/advance = 1, /obj/item/clothing/shoes/magboots/advance = 1,
/obj/item/storage/box/zipties = 1 /obj/item/storage/box/zipties = 1
@@ -104,3 +104,23 @@
if(visualsOnly) if(visualsOnly)
return return
H.mind.offstation_role = TRUE 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( var/list/blacklisted_full = list(
/datum/job/ntnavyofficer, /datum/job/ntnavyofficer,
/datum/job/ntspecops, /datum/job/ntspecops,
/datum/job/ntspecops/solgovspecops,
/datum/job/civilian, /datum/job/civilian,
/datum/job/syndicateofficer, /datum/job/syndicateofficer,
/datum/job/explorer // blacklisted so that HOPs don't try prioritizing it, then wonder why that doesn't work /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 var/mob/living/silicon/robot/R = occupant
if(!istype(R)) return ..() if(!istype(R)) return ..()
R.contents -= R.mmi QDEL_NULL(R.mmi)
qdel(R.mmi)
for(var/obj/item/I in R.module) // the tools the borg has; metal, glass, guns etc 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 for(var/obj/item/O in I) // the things inside the tools, if anything; mainly for janiborg trash bags
O.loc = R O.loc = R
@@ -771,7 +770,7 @@
return ..() 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)) if(istype(person_to_cryo.loc, /obj/machinery/cryopod))
return 0 return 0
if(isobj(person_to_cryo.loc)) if(isobj(person_to_cryo.loc))
@@ -779,7 +778,9 @@
O.force_eject_occupant(person_to_cryo) O.force_eject_occupant(person_to_cryo)
var/list/free_cryopods = list() var/list/free_cryopods = list()
for(var/obj/machinery/cryopod/P in GLOB.machines) 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 free_cryopods += P
var/obj/machinery/cryopod/target_cryopod = null var/obj/machinery/cryopod/target_cryopod = null
if(free_cryopods.len) if(free_cryopods.len)

View File

@@ -85,6 +85,13 @@
else else
do_animate("deny") 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) /obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0)
if(istype(mover) && mover.checkpass(PASSGLASS)) if(istype(mover) && mover.checkpass(PASSGLASS))
return 1 return 1

View File

@@ -51,6 +51,11 @@
"Emergency Response Team Officer" = "dsquadradio", "Emergency Response Team Officer" = "dsquadradio",
"Nanotrasen Navy Officer" = "dsquadradio", "Nanotrasen Navy Officer" = "dsquadradio",
"Special Operations 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 // Medical
"Chemist" = "medradio", "Chemist" = "medradio",
"Chief Medical Officer" = "medradio", "Chief Medical Officer" = "medradio",
@@ -119,7 +124,9 @@
/// List of ERT jobs /// 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") 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 /// 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 // Defined so code compiles and incase someone has a non-standard job
var/job_class = "radio" var/job_class = "radio"
// NOW FOR ACTUAL TOGGLES // NOW FOR ACTUAL TOGGLES
@@ -278,7 +285,7 @@
// Makes heads of staff bold // Makes heads of staff bold
if(toggle_command_bold) if(toggle_command_bold)
var/job = tcm.sender_job 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)) for(var/I in 1 to length(message_pieces))
var/datum/multilingual_say_piece/S = message_pieces[I] var/datum/multilingual_say_piece/S = message_pieces[I]
if(!S.message) 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. 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.") 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) 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 return
var/mob/dead/observer/O = pick(candidates) var/mob/dead/observer/O = pick(candidates)
R.key= O.key R.key = O.key
/obj/machinery/transformer/mime /obj/machinery/transformer/mime
name = "Mimetech Greyscaler" name = "Mimetech Greyscaler"

View File

@@ -287,7 +287,7 @@
A.Grant(S) 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") 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) var/mob/dead/observer/chosen = pick(candidates)
S.key = chosen.key S.key = chosen.key
S.mind.special_role = SPECIAL_ROLE_PYROCLASTIC_SLIME S.mind.special_role = SPECIAL_ROLE_PYROCLASTIC_SLIME

View File

@@ -67,6 +67,11 @@
/obj/effect/mapping_helpers/no_lava /obj/effect/mapping_helpers/no_lava
icon_state = "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 /obj/effect/mapping_helpers/airlock
layer = DOOR_HELPER_LAYER layer = DOOR_HELPER_LAYER
@@ -78,13 +83,9 @@
if(!mapload) if(!mapload)
log_world("### MAP WARNING, [src] spawned outside of mapload!") log_world("### MAP WARNING, [src] spawned outside of mapload!")
return return
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in src.loc var/obj/machinery/door/door = locate(/obj/machinery/door) in loc
if(airlock) if(door)
airlock.unres_sides ^= dir door.unres_sides ^= dir
else else
log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]") 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 //generic procs copied from obj/effect/alien
#define SPIDER_SOFT_CAP 30
#define SPIDER_HARD_CAP 40
/obj/structure/spider /obj/structure/spider
name = "web" name = "web"
desc = "it's stringy and sticky" desc = "it's stringy and sticky"
@@ -67,7 +69,11 @@
START_PROCESSING(SSobj, src) START_PROCESSING(SSobj, src)
/obj/structure/spider/eggcluster/process() /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) if(amount_grown >= 100)
var/num = rand(3, 12) var/num = rand(3, 12)
for(var/i in 1 to num) for(var/i in 1 to num)
@@ -75,7 +81,7 @@
S.faction = faction.Copy() S.faction = faction.Copy()
S.master_commander = master_commander S.master_commander = master_commander
if(player_spiders) if(player_spiders)
S.player_spiders = 1 S.player_spiders = TRUE
qdel(src) qdel(src)
/obj/structure/spider/spiderling /obj/structure/spider/spiderling
@@ -168,9 +174,13 @@
if(isturf(loc)) if(isturf(loc))
amount_grown += rand(0,2) amount_grown += rand(0,2)
if(amount_grown >= 100) if(amount_grown >= 100)
if(SSmobs.giant_spiders > SPIDER_HARD_CAP)
qdel(src)
return
if(!grow_as) if(!grow_as)
grow_as = pick(typesof(/mob/living/simple_animal/hostile/poison/giant_spider)) 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) var/mob/living/simple_animal/hostile/poison/giant_spider/S = new grow_as(loc)
SSmobs.giant_spiders++
S.faction = faction.Copy() S.faction = faction.Copy()
S.master_commander = master_commander S.master_commander = master_commander
if(player_spiders && !selecting_player) if(player_spiders && !selecting_player)
@@ -178,7 +188,7 @@
spawn() spawn()
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a giant spider?", ROLE_GSPIDER, TRUE, source = S) 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) var/mob/C = pick(candidates)
if(C) if(C)
S.key = C.key 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) /proc/empulse(turf/epicenter, heavy_range, light_range, log = FALSE, cause = null)
if(!epicenter) return if(!epicenter) return

View File

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

View File

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

View File

@@ -307,12 +307,18 @@
icon_state = "com_headset_alt" icon_state = "com_headset_alt"
item_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 /obj/item/radio/headset/ert/alt/commander
name = "ERT commander's bowman headset" name = "ERT commander's bowman headset"
desc = "The headset of the boss. Protects ears from flashbangs. Can transmit even if telecomms are down." desc = "The headset of the boss. Protects ears from flashbangs. Can transmit even if telecomms are down."
requires_tcomms = FALSE requires_tcomms = FALSE
instant = TRUE instant = TRUE
/obj/item/radio/headset/ert/alt/commander/solgov
name = "\improper Trans-Solar Federation Lieutenant's bowman headset"
/obj/item/radio/headset/centcom /obj/item/radio/headset/centcom
name = "centcom officer's bowman headset" name = "centcom officer's bowman headset"
desc = "The headset of final authority. Protects ears from flashbangs. Can transmit even if telecomms are down." 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)" name = "station intercom (General)"
desc = "Talk through this." desc = "Talk through this."
icon_state = "intercom" icon_state = "intercom"
layer = ABOVE_WINDOW_LAYER
anchored = 1 anchored = 1
w_class = WEIGHT_CLASS_BULKY w_class = WEIGHT_CLASS_BULKY
canhear_range = 2 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." desc = "A screwdriver with an ultra thin tip."
icon_state = "screwdriver_nuke" icon_state = "screwdriver_nuke"
toolspeed = 0.5 toolspeed = 0.5
random_color = FALSE
/obj/item/screwdriver/suicide_act(mob/user) /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>") 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" icon_state = "explosive"
origin_tech = "materials=2;combat=3;biotech=4;syndicate=4" origin_tech = "materials=2;combat=3;biotech=4;syndicate=4"
actions_types = list(/datum/action/item_action/hands_free/activate/always) actions_types = list(/datum/action/item_action/hands_free/activate/always)
var/detonating = FALSE
var/weak = 2 var/weak = 2
var/medium = 0.8 var/medium = 0.8
var/heavy = 0.4 var/heavy = 0.4
@@ -26,16 +27,20 @@
activate("death") activate("death")
/obj/item/implant/explosive/activate(cause) /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") 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) heavy = round(heavy)
medium = round(medium) medium = round(medium)
weak = round(weak) 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 the delay is short, just blow up already jeez
if(delay <= 7) if(delay <= 7)
explosion(src,heavy,medium,weak,weak, flame_range = weak) explosion(src, heavy, medium, weak, weak, flame_range = weak)
if(imp_in) if(imp_in)
imp_in.gib() imp_in.gib()
qdel(src) qdel(src)

View File

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

View File

@@ -61,6 +61,9 @@
return return
if(istype(W, /obj/item)) if(istype(W, /obj/item))
var/obj/item/IW = W 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) if((loadedWeightClass + IW.w_class) > maxWeightClass)
to_chat(user, "<span class='warning'>\The [IW] won't fit into \the [src]!</span>") to_chat(user, "<span class='warning'>\The [IW] won't fit into \the [src]!</span>")
return return

View File

@@ -584,3 +584,9 @@
name = "emergency response team janitor backpack" name = "emergency response team janitor backpack"
desc = "A spacious backpack with lots of pockets, worn by janitorial members of a Nanotrasen Emergency Response Team." desc = "A spacious backpack with lots of pockets, worn by janitorial members of a Nanotrasen Emergency Response Team."
icon_state = "ert_janitor" 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. /// No message on putting items in.
var/silent = FALSE var/silent = FALSE
/// List of objects which this item can store (if set, it can't store anything else) /// 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) /// 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) /// 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 var/max_w_class = WEIGHT_CLASS_SMALL
/// The sum of the w_classes of all the items in this storage item. /// 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. /// How much of the stack item do you get.
var/foldable_amt = 0 var/foldable_amt = 0
/// Lazy list of mobs which are currently viewing the storage inventory.
var/list/mobs_viewing
/obj/item/storage/Initialize(mapload) /obj/item/storage/Initialize(mapload)
. = ..() . = ..()
can_hold = typecacheof(can_hold) can_hold = typecacheof(can_hold)
@@ -73,6 +76,15 @@
closer.plane = ABOVE_HUD_PLANE closer.plane = ABOVE_HUD_PLANE
orient2hud() 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) /obj/item/storage/MouseDrop(obj/over_object)
if(!ismob(usr)) //so monkeys can take off their backpacks -- Urist if(!ismob(usr)) //so monkeys can take off their backpacks -- Urist
return return
@@ -177,11 +189,13 @@
user.client.screen += closer user.client.screen += closer
user.client.screen += contents user.client.screen += contents
user.s_active = src user.s_active = src
LAZYADDOR(mobs_viewing, user)
/** /**
* Hides the current container interface from `user`. * Hides the current container interface from `user`.
*/ */
/obj/item/storage/proc/hide_from(mob/user) /obj/item/storage/proc/hide_from(mob/user)
LAZYREMOVE(mobs_viewing, user) // Remove clientless mobs too
if(!user.client) if(!user.client)
return return
user.client.screen -= boxes user.client.screen -= boxes
@@ -190,6 +204,16 @@
if(user.s_active == src) if(user.s_active == src)
user.s_active = null 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) /obj/item/storage/proc/open(mob/user)
if(use_sound) if(use_sound)
playsound(loc, use_sound, 50, TRUE, -5) playsound(loc, use_sound, 50, TRUE, -5)
@@ -374,6 +398,12 @@
prevent_warning = TRUE prevent_warning = TRUE
I.forceMove(src) I.forceMove(src)
I.on_enter_storage(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)
if(usr.client && usr.s_active != src) if(usr.client && usr.s_active != src)
usr.client.screen -= I usr.client.screen -= I
@@ -412,7 +442,8 @@
var/obj/item/storage/fancy/F = src var/obj/item/storage/fancy/F = src
F.update_icon(TRUE) 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) if((M.s_active == src) && M.client)
M.client.screen -= I M.client.screen -= I
@@ -498,6 +529,10 @@
close(M) close(M)
add_fingerprint(user) add_fingerprint(user)
/obj/item/storage/equipped(mob/user, slot, initial)
. = ..()
update_viewers()
/obj/item/storage/attack_ghost(mob/user) /obj/item/storage/attack_ghost(mob/user)
if(isobserver(user)) if(isobserver(user))
// Revenants don't get to play with the toys. // Revenants don't get to play with the toys.
@@ -539,14 +574,6 @@
/obj/item/storage/proc/populate_contents() /obj/item/storage/proc/populate_contents()
return // Override 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) /obj/item/storage/emp_act(severity)
..() ..()
for(var/I in contents) 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/capulettium_plus(src)
new /obj/item/reagent_containers/syringe/sarin(src) new /obj/item/reagent_containers/syringe/sarin(src)
new /obj/item/reagent_containers/syringe/pancuronium(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() trigger_alarm()
emagged = TRUE emagged = TRUE
toggle_lock()
/obj/structure/displaycase/examine(mob/user) /obj/structure/displaycase/examine(mob/user)
. = ..() . = ..()
@@ -110,12 +111,12 @@
/obj/structure/displaycase/attackby(obj/item/I, mob/user, params) /obj/structure/displaycase/attackby(obj/item/I, mob/user, params)
if(I.GetID() && !broken && openable) if(I.GetID() && !broken && openable)
if(allowed(user) || emagged) if(allowed(user) || emagged)
to_chat(user, "<span class='notice'>You [open ? "close":"open"] [src].</span>") to_chat(user, "<span class='notice'>You [open ? "close":"open"] [src].</span>")
toggle_lock(user) toggle_lock()
else else
to_chat(user, "<span class='warning'>Access denied.</span>") to_chat(user, "<span class='warning'>Access denied.</span>")
else if(open && !showpiece) else if(open && !showpiece)
if(user.drop_item()) if(!(I.flags & (ABSTRACT | DROPDEL)) && user.drop_item())
I.forceMove(src) I.forceMove(src)
showpiece = I showpiece = I
to_chat(user, "<span class='notice'>You put [I] on display</span>") 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)) if(!I.use_tool(src, user, 20, volume = I.tool_volume))
return return
to_chat(user, "<span class='notice'>You [open ? "close":"open"] [src].</span>") 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) /obj/structure/displaycase/welder_act(mob/user, obj/item/I)
. = TRUE . = TRUE
if(default_welder_repair(user, I)) if(default_welder_repair(user, I))
broken = FALSE broken = FALSE
/obj/structure/displaycase/proc/toggle_lock(mob/user) /obj/structure/displaycase/proc/toggle_lock()
open = !open open = !open
update_icon() update_icon()

View File

@@ -28,7 +28,7 @@
if(GIRDER_DISASSEMBLED) if(GIRDER_DISASSEMBLED)
. += "<span class='notice'>[src] is disassembled! You probably shouldn't be able to see this examine message.</span>" . += "<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>\ 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>\ 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." You can dismantle the girder with a wrench."

View File

@@ -46,9 +46,16 @@
/obj/structure/inflatable/CanAtmosPass(turf/T) /obj/structure/inflatable/CanAtmosPass(turf/T)
return !density return !density
/obj/structure/inflatable/attack_hand(mob/user as mob) /obj/structure/inflatable/attack_hand(mob/user)
add_fingerprint(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() /obj/structure/inflatable/AltClick()
if(usr.stat || usr.restrained()) if(usr.stat || usr.restrained())
return return

View File

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

View File

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

View File

@@ -2300,26 +2300,40 @@
else if(href_list["cryossd"]) else if(href_list["cryossd"])
if(!check_rights(R_ADMIN)) if(!check_rights(R_ADMIN))
return return
var/mob/living/carbon/human/H = locateUID(href_list["cryossd"]) var/mob/living/M = locateUID(href_list["cryossd"])
if(!istype(H)) var/human = ishuman(M)
to_chat(usr, "<span class='warning'>This can only be used on instances of type /mob/living/carbon/human</span>") if(!human && !issilicon(M))
to_chat(usr, "<span class='warning'>This can only be used on humans and silicons.</span>")
return 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>") to_chat(usr, "<span class='warning'>This can only be used on living, SSD players.</span>")
return return
if(istype(H.loc, /obj/machinery/cryopod)) if(isAI(M))
var/obj/machinery/cryopod/P = H.loc 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() P.despawn_occupant()
log_admin("[key_name(usr)] despawned [H.job] [H] in cryo.") if(human)
message_admins("[key_name_admin(usr)] despawned [H.job] [H] in cryo.") var/mob/living/carbon/human/H = M
else if(cryo_ssd(H)) log_admin("[key_name(usr)] despawned [H.job] [H] in cryo.")
log_admin("[key_name(usr)] sent [H.job] [H] to cryo.") message_admins("[key_name_admin(usr)] despawned [H.job] [H] in cryo.")
message_admins("[key_name_admin(usr)] sent [H.job] [H] to 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 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>") 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(H, sound('sound/effects/adminhelp.ogg')) SEND_SOUND(M, sound('sound/effects/adminhelp.ogg'))
if(H.client) if(M.client)
window_flash(H.client) window_flash(M.client)
else if(href_list["FaxReplyTemplate"]) else if(href_list["FaxReplyTemplate"])
if(!check_rights(R_ADMIN)) if(!check_rights(R_ADMIN))
return 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"]") 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 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! 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 // All these vars are related to proc call protection

View File

@@ -528,8 +528,8 @@
if(!antnum || antnum <= 0) if(!antnum || antnum <= 0)
return return
log_admin("[key_name(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 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) for(var/mob/living/carbon/human/applicant in GLOB.player_list)
if(CandCheck(ROLE_VAMPIRE, applicant, temp)) if(CandCheck(ROLE_VAMPIRE, applicant, temp))

View File

@@ -74,7 +74,7 @@ GLOBAL_LIST_EMPTY(antagonists)
set waitfor = FALSE 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) 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) 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!") 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.") 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>") 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/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) 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 checking = FALSE
if(QDELETED(src) || !check_usability(user)) if(QDELETED(src) || !check_usability(user))
return return

View File

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

View File

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

View File

@@ -194,7 +194,7 @@
servant_mind.transfer_to(H) 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) 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) var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant") message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant")
H.key = C.key H.key = C.key

View File

@@ -206,11 +206,13 @@
icon_state = "solgovcberet" icon_state = "solgovcberet"
item_color = "solgovc" item_color = "solgovc"
dog_fashion = null 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 strip_delay = 80
/obj/item/clothing/head/beret/solgov/command/elite /obj/item/clothing/head/beret/solgov/command/elite
name = "\improper Trans-Solar Federation Specops Lieutenant's beret" 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" icon_state = "solgovceliteberet"
item_color = "solgovcelite" item_color = "solgovcelite"
resistance_flags = FIRE_PROOF

View File

@@ -125,28 +125,32 @@
dog_fashion = null dog_fashion = null
/obj/item/clothing/head/soft/solgov/marines /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) armor = list("melee" = 20, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60)
strip_delay = 60
icon_state = "solgovsoft_flipped" icon_state = "solgovsoft_flipped"
strip_delay = 60
flipped = TRUE flipped = TRUE
/obj/item/clothing/head/soft/solgov/marines/elite /obj/item/clothing/head/soft/solgov/marines/elite
name = "\improper Trans-Solar Federation Specops marine cap" 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" icon_state = "solgovelitesoft_flipped"
item_color = "solgovelite" item_color = "solgovelite"
resistance_flags = FIRE_PROOF
/obj/item/clothing/head/soft/solgov/marines/command /obj/item/clothing/head/soft/solgov/marines/command
name = "\improper Trans-Solar Federation lieutenant's cap" 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." 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" icon_state = "solgovcsoft_flipped"
item_color = "solgovc" item_color = "solgovc"
dog_fashion = null 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 strip_delay = 80
/obj/item/clothing/head/soft/solgov/marines/command/elite /obj/item/clothing/head/soft/solgov/marines/command/elite
name = "\improper Trans-Solar Federation Specops Lieutenant's cap" 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" icon_state = "solgovcelitesoft"
item_color = "solgovcelite" item_color = "solgovcelite"
resistance_flags = FIRE_PROOF

View File

@@ -259,3 +259,38 @@
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/berserker 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) armor = list(melee = 65, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 100, rad = 100, fire = 80, acid = 80)
slowdown = 0 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) armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
flags = STOPSPRESSUREDMAGE | THICKMATERIAL 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 /obj/item/clothing/suit/space/deathsquad/officer
name = "officer jacket" name = "officer jacket"
desc = "An armored jacket used in special operations." desc = "An armored jacket used in special operations."
@@ -99,6 +104,12 @@
resistance_flags = FIRE_PROOF | ACID_PROOF resistance_flags = FIRE_PROOF | ACID_PROOF
w_class = WEIGHT_CLASS_NORMAL 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 //Space santa outfit suit
/obj/item/clothing/head/helmet/space/santahat /obj/item/clothing/head/helmet/space/santahat
name = "Santa's hat" 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) armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30)
displays_id = 0 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 /obj/item/clothing/under/rank/centcom/blueshield
name = "formal blueshield's uniform" 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." 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 /obj/item/clothing/head/fluff/lfbowler //Lightfire: Hyperion
name = "Classy bowler hat" name = "classy bowler hat"
desc = "a very classy looking bowler hat" desc = "A very classy looking bowler hat."
icon = 'icons/obj/custom_items.dmi' icon = 'icons/obj/custom_items.dmi'
icon_state = "bowler_lightfire" icon_state = "bowler_lightfire"
/obj/item/clothing/under/fluff/lfvicsuit //Lightfire: Hyperion /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!" desc = "A blue and black victorian suit with silver buttons, very fancy!"
icon = 'icons/obj/custom_items.dmi' icon = 'icons/obj/custom_items.dmi'
lefthand_file = 'icons/mob/inhands/fluff_lefthand.dmi' lefthand_file = 'icons/mob/inhands/fluff_lefthand.dmi'

View File

@@ -4,46 +4,46 @@
INVOKE_ASYNC(src, .proc/make_sentient_mob) INVOKE_ASYNC(src, .proc/make_sentient_mob)
/datum/event/sentience/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/list/potential = list()
var/sentience_type = SENTIENCE_ORGANIC
for(var/mob/living/simple_animal/L in GLOB.alive_mob_list) for(var/mob/living/simple_animal/L in GLOB.alive_mob_list)
var/turf/T = get_turf(L) var/turf/T = get_turf(L)
if (!is_station_level(T.z)) if(!is_station_level(T.z))
continue 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 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 return FALSE
var/mob/living/simple_animal/SA = pick(potential) 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/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.key = SG.key
SA.universal_speak = 1 SA.universal_speak = TRUE
SA.sentience_act() SA.sentience_act()
SA.can_collar = 1 SA.can_collar = TRUE
SA.maxHealth = max(SA.maxHealth, 200) SA.maxHealth = max(SA.maxHealth, 200)
SA.health = SA.maxHealth SA.health = SA.maxHealth
SA.del_on_death = FALSE SA.del_on_death = FALSE
greet_sentient(SA) 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) print_command_report(sentience_report, "NAS Trurl Update", FALSE)
/datum/event/sentience/proc/greet_sentient(mob/living/carbon/human/M) /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='userdanger'>Hello world!</span>")
to_chat(M, "<span class='warning'>Due to freak radiation, you have gained \ 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>") human language!</span>")

View File

@@ -274,7 +274,7 @@
// Bust through windows or other stuff blocking the way // Bust through windows or other stuff blocking the way
if(!target.Enter(holder)) if(!target.Enter(holder))
for(var/atom/movable/AM in target) for(var/atom/movable/AM in target)
if(istype(AM, /obj/structure/spacevine) || !AM.density) if(!AM.density || isvineimmune(AM))
continue continue
AM.ex_act(severity) AM.ex_act(severity)
target.ex_act(severity) // vine immunity handled at /mob/ex_act target.ex_act(severity) // vine immunity handled at /mob/ex_act
@@ -697,8 +697,10 @@
. = ..() . = ..()
/proc/isvineimmune(atom/A) /proc/isvineimmune(atom/A)
. = FALSE
if(isliving(A)) if(isliving(A))
var/mob/living/M = A var/mob/living/M = A
if(("vines" in M.faction) || ("plants" in M.faction)) 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) playsound(loc, pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg'), 50, 1, -1)
return BRUTELOSS 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)) if(swiping || !istype(A, /obj/structure/spacevine) || get_turf(A) == get_turf(user))
return ..() return ..()
else else

View File

@@ -41,7 +41,7 @@
new /obj/effect/temp_visual/resonance(T, user, src, burst_time) new /obj/effect/temp_visual/resonance(T, user, src, burst_time)
user.changeNext_move(CLICK_CD_MELEE) 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)) if(check_allowed_items(target, 1))
CreateResonance(target, user) CreateResonance(target, user)
return TRUE 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>") to_chat(user, "<span class='notice'>You release the wisp. It begins to bob around your head.</span>")
icon_state = "lantern" icon_state = "lantern"
wisp.orbit(user, 20) INVOKE_ASYNC(wisp, /atom/movable/.proc/orbit, user, 20)
set_light(0) set_light(0)
user.update_sight() user.update_sight()

View File

@@ -87,7 +87,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
real_name = name real_name = name
//starts ghosts off with all HUDs. //starts ghosts off with all HUDs.
toggle_all_huds_on() toggle_all_huds_on(body)
..() ..()
/mob/dead/observer/Destroy() /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 return
GLOB.ghost_hud_panel.ui_interact(src) 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_DIAGNOSTIC)
show_me_the_hud(DATA_HUD_SECURITY_ADVANCED) show_me_the_hud(DATA_HUD_SECURITY_ADVANCED)
show_me_the_hud(DATA_HUD_MEDICAL_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() /mob/dead/observer/verb/set_dnr()
set name = "Set DNR" set name = "Set DNR"

View File

@@ -486,6 +486,14 @@
flags = RESTRICTED | HIVEMIND | NOBABEL flags = RESTRICTED | HIVEMIND | NOBABEL
follow = TRUE 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 /datum/language/ling
name = "Changeling" name = "Changeling"
desc = "Although they are normally wary and suspicious of each other, changelings can commune over a distance." 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?", 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") "Wipe Core", "No", "No", "Yes") != "Yes")
return 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.empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(loc)
GLOB.global_announcer.autosay("[src] has been moved to intelligence storage.", "Artificial Intelligence Oversight") 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 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>") 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() 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 mmi = null
if(connected_ai) if(connected_ai)
connected_ai.connected_robots -= src connected_ai.connected_robots -= src

View File

@@ -129,6 +129,7 @@
status_flags = 0 status_flags = 0
construct_type = "juggernaut" construct_type = "juggernaut"
mob_size = MOB_SIZE_LARGE 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) construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall)
force_threshold = 11 force_threshold = 11
playstyle_string = "<b>You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \ 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_living = "cow"
icon_dead = "cow_dead" icon_dead = "cow_dead"
icon_gib = "cow_gib" icon_gib = "cow_gib"
speak = list("moo?","moo","MOOOOOO") speak = list("Moo?","Moo","MOOOOOO")
speak_emote = list("moos","moos hauntingly") speak_emote = list("moos","moos hauntingly")
emote_hear = list("brays") emote_hear = list("brays")
emote_see = list("shakes its head") emote_see = list("shakes its head")

View File

@@ -56,6 +56,12 @@
for(var/obj/structure/spider/S in range(1, get_turf(src))) for(var/obj/structure/spider/S in range(1, get_turf(src)))
return S return S
/mob/living/simple_animal/hostile/poison/giant_spider/death(gibbed)
. = ..()
if(!.)
return FALSE
SSmobs.giant_spiders--
//nursemaids - these create webs and eggs //nursemaids - these create webs and eggs
/mob/living/simple_animal/hostile/poison/giant_spider/nurse /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." 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() /obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in()
return TRUE 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) if(!charging)
..() ..()

View File

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

View File

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

View File

@@ -33,6 +33,7 @@
projectilesound = 'sound/weapons/pierce.ogg' projectilesound = 'sound/weapons/pierce.ogg'
projectiletype = /obj/item/projectile/terrorqueenspit projectiletype = /obj/item/projectile/terrorqueenspit
spider_tier = TS_TIER_4 spider_tier = TS_TIER_4
loudspeaker = TRUE
spider_opens_doors = 2 spider_opens_doors = 2
web_type = /obj/structure/spider/terrorweb/queen 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() 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/killcount = 0
var/busy = 0 // leave this alone! 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 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/hasdied = 0
var/list/spider_special_drops = list() var/list/spider_special_drops = list()
var/attackstep = 0 var/attackstep = 0

View File

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

View File

@@ -49,6 +49,7 @@
name = "venus human trap" name = "venus human trap"
desc = "Now you know how the fly feels." desc = "Now you know how the fly feels."
icon_state = "venus_human_trap" icon_state = "venus_human_trap"
icon_living = "venus_human_trap"
mob_biotypes = MOB_ORGANIC | MOB_PLANT mob_biotypes = MOB_ORGANIC | MOB_PLANT
layer = MOB_LAYER + 0.9 layer = MOB_LAYER + 0.9
health = 50 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!") 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(matches)
if(M.client) 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)].") log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].")
else 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).") log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).")
/mob/Login() /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/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 var/mob/dead/observer/theghost = null
if(LAZYLEN(candidates)) if(length(candidates))
if(QDELETED(M))
return
theghost = pick(candidates) theghost = pick(candidates)
to_chat(M, "Your mob has been taken over by a ghost!") 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)])") 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(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) if(!client)
return 1 return 1
var/mob/dead/observer/observer = new() var/mob/dead/observer/observer = new(src)
src << browse(null, "window=playersetup") src << browse(null, "window=playersetup")
spawning = 1 spawning = 1
stop_sound_channel(CHANNEL_LOBBYMUSIC) stop_sound_channel(CHANNEL_LOBBYMUSIC)

View File

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

View File

@@ -730,7 +730,20 @@
return return
if(moveable && default_unfasten_wrench(user, I, time = 20)) if(moveable && default_unfasten_wrench(user, I, time = 20))
return 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>",\ 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='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>") "<span class='italics'>Everything suddenly goes silent.</span>")

View File

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

View File

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

View File

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

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