mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-30 16:47:13 +01:00
Sets prettier to run on the repo (#91379)
## About The Pull Request Prettier (an auto formatter) is set to only run within the tgui folder currently. This removes that limitation, allowing it to automatically format all supported files in the repo (.js, .html, .yml [etc](https://prettier.io/docs/)) I made a few exceptions for bundled and generated files ## Why It's Good For The Game I'm of the opinion that code should look uniform and am lazy enough to want CTRL-S to format files without having to think beyond that ## Changelog
This commit is contained in:
+5
-15
@@ -2,21 +2,11 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.py]
|
||||
indent_style = space
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Dockerfile]
|
||||
indent_style = space
|
||||
[*.{dm,json,md}]
|
||||
indent_style = tab
|
||||
|
||||
+33
-23
@@ -4,9 +4,9 @@
|
||||
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)
|
||||
1. [Headcoder](#headcoder)
|
||||
2. [Maintainers](#maintainers)
|
||||
3. [Issue Managers](#issue-managers)
|
||||
5. [Development Guides](#development-guides)
|
||||
6. [Pull Request Process](#pull-request-process)
|
||||
7. [Good Boy Points](#good-boy-points)
|
||||
@@ -80,13 +80,15 @@ Issue Managers help out the project by labelling bug reports and PRs and closing
|
||||
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, unless you are goofball.
|
||||
|
||||
- Label issues appropriately
|
||||
- Close issues when appropriate
|
||||
- Label PRs, unless you are goofball.
|
||||
|
||||
Things you **CAN'T** do:
|
||||
* [Close PRs](https://imgur.com/w2RqpX8.png): Only maintainers are allowed to close PRs. Do not hit that button.
|
||||
* Close issues purely for breaking a template if the same information is contained without it.
|
||||
|
||||
- [Close PRs](https://imgur.com/w2RqpX8.png): Only maintainers are allowed to close PRs. Do not hit that button.
|
||||
- Close issues purely for breaking a template if the same information is contained without it.
|
||||
|
||||
For more information reference the [Issue Manager Guide](./guides/ISSUE_MANAGER.md).
|
||||
|
||||
@@ -99,12 +101,15 @@ Our team is entirely voluntary, as such we extend our thanks to maintainers, iss
|
||||
## Development Guides
|
||||
|
||||
#### Writing readable code
|
||||
|
||||
[Style guide](./guides/STYLE.md)
|
||||
|
||||
#### Writing sane code
|
||||
|
||||
[Code standards](./guides/STANDARDS.md)
|
||||
|
||||
#### Writing understandable code
|
||||
|
||||
[Autodocumenting code](./guides/AUTODOC.md)
|
||||
|
||||
#### Misc
|
||||
@@ -124,38 +129,40 @@ Our team is entirely voluntary, as such we extend our thanks to maintainers, iss
|
||||
|
||||
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 here
|
||||
- Make sure your pull request complies to the requirements outlined here
|
||||
|
||||
* 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.
|
||||
- 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.
|
||||
|
||||
* 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.
|
||||
- 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).
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
* If your pull request is accepted, the code you add no longer belongs exclusively to you but to everyone; everyone is free to work on it, but you are also free to support or object to any changes being made, which will likely hold more weight, as you're the one who added the feature. It is a shame this has to be explicitly said, but there have been cases where this would've saved some trouble.
|
||||
- If your pull request is accepted, the code you add no longer belongs exclusively to you but to everyone; everyone is free to work on it, but you are also free to support or object to any changes being made, which will likely hold more weight, as you're the one who added the feature. It is a shame this has to be explicitly said, but there have been cases where this would've saved some trouble.
|
||||
|
||||
* 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
## Justifying Your Changes
|
||||
|
||||
You must explain why you are submitting the pull request in the "Why It's Good For The Game" section of your pull request, and how you think your change will be beneficial to the game. Failure to do so will be grounds for rejecting your pull request wholesale, or requiring that you fix it before your pull request is merged. A reasonable justification for your changes is a requirement.
|
||||
|
||||
Your "Why It's Good For The Game" section must make a good faith and reasonable attempt to:
|
||||
* Assert and argue that the current state of affairs in the game is not good, and needs changing.
|
||||
* Assert and argue that your pull request will either fix or help fix the problems you described.
|
||||
* Assert and argue that any downsides introduced by your solution as a matter of design, if any, are worth it, and why they are worth it.
|
||||
|
||||
- Assert and argue that the current state of affairs in the game is not good, and needs changing.
|
||||
- Assert and argue that your pull request will either fix or help fix the problems you described.
|
||||
- Assert and argue that any downsides introduced by your solution as a matter of design, if any, are worth it, and why they are worth it.
|
||||
|
||||
More controversial changes have higher standards for justification to be considered reasonable. A bugfix for example does not typically require any effort at all in justification as its value to the game is usually self evident, however a major feature overhaul or balance change may require significant explanation to adequately justify its supposed benefit to the game.
|
||||
|
||||
This is still a requirement if your pull request is supported and/or requested by maintainers before it is opened. This is still a requirement if your pull request is supported and/or requested by head coders before it is opened. The purpose of arguing for your changes is not to convince just the maintainer team of its merits, it is to document the "why" behind your changes to the game to a necessary level of detail. The reason behind a change must exist as it is the purpose of this codebase to improve the game, thus said reasoning must be adequately stated and explained.
|
||||
|
||||
This is also still a requirement if your pull request has a corresponding design document that justifies your changes inside it. You must always properly justify changes (those that actually need justification) within the pull request, even if you also do it elsewhere. This is to ensure that:
|
||||
|
||||
1. All reviewers can easily see the reasoning behind your changes on the pull request itself, no reliance on other sites required.
|
||||
2. The actual, manifested implementation of the idea behind the design document is being justified after said implementation is actually realized. This is in contrast to any reasoning put on the design document itself, which very well may have been made before any work was done on it, possibly even by an author different from the author of the pull request. Any idea in the design document may have had compromises put into it due to complications not seen in the original vision, thus the current state of the implementation (the pull request as it stands) must be defended, explained, and ultimately justified in and of itself. Of course, you should still list the design document the pull request is implementing, and may even use arguments from the design document if said arguments are applicable to the current reality of your proposed changes.
|
||||
|
||||
@@ -180,14 +187,17 @@ If you are porting features/tools from other codebases, you must give them credi
|
||||
Regarding sprites & sounds, you must credit the artist and possibly the codebase. All /tg/station assets including icons and sound are under a [Creative Commons 3.0 BY-SA license](https://creativecommons.org/licenses/by-sa/3.0/) unless otherwise indicated.
|
||||
|
||||
## 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 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).
|
||||
|
||||
- National Socialist Party of Germany content, National Socialist Party of Germany related content, or National Socialist Party of Germany references
|
||||
- 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.
|
||||
|
||||
## A word on Git
|
||||
|
||||
This repository uses `LF` line endings for all code as specified in the **.gitattributes** and **.editorconfig** files.
|
||||
|
||||
Unless overridden or a non standard git binary is used the line ending settings should be applied to your clone automatically.
|
||||
|
||||
@@ -5,7 +5,7 @@ description: Install Node using the version specified in `dependencies.sh`; addi
|
||||
|
||||
inputs:
|
||||
restore-yarn-cache:
|
||||
description: 'If `true`, restores the Yarn cache alongside installing node.'
|
||||
description: "If `true`, restores the Yarn cache alongside installing node."
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
+1
-1
@@ -23,4 +23,4 @@ reset_label = "GBP: Reset"
|
||||
"Sound" = 3
|
||||
"Sprites" = 3
|
||||
"Unit Tests" = 6
|
||||
"Wallening Revert Recovery" = 10
|
||||
"Wallening Revert Recovery" = 10
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# dmdoc
|
||||
|
||||
[DOCUMENTATION]: http://codedocs.tgstation13.org
|
||||
|
||||
[BYOND]: https://secure.byond.com/
|
||||
|
||||
[DMDOC]: https://github.com/SpaceManiac/SpacemanDMM/tree/master/crates/dmdoc
|
||||
|
||||
[DMDOC] is a documentation generator for DreamMaker, the scripting language
|
||||
@@ -16,6 +15,7 @@ This gives new developers a clickable reference [DOCUMENTATION] they can browse
|
||||
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.
|
||||
|
||||
@@ -25,22 +25,24 @@ We also require that when you touch older code, you must document the functions
|
||||
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
|
||||
A class _must_ always be autodocumented, and all public functions _must_ be documented
|
||||
|
||||
Internal functions *should* be documented, but may not be
|
||||
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 its definition
|
||||
|
||||
```
|
||||
/**
|
||||
* Short description of the proc
|
||||
@@ -54,12 +56,14 @@ the proc to jump to its definition
|
||||
```
|
||||
|
||||
### 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 its details
|
||||
|
||||
```
|
||||
/**
|
||||
* # Classname (Can be omitted if it's just going to be the typepath)
|
||||
@@ -74,13 +78,16 @@ Finally we give a longer multi paragraph description of the class and its detail
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
@@ -92,7 +99,9 @@ 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Required Tests (Continuous Integration)
|
||||
|
||||
> ℹ️ This is not the documentation for *writing* a test. You can find that in [the unit tests folder](../../code/modules/unit_tests/README.md).
|
||||
> ℹ️ This is not the documentation for _writing_ a test. You can find that in [the unit tests folder](../../code/modules/unit_tests/README.md).
|
||||
|
||||
Every pull request runs through a series of checks and tests to ensure its quality.
|
||||
|
||||
@@ -10,7 +10,7 @@ If after reading this guide you still do not understand why a check suite is fai
|
||||
|
||||
## Run Linters
|
||||
|
||||
The [linters](https://en.wikipedia.org/wiki/Lint_(software)) check the maps and code for common mistakes. This includes things like:
|
||||
The [linters](<https://en.wikipedia.org/wiki/Lint_(software)>) check the maps and code for common mistakes. This includes things like:
|
||||
|
||||
- Files not being included in the .dme
|
||||
- Misspelling Nanotrasen as NanoTrasen
|
||||
@@ -32,7 +32,7 @@ Linter failures are usually very easy to fix, and will hopefully be clear from t
|
||||
|
||||
## Compile Maps
|
||||
|
||||
This checks nothing more than that your code actually compiles, with slightly different requirements. Compile Maps forces all maps (including space ruins etc) to be compiled in, to make sure all of them are valid. If these tests pass, but other tests fail, it means your code *compiles* but not necessarily that it *works*.
|
||||
This checks nothing more than that your code actually compiles, with slightly different requirements. Compile Maps forces all maps (including space ruins etc) to be compiled in, to make sure all of them are valid. If these tests pass, but other tests fail, it means your code _compiles_ but not necessarily that it _works_.
|
||||
|
||||
## Integration Tests
|
||||
|
||||
@@ -44,7 +44,7 @@ Sometimes a test will fail on only one map, and not the others. This means two t
|
||||
|
||||
Screenshot tests exist to make sure things look the same before and after your commit. This helps us detect bugs such as humans not properly rendering clothing/limbs.
|
||||
|
||||
If your commit *does* change the appearance of something saved in a screenshot test, you will automatically receive a message on your PR showing you the before and after. From here, it will contain instructions for how to resolve the issue, whether it's a bug or intentional.
|
||||
If your commit _does_ change the appearance of something saved in a screenshot test, you will automatically receive a message on your PR showing you the before and after. From here, it will contain instructions for how to resolve the issue, whether it's a bug or intentional.
|
||||
|
||||
## Codeowner Reviews
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## 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://tgstation13.org/wiki/Downloading_the_source_code
|
||||
|
||||
Option 1:
|
||||
@@ -11,11 +12,10 @@ hassle if you want to make any changes at all, so it's not recommended.)
|
||||
|
||||
Option 3: Download a pre-compiled nightly at https://tgstation13.download/nightlies/ (same caveats as option 2)
|
||||
|
||||
*Warning: option 4 is out of date, and not maintained, use at your own risk*
|
||||
_Warning: option 4 is out of date, and not maintained, use at your own risk_
|
||||
|
||||
Option 4: Use our docker image that tracks the master branch (See commits for build status. Again, same caveats as option 2)
|
||||
|
||||
```
|
||||
docker run -d -p <your port>:1337 -v /path/to/your/config:/tgstation/config -v /path/to/your/data:/tgstation/data tgstation/tgstation <dream daemon options i.e. -public or -params>
|
||||
```
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Quickly setting up a development database with ezdb
|
||||
|
||||
While you do not need a database to code for tgstation, it is a prerequisite to many important features, especially on the admin side. Thus, if you are working in any code that benefits from it, it can be helpful to have one handy.
|
||||
|
||||
**ezdb** is a tool for quickly setting up an isolated development database. It will manage downloading MariaDB, creating the database, setting it up, and updating it when the code evolves. It is not recommended for use in production servers, but is perfect for quick development.
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
> Garbage collection is pretty gothic when you think about it.
|
||||
>
|
||||
>An object in code is like a ghost, clinging to its former life, and especially to the people it knew. It can only pass on and truly die when it has dealt with its unfinished business. And only when its been forgotten by everyone who ever knew it. If even one other object remembers it, it has a connection to the living world that lets it keep hanging on
|
||||
> An object in code is like a ghost, clinging to its former life, and especially to the people it knew. It can only pass on and truly die when it has dealt with its unfinished business. And only when its been forgotten by everyone who ever knew it. If even one other object remembers it, it has a connection to the living world that lets it keep hanging on
|
||||
>
|
||||
>There is a kind of sombre tone to fixing GC errors too, its almost shamanistic, making sure all these little objects clear up their final affairs in life before they die, to ensure they don't become ghosts
|
||||
> There is a kind of sombre tone to fixing GC errors too, its almost shamanistic, making sure all these little objects clear up their final affairs in life before they die, to ensure they don't become ghosts
|
||||
>
|
||||
> -- <cite>Nanako</cite>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
4. [Techniques for fixing hard deletes](#techniques-for-fixing-hard-deletes)
|
||||
5. [Help my code is erroring how fix](#help-my-code-is-erroring-how-fix)
|
||||
|
||||
|
||||
## What is Hard Deletion
|
||||
|
||||
Hard deletion is a very expensive operation that basically clears all references to some "thing" from memory. Objects that undergo this process are referred to as hard deletes, or simply harddels
|
||||
@@ -93,15 +92,15 @@ Now that you know the theory, let's go over what can actually cause hard deletes
|
||||
|
||||
The BYOND reference has a list [Here](https://secure.byond.com/docs/ref/#/DM/garbage), but it's not a complete one
|
||||
|
||||
* Stored in a var
|
||||
* An item in a list, or associated with a list item
|
||||
* Has a tag
|
||||
* Is on the map (always true for turfs)
|
||||
* Inside another atom's contents
|
||||
* Inside an atom's vis_contents
|
||||
* A temporary value in a still-running proc
|
||||
* Is a mob with a key
|
||||
* Is an image object attached to an atom
|
||||
- Stored in a var
|
||||
- An item in a list, or associated with a list item
|
||||
- Has a tag
|
||||
- Is on the map (always true for turfs)
|
||||
- Inside another atom's contents
|
||||
- Inside an atom's vis_contents
|
||||
- A temporary value in a still-running proc
|
||||
- Is a mob with a key
|
||||
- Is an image object attached to an atom
|
||||
|
||||
Let's briefly go over the more painful ones yeah?
|
||||
|
||||
@@ -284,6 +283,6 @@ If this fails, you're just gonna have to read over this doc. You can skip the th
|
||||
> -- <cite>Armhulenn</cite>
|
||||
|
||||
- The reference tracker, while powerful, is incredibly easy to break<br>
|
||||
If it weren't for those unit tests we'd still be missing list["a"] = list(ref)
|
||||
If it weren't for those unit tests we'd still be missing list["a"] = list(ref)
|
||||
- Everyone but me sucks, because everyone but me keeps adding new hard deletes
|
||||
- Garbage collection is a spook, best practice is to use a random reference in place of null, it scares the compiler demons
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## What is an Issue Manager
|
||||
|
||||
Issue Managers proactively manage issues for the repo by providing feedback, performing triage, and troubleshooting problems. They search through the codebase to link relevant code, issues, and PRs that help contributors identify and solve an issue.
|
||||
Issue Managers proactively manage issues for the repo by providing feedback, performing triage, and troubleshooting problems. They search through the codebase to link relevant code, issues, and PRs that help contributors identify and solve an issue.
|
||||
|
||||
## Triage An Issue
|
||||
|
||||
@@ -15,38 +15,41 @@ When examining new issues you should immediately notify a maintainer if you see
|
||||
- **Server Lagging** [[1]](https://github.com/tgstation/tgstation/issues/60193) [[2]](https://github.com/tgstation/tgstation/issues/51927) [[3]](https://github.com/tgstation/tgstation/issues/32762) - Something that is causing a _severe_ amount of lag during the game
|
||||
|
||||
#### Runtime Issue Reports
|
||||
|
||||
If an issue reports a runtime, it must have the actual runtime call stack provided by round logging or in-game debug menu (https://github.com/tgstation/tgstation/issues/70329#issuecomment-1279853883).
|
||||
|
||||
<details>
|
||||
<summary>Example runtime call stack</summary>
|
||||
|
||||
```
|
||||
[2022-10-15 16:12:38.902] runtime error: Cannot execute null.add().
|
||||
- proc name: visibility (/datum/cameranet/proc/visibility)
|
||||
- source file: cameranet.dm,88
|
||||
- usr: AI (/mob/living/silicon/ai)
|
||||
- src: Camera Net (/datum/cameranet)
|
||||
- usr.loc: the floor (150,25,4) (/turf/open/floor/circuit)
|
||||
- call stack:
|
||||
- Camera Net (/datum/cameranet): visibility(/list (/list), null, /list (/list), 1)
|
||||
- AI (/mob/living/silicon/ai): camera visibility(Inactive AI Eye (/mob/eye/camera/ai))
|
||||
- Inactive AI Eye (/mob/eye/camera/ai): setLoc(the floor (150,25,4) (/turf/open/floor/circuit), 0)
|
||||
- AI (/mob/living/silicon/ai): create eye()
|
||||
- AI (/mob/living/silicon/ai): Initialize(0, null, TagGamerGame2 (/mob/dead/new_player))
|
||||
- Atoms (/datum/controller/subsystem/atoms): InitAtom(AI (/mob/living/silicon/ai), 0, /list (/list))
|
||||
- AI (/mob/living/silicon/ai): New(0, null, TagGamerGame2 (/mob/dead/new_player))
|
||||
- AI (/mob/living/silicon/ai): New(the floor (150,25,4) (/turf/open/floor/circuit), null, TagGamerGame2 (/mob/dead/new_player))
|
||||
- /datum/job/ai (/datum/job/ai): get spawn mob(TagGamerGame2 (/client), AI (/obj/effect/landmark/start/ai))
|
||||
- TagGamerGame2 (/mob/dead/new_player): create character(AI (/obj/effect/landmark/start/ai))
|
||||
- Ticker (/datum/controller/subsystem/ticker): create characters()
|
||||
- Ticker (/datum/controller/subsystem/ticker): setup()
|
||||
- Ticker (/datum/controller/subsystem/ticker): fire(0)
|
||||
- Ticker (/datum/controller/subsystem/ticker): ignite(0)
|
||||
```
|
||||
```
|
||||
[2022-10-15 16:12:38.902] runtime error: Cannot execute null.add().
|
||||
- proc name: visibility (/datum/cameranet/proc/visibility)
|
||||
- source file: cameranet.dm,88
|
||||
- usr: AI (/mob/living/silicon/ai)
|
||||
- src: Camera Net (/datum/cameranet)
|
||||
- usr.loc: the floor (150,25,4) (/turf/open/floor/circuit)
|
||||
- call stack:
|
||||
- Camera Net (/datum/cameranet): visibility(/list (/list), null, /list (/list), 1)
|
||||
- AI (/mob/living/silicon/ai): camera visibility(Inactive AI Eye (/mob/eye/camera/ai))
|
||||
- Inactive AI Eye (/mob/eye/camera/ai): setLoc(the floor (150,25,4) (/turf/open/floor/circuit), 0)
|
||||
- AI (/mob/living/silicon/ai): create eye()
|
||||
- AI (/mob/living/silicon/ai): Initialize(0, null, TagGamerGame2 (/mob/dead/new_player))
|
||||
- Atoms (/datum/controller/subsystem/atoms): InitAtom(AI (/mob/living/silicon/ai), 0, /list (/list))
|
||||
- AI (/mob/living/silicon/ai): New(0, null, TagGamerGame2 (/mob/dead/new_player))
|
||||
- AI (/mob/living/silicon/ai): New(the floor (150,25,4) (/turf/open/floor/circuit), null, TagGamerGame2 (/mob/dead/new_player))
|
||||
- /datum/job/ai (/datum/job/ai): get spawn mob(TagGamerGame2 (/client), AI (/obj/effect/landmark/start/ai))
|
||||
- TagGamerGame2 (/mob/dead/new_player): create character(AI (/obj/effect/landmark/start/ai))
|
||||
- Ticker (/datum/controller/subsystem/ticker): create characters()
|
||||
- Ticker (/datum/controller/subsystem/ticker): setup()
|
||||
- Ticker (/datum/controller/subsystem/ticker): fire(0)
|
||||
- Ticker (/datum/controller/subsystem/ticker): ignite(0)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Downstream Issues Taken Upstream
|
||||
If an issue reports a bug encountered at a branch of the codebase or on a downstream server, it __MUST__ have a link to the branch or downstream codebase repo or it is eligible for closing (https://github.com/tgstation/tgstation/issues/70875#issuecomment-1295767891). Reproducing the issue on the compiled master of our codebase is also encouraged.
|
||||
|
||||
If an issue reports a bug encountered at a branch of the codebase or on a downstream server, it **MUST** have a link to the branch or downstream codebase repo or it is eligible for closing (https://github.com/tgstation/tgstation/issues/70875#issuecomment-1295767891). Reproducing the issue on the compiled master of our codebase is also encouraged.
|
||||
|
||||
<details>
|
||||
<summary>Image macro for your issue marking pleasure</summary>
|
||||
@@ -54,19 +57,20 @@ If an issue reports a bug encountered at a branch of the codebase or on a downst
|
||||

|
||||
|
||||
``
|
||||
|
||||
</details>
|
||||
|
||||
#### Link Code Snippets
|
||||
|
||||
To help triangulate bugs, search the GitHub repo to locate relevant code and attach it to an issue. Do this by creating a [link to the code](https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-a-permanent-link-to-a-code-snippet). This saves the contributors time from having to identify the problem and will be appreciated.
|
||||
To help triangulate bugs, search the GitHub repo to locate relevant code and attach it to an issue. Do this by creating a [link to the code](https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-a-permanent-link-to-a-code-snippet). This saves the contributors time from having to identify the problem and will be appreciated.
|
||||
|
||||
#### Use Gitblame
|
||||
|
||||
GitHub also has a tool called `gitblame` that is useful in [tracking code](https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#viewing-the-line-by-line-revision-history-for-a-file) to determine who and when someone made a change. This is ideally used to help solve old issues when there is uncertainty over which PR might have fixed it. It is also a good tool to use to link PRs that caused the issue.
|
||||
GitHub also has a tool called `gitblame` that is useful in [tracking code](https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#viewing-the-line-by-line-revision-history-for-a-file) to determine who and when someone made a change. This is ideally used to help solve old issues when there is uncertainty over which PR might have fixed it. It is also a good tool to use to link PRs that caused the issue.
|
||||
|
||||
#### Search For Keywords
|
||||
|
||||
When a new issue appears search for any keywords involved with the issue. This is important to prune for duplicates, match several issues to a test merge PR, or if you want to link multiple issues together since there is overlapping problems. (but not duplicate)
|
||||
When a new issue appears search for any keywords involved with the issue. This is important to prune for duplicates, match several issues to a test merge PR, or if you want to link multiple issues together since there is overlapping problems. (but not duplicate)
|
||||
|
||||
## Closing Issues
|
||||
|
||||
@@ -74,10 +78,10 @@ It is recommended to close issues in the following situations:
|
||||
|
||||
- **Feature Requests** [[1]](https://github.com/tgstation/tgstation/issues/55919) [[2]](https://github.com/tgstation/tgstation/issues/53342) [[3]](https://github.com/tgstation/tgstation/issues/45412) - The issue is a suggestion or request for a new feature to be added to the game.
|
||||
- **Working as Intended** [[1]](https://github.com/tgstation/tgstation/issues/62619) [[2]](https://github.com/tgstation/tgstation/issues/61511) [[3]](https://github.com/tgstation/tgstation/issues/60942) - The issue is detailing a problem that is _specifically intended_ by the code and is not considered a bug.
|
||||
- **Duplicates** [[1]](https://github.com/tgstation/tgstation/issues/62709) [[2]](https://github.com/tgstation/tgstation/issues/62364) [[3]](https://github.com/tgstation/tgstation/issues/61823) - The issue is detailing an identical problem from another issue. Do not automatically close the most recent issue. Instead compare both and close the one that provides the least information.
|
||||
- **Duplicates** [[1]](https://github.com/tgstation/tgstation/issues/62709) [[2]](https://github.com/tgstation/tgstation/issues/62364) [[3]](https://github.com/tgstation/tgstation/issues/61823) - The issue is detailing an identical problem from another issue. Do not automatically close the most recent issue. Instead compare both and close the one that provides the least information.
|
||||
- **Removed Features** [[1]](https://github.com/tgstation/tgstation/issues/48255) [[2]](https://github.com/tgstation/tgstation/issues/47194) [[3]](https://github.com/tgstation/tgstation/issues/45653) - The issue is referring to something that was removed from the codebase and no longer exists.
|
||||
- **Defective Issues** [[1]](https://github.com/tgstation/tgstation/issues/57366) [[2]](https://github.com/tgstation/tgstation/issues/48778) [[3]](https://github.com/tgstation/tgstation/issues/51520) - The issue is badly written and lacking information. Politely ask the person to add more information or rewrite the issue. If there is no response after a sufficient amount of time close the issue.
|
||||
- **Irreproducible Issues** [[1]](https://github.com/tgstation/tgstation/issues/51493) [[2]](https://github.com/tgstation/tgstation/issues/22796) [[3]](https://github.com/tgstation/tgstation/issues/25610) - The issue is old, cannot be reproduced, and nobody has reported a duplicate issue recently. If you feel _confident_ that the issue has been fixed at some point, list your reasons or link possible PRs that could have fixed it.
|
||||
- **Defective Issues** [[1]](https://github.com/tgstation/tgstation/issues/57366) [[2]](https://github.com/tgstation/tgstation/issues/48778) [[3]](https://github.com/tgstation/tgstation/issues/51520) - The issue is badly written and lacking information. Politely ask the person to add more information or rewrite the issue. If there is no response after a sufficient amount of time close the issue.
|
||||
- **Irreproducible Issues** [[1]](https://github.com/tgstation/tgstation/issues/51493) [[2]](https://github.com/tgstation/tgstation/issues/22796) [[3]](https://github.com/tgstation/tgstation/issues/25610) - The issue is old, cannot be reproduced, and nobody has reported a duplicate issue recently. If you feel _confident_ that the issue has been fixed at some point, list your reasons or link possible PRs that could have fixed it.
|
||||
- **Impossible to Fix Issues** [[1]](https://github.com/tgstation/tgstation/issues/524) [[2]](https://github.com/tgstation/tgstation/issues/2679) [[3]](https://github.com/tgstation/tgstation/issues/9637) - The issue is not possible to fix due to either vague details or a clearly defined problem.
|
||||
|
||||
## Reopening Issues
|
||||
@@ -88,4 +92,4 @@ In special cases a closed issue should be reopened if:
|
||||
- The initial problem has reappeared (after it was presumably fixed in a PR)
|
||||
- Someone feels that the issue was closed prematurely during discussion
|
||||
|
||||
If there is a dispute on whether an issue should remain closed, ask for a second opinion. Get clarification from another Issue Manager or Maintainer and respect their judgement as the final verdict.
|
||||
If there is a dispute on whether an issue should remain closed, ask for a second opinion. Get clarification from another Issue Manager or Maintainer and respect their judgement as the final verdict.
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
## MAPS
|
||||
|
||||
/tg/station currently has six station maps in rotation.
|
||||
* [Birdshot](https://tgstation13.org/wiki/Birdshot)
|
||||
* [DeltaStation](https://tgstation13.org/wiki/DeltaStation)
|
||||
* [IceBoxStation](https://tgstation13.org/wiki/IceboxStation)
|
||||
* [MetaStation](https://tgstation13.org/wiki/MetaStation)
|
||||
* [NorthStar](https://tgstation13.org/wiki/The_North_Star)
|
||||
* [TramStation](https://tgstation13.org/wiki/Tramstation)
|
||||
|
||||
- [Birdshot](https://tgstation13.org/wiki/Birdshot)
|
||||
- [DeltaStation](https://tgstation13.org/wiki/DeltaStation)
|
||||
- [IceBoxStation](https://tgstation13.org/wiki/IceboxStation)
|
||||
- [MetaStation](https://tgstation13.org/wiki/MetaStation)
|
||||
- [NorthStar](https://tgstation13.org/wiki/The_North_Star)
|
||||
- [TramStation](https://tgstation13.org/wiki/Tramstation)
|
||||
|
||||
Debug station maps.
|
||||
* [RuntimeStation](https://tgstation13.org/wiki/RuntimeStation)
|
||||
* [MultiZ](https://tgstation13.org/wiki/MultiZ)
|
||||
|
||||
- [RuntimeStation](https://tgstation13.org/wiki/RuntimeStation)
|
||||
- [MultiZ](https://tgstation13.org/wiki/MultiZ)
|
||||
|
||||
All maps have their own code file that is in the base of the `_maps` directory, or elsewhere in the codebase. For example, all of the station maps in rotation each have a corresponding JSON file and are loaded using the server's [configuration](#configuration) passed onto the Mapping subsystem. Maps are loaded dynamically when the game starts. Follow this guideline when adding your own map, to your fork, for easy compatibility.
|
||||
|
||||
@@ -25,8 +27,9 @@ If you are hosting a server, and want randomly picked maps to be played each rou
|
||||
<b>It is absolutely inadvisable to <i>ever</i> use the mapping utility offered by Dream Maker</b>. It is clunky and dated software that will steal your time, patience, and creative desires.
|
||||
|
||||
Instead, /tg/station map maintainers will always recommend using one of two modern and actively maintained programs.
|
||||
* [StrongDMM](https://github.com/SpaiR/StrongDMM) (Windows/Linux/MacOS)
|
||||
* [FastDMM2](https://github.com/monster860/FastDMM2) (Web-based Utility)
|
||||
|
||||
- [StrongDMM](https://github.com/SpaiR/StrongDMM) (Windows/Linux/MacOS)
|
||||
- [FastDMM2](https://github.com/monster860/FastDMM2) (Web-based Utility)
|
||||
|
||||
Both of the above programs have native TGM support, which is mandatory for all maps being submitted to this repository. Anytime you want to make changes to a map, it is imperative you use the [Map Merging tools](https://tgstation13.org/wiki/Map_Merger). When you clone your repository onto your machine for mapping, it's always a great idea to run `tools/hooks/Install.bat` at the very start of your mapping endeavors, as this will install Git hooks that help you automatically resolve any merge conflicts that come up while mapping.
|
||||
|
||||
@@ -38,7 +41,6 @@ UpdatePaths is a scripting tool that will automatically update all instances of
|
||||
|
||||
As a fast example, let's say you refactor some code, and you've changed the path of `/obj/item/weapon/gun/energy/laser` to `/obj/item/weapon/gun/energy/laser/pistol`. First, you would have to make a new file in the `tools/UpdatePaths/Scripts` [directory](https://github.com/tgstation/tgstation/tree/master/tools/UpdatePaths/Scripts), and name it `PRNUMBER_laser_pistol_split.txt` (with PRNUMBER being the number that your PR is assigned to, for book-keeping purposes). Then, you would have to add the following code to the file:
|
||||
|
||||
|
||||
```txt
|
||||
/obj/item/weapon/gun/energy/laser : /obj/item/weapon/gun/energy/laser/pistol{@OLD}
|
||||
```
|
||||
@@ -59,14 +61,14 @@ Map files for away missions are located in the `_maps/RandomZLevels` directory.
|
||||
|
||||
A majority of maps (outlined below) must be placed in their corresponding configuration file to allow server operators to enable/disable the map for any reason they desire. Follow the chart to see where you should add your new map.
|
||||
|
||||
| Type of Map | Associated File with Link |
|
||||
| ----------- | ----------- |
|
||||
| Station Maps | [`config/maps.txt`](https://github.com/tgstation/tgstation/blob/master/config/maps.txt) |
|
||||
| Space Ruins | [`config/spaceruinblacklist.txt`](https://github.com/tgstation/tgstation/blob/master/config/spaceruinblacklist.txt) |
|
||||
| Lavaland Ruins | [`config/lavaruinblacklist.txt`](https://github.com/tgstation/tgstation/blob/master/config/lavaruinblacklist.txt) |
|
||||
| Icemoon Ruins | [`config/iceruinblacklist.txt`](https://github.com/tgstation/tgstation/blob/master/config/iceruinblacklist.txt) |
|
||||
| Escape Shuttles | [`config/unbuyableshuttles.txt`](https://github.com/tgstation/tgstation/blob/master/config/unbuyableshuttles.txt) |
|
||||
| Away Missions | [`config/awaymissionconfig.txt`](https://github.com/tgstation/tgstation/blob/master/config/awaymissionconfig.txt) |
|
||||
| Type of Map | Associated File with Link |
|
||||
| --------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| Station Maps | [`config/maps.txt`](https://github.com/tgstation/tgstation/blob/master/config/maps.txt) |
|
||||
| Space Ruins | [`config/spaceruinblacklist.txt`](https://github.com/tgstation/tgstation/blob/master/config/spaceruinblacklist.txt) |
|
||||
| Lavaland Ruins | [`config/lavaruinblacklist.txt`](https://github.com/tgstation/tgstation/blob/master/config/lavaruinblacklist.txt) |
|
||||
| Icemoon Ruins | [`config/iceruinblacklist.txt`](https://github.com/tgstation/tgstation/blob/master/config/iceruinblacklist.txt) |
|
||||
| Escape Shuttles | [`config/unbuyableshuttles.txt`](https://github.com/tgstation/tgstation/blob/master/config/unbuyableshuttles.txt) |
|
||||
| Away Missions | [`config/awaymissionconfig.txt`](https://github.com/tgstation/tgstation/blob/master/config/awaymissionconfig.txt) |
|
||||
|
||||
Each .txt file will have instructions on how to appropriately add your map to the file. If you're unsure about certain values, ask for help during the PR process (or beforehand).
|
||||
|
||||
|
||||
+10
-10
@@ -4,26 +4,26 @@ If you already know what these numbers mean and you want to see them update fast
|
||||
|
||||
# Main Entries:
|
||||
|
||||
* CPU: What percentage of a tick the game is using before starting the next tick. If this is above 100 it means we are over budget.
|
||||
- CPU: What percentage of a tick the game is using before starting the next tick. If this is above 100 it means we are over budget.
|
||||
|
||||
* TickCount: How many ticks should have elapsed since the start of the game if no ticks were ever delayed from starting.
|
||||
- TickCount: How many ticks should have elapsed since the start of the game if no ticks were ever delayed from starting.
|
||||
|
||||
* TickDrift: How many ticks since the game started that have been delayed. Essentially this is how many ticks the game is running behind. If this is increasing then the game is currently not able to keep up with demand.
|
||||
- TickDrift: How many ticks since the game started that have been delayed. Essentially this is how many ticks the game is running behind. If this is increasing then the game is currently not able to keep up with demand.
|
||||
|
||||
* Internal Tick Usage: You might have heard of this referred to as "maptick". It's how much of the tick that an internal byond function called SendMaps() has taken recently. The higher this is the less time our code has to run. SendMaps() deals with sending players updates of their view of the game world so it has to run every tick but it's expensive so ideally this is optimized as much as possible. You can see a more detailed breakdown of the cost of SendMaps by looking at the profiler in the debug tab -> "Send Maps Profile".
|
||||
- Internal Tick Usage: You might have heard of this referred to as "maptick". It's how much of the tick that an internal byond function called SendMaps() has taken recently. The higher this is the less time our code has to run. SendMaps() deals with sending players updates of their view of the game world so it has to run every tick but it's expensive so ideally this is optimized as much as possible. You can see a more detailed breakdown of the cost of SendMaps by looking at the profiler in the debug tab -> "Send Maps Profile".
|
||||
|
||||
# Master Controller Entry:
|
||||
|
||||
* TickRate: How many Byond ticks go between each master controller iteration. By default this is 1 meaning the MC runs once every byond tick. But certain configurations can increase this slightly.
|
||||
- TickRate: How many Byond ticks go between each master controller iteration. By default this is 1 meaning the MC runs once every byond tick. But certain configurations can increase this slightly.
|
||||
|
||||
* Iteration: How many times the MC has ran since starting.
|
||||
- Iteration: How many times the MC has ran since starting.
|
||||
|
||||
* TickLimit: This SHOULD be what percentage of the tick the MC can use when it starts a run, however currently it just represents how much of the tick the MC can use by the time that SSstatpanels fires. Someone should fix that.
|
||||
- TickLimit: This SHOULD be what percentage of the tick the MC can use when it starts a run, however currently it just represents how much of the tick the MC can use by the time that SSstatpanels fires. Someone should fix that.
|
||||
|
||||
# Subsystem Entries:
|
||||
|
||||
Subsystems will typically have a base stat entry of the form:
|
||||
[ ] Name 12ms|28%(2%)|3
|
||||
Subsystems will typically have a base stat entry of the form:
|
||||
[ ] Name 12ms|28%(2%)|3
|
||||
|
||||
The brackets hold a letter if the subsystem is in a state other than idle.
|
||||
|
||||
@@ -33,4 +33,4 @@ The second numbered entry is like cost, but in percentage of an ideal tick this
|
||||
|
||||
The third entry (2%) is how much time this subsystem spent executing beyond the time it was allocated by the MC. This is bad, it means that this subsystem doesn't yield when it's taking too much time and makes the job of the MC harder. The MC will attempt to account for this but it is better for all subsystems to be able to correctly yield when their turn is done.
|
||||
|
||||
The fourth entry represents how many times this subsystem fires before it completes a run.
|
||||
The fourth entry represents how many times this subsystem fires before it completes a run.
|
||||
|
||||
@@ -3,9 +3,11 @@ Welcome to this short guide to the POLICY config mechanism.
|
||||
You are probably reading this guide because you have been informed your antagonist or ghost role needs to support policy configuration.
|
||||
|
||||
## Requirements
|
||||
|
||||
It is a requirement of /tg/station development that all ghost roles, antags, minor antags and event mobs of any kind must support the policy system when implemented.
|
||||
|
||||
## What is policy configuration
|
||||
|
||||
Policy configuration is a json file that the administrators of a server can edit, which contains a dictionary of keywords -> string message.
|
||||
|
||||
The policy text for a specific keyword should be displayed when relevant and appropriate, to allow server administrators to define the broad strokes of policy for some feature or mob.
|
||||
@@ -27,9 +29,11 @@ This is also accessible to the user if they use `/client/verb/policy()` which wi
|
||||
### Relaying Specific Policy To Players, Example
|
||||
|
||||
Here is a simple example taken from the slime pyroclastic event to relay specific policy.
|
||||
|
||||
```DM
|
||||
var/policy = get_policy(ROLE_PYROCLASTIC_SLIME)
|
||||
if (policy)
|
||||
to_chat(S, policy)
|
||||
```
|
||||
|
||||
It's recommended to use a define for your policy keyword to make it easily changeable by a developer
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# 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
|
||||
@@ -56,7 +57,7 @@ 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
|
||||
recompile the game. Once you start the server up again, you should be running
|
||||
the new version.
|
||||
|
||||
## HOSTING
|
||||
@@ -68,15 +69,15 @@ 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.
|
||||
- `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 TGS](../../tools/tgs_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://tgstation13.org/wiki/Downloading_the_source_code#Setting_up_the_database
|
||||
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://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.
|
||||
|
||||
@@ -86,12 +87,13 @@ Web delivery of game resources makes it quicker for players to join and reduces
|
||||
|
||||
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.
|
||||
- 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.
|
||||
|
||||
### All In One Amazon Web Services Hosting and Content delivery network.
|
||||
|
||||
**Important Note**
|
||||
It is very Importat to note that since AWS is all highly integrated its "easier" than some solutions. However the Price to ***Performance Ratio is terrible***.
|
||||
It is very Importat to note that since AWS is all highly integrated its "easier" than some solutions. However the Price to **_Performance Ratio is terrible_**.
|
||||
|
||||
/tg/ Using around 7TB of bandwidth a month. These costs add up. So AWS is probly only a solution for low to mid pop servers
|
||||
|
||||
@@ -103,16 +105,18 @@ This guide is also assuming you are setting up a production server and not a ser
|
||||
It is highly recommended to reference AWS support documentation while reading this guide. This guide is not a How to AWS.
|
||||
|
||||
**Required AWS Services**
|
||||
|
||||
1. Elastic Computer 2 (EC2)
|
||||
* What size and class is up to you but 4GB of RAM is a minimum.
|
||||
- What size and class is up to you but 4GB of RAM is a minimum.
|
||||
1. Route53
|
||||
* Domain registration and assigning "Elastic" IP addresses to said web addresses
|
||||
- Domain registration and assigning "Elastic" IP addresses to said web addresses
|
||||
1. S3
|
||||
* This will be your storage point and distrobution point for your .RSC file
|
||||
- This will be your storage point and distrobution point for your .RSC file
|
||||
1. Identity and Access Management (IAM)
|
||||
* Required for EC2 to S3 file transfers
|
||||
- Required for EC2 to S3 file transfers
|
||||
|
||||
**Required Software**
|
||||
|
||||
1. Microsoft Windows
|
||||
1. MariaDB
|
||||
1. tgstation-server (TGS)
|
||||
@@ -120,30 +124,32 @@ It is highly recommended to reference AWS support documentation while reading th
|
||||
1. AWS Command Line V2
|
||||
|
||||
**Instructions**
|
||||
|
||||
1. After you go through setting up an AWS account you will need to create an IAM role and an IAM user. the IAM user will be made for debug testing. The IAM role will be used as an internal credential for the EC2 instance to talk with S3
|
||||
* The role and user creation are almost identical. give them names, select programatic access, then you will click atatych existing policey, Here you can use admin access or S3 full access. both can be found via search. the difference for roles is that you will on the first step declare it for use with EC2 and this one will need full admin access
|
||||
- The role and user creation are almost identical. give them names, select programatic access, then you will click atatych existing policey, Here you can use admin access or S3 full access. both can be found via search. the difference for roles is that you will on the first step declare it for use with EC2 and this one will need full admin access
|
||||
1. Create your Amazon EC2 instance. There will be a config option asking for an IAM role. use the IAM role created in the previous step.
|
||||
* A blank Windows Server is recommended
|
||||
* You will also need to define a security policey. 3 are recommended. a Remote Desktop Protocol Policey, a Maria DB Policey, and an SS13 policey. the Latter will use the port(s) of your dream demon settings. Make sure the SS13 policey whitelists all IP addresses.
|
||||
- A blank Windows Server is recommended
|
||||
- You will also need to define a security policey. 3 are recommended. a Remote Desktop Protocol Policey, a Maria DB Policey, and an SS13 policey. the Latter will use the port(s) of your dream demon settings. Make sure the SS13 policey whitelists all IP addresses.
|
||||
1. Creat your S3 bucket. this is a very simple process. only thing you need to edit is making the bucket public and making sure its in the same region as your EC2 instance.
|
||||
1. In the EC2 control panel, go to Elastic IP's. get one and assign it to your EC2 instance. This will result in the server IP address not changing and is required for joining the game via url instead of ip address
|
||||
1. In Route 53 you will register a domain name. The you will create a hosted zone and tell your domain to use the IP address you used for your EC2 instance.
|
||||
1. Install the required software
|
||||
* AWSCL2 you will need to run the configuration using the IAM User you created above.
|
||||
* TGS: Make sure the scripts from /tools/tgs_scripts are installed per instructions after you have set up your repository and done your first fetch. You will need to Also install a batch file similar to what i have provided into the event scripts folder. You can manually run the batch file to test connection to your S3 bucket.
|
||||
* Copy `compile_options.dm` into code overrides preserving the directory structure and altering the code as mentioned in the above CDN instructions.
|
||||
* Filename: DeploymentComplete.bat
|
||||
- AWSCL2 you will need to run the configuration using the IAM User you created above.
|
||||
- TGS: Make sure the scripts from /tools/tgs_scripts are installed per instructions after you have set up your repository and done your first fetch. You will need to Also install a batch file similar to what i have provided into the event scripts folder. You can manually run the batch file to test connection to your S3 bucket.
|
||||
- Copy `compile_options.dm` into code overrides preserving the directory structure and altering the code as mentioned in the above CDN instructions.
|
||||
- Filename: DeploymentComplete.bat
|
||||
|
||||
```Batch
|
||||
@echo off
|
||||
cd "C:\Program Files\Amazon\AWSCLIV2"
|
||||
aws s3 cp "C:\Instance_Path\Game\Live\tgstation.rsc" s3://BucketName/tgstation.rsc --acl public-read
|
||||
```
|
||||
|
||||
7. In your TGS's instance's static config files edit resources.txt to point to the resource file uploaded by the batch file. it should resemble `http://BucketName.s3.AWSRegion.amazonaws.com/tgstation.rsc` You can get this url from the S3 object management page after its been uploaded for the first time. *Make sure you do not use use HTTPS. Byond can not do encryption*
|
||||
7. Tell TGS to fetch and deploy. If everything goes according to plan, your server will be compiled and the resource uploaded automatically to amazon S3. You can verify that by checking on the file your bucket via aws web management.
|
||||
7. Test your client side connection.
|
||||
* Tell TGS to run the compiled server
|
||||
* Attempt to log in. AWS has a stupid fast transfer speed. you should download client side data faster than you can recognize it happened.
|
||||
7. In your TGS's instance's static config files edit resources.txt to point to the resource file uploaded by the batch file. it should resemble `http://BucketName.s3.AWSRegion.amazonaws.com/tgstation.rsc` You can get this url from the S3 object management page after its been uploaded for the first time. _Make sure you do not use use HTTPS. Byond can not do encryption_
|
||||
8. Tell TGS to fetch and deploy. If everything goes according to plan, your server will be compiled and the resource uploaded automatically to amazon S3. You can verify that by checking on the file your bucket via aws web management.
|
||||
9. Test your client side connection.
|
||||
- Tell TGS to run the compiled server
|
||||
- Attempt to log in. AWS has a stupid fast transfer speed. you should download client side data faster than you can recognize it happened.
|
||||
|
||||
## IRC BOT SETUP
|
||||
|
||||
|
||||
+78
-55
@@ -17,41 +17,45 @@ As with the style guide, you are expected to follow these specifications in orde
|
||||
As BYOND's Dream Maker (henceforth "DM") is an object-oriented language, code must be object-oriented when possible in order to be more flexible when adding content to it. If you don't know what "object-oriented" means, we highly recommend you do some light research to grasp the basics.
|
||||
|
||||
### Avoid 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.
|
||||
|
||||
### 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.
|
||||
* This extends to much further than just numbers or strings. You should always sanity check that an input is valid, especially when it comes to datums or references!
|
||||
* Input stalling is a very common exploit / bug that involves opening an input window when in a valid state, and triggering the input after exiting the valid state. These can be very serious, and allow players to teleport across the map or remove someone's brain at any given moment. If you check the player must be in a specific context before an input, you should generally check that they are still in the context AFTER the input resolves.
|
||||
* For example, if you have an item which can be used (in hand) by a player to make it explode, but you want them to confirm (via prompt) that they want it to explode, you should check that the item is still in the player's hands after confirming. Otherwise, they could drop it and explode it at any moment they want.
|
||||
* Another less common exploit involves allowing a player to open multiple of an input at once. This may allow the player to stack effects, such as triggering 10 explosions when only 1 should be allowed. While a lot of code is generally built in a way making this infeasible (usually due to runtime errors), it is noteworthy regardless.
|
||||
* You should also consider if it would make sense to apply a timeout to your input, to prevent players from opening it and keeping it on their screen until convenient.
|
||||
- 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.
|
||||
|
||||
* SQL queries **must** use parameters for any sort of input data, and `format_table_name` for table names. Directly substituting input into SQL queries is how SQL injections happen, which parameterized queries prevent.
|
||||
* Good: `SSdbcore.NewQuery({"UPDATE [format_table_name("round")] SET map_name = :map_name WHERE id = :round_id"}, list("map_name" = current_map.map_name, "round_id" = GLOB.round_id))`
|
||||
* Bad: `SSdbcore.NewQuery({"UPDATE round SET map_name = '[current_map.map_name]' WHERE id = [GLOB.round_id]"}`
|
||||
- This extends to much further than just numbers or strings. You should always sanity check that an input is valid, especially when it comes to datums or references!
|
||||
- Input stalling is a very common exploit / bug that involves opening an input window when in a valid state, and triggering the input after exiting the valid state. These can be very serious, and allow players to teleport across the map or remove someone's brain at any given moment. If you check the player must be in a specific context before an input, you should generally check that they are still in the context AFTER the input resolves.
|
||||
- For example, if you have an item which can be used (in hand) by a player to make it explode, but you want them to confirm (via prompt) that they want it to explode, you should check that the item is still in the player's hands after confirming. Otherwise, they could drop it and explode it at any moment they want.
|
||||
- Another less common exploit involves allowing a player to open multiple of an input at once. This may allow the player to stack effects, such as triggering 10 explosions when only 1 should be allowed. While a lot of code is generally built in a way making this infeasible (usually due to runtime errors), it is noteworthy regardless.
|
||||
- You should also consider if it would make sense to apply a timeout to your input, to prevent players from opening it and keeping it on their screen until convenient.
|
||||
|
||||
* All calls to topics must be checked for correctness. Topic href calls can be easily faked by clients, so you should ensure that the call is valid for the state the item is in. Do not rely on the UI code to provide only valid topic calls, because it won't.
|
||||
* Don't expose a topic call to more than what you need it to. If you are only looking for an item inside an atom, don't look for every item in the world - just look in the atom's contents.
|
||||
* You rarely should call `locate(ref)` without specifying a list! This is a serious exploit vector which can be used to spawn Nar'sie or delete players across the map. Try narrowing it down via a list - such as `locate(ref) in contents`, to find an item in an atom's contents.
|
||||
- SQL queries **must** use parameters for any sort of input data, and `format_table_name` for table names. Directly substituting input into SQL queries is how SQL injections happen, which parameterized queries prevent.
|
||||
|
||||
* Information that players could use to metagame (that is, to identify round information and/or antagonist type via information that would not be available to them in character) should be kept as administrator only.
|
||||
- Good: `SSdbcore.NewQuery({"UPDATE [format_table_name("round")] SET map_name = :map_name WHERE id = :round_id"}, list("map_name" = current_map.map_name, "round_id" = GLOB.round_id))`
|
||||
- Bad: `SSdbcore.NewQuery({"UPDATE round SET map_name = '[current_map.map_name]' WHERE id = [GLOB.round_id]"}`
|
||||
|
||||
* It is recommended as well you do not expose information about the players - even something as simple as the number of people who have readied up at the start of the round can and has been used to try to identify the round type.
|
||||
- All calls to topics must be checked for correctness. Topic href calls can be easily faked by clients, so you should ensure that the call is valid for the state the item is in. Do not rely on the UI code to provide only valid topic calls, because it won't.
|
||||
|
||||
* Where you have code that can cause large-scale modification and *FUN*, make sure you start it out locked behind one of the default admin roles - use common sense to determine which role fits the level of damage a function could do.
|
||||
- Don't expose a topic call to more than what you need it to. If you are only looking for an item inside an atom, don't look for every item in the world - just look in the atom's contents.
|
||||
- You rarely should call `locate(ref)` without specifying a list! This is a serious exploit vector which can be used to spawn Nar'sie or delete players across the map. Try narrowing it down via a list - such as `locate(ref) in contents`, to find an item in an atom's contents.
|
||||
|
||||
- Information that players could use to metagame (that is, to identify round information and/or antagonist type via information that would not be available to them in character) should be kept as administrator only.
|
||||
|
||||
- It is recommended as well you do not expose information about the players - even something as simple as the number of people who have readied up at the start of the round can and has been used to try to identify the round type.
|
||||
|
||||
- Where you have code that can cause large-scale modification and _FUN_, make sure you start it out locked behind one of the default admin roles - use common sense to determine which role fits the level of damage a function could do.
|
||||
|
||||
### User Interfaces
|
||||
|
||||
* All new player-facing user interfaces must use TGUI, unless they are critical user interfaces.
|
||||
* All critical user interfaces must be usable with HTML or the interface.dmf, with tgui being *optional* for this UI.
|
||||
* Examples of critical user interfaces are the chat box, the observe button, the stat panel, and the chat input.
|
||||
* Documentation for TGUI can be found at:
|
||||
* [tgui/README.md](../tgui/README.md)
|
||||
* [tgui/tutorial-and-examples.md](../tgui/docs/tutorial-and-examples.md)
|
||||
- All new player-facing user interfaces must use TGUI, unless they are critical user interfaces.
|
||||
- All critical user interfaces must be usable with HTML or the interface.dmf, with tgui being _optional_ for this UI.
|
||||
- Examples of critical user interfaces are the chat box, the observe button, the stat panel, and the chat input.
|
||||
- Documentation for TGUI can be found at:
|
||||
- [tgui/README.md](../tgui/README.md)
|
||||
- [tgui/tutorial-and-examples.md](../tgui/docs/tutorial-and-examples.md)
|
||||
|
||||
### Dont override type safety checks
|
||||
|
||||
@@ -71,20 +75,22 @@ var/path_type = "/obj/item/baseball_bat"
|
||||
|
||||
### Other Notes
|
||||
|
||||
* Code should be modular where possible; if you are working on a new addition, then strongly consider putting it in its own file unless it makes sense to put it with similar ones (i.e. a new tool would go in the "tools.dm" file)
|
||||
- Code should be modular where possible; if you are working on a new addition, then strongly consider putting it in its own file unless it makes sense to put it with similar ones (i.e. a new tool would go in the "tools.dm" file)
|
||||
|
||||
* Bloated code may be necessary to add a certain feature, which means there has to be a judgement over whether the feature is worth having or not. You can help make this decision easier by making sure your code is modular.
|
||||
- Bloated code may be necessary to add a certain feature, which means there has to be a judgement over whether the feature is worth having or not. You can help make this decision easier by making sure your code is modular.
|
||||
|
||||
* You are expected to help maintain the code that you add, meaning that if there is a problem then you are likely to be approached in order to fix any issues, runtimes, or bugs.
|
||||
- You are expected to help maintain the code that you add, meaning that if there is a problem then you are likely to be approached in order to fix any issues, runtimes, or bugs.
|
||||
|
||||
* 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
## Structural
|
||||
|
||||
### No duplicated code (Don't repeat yourself)
|
||||
|
||||
Copying code from one place to another may be suitable for small, short-time projects, but /tg/station is a long-term project and highly discourages this.
|
||||
|
||||
Instead you can use object orientation, or simply placing repeated code in a function, to obey this specification easily.
|
||||
@@ -96,21 +102,23 @@ While we normally encourage (and in some cases, even require) bringing out of da
|
||||
|
||||
### Files
|
||||
|
||||
* Because runtime errors do not give the full path, try to avoid having files with the same name across folders.
|
||||
- Because runtime errors do not give the full path, try to avoid having files with the same name across folders.
|
||||
|
||||
* File names should not be mixed case, or contain spaces or any character that would require escaping in a uri.
|
||||
- File names should not be mixed case, or contain spaces or any character that would require escaping in a uri.
|
||||
|
||||
* Files and path accessed and referenced by code above simply being #included should be strictly lowercase to avoid issues on filesystems where case matters.
|
||||
- Files and path accessed and referenced by code above simply being #included should be strictly lowercase to avoid issues on filesystems where case matters.
|
||||
|
||||
### RegisterSignal()
|
||||
|
||||
#### PROC_REF Macros
|
||||
|
||||
When referencing procs in RegisterSignal, Callback and other procs you should use PROC_REF, TYPE_PROC_REF and GLOBAL_PROC_REF macros.
|
||||
They ensure compilation fails if the reffered to procs change names or get removed.
|
||||
The macro to be used depends on how the proc you're in relates to the proc you want to use:
|
||||
|
||||
PROC_REF if the proc you want to use is defined on the current proc type or any of its ancestor types.
|
||||
Example:
|
||||
|
||||
```
|
||||
/mob/proc/funny()
|
||||
to_chat(world,"knock knock")
|
||||
@@ -127,6 +135,7 @@ Example:
|
||||
|
||||
TYPE_PROC_REF if the proc you want to use is defined on a different unrelated type
|
||||
Example:
|
||||
|
||||
```
|
||||
/obj/thing/proc/funny()
|
||||
to_chat(world,"knock knock")
|
||||
@@ -139,6 +148,7 @@ Example:
|
||||
|
||||
GLOBAL_PROC_REF if the proc you want to use is a global proc.
|
||||
Example:
|
||||
|
||||
```
|
||||
/proc/funny()
|
||||
to_chat(world,"knock knock")
|
||||
@@ -152,11 +162,13 @@ Note that the same rules go for verbs too! We have VERB_REF() and TYPE_VERB_REF(
|
||||
#### 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.
|
||||
|
||||
Any sleeping behaviour that you need to perform inside a `SIGNAL_HANDLER` proc must be called asynchronously (e.g. with `INVOKE_ASYNC()`) or be redone to work asynchronously.
|
||||
@@ -174,12 +186,14 @@ If you decide to do this, you should make it clear with a comment explaining why
|
||||
### 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.
|
||||
|
||||
### Avoid unnecessary type checks and obscuring nulls in lists
|
||||
@@ -191,6 +205,7 @@ If we know the list is supposed to only contain the desired type then we want to
|
||||
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
|
||||
@@ -201,6 +216,7 @@ for(var/atom/thing in bag_of_atoms)
|
||||
```
|
||||
|
||||
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
|
||||
@@ -238,20 +254,21 @@ How do we solve this? By using delta-time. Delta-time is the amount of seconds y
|
||||
health -= health_loss * seconds_per_tick
|
||||
```
|
||||
|
||||
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 * seconds_per_tick, 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.
|
||||
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 \* seconds_per_tick, 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.
|
||||
|
||||
## Optimization
|
||||
|
||||
### Startup/Runtime tradeoffs with lists and the "hidden" init proc
|
||||
|
||||
First, read the comments in [this BYOND thread](http://www.byond.com/forum/?post=2086980&page=2#comment19776775), starting where the link takes you.
|
||||
|
||||
There are two key points here:
|
||||
|
||||
1) Defining a list in the variable's definition calls a hidden proc - init. If you have to define a list at startup, do so in New() (or preferably Initialize()) and avoid the overhead of a second call (Init() and then New())
|
||||
1. Defining a list in the variable's definition calls a hidden proc - init. If you have to define a list at startup, do so in New() (or preferably Initialize()) and avoid the overhead of a second call (Init() and then New())
|
||||
|
||||
2) It also consumes more memory to the point where the list is actually required, even if the object in question may never use it!
|
||||
2. It also consumes more memory to the point where the list is actually required, even if the object in question may never use it!
|
||||
|
||||
Remember: although this tradeoff makes sense in many cases, it doesn't cover them all. Think carefully about your addition before deciding if you need to use it.
|
||||
|
||||
@@ -261,8 +278,8 @@ BYOND will allow you to use a raw icon file or even an icon datum for underlays,
|
||||
|
||||
Converting them yourself to appearances and storing this converted value will ensure this process only has to happen once for the lifetime of the round. Helper functions exist to do most of the work for you.
|
||||
|
||||
|
||||
Bad:
|
||||
|
||||
```dm
|
||||
/obj/machine/update_overlays(blah)
|
||||
if (stat & broken)
|
||||
@@ -275,6 +292,7 @@ Bad:
|
||||
```
|
||||
|
||||
Good:
|
||||
|
||||
```dm
|
||||
/obj/machine/update_overlays(var/blah)
|
||||
var/static/on_overlay
|
||||
@@ -296,13 +314,12 @@ Good:
|
||||
|
||||
Note: images are appearances with extra steps, and don't incur the overhead in conversion.
|
||||
|
||||
|
||||
### Do not abuse associated lists.
|
||||
|
||||
Associated lists that could instead be variables or statically defined number indexed lists will use more memory, as associated lists have a 24 bytes per item overhead (vs 8 for lists and most vars), and are slower to search compared to static/global variables and lists with known indexes.
|
||||
|
||||
|
||||
Bad:
|
||||
|
||||
```dm
|
||||
/obj/machine/update_overlays(var/blah)
|
||||
var/static/our_overlays
|
||||
@@ -315,6 +332,7 @@ Bad:
|
||||
```
|
||||
|
||||
Good:
|
||||
|
||||
```dm
|
||||
#define OUR_ON_OVERLAY 1
|
||||
#define OUR_OFF_OVERLAY 2
|
||||
@@ -333,9 +351,11 @@ Good:
|
||||
#undef OUR_OFF_OVERLAY
|
||||
#undef OUR_BROKEN_OVERLAY
|
||||
```
|
||||
|
||||
Storing these in a flat (non-associated) list saves on memory, and using defines to reference locations in the list saves CPU time searching the list.
|
||||
|
||||
Also good:
|
||||
|
||||
```dm
|
||||
/obj/machine/update_overlays(var/blah)
|
||||
var/static/on_overlay
|
||||
@@ -350,6 +370,7 @@ Also good:
|
||||
return
|
||||
...
|
||||
```
|
||||
|
||||
Proc variables, static variables, and global variables are resolved at compile time, so the above is equivalent to the second example, but is easier to read, and avoids the need to store a list.
|
||||
|
||||
Note: While there has historically been a strong impulse to use associated lists for caching of computed values, this is the easy way out and leaves a lot of hidden overhead. Please keep this in mind when designing core/root systems that are intended for use by other code/coders. It's normally better for consumers of such systems to handle their own caching using vars and number indexed lists, than for you to do it using associated lists.
|
||||
@@ -359,6 +380,7 @@ Note: While there has historically been a strong impulse to use associated lists
|
||||
Like all languages, Dream Maker has its quirks, some of them are beneficial to us, some are harmful.
|
||||
|
||||
### Loops
|
||||
|
||||
#### In-To for-loops
|
||||
|
||||
`for(var/i = 1, i <= some_value, i++)` is a fairly standard way to write an incremental for loop in most languages (especially those in the C family), but 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`).
|
||||
@@ -427,7 +449,7 @@ Because of these problems, it is encouraged to prefer standard, explicit return
|
||||
|
||||
#### Exception: `. = ..()`
|
||||
|
||||
As hinted at before, `. = ..()` is *extremely* common. This will call the parent function, and preserve its return type. Code like this:
|
||||
As hinted at before, `. = ..()` is _extremely_ common. This will call the parent function, and preserve its return type. Code like this:
|
||||
|
||||
```dm
|
||||
/obj/item/spoon/attack()
|
||||
@@ -435,7 +457,7 @@ As hinted at before, `. = ..()` is *extremely* common. This will call the parent
|
||||
visible_message("Whack!")
|
||||
```
|
||||
|
||||
...is completely accepted, and in fact, usually *prefered* over:
|
||||
...is completely accepted, and in fact, usually _prefered_ over:
|
||||
|
||||
```dm
|
||||
/obj/item/spoon/attack()
|
||||
@@ -458,7 +480,7 @@ One unique property of DM is the ability for procs to error, but for code to con
|
||||
to_chat(world, "2")
|
||||
```
|
||||
|
||||
...would print both 1 *and* 2, which may be unexpected if you come from other languages.
|
||||
...would print both 1 _and_ 2, which may be unexpected if you come from other languages.
|
||||
|
||||
This is where `.` provides a new useful behavior--**a proc that runtimes will return `.`**.
|
||||
|
||||
@@ -511,12 +533,12 @@ The way they pull this off, while fine for the language itself, makes a mess of
|
||||
|
||||
The following is a list of procs, and their safe replacements.
|
||||
|
||||
* Removing something from the loop `walk(0)` -> `SSmove_manager.stop_looping()`
|
||||
* Move in a direction `walk()` -> `SSmove_manager.move()`
|
||||
* Move towards a thing, taking turf density into account`walk_to()` -> `SSmove_manager.move_to()`
|
||||
* Move in a thing's direction, ignoring turf density `walk_towards()` -> `SSmove_manager.home_onto()` and `SSmove_manager.move_towards_legacy()`, check the documentation to see which you like better
|
||||
* Move away from something, taking turf density into account `walk_away()` -> `SSmove_manager.move_away()`
|
||||
* Move to a random place nearby. NOT random walk `walk_rand()` -> `SSmove_manager.move_rand()` is random walk, `SSmove_manager.move_to_rand()` is walk to a random place
|
||||
- Removing something from the loop `walk(0)` -> `SSmove_manager.stop_looping()`
|
||||
- Move in a direction `walk()` -> `SSmove_manager.move()`
|
||||
- Move towards a thing, taking turf density into account`walk_to()` -> `SSmove_manager.move_to()`
|
||||
- Move in a thing's direction, ignoring turf density `walk_towards()` -> `SSmove_manager.home_onto()` and `SSmove_manager.move_towards_legacy()`, check the documentation to see which you like better
|
||||
- Move away from something, taking turf density into account `walk_away()` -> `SSmove_manager.move_away()`
|
||||
- Move to a random place nearby. NOT random walk `walk_rand()` -> `SSmove_manager.move_rand()` is random walk, `SSmove_manager.move_to_rand()` is walk to a random place
|
||||
|
||||
### Avoid pointer use
|
||||
|
||||
@@ -537,6 +559,7 @@ world << pointed_at // outputs "text a THIRD TIME"
|
||||
```
|
||||
|
||||
The problem with this is twofold.
|
||||
|
||||
- First: if you use a pointer to reference a var on a datum, it is essentially as if you held an invisible reference to that datum. This risks hard deletes in very unclear ways that cannot be tested for.
|
||||
- Second: People don't like, understand how pointers work? They mix them up with classical C pointers, when they're more like `std::shared_ptr`. This leads to code that just doesn't work properly, or is hard to follow without first getting your mind around it. It also risks hiding what code does in dumb ways because pointers don't have unique types.
|
||||
|
||||
@@ -553,18 +576,18 @@ Due to how they are internally represented as part of appearance, overlays and u
|
||||
|
||||
## SQL
|
||||
|
||||
* Do not use the shorthand sql insert format (where no column names are specified) because it unnecessarily breaks all queries on minor column changes and prevents using these tables for tracking outside related info such as in a connected site/forum.
|
||||
- Do not use the shorthand sql insert format (where no column names are specified) because it unnecessarily breaks all queries on minor column changes and prevents using these tables for tracking outside related info such as in a connected site/forum.
|
||||
|
||||
* All changes to the database's layout(schema) must be specified in the database changelog in SQL, as well as reflected in the schema files
|
||||
- All changes to the database's layout(schema) must be specified in the database changelog in SQL, as well as reflected in the schema files
|
||||
|
||||
* Any time the schema is changed the `schema_revision` table and `DB_MAJOR_VERSION` or `DB_MINOR_VERSION` defines must be incremented.
|
||||
- Any time the schema is changed the `schema_revision` table and `DB_MAJOR_VERSION` or `DB_MINOR_VERSION` defines must be incremented.
|
||||
|
||||
* Queries must never specify the database, be it in code, or in text files in the repo.
|
||||
- Queries must never specify the database, be it in code, or in text files in the repo.
|
||||
|
||||
* 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
+106
-51
@@ -1,4 +1,5 @@
|
||||
# Style Guide
|
||||
|
||||
This is the style you must follow when writing code. It's important to note that large parts of the codebase do not consistently follow these rules, but this does not free you of the requirement to follow them.
|
||||
|
||||
1. [General Guidelines](#general-guidelines)
|
||||
@@ -11,6 +12,7 @@ This is the style you must follow when writing code. It's important to note that
|
||||
## General Guidelines
|
||||
|
||||
### Tabs, not spaces
|
||||
|
||||
You must use tabs to indent your code, NOT 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.
|
||||
@@ -28,40 +30,46 @@ Do not use tabs/spaces for indentation in the middle of a code line. Not only is
|
||||
```
|
||||
|
||||
### Control statements
|
||||
|
||||
(if, while, for, etc)
|
||||
|
||||
* 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)`)
|
||||
- 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)`)
|
||||
|
||||
### Operators
|
||||
#### Spacing
|
||||
* Operators that should be separated by spaces
|
||||
* Boolean and logic operators like &&, || <, >, ==, etc (but not !)
|
||||
* Bitwise AND &
|
||||
* Argument separator operators like , (and ; when used in a forloop)
|
||||
* Assignment operators like = or += or the like
|
||||
* Operators that should not be separated by spaces
|
||||
* Bitwise OR |
|
||||
* Access operators like . and :
|
||||
* Parentheses ()
|
||||
* logical not !
|
||||
|
||||
Math operators like +, -, /, *, etc are up in the air, just choose which version looks more readable.
|
||||
#### Spacing
|
||||
|
||||
- Operators that should be separated by spaces
|
||||
- Boolean and logic operators like &&, || <, >, ==, etc (but not !)
|
||||
- Bitwise AND &
|
||||
- Argument separator operators like , (and ; when used in a forloop)
|
||||
- Assignment operators like = or += or the like
|
||||
- Operators that should not be separated by spaces
|
||||
- Bitwise OR |
|
||||
- Access operators like . and :
|
||||
- Parentheses ()
|
||||
- logical not !
|
||||
|
||||
Math operators like +, -, /, \*, etc are up in the air, just choose which version looks more readable.
|
||||
|
||||
#### Use
|
||||
* Bitwise AND - '&'
|
||||
* 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")`
|
||||
|
||||
- Bitwise AND - '&'
|
||||
- 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")`
|
||||
|
||||
### Use static instead of global
|
||||
|
||||
DM has a var keyword, called global. This var keyword is for vars inside of types. For instance:
|
||||
|
||||
```DM
|
||||
/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)
|
||||
|
||||
Isn't that confusing?
|
||||
@@ -69,17 +77,21 @@ Isn't that confusing?
|
||||
There is also an undocumented keyword called `static` that has the same behaviour as global but more correctly describes BYOND's behaviour. Therefore, we always use static instead of global where we need it, as it reduces suprise when reading BYOND code.
|
||||
|
||||
### Use early returns
|
||||
|
||||
Do not enclose a proc in an if-block when returning on a condition is more feasible
|
||||
This is bad:
|
||||
````DM
|
||||
|
||||
```DM
|
||||
/datum/datum1/proc/proc1()
|
||||
if (thing1)
|
||||
if (!thing2)
|
||||
if (thing3 == 30)
|
||||
do stuff
|
||||
````
|
||||
```
|
||||
|
||||
This is good:
|
||||
````DM
|
||||
|
||||
```DM
|
||||
/datum/datum1/proc/proc1()
|
||||
if (!thing1)
|
||||
return
|
||||
@@ -88,21 +100,26 @@ This is good:
|
||||
if (thing3 != 30)
|
||||
return
|
||||
do stuff
|
||||
````
|
||||
```
|
||||
|
||||
This prevents nesting levels from getting deeper then they need to be.
|
||||
|
||||
### No magic numbers or strings
|
||||
|
||||
This means stuff like having a "mode" variable for an object set to "1" or "2" with no clear indicator of what that means. Make these #defines with a name that more clearly states what it's for. For instance:
|
||||
````DM
|
||||
|
||||
```DM
|
||||
/datum/proc/do_the_thing(thing_to_do)
|
||||
switch(thing_to_do)
|
||||
if(1)
|
||||
(...)
|
||||
if(2)
|
||||
(...)
|
||||
````
|
||||
```
|
||||
|
||||
There's no indication of what "1" and "2" mean! Instead, you'd do something like this:
|
||||
````DM
|
||||
|
||||
```DM
|
||||
#define DO_THE_THING_REALLY_HARD 1
|
||||
#define DO_THE_THING_EFFICIENTLY 2
|
||||
/datum/proc/do_the_thing(thing_to_do)
|
||||
@@ -111,7 +128,8 @@ There's no indication of what "1" and "2" mean! Instead, you'd do something like
|
||||
(...)
|
||||
if(DO_THE_THING_EFFICIENTLY)
|
||||
(...)
|
||||
````
|
||||
```
|
||||
|
||||
This is clearer and enhances readability of your code! Get used to doing it!
|
||||
|
||||
### Use our time defines
|
||||
@@ -119,26 +137,31 @@ This is clearer and enhances readability of your code! Get used to doing it!
|
||||
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
|
||||
|
||||
- SECONDS
|
||||
- MINUTES
|
||||
- HOURS
|
||||
|
||||
This is bad:
|
||||
````DM
|
||||
|
||||
```DM
|
||||
/datum/datum1/proc/proc1()
|
||||
if(do_after(mob, 15))
|
||||
mob.dothing()
|
||||
````
|
||||
```
|
||||
|
||||
This is good:
|
||||
````DM
|
||||
|
||||
```DM
|
||||
/datum/datum1/proc/proc1()
|
||||
if(do_after(mob, 1.5 SECONDS))
|
||||
mob.dothing()
|
||||
````
|
||||
```
|
||||
|
||||
## Paths and Inheritence
|
||||
|
||||
### All BYOND paths must contain the full path
|
||||
|
||||
(i.e. absolute pathing)
|
||||
|
||||
DM will allow you nest almost any type keyword into a block, such as:
|
||||
@@ -195,26 +218,33 @@ The previous code made compliant:
|
||||
```
|
||||
|
||||
### Type paths must begin with a `/`
|
||||
|
||||
eg: `/datum/thing`, not `datum/thing`
|
||||
|
||||
### 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.
|
||||
|
||||
## Variables
|
||||
|
||||
### Use `var/name` format when declaring variables
|
||||
|
||||
While DM allows other ways of declaring variables, this one should be used for consistency.
|
||||
|
||||
### 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.
|
||||
|
||||
#### Any variable or argument that holds time and uses a unit of time other than decisecond must include the unit of time in the name.
|
||||
|
||||
For example, a proc argument named `delta_time` that marks the seconds between fires could confuse somebody who assumes it stores deciseconds. Naming it `delta_time_seconds` makes this clearer, naming it `seconds_per_tick` makes its purpose even clearer.
|
||||
|
||||
### Don't use abbreviations
|
||||
|
||||
Avoid variables like C, M, and H. Prefer names like "user", "victim", "weapon", etc.
|
||||
|
||||
```dm
|
||||
@@ -226,10 +256,12 @@ Avoid variables like C, M, and H. Prefer names like "user", "victim", "weapon",
|
||||
/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.
|
||||
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
|
||||
@@ -238,6 +270,7 @@ 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
|
||||
@@ -254,15 +287,17 @@ 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.
|
||||
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](./STANDARDS.md#avoid-unnecessary-type-checks-and-obscuring-nulls-in-lists), and making more descriptive variables is always encouraged.
|
||||
|
||||
@@ -284,7 +319,8 @@ for (var/i in reagents)
|
||||
```
|
||||
|
||||
### Don't abuse the increment/decrement operators
|
||||
`x++` and `++x` both will increment x, but the former will return x *before* it was incremented, while the latter will return x *after* it was incremented. Great if you want to be clever, or if you were a C programmer in the 70s, but it hurts the readability of code to anyone who isn't familiar with this. The convenience is not nearly good enough to justify this burden.
|
||||
|
||||
`x++` and `++x` both will increment x, but the former will return x _before_ it was incremented, while the latter will return x _after_ it was incremented. Great if you want to be clever, or if you were a C programmer in the 70s, but it hurts the readability of code to anyone who isn't familiar with this. The convenience is not nearly good enough to justify this burden.
|
||||
|
||||
```dm
|
||||
// Bad
|
||||
@@ -304,6 +340,7 @@ apples--
|
||||
```
|
||||
|
||||
### initial() versus ::
|
||||
|
||||
`::` is a compile time scope operator which we use as an alternative to `initial()`.
|
||||
It's used within the definition of a datum as opposed to `Initialize` or other procs.
|
||||
|
||||
@@ -323,6 +360,7 @@ It's used within the definition of a datum as opposed to `Initialize` or other p
|
||||
```
|
||||
|
||||
Another good use for it easy access of the parent's variables.
|
||||
|
||||
```dm
|
||||
/obj/item/fork/dangerous
|
||||
damage = parent_type::damage * 2
|
||||
@@ -333,34 +371,37 @@ Another good use for it easy access of the parent's variables.
|
||||
flags_1 = parent_type::flags_1 | FLAG_COOLER
|
||||
```
|
||||
|
||||
|
||||
It's important to note that `::` does not apply to every application of `initial()`.
|
||||
Primarily in cases where the type you're using for the initial value is not static.
|
||||
|
||||
For example,
|
||||
|
||||
```dm
|
||||
/proc/cmp_subsystem_init(datum/controller/subsystem/a, datum/controller/subsystem/b)
|
||||
return initial(b.init_order) - initial(a.init_order)
|
||||
```
|
||||
|
||||
could not use `::` as the provided types are not static.
|
||||
|
||||
## Procs
|
||||
|
||||
### 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.
|
||||
- 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.
|
||||
- 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
|
||||
@@ -370,6 +411,7 @@ These are bad:
|
||||
```
|
||||
|
||||
This is good:
|
||||
|
||||
```DM
|
||||
/datum/datum1
|
||||
var/getter_turned_into_variable
|
||||
@@ -388,9 +430,11 @@ This is good:
|
||||
```
|
||||
|
||||
### When passing vars through New() or Initialize()'s arguments, use src.var
|
||||
|
||||
Using src.var + naming the arguments the same as the var is the most readable and intuitive way to pass arguments into a new instance's vars. The main benefit is that you do not need to give arguments odd names with prefixes and suffixes that are easily forgotten in `new()` when sending named args.
|
||||
|
||||
This is very bad:
|
||||
|
||||
```DM
|
||||
/atom/thing
|
||||
var/is_red
|
||||
@@ -405,6 +449,7 @@ This is very bad:
|
||||
Future coders using this code will have to remember two differently named variables which are near-synonyms of eachother. One of them is only used in Initialize for one line.
|
||||
|
||||
This is bad:
|
||||
|
||||
```DM
|
||||
/atom/thing
|
||||
var/is_red
|
||||
@@ -416,9 +461,10 @@ This is bad:
|
||||
new /atom/thing(null, _is_red = TRUE)
|
||||
```
|
||||
|
||||
`_is_red` is being used to set `is_red` and yet means a random '_' needs to be appended to the front of the arg, same as all other args like this.
|
||||
`_is_red` is being used to set `is_red` and yet means a random '\_' needs to be appended to the front of the arg, same as all other args like this.
|
||||
|
||||
This is good:
|
||||
|
||||
```DM
|
||||
/atom/thing
|
||||
var/is_red
|
||||
@@ -440,7 +486,7 @@ Pop-quiz, what does this do?
|
||||
give_pizza(TRUE, 2)
|
||||
```
|
||||
|
||||
Well, obviously the `TRUE` makes the pizza hot, and `2` is the number of toppings.
|
||||
Well, obviously the `TRUE` makes the pizza hot, and `2` is the number of toppings.
|
||||
|
||||
Code like this can be very difficult to read, especially since our LSP does not show argument names at this time. Because of this, you should prefer to use named arguments where the meaning is not otherwise obvious.
|
||||
|
||||
@@ -477,6 +523,7 @@ proc_call_on_one_line(
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```dm
|
||||
/area/town
|
||||
var/list/places_to_visit = list(
|
||||
@@ -527,6 +574,7 @@ Macros are, in essence, direct copy and pastes into the code. They are one of th
|
||||
This section will assume you understand the following concepts:
|
||||
|
||||
### Language - Hygienic
|
||||
|
||||
We say a macro is [**hygienic**](https://en.wikipedia.org/wiki/Hygienic_macro) if, generally, it does not rely on input not given to it directly through the call site, and does not affect the call site outside of it in a way that could not be easily reused somewhere else.
|
||||
|
||||
An example of a non-hygienic macro is:
|
||||
@@ -545,14 +593,17 @@ Here are two examples of non-hygienic macros, because it affects its call site:
|
||||
```
|
||||
|
||||
### Language - Side effects/Pure
|
||||
We say something has [**side effects**](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) if it mutates anything outside of itself. We say something is **pure** if it does not.
|
||||
|
||||
We say something has [**side effects**](<https://en.wikipedia.org/wiki/Side_effect_(computer_science)>) if it mutates anything outside of itself. We say something is **pure** if it does not.
|
||||
|
||||
For example, this has no side effects, and is pure:
|
||||
|
||||
```dm
|
||||
#define MOTH_MAX_HEALTH 500
|
||||
```
|
||||
|
||||
This, however, performs a side effect of updating the health:
|
||||
|
||||
```dm
|
||||
#define MOTH_SET_HEALTH(moth, new_health) ##moth.set_health(##new_health)
|
||||
```
|
||||
@@ -560,9 +611,11 @@ This, however, performs a side effect of updating the health:
|
||||
Now that you're caught up on the terms, let's get into the guidelines.
|
||||
|
||||
### Naming
|
||||
|
||||
With little exception, macros should be SCREAMING_SNAKE_CASE.
|
||||
|
||||
### Put macro segments inside parentheses where possible.
|
||||
|
||||
This will save you from bugs down the line with operator precedence.
|
||||
|
||||
For example, the following macro:
|
||||
@@ -590,7 +643,7 @@ This is [a real bug that tends to come up](https://github.com/tgstation/tgstatio
|
||||
The same goes for arguments passed to a macro...
|
||||
|
||||
```
|
||||
// Guarantee
|
||||
// Guarantee
|
||||
#define CALCULATE_TEMPERATURE(base) (T20C + (##base))
|
||||
```
|
||||
|
||||
@@ -602,7 +655,7 @@ Consider the previously mentioned non-hygienic macro:
|
||||
#define GET_HEALTH(health_percent) ((##health_percent) * max_health)
|
||||
```
|
||||
|
||||
This relies on "max_health", but it is not obviously clear what the source is. This will also become worse if we *do* want to change where we get the source from. This would be preferential as:
|
||||
This relies on "max*health", but it is not obviously clear what the source is. This will also become worse if we \_do* want to change where we get the source from. This would be preferential as:
|
||||
|
||||
```dm
|
||||
#define GET_HEALTH(source, health_percent) ((##health_percent) * (##source).max_health)
|
||||
@@ -757,7 +810,7 @@ Because of how common using defines as constants is, this would seemingly imply
|
||||
set_color(PARTY_LIGHT_COLOR())
|
||||
```
|
||||
|
||||
...which *does* imply some work is happening.
|
||||
...which _does_ imply some work is happening.
|
||||
|
||||
BYOND does not support `#define PARTY_LIGHT_COLOR()`, so instead we would write the define as:
|
||||
|
||||
@@ -839,6 +892,7 @@ Sometimes the best macro is one that doesn't exist at all. Macros can make some
|
||||
```
|
||||
|
||||
This is a fairly egregious macro, and would be better off just written like:
|
||||
|
||||
```dm
|
||||
/obj/item/sword/proc/hit(mob/victim)
|
||||
attack(victim)
|
||||
@@ -846,9 +900,10 @@ This is a fairly egregious macro, and would be better off just written like:
|
||||
```
|
||||
|
||||
## Things that do not matter
|
||||
|
||||
The following coding styles are not only not enforced at all, but are generally frowned upon to change for little to no reason:
|
||||
|
||||
* English/British spelling on var/proc names
|
||||
* Color/Colour - both are fine, but keep in mind that BYOND uses `color` as a base variable
|
||||
* Spaces after control statements
|
||||
* `if()` and `if ()` - nobody cares!
|
||||
- English/British spelling on var/proc names
|
||||
- Color/Colour - both are fine, but keep in mind that BYOND uses `color` as a base variable
|
||||
- Spaces after control statements
|
||||
- `if()` and `if ()` - nobody cares!
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
The byond tick proceeds as follows:
|
||||
|
||||
1. procs sleeping via walk() are resumed (i dont know why these are first)
|
||||
|
||||
2. normal sleeping procs are resumed, in the order they went to sleep in the first place, this is where the MC wakes up and processes subsystems. a consequence of this is that the MC almost never resumes before other sleeping procs, because it only goes to sleep for 1 tick 99% of the time, and 99% of procs either go to sleep for less time than the MC (which guarantees that they entered the sleep queue earlier when its time to wake up) and/or were called synchronously from the MC's execution, almost all of the time the MC is the last sleeping proc to resume in any given tick. This is good because it means the MC can account for the cost of previous resuming procs in the tick, and minimizes overtime.
|
||||
@@ -8,14 +9,14 @@ The byond tick proceeds as follows:
|
||||
4. a few small things happen in byond internals
|
||||
|
||||
5. SendMaps is called for this tick, which processes the game state for all clients connected to the game and handles sending them changes
|
||||
in appearances within their view range. This is expensive and takes up a significant portion of our tick, about 0.45% per connected player
|
||||
as of 3/20/2022. meaning that with 50 players, 22.5% of our tick is being used up by just SendMaps, after all of our code has stopped executing. Thats only the average across all rounds, for most highpop rounds it can look like 0.6% of the tick per player, which is 30% for 50 players.
|
||||
in appearances within their view range. This is expensive and takes up a significant portion of our tick, about 0.45% per connected player
|
||||
as of 3/20/2022. meaning that with 50 players, 22.5% of our tick is being used up by just SendMaps, after all of our code has stopped executing. Thats only the average across all rounds, for most highpop rounds it can look like 0.6% of the tick per player, which is 30% for 50 players.
|
||||
|
||||
6. After SendMaps ends, client verbs sent to the server are executed, and its the last major step before the next tick begins.
|
||||
During the course of the tick, a client can send a command to the server saying that they have executed any verb. The actual code defined
|
||||
for that /verb/name() proc isnt executed until this point, and the way the MC is designed makes this especially likely to make verbs
|
||||
"overrun" the bounds of the tick they executed in, stopping the other tick from starting and thus delaying the MC firing in that tick.
|
||||
During the course of the tick, a client can send a command to the server saying that they have executed any verb. The actual code defined
|
||||
for that /verb/name() proc isnt executed until this point, and the way the MC is designed makes this especially likely to make verbs
|
||||
"overrun" the bounds of the tick they executed in, stopping the other tick from starting and thus delaying the MC firing in that tick.
|
||||
|
||||
The master controller can derive how much of the tick was used in: procs executing before it woke up (because of world.tick_usage), and SendMaps (because of world.map_cpu, since this is a running average you cant derive the tick spent on maptick on any particular tick). It cannot derive how much of the tick was used for sleeping procs resuming after the MC ran, or for verbs executing after SendMaps.
|
||||
The master controller can derive how much of the tick was used in: procs executing before it woke up (because of world.tick_usage), and SendMaps (because of world.map_cpu, since this is a running average you cant derive the tick spent on maptick on any particular tick). It cannot derive how much of the tick was used for sleeping procs resuming after the MC ran, or for verbs executing after SendMaps.
|
||||
|
||||
It is for these reasons why you should heavily limit processing done in verbs, while procs resuming after the MC are rare, verbs are not, and are much more likely to cause overtime since theyre literally at the end of the tick. If you make a verb, try to offload any expensive work to the beginning of the next tick via a verb management subsystem.
|
||||
|
||||
+70
-42
@@ -31,9 +31,9 @@ You'll find links to the relevant reference entries at the heading of each entry
|
||||
- [Gliding](#gliding)
|
||||
- [Sight](#sight)
|
||||
- [BYOND lighting](#byond-lighting)
|
||||
- [Luminosity](#luminosity)
|
||||
- [See in dark](#see-in-dark)
|
||||
- [Infrared](#infrared)
|
||||
- [Luminosity](#luminosity)
|
||||
- [See in dark](#see-in-dark)
|
||||
- [Infrared](#infrared)
|
||||
- [Invisibility](#invisibility)
|
||||
- [Layers](#layers)
|
||||
- [Planes](#planes)
|
||||
@@ -47,7 +47,7 @@ You'll find links to the relevant reference entries at the heading of each entry
|
||||
- [Color](#color)
|
||||
- [Transform](#transform)
|
||||
- [Lighting](#lighting)
|
||||
- [Animate()](#animate())
|
||||
- [Animate()](<#animate()>)
|
||||
- [GAGS](#gags)
|
||||
|
||||
## Appearances in BYOND
|
||||
@@ -137,15 +137,16 @@ Be careful of this.
|
||||
|
||||
The `vis_contents` list allows you to essentially say "Hey, render this thing ON me".
|
||||
|
||||
The definition of "ON" varies significantly with the `vis_flags` value of the *thing* being relayed.
|
||||
The definition of "ON" varies significantly with the `vis_flags` value of the _thing_ being relayed.
|
||||
See the ref [here](https://www.byond.com/docs/ref/#/atom/var/vis_flags).
|
||||
|
||||
Some flags of interest:
|
||||
|
||||
- `VIS_INHERIT_ID`: This allows you to link the object DIRECTLY to the thing it's drawn on,
|
||||
so clicking on the `vis_contents`'d object is just like clicking on the thing
|
||||
so clicking on the `vis_contents`'d object is just like clicking on the thing
|
||||
- `VIS_INHERIT_PLANE`: We will discuss [planes](#planes) more in future, but we use them to both effect rendering order and apply effects as a group.
|
||||
This flag changes the plane of any `vis_contents`'d object (while displayed on the source object) to the source's.
|
||||
This is occasionally useful, but should be used with care as it breaks any effects that rely on plane.
|
||||
This flag changes the plane of any `vis_contents`'d object (while displayed on the source object) to the source's.
|
||||
This is occasionally useful, but should be used with care as it breaks any effects that rely on plane.
|
||||
|
||||
Anything inside a `vis_contents` list will have its loc stored in its `vis_locs` variable.
|
||||
We very rarely use this, primarily just for clearing references from `vis_contents`.
|
||||
@@ -159,7 +160,6 @@ Careful how much you use it.
|
||||
|
||||
## Images
|
||||
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/image)
|
||||
|
||||
@@ -199,6 +199,7 @@ We then mirror this group of images into/out of the client's images list, based
|
||||
This is the pattern we use for things like the medihud, or robot trails.
|
||||
|
||||
## View
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/client/var/view)
|
||||
|
||||
@@ -231,6 +232,7 @@ See the ref for more details, but `normal` is gonna have the sharpest output, `d
|
||||
which causes some blur, and `blur` uses bilinear sampling, which causes a LOT of blur.
|
||||
|
||||
## Eye
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/client/var/eye)
|
||||
|
||||
@@ -241,10 +243,11 @@ It is by default `/client/var/mob` but it can be modified.
|
||||
This is how we accomplish ai eyes and ventcrawling, alongside most other effects that involve a player getting "into" something.
|
||||
|
||||
## Client Screen
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/HUD)
|
||||
|
||||
Similar to client images but not *quite* the same, we can also insert objects onto our client's literal screen
|
||||
Similar to client images but not _quite_ the same, we can also insert objects onto our client's literal screen
|
||||
|
||||
This is done by giving it an appropriate `screen_loc` value, and inserting it into the client's `screen` list.
|
||||
|
||||
@@ -271,6 +274,7 @@ This gets into dmf fuckery but you can use [window ids](https://www.byond.com/do
|
||||
Useful for creating popup windows and such.
|
||||
|
||||
## Blend Mode
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/blend_mode)
|
||||
|
||||
@@ -281,6 +285,7 @@ There's a whole bunch of options but really the only one you need to know offhan
|
||||
This is how we do lighting effects, since the lighting [plane](#planes) can be used to multiply just normal coloring. If it's all black, the full screen goes black.
|
||||
|
||||
## Appearance Flags
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/appearance_flags)
|
||||
|
||||
@@ -288,12 +293,14 @@ This is how we do lighting effects, since the lighting [plane](#planes) can be u
|
||||
I won't go over all of them, but I will discuss a few.
|
||||
|
||||
Flags of interest:
|
||||
|
||||
- `LONG_GLIDE`: without this, diagonal movements will automatically take sqrt(2) more time, to account for the greater distance. We do this calculus automatically, and so want this flipped to disable the behavior.
|
||||
- `KEEP_TOGETHER`: this allows us to force overlays to render in the same manner as the thing they're overlaid on. Most useful for humans to make alpha changes effect all overlays.
|
||||
- `PLANE_MASTER`: I will get into this later, but this allows us to use the [plane](#planes) var to relay renders onto screen objects, so we can apply visual effects and masks and such.
|
||||
- `TILE_BOUND`: By default if something is part in one tile and part in another it will display if either is visible. With this set it'll go off its loc value only.
|
||||
|
||||
## Gliding
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/gliding)
|
||||
|
||||
@@ -314,7 +321,7 @@ We will also occasionally use glide size as a way to force a transition between
|
||||
There's extra cruft here.
|
||||
|
||||
> Something you should know: Our gliding system attempts to account for time dilation when setting move rates.
|
||||
This is done in a very simplistic way however, so a spike in td will lead to jumping around as glide rate is outpaced by mob movement rate.
|
||||
> This is done in a very simplistic way however, so a spike in td will lead to jumping around as glide rate is outpaced by mob movement rate.
|
||||
|
||||
On that note, it is VERY important that glide rate is the same or near the same as actual move rate.
|
||||
Otherwise you will get strange jumping and jitter.
|
||||
@@ -328,17 +335,18 @@ This is why you'll sometime see a stutter in your step when slowed
|
||||
Just so you know, client movement works off `/client/var/move_delay` which sets the next time an input will be accepted. It's typically glide rate, but is in some cases just 1 tick.
|
||||
|
||||
## Sight
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/mob/var/sight)
|
||||
|
||||
`/mob/var/sight` is a set of bitflags that *mostly* set what HAS to render on your screen. Be that mobs, turfs, etc.
|
||||
`/mob/var/sight` is a set of bitflags that _mostly_ set what HAS to render on your screen. Be that mobs, turfs, etc.
|
||||
That said, there is some nuance here so I'ma get into that.
|
||||
|
||||
- `SEE_INFRA`: I'll get into this later, but infrared is essentially a copy of BYOND darkness, it's not something we currently use.
|
||||
- `SEE_BLACKNESS`: This relates heavily to [planes](#planes), essentially typically the "blackness" (that darkness that masks things that you can't see)
|
||||
is rendered separately, out of our control as "users".
|
||||
However, if the `SEE_BLACKNESS` flag is set, it will instead render on plane 0, the default BYOND plane.
|
||||
This allows us to capture it, and say, blur it, or redraw it elsewhere. This is in theory very powerful, but not possible with the 'side_map' [map format](https://www.byond.com/docs/ref/#/world/var/map_format)
|
||||
is rendered separately, out of our control as "users".
|
||||
However, if the `SEE_BLACKNESS` flag is set, it will instead render on plane 0, the default BYOND plane.
|
||||
This allows us to capture it, and say, blur it, or redraw it elsewhere. This is in theory very powerful, but not possible with the 'side_map' [map format](https://www.byond.com/docs/ref/#/world/var/map_format)
|
||||
|
||||
## BYOND Lighting
|
||||
|
||||
@@ -354,6 +362,7 @@ sort of like if there was a wall between them. This hiding uses BYOND darkness,
|
||||
I'll use this section to discuss all the little bits that contribute to this behavior
|
||||
|
||||
### Luminosity
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/luminosity)
|
||||
|
||||
@@ -361,9 +370,10 @@ I'll use this section to discuss all the little bits that contribute to this beh
|
||||
It's real simple, just a range of tiles that will be lit, respecting sight-lines and such of course.
|
||||
|
||||
> This "light" is how `/proc/view()` knows if something is in view or not. Oh by the by `view()` respects lighting.
|
||||
You can actually force it to use a particular mob's sight to avoid aspects of this, this is what `dview()` is
|
||||
> You can actually force it to use a particular mob's sight to avoid aspects of this, this is what `dview()` is
|
||||
|
||||
### See in Dark
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/mob/var/see_in_dark)
|
||||
|
||||
@@ -373,6 +383,7 @@ This is why when you stand in darkness you can see yourself, and why you can see
|
||||
It's quite simple, but worth describing.
|
||||
|
||||
### Infrared
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/mob/var/see_infrared)
|
||||
|
||||
@@ -380,6 +391,7 @@ Infrared vision can be thought of as a hidden copy of standard BYOND darkness.
|
||||
It's not something we actually use, but I think you should know about it, because the whole thing is real confusing without context.
|
||||
|
||||
## Invisibility
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/invisibility)
|
||||
|
||||
@@ -391,6 +403,7 @@ It's also used to hide some more then ghost invisible things, like some timers a
|
||||
`/mob/var/see_invisible` is the catcher of invisibility. If a mob's see_invisible is higher then a target/s invisibility, it'll be shown. Really basic stuff.
|
||||
|
||||
## Layers
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/layer)
|
||||
|
||||
@@ -412,6 +425,7 @@ We can think of this as applying to planes, since we don't want it interlaying w
|
||||
This allows us to keep relative layer differences while not needing to make all sources static. Often very useful.
|
||||
|
||||
## Planes
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/plane)
|
||||
|
||||
@@ -440,12 +454,13 @@ called `/atom/movable/plane_master_controller`.
|
||||
This is somewhat outmoded by our use of [render relays](#render-targetsource), but it's still valid and occasionally useful.
|
||||
|
||||
> Something you should know: Plane masters effect ONLY the map their screen_loc is on.
|
||||
For this reason, we are forced to generate whole copies of the set of plane masters with the proper screen_loc to make subviews look right
|
||||
> For this reason, we are forced to generate whole copies of the set of plane masters with the proper screen_loc to make subviews look right
|
||||
|
||||
> Warning: Planes have some restrictions on valid values. They NEED to be whole integers, and they NEED to have an absolute value of `10000`.
|
||||
This is to support `FLOAT_PLANE`, which lives out at the very edge of the 32 bit int range.
|
||||
> This is to support `FLOAT_PLANE`, which lives out at the very edge of the 32 bit int range.
|
||||
|
||||
## Render Target/Source
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/render_target)
|
||||
|
||||
@@ -454,7 +469,7 @@ The target object is given a `/atom/var/render_target` value, and anything that
|
||||
|
||||
When I say render onto, I mean it literally. It is like adding a second step in the rendering process.
|
||||
|
||||
You can even prepend * to the render target value to disable the initial render, and JUST render via the render source.
|
||||
You can even prepend \* to the render target value to disable the initial render, and JUST render via the render source.
|
||||
|
||||
### Our Implementation
|
||||
|
||||
@@ -462,13 +477,14 @@ We use render targets to create "render relays" which can be used to link [plane
|
||||
See [the renderer documentation](../../code/_onclick/hud/rendering/_render_readme.md) for visualizations for this.
|
||||
|
||||
> Of note: this linking behavior is accomplished by adding a screen object to link onto with a plane value of the desired PM we want to relay onto.
|
||||
Layer is VERY important here, and will be set based off the layer of the last plane master.
|
||||
This means plane order is not always the absolute order in which different plane masters render. Be careful of this.
|
||||
> Layer is VERY important here, and will be set based off the layer of the last plane master.
|
||||
> This means plane order is not always the absolute order in which different plane masters render. Be careful of this.
|
||||
|
||||
> To edit and display planes and plane connections in game, run the `Edit/Debug Planes` command.
|
||||
It will open a ui that allows you to view relay connections, plane master descriptions, and edit their values and effects.
|
||||
> It will open a ui that allows you to view relay connections, plane master descriptions, and edit their values and effects.
|
||||
|
||||
## Multiz
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- Reference: Hell of our own creation
|
||||
|
||||
@@ -483,10 +499,10 @@ Except not, for 2 reasons. One more annoying then the other.
|
||||
|
||||
- 1: It looked like dog doo-doo. This pattern destroyed the old planes of everything vis_contents'd, so effects/lighting/dropshadows broke bad.
|
||||
- 2: I alluded to this earlier, but it totally breaks the `side_map` [map format](https://www.byond.com/docs/ref/#/world/var/map_format)
|
||||
which I need for a massive resprite I'm helping with. This is because `side_map` changes how rendering order works,
|
||||
going off "distance" from the front of the frame.
|
||||
The issue here is it of course needs a way to group things that are even allowed to overlap, so it uses plane.
|
||||
So when you squish everything down onto one plane, this of course breaks horribly and fucks you.
|
||||
which I need for a massive resprite I'm helping with. This is because `side_map` changes how rendering order works,
|
||||
going off "distance" from the front of the frame.
|
||||
The issue here is it of course needs a way to group things that are even allowed to overlap, so it uses plane.
|
||||
So when you squish everything down onto one plane, this of course breaks horribly and fucks you.
|
||||
|
||||
Ok then, old way's not workable. What will we do instead?
|
||||
|
||||
@@ -506,6 +522,7 @@ There are a few edge cases where we need to work in explicitly offsets, but thos
|
||||
This is stupid, but it's makable, and what we do.
|
||||
|
||||
## Mouse Opacity
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/mouse_opacity)
|
||||
|
||||
@@ -519,12 +536,13 @@ We will on occasion use mouse opacity to expand hitboxes, but more often this is
|
||||
or just low alpha pixels on the sprite.
|
||||
|
||||
> Note: Mouse opacity will only matter if the atom is being rendered on its own. [Overlays](#overlays)(and [images](#images))
|
||||
will NOT work as expected with this.
|
||||
However, you can still have totally transparent overlays. If you render them onto a [plane master](#planes) with the desired mouse opacity value
|
||||
it will work as expected. This is because as a step of the rendering pipeline the overlay is rendered ONTO the plane master, and then the plane
|
||||
master's effects are applied.
|
||||
> will NOT work as expected with this.
|
||||
> However, you can still have totally transparent overlays. If you render them onto a [plane master](#planes) with the desired mouse opacity value
|
||||
> it will work as expected. This is because as a step of the rendering pipeline the overlay is rendered ONTO the plane master, and then the plane
|
||||
> master's effects are applied.
|
||||
|
||||
## Filters
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/filters)
|
||||
|
||||
@@ -547,9 +565,10 @@ we will need our own management system. This is why we, unlike byond, use a wrap
|
||||
This system has the potential to break animations and other such things. Take care.
|
||||
|
||||
> We have a debug tool for filters, called filterrific. You can access it in-game by vving an atom, going to the dropdown, and hitting `Edit Filters`
|
||||
It'll let you add and tweak *most* of the filters in BYOND.
|
||||
> It'll let you add and tweak _most_ of the filters in BYOND.
|
||||
|
||||
## Particles
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/{notes}/particles)
|
||||
|
||||
@@ -561,9 +580,10 @@ The type is `/obj/effect/abstract/particle_holder`. Interacting with it's real s
|
||||
It'll do the rest.
|
||||
|
||||
> We have a debug tool for particles. You can access it in-game by vving an atom, going to the dropdown, and hitting `Edit Particles`
|
||||
It'll let you add and tweak the particles attached to that atom.
|
||||
> It'll let you add and tweak the particles attached to that atom.
|
||||
|
||||
## Pixel Offsets
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/pixel_x)
|
||||
|
||||
@@ -582,6 +602,7 @@ This doesn't really matter for our current map format, but for anything that tak
|
||||
it matters a whole ton. It's kinda a hard idea to get across, but I hope you have at least some idea.
|
||||
|
||||
## Map Formats
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/world/var/map_format)
|
||||
|
||||
@@ -621,6 +642,7 @@ That's because we use [`LEGACY_MOVEMENT_MODE`](https://www.byond.com/docs/ref/#/
|
||||
This means we're almost always doomed to getting weird layering, cascading out from the source of the issue in a way that is quite hard to debug.
|
||||
|
||||
Oh right I forgot to mention. Sorta touched on it with [Pixel offsets](#pixel-offsets).
|
||||
|
||||
- `pixel_x/y` change something's position in physical space. So if you shoot this real high it'll layer as if it is where it appears to be,
|
||||
- `pixel_w/z` change something's VISUAL position. So you can LAYER as if you're at the bottom of the screen, but actually sit at the top.
|
||||
|
||||
@@ -632,13 +654,14 @@ What follows is a description, from lummox, of how physical conflicts are handle
|
||||
From [this post](https://www.byond.com/forum/post/2656961#comment26050050)
|
||||
|
||||
For the physical position, Y increases upward relative to the screen; view position has Y going downward. The topological order between two icons goes like this, in order:
|
||||
- 1) If the icons don't visually overlap, they don't care which goes first.
|
||||
- 2) If the icons do not overlap on the physical Y axis, whichever one is further back is drawn first.
|
||||
- 3) If one has a lower layer, it's drawn first.
|
||||
- 4) If one's "near" edge (physical Y) is closer to the top of the screen than the other, it gets drawn first.
|
||||
- 5) If one's center is left of the other, it's drawn first.
|
||||
- 6) If the two physical bounds are identical, whichever icon came first in the original array order is drawn first.
|
||||
- 7) All tiebreakers failed so the icons give up and don't care which goes first.
|
||||
|
||||
- 1. If the icons don't visually overlap, they don't care which goes first.
|
||||
- 2. If the icons do not overlap on the physical Y axis, whichever one is further back is drawn first.
|
||||
- 3. If one has a lower layer, it's drawn first.
|
||||
- 4. If one's "near" edge (physical Y) is closer to the top of the screen than the other, it gets drawn first.
|
||||
- 5. If one's center is left of the other, it's drawn first.
|
||||
- 6. If the two physical bounds are identical, whichever icon came first in the original array order is drawn first.
|
||||
- 7. All tiebreakers failed so the icons give up and don't care which goes first.
|
||||
|
||||
It is worth stating clearly. Things will only "visually overlap" if they sit on the same [plane](#planes).
|
||||
This is NOT counting [relays](#render-targetsource)/[plane masters](#planes) (when a plane master is relayed onto something else, you can think of it like drawing the WHOLE PLANE MASTER)
|
||||
@@ -651,7 +674,7 @@ One more thing. Big icons are fucked
|
||||
|
||||
From the byond reference
|
||||
|
||||
>If you use an icon wider than one tile, the "footprint" of the isometric icon (the actual map tiles it takes up) will always be a square. That is, if your normal tile size is 64 and you want to show a 128x128 icon, the icon is two tiles wide and so it will take up a 2×2-tile area on the map. The height of a big icon is irrelevant--any excess height beyond width/2 is used to show vertical features. To draw this icon properly, other tiles on that same ground will be moved behind it in the drawing order.
|
||||
> If you use an icon wider than one tile, the "footprint" of the isometric icon (the actual map tiles it takes up) will always be a square. That is, if your normal tile size is 64 and you want to show a 128x128 icon, the icon is two tiles wide and so it will take up a 2×2-tile area on the map. The height of a big icon is irrelevant--any excess height beyond width/2 is used to show vertical features. To draw this icon properly, other tiles on that same ground will be moved behind it in the drawing order.
|
||||
> One important warning about using big icons in isometric mode is that you should only do this with dense atoms. If part of a big mob icon covers the same tile as a tall building for instance, the tall building is moved back and it could be partially covered by other turfs that are actually behind it. A mob walking onto a very large non-dense turf icon would experience similar irregularities.
|
||||
|
||||
These can cause very annoying flickering. In fact, MUCH of how rendering works causes flickering. This is because we don't decide on a pixel by pixel case, the engine groups sprites up into a sort of rendering stack, unable to split them up.
|
||||
@@ -683,6 +706,7 @@ Legacy support for how byond rendering used to work. It essentially locked icon
|
||||
it would be automatically broken down into smaller icon states, which you would need to manually display. Not something we need to care about
|
||||
|
||||
## Color
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/color)
|
||||
|
||||
@@ -706,9 +730,10 @@ You can accomplish some really fun effects with this trick, it gives you a LOT o
|
||||
and leads to some fun vfx.
|
||||
|
||||
> We have a debug tool for color matrixes. Just VV an atom, go to the VV dropdown and look for the `Edit Color as Matrix` entry.
|
||||
It'll help visualize this process quite well. Play around with it, it's fun.
|
||||
> It'll help visualize this process quite well. Play around with it, it's fun.
|
||||
|
||||
## Transform
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/atom/var/transform)
|
||||
|
||||
@@ -721,12 +746,13 @@ helper procs for pretty much everything it can do.
|
||||
It's not totally without explanation, and I figured you might wanna know about it. Not a whole lot more to say tho. Neat tool.
|
||||
|
||||
## Lighting
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- Reference: Hell of our own creation
|
||||
|
||||
I wanted to take this chance to briefly explain the essentials of how our lighting system works.
|
||||
Essentially, each tile has a lighting [overlay](#overlays) (technically an [underlay](https://www.byond.com/docs/ref/#/atom/var/underlays)
|
||||
which is just overlays but drawn under).
|
||||
which is just overlays but drawn under).
|
||||
Anyway, each underlay is a color gradient, with red green and blue and alpha in each corner.
|
||||
Every "corner" (we call them lighting corners) on the map impacts the 4 colors that touch it.
|
||||
This is done with color matrixes. This allows us to apply color and lighting in a smooth way, while only needing 1 overlay per tile.
|
||||
@@ -736,6 +762,7 @@ But it covers the core idea, the rest should be derivable, and you're more quali
|
||||
and forget to update this file.
|
||||
|
||||
## Animate()
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Reference Entry](https://www.byond.com/docs/ref/#/proc/animate)
|
||||
|
||||
@@ -750,6 +777,7 @@ parallel. It's got a lot of nuance to it, but it's real useful. Works on filters
|
||||
Lets you give radiation glow a warm pulse, that sort of thing.
|
||||
|
||||
## GAGS
|
||||
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- Reference: Hell of our own creation
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Attributions
|
||||
|
||||
## Badges
|
||||
|
||||
`built-with-resentment.svg` and `contains-technical-debt.svg` were originally sourced from https://forthebadge.com/, with the repository located at https://github.com/BraveUX/for-the-badge. `made-in-byond.gif` is a user-generated modification of one of these badges provided by this service.
|
||||
|
||||
## Comics
|
||||
|
||||
@@ -13,24 +13,24 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.merged == true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Generate App Token
|
||||
id: app-token-generation
|
||||
uses: actions/create-github-app-token@v2
|
||||
if: env.APP_PRIVATE_KEY != '' && env.APP_ID != ''
|
||||
with:
|
||||
app-id: ${{ secrets.APP_ID }}
|
||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
env:
|
||||
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
APP_ID: ${{ secrets.APP_ID }}
|
||||
- name: Generate App Token
|
||||
id: app-token-generation
|
||||
uses: actions/create-github-app-token@v2
|
||||
if: env.APP_PRIVATE_KEY != '' && env.APP_ID != ''
|
||||
with:
|
||||
app-id: ${{ secrets.APP_ID }}
|
||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
env:
|
||||
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
APP_ID: ${{ secrets.APP_ID }}
|
||||
|
||||
- name: Run auto changelog
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { processAutoChangelog } = await import('${{ github.workspace }}/tools/pull_request_hooks/autoChangelog.js')
|
||||
await processAutoChangelog({ github, context })
|
||||
github-token: ${{ steps.app-token-generation.outputs.token || secrets.GITHUB_TOKEN }}
|
||||
- name: Run auto changelog
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { processAutoChangelog } = await import('${{ github.workspace }}/tools/pull_request_hooks/autoChangelog.js')
|
||||
await processAutoChangelog({ github, context })
|
||||
github-token: ${{ steps.app-token-generation.outputs.token || secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Autowiki
|
||||
on:
|
||||
schedule:
|
||||
- cron: "5 4 * * *"
|
||||
- cron: "5 4 * * *"
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -10,36 +10,36 @@ jobs:
|
||||
autowiki:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Check for AUTOWIKI_USERNAME"
|
||||
id: secrets_set
|
||||
env:
|
||||
ENABLER_SECRET: ${{ secrets.AUTOWIKI_USERNAME }}
|
||||
run: |
|
||||
unset SECRET_EXISTS
|
||||
if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
|
||||
echo "SECRETS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
uses: actions/checkout@v4
|
||||
- name: Install BYOND
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
uses: ./.github/actions/restore_or_install_byond
|
||||
- name: Install rust-g
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
run: |
|
||||
bash tools/ci/install_rust_g.sh
|
||||
- name: Compile and generate Autowiki files
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
run: |
|
||||
source $HOME/BYOND/byond/bin/byondsetup
|
||||
tools/build/build --ci autowiki
|
||||
- name: Run Autowiki
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
env:
|
||||
USERNAME: ${{ secrets.AUTOWIKI_USERNAME }}
|
||||
PASSWORD: ${{ secrets.AUTOWIKI_PASSWORD }}
|
||||
run: |
|
||||
cd tools/autowiki
|
||||
npm install
|
||||
cd ../..
|
||||
node tools/autowiki/autowiki.js data/autowiki_edits.txt data/autowiki_files/
|
||||
- name: "Check for AUTOWIKI_USERNAME"
|
||||
id: secrets_set
|
||||
env:
|
||||
ENABLER_SECRET: ${{ secrets.AUTOWIKI_USERNAME }}
|
||||
run: |
|
||||
unset SECRET_EXISTS
|
||||
if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
|
||||
echo "SECRETS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
uses: actions/checkout@v4
|
||||
- name: Install BYOND
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
uses: ./.github/actions/restore_or_install_byond
|
||||
- name: Install rust-g
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
run: |
|
||||
bash tools/ci/install_rust_g.sh
|
||||
- name: Compile and generate Autowiki files
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
run: |
|
||||
source $HOME/BYOND/byond/bin/byondsetup
|
||||
tools/build/build --ci autowiki
|
||||
- name: Run Autowiki
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
env:
|
||||
USERNAME: ${{ secrets.AUTOWIKI_USERNAME }}
|
||||
PASSWORD: ${{ secrets.AUTOWIKI_PASSWORD }}
|
||||
run: |
|
||||
cd tools/autowiki
|
||||
npm install
|
||||
cd ../..
|
||||
node tools/autowiki/autowiki.js data/autowiki_edits.txt data/autowiki_files/
|
||||
|
||||
@@ -3,11 +3,11 @@ name: CI Suite
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 'project/**'
|
||||
- master
|
||||
- "project/**"
|
||||
merge_group:
|
||||
branches:
|
||||
- master
|
||||
- master
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -65,14 +65,21 @@ jobs:
|
||||
|
||||
compare_screenshots:
|
||||
if: needs.collect_data.outputs.alternate_tests == '[]' || needs.run_alternate_tests.result == 'success'
|
||||
needs: [ collect_data, run_all_tests, run_alternate_tests ]
|
||||
needs: [collect_data, run_all_tests, run_alternate_tests]
|
||||
name: Compare Screenshot Tests
|
||||
uses: ./.github/workflows/compare_screenshots.yml
|
||||
|
||||
completion_gate: # Serves as a non-moving target for branch rulesets
|
||||
if: always() && !cancelled()
|
||||
name: Completion Gate
|
||||
needs: [ compare_screenshots, compile_all_maps, run_all_tests, run_alternate_tests, run_linters ]
|
||||
needs:
|
||||
[
|
||||
compare_screenshots,
|
||||
compile_all_maps,
|
||||
run_all_tests,
|
||||
run_alternate_tests,
|
||||
run_linters,
|
||||
]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Decide whether the needed jobs succeeded or failed
|
||||
|
||||
@@ -7,7 +7,6 @@ on:
|
||||
|
||||
jobs:
|
||||
assign-users:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
@@ -26,5 +25,5 @@ jobs:
|
||||
if: steps.CodeOwnersParser.outputs.owners != ''
|
||||
uses: tgstation/RequestReviewFromUser@v1
|
||||
with:
|
||||
separator: ' '
|
||||
separator: " "
|
||||
users: ${{ steps.CodeOwnersParser.outputs.owners }}
|
||||
|
||||
@@ -2,7 +2,7 @@ name: Compile changelogs
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
python-version: "3.x"
|
||||
|
||||
- name: "Install deps"
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: 'New PR Notification'
|
||||
name: "New PR Notification"
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, closed]
|
||||
|
||||
+49
-49
@@ -11,56 +11,56 @@ jobs:
|
||||
pull-requests: write # to apply labels
|
||||
issues: write # to apply labels
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Run Auto Labeler
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { get_updated_label_set } = await import('${{ github.workspace }}/tools/pull_request_hooks/autoLabel.js');
|
||||
const new_labels = await get_updated_label_set({ github, context });
|
||||
github.rest.issues.setLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: new_labels,
|
||||
});
|
||||
console.log(`Labels updated: ${new_labels}`);
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Run Auto Labeler
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { get_updated_label_set } = await import('${{ github.workspace }}/tools/pull_request_hooks/autoLabel.js');
|
||||
const new_labels = await get_updated_label_set({ github, context });
|
||||
github.rest.issues.setLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: new_labels,
|
||||
});
|
||||
console.log(`Labels updated: ${new_labels}`);
|
||||
gbp:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.action == 'opened' || github.event.action == 'closed'
|
||||
steps:
|
||||
- name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps"
|
||||
id: value_holder
|
||||
env:
|
||||
ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }}
|
||||
run: |
|
||||
unset SECRET_EXISTS
|
||||
if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
|
||||
echo "ACTIONS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup git
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
run: |
|
||||
git config --global user.name "gbp-action"
|
||||
git config --global user.email "<>"
|
||||
- name: Checkout alternate branch
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: "gbp-balances" # The branch name
|
||||
path: gbp-balances
|
||||
# This is to ensure we keep the gbp.toml from master
|
||||
# without having to update our separate branch.
|
||||
- name: Copy configuration
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml
|
||||
- name: GBP action
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: tgstation/gbp-action@master
|
||||
with:
|
||||
branch: "gbp-balances"
|
||||
directory: ./gbp-balances
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps"
|
||||
id: value_holder
|
||||
env:
|
||||
ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }}
|
||||
run: |
|
||||
unset SECRET_EXISTS
|
||||
if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
|
||||
echo "ACTIONS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup git
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
run: |
|
||||
git config --global user.name "gbp-action"
|
||||
git config --global user.email "<>"
|
||||
- name: Checkout alternate branch
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: "gbp-balances" # The branch name
|
||||
path: gbp-balances
|
||||
# This is to ensure we keep the gbp.toml from master
|
||||
# without having to update our separate branch.
|
||||
- name: Copy configuration
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml
|
||||
- name: GBP action
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: tgstation/gbp-action@master
|
||||
with:
|
||||
branch: "gbp-balances"
|
||||
directory: ./gbp-balances
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -2,43 +2,43 @@ name: GBP Collection
|
||||
# Every hour at the :20 minute mark. GitHub tells us to pick odd hours, instead of just using the start.
|
||||
on:
|
||||
schedule:
|
||||
- cron: "20 * * * *"
|
||||
- cron: "20 * * * *"
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
gbp_collection:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps"
|
||||
id: value_holder
|
||||
env:
|
||||
ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }}
|
||||
run: |
|
||||
unset SECRET_EXISTS
|
||||
if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
|
||||
echo "ACTIONS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup git
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
- name: Checkout alternate branch
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: "gbp-balances" # The branch name
|
||||
path: gbp-balances
|
||||
# This is to ensure we keep the gbp.toml from master
|
||||
# without having to update our separate branch.
|
||||
- name: Copy configuration
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml
|
||||
- name: GBP action
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: tgstation/gbp-action@master
|
||||
with:
|
||||
collect: "true"
|
||||
directory: ./gbp-balances
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: "Check for ACTION_ENABLER secret and pass true to output if it exists to be checked by later steps"
|
||||
id: value_holder
|
||||
env:
|
||||
ENABLER_SECRET: ${{ secrets.ACTION_ENABLER }}
|
||||
run: |
|
||||
unset SECRET_EXISTS
|
||||
if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
|
||||
echo "ACTIONS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup git
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
- name: Checkout alternate branch
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: "gbp-balances" # The branch name
|
||||
path: gbp-balances
|
||||
# This is to ensure we keep the gbp.toml from master
|
||||
# without having to update our separate branch.
|
||||
- name: Copy configuration
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
run: cp ./.github/gbp.toml ./gbp-balances/.github/gbp.toml
|
||||
- name: GBP action
|
||||
if: steps.value_holder.outputs.ACTIONS_ENABLED
|
||||
uses: tgstation/gbp-action@master
|
||||
with:
|
||||
collect: "true"
|
||||
directory: ./gbp-balances
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
name: Generate documentation
|
||||
on:
|
||||
schedule:
|
||||
- cron: "44 */6 * * *"
|
||||
- cron: "44 */6 * * *"
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
generate_documentation:
|
||||
permissions:
|
||||
contents: write # for JamesIves/github-pages-deploy-action to push changes in repo
|
||||
contents: write # for JamesIves/github-pages-deploy-action to push changes in repo
|
||||
if: ( !contains(github.event.head_commit.message, '[ci skip]') )
|
||||
runs-on: ubuntu-22.04
|
||||
concurrency: gen-docs
|
||||
|
||||
@@ -8,11 +8,11 @@ jobs:
|
||||
remove_guide_comments:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Remove guide comments
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { removeGuideComments } = await import('${{ github.workspace }}/tools/pull_request_hooks/removeGuideComments.js')
|
||||
await removeGuideComments({ github, context })
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Remove guide comments
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { removeGuideComments } = await import('${{ github.workspace }}/tools/pull_request_hooks/removeGuideComments.js')
|
||||
await removeGuideComments({ github, context })
|
||||
|
||||
@@ -3,29 +3,29 @@ on:
|
||||
workflow_run:
|
||||
workflows: [CI Suite]
|
||||
types:
|
||||
- completed
|
||||
- completed
|
||||
jobs:
|
||||
rerun_flaky_tests:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.run_attempt == 1 }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Rerun flaky tests
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { rerunFlakyTests } = await import('${{ github.workspace }}/tools/pull_request_hooks/rerunFlakyTests.js')
|
||||
await rerunFlakyTests({ github, context })
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Rerun flaky tests
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { rerunFlakyTests } = await import('${{ github.workspace }}/tools/pull_request_hooks/rerunFlakyTests.js')
|
||||
await rerunFlakyTests({ github, context })
|
||||
report_flaky_tests:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.run_attempt == 2 }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Report flaky tests
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { reportFlakyTests } = await import('${{ github.workspace }}/tools/pull_request_hooks/rerunFlakyTests.js')
|
||||
await reportFlakyTests({ github, context })
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Report flaky tests
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { reportFlakyTests } = await import('${{ github.workspace }}/tools/pull_request_hooks/rerunFlakyTests.js')
|
||||
await reportFlakyTests({ github, context })
|
||||
|
||||
@@ -7,6 +7,6 @@ jobs:
|
||||
link_rounds:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: tgstation/round_linker@master
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: tgstation/round_linker@master
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
+15
-16
@@ -2,29 +2,28 @@ name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
permissions:
|
||||
issues: write # for actions/stale to close stale issues
|
||||
pull-requests: write # for actions/stale to close stale PRs
|
||||
issues: write # for actions/stale to close stale issues
|
||||
pull-requests: write # for actions/stale to close stale PRs
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-pr-message: "This PR has been inactive for long enough to be automatically marked as stale. This means it is at risk of being auto closed in ~ 7 days, please address any outstanding review items and ensure your PR is finished, if these are all true and you are auto-staled anyway, you need to actively ask maintainers if your PR will be merged. Once you have done any of the previous actions then you should request a maintainer remove the stale label on your PR, to reset the stale timer. If you feel no maintainer will respond in that time, you may wish to close this PR youself, while you seek maintainer comment, as you will then be able to reopen the PR yourself"
|
||||
days-before-stale: 7
|
||||
days-before-close: 7
|
||||
stale-pr-label: 'Stale'
|
||||
days-before-issue-stale: -1
|
||||
stale-issue-label: 'Cleanup Flagged'
|
||||
remove-issue-stale-when-updated: false
|
||||
exempt-pr-labels: 'RED LABEL,Good First PR'
|
||||
operations-per-run: 300
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-pr-message: "This PR has been inactive for long enough to be automatically marked as stale. This means it is at risk of being auto closed in ~ 7 days, please address any outstanding review items and ensure your PR is finished, if these are all true and you are auto-staled anyway, you need to actively ask maintainers if your PR will be merged. Once you have done any of the previous actions then you should request a maintainer remove the stale label on your PR, to reset the stale timer. If you feel no maintainer will respond in that time, you may wish to close this PR youself, while you seek maintainer comment, as you will then be able to reopen the PR yourself"
|
||||
days-before-stale: 7
|
||||
days-before-close: 7
|
||||
stale-pr-label: "Stale"
|
||||
days-before-issue-stale: -1
|
||||
stale-issue-label: "Cleanup Flagged"
|
||||
remove-issue-stale-when-updated: false
|
||||
exempt-pr-labels: "RED LABEL,Good First PR"
|
||||
operations-per-run: 300
|
||||
|
||||
@@ -6,36 +6,36 @@
|
||||
name: Test Merge Detector
|
||||
on:
|
||||
schedule:
|
||||
- cron: "*/30 * * * *"
|
||||
- cron: "*/30 * * * *"
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
test_merge_bot:
|
||||
name: Test Merge Detector
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check for GET_TEST_MERGES_URL
|
||||
id: secrets_set
|
||||
env:
|
||||
ENABLER_SECRET: ${{ secrets.GET_TEST_MERGES_URL }}
|
||||
run: |
|
||||
unset SECRET_EXISTS
|
||||
if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
|
||||
echo "GET_TEST_MERGES_URL=$SECRET_EXISTS" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
if: steps.secrets_set.outputs.GET_TEST_MERGES_URL
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare module
|
||||
if: steps.secrets_set.outputs.GET_TEST_MERGES_URL
|
||||
run: |
|
||||
# This is needed because node-fetch needs import and doesn't work with require :/
|
||||
echo "{\"type\": \"module\"}" > package.json
|
||||
npm install node-fetch
|
||||
- name: Check for test merges
|
||||
if: steps.secrets_set.outputs.GET_TEST_MERGES_URL
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
GET_TEST_MERGES_URL: ${{ secrets.GET_TEST_MERGES_URL }}
|
||||
with:
|
||||
script: |
|
||||
const { processTestMerges } = await import('${{ github.workspace }}/tools/test_merge_bot/main.js')
|
||||
await processTestMerges({ github, context })
|
||||
name: Test Merge Detector
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check for GET_TEST_MERGES_URL
|
||||
id: secrets_set
|
||||
env:
|
||||
ENABLER_SECRET: ${{ secrets.GET_TEST_MERGES_URL }}
|
||||
run: |
|
||||
unset SECRET_EXISTS
|
||||
if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
|
||||
echo "GET_TEST_MERGES_URL=$SECRET_EXISTS" >> $GITHUB_OUTPUT
|
||||
- name: Checkout
|
||||
if: steps.secrets_set.outputs.GET_TEST_MERGES_URL
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare module
|
||||
if: steps.secrets_set.outputs.GET_TEST_MERGES_URL
|
||||
run: |
|
||||
# This is needed because node-fetch needs import and doesn't work with require :/
|
||||
echo "{\"type\": \"module\"}" > package.json
|
||||
npm install node-fetch
|
||||
- name: Check for test merges
|
||||
if: steps.secrets_set.outputs.GET_TEST_MERGES_URL
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
GET_TEST_MERGES_URL: ${{ secrets.GET_TEST_MERGES_URL }}
|
||||
with:
|
||||
script: |
|
||||
const { processTestMerges } = await import('${{ github.workspace }}/tools/test_merge_bot/main.js')
|
||||
await processTestMerges({ github, context })
|
||||
|
||||
@@ -2,39 +2,39 @@ name: TGS Test Suite
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- 'project/**'
|
||||
- 'gh-readonly-queue/master/**'
|
||||
- 'gh-readonly-queue/project/**'
|
||||
- master
|
||||
- "project/**"
|
||||
- "gh-readonly-queue/master/**"
|
||||
- "gh-readonly-queue/project/**"
|
||||
paths:
|
||||
- '.tgs.yml'
|
||||
- '.github/workflows/tgs_test.yml'
|
||||
- 'dependencies.sh'
|
||||
- 'code/__DEFINES/tgs.config.dm'
|
||||
- 'code/__DEFINES/tgs.dm'
|
||||
- 'code/game/world.dm'
|
||||
- 'code/modules/tgs/**'
|
||||
- 'tools/bootstrap/**'
|
||||
- 'tools/tgs_scripts/**'
|
||||
- 'tools/tgs_test/**'
|
||||
- ".tgs.yml"
|
||||
- ".github/workflows/tgs_test.yml"
|
||||
- "dependencies.sh"
|
||||
- "code/__DEFINES/tgs.config.dm"
|
||||
- "code/__DEFINES/tgs.dm"
|
||||
- "code/game/world.dm"
|
||||
- "code/modules/tgs/**"
|
||||
- "tools/bootstrap/**"
|
||||
- "tools/tgs_scripts/**"
|
||||
- "tools/tgs_test/**"
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- 'project/**'
|
||||
- master
|
||||
- "project/**"
|
||||
paths:
|
||||
- '.tgs.yml'
|
||||
- '.github/workflows/tgs_test.yml'
|
||||
- 'dependencies.sh'
|
||||
- 'code/__DEFINES/tgs.config.dm'
|
||||
- 'code/__DEFINES/tgs.dm'
|
||||
- 'code/game/world.dm'
|
||||
- 'code/modules/tgs/**'
|
||||
- 'tools/bootstrap/**'
|
||||
- 'tools/tgs_scripts/**'
|
||||
- 'tools/tgs_test/**'
|
||||
- ".tgs.yml"
|
||||
- ".github/workflows/tgs_test.yml"
|
||||
- "dependencies.sh"
|
||||
- "code/__DEFINES/tgs.config.dm"
|
||||
- "code/__DEFINES/tgs.dm"
|
||||
- "code/game/world.dm"
|
||||
- "code/modules/tgs/**"
|
||||
- "tools/bootstrap/**"
|
||||
- "tools/tgs_scripts/**"
|
||||
- "tools/tgs_test/**"
|
||||
merge_group:
|
||||
branches:
|
||||
- master
|
||||
- master
|
||||
env:
|
||||
TGS_API_PORT: 5000
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
|
||||
@@ -13,50 +13,50 @@ jobs:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v4
|
||||
- name: Clone
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Branch
|
||||
run: |
|
||||
git branch -f tgs-dmapi-update
|
||||
git checkout tgs-dmapi-update
|
||||
git reset --hard master
|
||||
- name: Branch
|
||||
run: |
|
||||
git branch -f tgs-dmapi-update
|
||||
git checkout tgs-dmapi-update
|
||||
git reset --hard master
|
||||
|
||||
- name: Apply DMAPI update
|
||||
uses: tgstation/tgs-dmapi-updater@v2
|
||||
id: dmapi-update
|
||||
with:
|
||||
header-path: 'code/__DEFINES/tgs.dm'
|
||||
library-path: 'code/modules/tgs'
|
||||
- name: Apply DMAPI update
|
||||
uses: tgstation/tgs-dmapi-updater@v2
|
||||
id: dmapi-update
|
||||
with:
|
||||
header-path: "code/__DEFINES/tgs.dm"
|
||||
library-path: "code/modules/tgs"
|
||||
|
||||
- name: Commit and Push
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git config user.name "tgstation-ci[bot]"
|
||||
git config user.email "179393467+tgstation-ci[bot]@users.noreply.github.com"
|
||||
git add .
|
||||
git commit -m 'Update TGS DMAPI'
|
||||
git push -f -u origin tgs-dmapi-update
|
||||
- name: Commit and Push
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git config user.name "tgstation-ci[bot]"
|
||||
git config user.email "179393467+tgstation-ci[bot]@users.noreply.github.com"
|
||||
git add .
|
||||
git commit -m 'Update TGS DMAPI'
|
||||
git push -f -u origin tgs-dmapi-update
|
||||
|
||||
- name: Generate App Token
|
||||
id: app-token-generation
|
||||
uses: actions/create-github-app-token@v2
|
||||
if: env.APP_PRIVATE_KEY != '' && env.APP_ID != ''
|
||||
with:
|
||||
app-id: ${{ secrets.APP_ID }}
|
||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
env:
|
||||
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
APP_ID: ${{ secrets.APP_ID }}
|
||||
- name: Generate App Token
|
||||
id: app-token-generation
|
||||
uses: actions/create-github-app-token@v2
|
||||
if: env.APP_PRIVATE_KEY != '' && env.APP_ID != ''
|
||||
with:
|
||||
app-id: ${{ secrets.APP_ID }}
|
||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
env:
|
||||
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
APP_ID: ${{ secrets.APP_ID }}
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: repo-sync/pull-request@v2
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
source_branch: "tgs-dmapi-update"
|
||||
destination_branch: "master"
|
||||
pr_title: "Automatic TGS DMAPI Update"
|
||||
pr_body: "This pull request updates the TGS DMAPI to the latest version. Please note any changes that may be breaking or unimplemented in your codebase by checking what changes are in the definitions file: code/__DEFINES/tgs.dm before merging.\n\n${{ steps.dmapi-update.outputs.release-notes }}"
|
||||
pr_label: "Tools"
|
||||
pr_allow_empty: false
|
||||
github_token: ${{ steps.app-token-generation.outputs.token || secrets.GITHUB_TOKEN }}
|
||||
- name: Create Pull Request
|
||||
uses: repo-sync/pull-request@v2
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
source_branch: "tgs-dmapi-update"
|
||||
destination_branch: "master"
|
||||
pr_title: "Automatic TGS DMAPI Update"
|
||||
pr_body: "This pull request updates the TGS DMAPI to the latest version. Please note any changes that may be breaking or unimplemented in your codebase by checking what changes are in the definitions file: code/__DEFINES/tgs.dm before merging.\n\n${{ steps.dmapi-update.outputs.release-notes }}"
|
||||
pr_label: "Tools"
|
||||
pr_allow_empty: false
|
||||
github_token: ${{ steps.app-token-generation.outputs.token || secrets.GITHUB_TOKEN }}
|
||||
|
||||
+18
-4
@@ -1,5 +1,19 @@
|
||||
# We don't want prettier to run on anything outside of the TGUI folder, so we have to do this.
|
||||
/*
|
||||
# Directories
|
||||
changelogs
|
||||
data
|
||||
font-awesome
|
||||
jquery
|
||||
juke
|
||||
|
||||
# We want it to run into the TGUI folder, however.
|
||||
!/tgui
|
||||
## Tgui
|
||||
node_modules
|
||||
.yarn
|
||||
# Avoid running on any bundles.
|
||||
tgui/public
|
||||
# Running it on tgui.html is fine, however.
|
||||
!/tgui/public/tgui.html
|
||||
# Specific files
|
||||
package-lock.json
|
||||
# File names / types
|
||||
*.min.*
|
||||
*.pnp.*
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# This file is okay being empty as it will tell prettier to run in the repo
|
||||
Vendored
+2
-8
@@ -82,10 +82,7 @@
|
||||
"request": "launch",
|
||||
"program": "${command:dreammaker.returnDreamDaemonPath}",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": [
|
||||
"${command:dreammaker.getFilenameDmb}",
|
||||
"-trusted"
|
||||
],
|
||||
"args": ["${command:dreammaker.getFilenameDmb}", "-trusted"],
|
||||
"preLaunchTask": "Build All"
|
||||
},
|
||||
{
|
||||
@@ -94,10 +91,7 @@
|
||||
"request": "launch",
|
||||
"program": "${command:dreammaker.returnDreamDaemonPath}",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": [
|
||||
"${command:dreammaker.getFilenameDmb}",
|
||||
"-trusted"
|
||||
],
|
||||
"args": ["${command:dreammaker.getFilenameDmb}", "-trusted"],
|
||||
"preLaunchTask": "Build All (low memory mode)"
|
||||
},
|
||||
{
|
||||
|
||||
Vendored
+1
-1
@@ -3,7 +3,7 @@
|
||||
"eslint.workingDirectories": ["./tgui"],
|
||||
"prettier.prettierPath": "./tgui/.yarn/sdks/prettier/index.cjs",
|
||||
"typescript.tsdk": "./tgui/.yarn/sdks/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"search.exclude": {
|
||||
"**/.yarn": true,
|
||||
"**/.pnp.*": true
|
||||
|
||||
Vendored
+39
-54
@@ -12,11 +12,7 @@
|
||||
"DM_EXE": "${config:dreammaker.byondPath}"
|
||||
}
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$dreammaker",
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"problemMatcher": ["$dreammaker", "$tsc", "$eslint-stylish"],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
@@ -37,11 +33,7 @@
|
||||
"DM_EXE": "${config:dreammaker.byondPath}"
|
||||
}
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$dreammaker",
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"problemMatcher": ["$dreammaker", "$tsc", "$eslint-stylish"],
|
||||
"group": {
|
||||
"kind": "build"
|
||||
},
|
||||
@@ -51,21 +43,27 @@
|
||||
{
|
||||
"type": "process",
|
||||
"command": "tools/build/build",
|
||||
"args": ["-DTESTING","-DREAGENTS_TESTING","-DTIMER_DEBUG","-DREFERENCE_DOING_IT_LIVE"],
|
||||
"args": [
|
||||
"-DTESTING",
|
||||
"-DREAGENTS_TESTING",
|
||||
"-DTIMER_DEBUG",
|
||||
"-DREFERENCE_DOING_IT_LIVE"
|
||||
],
|
||||
"windows": {
|
||||
"command": ".\\tools\\build\\build.bat",
|
||||
"args": ["-DTESTING","-DREAGENTS_TESTING","-DTIMER_DEBUG","-DREFERENCE_DOING_IT_LIVE"]
|
||||
"args": [
|
||||
"-DTESTING",
|
||||
"-DREAGENTS_TESTING",
|
||||
"-DTIMER_DEBUG",
|
||||
"-DREFERENCE_DOING_IT_LIVE"
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"env": {
|
||||
"DM_EXE": "${config:dreammaker.byondPath}"
|
||||
}
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$dreammaker",
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"problemMatcher": ["$dreammaker", "$tsc", "$eslint-stylish"],
|
||||
"group": {
|
||||
"kind": "build"
|
||||
},
|
||||
@@ -75,21 +73,29 @@
|
||||
{
|
||||
"type": "process",
|
||||
"command": "tools/build/build",
|
||||
"args": ["-DLOWMEMORYMODE","-DTESTING","-DREAGENTS_TESTING","-DTIMER_DEBUG","-DREFERENCE_DOING_IT_LIVE"],
|
||||
"args": [
|
||||
"-DLOWMEMORYMODE",
|
||||
"-DTESTING",
|
||||
"-DREAGENTS_TESTING",
|
||||
"-DTIMER_DEBUG",
|
||||
"-DREFERENCE_DOING_IT_LIVE"
|
||||
],
|
||||
"windows": {
|
||||
"command": ".\\tools\\build\\build.bat",
|
||||
"args": ["-DLOWMEMORYMODE","-DTESTING","-DREAGENTS_TESTING","-DTIMER_DEBUG","-DREFERENCE_DOING_IT_LIVE"]
|
||||
"args": [
|
||||
"-DLOWMEMORYMODE",
|
||||
"-DTESTING",
|
||||
"-DREAGENTS_TESTING",
|
||||
"-DTIMER_DEBUG",
|
||||
"-DREFERENCE_DOING_IT_LIVE"
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"env": {
|
||||
"DM_EXE": "${config:dreammaker.byondPath}"
|
||||
}
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$dreammaker",
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"problemMatcher": ["$dreammaker", "$tsc", "$eslint-stylish"],
|
||||
"group": {
|
||||
"kind": "build"
|
||||
},
|
||||
@@ -99,21 +105,17 @@
|
||||
{
|
||||
"type": "process",
|
||||
"command": "tools/build/build",
|
||||
"args": ["-DTESTING","-DMAP_TEST"],
|
||||
"args": ["-DTESTING", "-DMAP_TEST"],
|
||||
"windows": {
|
||||
"command": ".\\tools\\build\\build.bat",
|
||||
"args": ["-DTESTING","-DMAP_TEST"]
|
||||
"args": ["-DTESTING", "-DMAP_TEST"]
|
||||
},
|
||||
"options": {
|
||||
"env": {
|
||||
"DM_EXE": "${config:dreammaker.byondPath}"
|
||||
}
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$dreammaker",
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"problemMatcher": ["$dreammaker", "$tsc", "$eslint-stylish"],
|
||||
"group": {
|
||||
"kind": "build"
|
||||
},
|
||||
@@ -123,9 +125,7 @@
|
||||
{
|
||||
"type": "dreammaker",
|
||||
"dme": "tgstation.dme",
|
||||
"problemMatcher": [
|
||||
"$dreammaker"
|
||||
],
|
||||
"problemMatcher": ["$dreammaker"],
|
||||
"group": "build",
|
||||
"label": "dm: build - tgstation.dme"
|
||||
},
|
||||
@@ -140,10 +140,7 @@
|
||||
"windows": {
|
||||
"command": ".\\bin\\tgui-build.cmd"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"problemMatcher": ["$tsc", "$eslint-stylish"],
|
||||
"group": "build",
|
||||
"label": "tgui: build"
|
||||
},
|
||||
@@ -153,10 +150,7 @@
|
||||
"windows": {
|
||||
"command": ".\\bin\\tgui-dev.cmd"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"problemMatcher": ["$tsc", "$eslint-stylish"],
|
||||
"group": "build",
|
||||
"label": "tgui: dev server"
|
||||
},
|
||||
@@ -166,10 +160,7 @@
|
||||
"windows": {
|
||||
"command": ".\\bin\\tgui-bench.cmd"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"problemMatcher": ["$tsc", "$eslint-stylish"],
|
||||
"group": "build",
|
||||
"label": "tgui: bench"
|
||||
},
|
||||
@@ -179,10 +170,7 @@
|
||||
"windows": {
|
||||
"command": ".\\bin\\tgui-sonar.cmd"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"problemMatcher": ["$tsc", "$eslint-stylish"],
|
||||
"group": "build",
|
||||
"label": "tgui: sonar"
|
||||
},
|
||||
@@ -192,10 +180,7 @@
|
||||
"windows": {
|
||||
"command": ".\\bin\\tgfont.cmd"
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$tsc",
|
||||
"$eslint-stylish"
|
||||
],
|
||||
"problemMatcher": ["$tsc", "$eslint-stylish"],
|
||||
"group": "build",
|
||||
"label": "tgui: rebuild tgfont"
|
||||
}
|
||||
|
||||
@@ -7,22 +7,23 @@
|
||||
|
||||
[](.github/images/comics/131-bug-free.png) [](.github/images/comics/106-tech-debt-modified.png) [](https://www.reddit.com/r/SS13/comments/5oplxp/what_is_the_main_problem_with_byond_as_an_engine/dclbu1a)
|
||||
|
||||
| Website | Link |
|
||||
|---------------------------|------------------------------------------------|
|
||||
| Website | [https://tgstation13.org](https://tgstation13.org) |
|
||||
| Code | [https://github.com/tgstation/tgstation](https://github.com/tgstation/tgstation) |
|
||||
| Wiki | [https://tgstation13.org/wiki/Main_Page](https://tgstation13.org/wiki/Main_Page) |
|
||||
| Codedocs | [https://codedocs.tgstation13.org/](https://codedocs.tgstation13.org/) |
|
||||
| /tg/station Discord | [https://tgstation13.org/phpBB/viewforum.php?f=60](https://tgstation13.org/phpBB/viewforum.php?f=60) |
|
||||
| Coderbus Discord | [https://discord.gg/Vh8TJp9](https://discord.gg/Vh8TJp9) |
|
||||
| Website | Link |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| Website | [https://tgstation13.org](https://tgstation13.org) |
|
||||
| Code | [https://github.com/tgstation/tgstation](https://github.com/tgstation/tgstation) |
|
||||
| Wiki | [https://tgstation13.org/wiki/Main_Page](https://tgstation13.org/wiki/Main_Page) |
|
||||
| Codedocs | [https://codedocs.tgstation13.org/](https://codedocs.tgstation13.org/) |
|
||||
| /tg/station Discord | [https://tgstation13.org/phpBB/viewforum.php?f=60](https://tgstation13.org/phpBB/viewforum.php?f=60) |
|
||||
| Coderbus Discord | [https://discord.gg/Vh8TJp9](https://discord.gg/Vh8TJp9) |
|
||||
|
||||
This is the codebase for the /tg/station flavoured fork of SpaceStation 13.
|
||||
|
||||
Space Station 13 is a paranoia-laden round-based roleplaying game set against the backdrop of a nonsensical, metal death trap masquerading as a space station, with charming spritework designed to represent the sci-fi setting and its dangerous undertones. Have fun, and survive!
|
||||
|
||||
*All github inquiries (such as moderation actions) may be handled via the /tg/station discord [#coding-general](https://discord.com/channels/326822144233439242/326831214667235328). Simply ping the `@Maintainer` role, following the guide on asking questions located in the channel description, with your issue!*
|
||||
_All github inquiries (such as moderation actions) may be handled via the /tg/station discord [#coding-general](https://discord.com/channels/326822144233439242/326831214667235328). Simply ping the `@Maintainer` role, following the guide on asking questions located in the channel description, with your issue!_
|
||||
|
||||
## DOWNLOADING
|
||||
|
||||
[Downloading](.github/guides/DOWNLOADING.md)
|
||||
|
||||
[Running a server](.github/guides/RUNNING_A_SERVER.md)
|
||||
@@ -60,6 +61,6 @@ See LICENSE and GPLv3.txt for more details.
|
||||
|
||||
The TGS DMAPI is licensed as a subproject under the MIT license.
|
||||
|
||||
See the footer of [code/__DEFINES/tgs.dm](./code/__DEFINES/tgs.dm) and [code/modules/tgs/LICENSE](./code/modules/tgs/LICENSE) for the MIT license.
|
||||
See the footer of [code/\_\_DEFINES/tgs.dm](./code/__DEFINES/tgs.dm) and [code/modules/tgs/LICENSE](./code/modules/tgs/LICENSE) for the MIT license.
|
||||
|
||||
All assets including icons and sound are under a [Creative Commons 3.0 BY-SA license](https://creativecommons.org/licenses/by-sa/3.0/) unless otherwise indicated.
|
||||
|
||||
+75
-44
@@ -7,14 +7,17 @@ The latest database version is 5.28; The query to update the schema revision tab
|
||||
```sql
|
||||
INSERT INTO `schema_revision` (`major`, `minor`) VALUES (5, 31);
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```sql
|
||||
INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (5, 31);
|
||||
```
|
||||
|
||||
In any query remember to add a prefix to the table names if you use one.
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.30, 3 May 2025, by Atlanta-Ned
|
||||
Adds a `manifest` table.
|
||||
|
||||
@@ -34,7 +37,8 @@ CREATE TABLE `manifest` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.30, 1 May 2025, by Rengan
|
||||
Adds `crime_desc` field to the `citation` table to save the description of the crime.
|
||||
|
||||
@@ -42,7 +46,9 @@ Adds `crime_desc` field to the `citation` table to save the description of the c
|
||||
ALTER TABLE `citation`
|
||||
ADD COLUMN `crime_desc` TEXT NULL DEFAULT NULL AFTER `crime`;
|
||||
```
|
||||
-----------------------------------------------------
|
||||
|
||||
---
|
||||
|
||||
Version 5.29, 4 February 2024, by Tiviplus
|
||||
Fixed admin rank table flags being capped at 16 in the DB instead of 24 (byond max)
|
||||
|
||||
@@ -52,7 +58,9 @@ ALTER TABLE `admin_ranks`
|
||||
MODIFY COLUMN `exclude_flags` mediumint(5) unsigned NOT NULL,
|
||||
MODIFY COLUMN `can_edit_flags` mediumint(5) unsigned NOT NULL;
|
||||
```
|
||||
-----------------------------------------------------
|
||||
|
||||
---
|
||||
|
||||
Version 5.28, 1 November 2024, by Ghommie
|
||||
Added `fish_progress` as the first 'progress' subtype of 'datum/award/scores'
|
||||
|
||||
@@ -64,9 +72,12 @@ CREATE TABLE `fish_progress` (
|
||||
PRIMARY KEY (`ckey`,`progress_entry`)
|
||||
) ENGINE=InnoDB;
|
||||
```
|
||||
-----------------------------------------------------
|
||||
|
||||
---
|
||||
|
||||
Version 5.27, 26 April 2024, by zephyrtfa
|
||||
Add the ip intel whitelist table
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS `ipintel_whitelist`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
@@ -78,29 +89,37 @@ CREATE TABLE `ipintel_whitelist` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
```
|
||||
-----------------------------------------------------
|
||||
|
||||
---
|
||||
|
||||
Version 5.26, 03 December 2023, by distributivgesetz
|
||||
Set the default value of cloneloss to 0, as it's obsolete and it won't be set by blackbox anymore.
|
||||
|
||||
```sql
|
||||
ALTER TABLE `death` MODIFY COLUMN `cloneloss` SMALLINT(5) UNSIGNED DEFAULT '0';
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.25, 27 September 2023, by Jimmyl
|
||||
Removes the text_adventures table because it is no longer used
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS `text_adventures`;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.24, 17 May 2023, by LemonInTheDark
|
||||
Modified the library action table to fit ckeys properly, and to properly store ips.
|
||||
|
||||
```sql
|
||||
ALTER TABLE `library_action` MODIFY COLUMN `ckey` varchar(32) NOT NULL;
|
||||
ALTER TABLE `library_action` MODIFY COLUMN `ip_addr` int(10) unsigned NOT NULL;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.23, 28 December 2022, by Mothblocks
|
||||
Added `tutorial_completions` to mark what ckeys have completed contextual tutorials.
|
||||
|
||||
@@ -113,14 +132,16 @@ CREATE TABLE `tutorial_completions` (
|
||||
UNIQUE INDEX `ckey_tutorial_unique` (`ckey`, `tutorial_key`));
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.22, 22 December 2021, by Mothblocks
|
||||
Fixes a bug in `telemetry_connections` that limited the range of IPs.
|
||||
|
||||
```sql
|
||||
ALTER TABLE `telemetry_connections` MODIFY COLUMN `address` INT(10) UNSIGNED NOT NULL;
|
||||
```
|
||||
-----------------------------------------------------
|
||||
|
||||
---
|
||||
|
||||
Version 5.21, 15 December 2021, by Mothblocks
|
||||
Adds `telemetry_connections` table for tracking tgui telemetry.
|
||||
@@ -138,7 +159,8 @@ CREATE TABLE `telemetry_connections` (
|
||||
UNIQUE INDEX `unique_constraints` (`ckey` , `telemetry_ckey` , `address` , `computer_id`)
|
||||
);
|
||||
```
|
||||
-----------------------------------------------------
|
||||
|
||||
---
|
||||
|
||||
Version 5.20, 11 November 2021, by Mothblocks
|
||||
Adds `admin_ckey` field to the `known_alts` table to track who added what.
|
||||
@@ -148,7 +170,8 @@ ALTER TABLE `known_alts`
|
||||
ADD COLUMN `admin_ckey` VARCHAR(32) NOT NULL DEFAULT '*no key*' AFTER `ckey2`;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.19, 10 November 2021, by WalterMeldron
|
||||
Adds an urgent column to tickets for ahelps marked as urgent.
|
||||
|
||||
@@ -156,7 +179,8 @@ Adds an urgent column to tickets for ahelps marked as urgent.
|
||||
ALTER TABLE `ticket` ADD COLUMN `urgent` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `sender`;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.18, 1 November 2021, by Mothblocks
|
||||
Added `known_alts` table for tracking who not to create suspicious logins for.
|
||||
|
||||
@@ -170,7 +194,8 @@ CREATE TABLE `known_alts` (
|
||||
);
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.17, 8 October 2021, by MrStonedOne + Mothblocks
|
||||
Changes any table that requrired a NOT NULL round ID to now accept NULL. In the BSQL past, these were handled as 0, but in the move to rust-g this behavior was lost.
|
||||
|
||||
@@ -189,7 +214,8 @@ ALTER TABLE `player` CHANGE `lastseen_round_id` `lastseen_round_id` INT(11) UNSI
|
||||
ALTER TABLE `ticket` CHANGE `round_id` `round_id` INT(11) UNSIGNED NULL;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.16, 31 July 2021, by Atlanta-Ned
|
||||
Added `library_action` table for tracking reported library books and actions taken on them.
|
||||
|
||||
@@ -207,8 +233,8 @@ CREATE TABLE `library_action` (
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
-----------------------------------------------------
|
||||
Version 5.15, 2 June 2021, by Mothblocks
|
||||
Added verified admin connection log used for 2FA
|
||||
|
||||
@@ -224,7 +250,7 @@ CREATE TABLE `admin_connections` (
|
||||
UNIQUE INDEX `unique_constraints` (`ckey`, `ip`, `cid`));
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.14, xx May 2021, by Anturke
|
||||
Added exploration drone adventure table
|
||||
@@ -241,7 +267,7 @@ CREATE TABLE `text_adventures` (
|
||||
) ENGINE=InnoDB;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.13, 30 April 2021, by Atlanta Ned
|
||||
Added the `citation` table for tracking security citations in the database.
|
||||
@@ -270,14 +296,14 @@ AUTO_INCREMENT=1
|
||||
;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.12, 29 December 2020, by Missfox
|
||||
Modified table `messages`, adding column `playtime` to show the user's playtime when the note was created.
|
||||
|
||||
ALTER TABLE `messages` ADD `playtime` INT(11) NULL DEFAULT(NULL) AFTER `severity`
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.11, 7 September 2020, by bobbahbrown, MrStonedOne, and Jordie0608 (Updated 26 March 2021 by bobbahbrown)
|
||||
|
||||
@@ -291,7 +317,7 @@ ALTER TABLE `ticket`
|
||||
ADD INDEX `idx_ticket_act_time_rid` (`action`, `timestamp`, `round_id`);
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.10, 7 August 2020, by oranges
|
||||
|
||||
@@ -320,7 +346,7 @@ ALTER TABLE `player` DROP COLUMN `discord_id`;
|
||||
COMMIT;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.9, 19 April 2020, by Jordie0608
|
||||
Updates and improvements to poll handling.
|
||||
@@ -363,7 +389,7 @@ $$
|
||||
DELIMITER ;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.8, 7 April 2020, by Jordie0608
|
||||
Modified table `messages`, adding column `deleted_ckey` to record who deleted a message.
|
||||
@@ -372,7 +398,7 @@ Modified table `messages`, adding column `deleted_ckey` to record who deleted a
|
||||
ALTER TABLE `messages` ADD COLUMN `deleted_ckey` VARCHAR(32) NULL DEFAULT NULL AFTER `deleted`;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.7, 10 January 2020 by Atlanta-Ned
|
||||
Added ticket table for tracking ahelp tickets in the database.
|
||||
@@ -394,7 +420,7 @@ CREATE TABLE `ticket` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.6, 6 December 2019 by Anturke
|
||||
Added achievement_name and achievement_description columns to achievement_metadata table.
|
||||
@@ -403,7 +429,7 @@ Added achievement_name and achievement_description columns to achievement_metada
|
||||
ALTER TABLE `achievement_metadata` ADD COLUMN (`achievement_name` VARCHAR(64) NULL DEFAULT NULL, `achievement_description` VARCHAR(512) NULL DEFAULT NULL);
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.5, 26 October 2019 by Anturke
|
||||
Added achievement_metadata table.
|
||||
@@ -418,11 +444,11 @@ CREATE TABLE `achievement_metadata` (
|
||||
) ENGINE=InnoDB;
|
||||
```
|
||||
|
||||
-----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.4, 5 October 2019 by Anturke
|
||||
Added achievements table.
|
||||
See hub migration verb in _achievement_data.dm for details on migrating.
|
||||
See hub migration verb in \_achievement_data.dm for details on migrating.
|
||||
|
||||
```sql
|
||||
CREATE TABLE `achievements` (
|
||||
@@ -434,7 +460,7 @@ CREATE TABLE `achievements` (
|
||||
) ENGINE=InnoDB;
|
||||
```
|
||||
|
||||
----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.3, 6 July 2019, by Atlanta-Ned
|
||||
Added a `feedback` column to the admin table, used for linking to individual admin feedback threads. Currently this is only used for statistics tracking tools such as Statbus and isn't used by the game.
|
||||
@@ -443,7 +469,7 @@ Added a `feedback` column to the admin table, used for linking to individual adm
|
||||
ALTER TABLE `admin` ADD `feedback` VARCHAR(255) NULL DEFAULT NULL AFTER `rank`;
|
||||
```
|
||||
|
||||
----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.2, 30 May 2019, by AffectedArc07
|
||||
Added a field to the `player` table to track ckey and discord ID relationships
|
||||
@@ -452,7 +478,8 @@ Added a field to the `player` table to track ckey and discord ID relationships
|
||||
ALTER TABLE `player`
|
||||
ADD COLUMN `discord_id` BIGINT NULL DEFAULT NULL AFTER `flags`;
|
||||
```
|
||||
----------------------------------------------------
|
||||
|
||||
---
|
||||
|
||||
Version 5.1, 25 Feb 2018, by MrStonedOne
|
||||
Added four tables to enable storing of stickybans in the database since byond can lose them, and to enable disabling stickybans for a round without depending on a crash free round. Existing stickybans are automagically imported to the tables.
|
||||
@@ -492,7 +519,7 @@ CREATE TABLE `stickyban_matched_cid` (
|
||||
) ENGINE=InnoDB;
|
||||
```
|
||||
|
||||
----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 5.0, 28 October 2018, by Jordie0608
|
||||
Modified ban table to remove the need for the `bantype` column, a python script is used to migrate data to this new format.
|
||||
@@ -533,21 +560,21 @@ CREATE TABLE `ban` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
```
|
||||
|
||||
----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 4.7, 18 August 2018, by CitrusGender
|
||||
Modified table `messages`, adding column `severity` to classify notes based on their severity.
|
||||
|
||||
ALTER TABLE `messages` ADD `severity` enum('high','medium','minor','none') DEFAULT NULL AFTER `expire_timestamp`
|
||||
|
||||
----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 4.6, 11 August 2018, by Jordie0608
|
||||
Modified table `messages`, adding column `expire_timestamp` to allow for auto-"deleting" messages.
|
||||
|
||||
ALTER TABLE `messages` ADD `expire_timestamp` DATETIME NULL DEFAULT NULL AFTER `secret`;
|
||||
|
||||
----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 4.5, 9 July 2018, by Jordie0608
|
||||
Modified table `player`, adding column `byond_key` to store a user's key along with their ckey.
|
||||
@@ -555,20 +582,20 @@ To populate this new column run the included script 'populate_key_2018-07', see
|
||||
|
||||
ALTER TABLE `player` ADD `byond_key` VARCHAR(32) DEFAULT NULL AFTER `ckey`;
|
||||
|
||||
----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 4.4, 9 May 2018, by Jordie0608
|
||||
Modified table `round`, renaming column `start_datetime` to `initialize_datetime` and `end_datetime` to `shutdown_datetime` and adding columns to replace both under the same name in preparation for changes to TGS server initialization.
|
||||
|
||||
ALTER TABLE `round`
|
||||
ALTER `start_datetime` DROP DEFAULT;
|
||||
ALTER `start_datetime` DROP DEFAULT;
|
||||
ALTER TABLE `round`
|
||||
CHANGE COLUMN `start_datetime` `initialize_datetime` DATETIME NOT NULL AFTER `id`,
|
||||
ADD COLUMN `start_datetime` DATETIME NULL DEFAULT NULL AFTER `initialize_datetime`,
|
||||
CHANGE COLUMN `end_datetime` `shutdown_datetime` DATETIME NULL DEFAULT NULL AFTER `start_datetime`,
|
||||
ADD COLUMN `end_datetime` DATETIME NULL DEFAULT NULL AFTER `shutdown_datetime`;
|
||||
CHANGE COLUMN `start_datetime` `initialize_datetime` DATETIME NOT NULL AFTER `id`,
|
||||
ADD COLUMN `start_datetime` DATETIME NULL DEFAULT NULL AFTER `initialize_datetime`,
|
||||
CHANGE COLUMN `end_datetime` `shutdown_datetime` DATETIME NULL DEFAULT NULL AFTER `start_datetime`,
|
||||
ADD COLUMN `end_datetime` DATETIME NULL DEFAULT NULL AFTER `shutdown_datetime`;
|
||||
|
||||
----------------------------------------------------
|
||||
---
|
||||
|
||||
Version 4.3, 9 May 2018, by MrStonedOne
|
||||
Added table `role_time_log` and triggers `role_timeTlogupdate`, `role_timeTloginsert` and `role_timeTlogdelete` to update it from changes to `role_time`
|
||||
@@ -578,12 +605,15 @@ CREATE TABLE `role_time_log` ( `id` BIGINT NOT NULL AUTO_INCREMENT , `ckey` VARC
|
||||
DELIMITER $$
|
||||
CREATE TRIGGER `role_timeTlogupdate` AFTER UPDATE ON `role_time` FOR EACH ROW BEGIN INSERT into role_time_log (ckey, job, delta) VALUES (NEW.CKEY, NEW.job, NEW.minutes-OLD.minutes);
|
||||
END
|
||||
|
||||
$$
|
||||
CREATE TRIGGER `role_timeTloginsert` AFTER INSERT ON `role_time` FOR EACH ROW BEGIN INSERT into role_time_log (ckey, job, delta) VALUES (NEW.ckey, NEW.job, NEW.minutes);
|
||||
END
|
||||
$$
|
||||
CREATE TRIGGER `role_timeTlogdelete` AFTER DELETE ON `role_time` FOR EACH ROW BEGIN INSERT into role_time_log (ckey, job, delta) VALUES (OLD.ckey, OLD.job, 0-OLD.minutes);
|
||||
|
||||
CREATE TRIGGER `role_timeTlogdelete` AFTER DELETE ON `role_time` FOR EACH ROW BEGIN INSERT into role_time_log (ckey, job, delta) VALUES (OLD.ckey, OLD.job, 0-OLD.minutes);
|
||||
END
|
||||
|
||||
$$
|
||||
DELIMITER ;
|
||||
----------------------------------------------------
|
||||
@@ -1003,3 +1033,4 @@ UPDATE erro_library SET deleted = 1 WHERE id = someid
|
||||
(Replace someid with the id of the book you want to soft delete.)
|
||||
|
||||
----------------------------------------------------
|
||||
$$
|
||||
|
||||
+4
-1
@@ -11,7 +11,10 @@
|
||||
},
|
||||
"job_changes": {
|
||||
"Cook": {
|
||||
"additional_cqc_areas": ["/area/station/service/bar", "/area/station/commons/lounge"]
|
||||
"additional_cqc_areas": [
|
||||
"/area/station/service/bar",
|
||||
"/area/station/commons/lounge"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
},
|
||||
"job_changes": {
|
||||
"Cook": {
|
||||
"additional_cqc_areas": ["/area/station/service/bar", "/area/station/commons/lounge"]
|
||||
"additional_cqc_areas": [
|
||||
"/area/station/service/bar",
|
||||
"/area/station/commons/lounge"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
1. Create a new map inside the `_maps\safe_houses` folder using the TGM format.
|
||||
2. Create a new dm file inside `modules\bitrunning\virtual_domain\safe_houses` folder..
|
||||
4. Place exit and goal landmarks (obj/effect/landmark/bitrunning/..). Generally, 3 exits and 2 goals are ok.
|
||||
5. Ideally, leave 3 spaces for gear. This has usually been xy [1x1] [1x2] [1x3]
|
||||
6. Place the modular map connector at the bottom left tile.
|
||||
3. Place exit and goal landmarks (obj/effect/landmark/bitrunning/..). Generally, 3 exits and 2 goals are ok.
|
||||
4. Ideally, leave 3 spaces for gear. This has usually been xy [1x1] [1x2] [1x3]
|
||||
5. Place the modular map connector at the bottom left tile.
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -14,5 +14,4 @@
|
||||
- Consider that avatars are not invincible and still require air. If you're making a safe house, it should start with an area that accommodates for this.
|
||||
- For compatibility, your safe house should have a route open from the top center xy [3x0] of the map.
|
||||
- If you want a custom safehouse for a custom map with no modularity, no problem. Make whatever sizes you want, just ensure there are exit and goal effects placed.
|
||||
- Some maps can alter what is spawned into the safehouse by placing objects in the safehouse area. I'm using the left corner, starting from the top, for things like space gear.
|
||||
|
||||
- Some maps can alter what is spawned into the safehouse by placing objects in the safehouse area. I'm using the left corner, starting from the top, for things like space gear.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Making new virtual domains
|
||||
|
||||
## REQUIRED:
|
||||
|
||||
1. One way that the encrypted cache can spawn. This can be from a mob drop, a landmark (place a few, it'll pick one), or a signal landmark if you have a points system.
|
||||
2. Place a virtual domain baseturf helper in each area.
|
||||
3. If you're using modular safehouses, ensure that the map has ONE tile marked with the safehouse modular map loader (and set the KEY). it will need an open 7x6 area.
|
||||
@@ -9,12 +10,14 @@
|
||||
6. Place a virtual domain baseturf helper in each area.
|
||||
|
||||
## Converting an existing map
|
||||
|
||||
1. Create a new map using the existing map's size - give yourself enough room to enclose it with a binary wall. There's no need for any space outside of it, so ensure that it fits and is enclosed, nothing outside of this.
|
||||
2. Copy and paste the existing map into it.
|
||||
3. Find an accessible area for a safehouse, 7x6.
|
||||
4. Place a bottom left safehouse landmark somewhere on the map to load the safehouse.
|
||||
|
||||
## Notes
|
||||
|
||||
You shouldn't need to fully enclose your map in 15 tiles of binary filler. Using one solid wall should do the trick.
|
||||
|
||||
For areas, ideally just one on the map and one for the safehouse. Vdoms should never last so long as to need individual area power and atmos unless you're specifically going for a gimmick.
|
||||
|
||||
@@ -22,7 +22,10 @@
|
||||
],
|
||||
"job_changes": {
|
||||
"Cook": {
|
||||
"additional_cqc_areas": ["/area/station/service/bar", "/area/station/commons/lounge"]
|
||||
"additional_cqc_areas": [
|
||||
"/area/station/service/bar",
|
||||
"/area/station/commons/lounge"
|
||||
]
|
||||
},
|
||||
"Captain": {
|
||||
"special_charter": "asteroid"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
This folder is full of #define statements. They are similar to constants,
|
||||
This folder is full of #define statements. They are similar to constants,
|
||||
but must come before any code that references them, and they do not take up
|
||||
memory the way constants do.
|
||||
|
||||
The values in this folder are NOT options. They are not for hosts to play with.
|
||||
The values in this folder are NOT options. They are not for hosts to play with.
|
||||
Some of the values are arbitrary and only need to be different from similar constants;
|
||||
for example, the genetic mutation numbers in genetics.dm mean nothing, but MUST be distinct.
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
4. [Render plates](#render-plates)
|
||||
|
||||
## Byond internal functionality
|
||||
|
||||
This part of the guide will assume that you have read the byond reference entry for rendering at www.byond.com/docs/ref//#/{notes}/renderer
|
||||
|
||||
When you create an atom, this will always create an internal byond structure called an "appearance". This appearance you will likely be familiar with, as it is exposed through the /atom/var/appearance var. This appearance var holds data on how to render the object, ie what icon/icon_state/color etc it is using. Note that appearance vars will always copy, and do not hold a reference. When you update a var, for example lets pretend we add a filter, the appearance will be updated to include the filter. Note that, however, vis_contents objets are uniquely excluded from appearances. Then, when the filter is updated, the appearance will be recreated, and the atom marked as "dirty". After it has been updated, the SendMaps() function (sometimes also called maptick), which is a internal byond function that iterates over all objects in a clients view and in the clients.mob.contents, checks for "dirty" atoms, then resends any "dirty" appearances to clients as needed and unmarks them as dirty. This function is notoriously slow, but we can see its tick usage through the world.map_cpu var. We can also avoid more complex checks checking whether an object is visible on a clients screen by using the TILE_BOUND appearance flag.
|
||||
@@ -17,6 +18,7 @@ A) Talk to someone who has inside knowledge(like lummox) about it, most of this
|
||||
B) Use the undocumented debug printer which reads of data on icons rendering, this is very dense but can be useful in some cases. To use: Right click top tab -> Options & Messages -> Client -> Command -> Enter ".debug profile mapicons" and press Enter -> go to your Byond directory and find BYOND/cfg/mapicons.json . Yes this is one giant one-line json.
|
||||
|
||||
## Known internal snowflake
|
||||
|
||||
The following is an incomplete list of pitfalls that come from byond snowflake that are known, this list is obviously incomplete.
|
||||
|
||||
1. Transforms are very slow on clientside. This is not usually noticable, but if you start using large amounts of them it will grind you to a halt quickly, regardless of whether its on overlays or objs
|
||||
@@ -32,6 +34,7 @@ The following is an incomplete list of pitfalls that come from byond snowflake t
|
||||
11. Displacement filter: The byond "displacement filter" does not, as the name would make you expect, use displacement maps, but instead uses normal maps.
|
||||
|
||||
## The rendering solution
|
||||
|
||||
One of the main issues with making pretty effects is how objects can only render to one plane, and how filters can only be applied to single objects. Quite simply it means we can't apply effects to multiple planes at once, and an effect to one plane only by treating it as a single unit:
|
||||
|
||||

|
||||
@@ -52,6 +55,4 @@ Through these this allows us to treat planes as single objects, and lets us dist
|
||||
|
||||
The rendering system uses two objects to unify planes: render_relay and render_plates. Render relays use render_target/source and the relay_render_to_plane proc to replicate the plane master on the render relay. This render relay is then rendered onto a render_plate, which is a plane master that renders the render_relays onto itself. This plate can then be hierarchically rendered with the same process until it reaches the master render_plate, which is the plate that will actually render to the player. These plates naturally in the byond style have quirks. For example, rendering to two plates will double any effects such as color or filters, and as such you need to carefully manage how you render them. Keep in mind as well that when sorting the layers for rendering on a plane that they should not be negative, this is handled automatically in relay_render_to_plane. When debugging note that mouse_opacity can act bizarrely with this method, such as only allowing you to click things that are layered over objects on a certain plane but automatically setting the mouse_opacity should be handling this. Note that if you decide to manipulate a plane with internal byond objects that you will have to manually extrapolate the vars that are set if you want to render them to another plane (See blackness plane for example), and that this is not documented anywhere.
|
||||
|
||||
|
||||
Good luck and godspeed with coding
|
||||
- Just another contributor
|
||||
Good luck and godspeed with coding - Just another contributor
|
||||
|
||||
@@ -7,6 +7,7 @@ Dynamic rolls threat based on a special sauce formula:
|
||||
> [dynamic_curve_width][/datum/controller/global_vars/var/dynamic_curve_width] \* tan((3.1416 \* (rand() - 0.5) \* 57.2957795)) + [dynamic_curve_centre][/datum/controller/global_vars/var/dynamic_curve_centre]
|
||||
|
||||
This threat is split into two separate budgets--`round_start_budget` and `mid_round_budget`. For example, a round with 50 threat might be split into a 30 roundstart budget, and a 20 midround budget. The roundstart budget is used to apply antagonists applied on readied players when the roundstarts (`/datum/dynamic_ruleset/roundstart`). The midround budget is used for two types of rulesets:
|
||||
|
||||
- `/datum/dynamic_ruleset/midround` - Rulesets that apply to either existing alive players, or to ghosts. Think Blob or Space Ninja, which poll ghosts asking if they want to play as these roles.
|
||||
- `/datum/dynamic_ruleset/latejoin` - Rulesets that apply to the next player that joins. Think Syndicate Infiltrator, which converts a player just joining an existing round into traitor.
|
||||
|
||||
@@ -15,14 +16,15 @@ This split is done with a similar method, known as the ["lorentz distribution"](
|
||||
The process of creating these numbers occurs in `/datum/controller/subsystem/dynamic/proc/generate_threat` (for creating the threat level) and `/datum/controller/subsystem/dynamic/proc/generate_budgets` (for splitting the threat level into budgets).
|
||||
|
||||
## Deciding roundstart threats
|
||||
|
||||
In `/datum/controller/subsystem/dynamic/proc/roundstart()` (called when no admin chooses the rulesets explicitly), Dynamic uses the available roundstart budget to pick threats. This is done through the following system:
|
||||
|
||||
- All roundstart rulesets (remember, `/datum/dynamic_ruleset/roundstart`) are put into an associative list with their weight as the values (`drafted_rules`).
|
||||
- Until there is either no roundstart budget left, or until there is no ruleset we can choose from with the available threat, a `pickweight` is done based on the drafted_rules. If the same threat is picked twice, it will "scale up". The meaning of this depends on the ruleset itself, using the `scaled_times` variable; traitors for instance will create more the higher they scale.
|
||||
- If a ruleset is chosen with the `HIGH_IMPACT_RULESET` in its `flags`, then all other `HIGH_IMPACT_RULESET`s will be removed from `drafted_rules`. This is so that only one can ever be chosen.
|
||||
- If a ruleset has `LONE_RULESET` in its `flags`, then it will be removed from `drafted_rules`. This is to ensure it will only ever be picked once. An example of this in use is Wizard, to avoid creating multiple wizards.
|
||||
- If a ruleset is chosen with the `HIGH_IMPACT_RULESET` in its `flags`, then all other `HIGH_IMPACT_RULESET`s will be removed from `drafted_rules`. This is so that only one can ever be chosen.
|
||||
- If a ruleset has `LONE_RULESET` in its `flags`, then it will be removed from `drafted_rules`. This is to ensure it will only ever be picked once. An example of this in use is Wizard, to avoid creating multiple wizards.
|
||||
- After all roundstart threats are chosen, `/datum/dynamic_ruleset/proc/picking_roundstart_rule` is called for each, passing in the ruleset and the number of times it is scaled.
|
||||
- In this stage, `pre_execute` is called, which is the function that will determine what players get what antagonists. If this function returns FALSE for whatever reason (in the case of an error), then its threat is refunded.
|
||||
- In this stage, `pre_execute` is called, which is the function that will determine what players get what antagonists. If this function returns FALSE for whatever reason (in the case of an error), then its threat is refunded.
|
||||
|
||||
After this process is done, any leftover roundstart threat will be given to the existing midround budget (done in `/datum/controller/subsystem/dynamic/pre_setup()`).
|
||||
|
||||
@@ -35,6 +37,7 @@ The frequency of midround threats is based on the midround threat of the round.
|
||||
These midround roll points are then equidistantly spaced across the round, starting from `midround_lower_bound` (configurable) to `midround_upper_bound` (configurable), with a +/- of `midround_roll_distance` (configurable).
|
||||
|
||||
For example, if:
|
||||
|
||||
1. `midround_lower_bound` is `10 MINUTES`
|
||||
2. `midround_upper_bound` is `100 MINUTES`
|
||||
3. `midround_roll_distance` is `3 MINUTES`
|
||||
@@ -79,7 +82,7 @@ process() -> (For each midround rule...
|
||||
## Forced
|
||||
|
||||
For latejoin, it simply sets forced_latejoin_rule
|
||||
make_antag_chance(newPlayer) -> trim_candidates() -> ready(forced=TRUE) **NOTE no acceptable() call
|
||||
make_antag_chance(newPlayer) -> trim_candidates() -> ready(forced=TRUE) \*\*NOTE no acceptable() call
|
||||
|
||||
For midround, calls the below proc with forced = TRUE
|
||||
picking_specific_rule(ruletype,forced) -> forced OR acceptable(living_players, threat_level) -> trim_candidates() -> ready(forced) -> spend threat -> execute()
|
||||
@@ -89,25 +92,27 @@ picking_specific_rule(ruletype,forced) -> forced OR acceptable(living_players, t
|
||||
## Ruleset
|
||||
|
||||
acceptable(population,threat) just checks if enough threat_level for population indice.
|
||||
**NOTE that we currently only send threat_level as the second arg, not threat.
|
||||
\*\*NOTE that we currently only send threat_level as the second arg, not threat.
|
||||
ready(forced) checks if enough candidates and calls the map's map_ruleset(dynamic_ruleset) at the parent level
|
||||
|
||||
trim_candidates() varies significantly according to the ruleset type
|
||||
Roundstart: All candidates are new_player mobs. Check them for standard stuff: connected, desire role, not banned, etc.
|
||||
**NOTE Roundstart deals with both candidates (trimmed list of valid players) and mode.candidates (everyone readied up). Don't confuse them!
|
||||
\*\*NOTE Roundstart deals with both candidates (trimmed list of valid players) and mode.candidates (everyone readied up). Don't confuse them!
|
||||
Latejoin: Only one candidate, the latejoiner. Standard checks.
|
||||
Midround: Instead of building a single list candidates, candidates contains four lists: living, dead, observing, and living antags. Standard checks in trim_list(list).
|
||||
|
||||
Midround - Rulesets have additional types
|
||||
/from_ghosts: execute() -> send_applications() -> review_applications() -> finish_applications() -> finish_setup(mob/newcharacter, index) -> setup_role(role)
|
||||
**NOTE: execute() here adds dead players and observers to candidates list
|
||||
\*\*NOTE: execute() here adds dead players and observers to candidates list
|
||||
|
||||
## Configuration and variables
|
||||
|
||||
### Configuration
|
||||
|
||||
Configuration can be done through a `config/dynamic.json` file. One is provided as example in the codebase. This config file, loaded in `/datum/controller/subsystem/dynamic/pre_setup()`, directly overrides the values in the codebase, and so is perfect for making some rulesets harder/easier to get, turning them off completely, changing how much they cost, etc.
|
||||
|
||||
The format of this file is:
|
||||
|
||||
```json
|
||||
{
|
||||
"Dynamic": {
|
||||
@@ -142,37 +147,39 @@ The format of this file is:
|
||||
Note: Comments are not possible in this format, and are just in this document for the sake of readability.
|
||||
|
||||
### Rulesets
|
||||
|
||||
Rulesets have the following variables notable to developers and those interested in tuning.
|
||||
|
||||
- `required_candidates` - The number of people that *must be willing* (in their preferences) to be an antagonist with this ruleset. If the candidates do not meet this requirement, then the ruleset will not bother to be drafted.
|
||||
- `required_candidates` - The number of people that _must be willing_ (in their preferences) to be an antagonist with this ruleset. If the candidates do not meet this requirement, then the ruleset will not bother to be drafted.
|
||||
- `antag_cap` - Judges the amount of antagonists to apply, for both solo and teams. Note that some antagonists (such as traitors, lings, heretics, etc) will add more based on how many times they've been scaled. Written as a linear equation--ceil(x/denominator) + offset, or as a fixed constant. If written as a linear equation, will be in the form of `list("denominator" = denominator, "offset" = offset)`.
|
||||
- Examples include:
|
||||
- Traitor: `antag_cap = list("denominator" = 24)`. This means that for every 24 players, 1 traitor will be added (assuming no scaling).
|
||||
- Nuclear Emergency: `antag_cap = list("denominator" = 18, "offset" = 1)`. For every 18 players, 1 nuke op will be added. Starts at 1, meaning at 30 players, 3 nuke ops will be created, rather than 2.
|
||||
- Revolution: `antag_cap = 3`. There will always be 3 rev-heads, no matter what.
|
||||
- Examples include:
|
||||
- Traitor: `antag_cap = list("denominator" = 24)`. This means that for every 24 players, 1 traitor will be added (assuming no scaling).
|
||||
- Nuclear Emergency: `antag_cap = list("denominator" = 18, "offset" = 1)`. For every 18 players, 1 nuke op will be added. Starts at 1, meaning at 30 players, 3 nuke ops will be created, rather than 2.
|
||||
- Revolution: `antag_cap = 3`. There will always be 3 rev-heads, no matter what.
|
||||
- `minimum_required_age` - The minimum age in order to apply for the ruleset.
|
||||
- `weight` - How likely this ruleset is to be picked. A higher weight results in a higher chance of drafting.
|
||||
- `cost` - The initial cost of the ruleset. This cost is taken from either the roundstart or midround budget, depending on the ruleset.
|
||||
- `scaling_cost` - Cost for every *additional* application of this ruleset.
|
||||
- Suppose traitors has a `cost` of 8, and a `scaling_cost` of 5. This means that buying 1 application of the traitor ruleset costs 8 threat, but buying two costs 13 (8 + 5). Buying it a third time is 18 (8 + 5 + 5), etc.
|
||||
- `scaling_cost` - Cost for every _additional_ application of this ruleset.
|
||||
- Suppose traitors has a `cost` of 8, and a `scaling_cost` of 5. This means that buying 1 application of the traitor ruleset costs 8 threat, but buying two costs 13 (8 + 5). Buying it a third time is 18 (8 + 5 + 5), etc.
|
||||
- `pop_per_requirement` - The range of population each value in `requirements` represents. By default, this is 6.
|
||||
- If the value is five the range is 0-4, 5-9, 10-14, 15-19, 20-24, 25-29, 30-34, 35-39, 40-54, 45+.
|
||||
- If it is six the range is 0-5, 6-11, 12-17, 18-23, 24-29, 30-35, 36-41, 42-47, 48-53, 54+.
|
||||
- If it is seven the range is 0-6, 7-13, 14-20, 21-27, 28-34, 35-41, 42-48, 49-55, 56-62, 63+.
|
||||
- `requirements` - A list that represents, per population range (see: `pop_per_requirement`), how much threat is required to *consider* this ruleset. This is independent of how much it'll actually cost. This uses *threat level*, not the budget--meaning if a round has 50 threat level, but only 10 points of round start threat, a ruleset with a requirement of 40 can still be picked if it can be bought.
|
||||
- Suppose wizard has a `requirements` of `list(90,90,70,40,30,20,10,10,10,10)`. This means that, at 0-5 and 6-11 players, A station must have 90 threat in order for a wizard to be possible. At 12-17, 70 threat is required instead, etc.
|
||||
- `restricted_roles` - A list of jobs that *can't* be drafted by this ruleset. For example, cyborgs cannot be changelings, and so are in the `restricted_roles`.
|
||||
- `protected_roles` - Serves the same purpose of `restricted_roles`, except it can be turned off through configuration (`protect_roles_from_antagonist`). For example, security officers *shouldn't* be made traitor, so they are in Traitor's `protected_roles`.
|
||||
- When considering putting a role in `protected_roles` or `restricted_roles`, the rule of thumb is if it is *technically infeasible* to support that job in that role. There's no *technical* reason a security officer can't be a traitor, and so they are simply in `protected_roles`. There *are* technical reasons a cyborg can't be a changeling, so they are in `restricted_roles` instead.
|
||||
- If the value is five the range is 0-4, 5-9, 10-14, 15-19, 20-24, 25-29, 30-34, 35-39, 40-54, 45+.
|
||||
- If it is six the range is 0-5, 6-11, 12-17, 18-23, 24-29, 30-35, 36-41, 42-47, 48-53, 54+.
|
||||
- If it is seven the range is 0-6, 7-13, 14-20, 21-27, 28-34, 35-41, 42-48, 49-55, 56-62, 63+.
|
||||
- `requirements` - A list that represents, per population range (see: `pop_per_requirement`), how much threat is required to _consider_ this ruleset. This is independent of how much it'll actually cost. This uses _threat level_, not the budget--meaning if a round has 50 threat level, but only 10 points of round start threat, a ruleset with a requirement of 40 can still be picked if it can be bought.
|
||||
- Suppose wizard has a `requirements` of `list(90,90,70,40,30,20,10,10,10,10)`. This means that, at 0-5 and 6-11 players, A station must have 90 threat in order for a wizard to be possible. At 12-17, 70 threat is required instead, etc.
|
||||
- `restricted_roles` - A list of jobs that _can't_ be drafted by this ruleset. For example, cyborgs cannot be changelings, and so are in the `restricted_roles`.
|
||||
- `protected_roles` - Serves the same purpose of `restricted_roles`, except it can be turned off through configuration (`protect_roles_from_antagonist`). For example, security officers _shouldn't_ be made traitor, so they are in Traitor's `protected_roles`.
|
||||
- When considering putting a role in `protected_roles` or `restricted_roles`, the rule of thumb is if it is _technically infeasible_ to support that job in that role. There's no _technical_ reason a security officer can't be a traitor, and so they are simply in `protected_roles`. There _are_ technical reasons a cyborg can't be a changeling, so they are in `restricted_roles` instead.
|
||||
|
||||
This is not a complete list--search "configurable" in this README to learn more.
|
||||
|
||||
### Dynamic
|
||||
|
||||
The "Dynamic" key has the following configurable values:
|
||||
|
||||
- `pop_per_requirement` - The default value of `pop_per_requirement` for any ruleset that does not explicitly set it. Defaults to 6.
|
||||
- `latejoin_delay_min`, `latejoin_delay_max` - The time range, in deciseconds (take your seconds, and multiply by 10), for a latejoin to attempt rolling. Once this timer is finished, a new one will be created within the same range.
|
||||
- Suppose you have a `latejoin_delay_min` of 600 (60 seconds, 1 minute) and a `latejoin_delay_max` of 1800 (180 seconds, 3 minutes). Once the round starts, a random number in this range will be picked--let's suppose 1.5 minutes. After 1.5 minutes, Dynamic will decide if a latejoin threat should be created (a probability of `/datum/controller/subsystem/dynamic/proc/get_injection_chance()`). Regardless of its decision, a new timer will be started within the range of 1 to 3 minutes, repeatedly.
|
||||
- Suppose you have a `latejoin_delay_min` of 600 (60 seconds, 1 minute) and a `latejoin_delay_max` of 1800 (180 seconds, 3 minutes). Once the round starts, a random number in this range will be picked--let's suppose 1.5 minutes. After 1.5 minutes, Dynamic will decide if a latejoin threat should be created (a probability of `/datum/controller/subsystem/dynamic/proc/get_injection_chance()`). Regardless of its decision, a new timer will be started within the range of 1 to 3 minutes, repeatedly.
|
||||
- `threat_curve_centre` - A number between -5 and +5. A negative value will give a more peaceful round and a positive value will give a round with higher threat.
|
||||
- `threat_curve_width` - A number between 0.5 and 4. Higher value will favour extreme rounds and lower value rounds closer to the average.
|
||||
- `roundstart_split_curve_centre` - A number between -5 and +5. Equivalent to threat_curve_centre, but for the budget split. A negative value will weigh towards midround rulesets, and a positive value will weight towards roundstart ones.
|
||||
@@ -180,9 +187,10 @@ The "Dynamic" key has the following configurable values:
|
||||
- `random_event_hijack_minimum` - The minimum amount of time for antag random events to be hijacked. (See [Random Event Hijacking](#random-event-hijacking))
|
||||
- `random_event_hijack_maximum` - The maximum amount of time for antag random events to be hijacked. (See [Random Event Hijacking](#random-event-hijacking))
|
||||
- `hijacked_random_event_injection_chance` - The amount of injection chance to give to Dynamic when a random event is hijacked. (See [Random Event Hijacking](#random-event-hijacking))
|
||||
- `max_threat_level` - Sets the maximum amount of threat that can be rolled. Defaults to 100. You should only use this to *lower* the maximum threat, as raising it higher will not do anything.
|
||||
- `max_threat_level` - Sets the maximum amount of threat that can be rolled. Defaults to 100. You should only use this to _lower_ the maximum threat, as raising it higher will not do anything.
|
||||
|
||||
## Random Event "Hijacking"
|
||||
|
||||
Random events have the potential to be hijacked by Dynamic to keep the pace of midround injections, while also allowing greenshifts to contain some antagonists.
|
||||
|
||||
`/datum/round_event_control/dynamic_should_hijack` is a variable to random events to allow Dynamic to hijack them, and defaults to FALSE. This is set to TRUE for random events that spawn antagonists.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Networks: Subsystems that are conceptually networked IN-GAME
|
||||
|
||||
### Specifically, these subsystems are for in-game mechanics that are intended to rely on a digital/radio/physical network, such as telecomms servers or the powernet
|
||||
|
||||
The intent of this folder categorization is to be able to quickly reference what are intended to be in-game networks, vs what is logically networked inside the code.
|
||||
|
||||
@@ -15,11 +15,12 @@ If behaviors are selected, and the AI is in range, it will try to perform them.
|
||||
They also hold data for any of the actions they might need to use, such as cooldowns, whether or not they're currently fighting, etcetera this is stored in the blackboard, more information on that below.
|
||||
|
||||
### Blackboard
|
||||
|
||||
The blackboard is an associated list keyed with strings and with values of whatever you want. These store information the mob has such as "Am I attacking someone", "Do I have a weapon". By using an associated list like this, no data needs to be stored on the actions themselves, and you could make actions that work on multiple ai controllers if you so pleased by making the key to use a variable.
|
||||
|
||||
## AI Behavior
|
||||
AI behaviors are the actions an AI can take. These can range from "Do an emote" to "Attack this target until he is dead". They are singletons and should contain nothing but static data. Any dynamic data should be stored in the blackboard, to allow different controllers to use the same behaviors.
|
||||
|
||||
AI behaviors are the actions an AI can take. These can range from "Do an emote" to "Attack this target until he is dead". They are singletons and should contain nothing but static data. Any dynamic data should be stored in the blackboard, to allow different controllers to use the same behaviors.
|
||||
|
||||
# Guides:
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
# Learn AI
|
||||
|
||||
In ye olde days, we designed mob AI, and we built it into simple animals as they were the "non player controlled" mobs. Made sense at the time. But by coding AI directly into the mob, there was so little ability to make unique or complicated AI, and even when it was pulled off the code was hacky and non-reusable. the datum AI system was made to rectify these problems, and expand AI beyond just mobs.
|
||||
@@ -57,6 +56,7 @@ AI's work by planning specific behaviors, and subtrees are datums that bundle th
|
||||
```
|
||||
|
||||
Finally, we have some more minor things.
|
||||
|
||||
- ai_traits are flags for the AI, things like "STOP_MOVING_WHEN_PULLED" slightly modifying how the AI acts under some situations.
|
||||
- ai_movement is how the mob moves to its movement target. ranges from simple behaviors like ai_movement/dumb that awlays move in the direction of the target and hope there's nothing in the way, all the way to ai_movement/jps that plans and occasionally recalcuates more complicated paths, at the cost of more lag.
|
||||
- idle_behavior is just some simpler behavior to perform when nothing has been planned at all, like idle_behavior/idle_random_walk making a mob wander passively.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
# Template file for your new component
|
||||
|
||||
See _component.dm for detailed explanations
|
||||
See \_component.dm for detailed explanations
|
||||
|
||||
```dm
|
||||
/datum/component/mycomponent
|
||||
|
||||
@@ -6,4 +6,4 @@ Loosely adapted from /vg/. This is an entity component system for adding behavio
|
||||
|
||||
### [HackMD page for an introduction to the system as a whole.](https://hackmd.io/@tgstation/SignalsComponentsElements)
|
||||
|
||||
### See/Define signals and their arguments in [__DEFINES\components.dm](../../__DEFINES/components.dm)
|
||||
### See/Define signals and their arguments in [\_\_DEFINES\components.dm](../../__DEFINES/components.dm)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
# Template file for your new element
|
||||
|
||||
See _element.dm for detailed explanations
|
||||
See \_element.dm for detailed explanations
|
||||
|
||||
```dm
|
||||
/datum/element/myelement
|
||||
|
||||
@@ -80,6 +80,7 @@ This will display "LMB: Craft bola" when the user hovers over cable restraints w
|
||||
The basic system acknowledges the following two interactions:
|
||||
|
||||
### Self-defining items (Type A)
|
||||
|
||||
These are items that are defined by their behavior. These should define their contextual text within themselves, and not in their targets.
|
||||
|
||||
- Stun batons (LMB to stun, RMB to harm)
|
||||
@@ -87,7 +88,8 @@ These are items that are defined by their behavior. These should define their co
|
||||
- Health analyzers (LMB to scan for health/wounds [another piece of context], RMB to scans for chemicals)
|
||||
|
||||
### Receiving action defining objects (Type B)
|
||||
These are objects (not necessarily items) that are defined by what happens *to* them. These should define their contextual text within themselves, and not in their operating tools.
|
||||
|
||||
These are objects (not necessarily items) that are defined by what happens _to_ them. These should define their contextual text within themselves, and not in their operating tools.
|
||||
|
||||
- Tables (RMB with wrench to deconstruct)
|
||||
- Construction objects (LMB with glass to put in screen for computers)
|
||||
@@ -104,6 +106,7 @@ Note that you **must return `CONTEXTUAL_SCREENTIP_SET` if you change the context
|
||||
This signal is registered on **items**, and receives **the hovering object**, provided in the form of `obj/item/source, list/context, atom/target, mob/living/user`.
|
||||
|
||||
### `/atom/proc/register_item_context()`, and `/atom/proc/add_item_context()`
|
||||
|
||||
`/atom/proc/add_item_context()` is a proc intended to be overridden to easily create Type-B interactions (ones where atoms are hovered over by items). It receives the exact same arguments as `COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET`: `obj/item/source, list/context, atom/target, mob/living/user`.
|
||||
|
||||
In order for your `add_item_context()` method to be run, you **must** call `register_item_context()`.
|
||||
@@ -113,6 +116,7 @@ In order for your `add_item_context()` method to be run, you **must** call `regi
|
||||
This signal is registered on **atoms**, and receives **what the user is hovering with**, provided in the form of `atom/source, list/context, obj/item/held_item, mob/living/user`.
|
||||
|
||||
### `/atom/proc/register_context()`, and `/atom/proc/add_context()`
|
||||
|
||||
`/atom/proc/add_context()` is a proc intended to be overridden to easily create Type-B interactions (ones where atoms are hovered over by items). It receives the exact same arguments as `COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM`: `atom/source, list/context, obj/item/held_item, mob/living/user`.
|
||||
|
||||
In order for your `add_context()` method to be run, you **must** call `register_context()`.
|
||||
|
||||
@@ -40,14 +40,14 @@ The json is made up of some metadata and a list of layers used while creating th
|
||||
"type": "reference",
|
||||
"reference_type": "/datum/greyscale_config/some_other_config",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
[
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "highlights",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "reference",
|
||||
@@ -72,6 +72,7 @@ Once it is done generating it will be placed in an icon file with the icon state
|
||||
|
||||
Most commonly, we'll only use the "icon_state" type...
|
||||
Thus, **this will be the most common layout**:
|
||||
|
||||
```json
|
||||
{
|
||||
"full_icon_state_name": [
|
||||
@@ -79,18 +80,18 @@ Thus, **this will be the most common layout**:
|
||||
"type": "icon_state",
|
||||
"icon_state": "component_state_1",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "component_state_2",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "non_colorable_component_state",
|
||||
"blend_mode": "overlay",
|
||||
"blend_mode": "overlay"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -106,12 +107,14 @@ Additionally, for the sake of mappers, it's appreciated if there's a pre-made ob
|
||||
While the amount of dm code required to make a greyscale sprite was minimized as much as possible, some small amount is required anyway if you want anything to use it.
|
||||
|
||||
As an example:
|
||||
|
||||
```c
|
||||
/datum/greyscale_config/canister
|
||||
name = "Canister" //Required for debugging, will runtime without one!
|
||||
icon_file = 'icons/obj/pipes_n_cables/canisters/default.dmi'
|
||||
json_config = 'code/datums/greyscale/json_configs/canister_default.json'
|
||||
```
|
||||
|
||||
And that's all you need to make it usable by other code:
|
||||
|
||||
```c
|
||||
@@ -157,8 +160,10 @@ If you want your item to be colorable in a vending machine (or other places if t
|
||||
...
|
||||
flags_1 = IS_PLAYER_COLORABLE_1
|
||||
```
|
||||
However, **be extremely careful**, as this *requires* that you put *all* of the object's `flags_1` flags in that statement all over again. It's ugly, I know, but there's no
|
||||
|
||||
However, **be extremely careful**, as this _requires_ that you put _all_ of the object's `flags_1` flags in that statement all over again. It's ugly, I know, but there's no
|
||||
better way to do this with BYOND just yet. You can put multiple flags like this (not real flags):
|
||||
|
||||
```c
|
||||
/obj/item/clothing/head/beret
|
||||
...
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"bandana": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
}
|
||||
],
|
||||
"bandana": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [1]
|
||||
}
|
||||
],
|
||||
"bandana_up": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth_up",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
}
|
||||
]
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth_up",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "greyscale_bandana",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
{
|
||||
"greyscale_bandana": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "greyscale_bandana",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
},
|
||||
"greyscale_bandana": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "greyscale_bandana_skull",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
}
|
||||
]
|
||||
"type": "icon_state",
|
||||
"icon_state": "greyscale_bandana",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "greyscale_bandana_skull",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [2]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"greyscale_bandana": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "greyscale_bandana",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
},
|
||||
"greyscale_bandana": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "greyscale_bandana_stripe",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
}
|
||||
]
|
||||
"type": "icon_state",
|
||||
"icon_state": "greyscale_bandana",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "greyscale_bandana_stripe",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [2]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
{
|
||||
"bandskull": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
},
|
||||
"bandskull": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_skull",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
}
|
||||
],
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_skull",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [2]
|
||||
}
|
||||
],
|
||||
"bandskull_up": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth_up",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_skull_up",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
}
|
||||
]
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth_up",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_skull_up",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [2]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
{
|
||||
"bandstriped": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
},
|
||||
"bandstriped": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_stripe",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
}
|
||||
],
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_stripe",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [2]
|
||||
}
|
||||
],
|
||||
"bandstriped_up": [
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth_up",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_stripe_up",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
}
|
||||
]
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_cloth_up",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "bandana_stripe_up",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [2]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "beanie_stripe",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "beanie_base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "bench_corner_cover",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "bench_left_cover",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "bench_middle_cover",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "bench_right_cover",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "bench_solo_cover",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "beret",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
],
|
||||
"beret_flat": [
|
||||
@@ -12,7 +12,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "beret_flat",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "beret",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "badge",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "beret_worn",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "badge_worn",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "beret_worn",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
],
|
||||
"beret_flat": [
|
||||
@@ -12,7 +12,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "beret_worn",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "core_colour",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "blazer",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_obj_buckle",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_obj_belt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 3 ]
|
||||
"color_ids": [3]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_obj_shorts",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 4 ]
|
||||
"color_ids": [4]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buckle",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "belt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 3 ]
|
||||
"color_ids": [3]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "shorts",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 4 ]
|
||||
"color_ids": [4]
|
||||
}
|
||||
],
|
||||
"buttondown_shorts_d": [
|
||||
@@ -30,25 +30,25 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_d",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buckle",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "belt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 3 ]
|
||||
"color_ids": [3]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "shorts",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 4 ]
|
||||
"color_ids": [4]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_obj_buckle",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_obj_belt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 3 ]
|
||||
"color_ids": [3]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_obj_skirt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 4 ]
|
||||
"color_ids": [4]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buckle",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "belt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 3 ]
|
||||
"color_ids": [3]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "skirt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 4 ]
|
||||
"color_ids": [4]
|
||||
}
|
||||
],
|
||||
"buttondown_skirt_d": [
|
||||
@@ -30,25 +30,25 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_d",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buckle",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "belt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 3 ]
|
||||
"color_ids": [3]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "skirt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 4 ]
|
||||
"color_ids": [4]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_obj_buckle",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_obj_belt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 3 ]
|
||||
"color_ids": [3]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_obj_slacks",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 4 ]
|
||||
"color_ids": [4]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buckle",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "belt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 3 ]
|
||||
"color_ids": [3]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "slacks",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 4 ]
|
||||
"color_ids": [4]
|
||||
}
|
||||
],
|
||||
"buttondown_slacks_d": [
|
||||
@@ -30,25 +30,25 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "buttondown_d",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "buckle",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "belt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 3 ]
|
||||
"color_ids": [3]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "slacks",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 4 ]
|
||||
"color_ids": [4]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"type": "reference",
|
||||
"reference_type": "/datum/greyscale_config/canister/base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "reference",
|
||||
"reference_type": "/datum/greyscale_config/canister/post_effects",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
],
|
||||
"window-base": [
|
||||
@@ -18,7 +18,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "window-base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
"type": "reference",
|
||||
"reference_type": "/datum/greyscale_config/canister/base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
[
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "double_stripe",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
@@ -23,7 +23,7 @@
|
||||
"type": "reference",
|
||||
"reference_type": "/datum/greyscale_config/canister/post_effects",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
],
|
||||
"window-base": [
|
||||
@@ -31,7 +31,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "window-base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,19 +4,19 @@
|
||||
"type": "reference",
|
||||
"reference_type": "/datum/greyscale_config/canister/base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "hazard_stripes",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "reference",
|
||||
"reference_type": "/datum/greyscale_config/canister/post_effects",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
],
|
||||
"window-base": [
|
||||
@@ -24,7 +24,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "window-base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "outline",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
|
||||
@@ -4,19 +4,19 @@
|
||||
"type": "reference",
|
||||
"reference_type": "/datum/greyscale_config/canister/base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "stripe",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "reference",
|
||||
"reference_type": "/datum/greyscale_config/canister/post_effects",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
],
|
||||
"window-base": [
|
||||
@@ -24,7 +24,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "window-base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "cardiganskirt_skirt",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "cardiganskirt_cardigan",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
@@ -17,7 +17,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "base_dead",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "disk_mouth",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "base_friend",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
@@ -17,7 +17,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "base_friend_dead",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
@@ -22,7 +22,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "base_dead",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "megacarp_greyscale",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
@@ -17,7 +17,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "megacarp_dead_greyscale",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "circuit_board",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "light_colours",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "standard_colours",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "jumpsuit",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
],
|
||||
"oversuit_worn": [
|
||||
@@ -12,7 +12,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "oversuit",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
],
|
||||
"sneakers_worn": [
|
||||
@@ -20,7 +20,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "shoes_colored",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
@@ -33,7 +33,7 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "boots",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "cypher_base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "cypher_stripe",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
"type": "icon_state",
|
||||
"icon_state": "cypher_base",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 1 ]
|
||||
"color_ids": [1]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
"icon_state": "cypher_cargo",
|
||||
"blend_mode": "overlay",
|
||||
"color_ids": [ 2 ]
|
||||
"color_ids": [2]
|
||||
},
|
||||
{
|
||||
"type": "icon_state",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user