Difference between revisions of "Lua API:Elements"

From The Powder Toy
Jump to: navigation, search
(Add Update and Graphics function details)
(98.0: Replace with LBPHacker's version (mostly). Link to existing shared doc pages where needed.)
 
(5 intermediate revisions by 3 users not shown)
Line 1: Line 1:
The Elements API contains methods and constants for modifying and creating elements. If you want to add an update function or graphics function, use Update and Graphics properties. See the properties section for an example.
+
The <code>elements</code> API contains methods and constants for creating and modifying elements.
== Methods ==
 
  
=== elements.allocate ===
+
'''The shorter alias <code>elem</code> is also available.'''
number elements.allocate(string group, string name)
 
Use this function to create a new element. This function will return the id of your element, and create a unique identifier that can be used to modify the properties later. The identifier is in the form GROUP_PT_NAME, where group is the name of the mod or script (or just anything unique, like your username), and name is the name of the element. For example, elements.allocate("mymod", "virus") would create the identifier MYMOD_PT_VIRUS.
 
  
The identifier is added as a constant in the elements table, so elements.MYMOD_PT_VIRUS would be equivalent to the new element's id, and can be used as the elementID argument to any of the functions below.
+
Unless stated otherwise, all functions raise errors if supplied with parameters that disagree with their descriptions.
  
The new element is created with all the default properties, and won't be visible until you modify it to show up in the menu.
+
== Methods ==
 
 
Returns -1 on failure (there are no free spaces to create a new element).
 
  
=== elements.free ===  
+
=== elements.allocate ===
elements.free(number elementID)
 
Free a previously allocated element, so it will disappear from the game. The element id will be freed and can used later by another script. elementID must be a non-default element (i.e you cannot free the default WATR element)
 
  
=== elements.exists ===
+
Create a new element.
elements.exists(number elementID)
 
Returns true if an element under this ID exists and is Enabled, otherwise returns false
 
  
=== elements.loadDefault ===
+
<pre>elemNumber = elements.allocate(group, iname)</pre>
elements.loadDefault()
+
* <code>group</code>: string without underscores (<code>_</code>), the group the element belongs to; gets uppercased by the function
Resets all elements to the original state. This will also erase any elements created with any scripts, only the default elements will be available.
+
* <code>iname</code>: string without underscores (<code>_</code>), the internal name of the element; gets uppercased by the function
 +
* <code>elemNumber</code>: the positive element number allocated for the element created, or <code>-1</code> on error, if there are no free element numbers left
  
elements.loadDefault(number elementID)
+
<code>group</code> should be something unique to your script, and should be the same across the entire script. It is common to use a simpler version of your username or the script’s name, for example if your script is called ''Ultimate Chemistry Pack v3'', you might use <code>&quot;CHEMPACK3&quot;</code> as the group name.
Reset an element to its original state before it was modified
 
  
=== elements.element ===
+
<code>iname</code> should be unique to the element within your script, and should ultimately resemble the [[#elements.property|<code>Name</code> property]] of the element. For example, if your element’s name is ''C-6'' you should use <code>C6</code> as the internal name.
table elements.element(number elementID)
 
Returns a table containing all of an element's properties (Name, Description, etc)
 
  
elements.element(number elementID, table properties)
+
The resulting element identifier must be unique across all scripts in use at any point in time. Elements that seem like built-in elements, i.e. ones in the group <code>DEFAULT</code>, cannot be created. Note that, as stated above, both <code>group</code> and <code>iname</code> get uppercased, so <code>elements.allocate(&quot;CheMpaCk3&quot;, &quot;c6&quot;)</code> is equivalent to <code>elements.allocate(&quot;CHEMPACK3&quot;, &quot;C6&quot;)</code>.
Sets the properties from the given table onto the element.
 
  
These two functions are useful for copying or templating from already present elements, for example
+
Make the choice such that it is convenient to refer to your element via an [[#elements.group_pt_iname|<code>elements.[group]_PT_[iname]</code> constant]]. While it is perfectly possible to type <code>elem[&quot;Ultimate Chemistry Pack v3_PT_C-6&quot;]</code>, it is much more convenient to type <code>elem.CHEMPACK3_PT_C6</code>.
<syntaxhighlight lang="lua">
 
local myNewElement = elements.allocate("wiki", "expl")
 
elements.element(myNewElement, elements.element(elements.DEFAULT_PT_WATR))
 
elements.property(myNewElement, "Name", "EXPL")
 
elements.property(myNewElement, "Description", "This is an example element from the Wiki")
 
</syntaxhighlight>
 
In this example, the element properties for our new element (EXPL) are copied from WATR
 
  
<syntaxhighlight lang="lua">
+
The new element is created with all the default properties, and will not be visible until you modify it to show up in the menu.
local star = elements.allocate("ELEMENT", "STAR")
 
elements.element(star, elements.element(elements.DEFAULT_PT_DMND))
 
elements.property(star, "Name", "STAR")
 
elements.property(star, "Description", "STAR. Enough Pressure Makes It Explode Into LAVA.")
 
elements.property(star, "Colour", 0xFFFFFF)
 
elements.property(star, "MenuSection", elem.SC_SOLIDS)
 
elements.property(star, "HotAir", -0.009)
 
elements.property(star, "Weight", 333)
 
elements.property(star, "Temperature", 4556)
 
elements.property(star, "HighPressure", 200)
 
elements.property(star, "HighPressureTransition", elements.DEFAULT_PT_LAVA)
 
local function graphics1(i, colr, colg, colb)
 
    return 1,ren.FIRE_ADD,255,100,155,210,255,255,255,255
 
end
 
elements.property(star, "Graphics", graphics1)
 
</syntaxhighlight>
 
Another Example, from an actual script. For more info on graphics functions, see the legacy api page
 
  
 
=== elements.property ===
 
=== elements.property ===
object elements.property(number elementID, string property)
 
Gets the value of an element property
 
  
elements.property(number elementID, string property, object value)
+
Query or update a property of an element.
Sets the value of an element property
+
 
 +
<pre>propValue = elements.property(elemNumber, propName) -- query variant
 +
elements.property(elemNumber, propName, propValue) -- update variant
 +
elements.property(elemNumber, &quot;Update&quot;, propValue, [runWhen]) -- special update variant for the Update property</pre>
 +
* <code>elemNumber</code>: number of the element whose property is to be queried or updated
 +
* <code>propName</code>: string, name of the property to be queried or updated
 +
* <code>propValue</code>: various types, value of the property to be queried or updated
 +
* <code>runWhen</code>: number, specifies when the update function should be run, one of:
 +
** <code>elements.UPDATE_AFTER</code>: run before the built-in update function, this is the default
 +
** <code>elements.UPDATE_REPLACE</code>: run instead of the built-in update function
 +
** <code>elements.UPDATE_BEFORE</code>: run after the built-in update function
  
== Properties ==
+
For more information on what properties there are to use in elements.property, and how to use them, see this page: [[Element_Properties]].
After creating an element, you can modify many properties. Be sure to at a minimum set set Name, Description, Color, MenuVisible, and MenuSection.  
 
  
For more information on what properties there are to use in elements.property, and how to use them, see this page: [[Element_Properties]]
+
When working with the "MenuSection" or the "Properties" property, use one of the provided [[#Constants|constants]].
  
"Update" and "Graphics" are special properties, these can be used to set the update functions or graphics functions. Use a function as the value of the property to set. They are not included in the tables created with elements.element, and the functions can't be returned with elements.property either. This means copying all of an elements properties using elements.element will not set these two for the new element.
+
The "Identifier" property is read-only and cannot be set.
  
=== Update ===
+
Several event callback functions are implemented, such as "Update" and "Graphics". To set these, use a function for <code>propValue</code>. They are not included in the tables created with elements.element, and the functions can't be returned with elements.property either. This means copying all of an elements properties using elements.element will not set event callbacks for the new element. For help on creating these, see [[Element_Properties#Callback_functions]].
  
Allows you to replace or add on to an element's update function. Write a function like normal, and then put its name into this command. Use your element's ID or elem.*_PT_* constants for el_number. If replace is set to 1, the new function will be called after the original update function. If replace is set to 2, the original function will be overwritten. If replace is set to 3, the new function will be called before the original update function. Replace automatically defaults to 1.
+
=== elements.element ===
  
newfunction arguments: index, x, y, surround_space, nt
+
Query all or update multiple properties of an element.
  
Returns: return 1 from your function if the particle is killed.
+
<pre>elemProps = elements.element(elemNumber) -- query variant
 +
elements.element(elemNumber, elemProps) -- update variant</pre>
 +
* <code>elemNumber</code>: number of the element whose properties are to be queried or update
 +
* <code>elemProps</code>: table that maps property names to property values
  
<code>elements.property(number el_number, "Update", function newfunction)
+
The keys and values of <code>elemProps</code> are the same as the <code>propName</code> and <code>propValue</code> parameters of [[#elements.property|elements.property]]. The query variant returns all properties of the element in <code>elemProps</code> with the same caveats as [[#elements.property|elements.property]]. The update variant accepts any subset of properties, only updates the ones present in the table, applying the same checks as [[#elements.property|elements.property]].
  
elements.property(number el_number, "Update", function newfunction, number replace)</code>
+
This function is commonly used to base an element off another element by first copying all properties of the source element and applying them to the new element, and then customizing the new element a bit afterwards:
  
=== Graphics ===
+
<pre>local purpleGold = elem.allocate(&quot;EXAMPLE&quot;, &quot;PGLD&quot;)
 +
assert(purpleGold ~= -1, &quot;ran out of element numbers&quot;)
 +
elem.element(purpleGold, elem.element(elem.DEFAULT_PT_GOLD))
 +
elem.property(purpleGold, &quot;Name&quot;, &quot;PGLD&quot;)
 +
elem.property(purpleGold, &quot;Color&quot;, 0x8040FF)</pre>
  
Allows you to replace an element's graphics function. Write a function like normal, and then put its name into this command. Use your element's ID or elem.*_PT_* constants for el_number.
+
=== elements.exists ===
  
Function arguments: index, colr, colg, colb
+
Check whether a number is a real element number and refers to an element.
  
Returns: cache, pixel_mode, cola, colr, colg, colb, firea, firer, fireg, and fireb.
+
<pre>exists = elements.exists(elemNumber)</pre>
 +
* <code>elemNumber</code>: number of the element to be checked
 +
* <code>exists</code>: boolean, <code>true</code> if <code>elemNumber</code> refers to an element
  
Set cache to 1 if you don't want the function to ever be called again, preventing lag. Don't do this if you need the way your element looks to change depending on its properties.
+
If an element exists, there exists a corresponding [[#elements.group_pt_iname|<code>elements.[group]_PT_[iname]</code> constant]], and conversely, if there exists such a constant, there exists a corresponding element.
  
colr/g/b are the red, green, and blue colors of your element. firea/r/g/b set the fire colors, but pixel_mode needs to be set to 0x00022000 for them to work.
+
=== elements.free ===
  
<code>elements.property(number el_number, "Graphics", function newfunction)</code>
+
Free a previously allocated element.
  
The pixel mode values you can use are:
+
<pre>elements.free(elemNumber)</pre>
<pre>
+
* <code>elemNumber</code>: number of the element to be freed
PMODE_NONE 0x00000000 --prevents anything from being drawn
 
PMODE_FLAT 0x00000001 --draw a basic pixel, overwriting the color under it. Doesn't support cola.
 
PMODE_BLOB 0x00000002 --adds a blobby effect, like you were using blob (5) display mode
 
PMODE_BLUR 0x00000004 --used in liquids in fancy display mode
 
PMODE_GLOW 0x00000008 --Glow effect, used in elements like DEUT and TRON in fancy display mode
 
PMODE_SPARK 0x00000010 -- used for things such as GBMB at first, dimmer than other modes
 
PMODE_FLARE 0x00000020 --BOMB and other similar elements, brighter than PMODE_SPARK
 
PMODE_LFLARE 0x00000040 --brightest spark mode, used when DEST hits something
 
PMODE_ADD 0x00000080 --like PMODE_FLAT, but adds color to a pixel, instead of overwriting it.
 
PMODE_BLEND 0x00000100 --basically the same thing as PMODE_ADD, but has better OpenGL support
 
PSPEC_STICKMAN 0x00000200 --does nothing, because the stickmen won't get drawn unless it actually is one
 
  
NO_DECO 0x00001000 --prevents decoration from showing on the element (used in LCRY)
+
The element number is freed and can used later by another script. Built-in elements, i.e. elements in the group <code>DEFAULT</code>, cannot be freed.
DECO_FIRE 0x00002000 --Allow decoration to be drawn on using the fire effect (gasses have this set)
 
  
FIRE_ADD 0x00010000 --adds a weak fire effect around the element (ex. LAVA/LIGH)
+
<span id="elements.getbyname"></span>
FIRE_BLEND 0x00020000 --adds a stronger fire effect around the element, default for gasses
+
=== elements.getByName ===
  
EFFECT_GRAVIN 0x01000000 --adds a PRTI effect. Might take some coding in an update function to get it to work properly, PRTI uses life and ctype to create the effects
+
Find an element by name, the [[#elements.property|<code>Name</code> property]].
EFFECT_GRAVOUT 0x02000000 --adds a PRTO effect. Might take some coding in an update function to get it to work properly, PRTI uses life and ctype to create the effects
 
  
</pre>
+
<pre>elementNumber = elements.getByName(name)</pre>
 +
* <code>name</code>: string, the name to find the element by
 +
* <code>elemNumber</code>: positive number of the element <code>name</code> refers to, or <code>-1</code> on error if no such element exists
  
You can combine them in any way you want, you probably need more than one anyway. Radioactive elements default to PMODE_FLAT+PMODE_GLOW, liquids to PMODE_FLAT+PMODE_BLUR, and gasses to FIRE_BLEND+DECO_FIRE, with a firea of 125 and firer/g/b of colr/g/b divided by 2
+
This function converts a human-friendly element name to an element number, essentially the same way the PROP tool or the console works.
  
See this for a picture of what they look like: https://powdertoy.co.uk/Wiki/W/File:Particle_Drawing_Modes.png.html
+
<span id="elements.loaddefault"></span>
 +
=== elements.loadDefault ===
  
=== Some examples ===
+
Restore the set of elements to its initial state at startup.
  
<syntaxhighlight lang="lua">
+
<pre>elements.loadDefault()</pre>
local function funcUpdate(i,x,y,s,nt)
+
This frees all elements created and resets all properties of all built-in elements to their defaults.
    for r in sim.neighbors(x,y,1,1) do
 
        if sim.partProperty(r, "type") == elem.DEFAULT_PT_COAL then
 
            sim.partChangeType(r, elem.DEFAULT_PT_GOLD)
 
        end
 
    end
 
end
 
 
local function funcGraphics(i, colr, colg, colb)
 
    return 1,ren.FIRE_ADD,255,colr,colg,colb,255,100,0,255
 
end
 
 
elements.property(ELEM, "Update", funcUpdate)
 
elements.property(ELEM, "Graphics", funcGraphics)
 
</syntaxhighlight>
 
  
 
== Constants ==
 
== Constants ==
Any of these constants can be accessed with elements.<constant name here>
 
  
=== Element identifiers ===
+
<span id="elements.group_pt_iname"></span>
All of the default element identifiers are prefixed with <code>DEFAULT_PT_</code>, for example, the identifier for WATR is <code>DEFAULT_PT_WATR</code>. Do not assume all elements identifiers are the same as their names, TNT has the identifier BANG, for example. To find an elements identifier, you can check the source file for any given element in <tt>src/simulation/elements/</tt>.
+
=== elements.[group]_PT_[iname] ===
 +
 
 +
<pre>watrNumber = elements.DEFAULT_PT_WATR</pre>
 +
There are two ways to refer to elements: element numbers and element identifiers, which are strings of the form <code>&quot;[group]_PT_[iname]&quot;</code>. Both <code>[group]</code> and <code>[iname]</code> can be any string without underscores (<code>_</code>). All built-in elements are in group <code>DEFAULT</code>. For example, the identifier of WATR is <code>&quot;DEFAULT_PT_WATR&quot;</code>, because WATR belongs to the group <code>DEFAULT</code> and its internal name is <code>WATR</code>. This is different from the [[#elements.property|<code>Name</code> property]].
 +
 
 +
These constants map element identifiers to element numbers. The element number of WATR is <code>2</code>, so <code>elements.DEFAULT_PT_WATR</code> is also <code>2</code>.
  
 
=== Properties ===
 
=== Properties ===
More info on the properties can be found here: [[Element_Properties]]
+
 
 +
These constants should be used when setting "Properties". More info on the properties can be found here: [[Element_Properties#.Property_Constants]]
 +
 
 
; TYPE_PART
 
; TYPE_PART
 
; TYPE_LIQUID
 
; TYPE_LIQUID
Line 181: Line 143:
  
 
=== Menu sections ===
 
=== Menu sections ===
These are used for the menusection property
+
These are used for the MenuSection property
 +
 
 
; SC_WALL
 
; SC_WALL
 
; SC_ELEC
 
; SC_ELEC
Line 197: Line 160:
 
; SC_TOOL
 
; SC_TOOL
 
; SC_DECO
 
; SC_DECO
SC_CRACKER and SC_CRACKER2 are not accessible from lua or in the game, but have id numbers of 15 and 16
 
 
=== Flags ===
 
set in parts[i].flags
 
; FLAG_STAGNANT
 
: Used by liquids and powders to speed up simulation by moving them less
 
; FLAG_SKIPMOVE
 
: Given to PHOT by PCLN and PBCN to fix gaps in lasers, only useable by energy particles
 
; FLAG_WATEREQUAL
 
: Used internally for water equalization
 
; FLAG_MOVABLE
 
: Can be used to re-enable moving sponge
 
; FLAG_PHOTDECO
 
: Re-enables deco on photons for compatibility. Defined as the same value as FLAG_MOVABLE (they only apply to different elements)
 
 
[[Category:Lua]]
 

Latest revision as of 03:59, 13 March 2024

The elements API contains methods and constants for creating and modifying elements.

The shorter alias elem is also available.

Unless stated otherwise, all functions raise errors if supplied with parameters that disagree with their descriptions.

Methods

elements.allocate

Create a new element.

elemNumber = elements.allocate(group, iname)
  • group: string without underscores (_), the group the element belongs to; gets uppercased by the function
  • iname: string without underscores (_), the internal name of the element; gets uppercased by the function
  • elemNumber: the positive element number allocated for the element created, or -1 on error, if there are no free element numbers left

group should be something unique to your script, and should be the same across the entire script. It is common to use a simpler version of your username or the script’s name, for example if your script is called Ultimate Chemistry Pack v3, you might use "CHEMPACK3" as the group name.

iname should be unique to the element within your script, and should ultimately resemble the Name property of the element. For example, if your element’s name is C-6 you should use C6 as the internal name.

The resulting element identifier must be unique across all scripts in use at any point in time. Elements that seem like built-in elements, i.e. ones in the group DEFAULT, cannot be created. Note that, as stated above, both group and iname get uppercased, so elements.allocate("CheMpaCk3", "c6") is equivalent to elements.allocate("CHEMPACK3", "C6").

Make the choice such that it is convenient to refer to your element via an elements.[group]_PT_[iname] constant. While it is perfectly possible to type elem["Ultimate Chemistry Pack v3_PT_C-6"], it is much more convenient to type elem.CHEMPACK3_PT_C6.

The new element is created with all the default properties, and will not be visible until you modify it to show up in the menu.

elements.property

Query or update a property of an element.

propValue = elements.property(elemNumber, propName) -- query variant
elements.property(elemNumber, propName, propValue) -- update variant
elements.property(elemNumber, "Update", propValue, [runWhen]) -- special update variant for the Update property
  • elemNumber: number of the element whose property is to be queried or updated
  • propName: string, name of the property to be queried or updated
  • propValue: various types, value of the property to be queried or updated
  • runWhen: number, specifies when the update function should be run, one of:
    • elements.UPDATE_AFTER: run before the built-in update function, this is the default
    • elements.UPDATE_REPLACE: run instead of the built-in update function
    • elements.UPDATE_BEFORE: run after the built-in update function

For more information on what properties there are to use in elements.property, and how to use them, see this page: Element_Properties.

When working with the "MenuSection" or the "Properties" property, use one of the provided constants.

The "Identifier" property is read-only and cannot be set.

Several event callback functions are implemented, such as "Update" and "Graphics". To set these, use a function for propValue. They are not included in the tables created with elements.element, and the functions can't be returned with elements.property either. This means copying all of an elements properties using elements.element will not set event callbacks for the new element. For help on creating these, see Element_Properties#Callback_functions.

elements.element

Query all or update multiple properties of an element.

elemProps = elements.element(elemNumber) -- query variant
elements.element(elemNumber, elemProps) -- update variant
  • elemNumber: number of the element whose properties are to be queried or update
  • elemProps: table that maps property names to property values

The keys and values of elemProps are the same as the propName and propValue parameters of elements.property. The query variant returns all properties of the element in elemProps with the same caveats as elements.property. The update variant accepts any subset of properties, only updates the ones present in the table, applying the same checks as elements.property.

This function is commonly used to base an element off another element by first copying all properties of the source element and applying them to the new element, and then customizing the new element a bit afterwards:

local purpleGold = elem.allocate("EXAMPLE", "PGLD")
assert(purpleGold ~= -1, "ran out of element numbers")
elem.element(purpleGold, elem.element(elem.DEFAULT_PT_GOLD))
elem.property(purpleGold, "Name", "PGLD")
elem.property(purpleGold, "Color", 0x8040FF)

elements.exists

Check whether a number is a real element number and refers to an element.

exists = elements.exists(elemNumber)
  • elemNumber: number of the element to be checked
  • exists: boolean, true if elemNumber refers to an element

If an element exists, there exists a corresponding elements.[group]_PT_[iname] constant, and conversely, if there exists such a constant, there exists a corresponding element.

elements.free

Free a previously allocated element.

elements.free(elemNumber)
  • elemNumber: number of the element to be freed

The element number is freed and can used later by another script. Built-in elements, i.e. elements in the group DEFAULT, cannot be freed.

elements.getByName

Find an element by name, the Name property.

elementNumber = elements.getByName(name)
  • name: string, the name to find the element by
  • elemNumber: positive number of the element name refers to, or -1 on error if no such element exists

This function converts a human-friendly element name to an element number, essentially the same way the PROP tool or the console works.

elements.loadDefault

Restore the set of elements to its initial state at startup.

elements.loadDefault()

This frees all elements created and resets all properties of all built-in elements to their defaults.

Constants

elements.[group]_PT_[iname]

watrNumber = elements.DEFAULT_PT_WATR

There are two ways to refer to elements: element numbers and element identifiers, which are strings of the form "[group]_PT_[iname]". Both [group] and [iname] can be any string without underscores (_). All built-in elements are in group DEFAULT. For example, the identifier of WATR is "DEFAULT_PT_WATR", because WATR belongs to the group DEFAULT and its internal name is WATR. This is different from the Name property.

These constants map element identifiers to element numbers. The element number of WATR is 2, so elements.DEFAULT_PT_WATR is also 2.

Properties

These constants should be used when setting "Properties". More info on the properties can be found here: Element_Properties#.Property_Constants

TYPE_PART
TYPE_LIQUID
TYPE_GAS
TYPE_SOLID
TYPE_ENERGY
PROP_CONDUCTS
PROP_BLACK
PROP_NEUTPENETRATE
PROP_NEUTABSORB
PROP_NEUTPASS
PROP_DEADLY
PROP_HOT_GLOW
PROP_LIFE
PROP_RADIOACTIVE
PROP_LIFE_DEC
PROP_LIFE_KILL
PROP_LIFE_KILL_DEC
PROP_SPARKSETTLE
PROP_NOAMBHEAT
PROP_DRAWONCTYPE
PROP_NOCTYPEDRAW

Menu sections

These are used for the MenuSection property

SC_WALL
SC_ELEC
SC_POWERED
SC_SENSOR
SC_FORCE
SC_EXPLOSIVE
SC_GAS
SC_LIQUID
SC_POWDERS
SC_SOLIDS
SC_NUCLEAR
SC_SPECIAL
SC_LIFE
SC_TOOL
SC_DECO