-
Notifications
You must be signed in to change notification settings - Fork 305
Code Standards
Majority of content on this page borrowed from https://github.com/NTStation/NTstation13
This page is here to try and help you better your code so that it has a higher chance of getting into the Github repository, and to help you understand some of the quirky and weird qualities of the Dream maker (DM) language.
DM is a language that has a lot of options that perform the same action, but some of them are preferred over others.
/mob/living/carbon/human
The above is a FULL (also known as absolute) typepath, it is preferred over:
/mob/
/living/
/carbon/
/human/
In addition to Typepaths, the placement of the block(s) of code is standardized also (However Byond can still compile if you ignore this, but Standards and rules are what separate order from chaos!)
~80% of the game's code is written with Full paths, and the code block indented in (written one "tab" under) the definition. (This cannot be ignored, Byond will not compile the code if it's incorrectly indented)
Byond also allows procs and verbs to be defined with themselves Indented by one "tab" under the initial define of the object they are attached to.
Type path combinations (Indent numbers from the left of the file):
- (1)Full Type paths and code blocks indented by 1 -- This One is our favourite! 👍
- (2)Non/Indented paths with code indented by 2 -- A few sections of the "Old code" is written in this form, don't continue their tradition 👎
- (3)Full Type paths and code blocks indented by 2 -- Apparently this compiles, but it's ugly as sin 👎
- (4)Non/Indented paths with code indented by 1 -- This won't assign the code block to the path definition, which is functionally wrong! 👎
As special note is that if you alter a section of code (eg, fixing a bug, adding a feature) that is written
case (2), you are Heavily encouraged to alter it to case (1), It helps you and future coders!
mob
var/health
is preferred over:
mob
health
When defining variables, its full typepath must be written out and the var/ prefix must be added.
When Typecasting (Changing or working with the type of an object) single, capital letter variable names are allowed e.g.
var/mob/living/carbon/human/H <br>
is perfectly acceptable and the majority of the time seeing a H in the code, you'll know it means human, or M to mean Mob, etc.
The rules of Typepaths and Variables are also true for Procs and Verbs.
A Proc's name should be an indication of its function.
If a proc counts the number of legs, its name should reflect that.
/mob/proc/return_number_of_legs()
The above makes more sense than say
/mob/proc/legs()
return_number_of_legs() is clearly something that Returns the Number of legs where as legs() could do anything related to legs!
The colon operator allows you to tell the DM compiler to "trust" you when it encounters a : A colon allows you to check a variable on something that, by default does not have that variable You would use a Colon here to tell the compiler that the object to the left of the colon WILL have the variable on the right of the colon when that section of code runs. This is useful for large scale work in a short amount of time, but can cause bugs due to the compiler not checking for errors in that section of code due to the colon.
The colon operator is LAZY coding and a simple 2~ lines can fix it
e.g.
/obj/item
var/value = 10
for(var/atom/moveable/AM in Chest.contents)
AM:value += 10
Now, the /atom/moveable/AM covers Mobs, Objects, Anything that has the potential for Movement is a subtype of /atom/moveable (Note, full typepaths do NOT require you to write out the /atom/moveable part)
if an AM that was an /obj/item was passed through this code it's value would go up by ten. but what if the AM was a mob? Mobs don't have a value variable, DM would throw out an error. You can solve this issue like this:
/obj/item
var/value = 10
for(var/atom/moveable/AM in Chest.contents)
if(istype(AM, /obj/item))
var/obj/item/I = AM
I.value += 10
Now ONLY /obj/item will have their value increased by ten, because only they have the variable!
anything that isn't a /obj/item will not pass the istype() check.
doesSomething(), DoesSomething() or does_something()?
the first is camelCase, the second is Pascal case and the third is underscore delimiter case. All of these are accepted but the majority is written in underscore delimiter case, so please try to use that where possible.
Don't be afraid to add blank lines where you feel the code is too cramped together.
Try not to write code results on the same line as a statement.
e.g.
If(X) return Y
Vs.
If(X)
return Y
When writing a string in Byond/DM you have a list of built in text macros to apply to add unique things to your text string, some add icons into the text, and some change the colour of the text, as well as other nice things.
What we're talking about here are the text ones: \red \blue \green etc.
Don't use them. Just don't. Almost all of SS13's text that is not pure black uses a stylesheet specifically set up for SS13 so all text of the same "type" matches. These are used with HTML spans.
HTML Span quick course:
"<span class='NAME_OF_SPAN_CLASS'>Your text here. Etcetera.</span>"
Put the text inside of the areas as shown.
The names of span classes, as well as their hexidecimal colour values and HTML formatting are stored in stylesheet.dm.
Some common span classes include 'notice', 'warning', and 'danger'. Notice is a blue colored text. Warning is italicized and red. Danger is bold and red.
The act of creating large and complex code which serves only one purpose is very much frowned upon.
Please try and make your code modular, or useful for more things, as often as you can.
This also applies to things which have a standardized way of being done but the standardized form is not used for whatever reason.
If you're unsure on how to proceed with something, ask for help. Do not just copy large chunks of code and attempt to alter them to your needs. If the section of code you copied is bugged or broken, then the copy is also broken. This makes it MUCH harder to get rid of issues, because they're in more than one place.
If your code's function is not obvious to someone with functional knowledge of how code works, or if it's just a particularly weird (yet functional) way of doing things, make a comment explaining how and WHY you've done it the way you have. If the function of the code is clear to someone with reasonable knowledge of Dream Maker or of programming in general then your comment is pointless. If the code says that "H is a human," then don't write a comment saying that H is a human. Placing your name on sections of code you've slightly modified is generally frowned upon, but if you've created the entire section of code yourself you may add your name to it as you see fit.
There are a few files that we only want committing in special circumstances or we never want them committed, as it causes issues later down the line for other coders.
polaris.dme:
This file should only be committed when adding (ticking the file in DM) or removing (unticking the file in DM) files.
Eg:
You add a new mob, in its own new file: example_mob.dm
This file doesn't already exist in the repository's version of polaris.dme, but it does in your local version, you should commit the .dme. Otherwise, it will not compile.
polaris.int:
This file changes to reflect the last thing you had open in DM. It's so you can quickly get back to your work without having to remember the files you were working on. As long as you leave them open, when you close DM, they'll all be open for you the next time you use it. The problem is, it's almost 100% likely that two coders .int files will NOT match, which causes Github to view it as a change. This causes overwrite issues when committing. You cannot prevent this file from changing, however, so make sure to untick it from your commit when committing changes to your branch.
The traditional way to display a message of text to a player is with.
mob_ref << "Blah blah."
This is however somewhat outdated, and now it is much more preferred to use
to_chat(mob_ref, "Blah blah.")
instead, which will accomplish what the old way did but allow for future functionality by redirecting most output to a common proc.
Setting loc refers to this;
var/turf/foo = locate(50, 50, 1)
if(foo)
bar.loc = foo
In most circumstances, you should avoid setting an atom's loc variable as a means of teleporting it somewhere else. The reason for this is that setting loc directly does not inform the atom that it moved, or that if the atom was inside something, that it is now outside it, which can cause things to break.
Instead, use the forceMove()
proc, which will unconditionally move the atom to where you want it to go but also tell everything that it has moved.
The above example can be rewritten as;
var/turf/foo = locate(50, 50, 1)
if(foo)
bar.forceMove(foo)