Difference between revisions of "Coding-tutorial"

From The Powder Toy
Jump to: navigation, search
m (Fix link, I may try to update the guide eventually)
(Updated guide + fix code)
Line 22: Line 22:
 
'''Step One: Defining the Element'''
 
'''Step One: Defining the Element'''
  
Open powder.h in the editor of your choice (Visual Studio for windows users) and go to ~line 199 (Find: #define PT_NUM)
+
Open powder.h in the editor of your choice (Visual Studio for windows users) and go to ~line 217 (Find: #define PT_NUM)
<code c>
+
#define PT_NONE 0
#define PT_NONE 0
+
#define PT_DUST 1
#define PT_DUST 1
+
 
+
...
...
+
 
+
#define PT_GBMG 157
#define PT_FROG 145
+
#define PT_FIGH 158
#define PT_BRAN 146
+
#define PT_NUM  159
#define PT_NUM  147
 
</code>
 
  
 
Notice each element has an incrementing ID and the last number in the list is equal to the number of elements (it is one higher then the highest because the elements start on 0)? This is necessary to note because it is necessary to change when adding new elements.
 
Notice each element has an incrementing ID and the last number in the list is equal to the number of elements (it is one higher then the highest because the elements start on 0)? This is necessary to note because it is necessary to change when adding new elements.
Line 40: Line 38:
 
Each element's number is incremented by one. PT_NUM is the number of elements in the game (PT_NONE starts at zero).
 
Each element's number is incremented by one. PT_NUM is the number of elements in the game (PT_NONE starts at zero).
  
<code c>
+
#define PT_FIGH 158
#define PT_BRAN 146
+
#define PT_HETR 159
#define PT_HETR 147
+
#define PT_NUM  160
#define PT_NUM  148
 
</code>
 
  
 
When adding an element, place it before PT_NUM. Give it the number PT_NUM had previously and add one to PT_NUM's number. The elements should still increment by one.
 
When adding an element, place it before PT_NUM. Give it the number PT_NUM had previously and add one to PT_NUM's number. The elements should still increment by one.
Line 52: Line 48:
 
'''Step Two: Defining the Element's Primary Properties'''
 
'''Step Two: Defining the Element's Primary Properties'''
  
Still in powder.h, go to ~line 519 (Find: {"BRAN", PIXPACK(0xCCCC00) )
+
Still in powder.h (in the future, this may be moved into elementdata.c, line 172), go to ~line 654 (Find: {"FIGH", PIXPACK(0x000000)...
<code c>
+
 
{"", PIXPACK(0x000000), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 100, SC_SPECIAL, R_TEMP+0.0f +273.15f, 251, "Erases particles.", ST_NONE, 0, NULL},
+
{"", PIXPACK(0x000000), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, ...
{"DUST", PIXPACK(0xFFE0A0), 0.7f, 0.02f * CFDS, 0.96f, 0.80f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 1, 10, 0, 0, 30, 1, 85, SC_POWDERS, R_TEMP+0.0f +273.15f, 70, "Very light dust. Flammable.", ST_SOLID, TYPE_PART, NULL},
+
{"DUST", PIXPACK(0xFFE0A0), 0.7f, 0.02f * CFDS, 0.96f, 0.80f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 1, 10, 0, 0, ...
.....
+
.....
{"FROG", PIXPACK(0x00AA00), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 100, SC_LIFE2, 9000.0f, 40, "Frogs S12/B34/3", ST_NONE, TYPE_SOLID|PROP_LIFE, NULL},
+
{"FIGH", PIXPACK(0x000000), 0.5f, 0.00f * CFDS, 0.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.00f * CFDS, 0, 0, 0, 0, 0, 1, 1...
{"BRAN", PIXPACK(0xCCCC00), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 100, SC_LIFE2, 9000.0f, 40, "Brian 6 S6/B246/3", ST_NONE, TYPE_SOLID|PROP_LIFE, NULL},
+
//Name Colour Advec...
//Name Colour Advec Airdrag Airloss Loss Collid Grav Diffus Hotair Fal Burn Exp Mel Hrd M  Weights Section H Ins Description State Properties Function
 
  
</code>
 
 
  
 
These are the primary definitions for the elements. You can see what each variable means at the top of the code as well as just below.
 
These are the primary definitions for the elements. You can see what each variable means at the top of the code as well as just below.
  
'''Name''': The name of the element. Always use four letters, even if the element has a three letter name.
+
'''Name''': The name of the element. Try to use 4 letters, but some elements only have 3 (or 5)
  
  
Line 111: Line 104:
  
  
'''M''': Does it show up on the menu? 1 = yes, 0 = no.
+
'''Use''': Can it be created with the brush (or console)? 1 = yes, 0 = no. Always use 1
 +
 
 +
 
 +
'''M''': Does it show up on the menu? 1 = yes, 0 = no. Always use 1
  
  
Line 117: Line 113:
  
  
'''Section''': The section of the menu it is in. Prefix everything with 'SC_'.
+
'''Section''': The section of the menu it is in. Prefix everything with 'SC_'. Look at interface.h line 36 for the different section names
  
  
Line 127: Line 123:
  
 
'''Description''': A short one sentence description of the element, shown when you mouse over it in-game.
 
'''Description''': A short one sentence description of the element, shown when you mouse over it in-game.
 +
  
 
'''State''': What state is this element? Options are ST_NONE, ST_SOLID, ST_LIQUID, ST_GAS.
 
'''State''': What state is this element? Options are ST_NONE, ST_SOLID, ST_LIQUID, ST_GAS.
  
'''Properties''': Does this element have special properties? The properties can be found at ~214.  Separate each property with | inside the property variable.
+
 
 +
'''Properties''': Does this element have special properties? The properties can be found at ~232.  Separate each property with | inside the property variable. Some properties are below
 +
 
  
 
'''Function''':  This is new, and adds a huge performance increase, for now please put NULL here, we will come back to this later.  
 
'''Function''':  This is new, and adds a huge performance increase, for now please put NULL here, we will come back to this later.  
 +
 +
 +
'''Graphics Function''':  This is more new, and was created with the new drawing system. Put NULL here, unless you know how to create a graphics function
 +
 +
 +
Properties:
 +
There are 5 properties for the different states:
 +
 +
TYPE_PART(powders), TYPE_LIQUID, TYPE_SOLID, TYPE_GAS, and TYPE_ENERGY. You should pick one of these to use for your element.
 +
 +
If your element conducts electricity, use PROP_CONDUCTS.
 +
 +
PROP_DEADLY makes your element kill stickmen.
 +
 +
PROP_HOT_GLOW makes your element glow when hot, like metl does.
 +
 +
PROP_RADIOACTIVE makes your element radioactive.
 +
 +
There are a few more properties, but they are slightly less useful or don't do anything yet.
  
  
  
 
This is a lot to handle, and if you feel overwhelmed by some of the choices, try looking at elements similar to what you are creating and base the value off of that. The following values are an example of what your code is supposed to look like. The color of heater will be the same as the HEAT element, and it will be an indestructible solid in the special menu that transfers heat quickly.
 
This is a lot to handle, and if you feel overwhelmed by some of the choices, try looking at elements similar to what you are creating and base the value off of that. The following values are an example of what your code is supposed to look like. The color of heater will be the same as the HEAT element, and it will be an indestructible solid in the special menu that transfers heat quickly.
<code c>
+
...
...
+
{"FIGH", PIXPACK(0x000000),     0.5f, 0.00f * CFDS,     0.2f,     1.0f, 0.0f,   0.0f,   0.0f, ...
{"BRAN", PIXPACK(0xCCCC00), 0.0f, 0.00f * CFDS, 0.90f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 0, 1, 100, SC_LIFE2, 9000.0f, 40, "Brian 6 S6/B246/3", ST_NONE, TYPE_SOLID|PROP_LIFE, NULL},
+
{"HETR",    PIXPACK(0xFFBB00),    0.0f,    0.00f * CFDS,    0.90f,    0.00f,    0.0f,    0.0f,    0.00f,
{"HETR",    PIXPACK(0xFFBB00),    0.0f,    0.00f * CFDS,    0.90f,    0.00f,    0.0f,    0.0f,    0.00f,   0.000f  * CFDS,    0,    0,       0,    0,    1,    1,    100,    SC_SPECIAL,        22.0f+273.15f,           251,   "Heats objects it touches", ST_SOLID, TYPE_SOLID, NULL},
+
0.000f  * CFDS,    0,    0,     0,  0,    0,    1,    1,    100,    SC_SPECIAL,        22.0f+273.15f,   251,
};
+
"Heats objects it touches", ST_SOLID, TYPE_SOLID, NULL, NULL},
</code>
+
};
  
 
'''Step Three: Defining the Element's State Changes'''
 
'''Step Three: Defining the Element's State Changes'''
Line 148: Line 166:
  
  
Still in powder.h, ~line 681 (Find: /* GOL */ {IPL ... )
+
Still in powder.h, ~line 828 (or at the end of elementdata.c) Find:  
 
+
//     if low pressure if high pressure if low temperature if high temperature
<code c>
+
// Name     plv plt phv pht tlv tlt   thv tht
{ //     if low pressure if high pressure if low temperature if high temperature
+
/* NONE */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT},
// Name     plv plt phv pht tlv tlt thv tht
+
/* DUST */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT},
/* NONE */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT},
 
/* DUST */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT},
 
</code>
 
  
 
This part of the code is new as well, it replaces the old states table and replaces it with a transition table.  This means you will define when the element changes into another.  For example WATR, it will freeze at 273.15K, so in this table it has a transition at a LOW temp of 273.15, and will turn into ICE.  Similarly, water boils at 373, so it will have a transition at a HIGH temp of 373.  Here is the line for WATR:
 
This part of the code is new as well, it replaces the old states table and replaces it with a transition table.  This means you will define when the element changes into another.  For example WATR, it will freeze at 273.15K, so in this table it has a transition at a LOW temp of 273.15, and will turn into ICE.  Similarly, water boils at 373, so it will have a transition at a HIGH temp of 373.  Here is the line for WATR:
<code c>
 
 
/* WATR */ {IPL, NT, IPH, NT, 273.15f,PT_ICEI, 373.0f, PT_WTRV},
 
/* WATR */ {IPL, NT, IPH, NT, 273.15f,PT_ICEI, 373.0f, PT_WTRV},
</code>
 
  
 
This table now also has pressure transitions, such as ICE breaking into SNOW under pressure, this is done the same way, there is a LOW pressure change, and a HIGH pressure, here is the line for ICE:
 
This table now also has pressure transitions, such as ICE breaking into SNOW under pressure, this is done the same way, there is a LOW pressure change, and a HIGH pressure, here is the line for ICE:
<code c>
 
 
/* ICE  */ {IPL, NT, 0.8f, PT_SNOW, ITL, NT, 233.0f, ST},
 
/* ICE  */ {IPL, NT, 0.8f, PT_SNOW, ITL, NT, 233.0f, ST},
</code>
 
  
 
As you can see, there is a HIGH pressure transition of 0.8, which means if the pressure goes above 0.8, then it will turn into SNOW.
 
As you can see, there is a HIGH pressure transition of 0.8, which means if the pressure goes above 0.8, then it will turn into SNOW.
Line 172: Line 183:
  
 
lets add heater, this is simple because for our simple heater, it has no transitions, so everything should be IPL,IPH,ITL,ITH and NT.
 
lets add heater, this is simple because for our simple heater, it has no transitions, so everything should be IPL,IPH,ITL,ITH and NT.
<code c>
+
...
...
+
     /* FIGH  */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT},
     /* GOL  */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT},
+
     /* HETR  */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT},
     /* HETR  */ {IPL, NT, IPH, NT, ITL, NT, ITH, NT},
+
};
};
 
</code>
 
  
  
Line 187: Line 196:
  
 
Now we need to make a hetr.c file for our new heater element, if using visual studio, you should be able to right click on the source folder inside the project, and create a new file, and name it hetr.c  Once you have a blank hetr.c created and it is included in the project, we need to add a few things to this file.
 
Now we need to make a hetr.c file for our new heater element, if using visual studio, you should be able to right click on the source folder inside the project, and create a new file, and name it hetr.c  Once you have a blank hetr.c created and it is included in the project, we need to add a few things to this file.
<code c>
+
#include <element.h>
#include <element.h>
+
 
+
int update_HETR(UPDATE_FUNC_ARGS) {
int update_HETR(UPDATE_FUNC_ARGS) {
+
 
+
return 0;
return 0;
+
}
}
 
</code>
 
 
Before we go on with the actual code, we need to finish up a few things first so that the code actually knows there is a new update_HETR function.  Go back to powder.h at line ~250. You will see lots of int update_''''''(UPDATE_FUNC_ARGS); .  This list is sorted alphabetically so lets put in our new HETR function.
 
Before we go on with the actual code, we need to finish up a few things first so that the code actually knows there is a new update_HETR function.  Go back to powder.h at line ~250. You will see lots of int update_''''''(UPDATE_FUNC_ARGS); .  This list is sorted alphabetically so lets put in our new HETR function.
<code c>
+
int update_GOO(UPDATE_FUNC_ARGS);
int update_GOO(UPDATE_FUNC_ARGS);
+
int update_HETR(UPDATE_FUNC_ARGS);
int update_HETR(UPDATE_FUNC_ARGS);
+
int update_HSWC(UPDATE_FUNC_ARGS);
int update_HSWC(UPDATE_FUNC_ARGS);
 
</code>
 
 
Remember that function variable in the ptypes array? we need to let it know that HETR has a special function to use, instead of NULL.  Replace NULL with &update_HETR.
 
Remember that function variable in the ptypes array? we need to let it know that HETR has a special function to use, instead of NULL.  Replace NULL with &update_HETR.
<code c>
+
... "Heats objects it touches", ST_SOLID, TYPE_SOLID, &update_HETR},
{"HETR",    PIXPACK(0xFFBB00),    0.0f,    0.00f * CFDS,    0.90f,    0.00f,    0.0f,    0.0f,    0.00f,    0.000f  * CFDS,    0,    0,        0,    0,    1,    1,    100,    SC_SPECIAL,        22.0f+273.15f,            251,    "Heats objects it touches", ST_SOLID, TYPE_SOLID, &update_HETR},
 
</code>
 
  
 
Now that our new HETR function will be called properly, we can go back into hetr.c and finish it up. NOTE: Put all code BEFORE the return 0; line so when it finishes running, it will go back to the main code.  If you kill the particle from inside the function, please return 1;.  Our HETR element will not die, so you don't have to worry about that.
 
Now that our new HETR function will be called properly, we can go back into hetr.c and finish it up. NOTE: Put all code BEFORE the return 0; line so when it finishes running, it will go back to the main code.  If you kill the particle from inside the function, please return 1;.  Our HETR element will not die, so you don't have to worry about that.
  
 
Now we need to go over some useful ways of detecting particles, so that we can heat them.
 
Now we need to go over some useful ways of detecting particles, so that we can heat them.
<code c>
+
for(rx=-1; rx<2; rx++)
for(rx=-1; rx<2; rx++)
+
    for(ry=-1; ry<2; ry++)
    for(ry=-1; ry<2; ry++)
 
</code>
 
  
 
This code simply means "any particle touching a heater particle".
 
This code simply means "any particle touching a heater particle".
  
 
It basically sets the grid size in which particles are affected by the effect of the particle. In this case, it is a 3x3 grid around the center one.  If you are having trouble getting this, try thinking about rx and ry, as a radius around the current particle.
 
It basically sets the grid size in which particles are affected by the effect of the particle. In this case, it is a 3x3 grid around the center one.  If you are having trouble getting this, try thinking about rx and ry, as a radius around the current particle.
<code c>
+
for(rx=-2; rx<3; rx++)
for(rx=-2; rx<3; rx++)
+
    for(ry=-1; ry<2; ry++)
    for(ry=-1; ry<2; ry++)
 
</code>
 
 
This would make the grid affected 5x3
 
This would make the grid affected 5x3
<code c>
+
for(rx=-1; rx<2; rx++)
for(rx=-1; rx<2; rx++)
+
    for(ry=-2; ry<3; ry++)
    for(ry=-2; ry<3; ry++)
 
</code>
 
 
And this would flip the dimensions.
 
And this would flip the dimensions.
  
Line 231: Line 228:
  
 
Add this:
 
Add this:
<code c>
+
if(x+rx>=0 && y+ry>0 &&    x+rx<XRES && y+ry<YRES &&
if(x+rx>=0 && y+ry>0 &&    x+rx<XRES && y+ry<YRES &&
 
 
                 pmap[y+ry][x+rx] &&
 
                 pmap[y+ry][x+rx] &&
 
                 (pmap[y+ry][x+rx]&0xFF)!=PT_HETR&&
 
                 (pmap[y+ry][x+rx]&0xFF)!=PT_HETR&&
 
                 (pmap[y+ry][x+rx]&0xFF)!=0xFF)
 
                 (pmap[y+ry][x+rx]&0xFF)!=0xFF)
 
         {
 
         {
</code>
 
  
  
  
 
Your entire code should look like this so far:
 
Your entire code should look like this so far:
<code c>
 
 
         for(rx=-1; rx<2; rx++)
 
         for(rx=-1; rx<2; rx++)
 
             for(ry=-1; ry<2; ry++)
 
             for(ry=-1; ry<2; ry++)
1              if(x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES &&
+
1              if(x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES &&
2              pmap[y+ry][x+rx] &&
+
2              pmap[y+ry][x+rx] &&
3              (pmap[y+ry][x+rx]&0xFF)!=PT_HETR&&
+
3              (pmap[y+ry][x+rx]&0xFF)!=PT_HETR&&
4              (pmap[y+ry][x+rx]&0xFF)!=0xFF)
+
4              (pmap[y+ry][x+rx]&0xFF)!=0xFF)
5      {
+
5      {
</code>
 
 
1st line: If the current grid particle around the HETR pixel is within the screen, AND
 
1st line: If the current grid particle around the HETR pixel is within the screen, AND
  
Line 263: Line 256:
  
 
Now add this:
 
Now add this:
<code c>
+
r = pmap[y+ry][x+rx];
r = pmap[y+ry][x+rx];
 
</code>
 
  
 
It means you can just type 'r' instead of 'pmap[y+ry][x+rx]'. This will simplify code later on.
 
It means you can just type 'r' instead of 'pmap[y+ry][x+rx]'. This will simplify code later on.
 
NEW: because our hetr.c file is separate, we need to initialize these variables we are using inside hetr.c, add this as the first part of the update_HETR function.
 
NEW: because our hetr.c file is separate, we need to initialize these variables we are using inside hetr.c, add this as the first part of the update_HETR function.
<code c>
+
int r, rx, ry;
int r, rx, ry;
 
</code>
 
  
 
Now add
 
Now add
<code c>
+
if(parts[r>>8].temp + (parts[r>>8].temp*0.2f)<=MAX_TEMP)
if(parts[r>>8].temp + (parts[r>>8].temp*0.2f)<=MAX_TEMP)
 
</code>
 
 
Let's analyze.
 
Let's analyze.
  
Line 298: Line 285:
  
 
This ensures it won't go over the maximum temperature. Now add this:
 
This ensures it won't go over the maximum temperature. Now add this:
<code c>
+
parts[r>>8].temp += parts[r>>8].temp*0.2f;
parts[r>>8].temp += parts[r>>8].temp*0.2f;
 
</code>
 
  
 
In English,
 
In English,
Line 308: Line 293:
  
 
Now add:
 
Now add:
<code c>
+
else {
else {
+
    parts[r>>8].temp = MAX_TEMP;
    parts[r>>8].temp = MAX_TEMP;
+
}
}
 
</code>
 
  
 
IF the particle's temperature + 20% of it's temperature is less then then the maximum temperature possible, THEN add 20% of the particle's temperature to itself, ELSE the temperature is the maximum temperature.
 
IF the particle's temperature + 20% of it's temperature is less then then the maximum temperature possible, THEN add 20% of the particle's temperature to itself, ELSE the temperature is the maximum temperature.
Line 319: Line 302:
 
Now close the two brackets we used for the if statements to complete the section.
 
Now close the two brackets we used for the if statements to complete the section.
 
The entire hetr.c should now look like this:
 
The entire hetr.c should now look like this:
<code c>
+
#include <element.h>
#include <element.h>
 
 
   
 
   
int update_HETR(UPDATE_FUNC_ARGS) {
+
int update_HETR(UPDATE_FUNC_ARGS) {
 
     int r, rx, ry;
 
     int r, rx, ry;
 
     for(rx=-1; rx<2; rx++)
 
     for(rx=-1; rx<2; rx++)
Line 342: Line 324:
 
     }
 
     }
 
     return 0;
 
     return 0;
}
+
}
</code>
 
 
Congrats, your HETR element should now work.
 
Congrats, your HETR element should now work.
  
Line 367: Line 348:
 
5) Go to your Powder Toy repository page on GitHub and press "Pull Request" at the top.
 
5) Go to your Powder Toy repository page on GitHub and press "Pull Request" at the top.
  
6) Send the request to facialturd (Simon's username) and you should be done. Verify that the code has been changed if you like.
+
6) Do not send the request to Simon if it is the element created in this tutorial. He will not accept it. If it's a very good and very useful element, send the request to facialturd (Simon's username) and you should be done. Verify that the code has been changed if you like.
  
 
7) If Simon decides to accept your request, your code will be in the official Powder Toy source code. Congratulations!  
 
7) If Simon decides to accept your request, your code will be in the official Powder Toy source code. Congratulations!  

Revision as of 23:31, 9 December 2011

For a video tutorial, check out: gamerboy8864's Tutorial on how to mod The Powder Toy


This tutorial will give you guidelines on creating an element in The Powder Toy. We will use triclops200's heater element in this example. The color will be the same as the HEAT element, and it will be an indestructible solid in the special menu, that transfers heat quickly. Updated to latest source by cracker64, if you have any problems please contact me, if there are any obvious errors, feel free to correct.


It's not as simple as typing the name, color, and features, but it's almost that easy.


Note: Line numbers given here will probably change as more code is added. Look in the general area of the line number given. Use your editors "Find" tool to get the precise line number.


Part One: Defining the Element's Properties


Step One: Defining the Element

Open powder.h in the editor of your choice (Visual Studio for windows users) and go to ~line 217 (Find: #define PT_NUM)

#define PT_NONE 0
#define PT_DUST 1

...

#define PT_GBMG 157
#define PT_FIGH 158
#define PT_NUM  159

Notice each element has an incrementing ID and the last number in the list is equal to the number of elements (it is one higher then the highest because the elements start on 0)? This is necessary to note because it is necessary to change when adding new elements.

lets add a new value.

Each element's number is incremented by one. PT_NUM is the number of elements in the game (PT_NONE starts at zero).

#define PT_FIGH 158
#define PT_HETR 159
#define PT_NUM  160

When adding an element, place it before PT_NUM. Give it the number PT_NUM had previously and add one to PT_NUM's number. The elements should still increment by one.


Step Two: Defining the Element's Primary Properties

Still in powder.h (in the future, this may be moved into elementdata.c, line 172), go to ~line 654 (Find: {"FIGH", PIXPACK(0x000000)...

{"", PIXPACK(0x000000), 0.0f, 0.00f * CFDS, 1.00f, 0.00f, 0.0f, 0.0f, 0.00f, 0.000f * CFDS, 0, 0, 0, 0, 1, 1, 1, ...
{"DUST", PIXPACK(0xFFE0A0), 0.7f, 0.02f * CFDS, 0.96f, 0.80f, 0.0f, 0.1f, 0.00f, 0.000f * CFDS, 1, 10, 0, 0, ...
.....
{"FIGH", PIXPACK(0x000000), 0.5f, 0.00f * CFDS, 0.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.00f * CFDS, 0, 0, 0, 0, 0, 1, 1...	
//Name Colour Advec...


These are the primary definitions for the elements. You can see what each variable means at the top of the code as well as just below.

Name: The name of the element. Try to use 4 letters, but some elements only have 3 (or 5)


Colour: Color in hexadecimal code. Go to http://www.colorpicker.com/ to find the hexadecimal code (at the top) for your color. The hexadecimal code goes AFTER the "0x" prefix, always.


Advec: How much the particle is accelerated by moving air.


Airdrag: How much air the particle generates in the direction of travel.


Airloss: How much the particle slows down moving air (although this won't have as big an effect as a wall). 1 = no effect, 0 = maximum effect.


Loss: How much velocity the particle loses each frame. 1 = no loss, .5 = half loss.


Collid: Velocity is multiplied by this when the particle collides with something.


Grav: How fast the particle falls. A negative number means it floats.


Diffus: How much the particle "wiggles" around (think GAS).


Hotair: How much the particle increases the pressure by.


Fal: How does the particle move? 0 = solid, 1 = powder, 2 = liquid


Burn: Does it burn? 0 = no, higher numbers = higher "burnage".


Exp: Does it explode? 0 = no, 1 = when touching fire, 2 = when touching fire or when pressure > 2.5


Mel: Does it melt? 1 = yes, 0 = no.


Hrd: How much does acid affect it? 0 = no effect, higher numbers = higher effect.


Use: Can it be created with the brush (or console)? 1 = yes, 0 = no. Always use 1


M: Does it show up on the menu? 1 = yes, 0 = no. Always use 1


Weight: Heavier elements sink beneath lighter ones. 1 = Gas. 2 = Light, 98 = Heavy (liquids 0-49, powder 50-99). 100 = Solid. -1 is Neutrons and Photons.


Section: The section of the menu it is in. Prefix everything with 'SC_'. Look at interface.h line 36 for the different section names


H: What temperature does it have when created? Temperature is in Kelvin (Kelvin = degrees C + 273.15). R_TEMP+273.15f gives room temperature.


Ins: specific heat value (how fast it transfers heat to particles touching it), can be found by using the real life heat value in J/G K (or KJ/KG K) by 96.635/RealLifeValue. 0 - no heat transfer, 250 - maximum heat transfer speed.


Description: A short one sentence description of the element, shown when you mouse over it in-game.


State: What state is this element? Options are ST_NONE, ST_SOLID, ST_LIQUID, ST_GAS.


Properties: Does this element have special properties? The properties can be found at ~232. Separate each property with | inside the property variable. Some properties are below


Function: This is new, and adds a huge performance increase, for now please put NULL here, we will come back to this later.


Graphics Function: This is more new, and was created with the new drawing system. Put NULL here, unless you know how to create a graphics function


Properties: There are 5 properties for the different states:

TYPE_PART(powders), TYPE_LIQUID, TYPE_SOLID, TYPE_GAS, and TYPE_ENERGY. You should pick one of these to use for your element.

If your element conducts electricity, use PROP_CONDUCTS.

PROP_DEADLY makes your element kill stickmen.

PROP_HOT_GLOW makes your element glow when hot, like metl does.

PROP_RADIOACTIVE makes your element radioactive.

There are a few more properties, but they are slightly less useful or don't do anything yet.


This is a lot to handle, and if you feel overwhelmed by some of the choices, try looking at elements similar to what you are creating and base the value off of that. The following values are an example of what your code is supposed to look like. The color of heater will be the same as the HEAT element, and it will be an indestructible solid in the special menu that transfers heat quickly.

...
{"FIGH",	PIXPACK(0x000000),     0.5f,	0.00f * CFDS,     0.2f,      1.0f,	0.0f,    0.0f,    0.0f, ...
{"HETR",    PIXPACK(0xFFBB00),    0.0f,    0.00f * CFDS,    0.90f,    0.00f,    0.0f,    0.0f,    0.00f,
0.000f  * CFDS,    0,    0,      0,  0,    0,    1,    1,    100,    SC_SPECIAL,        22.0f+273.15f,   251,
"Heats objects it touches", ST_SOLID, TYPE_SOLID, NULL, NULL},
};

Step Three: Defining the Element's State Changes


Still in powder.h, ~line 828 (or at the end of elementdata.c) Find:

//	    if low pressure		if high pressure		if low temperature	if high temperature
// Name	    plv		plt	 	phv	pht			tlv	tlt	  	thv	tht
/* NONE */ {IPL,	NT,		IPH,	NT,			ITL,	NT,		ITH,	NT},
/* DUST */ {IPL,	NT,		IPH,	NT,			ITL,	NT,		ITH,	NT},

This part of the code is new as well, it replaces the old states table and replaces it with a transition table. This means you will define when the element changes into another. For example WATR, it will freeze at 273.15K, so in this table it has a transition at a LOW temp of 273.15, and will turn into ICE. Similarly, water boils at 373, so it will have a transition at a HIGH temp of 373. Here is the line for WATR: /* WATR */ {IPL, NT, IPH, NT, 273.15f,PT_ICEI, 373.0f, PT_WTRV},

This table now also has pressure transitions, such as ICE breaking into SNOW under pressure, this is done the same way, there is a LOW pressure change, and a HIGH pressure, here is the line for ICE: /* ICE */ {IPL, NT, 0.8f, PT_SNOW, ITL, NT, 233.0f, ST},

As you can see, there is a HIGH pressure transition of 0.8, which means if the pressure goes above 0.8, then it will turn into SNOW.

NOTE: For an element that does NOT have a transition at high/low pressure/temp, please follow the same format as the others and use IPL,IPH,ITL,ITH and NT.

lets add heater, this is simple because for our simple heater, it has no transitions, so everything should be IPL,IPH,ITL,ITH and NT.

...
   /* FIGH   */ {IPL,	NT,	IPH,	NT,	ITL,	NT,	ITH,	NT},
   /* HETR  */ {IPL,	NT,	IPH,	NT,	ITL,	NT,	ITH,	NT},
};


Step Four: Defining the Element's Special Properties

At this point, you would be able to compile, and the HETR would show up in the menu and you can place it, BUT it doesn't do anything! Now for the part where we actually code what the element does. Make sure to save powder.h.

This is also where new element creation is different from before, if you look inside the src folder, you will now see an elements folder. Inside here is a *.c file for each major element.

Now we need to make a hetr.c file for our new heater element, if using visual studio, you should be able to right click on the source folder inside the project, and create a new file, and name it hetr.c Once you have a blank hetr.c created and it is included in the project, we need to add a few things to this file.

#include <element.h>

int update_HETR(UPDATE_FUNC_ARGS) {

	return 0;
}

Before we go on with the actual code, we need to finish up a few things first so that the code actually knows there is a new update_HETR function. Go back to powder.h at line ~250. You will see lots of int update_'(UPDATE_FUNC_ARGS); . This list is sorted alphabetically so lets put in our new HETR function.

int update_GOO(UPDATE_FUNC_ARGS);
int update_HETR(UPDATE_FUNC_ARGS);
int update_HSWC(UPDATE_FUNC_ARGS);

Remember that function variable in the ptypes array? we need to let it know that HETR has a special function to use, instead of NULL. Replace NULL with &update_HETR.

... "Heats objects it touches", ST_SOLID, TYPE_SOLID, &update_HETR},

Now that our new HETR function will be called properly, we can go back into hetr.c and finish it up. NOTE: Put all code BEFORE the return 0; line so when it finishes running, it will go back to the main code. If you kill the particle from inside the function, please return 1;. Our HETR element will not die, so you don't have to worry about that.

Now we need to go over some useful ways of detecting particles, so that we can heat them.

for(rx=-1; rx<2; rx++)
    for(ry=-1; ry<2; ry++)

This code simply means "any particle touching a heater particle".

It basically sets the grid size in which particles are affected by the effect of the particle. In this case, it is a 3x3 grid around the center one. If you are having trouble getting this, try thinking about rx and ry, as a radius around the current particle.

for(rx=-2; rx<3; rx++)
    for(ry=-1; ry<2; ry++)

This would make the grid affected 5x3

for(rx=-1; rx<2; rx++)
    for(ry=-2; ry<3; ry++)

And this would flip the dimensions.


Add this:

if(x+rx>=0 && y+ry>0 &&    x+rx<XRES && y+ry<YRES &&
               pmap[y+ry][x+rx] &&
               (pmap[y+ry][x+rx]&0xFF)!=PT_HETR&&
               (pmap[y+ry][x+rx]&0xFF)!=0xFF)
       {


Your entire code should look like this so far:

       for(rx=-1; rx<2; rx++)
           for(ry=-1; ry<2; ry++)
1               if(x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES &&
2               pmap[y+ry][x+rx] &&
3               (pmap[y+ry][x+rx]&0xFF)!=PT_HETR&&
4               (pmap[y+ry][x+rx]&0xFF)!=0xFF)
5       {

1st line: If the current grid particle around the HETR pixel is within the screen, AND

2nd line: there is a particle in that point, AND

3rd line: that particle is not a HETR, AND (note: != means not equal)

4th line: that particle is not a wall,

5th line: THEN, do some code


Now add this:

r = pmap[y+ry][x+rx];

It means you can just type 'r' instead of 'pmap[y+ry][x+rx]'. This will simplify code later on. NEW: because our hetr.c file is separate, we need to initialize these variables we are using inside hetr.c, add this as the first part of the update_HETR function.

int r, rx, ry;

Now add

if(parts[r>>8].temp + (parts[r>>8].temp*0.2f)<=MAX_TEMP)

Let's analyze.

1) if ()

IF a is true, THEN b happens

2) parts[r>>8]

The currently selected particle (the one that's not HETR or a wall)

3) .temp

means it's temperature.


In English, the statement reads:

IF the particle's temperature + 20% of it's temperature is less then then the maximum temperature possible, THEN...


This ensures it won't go over the maximum temperature. Now add this:

parts[r>>8].temp += parts[r>>8].temp*0.2f;

In English,

IF the particle's temperature + 20% of it's temperature is less then then the maximum temperature possible, THEN add 20% of the particle's temperature to itself.


Now add:

else {
    parts[r>>8].temp = MAX_TEMP;
}

IF the particle's temperature + 20% of it's temperature is less then then the maximum temperature possible, THEN add 20% of the particle's temperature to itself, ELSE the temperature is the maximum temperature.


Now close the two brackets we used for the if statements to complete the section. The entire hetr.c should now look like this:

#include <element.h>

int update_HETR(UPDATE_FUNC_ARGS) {
   int r, rx, ry;
   for(rx=-1; rx<2; rx++)
       for(ry=-1; ry<2; ry++)
           if(x+rx>=0 && y+ry>0 && x+rx<XRES && y+ry<YRES &&
               pmap[y+ry][x+rx] &&
               (pmap[y+ry][x+rx]&0xFF)!=PT_HETR&&
               (pmap[y+ry][x+rx]&0xFF)!=0xFF)
   {
       r = pmap[y+ry][x+rx];
       if(parts[r>>8].temp+ (parts[r>>8].temp*0.2f)<=MAX_TEMP)
       {
           parts[r>>8].temp += parts[r>>8].temp*0.2f;
       }
       else 
       {
           parts[r>>8].temp = MAX_TEMP;
       }
   }
   return 0;
}

Congrats, your HETR element should now work.






Part Two: Uploading Your Work to GitHub (NOTE: GitHub is NOT necessary to just add elements, it is for getting code into the official)

1) Open SmartGit (make sure you've saved your changes in Visual Studio).

2) powder.c and any other files you may have changed should be listed as "Modified".

3) Press "Commit" at the top, list the things you have changed in the text box, and press "Commit".

4) Press "Push" at the top, and press "Push" again.

5) Go to your Powder Toy repository page on GitHub and press "Pull Request" at the top.

6) Do not send the request to Simon if it is the element created in this tutorial. He will not accept it. If it's a very good and very useful element, send the request to facialturd (Simon's username) and you should be done. Verify that the code has been changed if you like.

7) If Simon decides to accept your request, your code will be in the official Powder Toy source code. Congratulations!


Now you are done, if you have any more questions, type them at the discussion part of this page.

Welcome to coding the powder toy!