diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 1ed5ba76e7c0c..9256f3a461334 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -1,6 +1,7 @@
# CONTRIBUTING
## Introduction
+
This is the contribution guide for Paradise Station. These guidelines apply to
both new issues and new pull requests. If you are making a pull request, please refer to
the [Pull request](#pull-requests) section, and if you are making an issue report, please
@@ -8,79 +9,71 @@ refer to the [Issue Report](#issues) section, as well as the
[Issue Report Template](ISSUE_TEMPLATE.md).
## Commenting
+
If you comment on an active pull request or issue report, make sure your comment is
concise and to the point. Comments on issue reports or pull requests should be relevant
and friendly, not attacks on the author or adages about something minimally relevant.
If you believe an issue report is not a "bug", please point out specifically and concisely your reasoning in a comment on the issue itself.
-#### Guidelines:
- * Comments on Pull Requests and Issues should remain relevant to the subject in question and not derail discussions.
- * Under no circumstances are users to be attacked for their ideas or contributions. All participants on a given PR or issue are expected to be civil. Failure to do so will result in disciplinary action.
- For more details, see the [Code of Conduct](../CODE_OF_CONDUCT.md).
+### Comment Guidelines
+
+* Comments on Pull Requests and Issues should remain relevant to the subject in question and not derail discussions.
+* Under no circumstances are users to be attacked for their ideas or contributions. All participants on a given PR or issue are expected to be civil. Failure to do so will result in disciplinary action.
+* For more details, see the [Code of Conduct](../CODE_OF_CONDUCT.md).
## Issues
+
The Issues section is not a place to request features, or ask for things to be changed
because you think they should be that way; The Issues section is specifically for
reporting bugs in the code.
-#### Guidelines:
- * Issue reports should be as detailed as possible, and if applicable, should include
- instructions on how to reproduce the bug.
+### Issue Guidelines
+
+* Issue reports should be as detailed as possible, and if applicable, should include instructions on how to reproduce the bug.
## Pull requests
+
Players are welcome to participate in the development of this fork and submit their own
pull requests. If the work you are submitting is a new feature, or affects balance, it is
strongly recommended you get approval/traction for it from our forums before starting the
actual development.
-#### Guidelines:
- * Pull requests should be atomic; Make one commit for each distinct change, so if a part
- of a pull request needs to be removed/changed, you may simply modify that single commit.
- Due to limitations of the engine, this may not always be possible; but do try your best.
+### Pull Request Guidelines
+
+* Pull requests should be atomic; Make one commit for each distinct change, so if a part of a pull request needs to be removed/changed, you may simply modify that single commit. Due to limitations of the engine, this may not always be possible; but do try your best.
+
+* Keep your pull requests small and reviewable whenever possible. Do not bundle unrelated fixes even if not bundling them generates more pull requests. In case of mapping PRs that add features - consult a member of the development team on whether it would be appropriate to split up the PR to add the feature to multiple maps individually.
- * Keep your pull requests small and reviewable whenever possible. Do not bundle unrelated
- fixes even if not bundling them generates more pull requests. In case of mapping PRs that add features -
- consult a maintainer on whether it would be appropriate to split up the PR to add the
- feature to multiple maps individually.
+* Document and explain your pull requests thoroughly. Failure to do so will delay a PR as we question why changes were made. This is especially important if you're porting a PR from another codebase (i.e. TG) and divert from the original. Explaining with single comment on why you've made changes will help us review the PR faster and understand your decision making process.
- * Document and explain your pull requests thoroughly. Failure to do so will delay a PR as
- we question why changes were made. This is especially important if you're porting a PR
- from another codebase (i.e. TG) and divert from the original. Explaining with single
- comment on why you've made changes will help us review the PR faster and understand your
- decision making process.
+* Any pull request must have a changelog, this is to allow us to know when a PR is deployed on the live server. Inline changelogs are supported through the format described [here](https://github.com/ParadiseSS13/Paradise/pull/3291#issuecomment-172950466) and should be used rather than manually edited .yml file changelogs.
- * Any pull request must have a changelog, this is to allow us to know when a PR is deployed
- on the live server. Inline changelogs are supported through the format described
- [here](https://github.com/ParadiseSS13/Paradise/pull/3291#issuecomment-172950466)
- and should be used rather than manually edited .yml file changelogs.
+* Pull requests should not have any merge commits except in the case of fixing merge conflicts for an existing pull request. New pull requests should not have any merge commits. Use `git rebase` or `git reset` to update your branches, not `git pull`.
- * Pull requests should not have any merge commits except in the case of fixing merge
- conflicts for an existing pull request. New pull requests should not have any merge
- commits. Use `git rebase` or `git reset` to update your branches, not `git pull`.
+* Please explain why you are submitting the pull request, and how you think your change will be beneficial to the game. Failure to do so will be grounds for rejecting the PR.
- * Please explain why you are submitting the pull request, and how you think your change will be beneficial to the game. Failure to do so will be grounds for rejecting the PR.
+* If your pull request is not finished make sure it is at least testable in a live environment. Pull requests that do not at least meet this requirement may be closed at maintainer discretion. You may request a maintainer reopen the pull request when you're ready, or make a new one.
- * If your pull request is not finished make sure it is at least testable in a live environment. Pull requests that do not at least meet this requirement may be closed at maintainer discretion. You may request a maintainer reopen the pull request when you're ready, or make a new one.
+* While we have no issue helping contributors (and especially new contributors) bring reasonably sized contributions up to standards via the pull request review process, larger contributions are expected to pass a higher bar of completeness and code quality *before* you open a pull request. Maintainers may close such pull requests that are deemed to be substantially flawed. You should take some time to discuss with maintainers or other contributors on how to improve the changes.
- * While we have no issue helping contributors (and especially new contributors) bring reasonably sized contributions up to standards via the pull request review process, larger contributions are expected to pass a higher bar of completeness and code quality *before* you open a pull request. Maintainers may close such pull requests that are deemed to be substantially flawed. You should take some time to discuss with maintainers or other contributors on how to improve the changes.
+#### Using The Changelog
-#### Using Changelog
- * The tags able to be used in the changelog are: `add/soundadd/imageadd`, `del/sounddel/imagedel`, `tweak`, `fix`, `wip`, `spellcheck`, and `experiment`.
- * Without specifying a name it will default to using your GitHub name. Some examples include:
+* The tags able to be used in the changelog are: `add/soundadd/imageadd`, `del/sounddel/imagedel`, `tweak`, `fix`, `wip`, `spellcheck`, and `experiment`.
+* Without specifying a name it will default to using your GitHub name. Some examples include:
- ```
+```txt
:cl:
add: The ability to change the color of wires
del: Deleted depreciated wire merging now handled in parent
fix: Moving wires now follows the user input instead of moving the stack
/:cl:
- ```
- ```
+```
+
+```txt
:cl: UsernameHere
spellcheck: Fixes some misspelled words under Using Changelog
/:cl:
- ```
-
+```
## Specifications
@@ -88,15 +81,17 @@ As mentioned before, you are expected to follow these specifications in order to
sure you don't have to make any changes and we don't have to ask you to. Thank you for reading this section!
### Object Oriented Code
+
As BYOND's Dream Maker (henceforth "DM") is an object-oriented language, code must be object-oriented when possible in order to be more flexible when adding
content to it. If you don't know what "object-oriented" means, we highly recommend you do some light research to grasp the basics.
### All BYOND paths must contain the full path
+
(i.e. absolute pathing)
DM will allow you nest almost any type keyword into a block, such as:
-```DM
+```dm
datum
datum1
var
@@ -125,45 +120,52 @@ The use of this format is **not** allowed in this project, as it makes finding d
The previous code made compliant:
-```DM
+```dm
/datum/datum1
- var/varname1 = 1
- var/varname2
- var/static/varname3
- var/static/varname4
+ var/varname1 = 1
+ var/varname2
+ var/static/varname3
+ var/static/varname4
/datum/datum1/proc/proc1()
- code
+ code
+
/datum/datum1/proc/proc2()
- code
+ code
/datum/datum1/datum2
- varname1 = 0
+ varname1 = 0
+
/datum/datum1/datum2/proc/proc3()
- code
+ code
+
/datum/datum1/datum2/proc2()
- ..()
- code
+ ..()
+ code
```
### User Interfaces
-All new user interfaces in the game must be created using the TGUI framework. Documentation can be found inside the [`tgui/docs`](../tgui/docs) folder, and the [`README.md`](../tgui/README.md) file.
-This is to ensure all ingame UIs are snappy and respond well. An exception is made for user interfaces which are purely for OOC actions (Such as character creation, or anything admin related)
+
+All new user interfaces in the game must be created using the TGUI framework. Documentation can be found inside the [`tgui/docs`](../tgui/docs) folder, and the [`README.md`](../tgui/README.md) file. This is to ensure all ingame UIs are snappy and respond well. An exception is made for user interfaces which are purely for OOC actions (Such as character creation, or anything admin related)
### No overriding type safety checks
+
The use of the `:` operator to override type safety checks is not allowed. You must cast the variable to the proper type.
### Type paths must begin with a /
+
eg: `/datum/thing`, not `datum/thing`
### Datum type paths must began with "datum"
+
In DM, this is optional, but omitting it makes finding definitions harder. To be specific, you can declare the path `/arbitrary`, but it
will still be, in actuality, `/datum/arbitrary`. Write your code to reflect this.
### Do not use text/string based type paths
+
It is rarely allowed to put type paths in a text format, as there are no compile errors if the type path no longer exists. Here is an example:
-```DM
+```dm
//Bad
var/path_type = "/obj/item/baseball_bat"
@@ -171,13 +173,14 @@ var/path_type = "/obj/item/baseball_bat"
var/path_type = /obj/item/baseball_bat
```
-### Do not use `\The`.
+### Do not use `\The`
+
The `\The` macro doesn't actually do anything when used in the format `\The [atom reference]`. Directly referencing an atom in an embedded string
will automatically prefix `The` or `the` to it as appropriate. As an extension, when referencing an atom, don't use `[atom.name]`, use `[atom]`.
The only exception to this rule is when dealing with items "belonging" to a mob, in which case you should use `[mob]'s [atom.name]` to avoid `The`
ever forming.
-```DM
+```dm
//Bad
var/atom/A
"\The [A]"
@@ -187,18 +190,16 @@ var/atom/A
"[A]"
```
-### Use the pronoun library instead of `\his` macros.
+### Use the pronoun library instead of `\his` macros
+
We have a system in [`code/__HELPERS/pronouns.dm`](../code/__HELPERS/pronouns.dm) for addressing all forms of pronouns. This is useful in a number of ways;
- * BYOND's `\his` macro can be unpredictable on what object it references.
- Take this example: `"[user] waves \his [user.weapon] around, hitting \his opponents!"`.
- This will end up referencing the user's gender in the first occurence, but what about the second?
- It'll actually print the gender set on the weapon he's carrying, which is unintended - and there's no way around this.
- * It always prints the real `gender` variable of the atom it's referencing. This can lead to exposing a mob's gender even when their face is covered,
- which would normally prevent it's gender from being printed.
+
+* BYOND's `\his` macro can be unpredictable on what object it references. Take this example: `"[user] waves \his [user.weapon] around, hitting \his opponents!"`. This will end up referencing the user's gender in the first occurence, but what about the second? It'll actually print the gender set on the weapon he's carrying, which is unintended - and there's no way around this.
+* It always prints the real `gender` variable of the atom it's referencing. This can lead to exposing a mob's gender even when their face is covered, which would normally prevent it's gender from being printed.
The way to avoid these problems is to use the pronoun system. Instead of `"[user] waves \his arms."`, you can do `"[user] waves [user.p_their()] arms."`
-```DM
+```dm
//Bad
"[H] waves \his hands!"
"[user] waves \his [user.weapon] around, hitting \his opponents!"
@@ -209,6 +210,7 @@ The way to avoid these problems is to use the pronoun system. Instead of `"[user
```
### Use `[A.UID()]` over `\ref[A]`
+
BYOND has a system to pass "soft references" to datums, using the format `"\ref[datum]"` inside a string. This allows you to find the object just based
off of a text string, which is especially useful when dealing with the bridge between BYOND code and HTML/JS in UIs. It's resolved back into an object
reference by using `locate("\ref[datum]")` when the code comes back to BYOND. The issue with this is that locate() can return a unexpected datum
@@ -217,7 +219,8 @@ if the original datum has been deleted - BYOND recycles the references.
UID's are actually unique; they work off of a global counter and are not recycled. Each datum has one assigned to it when it's created, which can be
accessed by `[datum.UID()]`. You can use this as a snap-in replacement for `\ref` by changing any `locate(ref)` calls in your code to `locateUID(ref)`.
Usage of this system is mandatory for any `Topic()` calls, and will produce errors in Dream Daemon if it's not used.
-```DM
+
+```dm
//Bad
"Link!"
@@ -226,14 +229,17 @@ Usage of this system is mandatory for any `Topic()` calls, and will produce erro
```
### Use `var/name` format when declaring variables
+
While DM allows other ways of declaring variables, this one should be used for consistency.
### Tabs, not spaces
+
You must use tabs to indent your code, NOT SPACES.
(You may use spaces to align something, but you should tab to the block level first, then add the remaining spaces.)
### No hacky code
+
Hacky code, such as adding specific checks (ex: `istype(src, /obj/whatever)`), is highly discouraged and only allowed when there is ***no*** other option. (Protip: 'I couldn't immediately think of a proper way so thus there must be no other option' is not gonna cut it here! If you can't think of anything else, say that outright and admit that you need help with it. Maintainers, PR Reviewers, and other contributors who can help you exist for exactly that reason.)
You can avoid hacky code by using object-oriented methodologies, such as overriding a function (called "procs" in DM) or sectioning code into functions and
@@ -242,11 +248,13 @@ then overriding them as required.
The same also applies to bugfixes - If an invalid value is being passed into a proc from something that shouldn't have that value, don't fix it on the proc itself, fix it at its origin! (Where feasible)
### No duplicated code
+
Copying code from one place to another may be suitable for small, short-time projects, but Paradise is a long-term project and highly discourages this.
Instead you can use object orientation, or simply placing repeated code in a function, to obey this specification easily.
### 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:
@@ -258,13 +266,16 @@ There are two key points here:
Remember: although this tradeoff makes sense in many cases, it doesn't cover them all. Think carefully about your addition before deciding if you need to use it.
### Prefer `Initialize()` over `New()` for atoms
+
Our game controller is pretty good at handling long operations and lag, but it can't control what happens when the map is loaded, which calls `New()` for all atoms on the map. If you're creating a new atom, use the `Initialize()` proc to do what you would normally do in `New()`. This cuts down on the number of proc calls needed when the world is loaded.
While we normally encourage (and in some cases, even require) bringing out of date code up to date when you make unrelated changes near the out of date code, that is not the case for `New()` -> `Initialize()` conversions. These systems are generally more dependent on parent and children procs, so unrelated random conversions of existing things can cause bugs that take months to figure out.
### No implicit `var/`
+
When you declare a parameter in a proc, the `var/` is implicit. Do not include any implicit `var/` when declaring a variable.
-```DM
+
+```dm
//Bad
/obj/item/proc1(var/mob/input1, var/input2)
code
@@ -275,8 +286,10 @@ When you declare a parameter in a proc, the `var/` is implicit. Do not include a
```
### 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
//Bad
/datum/proc/do_the_thing(thing_to_do)
switch(thing_to_do)
@@ -285,8 +298,10 @@ This means stuff like having a "mode" variable for an object set to "1" or "2" w
if(2)
do_other_stuff()
```
+
There's no indication of what "1" and "2" mean! Instead, you should do something like this:
-```DM
+
+```dm
//Good
#define DO_THE_THING_REALLY_HARD 1
#define DO_THE_THING_EFFICIENTLY 2
@@ -298,9 +313,11 @@ There's no indication of what "1" and "2" mean! Instead, you should do something
if(DO_THE_THING_EFFICIENTLY)
do_other_stuff()
```
+
This is clearer and enhances readability of your code! Get used to doing it!
### Control statements
+
(if, while, for, etc)
* All control statements comparing a variable to a number should use the formula of `thing` `operator` `number`, not the reverse
@@ -318,21 +335,26 @@ This is clearer and enhances readability of your code! Get used to doing it!
```
### Player Output
+
Due to the use of "Goonchat", Paradise requires a special syntax for outputting text messages to players. Instead of `mob << "message"`, you must use `to_chat(mob, "message")`. Failure to do so will lead to your code not working.
### Use early 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
@@ -341,16 +363,19 @@ This is good:
if(thing3 != 30)
return
do stuff
-````
+```
+
This prevents nesting levels from getting deeper then they need to be.
### Use `addtimer()` instead of `sleep()` or `spawn()`
+
If you need to call a proc after a set amount of time, use `addtimer()` instead of `spawn()` / `sleep()` where feasible.
Though more complex, this method has greater performance. Additionally, unlike `spawn()` or `sleep()`, it can be cancelled.
-For more details, see https://github.com/tgstation/tgstation/pull/22933.
+For more details, see [https://github.com/tgstation/tgstation/pull/22933](https://github.com/tgstation/tgstation/pull/22933).
Look for code examples on how to properly use it.
-```DM
+
+```dm
//Bad
/datum/datum1/proc/proc1(target)
spawn(5 SECONDS)
@@ -360,10 +385,11 @@ Look for code examples on how to properly use it.
/datum/datum1/proc/proc1(target)
addtimer(CALLBACK(target, .proc/dothing, arg1, arg2, arg3), 5 SECONDS)
```
-This prevents nesting levels from getting deeper then they need to be.
### Operators
-#### Spacing
+
+#### Spacing of operators
+
* Operators that should be separated by spaces:
* Boolean and logic operators like `&&`, `||` `<`, `>`, `==`, etc. (But not `!`)
* Bitwise AND `&` and OR `|`.
@@ -375,78 +401,86 @@ This prevents nesting levels from getting deeper then they need to be.
* Parentheses `()`.
* Logical not `!`.
-#### Use
+#### Use of operators
+
* Bitwise AND `&`
* Should be written as `bitfield & bitflag` NEVER `bitflag & bitfield`, both are valid, but the latter is confusing and nonstandard.
* Associated lists declarations must have their key value quoted if it's a string
- ```DM
+```DM
//Bad
list(a = "b")
//Good
list("a" = "b")
- ```
+```
#### Bitflags
+
* We prefer using bitshift operators instead of directly typing out the value. I.E:
- ```
+```dm
#define MACRO_ONE (1<<0)
#define MACRO_TWO (1<<1)
#define MACRO_THREE (1<<2)
- ```
- Is preferable to:
- ```
+```
+
+Is preferable to:
+
+```dm
#define MACRO_ONE 1
#define MACRO_TWO 2
#define MACRO_THREE 4
- ```
- While it may initially look intimidating, `(1<Arbitrary text")
//Good
user.visible_message("Arbitrary text")
- ```
- * You should not use color macros (`\red, \blue, \green, \black`) to color text,
- instead, you should use span classes. `Red text`, `Blue text`.
+```
- ```
+* You should not use color macros (`\red, \blue, \green, \black`) to color text, instead, you should use span classes. `Red text`, `Blue text`.
+
+```dm
//Bad
to_chat(user, "\red Red text \black Black text")
//Good
to_chat(user, "Red textBlack text")
- ```
- * To use variables in strings, you should **never** use the `text()` operator, use
- embedded expressions directly in the string.
+```
- ```DM
+* To use variables in strings, you should **never** use the `text()` operator, use embedded expressions directly in the string.
+
+```dm
//Bad
to_chat(user, text("[] is leaking []!", name, liquid_type))
//Good
to_chat(user, "[name] is leaking [liquid_type]!")
- ```
- * To reference a variable/proc on the src object, you should **not** use
- `src.var`/`src.proc()`. The `src.` in these cases is implied, so you should just use
- `var`/`proc()`.
+```
- ```DM
+* To reference a variable/proc on the src object, you should **not** use `src.var`/`src.proc()`. The `src.` in these cases is implied, so you should just use `var`/`proc()`.
+
+```dm
//Bad
var/user = src.interactor
src.fill_reserves(user)
@@ -454,7 +488,7 @@ SS13 has a lot of legacy code that's never been updated. Here are some examples
//Good
var/user = interactor
fill_reserves(user)
- ```
+```
### Develop Secure Code
@@ -462,7 +496,7 @@ SS13 has a lot of legacy code that's never been updated. Here are some examples
* Calls to the database must be escaped properly - use proper parameters (values starting with a :). You can then replace these with a list of parameters, and these will be properly escaped during the query, and prevent any SQL injection.
- ```DM
+```dm
//Bad
var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey='[target_ckey]'")
@@ -470,7 +504,7 @@ SS13 has a lot of legacy code that's never been updated. Here are some examples
var/datum/db_query/query_watch = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey=:target_ckey", list(
"target_ckey" = target_ckey
)) // Note the use of parameters on the above line and :target_ckey in the query.
- ```
+```
* All calls to topics must be checked for correctness. Topic href calls can be easily faked by clients, so you should ensure that the call is valid for the state the item is in. Do not rely on the UI code to provide only valid topic calls, because it won't.
@@ -479,6 +513,7 @@ SS13 has a lot of legacy code that's never been updated. Here are some examples
* 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.
### Files
+
* 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.
@@ -486,6 +521,7 @@ SS13 has a lot of legacy code that's never been updated. Here are some examples
* Files and path accessed and referenced by code above simply being #included should be strictly lowercase to avoid issues on filesystems where case matters.
### SQL
+
* Do not use the shorthand sql insert format (where no column names are specified) because it unnecessarily breaks all queries on minor column changes and prevents using these tables for tracking outside related info such as in a connected site/forum.
* Use parameters for queries, as mentioned above in [Develop Secure Code](#develop-secure-code).
@@ -502,6 +538,7 @@ in the SQL/updates folder.
* Queries must never specify the database, be it in code, or in text files in the repo.
### Mapping Standards
+
* For map edit PRs, we do not accept 'change for the sake of change' remaps, unless you have very good reasoning to do so. Maintainers reserve the right to close your PR if we disagree with your reasoning.
* Map Merge
@@ -558,7 +595,9 @@ in the SQL/updates folder.
* Engine areas, or areas with a high probability of receiving explosions, should use reinforced flooring if appropriate.
* External areas, or areas where depressurisation is expected and normal, should use airless turf variants to prevent additional atmospherics load.
* Edits in mapping tools should generally be possible to replicate in-game. For this reason, avoid stacking multiple structures on the same tile (i.e. placing a light and an APC on the same wall.)
+
### Other Notes
+
* Code should be modular where possible; if you are working on a new addition, then strongly consider putting it in its own file unless it makes sense to put it with similar ones (i.e. a new tool would go in the `tools.dm` file)
* 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.
@@ -569,67 +608,68 @@ in the SQL/updates folder.
* All new var/proc names should use the American English spelling of words. This is for consistency with BYOND.
### Dream Maker Quirks/Tricks
+
Like all languages, Dream Maker has its quirks, some of them are beneficial to us, like these:
#### 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`).
-HOWEVER, if either `some_value` or `i` changes within the body of the for (underneath the `for(...)` header) or if you are looping over a list AND
-changing the length of the list then you can NOT use this type of for-loop!
+`for(var/i = 1, i <= some_value, i++)` is a fairly standard way to write an incremental for loop in most languages (especially those in the C family), but DM's `for(var/i in 1 to some_value)` syntax is oddly faster than its implementation of the former syntax; where possible, it's advised to use DM's syntax. (Note, the `to` keyword is inclusive, so it automatically defaults to replacing `<=`; if you want `<` then you should write it as `1 to some_value-1`).
+
+HOWEVER, if either `some_value` or `i` changes within the body of the for (underneath the `for(...)` header) or if you are looping over a list AND changing the length of the list then you can NOT use this type of for-loop!
### `for(var/A in list)` VS `for(var/i in 1 to list.len)`
-The former is faster than the latter, as shown by the following profile results:
-https://file.house/zy7H.png
-Code used for the test in a readable format:
-https://pastebin.com/w50uERkG
+
+The former is faster than the latter, as shown by the following profile results: [https://file.house/zy7H.png](https://file.house/zy7H.png)
+
+Code used for the test in a readable format: [https://pastebin.com/w50uERkG](https://pastebin.com/w50uERkG)
#### Istypeless for loops
+
A name for a differing syntax for writing for-each style loops in DM. It's NOT DM's standard syntax, hence why this is considered a quirk. Take a look at this:
-```DM
+
+```dm
var/list/bag_of_items = list(sword1, apple, coinpouch, sword2, sword3)
var/obj/item/sword/best_sword
for(var/obj/item/sword/S in bag_of_items)
- if(!best_sword || S.damage > best_sword.damage)
- best_sword = S
+ if(!best_sword || S.damage > best_sword.damage)
+ best_sword = S
```
-The above is a simple proc for checking all swords in a container and returning the one with the highest damage, and it uses DM's standard syntax for a
-for-loop by specifying a type in the variable of the for's header that DM interprets as a type to filter by. It performs this filter using `istype()` (or
-some internal-magic similar to `istype()` - this is BYOND, after all). This is fine in its current state for `bag_of_items`, but if `bag_of_items`
-contained ONLY swords, or only SUBTYPES of swords, then the above is inefficient. For example:
-```DM
+
+The above is a simple proc for checking all swords in a container and returning the one with the highest damage, and it uses DM's standard syntax for a for-loop by specifying a type in the variable of the for's header that DM interprets as a type to filter by. It performs this filter using `istype()` (or some internal-magic similar to `istype()` - this is BYOND, after all). This is fine in its current state for `bag_of_items`, but if `bag_of_items` contained ONLY swords, or only SUBTYPES of swords, then the above is inefficient. For example:
+
+```dm
var/list/bag_of_swords = list(sword1, sword2, sword3, sword4)
var/obj/item/sword/best_sword
for(var/obj/item/sword/S in bag_of_swords)
- if(!best_sword || S.damage > best_sword.damage)
- best_sword = S
+ if(!best_sword || S.damage > best_sword.damage)
+ best_sword = S
```
-specifies a type for DM to filter by.
-With the previous example that's perfectly fine, we only want swords, but if the bag only contains swords? Is DM still going to try to filter because we gave
-it a type to filter by? YES, and here comes the inefficiency. Wherever a list (or other container, such as an atom (in which case you're technically accessing
-their special contents list, but that's irrelevant)) contains datums of the same datatype or subtypes of the datatype you require for your loop's body,
-you can circumvent DM's filtering and automatic `istype()` checks by writing the loop as such:
-```DM
+The above code specifies a type for DM to filter by.
+
+With the previous example that's perfectly fine, we only want swords, but if the bag only contains swords? Is DM still going to try to filter because we gave it a type to filter by? YES, and here comes the inefficiency. Wherever a list (or other container, such as an atom (in which case you're technically accessing their special contents list, but that's irrelevant)) contains datums of the same datatype or subtypes of the datatype you require for your loop's body, you can circumvent DM's filtering and automatic `istype()` checks by writing the loop as such:
+
+```dm
var/list/bag_of_swords = list(sword, sword, sword, sword)
var/obj/item/sword/best_sword
for(var/s in bag_of_swords)
- var/obj/item/sword/S = s
- if(!best_sword || S.damage > best_sword.damage)
- best_sword = S
+ var/obj/item/sword/S = s
+ if(!best_sword || S.damage > best_sword.damage)
+ best_sword = S
```
+
Of course, if the list contains data of a mixed type then the above optimisation is DANGEROUS, as it will blindly typecast all data in the list as the
specified type, even if it isn't really that type, causing runtime errors (AKA your shit won't work if this happens).
#### Dot variable
-Like other languages in the C family, DM has a ```.``` or "Dot" operator, used for accessing variables/members/functions of an object instance.
-eg:
-```DM
+
+Like other languages in the C family, DM has a ```.``` or "Dot" operator, used for accessing variables/members/functions of an object instance. eg:
+
+```dm
var/mob/living/carbon/human/H = YOU_THE_READER
H.gib()
```
+
However, DM also has a dot *variable*, accessed just as `.` on its own, defaulting to a value of null. Now, what's special about the dot operator is that it is automatically returned (as in the `return` statement) at the end of a proc, provided the proc does not already manually return (`return count` for example.) Why is this special?
With `.` being everpresent in every proc, can we use it as a temporary variable? Of course we can! However, the `.` operator cannot replace a typecasted variable - it can hold data any other var in DM can, it just can't be accessed as one, although the `.` operator is compatible with a few operators that look weird but work perfectly fine, such as: `.++` for incrementing `.'s` value, or `.[1]` for accessing the first element of `.`, provided that it's a list.
@@ -638,10 +678,11 @@ With `.` being everpresent in every proc, can we use it as a temporary variable?
DM has a var keyword, called global. This var keyword is for vars inside of types. For instance:
-```DM
+```dm
/mob
- var/global/thing = TRUE
+ 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?
@@ -653,11 +694,14 @@ There is also an undocumented keyword called `static` that has the same behaviou
All new global vars must use the defines in [`code/__DEFINES/_globals.dm`](../code/__DEFINES/_globals.dm). Basic usage is as follows:
To declare a global var:
-```DM
+
+```dm
GLOBAL_VAR(my_global_here)
```
+
To access it:
-```
+
+```dm
GLOB.my_global_here = X
```
@@ -694,6 +738,7 @@ Each role inherits the lower role's responsibilities (IE: Headcoders also have c
`Review Team` members are people who are denoted as having reviews which can affect mergeability status. People included in this role are:
* [lewcc](https://github.com/lewcc)
+* [Sirryan2002](https://github.com/Sirryan2002)
---
diff --git a/.gitignore b/.gitignore
index 1b77727f6bdbe..05e36684f476c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,3 +52,6 @@ __pycache__/
# pyenv
.python-version
+
+# Tools some of us like to keep in the repo
+dmm-tools.exe