# Guide To Coding For Space Station 13 <style> :root{ --white-text: #ddd; --white-gray-text: #bbb; --black-text: #333; --gray-text: #666; --navbar-background: #242424; --navbar-alt-background: #363636; --gray-background: #23272a; --darkblue-background: #40788A; --darkblue-alt-background: #2c5460; --link-text: #339fff; --link-hover-text: #2d6da4; } body, .ui-comment-container .ui-comments-container { background-color: var(--gray-background) !important; font-size: 2em !important; } a{ color: var(--link-text); } a:hover, a:active{ color: var(--link-hover-text); } .navbar-default, .navbar-default * label, .navbar-default * span, .ui-comment-container .ui-comment-header, .ui-comment-container .ui-comment-input-container{ background-color: var(--navbar-background); color: var(--white-text); border-color: var(--gray-text); } .modal-dialog * .modal-body{ background-color: var(--navbar-background) !important; color: var(--white-text) !important; font-size: 2em; !important; } .ui-notification-label{ color: var(--white-text) !important; background-color: var(--darkblue-background); border-color: var(--darkblue-alt-background) !important; } .ui-notification-label:hover, .ui-notification-label:active, .ui-notification-label:focus{ background-color: var(--darkblue-alt-background); } .navbar-default .announcement-popover * , .modal-dialog * .modal-header, .modal-dialog * .modal-footer, .panel > .panel-heading, .panel-body{ background-color: var(--gray-background) !important; color: var(--white-text) !important; } .dropdown-menu, .dropdown-menu>li>a{ background-color: var(--gray-background) ; color: var(--white-text); } .markdown-body, .ui-toc-dropdown .nav > .active > a, .ui-comment-container .comment-blank-stats, .markdown-body h1 .octicon-link, .markdown-body h2 .octicon-link, .markdown-body h3 .octicon-link, .markdown-body h4 .octicon-link{ color: var(--white-text); } .ui-toc-dropdown .nav > .active > a:hover{ color: var(--gray-text); } .ui-lastchange, .ui-status-lastchange{ color: var(--white-gray-text); } .markdown-body > blockquote{ border-left: 0.25em solid #5882a7; color: var(--white-gray-text); } .markdown-body > table th, .markdown-body > table tr:nth-child(2n){ background-color: var(--navbar-background); } .markdown-body > table tr{ background-color: var(--navbar-alt-background); } .panel-body .markdown-body tr{ background-color: var(--navbar-background); border-color: var(--navbar-background); } .alert > blockquote{ border-left: 0.25em solid #ccc; } .ui-toc-dropdown { background-color: #23272A; border: 1px solid rgba(255,255,255,.15); box-shadow: 0 6px 12px rgba(255,255,255,.175); } /* Dark mode code block */ /* Imported from titangene/hackmd-dark-theme */ .markdown-body pre { background-color: #1e1e1e; border: 1px solid #555 !important; color: #dfdfdf; font-weight: 600; } .token.operator, .token.entity, .token.url, .language-css .token.string, .style .token.string { background: unset; } /* Dark mode alert boxes */ .alert-info { color: #f3fdff; background: #40788A; border-color: #2F7A95; } .alert-warning { color: #fffaf2; background: #936C36; border-color: #AE8443; } .alert-danger { color: #fff4f4; background: #834040; border-color: #8C2F2F } .alert-success { color: #F4FFF2; background-color: #436643; border-color: #358A28; } /* Stylized alert boxes */ .alert-warning>p::before, .alert-danger>p::before, .alert-info>p::before { white-space: pre; font-weight: bold; } </style> <style> /* * Visual Studio 2015 dark style * Author: Nicolas LLOBERA <nllobera@gmail.com> */ .hljs { display: block; overflow-x: auto; padding: 0.5em; background: #1E1E1E; color: #DCDCDC; } .hljs-keyword, .hljs-literal, .hljs-symbol, .hljs-name { color: #569CD6; } .hljs-link { color: #569CD6; text-decoration: underline; } .hljs-built_in, .hljs-type { color: #4EC9B0; } .hljs-number, .hljs-class { color: #B8D7A3; } .hljs-string, .hljs-meta-string { color: #D69D85; } .hljs-regexp, .hljs-template-tag { color: #9A5334; } .hljs-subst, .hljs-function, .hljs-title, .hljs-params, .hljs-formula { color: #DCDCDC; } .hljs-comment, .hljs-quote { color: #57A64A; font-style: italic; } .hljs-doctag { color: #608B4E; } .hljs-meta, .hljs-meta-keyword, .hljs-tag { color: #9B9B9B; } .hljs-variable, .hljs-template-variable { color: #BD63C5; } .hljs-attr, .hljs-attribute, .hljs-builtin-name { color: #9CDCFE; } .hljs-section { color: gold; } .hljs-emphasis { font-style: italic; } .hljs-strong { font-weight: bold; } .hljs-bullet, .hljs-selector-tag, .hljs-selector-id, .hljs-selector-class, .hljs-selector-attr, .hljs-selector-pseudo { color: #D7BA7D; } .hljs-addition { background-color: #144212; display: inline-block; width: 100%; } .hljs-deletion { background-color: #600; display: inline-block; width: 100%; } </style> ## Contents [toc] ## Introduction So, you want to code for space station 13, but have either never used byond or never coded in general? Well this guide is somewhat for you! In this guide we will go through the process of creating some new and original items for Space Station 13, as well as the process of creating a pull request and getting that item added into the game. Space Station 13 is an open source game, meaning that anybody can contribute to it and create, rework, remove or rebalance features. As such, many of the game developers are players themselves. Even if you have no experience with game development, SS13 is an excellent place to get started. Lets get started ## Precursor Before getting started with anything at all, it is highly recommend that you have played Space Station 13 for some time and know a few mechanics (Not only will it help you understand what the game needs, but if you are new to coding then having a code reference of similar items can be really helpful. For example, if you want to flash everyone in the area, then taking a look at the code for flashes or flashbangs could save you a lot of time trying to code it yourself!). Additionally, I would highly recommend against making PRs specifically touching balance as a first PR. Balance changes may be easy to code, but are some of the hardest to design and take a lot of work and testing to get right. Alright, if you have made it this far you will have absolutely no trouble getting through this guide, now its time to come up with an idea and get started. If at any point you get stuck, help is always available in the #coding section of discord (https://discord.com/invite/ss13). Head over to #role-requests once you have been verified and grab the coder role by reacting with the socks to the message in that channel. This will give you access to the coding channel where you can ask questions and seek help. (When asking questions, make sure you provide enough information and an image of the code you need help with, otherwise it may be difficult to see what is wrong. Also keep in mind that errors may occur due to the result of other lines, so any compilation errors may be a result of the code around your failing line.) The coderbus discord is a good discord for asking questions about Byond in general and is the server for coders from all codebases. https://discord.gg/Vh8TJp9 ## Setting up the development environment Space Station 13 runs on the Byond game engine, which comes with built in tools for making games. Unfortunately, due to the complexity of SS13 it cannot be built with just the tools provided by Byond. Building the game is done via build tools, which are automatically run when compiling in Visual Studio Code. ### Downloading the Code We can't do much without the code. Since the game is open source it is available for free on github at https://github.com/BeeStation/BeeStation-Hornet/. If you have experience with using git, then simply clone the repo and skip this step, otherwise continue onwards. Github is a version management program, this means that multiple people can work on the code at one time and their changes can be merged together (usually) with few issues. If you have no experience with git, I would recommend using Github Desktop, as it provides a nice GUI for github actions (Note it is only available on windows). **Download a Git Client** Github desktop can be downloaded from https://desktop.github.com/ Unfortunately, github desktop cannot perform all github actions, you may want to download git bash for a command line version of git to perform the actions that Git Desktop cannot handle (mainly resetting everything when you accidently screw up). https://git-scm.com/downloads **Create a fork** Navigate to https://github.com/BeeStation/BeeStation-Hornet and press the fork button in the top right of the github page. This will create a fork of the repository that you are free to change as you wish. **Clone your new repository** Now that you have created a fork of the codebase, you need to download it locally. On your github client, press the clone repository button and select the new created repository or enter the URL. Select an empty file to download the code to. You should now have all the files required to build the game. ### Setting up Visual Studio Code **Step 1:** Install visual studio code. https://code.visualstudio.com/ This will allow you to create code as well as compile the game. **Step 2:** Install the recommened plugins for VS code and navigate to the cloned repository. <details> <summary>Steps for opening the environment</summary> Select 'Open Folder...' from the file menu in the top left. (You can also do this from the main screen of visual studio when you first open it) ![](https://i.imgur.com/klD9aOL.png) Navigate to the folder which contains the beestation.dme file, and press select file (It should look like this). ![](https://i.imgur.com/67dcVoE.png) </details> <details> <summary>Steps for downloading extensions</summary> Navigate to this location ![](https://i.imgur.com/zbSutOK.png) Search for 'Byond' ![image](https://hackmd.io/_uploads/H1o-LHBWR.png) Download the Space Station 13 extension pack. ![image](https://hackmd.io/_uploads/HyqxIBSWC.png) Press the cog wheel on the extension labelled 'DM Language Client' and then add the path to your byond installation. If it hasn't automatically been able to locate it. ![](https://hackmd.io/_uploads/rJHeur96n.png) *You should only need to do this if Byond is not installed in the default location which should be: C:/Program Files (x86)/BYOND* **IF YOU SET A DEFAULT BYOND PATH, IT MUST BE THE FOLDER CALLED BYOND, AND NOT `BYOND/bin` or `BYOND/bin/dreamdaemon.exe`** </details> These plugins will provide several important features, such as code highlighting and auto-complete. They will make your life so much easier, they are basically required. ---- **Linux Step** (Ignore if you are using windows) If you are using Linux, then you will need to install node from your package manager or from https://nodejs.org/en/download/. Additionally, unless you are using TGS4 or later you will need to compile rust-g on the server and obtain a .so file to host the game (See https://github.com/tgstation/rust-g). (Byond doesn't have amazing support for linux, a lot of the UIs may be broken or unusable, so using windows is recommended.) ### Building the game Navigate to any .dm code file in visual studio and hit F5. This will begin compilation. After about a minute, the game should open. **NOTE: Byond will automatically enter safe-mode if the file the code is contained inside is different to the name of the .dme file. As such you MUST name the containing folder of the codebase 'beestation' otherwise you will be swarmed with a million popup messages making the game unplayable.** ### Important Buttons In the top right there are a bunch of buttons. **Server > Start Now:** Starts the game as soon as it is loaded. Much easier than waiting around. **Debug > View Runtimes** Shows any runtime errors that have occured in game. **Adminbus > Game Panel** Allows you to create objects. ## Creating our first item In this guide we will go through the process of creating a brand new item without previous experience of coding in Byond or working with the SS13 codebase. We will be creating the 'ethereal dance grenade'. This item is a grenade that when thrown will create a localized disco causing everyone who isn't an ethereal nearby to be forced to dance. The first thing to do is to create a new file to store the item in. Make sure to put it in a sensible location. In this case, we will create it in /code/game/objects/items/grenades/discogrenade.dm Note that the .dm extension is important. If you are creating a file in visual studio with the plugins, it should automatically be added to the beestation.dme file. If not, you will have to manually add it. ![](https://i.imgur.com/xVkSXH0.png) Note that this file is in alphabetical order. ![](https://i.imgur.com/6gp4kN6.png) Now we have an empty file, ready to be coded. Since our item is a grenade, lets take a look at an existing grenade to see how it functions. Here is the code for flashbangs. (You can use ctrl-shift-f in visual studio code to search through the entire codebase as long as you know the name of something.) ![](https://i.imgur.com/W4MXuOh.png) Lets take a look at what this code is doing and what it means. The top element of the grenade is the 'typepath'. ![](https://i.imgur.com/r5eIXnw.png) ### Object Oriented Programming In Byond Byond is an object oriented programming language. This means that items inherit from other items. Anything inherited from something else will automatically take all of the functionalities, behaviours, procs and variables from the thing it inherits from. #### Typepaths / Inheritence In this case, the typepath is `/obj/item/grenade/flashbang` This is a unique item that has a `/obj/item/grenade` parent type. The grenade parent type has functionality coded in it that causes the grenade to activate when used in the hand, and calls an explode proc when the timer runs out. The grenade parent type also inherits from the `/obj/item` type. The item type allows an item to be picked up and placed in the inventory. Why does any of this matter? If a proc is defined on a parent type, then there is no need to create it again. Since all grenades share the fact that they are used in a player's hand to activate, have a short timer and then explode we simply have to create a single grenade 'supertype' which does this meaning that we no longer have to duplicate code, which saves time, makes the code easier to edit (if there is a bug with grenades, you only have to change the super type and not every grenade) and easier to expand. This behaviour is critical for maintaining a large codebase such as Beestation. The next part of the code is the variable definitions of the type. ![](https://i.imgur.com/Mp3UPau.png) ![](https://i.imgur.com/6QG6qPa.png) Note that these variables do not start with `var/`, however flashbang_range does. This is because the variables name, icon_state, item_state, lefthand_file and righthand_file are all defined in a supertype somewhere. The variable flashbang_range is a variable unique to flashbangs (and any subtypes of flashbang). We wouldn't have access to flashbang_range on regular grenades, or welding tools, however if we created another 'super flashbang', instead of duplicating code we could create a new type (`/obj/item/grenade/flashbang/super`) and simply define flashbang_range as being something else. In this example, the entire code for making a super flashbang would be ``` /obj/item/grenade/flashbang/super name = "super flashbang" flashbang_range = 30 //This is a really big flashbang! ``` #### Inheritence - Polymorphism The next part of flashbangs is an example of a proc that inherits from another proc. ![](https://i.imgur.com/8JuP3Q6.png) The format for these is defined with the typepath of the item, followed by the name of the proc, followed by a list of the parameters (in this case lanced_by). Parameters are the inputs to a proc, when you call another proc you can pass in arguments which can then be used by that proc. ![](https://i.imgur.com/SSs7jqJ.png) Means that lanced_by is a local variable of type /mob/living. The value of this will be whatever has been passed in by the caller of this proc. #### Byond Polymorphism/Return value Syntax Special byond syntax. ![](https://i.imgur.com/EbztNqc.png) What is this? The variable `.` is the default return value of a proc. If a proc is exited, either by reaching the end of it, hitting a `return` (which immediately stops execution of a proc) or by encountering a runtime exception, the value stored in `.` will be passed to whatever called it. This value can be used with `var/a = function()` for example, or even used in an `if` statement to check if a condition was met. `if(function() == "success")` will enter the `if` block if `proc/function` returns "success". It is important to note that the value of `.` will not be returned, if you specifically tell a proc to return a value. For example: ``` /proc/test() . = "test" return "hello" ``` Will return "hello" instead of "test". The other special syntax used here is `..()`. In byond `..()` indicates that the parent proc should be called. For example: ``` /datum/proc/test() return 4 /datum/double/test() . = ..() . *= 2 ``` When running test() on a regular datum, the value 4 will be returned. However, if you create a datum of type `datum/double` and then execute test() on that, the second proc will be executed. This override proc, calls the parent proc (which in this case is `/datum/proc/test()`), stores the result of the parent call into the default return value (`.`) and then multiplies the value stored in the default return value (`.`) by 2. This will result in 8 being returned. #### Polymorphism/Typed proc calls **How do we create, store and then call a proc on a specific type?** Creating a new instance of a type is pretty simple in Byond. We simple use the `new` keyword and then enter the typepath of the thing we want to create. Example: ``` var/instance_of_test = new /datum/test() ``` This will create a new object of type `/datum/test` and store it within the local variable `instance_of_test`. In this case, we haven't specified the type of instance_of_test, meaning that we won't be able to activate the `test()` proc that we created in the above example. In order to have access to that, we need to set the type of instance_of_test. ``` var/datum/test/instance_of_test = new() ``` In this case we have created a new local variable with the name `instance_of_test` and the type `/datum/test`. Note how that because we defined the type of the variable, we do not need to provide `new` with the path of the object we want to create (it has assumed that we want to create `datum/test`). Now that we have defined our variable to be that specified type, we can call procs that are accessible to `datum/test`. This means that we can call any proc defined on `/datum` or on `/datum/test`. If we created a new subtype of `/datum/test` called `/datum/test/B` and created a proc on `/datum/test/B`, we would not be able to call it from this typed variable, since `/datum/test` cannot see procs defined in `/datum/test/B` (Inheritence is one-way.) To use procs defined on an instance of something, we can simply do this: ``` var/datum/test/instance_of_test = new() instance_of_test.test() ``` **Why is this useful?** Take for example `/obj/item/grenade/flashbang` `/obj/item/grenade` has a proc defined called `prime` which is where the grenade should explode. The behaviour of prime is defined as follows: ![](https://i.imgur.com/mN5gFDV.png) In english, this proc checks if the grenade is a dud, in which case it returns `FALSE`. It then makes it so the grenade cannot be used again. It then sends a signal to the grenade (which can be intercepted by components that respnod to grenade explosions) It then creates an explosion if an explosion range is defined and returns `TRUE`. When it comes to flashbangs, this isn't particularly useful which is why we overrode the proc on `/obj/item/grenade/flashbang` to add in the ability to flash nearby people when `prime` is triggered. Now lets say we have a list of all grenades. ``` var/list/obj/item/grenade/list_of_grenades = list() ``` This is a variable of type `list` which contains `/obj/item/grenade`s. Since the `prime` proc is defined on `/obj/item/grenade`, we can call `prime` on every single object that is stored within this list. Let's say we add a flashbang to this list. When we call `prime`, instead of calling `/obj/item/grenade/proc/prime()`, `/obj/item/grenade/flashbang/prime()` will be called instead, since the flashbang overrides `prime`. It may sound obvious but in practice it allows us to do many powerful things: ``` //Add a syndicate minibomb list_of_grenades += new /obj/item/grenade/syndieminibomb() //Add a flashbang list_of_grenades += new /obj/item/grenade/flashbang() ``` Our variable `list_of_grenades` now contains 2 things both of which are a `/obj/item/grenade` or a subtype of it. It conatins: - A syndicate minibomb - A flashbang Now we can trigger each of these grenades individually ``` //Get the first grenade (The minibomb) var/obj/item/grenade/first = list_of_grenades[1] ``` Allows us to take the first element of the list we created, which will take the syndicate minibomb and store it in the variable `first`. Note how we made the type of the variable `first` a `/obj/item/grenade` and not an `/obj/item/grenade/syndieminibomb`. This is because we don't actually know that the first item will be a minibomb, since we only told the list to store `/obj/item/grenade` and not specifically minibombs. Now if we want to detonate the minibomb we can call ``` first.prime(usr) ``` This will call `prime` on the minibomb. The most important part of all of this, and the reason there is so much written about this is that even though our variable `first` has the type `/obj/item/grenade`, when we call `prime` on `first` instead of calling `/obj/item/grenade/proc/prime`, `/obj/item/grenade/syndieminibomb/prime` will be called instead because first is a syndicate minibomb. This works for all subtypes, if instead the value of first was a flashbang, instead of calling `/obj/item/grenade/proc/prime`, `/obj/item/grenade/flashbang/prime` will be called instead, triggering a flashbang instead of an explosion. #### Iteration Finally, lets trigger every single grenade in the list of grenades. To do this, we will need a for loop. A for loop on a list will take every single element of a list, store it in a local variable and then execute a block of code. The code that will be executed is all code after the for loop that is intended by a **tab**. Lets make a basic foor loop: ``` for(var/item in list_of_grenades) message_admins("I have a grenade!") ``` This loop will go through every single object stored in the list `list_of_grenades` and for each element, will store the value of it into `item`. Then the block of code indented underneath the for loop will be executed once for every item in `list_of_grenades`. This is an extremely simple (and not very useful) case of a for loop that sends a message to the admins saying "I have a grenade!" one time for every item in that list. Now lets prime all the grenades in the list. To do that, we will need to be able to call the `prime` proc that is defined on `/obj/item/grenade`. To even access it, we will need to make our for loop typed. ``` for(var/obj/item/grenade/grenade_item in list_of_grenades) grenade_item.prime() ``` This will now locate all items that inherit from or are the type `/obj/item/grenade` that is stored in `list_of_grenades` and call the `prime` proc on them. Since our `list_of_grenades` stores 1 syndicate minibomb and 1 flashbang, this will create an explosion and a flash at the location of wherever those 2 items are. It is important to note that if you specify a variable type in a for loop (like we did above), byond will do a type check on every single item in the list. This means that if we added something that wasn't a grenade into `list_of_grenades`, then this for loop would skip the item that isn't a grenade. This is great and provides some safety to lists like this, however since in this case we know that `list_of_grenades` will never contain something that isn't a grenade we can optimise it by using `as()`, which will disable type checking on the for loop and process on every single element. ``` //Minor optimisation for(var/obj/item/grenade/grenade_item as() in list_of_grenades) grenade_item.prime() ``` An example of for loops in action would be the flashbang proc that we have already seen. ![](https://i.imgur.com/NGay1ib.png) These 2 for loops find every single living mob within a certain range that can see the flashbang and flashes them, then finds every living mob within a certain range that can hear the flashbang and soundbangs them. #### Selection The last important thing to understand before we get coding is selection (The `if` statement). This allows us to choose if we want to do something based on whether or not certain conditions are met. In byond `if` is pretty simple to use. Simply write `if`, followed by a boolean expression that evaluates to either true or false. If the condition evaluates to true when the `if` statement is executed, then the code inside the block will be executed, otherwise it won't be. For example ``` var/test = 5 if(test == 5) test_is_five() ``` In this case, we have the variable `test` which holds the value `5`. We then have an `if` statement that checks if the value of `test` is `5` or not. If the value of `test` is `5`, then we execute the function `test_is_five()` which is defined somewhere else in the code. If the value of `test` is not `5`, then we simply continue and ignore anything indented after the `if` statement. If we wanted to perform some action if and only if the value of test was not 5, then we could use `else`. ``` var/test = 6 if(test == 5) test_is_five() else test_is_not_five() ``` In this case, since the value of `test` is not `5`, the condition `test == 5` will return `FALSE`, meaning `test_is_five()` will not be executed and instead `test_is_not_five()` will be executed instead. **Null objects automatically evaluate to false, and objects that are not null automatically evaluate to true.** If you want to check if the value of a variable actually exists you can do: ``` var/obj/item/grenade/test_grenade if(test_grenade) test_grenade.prime() ``` This will first check if the value of test_grenade is defined, and if it is then it will prime it. If `test_grenade` hasn't been set to anything, then it will not prime. (This check is important if you don't know if a variable will exist or not, since it could result in runtime errors if `test_grenade` doesn't exist!) This specific code block can be simplified to: ``` test_grenade?.prime() ``` This will do the same thing as ``` if(test_grenade) test_grenade.prime() ``` **Checking the type of something** If you not only want to check if something exists, but also if it has a specific type, byond has a built in function called `istype`. This will check the type path of something. ``` //Create a new flashbang var/obj/item/grenade/test_grenade = new /obj/item/grenade/flashbang() //We know that test_grenade is a grenade, but we may not know the type of it. //(If we took it in as a parameter, then it could be any grenade at all). if(istype(test_grenade, /obj/item/grenade/flashbang)) grenade_is_flashbang() if(istype(test_grenade, /obj/item/grenade/syndieminibomb)) grenade_is_minibomb() ``` This will create a typed variable called `test_grenade` and store a flashbang inside of it. The first if statement then checks to see if the object stored inside `test_grenade` is a flashbang, and if it is, `grenade_is_flashbang()` is executed. It will then check if the object stored inside `test_grenade` is a syndicate minibomb, and if it is then `grenade_is_minibomb` is executed. This can be useful if you are looking for a very specific type of object in a list that could be anything. (For example, finding a specific item in an area, or seeing what a player was attacked by.) ### Using our new Coding Knowledge Now that we hopefully somewhat understand how Byond and object oriented programing languages are working, we can now get to coding our new item. #### Creating the typepath The first thing we need to do is create the typepath, and set the values of any parent variables. ![](https://i.imgur.com/wG4H3RT.png) This will create a new item that inherits from `obj/item/grenade` (meaning that we have all the grenade functionality already on this item). We have overriden the name, so that the name of this new item is 'Ethereal disco grenade' as well as the description and icons. Lets go and create a custom icon for our item. #### Creating Icons / Sprites To add a sprite into the game, we will need to add an icon into a .dmi file. Byond uses .dmi files to store icon and animation data. To edit this files, we will need to use DreamMaker. Open the DreamMaker app from byond. ![](https://i.imgur.com/8MJ001E.png) In the top right select File > Open Environment ![](https://i.imgur.com/JZbvvTq.png) Locate your beestation.dme file and open it. ![](https://i.imgur.com/PDACTos.png) This will load all of the game data into DreamMaker. **WARNING: DreamMaker can be somewhat unstable and may crash when a lot of icon files are opened. Save regularly.** On the left side of the application you should now see a list of folders. ![](https://i.imgur.com/NNf0pHQ.png) Open up icons, as this is where all of the icon data for SS13 is stored. **How does Byond choose which icon file to use?** Every `/atom` (Thing that exists physically in the world) has a variable called `icon`. This variable takes in a path to a .dmi file. In addition to `icon`, they also have `icon_state` which indicates which state to use in the icon. Icons work as spritesheets, with many different states. If we take a look at the code for grenades, we can see the value of icon and icon_state have already been set ![](https://i.imgur.com/nwAILJT.png) This means that our subtype (`/obj/item/grenade/discogrenade`) will by default use the icon file `'icons/obj/grenade.dmi'` and have the icon_state `"grenade"`. If we wanted to, we could create a brand new .dmi file and then set the `icon` value of our disco grenade to that new file, however in this case it makes sense to put the new grenade icon in the grenade file itself and simply change the `icon_state`. Navigate to `icons/obj/grenade.dmi` and open it. We are now presented with the grenade.dmi icon file and all of the icon states within it. ![](https://i.imgur.com/8zaZF7t.png) If you double click an icon, you can edit it or you can press the button in the top left to create a new one. Lets create a new icon state and name it 'disco'. The byond image editor is pretty basic, however we can make our item with animations. ![](https://i.imgur.com/mNKS2AJ.png) We have now created 2 new icon states within the grenade.dmi file. ![](https://i.imgur.com/yAyrZEc.png) **Make sure to save!** If we go back to the code, we can now set the icon and icon_state of our new grenade. ![](https://i.imgur.com/tPCmjyX.png) Since we haven't made an in hand sprite, we cannot change the item_state (this would need to be a change in these 2 files ![](https://i.imgur.com/IS5HMGl.png)). We now have a brand new grenade that looks fancy, however it does nothing. #### Coding Functinoality We want our disco grenade to do something when it explodes (create cluster party bombs.) To do this, lets override the prime proc. ![](https://i.imgur.com/bAPqKqe.png) In this simple form, the proc will call the parent proc and return the resulting value of that call. We want a flashing light to appear every time a disco grenade explodes. To find out how to do this, lets take a look at the code for the "flash" item. Here it is: (Finding similar items will be sped up if you know the exact name of the item in game.) ![](https://i.imgur.com/OngAVRi.png) If we scroll through the code for the flash, eventually we will find this: ![](https://i.imgur.com/0YXTqEq.png) This code turns on the light source associated with flashes and then calls a function that turns the light off after `FLASH_LIGHT_DURATION` deciseconds. This probably isn't the best way to do this, so I'll cheat a little bit and reveal the `flash_lighting_fx` proc which creates a temporary light source at a location. (Sometimes asking on discord is a good way to find a proc that you might need!) If we take a look at flash_lighting_fx, we can see there are 4 definitions for it. ![](https://i.imgur.com/gWhYtJl.png) One of them defines its behaviours for atoms, one defines its behaviour for turfs, one for objects and one for mobs. Since the type of our grenade is `/obj/item/grenade/discogrenade`, we will be using the proc defined for `/obj`, however since they all inherit from `flash_lighting_fx`, we only need to worry about the parameters of that. ![](https://i.imgur.com/MWk7Pya.png) It seems like this proc takes in 4 parameters, the range, the power, the colour and the duration. Lets create a temporary light flash with a random colour when the grenade is detonated. ![](https://i.imgur.com/jh4K45Y.png) Upon detonation our grenade will now pulse whatever colour we pass into it. Unfortunately our colour isn't quite random yet, so lets take a look at something that uses random colours. The ethereal disco ball. If we take a look at the code for the ethereal disco ball we can see this: ![](https://i.imgur.com/yZPttTb.png) or more specifically ![](https://i.imgur.com/z9BlEwa.png) So getting a random colour turns out to be pretty simple, we just have to call the `random_color()` proc. ![](https://i.imgur.com/6DZx4O0.png) Our disco grenade should now pulse a random colour when it detonates. Lets try it out in game!. If you want the game to load faster, remove the comment before //#define LOWMEMORYMODE in _maps/_basemap.dm. **MAKE SURE YOU REMOVE THIS BEFORE SUBMITTING A PR!** ![](https://i.imgur.com/iaBqe0q.png) This will activate low memory mode, making the game load a small map, ignore lavaland and load much quicker. Now we are in the game, navigate to the **Adminbus** tab in the top right and select **Game Panel** ![](https://i.imgur.com/aAhsphK.png) This will open up a menu with multiple options. ![](https://i.imgur.com/ewSDYRP.png) Select **Create Object**. Enter the type path of the object we created (In this case it will be `/obj/item/grenade/discogrenade`) ![](https://i.imgur.com/nHCnTRh.png) Select the object you want to create and hit **Spawn** Our item is now in the game! ![](https://i.imgur.com/L4eepCK.png) Lets try it out, prime it like any other grenade and throw it! ![](https://i.imgur.com/sQ2Dzf2.png) it's a little bit hard to see, so lets turn of the lights. ![](https://i.imgur.com/TQBLRyP.png) Our grenade is definitely flashing, but it is only flashing white. The first thing to do when debugging is check for runtime errors. Lets take a look in Debug > View Runtimes. ![](https://i.imgur.com/BVS4qML.png) Nothing... Let's take a look at some other implementations of flash_lighting_fx! Use shift-ctrl-f to search in Visual Studio. ![](https://i.imgur.com/SfPAIaI.png) Here is one: ![](https://i.imgur.com/m4yvxDH.png) Let's take a look at the light colour they use. ![](https://i.imgur.com/IrNYuqF.png) Hm, lets compare this to our own colour returned from random_color() Either set a break point at the variable colour to inspect the value in Visual studio editor, type `message_admins(colour)` to get an admin message or examine the `random_color()` proc and work out what it does. If you do any of these you may realise the issue: The working light colour starts with a `#`, however our one does not. Let's fix that: ![](https://i.imgur.com/fvI2EbO.png) In Byond you can insert variables into strings by using `"[var]"`, so lets insert a hash in front of our random color. You may have also noticed that the grenade wasn't deleted when we used it. Lets add a deletion in after it detonates. ![](https://i.imgur.com/RLIAndB.png) qdel is the SS13 deletion proc, it stands for quick delete. It is used to delete atoms from the world. It is extremely important that you don't have any references to the object you are deleting, otherwise you create hard-deletes. If you have a variable that contains an object, you need to ensure that the variable containing an object gets set to `null` when the object is destroyed. ![](https://i.imgur.com/kXR3iwV.png) And now it works! Next up, lets make it force nearby people to dance. Performing an action on nearby people is something that flashbangs already do, so we can take that part for ourselves! Lets create a variable to store the range in which we should make people dance, create a new proc that handles making people dance and then call that proc on every person within the specified range. The `viewers` proc is a built in byond proc. With the byond extension installed, you should be able to ctrl-click any proc to navigate to its definition. If it is a byond internal proc, the documentation will open. ![](https://i.imgur.com/0OIkVYZ.png) As we can see it takes in a range and a center. The `src` variable in byond is a reference to itself. If we pass src as a parameter, it passes the object that is currently calling this function. ![](https://i.imgur.com/UKKKeSy.png) Now we have a proc that gets called with a mob parameter that we can use to make people dance. We also want to freeze people that are dancing in place. We can do this by using `Stun`. ![](https://i.imgur.com/yVawB9q.png) **Your task to improve this code:** Time to get creative! Add some extra functionality to this item. Feel free to share your code in the #coding section of discord to let other people help you improve your code. Tasks: - Add some random variation to the emotes that play. (Hint: Use `switch` and `rand(1, 3)`) - Make it so this doesn't affect mindshielded people. (Hint: The kindle spell from clockcult has a special effect if someone is mindshielded (`/datum/clockcult/scripture/slab/kindle`)) - Make it so people with social anxiety get scared of dancing. (Hint: The infinite pizza box checks your quirks to see if you have a specific one `/obj/item/pizzabox/infinite`) <details> <summary>Example Solution</summary> ![](https://i.imgur.com/ZNTlEmj.png) </details> Make sure to compile and test your code thoroughly. Taking screenshots would be helpful for the pull request phase, as proving that your code works to maintainers will make it much easier for them to review and accept your PR. ![](https://i.imgur.com/xaEUC4a.png) Our item works, but its a little bit boring. Lets add some sound to it. If we take a look at the flashbang code once again, we can find the proc used for playing sound in Space Station 13. ![](https://i.imgur.com/T5rvc9i.png) ``` /proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff_exponent = SOUND_FALLOFF_EXPONENT, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, use_reverb = TRUE) ``` There are a lot of parameters to this, however a lot of them have default values already provided. If you do not enter a value in, the default value will be 0 / false / null, unless one is provided such as `use_reverb = TRUE`, in which case the default value will be `TRUE`. To find a sound file that you want to use, open up your file explorer and navigate to the sound section of the SS13 code. I am going to use some sound files from the instruments section of the game since instruments would be played at a party. Lets use the playsound proc to play a sound. We need to give it the source atom, which in this case would be the grenade, the file of the sound, for this case we will us some arbritary sound file ('sound/instruments/guitar/Cn5.ogg'), the volume of the sound (from 0 to 100), and some more optional parameters. Vary will make the sound have a randomised pitch. ![](https://i.imgur.com/6ZtpLhj.png) There we go, we now have a guitar playing every time the grenade detonates. **Extra Task:** Make it so the instrument that plays is randomised either from a list of guitar, piano, glockenspiel and violin or from a list of all possible instruments (Harder). (Hint: Byond has a built in function called pick, which will return one of the parameters randomly) <details> <summary> Example Solution </summary> ![](https://i.imgur.com/KBozwHV.png) </details> The last thing we are going to do is make our item a little bit more interesting, by having it blast grenades everywhere. Lets create another grenade to spawn grenades. (Note that cluster grenades already exist, however we will not be using the subtype of them for example purposes. If you were creating it properly, you would use the clusterbuster grenade instead, since it would make the code a lot cleaner!) Lets create a new discogrenade subtype that spawns more disco grenades as well as an ethereal disco ball. ![](https://i.imgur.com/NR1TOWD.png) Override the prime proc and make the grenade spawn an ethereal disco ball at the current location. **The new keyword in Byond can take in parameters. The parameters are passed as arguments to the /New() method which are then passed on to /Initialize(). Creating atoms in Byond has a required parameter however, which is the location to spawn it at.** **When creating a new object, it will be placed inside the first parameter of the new action. If this is an item, then the item will be inside that item and may not be accessible. If you pass in a turf, it will spawn on the ground.** ![](https://i.imgur.com/tUKm0xY.png) Now we have this code: ![](https://i.imgur.com/ltVvdUw.png) Lets make it spawn more grenades. For loops can loop through elements in a list, but it can also loop through numbers ``` for(var/i in 1 to 20) ``` Will loop through all the numbers between 1 and 20. This allows us to run a specific function a set number of times, without having to copy and paste a block of code N number of times. ![](https://i.imgur.com/EsyLt9V.png) Currently, this will just spawn a grenade at the location of the parent grenade and do nothing. To do anything with the sub grenades, we will need to store the created grenade into a variable. Lets change this code to: ![](https://i.imgur.com/H5tSFip.png) Every time a new grenade spawns, we want it to be primed automatically as if someone activated it in their hand. lets take a look at the `attack_self` proc of grenade, as this is what is called when someone uses a grenade in their hand. ![](https://i.imgur.com/JMdae0y.png) It looks like `preprime` is the proc that activates the grenade, lets take a look at it. ![](https://i.imgur.com/9YkUVUr.png) This line is what causes the explosion to happen, so lets copy it and make some modifications. ![](https://i.imgur.com/zPIqgVl.png) What does any of this mean? The adds a timer which calls a callback on the subgrenade to trigger the prime proc defined on `/obj/item/grenade` after 5 to 50 deciseconds (0.5 to 5 seconds). My final task to you is to have the grenade thrown in a random direction after it is created. These procs may be useful: ``` throw_at(target, range, speed, ...): Throws an atom at a specified target. pick(): Picks a random element from a list view(distance, center): Gets everything in view within a certain range ``` <details> <summary> Example Solution </summary> ![](https://i.imgur.com/fxj4Yts.png) Note that this could be improved by only checking for nearby turfs, since this will be biased towards places with more items in it (View returns everything, not just turfs!). </details> ### Final Code <details> <summary> Final Code: Useful if you have a problem and you don't know why </summary> ``` /obj/item/grenade/discogrenade //Set the name of the item name = "Ethereal disco grenade" //Set the examine description of the item desc = "An unethical grenade that will cause anyone nearby to be forced into a dancing frenzy! \ Ethereals may not like this, and may even be slightly offended by this act." //Set the icon of the item (What it looks like on the ground) icon_state = "disco" //Set the item state of the item (What it looks like in a hand) item_state = "flashbang" //The range at which we force people to dance var/dance_range = 4 //The time at which we stun for var/stun_time = 5 SECONDS /obj/item/grenade/discogrenade/prime(mob/living/lanced_by) . = ..() //If our parent call returned false, we are a dud! if(!.) return //Store a colour in a local variable //Note that color is a built in byond variable, so using the british spelling for //the local variable colour is a good idea. var/colour = "#[random_color()]" //Execute the lighting flash, with a range of 3, power of 6, color of the variable colour and a time of 0.5 SECONDS. flash_lighting_fx(3, 3, colour, 0.5 SECONDS) //Find all people nearby that can see src (src is ourselves) for(var/mob/living/dancer in viewers(dance_range, src)) make_dance(dancer) //Play a sound //Select a random instrument var/selected_instrument = pick("guitar", "piano", "glockenspiel", "violin") //Since we cannot include [] in paths, we have to get the file from a string instead playsound(src, file("sound/instruments/[selected_instrument]/Cn5.ogg"), 100, TRUE) //Delete the object qdel(src) /obj/item/grenade/discogrenade/proc/make_dance(mob/living/dancer) //Early return is the dancer has a mindshield if(HAS_TRAIT(dancer, TRAIT_MINDSHIELD)) //Send a message to the user to tell them they are protected to_chat(dancer, "<span class='warning>You are protected from the urge to dance!</span>") //Return: This will immediately stop execution of the current proc. //Early returns are prefered to using blocks of if and else as it leads to cleaner code. return //Check if the user has the social anxiety quirk if(dancer.has_quirk(/datum/quirk/social_anxiety)) //Send a message to the person with social anxiety to_chat(dancer, "<span class='userdanger'>You really don't like this...</span>") //Paralyze them for 10 seconds (Forces them to fall over) dancer.Paralyze(10 SECONDS) //Drop all their items dancer.drop_all_held_items() //Return from the proc and don't continue execution return //Trigger a random emote switch(rand(1, 3)) if(1) //Trigger the dance emote. (Note: Emotes are predefined) dancer.emote("dance") if(2) //Trigger the spin emote. (Note: Emotes are predefined) dancer.emote("spin") if(3) //Trigger the flip emote. (Note: Emotes are predefined) dancer.emote("flip") //Stun the dancer for the stun_time, so that they cannot move dancer.Stun(stun_time) /obj/item/grenade/discogrenade/spawner var/amount_spawned = 15 /obj/item/grenade/discogrenade/spawner/prime(mob/living/lanced_by) . = ..() //If we were a dud, return if(!.) return //Get the turf that this item is currently on var/turf/spawn_location = get_turf(src) //Create an ethereal disco ball at the location of this grenade new /obj/structure/etherealball(spawn_location) var/list/everything_in_view = view(8, src) //Create subgrenades for(var/i in 1 to amount_spawned) var/obj/item/grenade/discogrenade/subgrenade = new(spawn_location) addtimer(CALLBACK(subgrenade, /obj/item/grenade.proc/prime), rand(5, 50)) subgrenade.throw_at(pick(everything_in_view), 8, 3) ``` </details> ## Creating a pull request Open your git client and take a look at your changes. ![](https://i.imgur.com/srzJ0H7.png) It is important to note that before starting to code, we forgot to create a new branch. Branches in git are a way of seperating one set of changes from another, and allow you to organise your changes and work on multiple things at once. Lets make a new branch. If you are using github desktop, press the button in the top middle that says 'current branch' and select 'new branch'. ![](https://i.imgur.com/Odp4GEI.png) Give your branch a sensible name and press 'create branch' ![](https://i.imgur.com/ndZprEb.png) ![](https://i.imgur.com/pi32Zfi.png) If you have already made some changes, you will be presented with 2 options: leave the changes or bring them. Since we want to put the changes we made onto a new branch, press 'Bring my changes'. **Important: Never edit your master!** You should always keep the master branch free of any commits, since all branches you make are based on your master branch. If you make changes to your master branch then every single PR in the future you try to make will contain the same changes. Next up, examine your changes and make sure everything seems okay. ![](https://i.imgur.com/W9Gx1RV.png) Whoops: We forgot to disable low memory mode. We can right click and discard that change. The other 2 changes are okay to go, so enter a summary and press commit. ![](https://i.imgur.com/VkRxvmt.png) Make sure you publish the branch to github, so that it is not only stored on your local machine. ![](https://i.imgur.com/jgGPkIJ.png) After doing all of that, we now have the option to create a pull request. Hit that button and it will open up the browser. You will be provided with a template and can fill out the details yourself. The more you prove that your item is well tested and works, the quicker it will get approved by maintainers, and the quicker it will be in the game. Congratulations! You are now a Space Station 13 contributor. By completing this guide, you just completed the first steps towards learning how to code on large playerbases, becoming a contributor, becoming a maintainer, becoming a head-dev and taking over the world! Unfortunately, the ethereal dance grenade already existe (https://github.com/BeeStation/BeeStation-Hornet/pull/1127), so your PR will most likely get closed for being a duplicate, however don't let that stop you from making new, unique and original items that have never been thought of before. Your imagination is now the only thing stopping you. Good luck!