Merge remote-tracking branch 'citadel/master' into combat_v7

This commit is contained in:
silicons
2021-06-27 10:44:51 -07:00
209 changed files with 13698 additions and 2095 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ indent_size = 4
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
# end_of_line = lf
end_of_line = lf
[*.yml]
indent_style = space
+108
View File
@@ -0,0 +1,108 @@
# dmdoc
[DOCUMENTATION]: http://codedocs.tgstation13.org
[BYOND]: https://secure.byond.com/
[DMDOC]: https://github.com/SpaceManiac/SpacemanDMM/tree/master/src/dmdoc
[DMDOC] is a documentation generator for DreamMaker, the scripting language
of the [BYOND] game engine. It produces simple static HTML files based on
documented files, macros, types, procs, and vars.
We use **dmdoc** to generate [DOCUMENTATION] for our code, and that documentation
is automatically generated and built on every new commit to the master branch
This gives new developers a clickable reference [DOCUMENTATION] they can browse to better help
gain understanding of the /tg/station codebase structure and api reference.
## Documenting code on /tg/station
We use block comments to document procs and classes, and we use `///` line comments
when documenting individual variables.
It is required that all new code be covered with DMdoc code, according to the [Requirements](#Required)
We also require that when you touch older code, you must document the functions that you
have touched in the process of updating that code
### Required
A class *must* always be autodocumented, and all public functions *must* be documented
All class level defined variables *must* be documented
Internal functions *should* be documented, but may not be
A public function is any function that a developer might reasonably call while using
or interacting with your object. Internal functions are helper functions that your
public functions rely on to implement logic
### Documenting a proc
When documenting a proc, we give a short one line description (as this is shown
next to the proc definition in the list of all procs for a type or global
namespace), then a longer paragraph which will be shown when the user clicks on
the proc to jump to it's definition
```
/**
* Short description of the proc
*
* Longer detailed paragraph about the proc
* including any relevant detail
* Arguments:
* * arg1 - Relevance of this argument
* * arg2 - Relevance of this argument
*/
```
### Documenting a class
We first give the name of the class as a header, this can be omitted if the name is
just going to be the typepath of the class, as dmdoc uses that by default
Then we give a short oneline description of the class
Finally we give a longer multi paragraph description of the class and it's details
```
/**
* # Classname (Can be omitted if it's just going to be the typepath)
*
* The short overview
*
* A longer
* paragraph of functionality about the class
* including any assumptions/special cases
*
*/
```
### Documenting a variable/define
Give a short explanation of what the variable, in the context of the class, or define is.
```
/// Type path of item to go in suit slot
var/suit = null
```
## Module level description of code
Modules are the best way to describe the structure/intent of a package of code
where you don't want to be tied to the formal layout of the class structure.
On /tg/station we do this by adding markdown files inside the `code` directory
that will also be rendered and added to the modules tree. The structure for
these is deliberately not defined, so you can be as freeform and as wheeling as
you would like.
[Here is a representative example of what you might write](http://codedocs.tgstation13.org/code/modules/keybindings/readme.html)
## Special variables
You can use certain special template variables in DM DOC comments and they will be expanded
```
[DEFINE_NAME] - Expands to a link to the define definition if documented
[/mob] - Expands to a link to the docs for the /mob class
[/mob/proc/Dizzy] - Expands to a link that will take you to the /mob class and anchor you to the dizzy proc docs
[/mob/var/stat] - Expands to a link that will take you to the /mob class and anchor you to the stat var docs
```
You can customise the link name by using `[link name][link shorthand].`
eg. `[see more about dizzy here] [/mob/proc/Dizzy]`
This is very useful to quickly link to other parts of the autodoc code to expand
upon a comment made, or reasoning about code
+345 -90
View File
@@ -1,8 +1,29 @@
# CONTRIBUTING
## Reporting Issues
1. [Reporting Issues](#reporting-issues)
2. [Introduction](#introduction)
3. [Getting Started](#getting-started)
4. [Meet the Team](#meet-the-team)
1. [Headcoder](#headcoder)
2. [Maintainers](#maintainers)
3. [Issue Managers](#issue-managers)
5. [Specifications](#specifications)
6. [Pull Request Process](#pull-request-process)
7. [Porting features/sprites/sounds/tools from other codebases](#porting-featuresspritessoundstools-from-other-codebases)
8. [Banned content](#banned-content)
9. [A word on Git](#a-word-on-git)
See [this page](http://tgstation13.org/wiki/Reporting_Issues) for a guide and format to issue reports.
## Reporting Issues
If you ever encounter a bug in-game, the best way to let a coder know about it is with our GitHub Issue Tracker. Please make sure you use the supplied issue template, and include the round ID for the server.
(If you don't have an account, making a new one takes only one minute.)
If you have difficulty, ask for help in the #coding-general channel on our discord.
## Citadel Station 13
Notice: We are currently using a near-copy of /tg/station's CONTRIBUTING.md file. Keep in mind which codebase you are on.
Any questions regarding help for development/spriting/coding/etc should go on #development-discussion-main on our discord.
https://discord.gg/fkVVfVH48n
## Introduction
@@ -15,35 +36,63 @@ First things first, we want to make it clear how you can contribute (if you've n
/tg/station doesn't have a list of goals and features to add; we instead allow freedom for contributors to suggest and create their ideas for the game. That doesn't mean we aren't determined to squash bugs, which unfortunately pop up a lot due to the deep complexity of the game. Here are some useful starting guides, if you want to contribute or if you want to know what challenges you can tackle with zero knowledge about the game's code structure.
If you want to contribute the first thing you'll need to do is [set up Git](http://tgstation13.org/wiki/Setting_up_git) so you can download the source code.
After setting it up, optionally navigate your git commandline to the project folder and run the command: `git config blame.ignoreRevsFile .git-blame-ignore-revs`.
We have a [list of guides on the wiki](http://www.tgstation13.org/wiki/index.php/Guides#Development_and_Contribution_Guides) that will help you get started contributing to /tg/station with Git and Dream Maker. For beginners, it is recommended you work on small projects like bugfixes at first. If you need help learning to program in BYOND, check out this [repository of resources](http://www.byond.com/developer/articles/resources).
We have a [list of guides on the wiki](http://www.tgstation13.org/wiki/Guides#Development_and_Contribution_Guides) that will help you get started contributing to /tg/station with Git and Dream Maker. For beginners, it is recommended you work on small projects like bugfixes at first. If you need help learning to program in BYOND, check out this [repository of resources](http://www.byond.com/developer/articles/resources).
There is an open list of approachable issues for [your inspiration here](https://github.com/tgstation/-tg-station/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+First+Issue%22).
There is an open list of approachable issues for [your inspiration here](https://github.com/Citadel-Station-13/Citadel-Station-13/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+First+Issue%22).
You can of course, as always, ask for help at [#coderbus](irc://irc.rizon.net/coderbus) on irc.rizon.net. We're just here to have fun and help out, so please don't expect professional support.
You can of course, as always, ask for help on the Discord channels or the forums. We're just here to have fun and help out, so please don't expect professional support.
## Meet the Team
**Design Lead**
The Design Lead has the final say on what gameplay changes get into and out of the game. He or she has full veto power on any feature or balance additions, changes, or removals, and establishes a general, personally-preferred direction for the game.
**Headcoder**
### Headcoder
The Headcoder is responsible for controlling, adding, and removing maintainers from the project. In addition to filling the role of a normal maintainer, they have sole authority on who becomes a maintainer, as well as who remains a maintainer and who does not.
**Art Director**
The Art Director controls sprites and aesthetic that get into the game. While sprites for brand-new additions are generally accepted regardless of quality, modified current art assets fall to the Art Director, who can decide whether or not a sprite tweak is both merited and a suitable replacement.
They also control the general "perspective" of the game - how sprites should generally look, especially the angle from which they're viewed. An example of this is the [3/4 perspective](http://static.tvtropes.org/pmwiki/pub/images/kakarikovillage.gif), which is a bird's eye view from above the object being viewed.
**Maintainers**
### Maintainers
Maintainers are quality control. If a proposed pull request doesn't meet the following specifications, they can request you to change it, or simply just close the pull request. Maintainers are required to give a reason for closing the pull request.
Maintainers can revert your changes if they feel they are not worth maintaining or if they did not live up to the quality specifications.
<details>
<summary>Maintainer Guidelines</summary>
These are the few directives we have for project maintainers.
- Do not merge PRs you create.
- Do not merge PRs until 24 hours have passed since it was opened. Exceptions include:
- Emergency fixes.
- Try to get secondary maintainer approval before merging if you are able to.
- PRs with empty commits intended to generate a changelog.
- Do not merge PRs that contain content from the [banned content list](./CONTRIBUTING.md#banned-content).
These are not steadfast rules as maintainers are expected to use their best judgement when operating.
Our team is entirely voluntary, as such we extend our thanks to maintainers, issue managers, and contributors alike for helping keep the project alive.
</details>
### Issue Managers
Issue Managers help out the project by labelling bug reports and PRs and closing bug reports which are duplicates or are no longer applicable.
<details>
<summary>What You Can and Can't Do as an Issue Manager</summary>
This should help you understand what you can and can't do with your newfound github permissions.
Things you **CAN** do:
* Label issues appropriately
* Close issues when appropriate
* Label PRs when appropriate
Things you **CAN'T** do:
* [Close PRs](https://imgur.com/w2RqpX8.png): Only maintainers are allowed to close PRs. Do not hit that button.
</details>
## Specifications
As mentioned before, you are expected to follow these specifications in order to make everyone's lives easier. It'll save both your time and ours, by making sure you don't have to make any changes and we don't have to ask you to. Thank you for reading this section!
@@ -57,6 +106,7 @@ As BYOND's Dream Maker (henceforth "DM") is an object-oriented language, code mu
DM will allow you nest almost any type keyword into a block, such as:
```DM
// Not our style!
datum
datum1
var
@@ -77,7 +127,7 @@ datum
proc3()
code
proc2()
..()
. = ..()
code
```
@@ -86,6 +136,7 @@ The use of this is not allowed in this project as it makes finding definitions v
The previous code made compliant:
```DM
// Matches /tg/station style.
/datum/datum1
var/varname1
var/varname2
@@ -101,18 +152,50 @@ The previous code made compliant:
/datum/datum1/datum2/proc/proc3()
code
/datum/datum1/datum2/proc2()
..()
. = ..()
code
```
### All `process` procs need to make use of delta-time and be frame independent
In a lot of our older code, `process()` is frame dependent. Here's some example mob code:
```DM
/mob/testmob
var/health = 100
var/health_loss = 4 //We want to lose 2 health per second, so 4 per SSmobs process
/mob/testmob/process(delta_time) //SSmobs runs once every 2 seconds
health -= health_loss
```
As the mobs subsystem runs once every 2 seconds, the mob now loses 4 health every process, or 2 health per second. This is called frame dependent programming.
Why is this an issue? If someone decides to make it so the mobs subsystem processes once every second (2 times as fast), your effects in process() will also be two times as fast. Resulting in 4 health loss per second rather than 2.
How do we solve this? By using delta-time. Delta-time is the amount of seconds you would theoretically have between 2 process() calls. In the case of the mobs subsystem, this would be 2 (As there is 2 seconds between every call in `process()`). Here is a new example using delta-time:
```DM
/mob/testmob
var/health = 100
var/health_loss = 2 //Health loss every second
/mob/testmob/process(delta_time) //SSmobs runs once every 2 seconds
health -= health_loss * delta_time
```
In the above example, we made our health_loss variable a per second value rather than per process. In the actual process() proc we then make use of deltatime. Because SSmobs runs once every 2 seconds. Delta_time would have a value of 2. This means that by doing health_loss * delta_time, you end up with the correct amount of health_loss per process, but if for some reason the SSmobs subsystem gets changed to be faster or slower in a PR, your health_loss variable will work the same.
For example, if SSmobs is set to run once every 4 seconds, it would call process once every 4 seconds and multiply your health_loss var by 4 before subtracting it. Ensuring that your code is frame independent.
### 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.
### Type paths must begin with a /
### Type paths must begin with a `/`
eg: `/datum/thing`, not `datum/thing`
### Type paths must be lowercase
eg: `/datum/thing/blue`, not `datum/thing/BLUE` or `datum/thing/Blue`
### Type paths must be snake case
eg: `/datum/blue_bird`, not `/datum/BLUEBIRD` or `/datum/BlueBird` or `/datum/Bluebird` or `/datum/blueBird`
### Datum type paths must began with "datum"
In DM, this is optional, but omitting it makes finding definitions harder.
@@ -128,16 +211,28 @@ var/path_type = /obj/item/baseball_bat
var/path_type = "/obj/item/baseball_bat"
```
### Use var/name format when declaring variables
### Use `var/name` format when declaring variables
While DM allows other ways of declaring variables, this one should be used for consistency.
### Tabs, not spaces
You must use tabs to indent your code, NOT SPACES.
(You may use spaces to align something, but you should tab to the block level first, then add the remaining spaces)
Do not use tabs/spaces for indentation in the middle of a code line. Not only is this inconsistent because the size of a tab is undefined, but it means that, should the line you're aligning to change size at all, we have to adjust a ton of other code. Plus, it often time hurts readability.
```dm
// Bad
#define SPECIES_MOTH "moth"
#define SPECIES_LIZARDMAN "lizardman"
#define SPECIES_FELINID "felinid"
// Good
#define SPECIES_MOTH "moth"
#define SPECIES_LIZARDMAN "lizardman"
#define SPECIES_FELINID "felinid"
```
### No hacky code
Hacky code, such as adding specific checks, is highly discouraged and only allowed when there is ***no*** other option. (Protip: 'I couldn't immediately think of a proper way so thus there must be no other option' is not gonna cut it here! If you can't think of anything else, say that outright and admit that you need help with it. Maintainers exist for exactly that reason.)
Hacky code, such as adding specific checks, is highly discouraged and only allowed when there is ***no*** other option. (Protip: "I couldn't immediately think of a proper way so thus there must be no other option" is not gonna cut it here! If you can't think of anything else, say that outright and admit that you need help with it. Maintainers exist for exactly that reason.)
You can avoid hacky code by using object-oriented methodologies, such as overriding a function (called "procs" in DM) or sectioning code into functions and then overriding them as required.
@@ -158,7 +253,7 @@ There are two key points here:
Remember: although this tradeoff makes sense in many cases, it doesn't cover them all. Think carefully about your addition before deciding if you need to use it.
### Prefer `Initialize()` over `New()` for atoms
Our game controller is pretty good at handling long operations and lag, but it can't control what happens when the map is loaded, which calls `New` for all atoms on the map. If you're creating a new atom, use the `Initialize` proc to do what you would normally do in `New`. This cuts down on the number of proc calls needed when the world is loaded. See here for details on `Initialize`: https://github.com/tgstation/tgstation/blob/master/code/game/atoms.dm#L49
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. See here for details on `Initialize`: https://github.com/tgstation/tgstation/blob/34775d42a2db4e0f6734560baadcfcf5f5540910/code/game/atoms.dm#L166
While we normally encourage (and in some cases, even require) bringing out of date code up to date when you make unrelated changes near the out of date code, that is not the case for `New` -> `Initialize` conversions. These systems are generally more dependant on parent and children procs so unrelated random conversions of existing things can cause bugs that take months to figure out.
### No magic numbers or strings
@@ -187,7 +282,7 @@ This is clearer and enhances readability of your code! Get used to doing it!
### Control statements
(if, while, for, etc)
* All control statements must not contain code on the same line as the statement (`if (blah) return`)
* No control statement may contain code on the same line as the statement (`if (blah) return`)
* All control statements comparing a variable to a number should use the formula of `thing` `operator` `number`, not the reverse (eg: `if (count <= 10)` not `if (10 >= count)`)
### Use early return
@@ -213,6 +308,97 @@ This is good:
````
This prevents nesting levels from getting deeper then they need to be.
### Use our time defines
The codebase contains some defines which will automatically multiply a number by the correct amount to get a number in deciseconds. Using these is preffered over using a literal amount in deciseconds.
The defines are as follows:
* SECONDS
* MINUTES
* HOURS
This is bad:
````DM
/datum/datum1/proc/proc1()
if(do_after(mob, 15))
mob.dothing()
````
This is good:
````DM
/datum/datum1/proc/proc1()
if(do_after(mob, 1.5 SECONDS))
mob.dothing()
````
### Getters and setters
* Avoid getter procs. They are useful tools in languages with that properly enforce variable privacy and encapsulation, but DM is not one of them. The upfront cost in proc overhead is met with no benefits, and it may tempt to develop worse code.
This is bad:
```DM
/datum/datum1/proc/simple_getter()
return gotten_variable
```
Prefer to either access the variable directly or use a macro/define.
* Make usage of variables or traits, set up through condition setters, for a more maintainable alternative to compex and redefined getters.
These are bad:
```DM
/datum/datum1/proc/complex_getter()
return condition ? VALUE_A : VALUE_B
/datum/datum1/child_datum/complex_getter()
return condition ? VALUE_C : VALUE_D
```
This is good:
```DM
/datum/datum1
var/getter_turned_into_variable
/datum/datum1/proc/set_condition(new_value)
if(condition == new_value)
return
condition = new_value
on_condition_change()
/datum/datum1/proc/on_condition_change()
getter_turned_into_variable = condition ? VALUE_A : VALUE_B
/datum/datum1/child_datum/on_condition_change()
getter_turned_into_variable = condition ? VALUE_C : VALUE_D
```
### Avoid unnecessary type checks and obscuring nulls in lists
Typecasting in `for` loops carries an implied `istype()` check that filters non-matching types, nulls included. The `as anything` key can be used to skip the check.
If we know the list is supposed to only contain the desired type then we want to skip the check not only for the small optimization it offers, but also to catch any null entries that may creep into the list.
Nulls in lists tend to point to improperly-handled references, making hard deletes hard to debug. Generating a runtime in those cases is more often than not positive.
This is bad:
```DM
var/list/bag_of_atoms = list(new /obj, new /mob, new /atom, new /atom/movable, new /atom/movable)
var/highest_alpha = 0
for(var/atom/thing in bag_of_atoms)
if(thing.alpha <= highest_alpha)
continue
highest_alpha = thing.alpha
```
This is good:
```DM
var/list/bag_of_atoms = list(new /obj, new /mob, new /atom, new /atom/movable, new /atom/movable)
var/highest_alpha = 0
for(var/atom/thing as anything in bag_of_atoms)
if(thing.alpha <= highest_alpha)
continue
highest_alpha = thing.alpha
```
### 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
@@ -245,15 +431,24 @@ This prevents nesting levels from getting deeper then they need to be.
* Primary keys are inherently immutable and you must never do anything to change the primary key of a row or entity. This includes preserving auto increment numbers of rows when copying data to a table in a conversion script. No amount of bitching about gaps in ids or out of order ids will save you from this policy.
* The ttl for data from the database is 10 seconds. You must have a compelling reason to store and reuse data for longer then this.
* Do not write stored and transformed data to the database, instead, apply the transformation to the data in the database directly.
* ie: SELECTing a number from the database, doubling it, then updating the database with the doubled number. If the data in the database changed between step 1 and 3, you'll get an incorrect result. Instead, directly double it in the update query. `UPDATE table SET num = num*2` instead of `UPDATE table SET num = [num]`.
* if the transformation is user provided (such as allowing a user to edit a string), you should confirm the value being updated did not change in the database in the intervening time before writing the new user provided data by checking the old value with the current value in the database, and if it has changed, allow the user to decide what to do next.
### Mapping Standards
* Adding, Removing, or Replacing Station Maps
* All pull requests adding, removing, or replacing station maps must receive prior approval from a maptainer, or they will be closed without additional warning.
* TGM Format & Map Merge
* All new maps submitted to the repo through a pull request must be in TGM format (unless there is a valid reason present to have it in the default BYOND format.) This is done using the [Map Merge](https://github.com/tgstation/tgstation/wiki/Map-Merger) utility included in the repo to convert the file to TGM format.
* All new maps submitted to the repo through a pull request must be in TGM format (unless there is a valid reason present to have it in the default BYOND format). This is done using the [Map Merge](https://tgstation13.org/wiki/Map_Merger) utility included in the repo to convert the file to TGM format.
* Likewise, you MUST run Map Merge prior to opening your PR when updating existing maps to minimize the change differences (even when using third party mapping programs such as FastDMM.)
* Failure to run Map Merge on a map after using third party mapping programs (such as FastDMM) greatly increases the risk of the map's key dictionary becoming corrupted by future edits after running map merge. Resolving the corruption issue involves rebuilding the map's key dictionary; id est rewriting all the keys contained within the map by reconverting it from BYOND to TGM format - which creates very large differences that ultimately delay the PR process and is extremely likely to cause merge conflicts with other pull requests.
* Variable Editing (Var-edits)
* While var-editing an item within the editor is perfectly fine, it is preferred that when you are changing the base behavior of an item (how it functions) that you make a new subtype of that item within the code, especially if you plan to use the item in multiple locations on the same map, or across multiple maps. This makes it easier to make corrections as needed to all instances of the item at one time as opposed to having to find each instance of it and change them all individually.
* Subtypes only intended to be used on away mission or ruin maps should be contained within an .dm file with a name corresponding to that map within `code\modules\awaymissions` or `code\modules\ruins` respectively. This is so in the event that the map is removed, that subtype will be removed at the same time as well to minimize leftover/unused data within the repo.
* Subtypes only intended to be used on away mission or ruin maps should be contained within a .dm file with a name corresponding to that map within `code\modules\awaymissions` or `code\modules\ruins` respectively. This is so in the event that the map is removed, that subtype will be removed at the same time as well to minimize leftover/unused data within the repo.
* Please attempt to clean out any dirty variables that may be contained within items you alter through var-editing. For example, due to how DM functions, changing the `pixel_x` variable from 23 to 0 will leave a dirty record in the map's code of `pixel_x = 0`. Likewise this can happen when changing an item's icon to something else and then back. This can lead to some issues where an item's icon has changed within the code, but becomes broken on the map due to it still attempting to use the old entry.
* Areas should not be var-edited on a map to change it's name or attributes. All areas of a single type and it's altered instances are considered the same area within the code, and editing their variables on a map can lead to issues with powernets and event subsystems which are difficult to debug.
@@ -264,6 +459,99 @@ This prevents nesting levels from getting deeper then they need to be.
* [tgui/README.md](../tgui/README.md)
* [tgui/tutorial-and-examples.md](../tgui/docs/tutorial-and-examples.md)
### Signal Handlers
All procs that are registered to listen for signals using `RegisterSignal()` must contain at the start of the proc `SIGNAL_HANDLER` eg;
```
/type/path/proc/signal_callback()
SIGNAL_HANDLER
// rest of the code
```
This is to ensure that it is clear the proc handles signals and turns on a lint to ensure it does not sleep.
There exists `SIGNAL_HANDLER_DOES_SLEEP`, but this is only for legacy signal handlers that still sleep, new/changed code should not use this.
### Enforcing parent calling
When adding new signals to root level procs, eg;
```
/atom/proc/setDir(newdir)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir)
dir = newdir
```
The `SHOULD_CALL_PARENT(TRUE)` lint should be added to ensure that overrides/child procs call the parent chain and ensure the signal is sent.
### Use descriptive and obvious names
Optimize for readability, not writability. While it is certainly easier to write `M` than `victim`, it will cause issues down the line for other developers to figure out what exactly your code is doing, even if you think the variable's purpose is obvious.
#### Don't use abbreviations
Avoid variables like C, M, and H. Prefer names like "user", "victim", "weapon", etc.
```dm
// What is M? The user? The target?
// What is A? The target? The item?
/proc/use_item(mob/M, atom/A)
// Much better!
/proc/use_item(mob/user, atom/target)
```
Unless it is otherwise obvious, try to avoid just extending variables like "C" to "carbon"--this is slightly more helpful, but does not describe the *context* of the use of the variable.
#### Naming things when typecasting
When typecasting, keep your names descriptive:
```dm
var/mob/living/living_target = target
var/mob/living/carbon/carbon_target = living_target
```
Of course, if you have a variable name that better describes the situation when typecasting, feel free to use it.
Note that it's okay, semantically, to use the same variable name as the type, e.g.:
```dm
var/atom/atom
var/client/client
var/mob/mob
```
Your editor may highlight the variable names, but BYOND, and we, accept these as variable names:
```dm
// This functions properly!
var/client/client = CLIENT_FROM_VAR(usr)
// vvv this may be highlighted, but it's fine!
client << browse(...)
```
#### Name things as directly as possible
`was_called` is better than `has_been_called`. `notify` is better than `do_notification`.
#### Avoid negative variable names
`is_flying` is better than `is_not_flying`. `late` is better than `not_on_time`.
This prevents double-negatives (such as `if (!is_not_flying)` which can make complex checks more difficult to parse.
#### Exceptions to variable names
Exceptions can be made in the case of inheriting existing procs, as it makes it so you can use named parameters, but *new* variable names must follow these standards. It is also welcome, and encouraged, to refactor existing procs to use clearer variable names.
Naming numeral iterator variables `i` is also allowed, but do remember to [Avoid unnecessary type checks and obscuring nulls in lists](#avoid-unnecessary-type-checks-and-obscuring-nulls-in-lists), and making more descriptive variables is always encouraged.
```dm
// Bad
for (var/datum/reagent/R as anything in reagents)
// Good
for (var/datum/reagent/deadly_reagent as anything in reagents)
// Allowed, but still has the potential to not be clear. What does `i` refer to?
for (var/i in 1 to 12)
// Better
for (var/month in 1 to 12)
// Bad, only use `i` for numeral loops
for (var/i in reagents)
```
### 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)
@@ -273,6 +561,8 @@ This prevents nesting levels from getting deeper then they need to be.
* Do not divide when you can easily convert it to multiplication. (ie `4/2` should be done as `4*0.5`)
* Separating single lines into more readable blocks is not banned, however you should use it only where it makes new information more accessible, or aids maintainability. We do not have a column limit, and mass conversions will not be received well.
* If you used regex to replace code during development of your code, post the regex in your PR for the benefit of future developers and downstream users.
* Changes to the `/config` tree must be made in a way that allows for updating server deployments while preserving previous behaviour. This is due to the fact that the config tree is to be considered owned by the user and not necessarily updated alongside the remainder of the code. The code to preserve previous behaviour may be removed at some point in the future given the OK by maintainers.
@@ -306,77 +596,43 @@ Math operators like +, -, /, *, etc are up in the air, just choose which version
#### Use
* 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 `variable & CONSTANT` NEVER `CONSTANT & variable`. Both are valid, but the latter is confusing and nonstandard.
* Associated lists declarations must have their key value quoted if it's a string
* WRONG: list(a = "b")
* RIGHT: list("a" = "b")
* WRONG: `list(a = "b")`
* RIGHT: `list("a" = "b")`
### Dream Maker Quirks/Tricks
Like all languages, Dream Maker has its quirks, some of them are beneficial to us, like these
#### In-To for-loops
```for(var/i = 1, i <= some_value, i++)``` is a fairly standard way to write an incremental for loop in most languages (especially those in the C family), but DM's ```for(var/i in 1 to some_value)``` syntax is oddly faster than its implementation of the former syntax; where possible, it's advised to use DM's syntax. (Note, the ```to``` keyword is inclusive, so it automatically defaults to replacing ```<=```; if you want ```<``` then you should write it as ```1 to some_value-1```).
`for(var/i = 1, i <= some_value, i++)` is a fairly standard way to write an incremental for loop in most languages (especially those in the C family), but DM's `for(var/i in 1 to some_value)` syntax is oddly faster than its implementation of the former syntax; where possible, it's advised to use DM's syntax. (Note, the `to` keyword is inclusive, so it automatically defaults to replacing `<=`; if you want `<` then you should write it as `1 to some_value-1`).
HOWEVER, if either ```some_value``` or ```i``` changes within the body of the for (underneath the ```for(...)``` header) or if you are looping over a list AND changing the length of the list then you can NOT use this type of for-loop!
HOWEVER, if either `some_value` or `i` changes within the body of the for (underneath the `for(...)` header) or if you are looping over a list AND changing the length of the list then you can NOT use this type of for-loop!
### for(var/A in list) VS for(var/i in 1 to list.len)
#### `for(var/A in list)` versus `for(var/i in 1 to list.len)`
The former is faster than the latter, as shown by the following profile results:
https://file.house/zy7H.png
Code used for the test in a readable format:
https://pastebin.com/w50uERkG
#### Istypeless for loops
A name for a differing syntax for writing for-each style loops in DM. It's NOT DM's standard syntax, hence why this is considered a quirk. Take a look at this:
```DM
var/list/bag_of_items = list(sword, apple, coinpouch, sword, sword)
var/obj/item/sword/best_sword
for(var/obj/item/sword/S in bag_of_items)
if(!best_sword || S.damage > best_sword.damage)
best_sword = S
```
The above is a simple proc for checking all swords in a container and returning the one with the highest damage, and it uses DM's standard syntax for a for-loop by specifying a type in the variable of the for's header that DM interprets as a type to filter by. It performs this filter using ```istype()``` (or some internal-magic similar to ```istype()``` - this is BYOND, after all). This is fine in its current state for ```bag_of_items```, but if ```bag_of_items``` contained ONLY swords, or only SUBTYPES of swords, then the above is inefficient. For example:
```DM
var/list/bag_of_swords = list(sword, sword, sword, sword)
var/obj/item/sword/best_sword
for(var/obj/item/sword/S in bag_of_swords)
if(!best_sword || S.damage > best_sword.damage)
best_sword = S
```
specifies a type for DM to filter by.
With the previous example that's perfectly fine, we only want swords, but here the bag only contains swords? Is DM still going to try to filter because we gave it a type to filter by? YES, and here comes the inefficiency. Wherever a list (or other container, such as an atom (in which case you're technically accessing their special contents list, but that's irrelevant)) contains datums of the same datatype or subtypes of the datatype you require for your loop's body,
you can circumvent DM's filtering and automatic ```istype()``` checks by writing the loop as such:
```DM
var/list/bag_of_swords = list(sword, sword, sword, sword)
var/obj/item/sword/best_sword
for(var/s in bag_of_swords)
var/obj/item/sword/S = s
if(!best_sword || S.damage > best_sword.damage)
best_sword = S
```
Of course, if the list contains data of a mixed type then the above optimisation is DANGEROUS, as it will blindly typecast all data in the list as the specified type, even if it isn't really that type, causing runtime errors.
#### Dot variable
Like other languages in the C family, DM has a ```.``` or "Dot" operator, used for accessing variables/members/functions of an object instance.
Like other languages in the C family, DM has a `.` or "Dot" operator, used for accessing variables/members/functions of an object instance.
eg:
```DM
var/mob/living/carbon/human/H = YOU_THE_READER
H.gib()
```
However, DM also has a dot variable, accessed just as ```.``` on its own, defaulting to a value of null. Now, what's special about the dot operator is that it is automatically returned (as in the ```return``` statement) at the end of a proc, provided the proc does not already manually return (```return count``` for example.) Why is this special?
However, DM also has a dot variable, accessed just as `.` on its own, defaulting to a value of null. Now, what's special about the dot operator is that it is automatically returned (as in the `return` statement) at the end of a proc, provided the proc does not already manually return (`return count` for example.) Why is this special?
With ```.``` being everpresent in every proc, can we use it as a temporary variable? Of course we can! However, the ```.``` operator cannot replace a typecasted variable - it can hold data any other var in DM can, it just can't be accessed as one, although the ```.``` operator is compatible with a few operators that look weird but work perfectly fine, such as: ```.++``` for incrementing ```.'s``` value, or ```.[1]``` for accessing the first element of ```.```, provided that it's a list.
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.
## Globals versus static
### Globals versus static
DM has a var keyword, called global. This var keyword is for vars inside of types. For instance:
```DM
mob
var
global
thing = TRUE
/mob
var/global/thing = TRUE
```
This does NOT mean that you can access it everywhere like a global var. Instead, it means that that var will only exist once for all instances of its type, in this case that var will only exist once for all mobs - it's shared across everything in its type. (Much more like the keyword `static` in other languages like PHP/C++/C#/Java)
@@ -388,11 +644,13 @@ There is also an undocumented keyword called `static` that has the same behaviou
There is no strict process when it comes to merging pull requests. Pull requests will sometimes take a while before they are looked at by a maintainer; the bigger the change, the more time it will take before they are accepted into the code. Every team member is a volunteer who is giving up their own time to help maintain and contribute, so please be courteous and respectful. Here are some helpful ways to make it easier for you and for the maintainers when making a pull request.
* Make sure your pull request complies to the requirements outlined in [this guide](http://tgstation13.org/wiki/Getting_Your_Pull_Accepted)
* Make sure your pull request complies to the requirements outlined here
* You are going to be expected to document all your changes in the pull request. Failing to do so will mean delaying it as we will have to question why you made the change. On the other hand, you can speed up the process by making the pull request readable and easy to understand, with diagrams or before/after data.
* You are expected to have tested your pull requests if it is anything that would warrant testing. Text only changes, single number balance changes, and similar generally don't need testing, but anything else does. This means by extension web edits are disallowed for larger changes.
* We ask that you use the changelog system to document your change, which prevents our players from being caught unaware by changes - you can find more information about this [on this wiki page](http://tgstation13.org/wiki/Guide_to_Changelogs).
* You are going to be expected to document all your changes in the pull request. Failing to do so will mean delaying it as we will have to question why you made the change. On the other hand, you can speed up the process by making the pull request readable and easy to understand, with diagrams or before/after data. Should you be optimizing a routine you must provide proof by way of profiling that your changes are faster.
* We ask that you use the changelog system to document your player facing changes, which prevents our players from being caught unaware by said changes - you can find more information about this [on this wiki page](http://tgstation13.org/wiki/Guide_to_Changelogs).
* If you are proposing multiple changes, which change many different aspects of the code, you are expected to section them off into different pull requests in order to make it easier to review them and to deny/accept the changes that are deemed acceptable.
@@ -400,10 +658,12 @@ There is no strict process when it comes to merging pull requests. Pull requests
* Please explain why you are submitting the pull request, and how you think your change will be beneficial to the game. Failure to do so will be grounds for rejecting the PR.
* If your pull request is not finished make sure it is at least testable in a live environment. Pull requests that do not at least meet this requirement will be closed. You may request a maintainer reopen the pull request when you're ready, or make a new one.
* If your pull request is not finished, you may open it as a draft for potential review. If you open it as a full-fledged PR make sure it is at least testable in a live environment. Pull requests that do not at least meet this requirement will be closed. You may request a maintainer reopen the pull request when you're ready, or make a new one.
* While we have no issue helping contributors (and especially new contributors) bring reasonably sized contributions up to standards via the pull request review process, larger contributions are expected to pass a higher bar of completeness and code quality *before* you open a pull request. Maintainers may close such pull requests that are deemed to be substantially flawed. You should take some time to discuss with maintainers or other contributors on how to improve the changes.
* After leaving reviews on an open pull request, maintainers may convert it to a draft. Once you have addressed all their comments to the best of your ability, feel free to mark the pull as `Ready for Review` again.
## Porting features/sprites/sounds/tools from other codebases
If you are porting features/tools from other codebases, you must give them credit where it's due. Typically, crediting them in your pull request and the changelog is the recommended way of doing it. Take note of what license they use though, porting stuff from AGPLv3 and GPLv3 codebases are allowed.
@@ -413,19 +673,14 @@ Regarding sprites & sounds, you must credit the artist and possibly the codebase
## Banned content
Do not add any of the following in a Pull Request or risk getting the PR closed:
* National Socialist Party of Germany content, National Socialist Party of Germany related content, or National Socialist Party of Germany references
* Code where one line of code is split across mutiple lines (except for multiple, separate strings and comments; in those cases, existing longer lines must not be split up)
* Code adding, removing, or updating the availability of alien races/species/human mutants without prior approval. Pull requests attempting to add or remove features from said races/species/mutants require prior approval as well.
* Code which violates GitHub's [terms of service](https://github.com/site/terms).
Just because something isn't on this list doesn't mean that it's acceptable. Use common sense above all else.
## Content requiring prior approval
Certain types of changes may require prior approval from maintainers. This currently includes:
* Code adding, removing, or updating the availability of alien races/species/human mutants. This includes pull requests attempting to add or remove features from said races/species/mutants. (Requires approval from at least one maintainer)
* Code adding, removing, or modifying the functionality of adult-oriented features (such as, but not limited to: vore, genitals, MKUltra, and more). This also includes pull requests attempting to add or remove these features outright. (Requires approval from at least half of the formal maintainer team)
The above content requires approval from the specified amount of maintainers prior to PR creation. Seeking approval must be done via a @Maintainer ping in a relevant development/code or staff channel on the Discord, otherwise it will be considered insufficient. If a PR contains any of the above content, but the creator does not have sufficient approval prior to the PR's creation, then the PR may be closed by any maintainer, at any time, for any reason.
## A word on Git
Yes, we know that the files have a tonne of mixed Windows and Linux line endings. Attempts to fix this have been met with less than stellar success, and as such we have decided to give up caring until there comes a time when it matters.
This repository uses `LF` line endings for all code as specified in the **.gitattributes** and **.editorconfig** files.
Therefore, EOF settings of main repo are forbidden territory one must avoid wandering into, at risk of losing body and/or mind to the Git gods.
Unless overridden or a non standard git binary is used the line ending settings should be applied to your clone automatically.
Note: VSC requires an [extension](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) to take advantage of editorconfig.
+10
View File
@@ -0,0 +1,10 @@
## DOWNLOADING
There are a number of ways to download the source code. Some are described here, an alternative all-inclusive guide is also located at https://www.tgstation13.org/wiki/Downloading_the_source_code
Option 1:
Follow this: https://www.tgstation13.org/wiki/Setting_up_git, only with Citadel Station's repository instead of /tg/'s.
Option 2: Download the source code as a zip by clicking the ZIP button in the
code tab of https://github.com/Citadel-Station-13/Citadel-Station-13
(note: this will use a lot of bandwidth if you wish to update and is a lot of
hassle if you want to make any changes at all, so it's not recommended.)
+95
View File
@@ -0,0 +1,95 @@
# INSTALLATION
First-time installation should be fairly straightforward. First, you'll need
BYOND installed. You can get it from https://www.byond.com/download. Once you've done
that, extract the game files to wherever you want to keep them. This is a
sourcecode-only release, so the next step is to compile the server files.
Double-click `BUILD.bat` in the root directory of the source code. This'll take
a little while, and if everything's done right you'll get a message like this:
```
saving tgstation.dmb (DEBUG mode)
tgstation.dmb - 0 errors, 0 warnings
```
If you see any errors or warnings, something has gone wrong - possibly a corrupt
download or the files extracted wrong. If problems persist, ask for assistance
in irc://irc.rizon.net/coderbus
Once that's done, open up the config folder. You'll want to edit config.txt to
set the probabilities for different gamemodes in Secret and to set your server
location so that all your players don't get disconnected at the end of each
round. It's recommended you don't turn on the gamemodes with probability 0,
except Extended, as they have various issues and aren't currently being tested,
so they may have unknown and bizarre bugs. Extended is essentially no mode, and
isn't in the Secret rotation by default as it's just not very fun.
You'll also want to edit config/admins.txt to remove the default admins and add
your own. "Game Master" is the highest level of access, and probably the one
you'll want to use for now. You can set up your own ranks and find out more in
config/admin_ranks.txt
The format is
```
byondkey = Rank
```
where the admin rank must be properly capitalised.
This codebase also depends on a native library called rust-g. A precompiled
Windows DLL is included in this repository, but Linux users will need to build
and install it themselves. Directions can be found at the [rust-g
repo](https://github.com/tgstation/rust-g).
Finally, to start the server, run Dream Daemon and enter the path to your
compiled tgstation.dmb file. Make sure to set the port to the one you
specified in the config.txt, and set the Security box to 'Safe'. Then press GO
and the server should start up and be ready to join. It is also recommended that
you set up the SQL backend (see below).
## UPDATING
To update an existing installation, first back up your /config and /data folders
as these store your server configuration, player preferences and banlist.
Then, extract the new files (preferably into a clean directory, but updating in
place should work fine), copy your /config and /data folders back into the new
install, overwriting when prompted except if we've specified otherwise, and
recompile the game. Once you start the server up again, you should be running
the new version.
## HOSTING
If you'd like a more robust server hosting option for tgstation and its
derivatives. Check out our server tools suite at
https://github.com/tgstation/tgstation-server
If you decide to go this route, here are /tg/ specific details on hosting with TGS.
- We have two directories which should be setup in the instance's `Configuration/GameStaticFiles` directory:
- `config` should be where you place your production configuration. Overwrites the default contents of the repo's [config](../config) directory.
- `data` should be initially created as an empty directory. The game stores persistent data here.
- You should incorporate our [custom build scripts for TGS4](../tools/tgs4_scripts) in the instance's `Configuration/EventScripts` directory. These handle including TGUI in the build and setting up rust-g on Linux.
- Deployment security level must be set to `Trusted` or it will likely fail due to our native library usage.
- We highly recommend using the BYOND version specified in [dependencies.sh](../dependencies.sh) to avoid potential unrecorded issues.
## SQL SETUP
The SQL backend requires a Mariadb server running 10.2 or later. Mysql is not supported but Mariadb is a drop in replacement for mysql. SQL is required for the library, stats tracking, admin notes, and job-only bans, among other features, mostly related to server administration. Your server details go in /config/dbconfig.txt, and the SQL schema is in /SQL/tgstation_schema.sql and /SQL/tgstation_schema_prefix.sql depending on if you want table prefixes. More detailed setup instructions are located here: https://www.tgstation13.org/wiki/Downloading_the_source_code#Setting_up_the_database
If you are hosting a testing server on windows you can use a standalone version of MariaDB pre load with a blank (but initialized) tgdb database. Find them here: https://tgstation13.download/database/ Just unzip and run for a working (but insecure) database server. Includes a zipped copy of the data folder for easy resetting back to square one.
## WEB/CDN RESOURCE DELIVERY
Web delivery of game resources makes it quicker for players to join and reduces some of the stress on the game server.
1. Edit compile_options.dm to set the `PRELOAD_RSC` define to `0`
1. Add a url to config/external_rsc_urls pointing to a .zip file containing the .rsc.
* If you keep up to date with /tg/ you could reuse /tg/'s rsc cdn at http://tgstation13.download/byond/tgstation.zip. Otherwise you can use cdn services like CDN77 or cloudflare (requires adding a page rule to enable caching of the zip), or roll your own cdn using route 53 and vps providers.
* Regardless even offloading the rsc to a website without a CDN will be a massive improvement over the in game system for transferring files.
## IRC BOT SETUP
Included in the repository is a python3 compatible IRC bot capable of relaying adminhelps to a specified
IRC channel/server, see the /tools/minibot folder for more
+20
View File
@@ -0,0 +1,20 @@
no_balance_label = "GBP: No Update"
reset_label = "GBP: Reset"
[points]
"Atomic" = 2
"Balance/Rebalance" = -8
"Code Improvement" = 2
"Feature" = -10
"Feedback" = 2
"Fix" = 3
"Grammar and Formatting" = 1
"Logging" = 1
"Performance" = 12
"Priority: CRITICAL" = 20
"Priority: High" = 15
"Quality of Life" = 1
"Refactor" = 10
"Sound" = 3
"Sprites" = 3
"Unit Tests" = 6
+9 -5
View File
@@ -58,8 +58,9 @@ jobs:
bash tools/ci/install_byond.sh
source $HOME/BYOND/byond/bin/byondsetup
python3 tools/ci/template_dm_generator.py
tgui/bin/tgui --build
bash tools/ci/dm.sh -DCIBUILDING -DCITESTING -DALL_MAPS tgstation.dme
tools/build/build
env:
CBT_BUILD_MODE : ALL_MAPS
run_all_tests:
if: "!contains(github.event.head_commit.message, '[ci skip]')"
@@ -97,9 +98,10 @@ jobs:
run: |
bash tools/ci/install_byond.sh
source $HOME/BYOND/byond/bin/byondsetup
tgui/bin/tgui --build
bash tools/ci/dm.sh -DCIBUILDING tgstation.dme
# bash tools/ci/run_server.sh
tools/build/build
# bash tools/ci/run_server.sh
env:
CBT_BUILD_MODE: TEST_RUN
test_windows:
if: "!contains(github.event.head_commit.message, '[ci skip]')"
@@ -109,6 +111,8 @@ jobs:
- uses: actions/checkout@v2
- name: Compile
run: pwsh tools/ci/build.ps1
env:
DM_EXE: "C:\\byond\\bin\\dm.exe"
- name: Create artifact
run: |
md deploy
@@ -323,6 +323,9 @@
icon_state = "1-2"
},
/obj/machinery/atmospherics/pipe/simple/green/visible,
/obj/effect/turf_decal/stripes/line{
dir = 4
},
/turf/open/floor/engine,
/area/engineering/main)
"fa" = (
@@ -1791,6 +1794,9 @@
/obj/structure/cable/yellow{
icon_state = "1-8"
},
/obj/effect/turf_decal/stripes/line{
dir = 8
},
/turf/open/floor/engine,
/area/engineering/main)
"SR" = (
@@ -289,7 +289,7 @@
name = "Supermatter Engine Room";
req_access_txt = "10"
},
/turf/open/floor/engine,
/turf/open/floor/plasteel,
/area/engineering/main)
"kB" = (
/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
@@ -463,14 +463,6 @@
/obj/machinery/atmospherics/pipe/heat_exchanging/junction,
/turf/open/floor/engine/vacuum,
/area/engineering/main)
"oS" = (
/obj/machinery/door/firedoor,
/obj/machinery/door/airlock/engineering/glass{
name = "Supermatter Engine Room";
req_access_txt = "10"
},
/turf/open/floor/engine,
/area/engineering/main)
"pY" = (
/obj/machinery/atmospherics/components/unary/portables_connector{
dir = 8
@@ -884,7 +876,7 @@
name = "Supermatter Engine Room";
req_access_txt = "10"
},
/turf/open/floor/engine,
/turf/open/floor/plasteel,
/area/engineering/main)
"AT" = (
/obj/machinery/atmospherics/pipe/simple/orange/visible{
@@ -2344,7 +2336,7 @@ YZ
yf
"}
(17,1,1) = {"
oS
CH
TN
Wm
ou
@@ -2400,7 +2392,7 @@ YZ
yf
"}
(19,1,1) = {"
oS
CH
kd
tP
ou
+36 -35
View File
@@ -43566,22 +43566,6 @@
},
/turf/open/floor/plasteel,
/area/science/xenobiology)
"cXc" = (
/obj/structure/cable/white{
icon_state = "4-8"
},
/obj/machinery/shower{
dir = 4;
name = "emergency shower"
},
/obj/effect/turf_decal/stripes/end{
dir = 8
},
/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
dir = 4
},
/turf/open/floor/plasteel/white,
/area/science/xenobiology)
"cXd" = (
/obj/structure/cable/white{
icon_state = "4-8"
@@ -43619,22 +43603,6 @@
/obj/effect/landmark/event_spawn,
/turf/open/floor/plasteel/white,
/area/science/xenobiology)
"cXg" = (
/obj/structure/cable/white{
icon_state = "4-8"
},
/obj/machinery/shower{
dir = 8;
name = "emergency shower"
},
/obj/effect/turf_decal/stripes/end{
dir = 4
},
/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
dir = 8
},
/turf/open/floor/plasteel/white,
/area/science/xenobiology)
"cXh" = (
/obj/structure/cable/white{
icon_state = "2-4"
@@ -73923,6 +73891,15 @@
},
/turf/open/floor/plasteel/grimy,
/area/commons/dorms)
"ffl" = (
/obj/effect/turf_decal/bot,
/obj/machinery/shower{
dir = 8;
name = "emergency shower";
pixel_y = -4
},
/turf/open/floor/plasteel,
/area/science/xenobiology)
"ffE" = (
/obj/structure/cable/white{
icon_state = "0-2"
@@ -81764,6 +81741,18 @@
},
/turf/open/floor/plasteel,
/area/ai_monitored/command/storage/eva)
"hMb" = (
/obj/structure/cable/white{
icon_state = "4-8"
},
/obj/effect/turf_decal/stripes/end{
dir = 4
},
/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
dir = 8
},
/turf/open/floor/plasteel/white,
/area/science/xenobiology)
"hMm" = (
/obj/structure/table/wood,
/obj/item/toy/cards/deck,
@@ -101594,6 +101583,18 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/closed/wall,
/area/engineering/break_room)
"oCd" = (
/obj/structure/cable/white{
icon_state = "4-8"
},
/obj/effect/turf_decal/stripes/end{
dir = 8
},
/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
dir = 4
},
/turf/open/floor/plasteel/white,
/area/science/xenobiology)
"oCi" = (
/obj/structure/bookcase/manuals/engineering,
/turf/open/floor/wood,
@@ -157550,7 +157551,7 @@ cQv
cRY
cTJ
tXb
cXc
oCd
cYS
dax
dci
@@ -159092,7 +159093,7 @@ kiv
cSc
cTO
hDD
cXg
hMb
oJE
dav
dcm
@@ -160889,7 +160890,7 @@ cNh
cOQ
cQy
cSh
cTT
ffl
cVR
cXk
cYZ
+131 -125
View File
@@ -4911,22 +4911,6 @@
},
/turf/open/floor/plasteel,
/area/security/brig)
"anj" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/machinery/light{
dir = 4
},
/obj/structure/extinguisher_cabinet{
pixel_x = 27
},
/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
dir = 4
},
/obj/effect/turf_decal/tile/red{
dir = 4
},
/turf/open/floor/plasteel,
/area/security/brig)
"ank" = (
/obj/structure/table,
/obj/item/storage/box/evidence,
@@ -29465,19 +29449,6 @@
},
/turf/open/floor/plasteel/white,
/area/science/research)
"ckg" = (
/obj/structure/sink{
dir = 4;
pixel_x = 11
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 9
},
/obj/effect/turf_decal/stripes/line{
dir = 4
},
/turf/open/floor/plasteel/white,
/area/science/research)
"ckh" = (
/obj/machinery/light{
dir = 4
@@ -30069,21 +30040,6 @@
/obj/effect/turf_decal/stripes/line,
/turf/open/floor/plasteel/white,
/area/science/research)
"clH" = (
/obj/machinery/shower{
dir = 8
},
/obj/item/radio/intercom{
dir = 8;
name = "Station Intercom (General)";
pixel_y = -28
},
/obj/effect/turf_decal/stripes/line{
dir = 6
},
/obj/effect/landmark/event_spawn,
/turf/open/floor/plasteel/white,
/area/science/research)
"clI" = (
/obj/machinery/firealarm{
dir = 8;
@@ -44905,6 +44861,13 @@
/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
dir = 1
},
/obj/machinery/door/window/northleft{
base_state = "right";
dir = 8;
icon_state = "right";
name = "Containment Pen #6";
req_access_txt = "55"
},
/turf/open/floor/plasteel,
/area/science/xenobiology)
"cTg" = (
@@ -50265,6 +50228,21 @@
},
/turf/open/floor/plasteel,
/area/service/hydroponics)
"egm" = (
/obj/machinery/light{
dir = 4
},
/obj/structure/extinguisher_cabinet{
pixel_x = 27
},
/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
dir = 4
},
/obj/effect/turf_decal/tile/red{
dir = 4
},
/turf/open/floor/plasteel,
/area/security/brig)
"egt" = (
/obj/machinery/camera/autoname,
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
@@ -51280,6 +51258,13 @@
},
/turf/open/floor/plasteel/dark,
/area/commons/fitness/recreation)
"eHV" = (
/obj/machinery/light/small{
dir = 4
},
/obj/machinery/air_sensor/atmos/nitrous_tank,
/turf/open/floor/engine/n2o,
/area/engineering/atmos)
"eJd" = (
/obj/structure/closet/secure_closet/RD,
/obj/machinery/keycard_auth{
@@ -51449,6 +51434,15 @@
},
/turf/open/floor/engine,
/area/engineering/main)
"eOP" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 9
},
/obj/effect/turf_decal/stripes/line{
dir = 4
},
/turf/open/floor/plasteel/white,
/area/science/research)
"ePg" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 4
@@ -52531,10 +52525,6 @@
dir = 8
},
/area/service/chapel/main)
"fwH" = (
/obj/machinery/air_sensor/atmos/nitrogen_tank,
/turf/open/floor/engine/n2,
/area/engineering/atmos)
"fxb" = (
/obj/machinery/vending/engivend,
/obj/structure/cable/yellow{
@@ -52880,6 +52870,10 @@
/obj/item/clothing/under/misc/burial,
/turf/open/floor/plasteel/grimy,
/area/service/chapel/office)
"fId" = (
/obj/effect/turf_decal/vg_decals/atmos/nitrogen,
/turf/open/floor/engine/n2,
/area/engineering/atmos)
"fIC" = (
/obj/machinery/atmospherics/components/unary/vent_pump/on{
dir = 8
@@ -53288,10 +53282,6 @@
},
/turf/open/floor/wood,
/area/service/theater)
"fXs" = (
/obj/machinery/light/small,
/turf/open/floor/engine/n2,
/area/engineering/atmos)
"fXZ" = (
/obj/structure/cable{
icon_state = "0-2"
@@ -53609,6 +53599,11 @@
/obj/machinery/atmospherics/components/trinary/filter/flipped/critical,
/turf/open/floor/engine,
/area/engineering/main)
"gim" = (
/obj/machinery/light/small,
/obj/machinery/air_sensor/atmos/oxygen_tank,
/turf/open/floor/engine/o2,
/area/engineering/atmos)
"gin" = (
/obj/structure/cable/yellow{
icon_state = "4-8"
@@ -55483,6 +55478,10 @@
},
/turf/open/floor/plasteel,
/area/cargo/miningoffice)
"htG" = (
/obj/effect/turf_decal/vg_decals/atmos/air,
/turf/open/floor/engine/air,
/area/engineering/atmos)
"htH" = (
/obj/machinery/firealarm{
dir = 8;
@@ -57096,10 +57095,6 @@
},
/turf/open/floor/plasteel,
/area/service/hydroponics)
"iqT" = (
/obj/machinery/air_sensor/atmos/air_tank,
/turf/open/floor/engine/air,
/area/engineering/atmos)
"iqU" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/structure/chair/stool,
@@ -59323,12 +59318,6 @@
/obj/machinery/light,
/turf/open/floor/wood,
/area/service/bar)
"jGW" = (
/obj/machinery/light/small{
dir = 4
},
/turf/open/floor/engine/n2o,
/area/engineering/atmos)
"jIg" = (
/obj/structure/sign/directions/evac,
/obj/structure/sign/directions/medical{
@@ -60383,10 +60372,6 @@
},
/turf/open/floor/plasteel,
/area/service/bar)
"kro" = (
/obj/machinery/air_sensor/atmos/oxygen_tank,
/turf/open/floor/engine/o2,
/area/engineering/atmos)
"krD" = (
/turf/closed/wall,
/area/science/circuit)
@@ -62374,10 +62359,6 @@
dir = 5
},
/area/service/kitchen)
"lwJ" = (
/obj/machinery/light/small,
/turf/open/floor/engine/o2,
/area/engineering/atmos)
"lwL" = (
/turf/open/floor/plasteel,
/area/service/janitor)
@@ -62593,6 +62574,13 @@
dir = 1
},
/area/engineering/atmos)
"lCt" = (
/obj/machinery/light/small{
dir = 4
},
/obj/machinery/air_sensor/atmos/toxin_tank,
/turf/open/floor/engine/plasma,
/area/engineering/atmos)
"lDu" = (
/obj/machinery/door/firedoor,
/obj/structure/cable/yellow{
@@ -62632,6 +62620,10 @@
},
/turf/open/floor/plasteel,
/area/commons/dorms)
"lFn" = (
/obj/effect/turf_decal/vg_decals/atmos/mix,
/turf/open/floor/engine/vacuum,
/area/engineering/atmos)
"lFD" = (
/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
dir = 1
@@ -62883,6 +62875,10 @@
dir = 1
},
/area/command/gateway)
"lNP" = (
/obj/effect/turf_decal/vg_decals/atmos/plasma,
/turf/open/floor/engine/plasma,
/area/engineering/atmos)
"lOi" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -63714,12 +63710,6 @@
},
/turf/open/floor/plasteel,
/area/construction/storage_wing)
"mkz" = (
/obj/machinery/light/small{
dir = 4
},
/turf/open/floor/engine/vacuum,
/area/engineering/atmos)
"mkS" = (
/obj/structure/closet/boxinggloves,
/obj/effect/turf_decal/tile/neutral{
@@ -64408,12 +64398,6 @@
},
/turf/open/floor/plasteel,
/area/cargo/qm)
"mEw" = (
/obj/machinery/light/small{
dir = 4
},
/turf/open/floor/engine/plasma,
/area/engineering/atmos)
"mEE" = (
/obj/structure/disposalpipe/segment{
dir = 4
@@ -64896,10 +64880,6 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/turf/open/floor/plasteel/chapel,
/area/service/chapel/main)
"mWu" = (
/obj/machinery/air_sensor/atmos/toxin_tank,
/turf/open/floor/engine/plasma,
/area/engineering/atmos)
"mWy" = (
/obj/effect/turf_decal/stripes/line{
dir = 4
@@ -65067,10 +65047,6 @@
},
/turf/open/floor/plasteel,
/area/service/hydroponics)
"naS" = (
/obj/machinery/air_sensor/atmos/carbon_tank,
/turf/open/floor/engine/co2,
/area/engineering/atmos)
"nbl" = (
/obj/structure/cable/yellow{
icon_state = "4-8"
@@ -65605,6 +65581,18 @@
},
/turf/open/floor/plasteel,
/area/hallway/secondary/service)
"nyA" = (
/obj/item/radio/intercom{
dir = 8;
name = "Station Intercom (General)";
pixel_y = -28
},
/obj/effect/turf_decal/stripes/line{
dir = 6
},
/obj/effect/landmark/event_spawn,
/turf/open/floor/plasteel/white,
/area/science/research)
"nzp" = (
/obj/structure/urinal{
pixel_y = 29
@@ -65730,10 +65718,6 @@
/obj/machinery/bookbinder,
/turf/open/floor/wood,
/area/service/library)
"nFp" = (
/obj/machinery/air_sensor/atmos/nitrous_tank,
/turf/open/floor/engine/n2o,
/area/engineering/atmos)
"nFz" = (
/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden,
/turf/closed/wall/r_wall,
@@ -68497,10 +68481,6 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/turf/open/floor/plasteel,
/area/commons/fitness/recreation)
"pii" = (
/obj/machinery/light/small,
/turf/open/floor/engine/air,
/area/engineering/atmos)
"piB" = (
/obj/machinery/vending/hydroseeds{
slogan_delay = 700
@@ -69708,6 +69688,13 @@
},
/turf/open/floor/plasteel,
/area/cargo/miningoffice)
"pSz" = (
/obj/machinery/light/small{
dir = 4
},
/obj/machinery/air_sensor/atmos/mix_tank,
/turf/open/floor/engine/vacuum,
/area/engineering/atmos)
"pSE" = (
/obj/effect/landmark/start/captain,
/obj/machinery/airalarm{
@@ -72067,6 +72054,11 @@
/obj/item/storage/dice,
/turf/open/floor/plasteel,
/area/commons/locker)
"rjT" = (
/obj/machinery/light/small,
/obj/machinery/air_sensor/atmos/air_tank,
/turf/open/floor/engine/air,
/area/engineering/atmos)
"rkx" = (
/turf/closed/wall/r_wall,
/area/engineering/supermatter)
@@ -73807,6 +73799,13 @@
},
/turf/open/floor/plasteel/cafeteria,
/area/command/heads_quarters/cmo)
"smL" = (
/obj/machinery/light/small{
dir = 4
},
/obj/machinery/air_sensor/atmos/carbon_tank,
/turf/open/floor/engine/co2,
/area/engineering/atmos)
"smU" = (
/turf/closed/wall,
/area/engineering/storage/tech)
@@ -74471,6 +74470,10 @@
/obj/machinery/field/generator,
/turf/open/floor/plating,
/area/engineering/main)
"sLx" = (
/obj/effect/turf_decal/vg_decals/atmos/nitrous_oxide,
/turf/open/floor/engine/n2o,
/area/engineering/atmos)
"sLW" = (
/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
dir = 4
@@ -74817,10 +74820,6 @@
},
/turf/open/floor/plasteel,
/area/engineering/main)
"sZg" = (
/obj/machinery/air_sensor/atmos/mix_tank,
/turf/open/floor/engine/vacuum,
/area/engineering/atmos)
"sZz" = (
/obj/structure/table,
/obj/item/paper/fluff/holodeck/disclaimer,
@@ -76314,12 +76313,6 @@
/obj/machinery/portable_atmospherics/canister/air,
/turf/open/floor/plating,
/area/maintenance/port)
"tUr" = (
/obj/machinery/light/small{
dir = 4
},
/turf/open/floor/engine/co2,
/area/engineering/atmos)
"tUN" = (
/obj/machinery/conveyor_switch/oneway{
id = "packageSort2";
@@ -79928,6 +79921,11 @@
},
/turf/open/floor/plasteel,
/area/cargo/sorting)
"wlz" = (
/obj/machinery/light/small,
/obj/machinery/air_sensor/atmos/nitrogen_tank,
/turf/open/floor/engine/n2,
/area/engineering/atmos)
"wlC" = (
/obj/machinery/atmospherics/pipe/simple/orange/visible{
dir = 5
@@ -80973,6 +80971,10 @@
/obj/structure/lattice,
/turf/open/space/basic,
/area/space/nearstation)
"wNX" = (
/obj/effect/turf_decal/vg_decals/atmos/oxygen,
/turf/open/floor/engine/o2,
/area/engineering/atmos)
"wOW" = (
/obj/structure/window/reinforced{
dir = 4
@@ -81790,6 +81792,10 @@
},
/turf/open/floor/plasteel,
/area/service/hydroponics)
"xlP" = (
/obj/effect/turf_decal/vg_decals/atmos/carbon_dioxide,
/turf/open/floor/engine/co2,
/area/engineering/atmos)
"xlU" = (
/obj/structure/cable/yellow{
icon_state = "2-8"
@@ -108976,7 +108982,7 @@ aiw
ajp
akz
ajQ
anj
egm
aot
apG
apG
@@ -114185,8 +114191,8 @@ bZo
bZo
chp
iTm
ckg
clH
eOP
nyA
bZn
coa
cpl
@@ -125234,9 +125240,9 @@ rVI
qHn
aaf
dYX
fwH
fId
sTB
fXs
wlz
dPI
aaf
aaa
@@ -126262,9 +126268,9 @@ rSI
uKL
aaf
dYX
kro
wNX
eFn
lwJ
gim
dPI
aaf
aaa
@@ -127290,9 +127296,9 @@ rSI
uKL
aaf
dYX
iqT
htG
wFX
pii
rjT
dPI
aaf
aaa
@@ -129068,19 +129074,19 @@ aaa
aaf
dPI
xXd
sZg
lFn
pAr
dPI
qGy
nFp
sLx
kNs
dPI
iiP
mWu
lNP
lBv
dPI
ipG
naS
xlP
ktS
dPI
aaf
@@ -129582,19 +129588,19 @@ aaf
aaf
dPI
jQT
mkz
pSz
gdM
dPI
izZ
jGW
eHV
mMU
dPI
oMq
mEw
lCt
erE
dPI
hGX
tUr
smL
rxl
dPI
aaf
+36 -23
View File
@@ -46715,15 +46715,6 @@
},
/turf/open/floor/plating,
/area/engineering/main)
"cda" = (
/obj/structure/reflector/single/anchored{
dir = 10
},
/obj/machinery/light/small{
dir = 1
},
/turf/open/floor/plating,
/area/engineering/main)
"cdc" = (
/obj/effect/turf_decal/stripes/line{
dir = 8
@@ -57562,6 +57553,15 @@
},
/turf/open/floor/plasteel/white,
/area/science/xenobiology)
"mQB" = (
/obj/structure/cable{
icon_state = "0-8"
},
/obj/machinery/light/small{
dir = 1
},
/turf/open/floor/plating,
/area/engineering/main)
"mSc" = (
/obj/machinery/space_heater,
/turf/open/floor/plating,
@@ -58846,12 +58846,6 @@
/obj/structure/lattice/catwalk,
/turf/open/space/basic,
/area/space/nearstation)
"pCo" = (
/obj/machinery/light/small{
dir = 1
},
/turf/open/floor/plating,
/area/engineering/main)
"pDP" = (
/obj/machinery/vending/assist,
/obj/effect/turf_decal/tile/neutral{
@@ -58929,6 +58923,19 @@
/obj/structure/closet/crate,
/turf/open/floor/plating,
/area/maintenance/department/cargo)
"pKU" = (
/obj/machinery/power/emitter/anchored{
dir = 8;
state = 2
},
/obj/structure/cable{
icon_state = "0-4"
},
/obj/machinery/light/small{
dir = 1
},
/turf/open/floor/plating,
/area/engineering/main)
"pMG" = (
/obj/structure/sink{
dir = 8;
@@ -61603,6 +61610,12 @@
"wcs" = (
/turf/open/floor/plasteel/dark,
/area/engineering/main)
"wcE" = (
/obj/structure/reflector/single/anchored{
dir = 10
},
/turf/open/floor/plating,
/area/engineering/main)
"wdx" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 6
@@ -93554,8 +93567,8 @@ bYT
bZB
caq
cbk
aac
ccY
bXk
mQB
cdT
ccY
cbX
@@ -94068,8 +94081,8 @@ bYV
bZA
cam
ccW
bXk
cda
aac
wcE
wcs
wcs
wcs
@@ -94582,8 +94595,8 @@ bYX
bZA
cam
lfx
bXk
pCo
aac
cbX
wcs
wcs
wcs
@@ -95096,8 +95109,8 @@ wjm
bZF
cbm
mgz
aac
oHa
bXk
pKU
oHa
eWi
cbX
+137 -49
View File
@@ -5352,7 +5352,6 @@
dir = 4
},
/obj/machinery/holopad,
/mob/living/carbon/human/species/corporate,
/turf/open/floor/wood,
/area/command/corporate_showroom)
"ajQ" = (
@@ -14107,6 +14106,15 @@
/obj/machinery/disposal/bin,
/turf/open/floor/carpet,
/area/command/heads_quarters/hos)
"bXH" = (
/obj/structure/disposalpipe/segment{
dir = 6
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
dir = 6
},
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/surface/outdoors)
"bXO" = (
/obj/machinery/door/morgue{
name = "Confession Booth (Chaplain)";
@@ -15923,6 +15931,10 @@
},
/turf/open/floor/plasteel/white,
/area/medical/medbay/central)
"cVv" = (
/obj/structure/fence,
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/surface/outdoors)
"cVG" = (
/obj/structure/cable{
icon_state = "1-2"
@@ -18135,6 +18147,15 @@
/obj/structure/disposalpipe/segment,
/turf/open/floor/plasteel/dark,
/area/tcommsat/computer)
"evX" = (
/obj/structure/disposalpipe/segment{
dir = 4
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
dir = 4
},
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/surface/outdoors)
"evY" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on,
/turf/open/floor/plasteel,
@@ -22209,6 +22230,18 @@
/obj/effect/spawner/structure/window/reinforced,
/turf/open/floor/plating,
/area/service/bar)
"hbg" = (
/obj/structure/cable{
icon_state = "1-8"
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{
dir = 9
},
/obj/structure/disposalpipe/segment{
dir = 9
},
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/surface/outdoors)
"hch" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/structure/table/wood,
@@ -25768,6 +25801,16 @@
/obj/structure/disposalpipe/segment,
/turf/open/floor/carpet,
/area/hallway/secondary/exit/departure_lounge)
"joQ" = (
/obj/structure/disposalpipe/segment{
dir = 4
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
dir = 4
},
/obj/machinery/light/floor,
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/surface/outdoors)
"joT" = (
/obj/structure/cable{
icon_state = "0-2"
@@ -26724,6 +26767,7 @@
/obj/structure/disposalpipe/segment{
dir = 4
},
/obj/machinery/gear_painter,
/turf/open/floor/wood,
/area/commons/dorms)
"jYS" = (
@@ -32604,6 +32648,11 @@
},
/turf/open/floor/engine,
/area/engineering/main)
"nSk" = (
/obj/machinery/light,
/obj/machinery/gear_painter,
/turf/open/floor/plasteel,
/area/hallway/secondary/exit/departure_lounge)
"nSX" = (
/obj/machinery/atmospherics/components/binary/pump/on{
dir = 1;
@@ -35905,6 +35954,15 @@
/obj/machinery/light/floor,
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/surface/outdoors)
"pQf" = (
/obj/structure/disposalpipe/segment{
dir = 9
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
dir = 9
},
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/surface/outdoors)
"pQg" = (
/obj/machinery/atmospherics/pipe/simple/green/visible{
dir = 6
@@ -38839,6 +38897,21 @@
},
/turf/open/floor/plating/asteroid/snow/icemoon,
/area/ai_monitored/security/armory)
"rOA" = (
/obj/structure/cable{
icon_state = "1-2"
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/visible,
/obj/structure/disposalpipe/segment,
/obj/structure/disposalpipe/segment{
dir = 4
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
dir = 4
},
/obj/machinery/light/floor,
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/surface/outdoors)
"rOD" = (
/obj/machinery/door/airlock/engineering/glass{
name = "Server Room";
@@ -48303,6 +48376,21 @@
},
/turf/open/floor/plasteel,
/area/hallway/primary/central)
"xYc" = (
/obj/structure/cable{
icon_state = "4-8"
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{
dir = 4
},
/obj/structure/disposalpipe/segment{
dir = 4
},
/obj/structure/disposalpipe/segment,
/obj/machinery/atmospherics/pipe/simple/supply/visible,
/obj/machinery/light/floor,
/turf/open/floor/plating/snowed/smoothed/icemoon,
/area/icemoon/surface/outdoors)
"xYD" = (
/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden,
/turf/open/floor/carpet,
@@ -74479,9 +74567,9 @@ ydp
avT
avT
avT
xUL
vcG
lzB
bXH
xYc
pQf
avT
avT
avT
@@ -74736,9 +74824,9 @@ ydp
avT
avT
avT
evX
vcG
xUL
lcv
wgu
xUL
xUL
xUL
@@ -74993,9 +75081,9 @@ avT
avT
avT
avT
xUL
evX
vcG
lzB
xUL
xUL
xUL
xUL
@@ -75250,9 +75338,9 @@ ydp
avT
avT
avT
xUL
evX
vcG
lzB
xUL
avT
avT
avT
@@ -75507,9 +75595,9 @@ avT
avT
avT
avT
xUL
evX
vcG
lzB
xUL
avT
avT
avT
@@ -75764,9 +75852,9 @@ avT
avT
avT
avT
joQ
lcv
xUL
vcG
lzB
avT
avT
avT
@@ -76021,9 +76109,9 @@ avT
avT
avT
avT
xUL
evX
vcG
lzB
xUL
avT
avT
avT
@@ -76278,9 +76366,9 @@ avT
avT
avT
avT
xUL
evX
vcG
lzB
xUL
avT
avT
avT
@@ -76535,10 +76623,10 @@ ydp
ydp
avT
avT
xUL
lcv
wgu
evX
vcG
avT
krq
avT
ydp
ydp
@@ -76792,11 +76880,11 @@ ydp
ydp
ydp
avT
xUL
evX
vcG
lzB
nTz
xiw
avT
cVv
ydp
ydp
ydp
@@ -77049,9 +77137,8 @@ ydp
ydp
avT
avT
xUL
evX
vcG
lzB
avT
xUL
xUL
@@ -77064,6 +77151,7 @@ xUL
xUL
xUL
xUL
xUL
avT
wql
wql
@@ -77306,9 +77394,9 @@ avT
avT
avT
avT
xUL
evX
vcG
lzB
avT
xUL
xUL
xUL
@@ -77563,9 +77651,9 @@ avT
avT
avT
avT
xUL
evX
vcG
lzB
avT
xUL
xUL
xUL
@@ -77820,9 +77908,9 @@ avT
avT
avT
avT
xUL
joQ
lcv
nDN
avT
xUL
xUL
xUL
@@ -78077,9 +78165,9 @@ avT
avT
avT
avT
xUL
evX
vcG
lzB
avT
xUL
xUL
xUL
@@ -78334,9 +78422,9 @@ avT
avT
avT
avT
xUL
evX
vcG
lzB
avT
xUL
xUL
xUL
@@ -78591,9 +78679,8 @@ xUL
xUL
xUL
xUL
xUL
evX
vcG
lzB
avT
xUL
xUL
@@ -78606,6 +78693,7 @@ xUL
xUL
xUL
xUL
xUL
avT
avT
bwt
@@ -78848,14 +78936,14 @@ hnN
nGx
nGx
nGx
nGx
xtV
lzB
rOA
hbg
nTz
xiw
avT
avT
avT
avT
xUL
xUL
xUL
xUL
bok
unA
xjZ
@@ -79105,11 +79193,11 @@ ybT
sLv
sLv
sLv
sLv
sLv
asr
pQf
avT
avT
hho
avT
avT
krq
avT
@@ -83490,7 +83578,7 @@ wbE
wbE
wbE
uBa
faZ
nSk
bBL
bBL
bBL
+5 -1
View File
@@ -17,7 +17,11 @@
} while(0);
#define CHECK_MULTIPLE_BITFIELDS(flagvar, flags) (((flagvar) & (flags)) == (flags))
GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768))
GLOBAL_LIST_INIT(bitflags, list(
1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23
))
// for /datum/var/datum_flags
#define DF_USE_TAG (1<<0)
+2 -2
View File
@@ -148,10 +148,10 @@
#define CANVERTICALATMOSPASS(A, O) ( A.CanAtmosPassVertical == ATMOS_PASS_PROC ? A.CanAtmosPass(O, TRUE) : ( A.CanAtmosPassVertical == ATMOS_PASS_DENSITY ? !A.density : A.CanAtmosPassVertical ) )
//OPEN TURF ATMOS
#define OPENTURF_DEFAULT_ATMOS "o2=22;n2=82;TEMP=293.15" //the default air mix that open turfs spawn
#define OPENTURF_DEFAULT_ATMOS "o2=21.78;n2=82.36;TEMP=293.15" //the default air mix that open turfs spawn, also is what the station vents output at assuming a 21/79% o2/n2 mix
#define TCOMMS_ATMOS "n2=100;TEMP=80" //-193,15degC telecommunications. also used for xenobiology slime killrooms
#define AIRLESS_ATMOS "TEMP=2.7" //space
#define FROZEN_ATMOS "o2=22;n2=82;TEMP=180" //-93.15degC snow and ice turfs
#define FROZEN_ATMOS "o2=21.78;n2=82.36;TEMP=180" //-93.15degC snow and ice turfs
#define BURNMIX_ATMOS "o2=2500;plasma=5000;TEMP=370" //used in the holodeck burn test program
//ATMOSPHERICS DEPARTMENT GAS TANK TURFS
+1
View File
@@ -518,6 +518,7 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
#define VOMIT_TOXIC 1
#define VOMIT_PURPLE 2
#define VOMIT_NANITE 3
// possible bitflag return values of intercept_zImpact(atom/movable/AM, levels = 1) calls
#define FALL_INTERCEPTED (1<<0) //Stops the movable from falling further and crashing on the ground
+5 -1
View File
@@ -12,6 +12,11 @@
#define NANITE_CLOUD_DISABLE 2
#define NANITE_CLOUD_ENABLE 3
//Nanite excess thresholds
#define NANITE_EXCESS_MINOR 25
#define NANITE_EXCESS_VOMIT 100
#define NANITE_EXCESS_BURST 350
///Nanite Protocol types
#define NANITE_PROTOCOL_REPLICATION "nanite_replication"
#define NANITE_PROTOCOL_STORAGE "nanite_storage"
@@ -45,4 +50,3 @@
#define NES_SCAN_TYPE "Scan Type"
#define NES_BUTTON_NAME "Button Name"
#define NES_ICON "Icon"
#define NES_COLOR "Color"
+3 -1
View File
@@ -37,6 +37,7 @@
#define ROLE_INTERNAL_AFFAIRS "internal affairs agent"
#define ROLE_GANG "gangster"
#define ROLE_BLOODSUCKER "bloodsucker"
#define ROLE_SPACE_DRAGON "Space Dragon"
//#define ROLE_MONSTERHUNTER "monster hunter" Disabled for now
#define ROLE_GHOSTCAFE "ghostcafe"
#define ROLE_MINOR_ANTAG "minorantag"
@@ -70,7 +71,8 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_SENTIENCE,
ROLE_GANG = /datum/game_mode/gang,
ROLE_HERETIC = /datum/game_mode/heretics,
ROLE_BLOODSUCKER = /datum/game_mode/bloodsucker
ROLE_BLOODSUCKER = /datum/game_mode/bloodsucker,
ROLE_SPACE_DRAGON
//ROLE_MONSTERHUNTER Disabled for now
))
+21 -17
View File
@@ -1,6 +1,6 @@
// tgstation-server DMAPI
#define TGS_DMAPI_VERSION "5.2.9"
#define TGS_DMAPI_VERSION "6.0.3"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
@@ -67,7 +67,7 @@
#define TGS_EVENT_REPO_CHECKOUT 1
/// When the repository performs a fetch operation. No parameters
#define TGS_EVENT_REPO_FETCH 2
/// When the repository merges a pull request. Parameters: PR Number, PR Sha, (Nullable) Comment made by TGS user
/// When the repository test merges. Parameters: PR Number, PR Sha, (Nullable) Comment made by TGS user
#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
/// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path
#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
@@ -95,8 +95,13 @@
#define TGS_EVENT_WATCHDOG_SHUTDOWN 15
/// Before the watchdog detaches for a TGS update/restart. No parameters.
#define TGS_EVENT_WATCHDOG_DETACH 16
// We don't actually implement this value as the DMAPI can never receive it
// We don't actually implement these 4 events as the DMAPI can never receive them.
// #define TGS_EVENT_WATCHDOG_LAUNCH 17
// #define TGS_EVENT_WATCHDOG_CRASH 18
// #define TGS_EVENT_WORLD_END_PROCESS 19
// #define TGS_EVENT_WORLD_REBOOT 20
/// Watchdog event when TgsInitializationComplete() is called. No parameters.
#define TGS_EVENT_WORLD_PRIME 21
// OTHER ENUMS
@@ -130,7 +135,6 @@
*
* This may use [/world/var/sleep_offline] to make this happen so ensure no changes are made to it while this call is running.
* Afterwards, consider explicitly setting it to what you want to avoid this BYOND bug: http://www.byond.com/forum/post/2575184
* Before this point, note that any static files or directories may be in use by another server. Your code should account for this.
* This function should not be called before ..() in [/world/proc/New].
*/
/world/proc/TgsInitializationComplete()
@@ -140,7 +144,7 @@
#define TGS_TOPIC var/tgs_topic_return = TgsTopic(args[1]); if(tgs_topic_return) return tgs_topic_return
/**
* Call this at the beginning of [world/proc/Reboot].
* Call this as late as possible in [world/proc/Reboot].
*/
/world/proc/TgsReboot()
return
@@ -152,6 +156,8 @@
/datum/tgs_revision_information
/// Full SHA of the commit.
var/commit
/// ISO 8601 timestamp of when the commit was created
var/timestamp
/// Full sha of last known remote commit. This may be null if the TGS repository is not currently tracking a remote branch.
var/origin_commit
@@ -190,21 +196,19 @@
/// Represents a merge of a GitHub pull request.
/datum/tgs_revision_information/test_merge
/// The pull request number.
/// The test merge number.
var/number
/// The pull request title when it was merged.
/// The test merge source's title when it was merged.
var/title
/// The pull request body when it was merged.
/// The test merge source's body when it was merged.
var/body
/// The GitHub username of the pull request's author.
/// The Username of the test merge source's author.
var/author
/// An http URL to the pull request.
/// An http URL to the test merge source.
var/url
/// The SHA of the pull request when that was merged.
var/pull_request_commit
/// ISO 8601 timestamp of when the pull request was merged.
var/time_merged
/// (Nullable) Comment left by the TGS user who initiated the merge..
/// The SHA of the test merge when that was merged.
var/head_commit
/// Optional comment left by the TGS user who initiated the merge.
var/comment
/// Represents a connected chat channel.
@@ -263,11 +267,11 @@
// API FUNCTIONS
/// Returns the maximum supported [/datum/tgs_version] of the DMAPI.
/world/proc/TgsMaximumAPIVersion()
/world/proc/TgsMaximumApiVersion()
return
/// Returns the minimum supported [/datum/tgs_version] of the DMAPI.
/world/proc/TgsMinimumAPIVersion()
/world/proc/TgsMinimumApiVersion()
return
/**
+1
View File
@@ -341,3 +341,4 @@
#define ACTIVE_PARRY_TRAIT "active_parry"
#define STICKY_NODROP "sticky-nodrop" //sticky nodrop sounds like a bad soundcloud rapper's name
#define TRAIT_SACRIFICED "sacrificed" //Makes sure that people cant be cult sacrificed twice.
#define TRAIT_SPACEWALK "spacewalk"
+2 -1
View File
@@ -123,7 +123,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_HIGH_BLOOD" = TRAIT_HIGH_BLOOD,
"TRAIT_EMPATH" = TRAIT_EMPATH,
"TRAIT_FRIENDLY" = TRAIT_FRIENDLY,
"TRAIT_IWASBATONED" = TRAIT_IWASBATONED
"TRAIT_IWASBATONED" = TRAIT_IWASBATONED,
"TRAIT_SPACEWALK" = TRAIT_SPACEWALK
),
/obj/item/bodypart = list(
"TRAIT_PARALYSIS" = TRAIT_PARALYSIS
+12 -9
View File
@@ -1,10 +1,13 @@
/*!
* Copyright (c) 2020 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/
/**
* tgui subsystem
*
* Contains all tgui state and subsystem code.
*
* Copyright (c) 2020 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/
SUBSYSTEM_DEF(tgui)
@@ -42,8 +45,8 @@ SUBSYSTEM_DEF(tgui)
var/datum/tgui/ui = current_run[current_run.len]
current_run.len--
// TODO: Move user/src_object check to process()
if(ui && ui.user && ui.src_object)
ui.process()
if(ui?.user && ui.src_object)
ui.process(wait * 0.1)
else
open_uis.Remove(ui)
if(MC_TICK_CHECK)
@@ -191,8 +194,8 @@ SUBSYSTEM_DEF(tgui)
return count
for(var/datum/tgui/ui in open_uis_by_src[key])
// Check if UI is valid.
if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.process(force = 1)
if(ui?.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.process(wait * 0.1, force = 1)
count++
return count
@@ -213,7 +216,7 @@ SUBSYSTEM_DEF(tgui)
return count
for(var/datum/tgui/ui in open_uis_by_src[key])
// Check if UI is valid.
if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
if(ui?.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.close()
count++
return count
@@ -230,7 +233,7 @@ SUBSYSTEM_DEF(tgui)
for(var/key in open_uis_by_src)
for(var/datum/tgui/ui in open_uis_by_src[key])
// Check if UI is valid.
if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user))
if(ui?.src_object && ui.user && ui.src_object.ui_host(ui.user))
ui.close()
count++
return count
@@ -251,7 +254,7 @@ SUBSYSTEM_DEF(tgui)
return count
for(var/datum/tgui/ui in user.tgui_open_uis)
if(isnull(src_object) || ui.src_object == src_object)
ui.process(force = 1)
ui.process(wait * 0.1, force = 1)
count++
return count
+9
View File
@@ -793,6 +793,15 @@
small_icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
small_icon_state = "ash_whelp"
/datum/action/small_sprite/megafauna/colossus
small_icon_state = "Basilisk"
/datum/action/small_sprite/megafauna/bubblegum
small_icon_state = "goliath2"
/datum/action/small_sprite/megafauna/legion
small_icon_state = "mega_legion"
/datum/action/small_sprite/Trigger()
..()
if(!small)
+64 -3
View File
@@ -170,6 +170,7 @@
/**
* Used to rid ourselves
*/
///Deletes nanites!
/datum/component/nanites/proc/delete_nanites()
if(can_be_deleted)
qdel(src)
@@ -213,7 +214,7 @@
/datum/component/nanites/proc/check_viral_prevention()
return SEND_SIGNAL(src, COMSIG_NANITE_INTERNAL_VIRAL_PREVENTION_CHECK)
//Syncs the nanite component to another, making it so programs are the same with the same programming (except activation status)
///Syncs the nanite component to another, making it so programs are the same with the same programming (except activation status)
/datum/component/nanites/proc/sync(datum/signal_source, datum/component/nanites/source, full_overwrite = TRUE, copy_activation = FALSE)
var/list/programs_to_remove = programs.Copy() - permanent_programs
var/list/programs_to_add = source.programs.Copy()
@@ -233,6 +234,7 @@
var/datum/nanite_program/SNP = X
add_program(null, SNP.copy())
///Syncs the nanites to their assigned cloud copy, if it is available. If it is not, there is a small chance of a software error instead.
/datum/component/nanites/proc/cloud_sync()
if(cloud_id)
var/datum/nanite_cloud_backup/backup = SSnanites.get_cloud_backup(cloud_id)
@@ -246,6 +248,7 @@
var/datum/nanite_program/NP = pick(programs)
NP.software_error()
///Adds a nanite program, replacing existing unique programs of the same type. A source program can be specified to copy its programming onto the new one.
/datum/component/nanites/proc/add_program(datum/source, datum/nanite_program/new_program, datum/nanite_program/source_program)
for(var/X in programs)
var/datum/nanite_program/NP = X
@@ -268,11 +271,67 @@
adjust_nanites(null, -amount)
return (nanite_volume > 0)
///Modifies the current nanite volume, then checks if the nanites are depleted or exceeding the maximum amount
/datum/component/nanites/proc/adjust_nanites(datum/source, amount)
nanite_volume = clamp(nanite_volume + amount, 0, max_nanites)
SIGNAL_HANDLER
nanite_volume = max(nanite_volume + amount, 0) //Lets not have negative nanite counts on permanent ones.
if(nanite_volume > max_nanites)
reject_excess_nanites()
if(nanite_volume <= 0) //oops we ran out
nanites_depleted()
/**
* Handles how nanites leave the host's body if they find out that they're currently exceeding the maximum supported amount
*
* IC explanation:
* Normally nanites simply discard excess volume by slowing replication or 'sweating' it out in imperceptible amounts,
* but if there is a large excess volume, likely due to a programming change that leaves them unable to support their current volume,
* the nanites attempt to leave the host as fast as necessary to prevent nanite poisoning. This can range from minor oozing to nanites
* rapidly bursting out from every possible pathway, causing temporary inconvenience to the host.
*/
/datum/component/nanites/proc/reject_excess_nanites()
var/excess = nanite_volume - max_nanites
nanite_volume = max_nanites
switch(excess)
if(0 to NANITE_EXCESS_MINOR) //Minor excess amount, the extra nanites are quietly expelled without visible effects
return
if((NANITE_EXCESS_MINOR + 0.1) to NANITE_EXCESS_VOMIT) //Enough nanites getting rejected at once to be visible to the naked eye
host_mob.visible_message("<span class='warning'>A grainy grey slurry starts oozing out of [host_mob].</span>", "<span class='warning'>A grainy grey slurry starts oozing out of your skin.</span>", null, 4);
if((NANITE_EXCESS_VOMIT + 0.1) to NANITE_EXCESS_BURST) //Nanites getting rejected in massive amounts, but still enough to make a semi-orderly exit through vomit
if(iscarbon(host_mob))
var/mob/living/carbon/C = host_mob
host_mob.visible_message("<span class='warning'>[host_mob] vomits a grainy grey slurry!</span>", "<span class='warning'>You suddenly vomit a metallic-tasting grainy grey slurry!</span>", null);
C.vomit(0, FALSE, TRUE, FLOOR(excess / 100, 1), FALSE, VOMIT_NANITE, FALSE, TRUE, 0)
else
host_mob.visible_message("<span class='warning'>A metallic grey slurry bursts out of [host_mob]'s skin!</span>", "<span class='userdanger'>A metallic grey slurry violently bursts out of your skin!</span>", null);
if(isturf(host_mob.drop_location()))
var/turf/T = host_mob.drop_location()
T.add_vomit_floor(host_mob, VOMIT_NANITE, 0)
if((NANITE_EXCESS_BURST + 0.1) to INFINITY) //Way too many nanites, they just leave through the closest exit before they harm/poison the host
host_mob.visible_message("<span class='warning'>A torrent of metallic grey slurry violently bursts out of [host_mob]'s face and floods out of [host_mob.p_their()] skin!</span>",
"<span class='userdanger'>A torrent of metallic grey slurry violently bursts out of your eyes, ears, and mouth, and floods out of your skin!</span>");
host_mob.blind_eyes(15) //nanites coming out of your eyes
host_mob.Paralyze(120)
if(iscarbon(host_mob))
var/mob/living/carbon/C = host_mob
var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS)
if(ears)
ears.adjustEarDamage(0, 30) //nanites coming out of your ears
C.vomit(0, FALSE, TRUE, 2, FALSE, VOMIT_NANITE, FALSE, TRUE, 0) //nanites coming out of your mouth
//nanites everywhere
if(isturf(host_mob.drop_location()))
var/turf/T = host_mob.drop_location()
T.add_vomit_floor(host_mob, VOMIT_NANITE, 0)
for(var/turf/adjacent_turf in oview(host_mob, 1))
if(adjacent_turf.density || !adjacent_turf.Adjacent(T))
continue
adjacent_turf.add_vomit_floor(host_mob, VOMIT_NANITE, 0)
///Updates the nanite volume bar visible in diagnostic HUDs
/datum/component/nanites/proc/set_nanite_bar(remove = FALSE)
var/image/holder = host_mob.hud_list[DIAG_NANITE_FULL_HUD]
var/icon/I = icon(host_mob.icon, host_mob.icon_state, host_mob.dir)
@@ -346,7 +405,9 @@
nanite_volume = clamp(amount, 0, max_nanites)
/datum/component/nanites/proc/set_max_volume(datum/source, amount)
max_nanites = max(1, max_nanites)
SIGNAL_HANDLER
max_nanites = max(1, amount)
/datum/component/nanites/proc/set_cloud(datum/source, amount)
cloud_id = clamp(amount, 0, 100)
+4 -4
View File
@@ -16,7 +16,7 @@
if(revinfo)
commit = revinfo.commit
originmastercommit = revinfo.origin_commit
date = rustg_git_commit_date(commit)
date = revinfo.timestamp || rustg_git_commit_date(commit)
// goes to DD log and config_error.txt
log_world(get_log_message())
@@ -29,8 +29,8 @@
for(var/line in testmerge)
var/datum/tgs_revision_information/test_merge/tm = line
msg += "Test merge active of PR #[tm.number] commit [tm.pull_request_commit]"
SSblackbox.record_feedback("associative", "testmerged_prs", 1, list("number" = "[tm.number]", "commit" = "[tm.pull_request_commit]", "title" = "[tm.title]", "author" = "[tm.author]"))
msg += "Test merge active of PR #[tm.number] commit [tm.head_commit]"
SSblackbox.record_feedback("associative", "testmerged_prs", 1, list("number" = "[tm.number]", "commit" = "[tm.head_commit]", "title" = "[tm.title]", "author" = "[tm.author]"))
if(commit && commit != originmastercommit)
msg += "HEAD: [commit]"
@@ -45,7 +45,7 @@
. = header ? "The following pull requests are currently test merged:<br>" : ""
for(var/line in testmerge)
var/datum/tgs_revision_information/test_merge/tm = line
var/cm = tm.pull_request_commit
var/cm = tm.head_commit
var/details = ": '" + html_encode(tm.title) + "' by " + html_encode(tm.author) + " at commit " + html_encode(copytext_char(cm, 1, 11))
if(details && findtext(details, "\[s\]") && (!usr || !usr.client.holder))
continue
+1 -1
View File
@@ -642,7 +642,7 @@
O.Remove()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
C.vomit(0, toxic = TRUE)
C.vomit(0)
O.forceMove(get_turf(owner))
if(isliving(owner))
var/mob/living/L = owner
+7 -6
View File
@@ -106,12 +106,7 @@
A.aiControlDisabled = 1
else if(A.aiControlDisabled == -1)
A.aiControlDisabled = 2
sleep(10)
if(A)
if(A.aiControlDisabled == 1)
A.aiControlDisabled = 0
else if(A.aiControlDisabled == 2)
A.aiControlDisabled = -1
addtimer(CALLBACK(A, /obj/machinery/door/airlock.proc/reset_ai_wire), 1 SECONDS)
if(WIRE_SHOCK) // Pulse to shock the door for 10 ticks.
if(!A.secondsElectrified)
A.set_electrified(30, usr)
@@ -125,6 +120,12 @@
A.lights = !A.lights
A.update_icon()
/obj/machinery/door/airlock/proc/reset_ai_wire()
if(aiControlDisabled == 1)
aiControlDisabled = 0
else if(aiControlDisabled == 2)
aiControlDisabled = -1
/datum/wires/airlock/on_cut(wire, mend)
var/obj/machinery/door/airlock/A = holder
if(usr && !A.hasSiliconAccessInArea(usr) && A.isElectrified() && A.shock(usr, 100))
+8
View File
@@ -73,6 +73,8 @@
myseed.adjust_yield(rand(-3,2))
myseed.adjust_production(rand(-3,3))
myseed.endurance = clamp(myseed.endurance + rand(-3,2), 0, 100) // adjust_endurance has a min value of 10, need to edit directly
// Scale health to endurance
max_integrity = obj_integrity = 10 + myseed.endurance / 2
delay_spread = delay_spread - myseed.production * 100 //So the delay goes DOWN with better stats instead of up. :I
var/datum/plant_gene/trait/glow/G = myseed.get_gene(/datum/plant_gene/trait/glow)
if(ispath(G)) // Seeds were ported to initialize so their genes are still typepaths here, luckily their initializer is smart enough to handle us doing this
@@ -193,8 +195,14 @@
/obj/structure/glowshroom/proc/Decay(spread, amount)
if (spread) // Decay due to spread
myseed.endurance -= amount
max_integrity = min(max_integrity, 10 + myseed.endurance / 2)
if(obj_integrity > max_integrity)
obj_integrity = max_integrity
else // Timed decay
myseed.endurance -= 1
max_integrity = min(max_integrity, 10 + myseed.endurance / 2)
if(obj_integrity > max_integrity)
obj_integrity = max_integrity
if (myseed.endurance > 0)
addtimer(CALLBACK(src, .proc/Decay), delay_decay, FALSE) // Recall decay timer
return
+5 -2
View File
@@ -399,7 +399,7 @@
on_sound = 'sound/weapons/batonextend.ogg'
on_icon_state = "telebaton_1"
off_icon_state = "telebaton_0"
on_item_state = "nullrod"
on_item_state = "telebaton_1"
force_on = 10
force_off = 0
weight_class_on = WEIGHT_CLASS_BULKY
@@ -472,7 +472,7 @@
on_stun_sound = 'sound/effects/contractorbatonhit.ogg'
on_icon_state = "contractor_baton_1"
off_icon_state = "contractor_baton_0"
on_item_state = "contractor_baton"
on_item_state = "contractor_baton_1"
force_on = 16
force_off = 5
weight_class_on = WEIGHT_CLASS_NORMAL
@@ -695,11 +695,14 @@
playsound(src, 'sound/weapons/batonextend.ogg', 50, 1)
/obj/item/melee/roastingstick/proc/finish_roasting(user, atom/target)
if(!held_sausage || held_sausage.roasted)
return // no
to_chat(user, "You finish roasting [held_sausage]")
playsound(src,'sound/items/welder2.ogg',50,1)
held_sausage.add_atom_colour(rgb(103,63,24), FIXED_COLOUR_PRIORITY)
held_sausage.name = "[target.name]-roasted [held_sausage.name]"
held_sausage.desc = "[held_sausage.desc] It has been cooked to perfection on \a [target]."
held_sausage.roasted = TRUE
update_icon()
/obj/item/melee/cleric_mace
+4 -2
View File
@@ -4,10 +4,10 @@
desc = "A handheld tracking device that locks onto certain signals."
icon = 'icons/obj/device.dmi'
icon_state = "pinpointer"
item_state = "pinpointer"
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
throw_speed = 3
@@ -97,6 +97,7 @@
name = "crew pinpointer"
desc = "A handheld tracking device that points to crew suit sensors."
icon_state = "pinpointer_crew"
item_state = "pinpointer_crew"
custom_price = PRICE_ABOVE_EXPENSIVE
var/has_owner = FALSE
var/pinpointer_owner = null
@@ -208,6 +209,7 @@
name = "fugitive pinpointer"
desc = "A handheld tracking device that locates the bounty hunter shuttle for quick escapes."
icon_state = "pinpointer_hunter"
item_state = "pinpointer_black"
var/obj/shuttleport
/obj/item/pinpointer/shuttle/Initialize(mapload)
@@ -231,4 +233,4 @@
/obj/item/pinpointer/custom
resets_target = FALSE
+1 -1
View File
@@ -280,7 +280,7 @@
if(lube & NO_SLIP_WHEN_WALKING)
if(C.m_intent == MOVE_INTENT_WALK)
return FALSE
if(ishuman(C) && !(lube & SLIP_WHEN_JOGGING))
if(ishuman(C) && !(lube & SLIP_WHEN_JOGGING) && CONFIG_GET(flag/sprint_enabled))
var/mob/living/carbon/human/H = C
if(!(H.combat_flags & COMBAT_FLAG_SPRINT_ACTIVE) && H.getStaminaLoss() <= 20)
return FALSE
+11 -8
View File
@@ -579,7 +579,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
/turf/AllowDrop()
return TRUE
/turf/proc/add_vomit_floor(mob/living/M, toxvomit = NONE)
/turf/proc/add_vomit_floor(mob/living/M, toxvomit = NONE, purge_ratio = 0.1)
var/obj/effect/decal/cleanable/vomit/V = new /obj/effect/decal/cleanable/vomit(src, M.get_static_viruses())
//if the vomit combined, apply toxicity and reagents to the old vomit
@@ -587,21 +587,24 @@ GLOBAL_LIST_EMPTY(station_turfs)
V = locate() in src
if(!V) //the decal was spawned on a wall or groundless turf and promptly qdeleted.
return
// Make toxins and blazaam vomit look different
// Apply the proper icon set based on vomit type
if(toxvomit == VOMIT_PURPLE)
V.icon_state = "vomitpurp_[pick(1,4)]"
else if (toxvomit == VOMIT_TOXIC)
V.icon_state = "vomittox_[pick(1,4)]"
if (iscarbon(M))
var/mob/living/carbon/C = M
if(C.reagents)
clear_reagents_to_vomit_pool(C,V)
else if (toxvomit == VOMIT_NANITE)
V.name = "metallic slurry"
V.desc = "A puddle of metallic slurry that looks vaguely like very fine sand. It almost seems like it's moving..."
V.icon_state = "vomitnanite_[pick(1,4)]"
if (purge_ratio && iscarbon(M))
clear_reagents_to_vomit_pool(M, V, purge_ratio)
/proc/clear_reagents_to_vomit_pool(mob/living/carbon/M, obj/effect/decal/cleanable/vomit/V)
/proc/clear_reagents_to_vomit_pool(mob/living/carbon/M, obj/effect/decal/cleanable/vomit/V, purge_ratio = 0.1)
for(var/datum/reagent/consumable/R in M.reagents.reagent_list) //clears the stomach of anything that might be digested as food
if(R.nutriment_factor > 0)
M.reagents.del_reagent(R.type)
M.reagents.trans_to(V, M.reagents.total_volume / 10)
var/chemicals_lost = M.reagents.total_volume * purge_ratio
M.reagents.trans_to(V, chemicals_lost)
//Whatever happens after high temperature fire dies out or thermite reaction works.
//Should return new turf
@@ -684,6 +684,7 @@
if(HAS_TRAIT(user, TRAIT_NOPUGILIST))
to_chat(user, "<span class='warning'>We would gain nothing by forming our fists into brute-force weapons when we are trained in precision martial arts!</span>")
return
return ..()
/obj/item/clothing/gloves/fingerless/pugilist/cling // switches between lesser GotNS and Big Punchy Rib Breaky Hands
name = "hewn bone gauntlets"
@@ -181,9 +181,15 @@
make_glow()
glow.icon_state = "clockwork_gateway_disrupted"
resistance_flags |= INDESTRUCTIBLE
sleep(27)
explosion(src, 1, 3, 8, 8)
sound_to_playing_players('sound/effects/explosion_distant.ogg', volume = 50)
addtimer(CALLBACK(src, .proc/go_boom), 2.7 SECONDS)
return
qdel(src)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/go_boom()
if(QDELETED(src))
return
explosion(src, 1, 3, 8, 8)
sound_to_playing_players('sound/effects/explosion_distant.ogg', volume = 50)
qdel(src)
/obj/structure/destructible/clockwork/massive/celestial_gateway/proc/make_glow()
@@ -61,6 +61,7 @@
name = "syndicate pinpointer"
desc = "A handheld tracking device that locks onto certain signals. It's configured to switch tracking modes once it detects the activation signal of a nuclear device."
icon_state = "pinpointer_syndicate"
item_state = "pinpointer_black"
/obj/item/pinpointer/syndicate_cyborg // Cyborg pinpointers just look for a random operative.
name = "cyborg syndicate pinpointer"
@@ -0,0 +1,79 @@
/datum/antagonist/space_dragon
name = "Space Dragon"
roundend_category = "space dragons"
antagpanel_category = "Space Dragon"
job_rank = ROLE_SPACE_DRAGON
show_in_antagpanel = TRUE
show_name_in_check_antagonists = TRUE
var/list/datum/mind/carp = list()
/datum/antagonist/space_dragon/greet()
to_chat(owner, "<b>Endless time and space we have moved through. We do not remember from where we came, we do not know where we will go. All space belongs to us.\n\
Space is an empty void, of which our kind is the apex predator, and there was little to rival our claim to this title.\n\
But now, we find intruders spread out amongst our claim, willing to fight our teeth with magics unimaginable, their dens like lights flicking in the depths of space.\n\
Today, we will snuff out one of those lights.</b>")
to_chat(owner, "<span class='boldwarning'>You have five minutes to find a safe location to place down the first rift. If you take longer than five minutes to place a rift, you will be returned from whence you came.\n\
Alt click to cause a gust around you!</span>")
owner.announce_objectives()
SEND_SOUND(owner.current, sound('sound/magic/demon_attack1.ogg'))
/datum/antagonist/space_dragon/proc/forge_objectives()
var/datum/objective/summon_carp/summon = new()
summon.dragon = src
objectives += summon
/datum/antagonist/space_dragon/on_gain()
forge_objectives()
. = ..()
/datum/objective/summon_carp
var/datum/antagonist/space_dragon/dragon
explanation_text = "Summon and protect the rifts to flood the station with carp."
/datum/antagonist/space_dragon/roundend_report()
var/list/parts = list()
var/datum/objective/summon_carp/S = locate() in objectives
if(S.check_completion())
parts += "<span class='redtext big'>The [name] has succeeded! Station space has been reclaimed by the space carp!</span>"
parts += printplayer(owner)
var/objectives_complete = TRUE
if(objectives.len)
parts += printobjectives(objectives)
for(var/datum/objective/objective in objectives)
if(!objective.check_completion())
objectives_complete = FALSE
break
if(objectives_complete)
parts += "<span class='greentext big'>The [name] was successful!</span>"
else
parts += "<span class='redtext big'>The [name] has failed!</span>"
parts += "<span class='header'>The [name] was assisted by:</span>"
parts += printplayerlist(carp)
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
/datum/antagonist/space_dragon/admin_add(datum/mind/new_owner, mob/admin)
// pick the spawn loc
var/list/spawn_locs = list()
for(var/obj/effect/landmark/carpspawn/carp_spawn in GLOB.landmarks_list)
if(!isturf(carp_spawn.loc))
stack_trace("Carp spawn found not on a turf: [carp_spawn.type] on [isnull(carp_spawn.loc) ? "null" : carp_spawn.loc.type]")
continue
spawn_locs += carp_spawn.loc
if(!spawn_locs.len)
message_admins("No valid spawn locations found, aborting...")
return MAP_ERROR
// spawn our dragon
var/mob/living/simple_animal/hostile/space_dragon/S = new(pick(spawn_locs))
// gib or delete the old mob here
new_owner.current.gib()
// alternativelly, isntead of using the code above to pick a location, we can gib the mob, then spawn the dragon where it died for a goresome transformation
//mind transfer and role setup
new_owner.transfer_to(S)
new_owner.assigned_role = "Space Dragon"
new_owner.special_role = "Space Dragon"
playsound(S, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
. = ..()
return SUCCESSFUL_SPAWN
@@ -244,6 +244,7 @@
name = "contractor pinpointer"
desc = "A handheld tracking device that locks onto certain signals. Ignores suit sensors, but is much less accurate."
icon_state = "pinpointer_syndicate"
item_state = "pinpointer_black"
minimum_range = 25
has_owner = TRUE
ignore_suit_sensor_level = TRUE
@@ -166,6 +166,13 @@
)
parents = list("font-awesome.css" = 'html/font-awesome/css/all.min.css')
/datum/asset/simple/namespaced/tgfont
assets = list(
"tgfont.eot" = 'tgui/packages/tgfont/dist/tgfont.eot',
"tgfont.woff2" = 'tgui/packages/tgfont/dist/tgfont.woff2',
)
parents = list("tgfont.css" = 'tgui/packages/tgfont/dist/tgfont.css')
/datum/asset/spritesheet/chat
name = "chat"
@@ -21,7 +21,7 @@
air_contents.set_volume(volume)
air_contents.set_temperature(T20C)
if(gas_type)
air_contents.set_moles(AIR_CONTENTS)
air_contents.set_moles(gas_type, AIR_CONTENTS)
name = "[name] ([GLOB.meta_gas_names[gas_type]])"
setPipingLayer(piping_layer)
@@ -32,8 +32,8 @@
/obj/machinery/atmospherics/components/unary/tank/air/New()
..()
var/datum/gas_mixture/air_contents = airs[1]
air_contents.set_moles(/datum/gas/oxygen, AIR_CONTENTS * 0.2)
air_contents.set_moles(/datum/gas/nitrogen, AIR_CONTENTS * 0.8)
air_contents.set_moles(/datum/gas/oxygen, AIR_CONTENTS * 0.21)
air_contents.set_moles(/datum/gas/nitrogen, AIR_CONTENTS * 0.79)
/obj/machinery/atmospherics/components/unary/tank/carbon_dioxide
gas_type = /datum/gas/carbon_dioxide
+5 -3
View File
@@ -84,17 +84,19 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they
/datum/export/New()
..()
SSprocessing.processing += src
START_PROCESSING(SSprocessing, src)
init_cost = cost
export_types = typecacheof(export_types)
exclude_types = typecacheof(exclude_types)
/datum/export/Destroy()
SSprocessing.processing -= src
STOP_PROCESSING(SSprocessing, src)
return ..()
/datum/export/process()
..()
. = ..()
if(!k_elasticity)
return PROCESS_KILL
cost *= NUM_E**(k_elasticity * (1/30))
if(cost > init_cost)
cost = init_cost
+5 -2
View File
@@ -2,8 +2,11 @@
name = "Cat Surgeon"
typepath = /datum/round_event/cat_surgeon
max_occurrences = 1
weight = 10
weight = 8
/datum/round_event/cat_surgeon/announce(fake)
priority_announce("One of our... ahem... 'special' cases has escaped. Our sensors now show their tracker implant on your station. On an unrelated note, has anyone seen our cats?",
sender_override = "Nanotrasen Psych Ward")
/datum/round_event/cat_surgeon/start()
var/list/spawn_locs = list()
@@ -16,7 +19,7 @@
var/turf/T = get_turf(pick(spawn_locs))
var/mob/living/simple_animal/hostile/cat_butcherer/S = new(T)
playsound(S, 'sound/misc/catscream.ogg', 50, 1, -1)
playsound(S, 'sound/misc/catscream.ogg', 75, 1, -1)
message_admins("A cat surgeon has been spawned at [COORD(T)][ADMIN_JMP(T)]")
log_game("A cat surgeon has been spawned at [COORD(T)]")
return SUCCESSFUL_SPAWN
+45
View File
@@ -0,0 +1,45 @@
/datum/round_event_control/space_dragon
name = "Spawn Space Dragon"
typepath = /datum/round_event/ghost_role/space_dragon
weight = 8
max_occurrences = 1
min_players = 20
/datum/round_event/ghost_role/space_dragon
minimum_required = 1
role_name = "Space Dragon"
announceWhen = 10
/datum/round_event/ghost_role/space_dragon/announce(fake)
priority_announce("A large organic energy flux has been recorded near of [station_name()], please stand-by.", "Lifesign Alert")
/datum/round_event/ghost_role/space_dragon/spawn_role()
var/list/spawn_locs = list()
for(var/obj/effect/landmark/carpspawn/carp_spawn in GLOB.landmarks_list)
if(!isturf(carp_spawn.loc))
stack_trace("Carp spawn found not on a turf: [carp_spawn.type] on [isnull(carp_spawn.loc) ? "null" : carp_spawn.loc.type]")
continue
spawn_locs += carp_spawn.loc
if(!spawn_locs.len)
message_admins("No valid spawn locations found, aborting...")
return MAP_ERROR
var/list/candidates = get_candidates(ROLE_SPACE_DRAGON, null, ROLE_SPACE_DRAGON)
if(!candidates.len)
return NOT_ENOUGH_PLAYERS
var/mob/dead/selected = pick_n_take(candidates)
var/datum/mind/player_mind = new /datum/mind(selected.key)
player_mind.active = TRUE
var/mob/living/simple_animal/hostile/space_dragon/S = new(pick(spawn_locs))
player_mind.transfer_to(S)
player_mind.assigned_role = "Space Dragon"
player_mind.special_role = "Space Dragon"
player_mind.add_antag_datum(/datum/antagonist/space_dragon)
playsound(S, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a Space Dragon by an event.")
log_game("[key_name(S)] was spawned as a Space Dragon by an event.")
spawned_mobs += S
return SUCCESSFUL_SPAWN
+11 -12
View File
@@ -514,17 +514,17 @@
return 0
return ..()
/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, toxic = FALSE)
if(HAS_TRAIT(src, TRAIT_NOHUNGER))
return 1
/mob/living/carbon/proc/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, vomit_type = VOMIT_TOXIC, harm = TRUE, force = FALSE, purge_ratio = 0.1)
if(HAS_TRAIT(src, TRAIT_NOHUNGER) && !force)
return TRUE
if(nutrition < 100 && !blood)
if(nutrition < 100 && !blood && !force)
if(message)
visible_message("<span class='warning'>[src] dry heaves!</span>", \
"<span class='userdanger'>You try to throw up, but there's nothing in your stomach!</span>")
if(stun)
DefaultCombatKnockdown(200)
return 1
return TRUE
if(is_mouth_covered()) //make this add a blood/vomit overlay later it'll be hilarious
if(message)
@@ -537,30 +537,29 @@
visible_message("<span class='danger'>[src] throws up!</span>", "<span class='userdanger'>You throw up!</span>")
if(!isflyperson(src))
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "vomit", /datum/mood_event/vomit)
if(stun)
Stun(80)
playsound(get_turf(src), 'sound/effects/splat.ogg', 50, 1)
playsound(get_turf(src), 'sound/effects/splat.ogg', 50, TRUE)
var/turf/T = get_turf(src)
if(!blood)
adjust_nutrition(-lost_nutrition)
adjustToxLoss(-3)
for(var/i=0 to distance)
if(blood)
if(T)
add_splatter_floor(T)
if(stun)
if(harm)
adjustBruteLoss(3)
else if(src.reagents.has_reagent(/datum/reagent/consumable/ethanol/blazaam))
if(T)
T.add_vomit_floor(src, VOMIT_PURPLE)
else
if(T)
T.add_vomit_floor(src, VOMIT_TOXIC)//toxic barf looks different
T.add_vomit_floor(src, vomit_type, purge_ratio) //toxic barf looks different || call purge when doing detoxicfication to pump more chems out of the stomach.
T = get_step(T, dir)
if (is_blocked_turf(T))
break
return 1
return TRUE
/mob/living/carbon/proc/spew_organ(power = 5, amt = 1)
var/list/spillable_organs = list()
@@ -840,7 +840,7 @@
override = dna.species.override_float
..()
/mob/living/carbon/human/vomit(lost_nutrition = 10, blood = 0, stun = 1, distance = 0, message = 1, toxic = 0)
/mob/living/carbon/human/vomit(lost_nutrition = 10, blood = FALSE, stun = TRUE, distance = 1, message = TRUE, vomit_type = VOMIT_TOXIC, harm = TRUE, force = FALSE, purge_ratio = 0.1)
if(blood && dna?.species && (NOBLOOD in dna.species.species_traits))
if(message)
visible_message("<span class='warning'>[src] dry heaves!</span>", \
@@ -1095,7 +1095,7 @@
* * Rock / Brownish if a golem
* * Green if none of the others apply (aka, generic organic)
*/
/mob/living/carbon/human/proc/spec_trait_examine_font()
/mob/living/carbon/human/proc/spec_trait_examine_font()
if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
return "<font color='#aaa9ad'>"
if(HAS_TRAIT(src, TRAIT_TOXINLOVER))
+1 -1
View File
@@ -273,7 +273,7 @@
if(getToxLoss() >= 45 && nutrition > 20 && !HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
lastpuke += prob(50)
if(lastpuke >= 50) // about 25 second delay I guess
vomit(20, toxic = TRUE)
vomit(20)
lastpuke = 0
@@ -1,4 +1,5 @@
/mob/living/silicon/examine(mob/user) //Displays a silicon's laws to ghosts
. = ..()
if(laws && isobserver(user))
. += "<b>[src] has the following laws:</b>"
for(var/law in laws.get_law_list(include_zeroth = TRUE))
@@ -0,0 +1,690 @@
/// The carp rift is currently charging.
#define CHARGE_ONGOING 0
/// The carp rift is currently charging and has output a final warning.
#define CHARGE_FINALWARNING 1
/// The carp rift is now fully charged.
#define CHARGE_COMPLETED 2
/// The darkness threshold for space dragon when choosing a color
#define DARKNESS_THRESHOLD 50
/**
* # Space Dragon
*
* A space-faring leviathan-esque monster which breathes fire and summons carp. Spawned during its respective midround antagonist event.
*
* A space-faring monstrosity who has the ability to breathe dangerous fire breath and uses its powerful wings to knock foes away.
* Normally spawned as an antagonist during the Space Dragon event, Space Dragon's main goal is to open three rifts from which to pull a great tide of carp onto the station.
* Space Dragon can summon only one rift at a time, and can do so anywhere a blob is allowed to spawn. In order to trigger his victory condition, Space Dragon must summon and defend three rifts while they charge.
* Space Dragon, when spawned, has five minutes to summon the first rift. Failing to do so will cause Space Dragon to return from whence he came.
* When the rift spawns, ghosts can interact with it to spawn in as space carp to help complete the mission. One carp is granted when the rift is first summoned, with an extra one every 30 seconds.
* Once the victory condition is met, all current rifts become invulnerable to damage, are allowed to spawn infinite sentient space carp, and Space Dragon gets unlimited rage.
* Alternatively, if the shuttle arrives while Space Dragon is still active, their victory condition will automatically be met and all the rifts will immediately become fully charged.
* If a charging rift is destroyed, Space Dragon will be incredibly slowed, and the endlag on his gust attack is greatly increased on each use.
* Space Dragon has the following abilities to assist him with his objective:
* - Can shoot fire in straight line, dealing 30 burn damage and setting those suseptible on fire.
* - Can use his wings to temporarily stun and knock back any nearby mobs. This attack has no cooldown, but instead has endlag after the attack where Space Dragon cannot act. This endlag's time decreases over time, but is added to every time he uses the move.
* - Can swallow mob corpses to heal for half their max health. Any corpses swallowed are stored within him, and will be regurgitated on death.
* - Can tear through any type of wall. This takes 4 seconds for most walls, and 12 seconds for reinforced walls.
*/
/mob/living/simple_animal/hostile/space_dragon
name = "Space Dragon"
desc = "A vile, leviathan-esque creature that flies in the most unnatural way. Looks slightly similar to a space carp."
maxHealth = 400
health = 400
a_intent = INTENT_HARM
speed = 0
attack_verb_continuous = "chomps"
attack_verb_simple = "chomp"
attack_sound = 'sound/magic/demon_attack1.ogg'
deathsound = 'sound/creatures/space_dragon_roar.ogg'
icon = 'icons/mob/spacedragon.dmi'
icon_state = "spacedragon"
icon_living = "spacedragon"
icon_dead = "spacedragon_dead"
obj_damage = 50
environment_smash = ENVIRONMENT_SMASH_NONE
flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1
melee_damage_upper = 35
melee_damage_lower = 35
mob_size = MOB_SIZE_LARGE
armour_penetration = 30
pixel_x = -16
turns_per_move = 5
movement_type = FLYING
health_doll_icon = "spacedragon"
ranged = TRUE
mouse_opacity = MOUSE_OPACITY_ICON
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30)
deathmessage = "screeches as its wings turn to dust and it collapses on the floor, its life extinguished."
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = 1500
faction = list("carp")
pressure_resistance = 200
/// Current time since the the last rift was activated. If set to -1, does not increment.
var/riftTimer = 0
/// Maximum amount of time which can pass without a rift before Space Dragon despawns.
var/maxRiftTimer = 300
/// How much endlag using Wing Gust should apply. Each use of wing gust increments this, and it decreases over time.
var/tiredness = 0
/// A multiplier to how much each use of wing gust should add to the tiredness variable. Set to 5 if the current rift is destroyed.
var/tiredness_mult = 1
/// The distance Space Dragon's gust reaches
var/gust_distance = 4
/// The amount of tiredness to add to Space Dragon per use of gust
var/gust_tiredness = 30
/// Determines whether or not Space Dragon is in the middle of using wing gust. If set to true, prevents him from moving and doing certain actions.
var/using_special = FALSE
/// Determines whether or not Space Dragon is currently tearing through a wall.
var/tearing_wall = FALSE
/// A list of all of the rifts created by Space Dragon. Used for setting them all to infinite carp spawn when Space Dragon wins, and removing them when Space Dragon dies.
var/list/obj/structure/carp_rift/rift_list = list()
/// How many rifts have been successfully charged
var/rifts_charged = 0
/// Whether or not Space Dragon has completed their objective, and thus triggered the ending sequence.
var/objective_complete = FALSE
/// The innate ability to summon rifts
var/datum/action/innate/summon_rift/rift
/// The color of the space dragon.
var/chosen_color
/mob/living/simple_animal/hostile/space_dragon/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
rift = new
rift.Grant(src)
/mob/living/simple_animal/hostile/space_dragon/Login()
. = ..()
if(!chosen_color)
dragon_name()
color_selection()
/mob/living/simple_animal/hostile/space_dragon/Life()
. = ..()
tiredness = max(tiredness - 1, 0)
for(var/mob/living/consumed_mob in src)
if(consumed_mob.stat == DEAD)
continue
playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
visible_message("<span class='danger'>[src] vomits up [consumed_mob]!</span>")
consumed_mob.forceMove(loc)
consumed_mob.Paralyze(50)
if((rifts_charged == 3 || (SSshuttle.emergency.mode == SHUTTLE_DOCKED && rifts_charged > 0)) && !objective_complete)
victory()
if(riftTimer == -1)
return
riftTimer = min(riftTimer + 1, maxRiftTimer + 1)
if(riftTimer == (maxRiftTimer - 60))
to_chat(src, "<span class='boldwarning'>You have a minute left to summon the rift! Get to it!</span>")
return
if(riftTimer >= maxRiftTimer)
to_chat(src, "<span class='boldwarning'>You've failed to summon the rift in a timely manner! You're being pulled back from whence you came!</span>")
destroy_rifts()
playsound(src, 'sound/magic/demon_dies.ogg', 100, TRUE)
QDEL_NULL(src)
/mob/living/simple_animal/hostile/space_dragon/AttackingTarget()
if(using_special)
return
if(target == src)
to_chat(src, "<span class='warning'>You almost bite yourself, but then decide against it.</span>")
return
if(istype(target, /turf/closed/wall))
if(tearing_wall)
return
tearing_wall = TRUE
var/turf/closed/wall/thewall = target
to_chat(src, "<span class='warning'>You begin tearing through the wall...</span>")
playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE)
var/timetotear = 40
if(istype(target, /turf/closed/wall/r_wall))
timetotear = 120
if(do_after(src, timetotear, target = thewall))
if(istype(thewall, /turf/open))
return
thewall.dismantle_wall(1)
playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
tearing_wall = FALSE
return
if(isliving(target)) //Swallows corpses like a snake to regain health.
var/mob/living/L = target
if(L.stat == DEAD)
to_chat(src, "<span class='warning'>You begin to swallow [L] whole...</span>")
if(do_after(src, 30, target = L))
if(eat(L))
adjustHealth(-L.maxHealth * 0.5)
return
. = ..()
if(istype(target, /obj/mecha))
var/obj/mecha/M = target
M.take_damage(50, BRUTE, MELEE, 1)
/mob/living/simple_animal/hostile/space_dragon/AltClickOn(atom/A)
. = ..()
if(using_special)
return
using_special = TRUE
icon_state = "spacedragon_gust"
add_dragon_overlay()
useGust(0)
/mob/living/simple_animal/hostile/space_dragon/Move()
if(!using_special)
..()
/mob/living/simple_animal/hostile/space_dragon/OpenFire()
if(using_special)
return
ranged_cooldown = world.time + ranged_cooldown_time
fire_stream()
/mob/living/simple_animal/hostile/space_dragon/death(gibbed)
empty_contents()
if(!objective_complete)
destroy_rifts()
..()
add_dragon_overlay()
/mob/living/simple_animal/hostile/space_dragon/revive(full_heal, admin_revive)
. = ..()
add_dragon_overlay()
/mob/living/simple_animal/hostile/space_dragon/wabbajack_act(mob/living/new_mob)
empty_contents()
. = ..()
/**
* Allows space dragon to choose its own name.
*
* Prompts the space dragon to choose a name, which it will then apply to itself.
* If the name is invalid, will re-prompt the dragon until a proper name is chosen.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/dragon_name()
var/chosen_name = sanitize_name(reject_bad_text(stripped_input(src, "What would you like your name to be?", "Choose Your Name", real_name, MAX_NAME_LEN)))
if(!chosen_name)
to_chat(src, "<span class='warning'>Not a valid name, please try again.</span>")
dragon_name()
return
visible_message("<span class='notice'>Your name is now <span class='name'>[chosen_name]</span>, the feared Space Dragon.</span>")
fully_replace_character_name(null, chosen_name)
/**
* Allows space dragon to choose a color for itself.
*
* Prompts the space dragon to choose a color, from which it will then apply to itself.
* If an invalid color is given, will re-prompt the dragon until a proper color is chosen.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/color_selection()
chosen_color = input(src,"What would you like your color to be?","Choose Your Color", COLOR_WHITE) as color|null
if(!chosen_color) //redo proc until we get a color
to_chat(src, "<span class='warning'>Not a valid color, please try again.</span>")
color_selection()
return
var/temp_hsv = RGBtoHSV(chosen_color)
if(ReadHSV(temp_hsv)[3] < DARKNESS_THRESHOLD)
to_chat(src, "<span class='danger'>Invalid color. Your color is not bright enough.</span>")
color_selection()
return
add_atom_colour(chosen_color, FIXED_COLOUR_PRIORITY)
add_dragon_overlay()
/**
* Adds the proper overlay to the space dragon.
*
* Clears the current overlay on space dragon and adds a proper one for whatever animation he's in.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/add_dragon_overlay()
cut_overlays()
if(stat == DEAD)
var/mutable_appearance/overlay = mutable_appearance(icon, "overlay_dead")
overlay.appearance_flags = RESET_COLOR
add_overlay(overlay)
return
if(!using_special)
var/mutable_appearance/overlay = mutable_appearance(icon, "overlay_base")
overlay.appearance_flags = RESET_COLOR
add_overlay(overlay)
return
if(using_special)
var/mutable_appearance/overlay = mutable_appearance(icon, "overlay_gust")
overlay.appearance_flags = RESET_COLOR
add_overlay(overlay)
/**
* Determines a line of turfs from sources's position to the target with length range.
*
* Determines a line of turfs from the source's position to the target with length range.
* The line will extend on past the target if the range is large enough, and not reach the target if range is small enough.
* Arguments:
* * offset - whether or not to aim slightly to the left or right of the target
* * range - how many turfs should we go out for
* * atom/at - The target
*/
/mob/living/simple_animal/hostile/space_dragon/proc/line_target(offset, range, atom/at = target)
if(!at)
return
var/angle = ATAN2(at.x - src.x, at.y - src.y) + offset
var/turf/T = get_turf(src)
for(var/i in 1 to range)
var/turf/check = locate(src.x + cos(angle) * i, src.y + sin(angle) * i, src.z)
if(!check)
break
T = check
return (getline(src, T) - get_turf(src))
/**
* Spawns fire at each position in a line from the source to the target.
*
* Spawns fire at each position in a line from the source to the target.
* Stops if it comes into contact with a solid wall, a window, or a door.
* Delays the spawning of each fire by 1.5 deciseconds.
* Arguments:
* * atom/at - The target
*/
/mob/living/simple_animal/hostile/space_dragon/proc/fire_stream(atom/at = target)
playsound(get_turf(src),'sound/magic/fireball.ogg', 200, TRUE)
var/range = 20
var/list/turfs = list()
turfs = line_target(0, range, at)
var/delayFire = -1.0
for(var/turf/T in turfs)
if(istype(T, /turf/closed))
return
for(var/obj/structure/window/W in T.contents)
return
for(var/obj/machinery/door/D in T.contents)
if(D.density)
return
delayFire += 1.0
addtimer(CALLBACK(src, .proc/dragon_fire_line, T), delayFire)
/**
* What occurs on each tile to actually create the fire.
*
* Creates a fire on the given turf.
* It creates a hotspot on the given turf, damages any living mob with 30 burn damage, and damages mechs by 50.
* It can only hit any given target once.
* Arguments:
* * turf/T - The turf to trigger the effects on.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/dragon_fire_line(turf/T)
var/list/hit_list = list()
hit_list += src
new /obj/effect/hotspot(T)
T.hotspot_expose(700,50,1)
for(var/mob/living/L in T.contents)
if(L in hit_list)
continue
hit_list += L
L.adjustFireLoss(30)
to_chat(L, "<span class='userdanger'>You're hit by [src]'s fire breath!</span>")
// deals damage to mechs
for(var/obj/mecha/M in T.contents)
if(M in hit_list)
continue
hit_list += M
M.take_damage(50, BRUTE, MELEE, 1)
/**
* Handles consuming and storing consumed things inside Space Dragon
*
* Plays a sound and then stores the consumed thing inside Space Dragon.
* Used in AttackingTarget(), paired with a heal should it succeed.
* Arguments:
* * atom/movable/A - The thing being consumed
*/
/mob/living/simple_animal/hostile/space_dragon/proc/eat(atom/movable/A)
if(A && A.loc != src)
playsound(src, 'sound/magic/demon_attack1.ogg', 100, TRUE)
visible_message("<span class='warning'>[src] swallows [A] whole!</span>")
A.forceMove(src)
return TRUE
return FALSE
/**
* Disperses the contents of the mob on the surrounding tiles.
*
* Randomly places the contents of the mob onto surrounding tiles.
* Has a 10% chance to place on the same tile as the mob.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/empty_contents()
for(var/atom/movable/AM in src)
AM.forceMove(loc)
if(prob(90))
step(AM, pick(GLOB.alldirs))
/**
* Resets Space Dragon's status after using wing gust.
*
* Resets Space Dragon's status after using wing gust.
* If it isn't dead by the time it calls this method, reset the sprite back to the normal living sprite.
* Also sets the using_special variable to FALSE, allowing Space Dragon to move and attack freely again.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/reset_status()
if(stat != DEAD)
icon_state = "spacedragon"
using_special = FALSE
add_dragon_overlay()
/**
* Handles Space Dragon's temporary empowerment after boosting a rift.
*
* Empowers and depowers Space Dragon after a successful rift charge.
* Empowered, Space Dragon regains all his health and becomes temporarily faster for 30 seconds, along with being tinted red.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/rift_empower(is_permanent)
fully_heal()
add_filter("anger_glow", 3, list("type" = "outline", "color" = "#ff330030", "size" = 5))
add_movespeed_modifier(/datum/movespeed_modifier/dragon_rage)
addtimer(CALLBACK(src, .proc/rift_depower), 30 SECONDS)
/**
* Gives Space Dragon their the rift speed buff permanantly.
*
* Gives Space Dragon the enraged speed buff from charging rifts permanantly.
* Only happens in circumstances where Space Dragon completes their objective.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/permanant_empower()
fully_heal()
add_filter("anger_glow", 3, list("type" = "outline", "color" = "#ff330030", "size" = 5))
add_movespeed_modifier(/datum/movespeed_modifier/dragon_rage)
/**
* Removes Space Dragon's rift speed buff.
*
* Removes Space Dragon's speed buff from charging a rift. This is only called
* in rift_empower, which uses a timer to call this after 30 seconds. Also
* removes the red glow from Space Dragon which is synonymous with the speed buff.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/rift_depower()
remove_filter("anger_glow")
remove_movespeed_modifier(/datum/movespeed_modifier/dragon_rage)
/**
* Destroys all of Space Dragon's current rifts.
*
* QDeletes all the current rifts after removing their references to other objects.
* Currently, the only reference they have is to the Dragon which created them, so we clear that before deleting them.
* Currently used when Space Dragon dies or one of his rifts is destroyed.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/destroy_rifts()
rifts_charged = 0
add_movespeed_modifier(/datum/movespeed_modifier/dragon_depression)
riftTimer = -1
tiredness_mult = 5
playsound(src, 'sound/vehicles/rocketlaunch.ogg', 100, TRUE)
for(var/obj/structure/carp_rift/rift in rift_list)
rift.dragon = null
rift_list -= rift
if(!QDELETED(rift))
QDEL_NULL(rift)
/**
* Handles wing gust from the windup all the way to the endlag at the end.
*
* Handles the wing gust attack from start to finish, based on the timer.
* When intially triggered, starts at 0. Until the timer reaches 10, increase Space Dragon's y position by 2 and call back to the function in 1.5 deciseconds.
* When the timer is at 10, trigger the attack. Change Space Dragon's sprite. reset his y position, and push all living creatures back in a 3 tile radius and stun them for 5 seconds.
* Stay in the ending state for how much our tiredness dictates and add to our tiredness.
* Arguments:
* * timer - The timer used for the windup.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/useGust(timer)
if(timer != 10)
pixel_y = pixel_y + 2;
addtimer(CALLBACK(src, .proc/useGust, timer + 1), 1.5)
return
pixel_y = 0
icon_state = "spacedragon_gust_2"
cut_overlays()
var/mutable_appearance/overlay = mutable_appearance(icon, "overlay_gust_2")
overlay.appearance_flags = RESET_COLOR
add_overlay(overlay)
playsound(src, 'sound/effects/gravhit.ogg', 100, TRUE)
var/gust_locs = spiral_range_turfs(gust_distance, get_turf(src))
var/list/hit_things = list()
for(var/turf/T in gust_locs)
for(var/mob/living/L in T.contents)
if(L == src)
continue
hit_things += L
visible_message("<span class='boldwarning'>[L] is knocked back by the gust!</span>")
to_chat(L, "<span class='userdanger'>You're knocked back by the gust!</span>")
var/dir_to_target = get_dir(get_turf(src), get_turf(L))
var/throwtarget = get_edge_target_turf(target, dir_to_target)
L.safe_throw_at(throwtarget, 10, 1, src)
L.Paralyze(50)
addtimer(CALLBACK(src, .proc/reset_status), 4 + ((tiredness * tiredness_mult) / 10))
tiredness = tiredness + (gust_tiredness * tiredness_mult)
/**
* Sets up Space Dragon's victory for completing the objectives.
*
* Triggers when Space Dragon completes his objective.
* Calls the shuttle with a coefficient of 3, making it impossible to recall.
* Sets all of his rifts to allow for infinite sentient carp spawns
* Also plays appropiate sounds and CENTCOM messages.
*/
/mob/living/simple_animal/hostile/space_dragon/proc/victory()
objective_complete = TRUE
permanant_empower()
var/datum/antagonist/space_dragon/S = mind.has_antag_datum(/datum/antagonist/space_dragon)
if(S)
var/datum/objective/summon_carp/main_objective = locate() in S.objectives
if(main_objective)
main_objective.completed = TRUE
priority_announce("A large amount of lifeforms have been detected approaching [station_name()] at extreme speeds. Remaining crew are advised to evacuate as soon as possible.", "Central Command Wildlife Observations")
sound_to_playing_players('sound/creatures/space_dragon_roar.ogg')
for(var/obj/structure/carp_rift/rift in rift_list)
rift.carp_stored = 999999
rift.time_charged = rift.max_charge
/datum/action/innate/summon_rift
name = "Summon Rift"
desc = "Summon a rift to bring forth a horde of space carp."
background_icon_state = "bg_default"
icon_icon = 'icons/mob/actions/actions_space_dragon.dmi'
button_icon_state = "carp_rift"
/datum/action/innate/summon_rift/Activate()
var/mob/living/simple_animal/hostile/space_dragon/S = owner
if(S.using_special)
return
if(S.riftTimer == -1)
to_chat(S, "<span class='warning'>Your death has left you unable to summon rifts!</span>")
return
var/area/A = get_area(S)
if(!(A.area_flags & (VALID_TERRITORY | BLOBS_ALLOWED)))
to_chat(S, "<span class='warning'>You can't summon a rift here! Try summoning somewhere secure within the station!</span>")
return
for(var/obj/structure/carp_rift/rift in S.rift_list)
var/area/RA = get_area(rift)
if(RA == A)
to_chat(S, "<span class='warning'>You've already summoned a rift in this area! You have to summon again somewhere else!</span>")
return
to_chat(S, "<span class='warning'>You begin to open a rift...</span>")
if(do_after(S, 100, target = S))
for(var/obj/structure/carp_rift/c in S.loc.contents)
return
var/obj/structure/carp_rift/CR = new /obj/structure/carp_rift(S.loc)
playsound(S, 'sound/vehicles/rocketlaunch.ogg', 100, TRUE)
S.riftTimer = -1
CR.dragon = S
S.rift_list += CR
to_chat(S, "<span class='boldwarning'>The rift has been summoned. Prevent the crew from destroying it at all costs!</span>")
notify_ghosts("The Space Dragon has opened a rift!", source = CR, action = NOTIFY_ORBIT, flashwindow = FALSE, header = "Carp Rift Opened")
qdel(src)
/**
* # Carp Rift
*
* The portals Space Dragon summons to bring carp onto the station.
*
* The portals Space Dragon summons to bring carp onto the station. His main objective is to summon 3 of them and protect them from being destroyed.
* The portals can summon sentient space carp in limited amounts. The portal also changes color based on whether or not a carp spawn is available.
* Once it is fully charged, it becomes indestructible, and intermitently spawns non-sentient carp. It is still destroyed if Space Dragon dies.
*/
/obj/structure/carp_rift
name = "carp rift"
desc = "A rift akin to the ones space carp use to travel long distances."
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 50, BIO = 100, RAD = 100, FIRE = 100, ACID = 100)
max_integrity = 300
icon = 'icons/obj/carp_rift.dmi'
icon_state = "carp_rift_carpspawn"
light_color = LIGHT_COLOR_PURPLE
light_range = 10
anchored = TRUE
density = FALSE
layer = MASSIVE_OBJ_LAYER
/// The amount of time the rift has charged for.
var/time_charged = 0
/// The maximum charge the rift can have.
var/max_charge = 300
/// How many carp spawns it has available.
var/carp_stored = 1
/// A reference to the Space Dragon that created it.
var/mob/living/simple_animal/hostile/space_dragon/dragon
/// Current charge state of the rift.
var/charge_state = CHARGE_ONGOING
/// The interval for adding additional space carp spawns to the rift.
var/carp_interval = 30
/// The time since an extra carp was added to the ghost role spawning pool.
var/last_carp_inc = 0
/obj/structure/carp_rift/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
/obj/structure/carp_rift/examine(mob/user)
. = ..()
if(time_charged < max_charge)
. += "<span class='notice'>It seems to be [(time_charged / max_charge) * 100]% charged.</span>"
else
. += "<span class='warning'>This one is fully charged. In this state, it is poised to transport a much larger amount of carp than normal.</span>"
if(isobserver(user))
. += "<span class='notice'>It has [carp_stored] carp available to spawn as.</span>"
/obj/structure/carp_rift/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
playsound(src, 'sound/magic/lightningshock.ogg', 50, TRUE)
/obj/structure/carp_rift/Destroy()
STOP_PROCESSING(SSobj, src)
if(time_charged != max_charge + 1)
dragon?.destroy_rifts()
if(dragon)
to_chat(dragon, "<span class='boldwarning'>A rift has been destroyed! You have failed, and find yourself weakened.</span>")
return ..()
/obj/structure/carp_rift/process(delta_time)
// Heal carp on our loc.
for(var/mob/living/simple_animal/hostile/hostilehere in loc)
if("carp" in hostilehere.faction)
hostilehere.adjustHealth(-5 * delta_time)
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(hostilehere))
H.color = "#0000FF"
// If we're fully charged, just start mass spawning carp and move around.
if(charge_state == CHARGE_COMPLETED)
if(DT_PROB(1.25, delta_time))
new /mob/living/simple_animal/hostile/carp(loc)
if(DT_PROB(1.5, delta_time))
var/rand_dir = pick(GLOB.cardinals)
Move(get_step(src, rand_dir), rand_dir)
return
// Increase time trackers and check for any updated states.
time_charged = min(time_charged + delta_time, max_charge)
last_carp_inc += delta_time
update_check()
/obj/structure/carp_rift/attack_ghost(mob/user)
. = ..()
summon_carp(user)
/**
* Does a series of checks based on the portal's status.
*
* Performs a number of checks based on the current charge of the portal, and triggers various effects accordingly.
* If the current charge is a multiple of carp_interval, add an extra carp spawn.
* If we're halfway charged, announce to the crew our location in a CENTCOM announcement.
* If we're fully charged, tell the crew we are, change our color to yellow, become invulnerable, and give Space Dragon the ability to make another rift, if he hasn't summoned 3 total.
*/
/obj/structure/carp_rift/proc/update_check()
// If the rift is fully charged, there's nothing to do here anymore.
if(charge_state == CHARGE_COMPLETED)
return
// Can we increase the carp spawn pool size?
if(last_carp_inc >= carp_interval)
carp_stored++
icon_state = "carp_rift_carpspawn"
if(light_color != LIGHT_COLOR_PURPLE)
light_color = LIGHT_COLOR_PURPLE
update_light()
notify_ghosts("The carp rift can summon an additional carp!", source = src, action = NOTIFY_ORBIT, flashwindow = FALSE, header = "Carp Spawn Available")
last_carp_inc -= carp_interval
// Is the rift now fully charged?
if(time_charged >= max_charge)
charge_state = CHARGE_COMPLETED
var/area/A = get_area(src)
priority_announce("Spatial object has reached peak energy charge in [initial(A.name)], please stand-by.", "Central Command Wildlife Observations")
obj_integrity = INFINITY
icon_state = "carp_rift_charged"
light_color = LIGHT_COLOR_YELLOW
update_light()
armor = list(MELEE = 100, BULLET = 100, LASER = 100, ENERGY = 100, BOMB = 100, BIO = 100, RAD = 100, FIRE = 100, ACID = 100)
resistance_flags = INDESTRUCTIBLE
dragon.rifts_charged += 1
if(dragon.rifts_charged != 3 && !dragon.objective_complete)
dragon.rift = new
dragon.rift.Grant(dragon)
dragon.riftTimer = 0
dragon.rift_empower()
// Early return, nothing to do after this point.
return
// Do we need to give a final warning to the station at the halfway mark?
if(charge_state < CHARGE_FINALWARNING && time_charged >= (max_charge * 0.5))
charge_state = CHARGE_FINALWARNING
var/area/A = get_area(src)
priority_announce("A rift is causing an unnaturally large energy flux in [initial(A.name)]. Stop it at all costs!", "Central Command Wildlife Observations", sound = 'sound/announcer/classic/spanomalies.ogg')
/**
* Used to create carp controlled by ghosts when the option is available.
*
* Creates a carp for the ghost to control if we have a carp spawn available.
* Gives them prompt to control a carp, and if our circumstances still allow if when they hit yes, spawn them in as a carp.
* Also add them to the list of carps in Space Dragon's antgonist datum, so they'll be displayed as having assisted him on round end.
* Arguments:
* * mob/user - The ghost which will take control of the carp.
*/
/obj/structure/carp_rift/proc/summon_carp(mob/user)
if(carp_stored <= 0)//Not enough carp points
return FALSE
var/carp_ask = alert("Become a carp?", "Help bring forth the horde?", "Yes", "No")
if(carp_ask == "No" || !src || QDELETED(src) || QDELETED(user))
return FALSE
if(carp_stored <= 0)
to_chat(user, "<span class='warning'>The rift already summoned enough carp!</span>")
return FALSE
var/mob/living/simple_animal/hostile/carp/newcarp = new /mob/living/simple_animal/hostile/carp(loc)
newcarp.key = user.key
var/datum/antagonist/space_dragon/S = dragon.mind.has_antag_datum(/datum/antagonist/space_dragon)
if(S)
S.carp += newcarp.mind
to_chat(newcarp, "<span class='boldwarning'>You have arrived in order to assist the space dragon with securing the rifts. Do not jeopardize the mission, and protect the rifts at all costs!</span>")
carp_stored--
if(carp_stored <= 0 && charge_state < CHARGE_COMPLETED)
icon_state = "carp_rift"
light_color = LIGHT_COLOR_BLUE
update_light()
return TRUE
#undef CHARGE_ONGOING
#undef CHARGE_FINALWARNING
#undef CHARGE_COMPLETED
#undef DARKNESS_THRESHOLD
+1 -1
View File
@@ -208,7 +208,7 @@
///For moving in space
///return TRUE for movement 0 for none
/mob/Process_Spacemove(movement_dir = 0)
if(spacewalk || ..())
if(HAS_TRAIT(src, TRAIT_SPACEWALK) || spacewalk || ..())
return TRUE
var/atom/movable/backup = get_spacemove_backup()
if(backup)
+6
View File
@@ -160,3 +160,9 @@
var/current_tiles = 10 / max(existing, world.tick_lag)
var/minimum_speed = 10 / min(max(SAMT.config_entry_value, current_tiles), current_tiles + SMTI.config_entry_value)
. = min(., max(minimum_speed, existing - SSI.config_entry_value))
/datum/movespeed_modifier/dragon_rage
multiplicative_slowdown = -0.5
/datum/movespeed_modifier/dragon_depression
multiplicative_slowdown = 5
+1 -7
View File
@@ -1389,13 +1389,7 @@
charging = APC_NOT_CHARGING
chargecount = 0
longtermpower = max(-10,longtermpower - 2)
if(cell.charge >= cur_used)
cell.use(GLOB.CELLRATE * cur_used)
else
// This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room.
equipment = autoset(equipment, 0)
lighting = autoset(lighting, 0)
environ = autoset(environ, 0)
cell.use(min(GLOB.CELLRATE * cur_used, cell.charge))
// set channels based on remaining charge
@@ -2196,7 +2196,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
if(prob(10))
stored_teleports += rand(2,6)
if(prob(70))
M.vomit()
M.vomit(vomit_type = VOMIT_PURPLE)
return ..()
/datum/reagent/consumable/ethanol/planet_cracker
@@ -2593,7 +2593,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
alcohol_description = "sweet"
else
alcohol_description = "watery" //How the hell did you get negative boozepwr?
var/list/fruits = list()
if(names_in_order.len <= 3)
fruits = names_in_order
@@ -735,7 +735,7 @@
/datum/reagent/toxin/spewium/on_mob_life(mob/living/carbon/C)
.=..()
if(current_cycle >=11 && prob(min(50,current_cycle)))
C.vomit(10, prob(10), prob(50), rand(0,4), TRUE, prob(30))
C.vomit(10, prob(10), prob(50), rand(0,4), TRUE)
for(var/datum/reagent/toxin/R in C.reagents.reagent_list)
if(R != src)
C.reagents.remove_reagent(R.type,1)
@@ -72,7 +72,6 @@
R.stun(20)
R.reveal(100)
R.adjustHealth(50)
sleep(20)
for(var/mob/living/carbon/C in get_hearers_in_view(round(multiplier/48,1),get_turf(holder.my_atom)))
if(iscultist(C))
to_chat(C, "<span class='userdanger'>The divine explosion sears you!</span>")
@@ -433,20 +432,24 @@
var/T1 = multiplier * 20 //100 units : Zap 3 times, with powers 2000/5000/12000. Tesla revolvers have a power of 10000 for comparison.
var/T2 = multiplier * 50
var/T3 = multiplier * 120
sleep(5)
var/added_delay = 0.5 SECONDS
if(multiplier >= 75)
tesla_zap(holder.my_atom, 7, T1, zap_flags)
playsound(holder.my_atom, 'sound/machines/defib_zap.ogg', 50, 1)
sleep(15)
addtimer(CALLBACK(src, .proc/zappy_zappy, holder, T1), added_delay)
added_delay += 1.5 SECONDS
if(multiplier >= 40)
tesla_zap(holder.my_atom, 7, T2, zap_flags)
playsound(holder.my_atom, 'sound/machines/defib_zap.ogg', 50, 1)
sleep(15)
addtimer(CALLBACK(src, .proc/zappy_zappy, holder, T2), added_delay)
added_delay += 1.5 SECONDS
if(multiplier >= 10) //10 units minimum for lightning, 40 units for secondary blast, 75 units for tertiary blast.
tesla_zap(holder.my_atom, 7, T3, zap_flags)
playsound(holder.my_atom, 'sound/machines/defib_zap.ogg', 50, 1)
addtimer(CALLBACK(src, .proc/zappy_zappy, holder, T3), added_delay)
..()
/datum/chemical_reaction/reagent_explosion/teslium_lightning/proc/zappy_zappy(datum/reagents/holder, power)
if(QDELETED(holder.my_atom))
return
tesla_zap(holder.my_atom, 7, power, zap_flags)
playsound(holder.my_atom, 'sound/machines/defib_zap.ogg', 50, TRUE)
/datum/chemical_reaction/reagent_explosion/teslium_lightning/heat
id = "teslium_lightning2"
required_temp = 474
@@ -3,6 +3,9 @@
var/deletes_extract = TRUE
/datum/chemical_reaction/slime/on_reaction(datum/reagents/holder)
use_slime_core(holder)
/datum/chemical_reaction/slime/proc/use_slime_core(datum/reagents/holder)
SSblackbox.record_feedback("tally", "slime_cores_used", 1, "type")
if(deletes_extract)
delete_extract(holder)
@@ -570,7 +573,9 @@
required_other = TRUE
/datum/chemical_reaction/slime/slimestop/on_reaction(datum/reagents/holder)
sleep(50)
addtimer(CALLBACK(src, .proc/slime_stop, holder), 5 SECONDS)
/datum/chemical_reaction/slime/slimestop/proc/slime_stop(datum/reagents/holder)
var/obj/item/slime_extract/sepia/extract = holder.my_atom
var/turf/T = get_turf(holder.my_atom)
new /obj/effect/timestop(T, null, null, null)
@@ -579,8 +584,7 @@
var/mob/lastheld = get_mob_by_key(holder.my_atom.fingerprintslast)
if(lastheld && !lastheld.equip_to_slot_if_possible(extract, SLOT_HANDS, disable_warning = TRUE))
extract.forceMove(get_turf(lastheld))
..()
use_slime_core(holder)
/datum/chemical_reaction/slime/slimecamera
name = "Slime Camera"
@@ -184,10 +184,11 @@
/datum/design/syringe
name = "Syringe"
id = "syringe"
build_type = AUTOLATHE
build_type = AUTOLATHE | PROTOLATHE
materials = list(/datum/material/iron = 10, /datum/material/glass = 20)
build_path = /obj/item/reagent_containers/syringe
category = list("initial", "Medical")
category = list("initial", "Medical","Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/health_sensor
name = "Health Sensor"
@@ -201,6 +201,15 @@
build_path = /obj/item/storage/hypospraykit // let's not summon new hyposprays thanks
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
/datum/design/hypospray/mkii
name = "Hypospray Mk. II"
id = "hypospray_mkii"
build_type = PROTOLATHE
materials = list(/datum/material/iron = 1600, /datum/material/glass = 1000)
build_path = /obj/item/hypospray/mkii
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
/datum/design/blood_bag
name = "Empty Blood Bag"
@@ -246,8 +246,8 @@
category = list("Augmentation Nanites")
/datum/design/nanites/coagulating
name = "Rapid Coagulation"
desc = "The nanites induce rapid coagulation when the host is wounded, dramatically reducing bleeding rate."
name = "Vein Repressurization"
desc = "The nanites re-route circulating blood away from open wounds, dramatically reducing bleeding rate."
id = "coagulating_nanites"
program_type = /datum/nanite_program/coagulating
category = list("Augmentation Nanites")
@@ -558,15 +558,15 @@
program_type = /datum/nanite_program/protocol/factory
category = list("Protocols_Nanites")
/datum/design/nanites/tinker
name = "Tinker Protocol"
desc = "Replication Protocol: the nanites learn to use metallic material in the host's bloodstream to speed up the replication process."
id = "tinker_nanites"
program_type = /datum/nanite_program/protocol/tinker
/datum/design/nanites/pyramid
name = "Pyramid Protocol"
desc = "Replication Protocol: the nanites implement an alternate cooperative replication protocol that is more efficient as long as the saturation level is above 80%."
id = "pyramid_nanites"
program_type = /datum/nanite_program/protocol/pyramid
category = list("Protocols_Nanites")
/datum/design/nanites/offline
name = "Offline Production Protocol"
name = "Eclipse Protocol"
desc = "Replication Protocol: while the host is asleep or otherwise unconcious, the nanites exploit the reduced interference to replicate more quickly."
id = "offline_nanites"
program_type = /datum/nanite_program/protocol/offline
@@ -578,3 +578,32 @@
id = "synergy_nanites"
program_type = /datum/nanite_program/protocol/synergy
category = list("Protocols_Nanites")
/datum/design/nanites/hive
name = "Hive Protocol"
desc = "Storage Protocol: the nanites use a more efficient grid arrangment for volume storage, increasing maximum volume in a host."
id = "hive_nanites"
program_type = /datum/nanite_program/protocol/hive
category = list("Protocols_Nanites")
/datum/design/nanites/zip
name = "Zip Protocol"
desc = "Storage Protocol: the nanites are disassembled and compacted when unused, greatly increasing the maximum volume while in a host. However, the process slows down the replication rate slightly."
id = "zip_nanites"
program_type = /datum/nanite_program/protocol/zip
category = list("Protocols_Nanites")
/datum/design/nanites/free_range
name = "Free-range Protocol"
desc = "Storage Protocol: the nanites discard their default storage protocols in favour of a cheaper and more organic approach. Reduces maximum volume, but increases the replication rate."
id = "free_range_nanites"
program_type = /datum/nanite_program/protocol/free_range
category = list("Protocols_Nanites")
/datum/design/nanites/unsafe_storage
name = "S.L.O. Protocol"
desc = "Storage Protocol: 'S.L.O.P.', or Storage Level Override Protocol, completely disables the safety measures normally present in nanites,\
allowing them to reach much higher saturation levels, but at the risk of causing internal damage to the host."
id = "unsafe_storage_nanites"
program_type = /datum/nanite_program/protocol/unsafe_storage
category = list("Protocols_Nanites")
@@ -4,6 +4,7 @@
icon = 'icons/obj/machines/research.dmi'
icon_state = "nanite_cloud_controller"
circuit = /obj/item/circuitboard/computer/nanite_cloud_controller
icon_screen = "nanite_cloud_controller_screen"
var/obj/item/disk/nanite_program/disk
var/list/datum/nanite_cloud_backup/cloud_backups = list()
@@ -144,6 +145,7 @@
cloud_program["rules"] = rules
if(LAZYLEN(rules))
cloud_program["has_rules"] = TRUE
cloud_program["all_rules_required"] = P.all_rules_required
var/list/extra_settings = P.get_extra_settings_frontend()
cloud_program["extra_settings"] = extra_settings
@@ -232,6 +234,15 @@
investigate_log("[key_name(usr)] removed rule [rule.display()] from program [P.name] in cloud #[current_view]", INVESTIGATE_NANITES)
. = TRUE
if("toggle_rule_logic")
var/datum/nanite_cloud_backup/backup = get_backup(current_view)
if(backup)
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
var/datum/component/nanites/nanites = backup.nanites
var/datum/nanite_program/P = nanites.programs[text2num(params["program_id"])]
P.all_rules_required = !P.all_rules_required
investigate_log("[key_name(usr)] edited rule logic for program [P.name] into [P.all_rules_required ? "All" : "Any"] in cloud #[current_view]", INVESTIGATE_NANITES)
. = TRUE
/datum/nanite_cloud_backup
var/cloud_id = 0
@@ -26,6 +26,14 @@
. = ..()
linked_techweb = SSresearch.science_tech
/obj/machinery/nanite_program_hub/update_overlays()
. = ..()
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
if((stat & (NOPOWER|MAINT|BROKEN)) || panel_open)
return
SSvis_overlays.add_vis_overlay(src, icon, "nanite_program_hub_on", layer, plane)
SSvis_overlays.add_vis_overlay(src, icon, "nanite_program_hub_on", EMISSIVE_LAYER, EMISSIVE_PLANE)
/obj/machinery/nanite_program_hub/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/disk/nanite_program))
var/obj/item/disk/nanite_program/N = I
@@ -11,6 +11,14 @@
flags_1 = HEAR_1
circuit = /obj/item/circuitboard/machine/nanite_programmer
/obj/machinery/nanite_programmer/update_overlays()
. = ..()
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
if((stat & (NOPOWER|MAINT|BROKEN)) || panel_open)
return
SSvis_overlays.add_vis_overlay(src, icon, "nanite_programmer_on", layer, plane)
SSvis_overlays.add_vis_overlay(src, icon, "nanite_programmer_on", EMISSIVE_LAYER, EMISSIVE_PLANE)
/obj/machinery/nanite_programmer/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/disk/nanite_program))
var/obj/item/disk/nanite_program/N = I
@@ -48,11 +48,12 @@
//Extra settings
///Don't ever override this or I will come to your house and stand menacingly behind a bush
var/list/extra_settings = list()
VAR_FINAL/list/extra_settings = list()
//Rules
//Rules that automatically manage if the program's active without requiring separate sensor programs
var/list/datum/nanite_rule/rules = list()
var/all_rules_required = TRUE //Whether all rules are required for positive condition or any of specified
/// Corruptable - able to have code/configuration changed
var/corruptable = TRUE
@@ -76,6 +77,9 @@
if(nanites)
nanites.programs -= src
nanites.permanent_programs -= src
for(var/datum/nanite_rule/rule as anything in rules)
rule.remove()
rules.Cut()
return ..()
/**
@@ -108,6 +112,7 @@
for(var/R in rules)
var/datum/nanite_rule/rule = R
rule.copy_to(target)
target.all_rules_required = all_rules_required
if(istype(target,src))
copy_extra_settings_to(target)
@@ -186,14 +191,17 @@
if(timer_shutdown_next && world.time > timer_shutdown_next)
deactivate()
timer_shutdown_next = 0
return
if(timer_trigger && world.time > timer_trigger_next)
trigger()
timer_trigger_next = world.time + timer_trigger
return
if(timer_trigger_delay_next && world.time > timer_trigger_delay_next)
trigger(delayed = TRUE)
timer_trigger_delay_next = 0
return
if(check_conditions() && consume_nanites(use_rate))
if(!passive_enabled)
@@ -203,14 +211,18 @@
if(passive_enabled)
disable_passive_effect()
//If false, disables active and passive effects, but doesn't consume nanites
//If false, disables active, passive effects, and triggers without consuming nanites
//Can be used to avoid consuming nanites for nothing
/datum/nanite_program/proc/check_conditions()
if (!LAZYLEN(rules))
return TRUE
for(var/R in rules)
var/datum/nanite_rule/rule = R
if(!rule.check_rule())
if(!all_rules_required && rule.check_rule())
return TRUE
if(all_rules_required && !rule.check_rule())
return FALSE
return TRUE
return all_rules_required ? TRUE : FALSE
//Constantly procs as long as the program is active
/datum/nanite_program/proc/active_effect()
@@ -235,6 +247,8 @@
return
if(world.time < next_trigger)
return
if(!check_conditions())
return
if(!consume_nanites(trigger_cost))
return
next_trigger = world.time + trigger_cooldown
@@ -251,18 +265,22 @@
if(program_flags & NANITE_EMP_IMMUNE)
return
if(prob(severity / 2))
host_mob.investigate_log("[src] nanite program received a software error due to emp.", INVESTIGATE_NANITES)
software_error()
/datum/nanite_program/proc/on_shock(shock_damage)
if(!(program_flags & NANITE_SHOCK_IMMUNE))
if(prob(10))
host_mob.investigate_log("[src] nanite program received a software error due to shock.", INVESTIGATE_NANITES)
software_error()
else if(prob(33))
host_mob.investigate_log("[src] nanite program was deleted due to shock.", INVESTIGATE_NANITES)
self_destruct()
/datum/nanite_program/proc/on_minor_shock()
if(!(program_flags & NANITE_SHOCK_IMMUNE))
if(prob(10))
host_mob.investigate_log("[src] nanite program received a software error due to minor shock.", INVESTIGATE_NANITES)
software_error()
/datum/nanite_program/proc/on_death()
@@ -273,10 +291,12 @@
type = rand(1,is_permanent()? 4 : 5)
switch(type)
if(1)
host_mob.investigate_log("[src] nanite program was deleted by software error.", INVESTIGATE_NANITES)
self_destruct() //kill switch
return
if(2) //deprogram codes
if(corruptable)
host_mob.investigate_log("[src] nanite program was de-programmed by software error.", INVESTIGATE_NANITES)
activation_code = 0
deactivation_code = 0
kill_code = 0
@@ -284,13 +304,16 @@
if(3)
if(error_flicking)
toggle() //enable/disable
host_mob.investigate_log("[src] nanite program was toggled by software error.", INVESTIGATE_NANITES)
if(4)
if(error_flicking && can_trigger)
host_mob.investigate_log("[src] nanite program was triggered by software error.", INVESTIGATE_NANITES)
trigger()
if(5) //Program is scrambled and does something different
if(corruptable)
var/rogue_type = pick(rogue_types)
var/datum/nanite_program/rogue = new rogue_type
host_mob.investigate_log("[src] nanite program was converted into [rogue.name] by software error.", INVESTIGATE_NANITES)
nanites.add_program(null, rogue, src)
self_destruct()
@@ -315,9 +338,7 @@
if(is_permanent())
return
qdel(src)
///A nanite program containing a behaviour protocol. Only one protocol of each class can be active at once.
//Moved to being 'normally' researched due to lack of B.E.P.I.S.
/datum/nanite_program/protocol
name = "Nanite Protocol"
var/protocol_class = NONE
@@ -337,4 +358,3 @@
if(nanites)
nanites.protocols -= src
return ..()
@@ -2,7 +2,7 @@
/datum/nanite_program/regenerative
name = "Accelerated Regeneration"
desc = "The nanites boost the host's natural regeneration, increasing their healing speed. Does not consume nanites if the host is unharmed."
desc = "The nanites boost the host's natural regeneration, increasing their healing speed. Will not consume nanites while the host is unharmed."
use_rate = 0.5
rogue_types = list(/datum/nanite_program/necrotic)
@@ -31,7 +31,7 @@
/datum/nanite_program/temperature
name = "Temperature Adjustment"
desc = "The nanites adjust the host's internal temperature to an ideal level."
desc = "The nanites adjust the host's internal temperature to an ideal level. Will not consume nanites while the host is at a normal body temperature."
use_rate = 3.5
rogue_types = list(/datum/nanite_program/skin_decay)
@@ -53,10 +53,12 @@
rogue_types = list(/datum/nanite_program/suffocating, /datum/nanite_program/necrotic)
/datum/nanite_program/purging/check_conditions()
. = ..()
if(!. || !host_mob.reagents)
return FALSE // No trying to purge simple mobs
var/foreign_reagent = length(host_mob.reagents?.reagent_list)
if(!host_mob.getToxLoss() && !foreign_reagent)
return FALSE
return ..()
/datum/nanite_program/purging/active_effect()
host_mob.adjustToxLoss(-1)
@@ -68,7 +70,7 @@
/datum/nanite_program/brain_heal
name = "Neural Regeneration"
desc = "The nanites fix neural connections in the host's brain, reversing brain damage and minor traumas."
desc = "The nanites fix neural connections in the host's brain, reversing brain damage and minor traumas. Will not consume nanites while it would not have an effect."
use_rate = 1.5
rogue_types = list(/datum/nanite_program/brain_decay)
@@ -91,7 +93,7 @@
/datum/nanite_program/blood_restoring
name = "Blood Regeneration"
desc = "The nanites stimulate and boost blood cell production in the host."
desc = "The nanites stimulate and boost blood cell production in the host. Will not consume nanites while the host has a safe blood level."
use_rate = 1
rogue_types = list(/datum/nanite_program/suffocating)
@@ -111,7 +113,7 @@
/datum/nanite_program/repairing
name = "Mechanical Repair"
desc = "The nanites fix damage in the host's mechanical limbs."
desc = "The nanites fix damage in the host's mechanical limbs. Will not consume nanites while the host's mechanical limbs are undamaged, or while the host has no mechanical limbs."
use_rate = 0.5
rogue_types = list(/datum/nanite_program/necrotic)
@@ -153,13 +155,15 @@
rogue_types = list(/datum/nanite_program/suffocating, /datum/nanite_program/necrotic)
/datum/nanite_program/purging_advanced/check_conditions()
. = ..()
if(!. || !host_mob.reagents)
return FALSE
var/foreign_reagent = FALSE
for(var/datum/reagent/toxin/R in host_mob.reagents.reagent_list)
foreign_reagent = TRUE
break
if(!host_mob.getToxLoss() && !foreign_reagent)
return FALSE
return ..()
/datum/nanite_program/purging_advanced/active_effect()
host_mob.adjustToxLoss(-1, forced = TRUE)
@@ -1,7 +1,8 @@
//Replication Protocols
/datum/nanite_program/protocol/kickstart
name = "Kickstart Protocol"
desc = "Replication Protocol: the nanites focus on early growth, heavily boosting replication rate for a few minutes after the initial implantation."
desc = "Replication Protocol: the nanites focus on early growth, heavily boosting replication rate for a few minutes after the initial implantation, \
resulting in an additional 420 nanite volume being produced during the first two minutes."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_REPLICATION
@@ -17,8 +18,9 @@
/datum/nanite_program/protocol/factory
name = "Factory Protocol"
desc = "Replication Protocol: the nanites build a factory matrix within the host, gradually increasing replication speed over time. \
The factory decays if the protocol is not active, or if the nanites are disrupted by shocks or EMPs."
desc = "Replication Protocol: the nanites build a factory matrix within the host, gradually increasing replication speed over time, \
granting a maximum of 2 additional nanite production after roughly 17 minutes. \
The factory decays if the protocol is not active, or if the nanites are disrupted by shocks or EMPs."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_REPLICATION
@@ -46,43 +48,26 @@
factory_efficiency = min(factory_efficiency + 1, max_efficiency)
nanites.adjust_nanites(null, round(0.002 * factory_efficiency, 0.1))
/datum/nanite_program/protocol/tinker
name = "Tinker Protocol"
desc = "Replication Protocol: the nanites learn to use metallic material in the host's bloodstream to speed up the replication process."
/datum/nanite_program/protocol/pyramid
name = "Pyramid Protocol"
desc = "Replication Protocol: the nanites implement an alternate cooperative replication protocol that is active as long as the nanite saturation level is above 80%, \
resulting in an additional volume production of 1.2 per second."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_REPLICATION
var/boost = 2
var/list/valid_reagents = list(
/datum/reagent/iron,
/datum/reagent/copper,
/datum/reagent/gold,
/datum/reagent/silver,
/datum/reagent/mercury,
/datum/reagent/aluminium,
/datum/reagent/silicon)
var/boost = 1.2
/datum/nanite_program/protocol/tinker/check_conditions()
if(!nanites.host_mob.reagents)
/datum/nanite_program/protocol/pyramid/check_conditions()
if((nanites.nanite_volume / nanites.max_nanites) < 0.8)
return FALSE
var/found_reagent = FALSE
var/datum/reagents/R = nanites.host_mob.reagents
for(var/VR in valid_reagents)
if(R.has_reagent(VR, 0.5))
R.remove_reagent(VR, 0.5)
found_reagent = TRUE
break
if(!found_reagent)
return FALSE
return ..()
/datum/nanite_program/protocol/tinker/active_effect()
/datum/nanite_program/protocol/pyramid/active_effect()
nanites.adjust_nanites(null, boost)
/datum/nanite_program/protocol/offline
name = "Offline Production Protocol"
name = "Eclipse Protocol"
desc = "Replication Protocol: while the host is asleep or otherwise unconcious, the nanites exploit the reduced interference to replicate more quickly."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
@@ -105,7 +90,6 @@
/datum/nanite_program/protocol/offline/active_effect()
nanites.adjust_nanites(null, boost)
/datum/nanite_program/protocol/synergy
name = "Synergy Protocol"
desc = "Replication Protocol: the nanites syncronize their tasks and processes within a host, leading to an increase in replication speed proportional to the current nanite volume."
@@ -116,3 +100,195 @@
/datum/nanite_program/protocol/synergy/active_effect()
nanites.adjust_nanites(null, round(max_boost * (nanites.nanite_volume / nanites.max_nanites), 0.1))
/datum/nanite_program/protocol/hive
name = "Hive Protocol"
desc = "Storage Protocol: the nanites use a more efficient grid arrangment for volume storage, increasing maximum volume to 750."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_STORAGE
var/extra_volume = 250
/datum/nanite_program/protocol/hive/enable_passive_effect()
. = ..()
nanites.set_max_volume(null, nanites.max_nanites + extra_volume)
/datum/nanite_program/protocol/hive/disable_passive_effect()
. = ..()
nanites.set_max_volume(null, nanites.max_nanites - extra_volume)
/datum/nanite_program/protocol/zip
name = "Zip Protocol"
desc = "Storage Protocol: the nanites are disassembled and compacted when unused, increasing the maximum volume to 1000. However, the process slows down their replication rate slightly."
use_rate = 0.2
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_STORAGE
var/extra_volume = 500
/datum/nanite_program/protocol/zip/enable_passive_effect()
. = ..()
nanites.set_max_volume(null, nanites.max_nanites + extra_volume)
/datum/nanite_program/protocol/zip/disable_passive_effect()
. = ..()
nanites.set_max_volume(null, nanites.max_nanites - extra_volume)
/datum/nanite_program/protocol/free_range
name = "Free-range Protocol"
desc = "Storage Protocol: the nanites discard their default storage protocols in favour of a cheaper and more organic approach. Reduces maximum volume to 250, but increases the replication rate by 0.5."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_STORAGE
var/boost = 0.5
var/extra_volume = -250
/datum/nanite_program/protocol/free_range/enable_passive_effect()
. = ..()
nanites.set_max_volume(null, nanites.max_nanites + extra_volume)
/datum/nanite_program/protocol/free_range/disable_passive_effect()
. = ..()
nanites.set_max_volume(null, nanites.max_nanites - extra_volume)
/datum/nanite_program/protocol/free_range/active_effect()
nanites.adjust_nanites(null, boost)
/datum/nanite_program/protocol/unsafe_storage
name = "S.L.O. Protocol"
desc = "Storage Protocol: 'S.L.O.P.', or Storage Level Override Protocol, completely disables the safety measures normally present in nanites, \
allowing them to reach a whopping maximum volume level of 2000, but at the risk of causing damage to the host at nanite concentrations above the standard limit of 500."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_STORAGE
var/extra_volume = 1500
var/next_warning = 0
var/min_warning_cooldown = 120
var/max_warning_cooldown = 350
var/volume_warnings_stage_1 = list("You feel a dull pain in your abdomen.",
"You feel a tickling sensation in your abdomen.")
var/volume_warnings_stage_2 = list("You feel a dull pain in your stomach.",
"You feel a dull pain when breathing.",
"Your stomach grumbles.",
"You feel a tickling sensation in your throat.",
"You feel a tickling sensation in your lungs.",
"You feel a tickling sensation in your stomach.",
"Your lungs feel stiff.")
var/volume_warnings_stage_3 = list("You feel a dull pain in your chest.",
"You hear a faint buzzing coming from nowhere.",
"You hear a faint buzzing inside your head.",
"Your head aches.")
var/volume_warnings_stage_4 = list("You feel a dull pain in your ears.",
"You feel a dull pain behind your eyes.",
"You hear a loud, echoing buzz inside your ears.",
"You feel dizzy.",
"You feel an itch coming from behind your eyes.",
"Your eardrums itch.",
"You see tiny grey motes drifting in your field of view.")
var/volume_warnings_stage_5 = list("You feel sick.",
"You feel a dull pain from every part of your body.",
"You feel nauseous.")
var/volume_warnings_stage_6 = list("Your skin itches and burns.",
"Your muscles ache.",
"You feel tired.",
"You feel something skittering under your skin.",)
/datum/nanite_program/protocol/unsafe_storage/enable_passive_effect()
. = ..()
nanites.set_max_volume(null, nanites.max_nanites + extra_volume)
/datum/nanite_program/protocol/unsafe_storage/disable_passive_effect()
. = ..()
nanites.set_max_volume(null, nanites.max_nanites - extra_volume)
/datum/nanite_program/protocol/unsafe_storage/active_effect()
if(!iscarbon(host_mob))
if(prob(10))
host_mob.adjustBruteLoss(((max(nanites.nanite_volume - 450, 0) / 450) ** 2 ) * 0.5) // 0.5 -> 2 -> 4.5 -> 8 damage per successful tick
return
var/mob/living/carbon/C = host_mob
if(nanites.nanite_volume < 500)
return
var/current_stage = 0
if(nanites.nanite_volume > 500) //Liver is the main hub of nanite replication and the first to be threatened by excess volume
if(prob(10))
var/obj/item/organ/liver/liver = C.getorganslot(ORGAN_SLOT_LIVER)
if(liver)
liver.applyOrganDamage(0.6)
current_stage++
if(nanites.nanite_volume > 750) //Extra volume spills out in other central organs
if(prob(10))
var/obj/item/organ/stomach/stomach = C.getorganslot(ORGAN_SLOT_STOMACH)
if(stomach)
stomach.applyOrganDamage(0.75)
if(prob(10))
var/obj/item/organ/lungs/lungs = C.getorganslot(ORGAN_SLOT_LUNGS)
if(lungs)
lungs.applyOrganDamage(0.75)
current_stage++
if(nanites.nanite_volume > 1000) //Extra volume spills out in more critical organs
if(prob(10))
var/obj/item/organ/heart/heart = C.getorganslot(ORGAN_SLOT_HEART)
if(heart)
heart.applyOrganDamage(0.75)
if(prob(10))
var/obj/item/organ/brain/brain = C.getorganslot(ORGAN_SLOT_BRAIN)
if(brain)
brain.applyOrganDamage(0.75)
current_stage++
if(nanites.nanite_volume > 1250) //Excess nanites start invading smaller organs for more space, including sensory organs
if(prob(13))
var/obj/item/organ/eyes/eyes = C.getorganslot(ORGAN_SLOT_EYES)
if(eyes)
eyes.applyOrganDamage(0.75)
if(prob(13))
var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS)
if(ears)
ears.applyOrganDamage(0.75)
current_stage++
if(nanites.nanite_volume > 1500) //Nanites start spilling into the bloodstream, causing toxicity
if(prob(15))
C.adjustToxLoss(0.5, TRUE, forced = TRUE) //Not healthy for slimepeople either
current_stage++
if(nanites.nanite_volume > 1750) //Nanites have almost reached their physical limit, and the pressure itself starts causing tissue damage
if(prob(15))
C.adjustBruteLoss(0.75, TRUE)
current_stage++
volume_warning(current_stage)
/datum/nanite_program/protocol/unsafe_storage/proc/volume_warning(tier)
if(world.time < next_warning)
return
var/list/main_warnings
var/list/extra_warnings
switch(tier)
if(1)
main_warnings = volume_warnings_stage_1
extra_warnings = null
if(2)
main_warnings = volume_warnings_stage_2
extra_warnings = volume_warnings_stage_1
if(3)
main_warnings = volume_warnings_stage_3
extra_warnings = volume_warnings_stage_1 + volume_warnings_stage_2
if(4)
main_warnings = volume_warnings_stage_4
extra_warnings = volume_warnings_stage_1 + volume_warnings_stage_2 + volume_warnings_stage_3
if(5)
main_warnings = volume_warnings_stage_5
extra_warnings = volume_warnings_stage_1 + volume_warnings_stage_2 + volume_warnings_stage_3 + volume_warnings_stage_4
if(6)
main_warnings = volume_warnings_stage_6
extra_warnings = volume_warnings_stage_1 + volume_warnings_stage_2 + volume_warnings_stage_3 + volume_warnings_stage_4 + volume_warnings_stage_5
if(prob(35))
to_chat(host_mob, "<span class='warning'>[pick(main_warnings)]</span>")
next_warning = world.time + rand(min_warning_cooldown, max_warning_cooldown)
else if(islist(extra_warnings))
to_chat(host_mob, "<span class='warning'>[pick(extra_warnings)]</span>")
next_warning = world.time + rand(min_warning_cooldown, max_warning_cooldown)
@@ -235,8 +235,7 @@
/datum/nanite_program/sensor/voice
name = "Voice Sensor"
desc = "Sends a signal when the nanites hear a determined word or sentence."
var/spent = FALSE
desc = "The nanites receive a signal when they detect a specific, preprogrammed word or phrase being said."
/datum/nanite_program/sensor/voice/register_extra_settings()
. = ..()
@@ -248,16 +247,17 @@
RegisterSignal(host_mob, COMSIG_MOVABLE_HEAR, .proc/on_hear)
/datum/nanite_program/sensor/voice/on_mob_remove()
UnregisterSignal(host_mob, COMSIG_MOVABLE_HEAR, .proc/on_hear)
UnregisterSignal(host_mob, COMSIG_MOVABLE_HEAR)
/datum/nanite_program/sensor/voice/proc/on_hear(datum/source, list/hearing_args)
SIGNAL_HANDLER
var/datum/nanite_extra_setting/sentence = extra_settings[NES_SENTENCE]
var/datum/nanite_extra_setting/inclusive = extra_settings[NES_INCLUSIVE_MODE]
if(!sentence.get_value())
return
if(inclusive.get_value())
if(findtextEx(hearing_args[HEARING_RAW_MESSAGE], sentence))
if(findtext(hearing_args[HEARING_RAW_MESSAGE], sentence.get_value()))
send_code()
else
if(hearing_args[HEARING_RAW_MESSAGE] == sentence)
if(lowertext(hearing_args[HEARING_RAW_MESSAGE]) == lowertext(sentence.get_value()))
send_code()
@@ -2,7 +2,7 @@
/datum/nanite_program/sleepy
name = "Sleep Induction"
desc = "The nanites cause rapid narcolepsy when triggered."
desc = "The nanites induce rapid narcolepsy when triggered."
can_trigger = TRUE
trigger_cost = 15
trigger_cooldown = 1200
@@ -116,13 +116,13 @@
//Can receive transmissions from a nanite communication remote for customized messages
/datum/nanite_program/comm
can_trigger = TRUE
var/comm_code = 0
var/comm_message = ""
/datum/nanite_program/comm/register_extra_settings()
extra_settings[NES_COMM_CODE] = new /datum/nanite_extra_setting/number(0, 0, 9999)
/datum/nanite_program/comm/proc/receive_comm_signal(signal_comm_code, comm_message, comm_source)
var/datum/nanite_extra_setting/comm_code = extra_settings[NES_COMM_CODE]
if(!activated || !comm_code)
return
if(signal_comm_code == comm_code)
@@ -138,7 +138,8 @@
rogue_types = list(/datum/nanite_program/brain_misfire, /datum/nanite_program/brain_decay)
var/static/list/blacklist = list(
"*surrender",
"*collapse"
"*collapse",
"*faint",
)
/datum/nanite_program/comm/speech/register_extra_settings()
@@ -207,24 +207,23 @@
//Syncs the nanites with the cumulative current mob's access level. Can potentially wipe existing access.
/datum/nanite_program/access/on_trigger(comm_message)
var/list/new_access = list()
var/obj/item/current_item
current_item = host_mob.get_active_held_item()
if(current_item)
new_access += current_item.GetAccess()
current_item = host_mob.get_inactive_held_item()
if(current_item)
new_access += current_item.GetAccess()
var/list/potential_items = list()
potential_items += host_mob.get_active_held_item()
potential_items += host_mob.get_inactive_held_item()
if(ishuman(host_mob))
var/mob/living/carbon/human/H = host_mob
current_item = H.wear_id
if(current_item)
new_access += current_item.GetAccess()
potential_items += H.wear_id
else if(isanimal(host_mob))
potential_items += host_mob.pulling
var/mob/living/simple_animal/A = host_mob
current_item = A.access_card
if(current_item)
new_access += current_item.GetAccess()
potential_items += A.access_card
var/list/new_access = list()
for(var/obj/item/I in potential_items)
new_access += I.GetAccess()
access = new_access
/datum/nanite_program/spreading
@@ -253,6 +252,7 @@
//this will potentially take over existing nanites!
infectee.AddComponent(/datum/component/nanites, 10)
SEND_SIGNAL(infectee, COMSIG_NANITE_SYNC, nanites)
SEND_SIGNAL(infectee, COMSIG_NANITE_SET_CLOUD, nanites.cloud_id)
infectee.investigate_log("was infected by spreading nanites by [key_name(host_mob)] at [AREACOORD(infectee)].", INVESTIGATE_NANITES)
/datum/nanite_program/nanite_sting
@@ -277,13 +277,15 @@
//unlike with Infective Exo-Locomotion, this can't take over existing nanites, because Nanite Sting only targets non-hosts.
infectee.AddComponent(/datum/component/nanites, 5)
SEND_SIGNAL(infectee, COMSIG_NANITE_SYNC, nanites)
SEND_SIGNAL(infectee, COMSIG_NANITE_SET_CLOUD, nanites.cloud_id)
infectee.investigate_log("was infected by a nanite cluster by [key_name(host_mob)] at [AREACOORD(infectee)].", INVESTIGATE_NANITES)
to_chat(infectee, "<span class='warning'>You feel a tiny prick.</span>")
/datum/nanite_program/mitosis
name = "Mitosis"
desc = "The nanites gain the ability to self-replicate, using bluespace to power the process. Becomes more effective the more nanites are already in the host.\
The replication has also a chance to corrupt the nanite programming due to copy faults - cloud sync is highly recommended."
desc = "The nanites gain the ability to self-replicate, using bluespace to power the process. Becomes more effective the more nanites are already in the host; \
For every 50 nanite volume in the host, the production rate is increased by 0.5. The replication has also a chance to corrupt the nanite programming \
due to copy faults - constant cloud sync is highly recommended."
use_rate = 0
rogue_types = list(/datum/nanite_program/toxic)
@@ -306,16 +308,14 @@
/datum/nanite_program/dermal_button/register_extra_settings()
extra_settings[NES_SENT_CODE] = new /datum/nanite_extra_setting/number(1, 1, 9999)
extra_settings[NES_BUTTON_NAME] = new /datum/nanite_extra_setting/text("Button")
extra_settings[NES_ICON] = new /datum/nanite_extra_setting/type("power", list("one","two","three","four","five","plus","minus","power"))
extra_settings[NES_COLOR] = new /datum/nanite_extra_setting/type("green", list("green","red","yellow","blue"))
extra_settings[NES_ICON] = new /datum/nanite_extra_setting/type("power", list("blank","one","two","three","four","five","plus","minus","exclamation","question","cross","info","heart","skull","brain","brain_damage","injection","blood","shield","reaction","network","power","radioactive","electricity","magnetism","scan","repair","id","wireless","say","sleep","bomb"))
/datum/nanite_program/dermal_button/enable_passive_effect()
. = ..()
var/datum/nanite_extra_setting/bn_name = extra_settings[NES_BUTTON_NAME]
var/datum/nanite_extra_setting/bn_icon = extra_settings[NES_ICON]
var/datum/nanite_extra_setting/bn_color = extra_settings[NES_COLOR]
if(!button)
button = new(src, bn_name.get_value(), bn_icon.get_value(), bn_color.get_value())
button = new(src, bn_name.get_value(), bn_icon.get_value())
button.target = host_mob
button.Grant(host_mob)
@@ -339,14 +339,14 @@
name = "Button"
icon_icon = 'icons/mob/actions/actions_items.dmi'
check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
button_icon_state = "power_green"
button_icon_state = "nanite_power"
var/datum/nanite_program/dermal_button/program
/datum/action/innate/nanite_button/New(datum/nanite_program/dermal_button/_program, _name, _icon, _color)
/datum/action/innate/nanite_button/New(datum/nanite_program/dermal_button/_program, _name, _icon)
..()
program = _program
name = _name
button_icon_state = "[_icon]_[_color]"
button_icon_state = "nanite_[_icon]"
/datum/action/innate/nanite_button/Activate()
program.press()
@@ -45,8 +45,8 @@
/datum/nanite_program/aggressive_replication
name = "Aggressive Replication"
desc = "Nanites will consume organic matter to improve their replication rate, damaging the host. The efficiency increases with the volume of nanites, requiring 200 to break even."
use_rate = 0
desc = "Nanites will consume organic matter to improve their replication rate, damaging the host. The efficiency increases with the volume of nanites, requiring 200 to break even, \
and scaling linearly for a net positive of 0.1 production rate per 20 nanite volume beyond that."
rogue_types = list(/datum/nanite_program/necrotic)
/datum/nanite_program/aggressive_replication/active_effect()
@@ -104,6 +104,6 @@
// Default research tech, prevents bricking
design_ids = list("basic_matter_bin", "basic_cell", "basic_scanning", "basic_capacitor", "basic_micro_laser", "micro_mani", "desttagger", "handlabel", "packagewrap",
"destructive_analyzer", "circuit_imprinter", "experimentor", "rdconsole", "bepis", "design_disk", "tech_disk", "rdserver", "rdservercontrol", "mechfab", "paystand",
"space_heater", "beaker", "large_beaker", "xlarge_beaker", "bucket", "hypovial", "large_hypovial",
"space_heater", "beaker", "large_beaker", "xlarge_beaker", "bucket", "hypovial", "large_hypovial", "syringe", "pillbottle",
"sec_shellclip", "sec_beanbag", "sec_rshot", "sec_bshot", "sec_slug", "sec_islug", "sec_dart", "sec_38", "sec_38lethal",
"rglass","plasteel","plastitanium","plasmaglass","plasmareinforcedglass","titaniumglass","plastitaniumglass")
@@ -13,7 +13,7 @@
display_name = "Advanced Biotechnology"
description = "Advanced Biotechnology"
prereq_ids = list("biotech")
design_ids = list("piercesyringe", "crewpinpointer", "smoke_machine", "plasmarefiller", "limbgrower", "meta_beaker", "healthanalyzer_advanced", "harvester", "holobarrier_med", "defibrillator_compact", "smartdartgun", "medicinalsmartdart", "pHmeter", "containmentbodybag")
design_ids = list("piercesyringe", "crewpinpointer", "smoke_machine", "plasmarefiller", "limbgrower", "meta_beaker", "healthanalyzer_advanced", "harvester", "holobarrier_med", "defibrillator_compact", "smartdartgun", "medicinalsmartdart", "pHmeter", "hypospray_mkii", "containmentbodybag")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/bio_process
+3 -3
View File
@@ -40,7 +40,7 @@
if(5)
api_datum = /datum/tgs_api/v5
var/datum/tgs_version/max_api_version = TgsMaximumAPIVersion();
var/datum/tgs_version/max_api_version = TgsMaximumApiVersion();
if(version.suite != null && version.minor != null && version.patch != null && version.deprecated_patch != null && version.deprefixed_parameter > max_api_version.deprefixed_parameter)
TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
api_datum = /datum/tgs_api/latest
@@ -64,10 +64,10 @@
TGS_WRITE_GLOBAL(tgs, null)
TGS_ERROR_LOG("Failed to activate API!")
/world/TgsMaximumAPIVersion()
/world/TgsMaximumApiVersion()
return new /datum/tgs_version("5.x.x")
/world/TgsMinimumAPIVersion()
/world/TgsMinimumApiVersion()
return new /datum/tgs_version("3.2.x")
/world/TgsInitializationComplete()
+5 -5
View File
@@ -62,7 +62,7 @@
comms_key = world.params[SERVICE_WORLD_PARAM]
instance_name = world.params[SERVICE_INSTANCE_PARAM]
if(!instance_name)
instance_name = "TG Station Server" //maybe just upgraded
instance_name = "TG Station Server" //maybe just upgraded
var/list/logs = file2list(".git/logs/HEAD")
if(logs.len)
@@ -92,14 +92,14 @@
if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL))
TGS_ERROR_LOG("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.")
return
call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
return TRUE
/datum/tgs_api/v3210/OnTopic(T)
var/list/params = params2list(T)
var/their_sCK = params[SERVICE_CMD_PARAM_KEY]
if(!their_sCK)
return FALSE //continue world/Topic
return FALSE //continue world/Topic
if(their_sCK != comms_key)
return "Invalid comms key!";
@@ -160,7 +160,7 @@
var/datum/tgs_revision_information/test_merge/tm = new
tm.number = text2num(I)
var/list/entry = json[I]
tm.pull_request_commit = entry["commit"]
tm.head_commit = entry["commit"]
tm.author = entry["author"]
tm.title = entry["title"]
. += tm
@@ -176,7 +176,7 @@
return ri
/datum/tgs_api/v3210/EndProcess()
sleep(world.tick_lag) //flush the buffers
sleep(world.tick_lag) //flush the buffers
ExportService(SERVICE_REQUEST_KILL_PROCESS)
/datum/tgs_api/v3210/ChatChannelInfo()
+5 -5
View File
@@ -92,7 +92,7 @@
var/list/json = cached_json["testMerges"]
for(var/entry in json)
var/datum/tgs_revision_information/test_merge/tm = new
tm.time_merged = text2num(entry["timeMerged"])
tm.timestamp = text2num(entry["timeMerged"])
var/list/revInfo = entry["revision"]
if(revInfo)
@@ -104,7 +104,7 @@
tm.url = entry["url"]
tm.author = entry["author"]
tm.number = entry["number"]
tm.pull_request_commit = entry["pullRequestRevision"]
tm.head_commit = entry["pullRequestRevision"]
tm.comment = entry["comment"]
cached_test_merges += tm
@@ -118,7 +118,7 @@
var/list/params = params2list(T)
var/their_sCK = params[TGS4_INTEROP_ACCESS_IDENTIFIER]
if(!their_sCK)
return FALSE //continue world/Topic
return FALSE //continue world/Topic
if(their_sCK != access_identifier)
return "Invalid comms key!";
@@ -192,7 +192,7 @@
//request a new port
export_lock = FALSE
var/list/new_port_json = Export(TGS4_COMM_NEW_PORT, list(TGS4_PARAMETER_DATA = "[world.port]"), TRUE) //stringify this on purpose
var/list/new_port_json = Export(TGS4_COMM_NEW_PORT, list(TGS4_PARAMETER_DATA = "[world.port]"), TRUE) //stringify this on purpose
if(!new_port_json)
TGS_ERROR_LOG("No new port response from server![TGS4_PORT_CRITFAIL_MESSAGE]")
@@ -235,7 +235,7 @@
var/port = result[TGS4_PARAMETER_DATA]
if(!isnum(port))
return //this is valid, server may just want use to reboot
return //this is valid, server may just want use to reboot
if(port == 0)
//to byond 0 means any port and "none" means close vOv
+1
View File
@@ -79,6 +79,7 @@
#define DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES "chatResponses"
#define DMAPI5_REVISION_INFORMATION_COMMIT_SHA "commitSha"
#define DMAPI5_REVISION_INFORMATION_TIMESTAMP "timestamp"
#define DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA "originCommitSha"
#define DMAPI5_CHAT_USER_ID "id"
+15 -8
View File
@@ -18,7 +18,9 @@
var/initialized = FALSE
/datum/tgs_api/v5/ApiVersion()
return new /datum/tgs_version(TGS_DMAPI_VERSION)
return new /datum/tgs_version(
#include "interop_version.dm"
)
/datum/tgs_api/v5/OnWorldNew(minimum_required_security_level)
server_port = world.params[DMAPI5_PARAM_SERVER_PORT]
@@ -48,6 +50,7 @@
if(istype(revisionData))
revision = new
revision.commit = revisionData[DMAPI5_REVISION_INFORMATION_COMMIT_SHA]
revision.timestamp = revisionData[DMAPI5_REVISION_INFORMATION_TIMESTAMP]
revision.origin_commit = revisionData[DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA]
else
TGS_ERROR_LOG("Failed to decode [DMAPI5_RUNTIME_INFORMATION_REVISION] from runtime information!")
@@ -63,15 +66,18 @@
if(revInfo)
tm.commit = revisionData[DMAPI5_REVISION_INFORMATION_COMMIT_SHA]
tm.origin_commit = revisionData[DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA]
tm.timestamp = entry[DMAPI5_REVISION_INFORMATION_TIMESTAMP]
else
TGS_WARNING_LOG("Failed to decode [DMAPI5_TEST_MERGE_REVISION] from test merge #[tm.number]!")
tm.time_merged = text2num(entry[DMAPI5_TEST_MERGE_TIME_MERGED])
if(!tm.timestamp)
tm.timestamp = entry[DMAPI5_TEST_MERGE_TIME_MERGED]
tm.title = entry[DMAPI5_TEST_MERGE_TITLE_AT_MERGE]
tm.body = entry[DMAPI5_TEST_MERGE_BODY_AT_MERGE]
tm.url = entry[DMAPI5_TEST_MERGE_URL]
tm.author = entry[DMAPI5_TEST_MERGE_AUTHOR]
tm.pull_request_commit = entry[DMAPI5_TEST_MERGE_PULL_REQUEST_REVISION]
tm.head_commit = entry[DMAPI5_TEST_MERGE_PULL_REQUEST_REVISION]
tm.comment = entry[DMAPI5_TEST_MERGE_COMMENT]
test_merges += tm
@@ -98,18 +104,19 @@
return json_encode(response)
/datum/tgs_api/v5/OnTopic(T)
if(!initialized)
return FALSE //continue world/Topic
var/list/params = params2list(T)
var/json = params[DMAPI5_TOPIC_DATA]
if(!json)
return FALSE
return FALSE // continue to /world/Topic
var/list/topic_parameters = json_decode(json)
if(!topic_parameters)
return TopicResponse("Invalid topic parameters json!");
if(!initialized)
TGS_WARNING_LOG("Missed topic due to not being initialized: [T]")
return TRUE // too early to handle, but it's still our responsibility
var/their_sCK = topic_parameters[DMAPI5_PARAMETER_ACCESS_IDENTIFIER]
if(their_sCK != access_identifier)
return TopicResponse("Failed to decode [DMAPI5_PARAMETER_ACCESS_IDENTIFIER] from: [json]!");
@@ -266,7 +273,7 @@
var/port = result[DMAPI5_BRIDGE_RESPONSE_NEW_PORT]
if(!isnum(port))
return //this is valid, server may just want use to reboot
return //this is valid, server may just want use to reboot
if(port == 0)
//to byond 0 means any port and "none" means close vOv
+1
View File
@@ -0,0 +1 @@
"5.3.0"
+1
View File
@@ -79,6 +79,7 @@
#undef DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES
#undef DMAPI5_REVISION_INFORMATION_COMMIT_SHA
#undef DMAPI5_REVISION_INFORMATION_TIMESTAMP
#undef DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA
#undef DMAPI5_CHAT_USER_ID
+15
View File
@@ -0,0 +1,15 @@
/*!
* Copyright (c) 2021 Arm A. Hammer
* SPDX-License-Identifier: MIT
*/
/**
* tgui state: never_state
*
* Always closes the UI, no matter what. See the ui_state in religious_tool.dm to see an example
*/
GLOBAL_DATUM_INIT(never_state, /datum/ui_state/never_state, new)
/datum/ui_state/never_state/can_use_topic(src_object, mob/user)
return UI_CLOSE
+3 -1
View File
@@ -94,6 +94,8 @@
window.send_message("ping")
var/flush_queue = window.send_asset(get_asset_datum(
/datum/asset/simple/namespaced/fontawesome))
flush_queue |= window.send_asset(get_asset_datum(
/datum/asset/simple/namespaced/tgfont))
for(var/datum/asset/asset in src_object.ui_assets(user))
flush_queue |= window.send_asset(asset)
if (flush_queue)
@@ -241,7 +243,7 @@
* Run an update cycle for this UI. Called internally by SStgui
* every second or so.
*/
/datum/tgui/process(force = FALSE)
/datum/tgui/process(delta_time, force = FALSE)
if(closing)
return
var/datum/host = src_object.ui_host(user)
+2
View File
@@ -47,7 +47,9 @@
get_asset_datum(/datum/asset/simple/tgui_panel),
))
window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/fontawesome))
window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/tgfont))
window.send_asset(get_asset_datum(/datum/asset/spritesheet/chat))
// Other setup
request_telemetry()
addtimer(CALLBACK(src, .proc/on_initialize_timed_out), 5 SECONDS)
+2 -2
View File
@@ -256,7 +256,7 @@
/obj/vehicle/ridden/scooter/wheelys/Initialize()
. = ..()
var/datum/component/riding/D = LoadComponent(/datum/component/riding)
D.vehicle_move_delay = 0
D.vehicle_move_delay = 1
D.set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER)
D.set_vehicle_dir_layer(NORTH, OBJ_LAYER)
D.set_vehicle_dir_layer(EAST, OBJ_LAYER)
@@ -280,7 +280,7 @@
unbuckle_mob(H)
H.throw_at(throw_target, 4, 3)
H.DefaultCombatKnockdown(30)
H.adjustStaminaLoss(10)
H.adjustStaminaLoss(30)
var/head_slot = H.get_item_by_slot(SLOT_HEAD)
if(!head_slot || !(istype(head_slot,/obj/item/clothing/head/helmet) || istype(head_slot,/obj/item/clothing/head/hardhat)))
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 1)
+2
View File
@@ -276,6 +276,8 @@
if(href_list["outsidepick"])
var/atom/movable/tgt = locate(href_list["outsidepick"])
var/obj/belly/OB = locate(href_list["outsidebelly"])
if(!istype(OB))
return
if(!(tgt in OB)) //Aren't here anymore, need to update menu.
return TRUE
var/intent = "Examine"
+108 -129
View File
@@ -50,6 +50,114 @@
-->
<div class="commit sansserif">
<h2 class="date">25 June 2021</h2>
<h3 class="author">MrJWhit updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Adds two missing decals to the 5x5 SM.</li>
</ul>
<h3 class="author">brokenOculus updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">pillbottles and syringesare now printable from the medbay protolathe, shiftstart. Hyposprays are now printable in medbay lathe under advanced biotech.</li>
</ul>
<h2 class="date">24 June 2021</h2>
<h3 class="author">WanderingFox95 updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">An announcement to let players know the Cat Surgeon has come to visit.</li>
<li class="balance">Upped the Volume of his spawn noise and lowered the spawn weight slightly.</li>
</ul>
<h2 class="date">23 June 2021</h2>
<h3 class="author">DeltaFire15 updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">A bunch of small nanite things should be less wonky</li>
<li class="bugfix">Slimes are no longer immune to vomiting (undocumented change from a previous PR)</li>
<li class="bugfix">Fruit wine exists again.</li>
<li class="code_imp">Airlock hacking no longer sleeps.</li>
<li class="code_imp">The clockwork gateway deconstruction no longer sleeps.</li>
<li class="code_imp">Teslium reactions and Holywater booms no longer sleep.</li>
<li class="code_imp">Slime timestop no longer sleeps.</li>
</ul>
<h3 class="author">Putnam3145 updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">inelastic exports no longer uselessly do exponential functions</li>
</ul>
<h2 class="date">22 June 2021</h2>
<h3 class="author">bunny232 updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Adds a missing win door to meta xenobiology pen 6</li>
</ul>
<h3 class="author">silicons updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">no more doubleroasting</li>
</ul>
<h2 class="date">21 June 2021</h2>
<h3 class="author">silicons updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">glowshroom scaling</li>
</ul>
<h3 class="author">timothyteakettle updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">vore is 0.1% less shitcode</li>
</ul>
<h2 class="date">20 June 2021</h2>
<h3 class="author">Arturlang updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Port's TG's nanite storage modification programs from the bepis</li>
<li class="bugfix">Fixes infective nanite progreams not syncing cloud ID.</li>
<li class="rscadd">Add's off icons for nanite machinery</li>
<li class="bugfix">Fixes antitox nanites runtiming on simplemobs</li>
<li class="rscadd">Updates the nanite dermal button icon set</li>
<li class="rscadd">Adds the ability to select the logic for nanite rules</li>
<li class="bugfix">Nanite programs with triggers won't ignore rules.</li>
<li class="bugfix">Coagluating nanite program research no longer has the wrong name tweak: Nanite program's have better descriptions now tweak: Nanite subdermal ID's now also include pulled ID's for simplemobs</li>
<li class="bugfix">Nanite voice sensors should properly work now.</li>
<li class="bugfix">Fixes nanite comm remote, now they should actually work</li>
</ul>
<h3 class="author">EmeraldSundisk updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Adds ColorMates to Snow Taxi tweak: Slight adjustments near the arrival shuttle landing zone</li>
<li class="rscdel">Removes an undesired corporate entity</li>
</ul>
<h3 class="author">Nanotrasen Structual Engineering Division updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Added lables to the atmos tanks on Metastation.</li>
<li class="rscadd">Adjusted Pubbystation's emitter room wall layout to prevent light-breakage on startup of emitters.</li>
<li class="rscdel">Removed frestanding sink and showers from Metastation science airlock, and Deltastation Xenobio. Added an emergency shower next to the kill room.</li>
<li class="bugfix">Removed a leftover pipe in Metastation security hallway.</li>
</ul>
<h3 class="author">bunny232 updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Pressure tanks other then air tanks start with gas!</li>
</ul>
<h3 class="author">keronshb updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Adds the Space Dragon midround event</li>
<li class="soundadd">Space Dragon sounds</li>
<li class="imageadd">Space Dragon + effects</li>
<li class="code_imp">Added Spacewalk trait</li>
<li class="code_imp">Gibs the original owner if they are turned into a Space Dragon with the traitor panel</li>
<li class="admin">logging for Space Dragon creation</li>
</ul>
<h3 class="author">kiwedespars updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">a downside to wheely heelies ((made it actually detrimental))</li>
</ul>
<h3 class="author">zeroisthebiggay updated:</h3>
<ul class="changes bgimages16">
<li class="imageadd">beltslot sprites for various items</li>
<li class="imageadd">resprite for telebaton</li>
</ul>
<h2 class="date">19 June 2021</h2>
<h3 class="author">keronshb updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Ling Bone Gauntlets work again</li>
</ul>
<h2 class="date">17 June 2021</h2>
<h3 class="author">Vynzill updated:</h3>
<ul class="changes bgimages16">
@@ -512,135 +620,6 @@
<ul class="changes bgimages16">
<li class="admin">Admins just got a new TGUI Select Equipment menu tweak: Prevents the window from creating sprites for any animated version there might be. (this guarantees consistant sprite size/amount)</li>
</ul>
<h2 class="date">25 April 2021</h2>
<h3 class="author">DrPainis updated:</h3>
<ul class="changes bgimages16">
<li class="spellcheck">Bubblegum is now capitalized.</li>
</ul>
<h2 class="date">22 April 2021</h2>
<h3 class="author">Whoneedspacee updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">new arena attack where ash drake summons lava around you</li>
<li class="rscdel">removed old swooping above you, instead flies above you instantly</li>
<li class="balance">ash drake now spawns temporary lava pools instead of meteors falling down</li>
<li class="balance">ash drake takes twice as long to swoop down now that he instantly goes above you</li>
<li class="balance">ash drake now moves twice as fast</li>
<li class="balance">increases the odds of lava spawns in the lava pool attack</li>
<li class="balance">increases fire line damage and decreases lava attacks direct damage tweak: ash drake fire now shoots in the direction of the target tweak: changes times of certain animations tweak: changes sounds of meteor falling to lava creation</li>
<li class="bugfix">a bug where ash drakes attacks did not damage mechs</li>
<li class="imageadd">changes meteor icon to lava creation animation from lava staff</li>
<li class="rscadd">Mass fire attack, sends fire out from the ash drake in all directions</li>
<li class="rscadd">Adds an enraged attack for ash drake, heals him as well as making him glow and go faster, spawning massive amounts of fire in all directions</li>
<li class="rscdel">Removes the old triple swoop with lava pools attack tweak: Lava pools can now spawn with the normal fire breath attack sometimes tweak: Lava pools now have changed delays for lesser amounts so they don't all just place around one area tweak: Increases default swoop delay</li>
<li class="balance">Teleporting out of the lava arena now has some actual consequences by enraging the ash drake</li>
<li class="bugfix">Makes lava arena a bit less laggy by not recalculating range_turfs every time</li>
<li class="bugfix">Fixes the arena attack selecting inaccessible tiles as the safe tile though this will not change the turfs to basalt temporarily to prevent moving through indestructible walls</li>
<li class="bugfix">Fire lines would not spawn if their range would place their final turf location outside of the map</li>
<li class="bugfix">The arena attack will no longer destroy indestructible open turfs</li>
<li class="balance">ash drake fire does less damage now</li>
<li class="balance">ash drake takes longer to swoop down now</li>
<li class="balance">tiles take longer to fully convert into lava now, slowing down the arena attack as well</li>
<li class="balance">fire breath now moves slower</li>
<li class="balance">triple fire breath for the lava swoop only happens below half health now</li>
<li class="bugfix">The arena attack not making safespots when you fight it in a mech</li>
</ul>
<h2 class="date">21 April 2021</h2>
<h3 class="author">necromanceranne updated:</h3>
<ul class="changes bgimages16">
<li class="balance">Stun batons (not police batons/telebatons) no longer knockdown on leftclick.</li>
<li class="balance">Stun batons apply a knockdown and tase effect on right click, but once every few seconds (they still don't disarm). They are vulnerable to a shove disarm briefly, however. Standard batons have a cooldown of 5 seconds. Stun prods have a cooldown of 7 seconds.</li>
<li class="balance">Taser resistance prevents the knockdown, so any chem that grants this (like adrenals) protects you from this knockdown.</li>
<li class="balance">Stun batons apply a stagger when they hit someone, preventing sprinting for a few seconds.</li>
<li class="balance">Stun batons respect melee armor for their stamina damage, but their cells, based on max charge, grant armor penetration. For every 1000 charge, they gain 1 armor pen. (Roundstart batons have 15 pen, just fyi)</li>
<li class="balance">Shoves can disarm you of any item, not just guns.</li>
<li class="bugfix">Removes a duplicate trait definition for TRAIT_NICE_SHOT.</li>
</ul>
<h2 class="date">20 April 2021</h2>
<h3 class="author">BlueWildrose updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">New slimeperson organs that aren't really that different from humans for now.</li>
<li class="imageadd">Some blue organs for slimepeople.</li>
<li class="rscadd">Space pirate sleepers can now be crowbared to be destroyed.</li>
</ul>
<h3 class="author">DrPainis updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">ash drake meat</li>
</ul>
<h3 class="author">Hatterhat updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Plastitanium glass now properly applies the *2 bonus for integrity and efficiency when used as a solar panel.</li>
</ul>
<h3 class="author">HeroWithYay updated:</h3>
<ul class="changes bgimages16">
<li class="imageadd">replaced some icons</li>
</ul>
<h3 class="author">Putnam3145 updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Bluespace pipes, which can teleport gas over long distances</li>
<li class="rscadd">Donk co traitor class (assassin-heavy)</li>
<li class="rscadd">Waffle co traitor class (freeform)</li>
<li class="rscadd">Admin-only activity tracking system only attached to antags for now tweak: Objective rerolling can now be done twice</li>
<li class="bugfix">Sabotage objectives won't give "free objective" anymore</li>
</ul>
<h3 class="author">The0bserver, TripleZeta, and AsciiSquid updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">New, easily concealable weapons, chambered in .38, .357, and .45-70 Govt. Fun for the whole family!</li>
<li class="rscadd">Some smugglers seem to have acquired a high amount of .38 derringers, and are looking to offload them to those of gray morality, with no questions asked!</li>
<li class="rscadd">An enigmatic gun collector has seen fit to do special acquisition work for the Gorlex Marauders, selling the fruits of his labor for a premium price. If you have the right electomagnetic sequence, you might be able to contact him to acquire a piece of his armory.</li>
</ul>
<h3 class="author">coiax updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Nuke ops can now purchase a box of "deathrattle implants". When an implanted person dies, all the other users of the implant will get a message, saying who died and where they died.</li>
</ul>
<h3 class="author">keronshb updated:</h3>
<ul class="changes bgimages16">
<li class="balance">Weight per blood is .03 now instead of .05</li>
<li class="balance">Dragnet Snare breakout timer is now 2.5 seconds down from 5 seconds.</li>
</ul>
<h3 class="author">qweq12yt updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Fixed a bug where some cargo crates would never arrive and still charge users</li>
</ul>
<h3 class="author">zeroisthebiggay updated:</h3>
<ul class="changes bgimages16">
<li class="rscdel">the box ghost burger</li>
</ul>
<h2 class="date">18 April 2021</h2>
<h3 class="author">BlueWildrose updated:</h3>
<ul class="changes bgimages16">
<li class="balance">(TGport-Kriskog) Reduced blight cost to 75, more in line with its underwhelming nature. tweak: (TGport-Kriskog) Revenants now only use stolen essence to unlock new spells. No more counting corpses or waiting for regen before draining. tweak: (TGport-Kriskog) Spell unlock costs adjusted accordingly, defile upped from 0 to a cost of 10. tweak: (TGport-Kriskog) Drain targets in soft-crit will be stunned, to prevent them crawling away.</li>
<li class="bugfix">(TGport-ShizCalev) Fixed revenant's light overload ability not blowing lights in a square if there was another broken/burnt out/empty light in it.</li>
</ul>
<h3 class="author">DeltaFire15 updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Mechs now do not get drained an absurd amount of energy when EMPd.</li>
<li class="bugfix">Organic healing surgeries no longer show up for people without any organic bodyparts.</li>
</ul>
<h3 class="author">SandPoot updated:</h3>
<ul class="changes bgimages16">
<li class="rscadd">Adds a fancy TGUI interface for the cloning computer.</li>
<li class="rscdel">Destroys the old cloning interface. tweak: Alt-Click now removes disks from the cloning computer.</li>
<li class="refactor">Replaced way too much code for the cloning computer.</li>
<li class="refactor">Cloning scan's implant now outputs a list if desired.</li>
</ul>
<h2 class="date">16 April 2021</h2>
<h3 class="author">BlueWildrose updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">(TGport-Timberpoes) You can once again pay off the pirate event from the communications console without it silently failing for no obvious reason.</li>
<li class="bugfix">Fixed being unable to delete messages from the communication consoles message list save for the one on the bottom.</li>
<li class="bugfix">The data siphon that the space pirates have will no longer go invisible when it begins siphoning.</li>
<li class="bugfix">If the space pirate's "offer" has been rejected, there is now announcement feedback for if this does happen.</li>
</ul>
<h3 class="author">DeltaFire15 updated:</h3>
<ul class="changes bgimages16">
<li class="bugfix">Borgs can now use tank dispensers (again?)</li>
</ul>
</div>
<b>GoonStation 13 Development Team</b>
+75
View File
@@ -29495,3 +29495,78 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
timothyteakettle:
- bugfix: fixes an oversight causing embed jostling to do 2x as much damage as it
should
2021-06-19:
keronshb:
- bugfix: Ling Bone Gauntlets work again
2021-06-20:
Arturlang:
- rscadd: Port's TG's nanite storage modification programs from the bepis
- bugfix: Fixes infective nanite progreams not syncing cloud ID.
- rscadd: Add's off icons for nanite machinery
- bugfix: Fixes antitox nanites runtiming on simplemobs
- rscadd: Updates the nanite dermal button icon set
- rscadd: Adds the ability to select the logic for nanite rules
- bugfix: Nanite programs with triggers won't ignore rules.
- bugfix: 'Coagluating nanite program research no longer has the wrong name tweak:
Nanite program''s have better descriptions now tweak: Nanite subdermal ID''s
now also include pulled ID''s for simplemobs'
- bugfix: Nanite voice sensors should properly work now.
- bugfix: Fixes nanite comm remote, now they should actually work
EmeraldSundisk:
- rscadd: 'Adds ColorMates to Snow Taxi tweak: Slight adjustments near the arrival
shuttle landing zone'
- rscdel: Removes an undesired corporate entity
Nanotrasen Structual Engineering Division:
- rscadd: Added lables to the atmos tanks on Metastation.
- rscadd: Adjusted Pubbystation's emitter room wall layout to prevent light-breakage
on startup of emitters.
- rscdel: Removed frestanding sink and showers from Metastation science airlock,
and Deltastation Xenobio. Added an emergency shower next to the kill room.
- bugfix: Removed a leftover pipe in Metastation security hallway.
bunny232:
- bugfix: Pressure tanks other then air tanks start with gas!
keronshb:
- rscadd: Adds the Space Dragon midround event
- soundadd: Space Dragon sounds
- imageadd: Space Dragon + effects
- code_imp: Added Spacewalk trait
- code_imp: Gibs the original owner if they are turned into a Space Dragon with
the traitor panel
- admin: logging for Space Dragon creation
kiwedespars:
- rscadd: a downside to wheely heelies ((made it actually detrimental))
zeroisthebiggay:
- imageadd: beltslot sprites for various items
- imageadd: resprite for telebaton
2021-06-21:
silicons:
- bugfix: glowshroom scaling
timothyteakettle:
- bugfix: vore is 0.1% less shitcode
2021-06-22:
bunny232:
- bugfix: Adds a missing win door to meta xenobiology pen 6
silicons:
- bugfix: no more doubleroasting
2021-06-23:
DeltaFire15:
- bugfix: A bunch of small nanite things should be less wonky
- bugfix: Slimes are no longer immune to vomiting (undocumented change from a previous
PR)
- bugfix: Fruit wine exists again.
- code_imp: Airlock hacking no longer sleeps.
- code_imp: The clockwork gateway deconstruction no longer sleeps.
- code_imp: Teslium reactions and Holywater booms no longer sleep.
- code_imp: Slime timestop no longer sleeps.
Putnam3145:
- bugfix: inelastic exports no longer uselessly do exponential functions
2021-06-24:
WanderingFox95:
- rscadd: An announcement to let players know the Cat Surgeon has come to visit.
- balance: Upped the Volume of his spawn noise and lowered the spawn weight slightly.
2021-06-25:
MrJWhit:
- rscadd: Adds two missing decals to the 5x5 SM.
brokenOculus:
- rscadd: pillbottles and syringesare now printable from the medbay protolathe,
shiftstart. Hyposprays are now printable in medbay lathe under advanced biotech.
@@ -0,0 +1,4 @@
author: "Putnam3145"
delete-after: True
changes:
- bugfix: "APCs aren't infinite power anymore"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 959 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.
+3
View File
@@ -1736,6 +1736,7 @@
#include "code\modules\antagonists\slaughter\slaughter.dm"
#include "code\modules\antagonists\slaughter\slaughter_antag.dm"
#include "code\modules\antagonists\slaughter\slaughterevent.dm"
#include "code\modules\antagonists\space_dragon\space_dragon.dm"
#include "code\modules\antagonists\survivalist\survivalist.dm"
#include "code\modules\antagonists\swarmer\swarmer.dm"
#include "code\modules\antagonists\swarmer\swarmer_event.dm"
@@ -2105,6 +2106,7 @@
#include "code\modules\events\radiation_storm.dm"
#include "code\modules\events\sentience.dm"
#include "code\modules\events\shuttle_loan.dm"
#include "code\modules\events\space_dragon.dm"
#include "code\modules\events\spacevine.dm"
#include "code\modules\events\spider_infestation.dm"
#include "code\modules\events\spontaneous_appendicitis.dm"
@@ -2808,6 +2810,7 @@
#include "code\modules\mob\living\simple_animal\hostile\russian.dm"
#include "code\modules\mob\living\simple_animal\hostile\sharks.dm"
#include "code\modules\mob\living\simple_animal\hostile\skeleton.dm"
#include "code\modules\mob\living\simple_animal\hostile\space_dragon.dm"
#include "code\modules\mob\living\simple_animal\hostile\statue.dm"
#include "code\modules\mob\living\simple_animal\hostile\stickman.dm"
#include "code\modules\mob\living\simple_animal\hostile\syndicate.dm"
+39 -11
View File
@@ -1,15 +1,43 @@
rules:
## Enforce a maximum cyclomatic complexity allowed in a program
complexity: [error, { max: 25 }]
# complexity: [warn, { max: 25 }]
## Enforce consistent brace style for blocks
brace-style: [error, stroustrup, { allowSingleLine: false }]
# brace-style: [warn, stroustrup, { allowSingleLine: false }]
## Enforce the consistent use of either backticks, double, or single quotes
quotes: [error, single, {
avoidEscape: true,
allowTemplateLiterals: true,
}]
react/jsx-closing-bracket-location: [error, {
selfClosing: after-props,
nonEmpty: after-props,
}]
react/display-name: error
# quotes: [warn, single, {
# avoidEscape: true,
# allowTemplateLiterals: true,
# }]
# react/jsx-closing-bracket-location: [warn, {
# selfClosing: after-props,
# nonEmpty: after-props,
# }]
# react/display-name: warn
## Radar
## ------------------------------------------------------
# radar/cognitive-complexity: warn
radar/max-switch-cases: warn
radar/no-all-duplicated-branches: warn
radar/no-collapsible-if: warn
radar/no-collection-size-mischeck: warn
radar/no-duplicate-string: warn
radar/no-duplicated-branches: warn
radar/no-element-overwrite: warn
radar/no-extra-arguments: warn
radar/no-identical-conditions: warn
radar/no-identical-expressions: warn
radar/no-identical-functions: warn
radar/no-inverted-boolean-check: warn
radar/no-one-iteration-loop: warn
radar/no-redundant-boolean: warn
radar/no-redundant-jump: warn
radar/no-same-line-conditional: warn
radar/no-small-switch: warn
radar/no-unused-collection: warn
radar/no-use-of-empty-return-value: warn
radar/no-useless-catch: warn
radar/prefer-immediate-return: warn
radar/prefer-object-literal: warn
radar/prefer-single-boolean-return: warn
radar/prefer-while: warn
+2 -4
View File
@@ -9,9 +9,8 @@ env:
es6: true
browser: true
node: true
globals:
Byond: readonly
plugins:
- radar
- react
settings:
react:
@@ -20,7 +19,6 @@ rules:
## Possible Errors
## ----------------------------------------
## Enforce “for” loop update clause moving the counter in the right
## direction.
# for-direction: error
@@ -509,7 +507,7 @@ rules:
## Require braces around arrow function bodies
# arrow-body-style: error
## Require parentheses around arrow function arguments
arrow-parens: [error, as-needed]
# arrow-parens: [error, as-needed]
## Enforce consistent spacing before and after the arrow in arrow functions
arrow-spacing: [error, { before: true, after: true }]
## Require super() calls in constructors
+12
View File
@@ -0,0 +1,12 @@
arrowParens: always
bracketSpacing: true
endOfLine: lf
jsxBracketSameLine: true
jsxSingleQuote: false
printWidth: 80
proseWrap: preserve
quoteProps: preserve
semi: true
singleQuote: true
tabWidth: 2
trailingComma: es5

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