Commands
Each line of your script is considered a “Command”. EasyCommands supports a variety of different commands to accomplish tasks. Some Commands manage the program, such as creating Variables, creating Threads, or starting/stopping/pausing/restarting the program. Other commands can interact with Blocks, listen for or send commands on other grids, transfer items between inventories, check conditions, perform aggregations, and more. Below is a list of all command types supported by EasyCommands.
Comments
Any line beginning with “#” will be treated as a comment and ignored by the script. Feel free to add as many as you like!
#This is a comment!
print "I'm not a comment!"
Nested Commands
Nested commands are a way of executing multiple commands, sequentially, as a unit. This allows you to execute a series of commands when a condition is met (or not met).
if the lights are on
Print "The Lights are on!"
Print "You're using power!"
Conversely, in the following example “You’re using power!” would always be printed but “The Lights are on!” would only be printed if the lights are on:
if the lights are on
Print "The Lights are on!"
Print "You're using power!"
There’s no requirement that you use 1 vs 2 vs 4 spaces for nesting commands together, but you’ll need to use consistent spacing for a set of nested commands to be interpreted correctly.
Nesting Spaces
You can further nest spaces if you want to use nested commands inside of another nested command. Just make sure that all the items intended to be part of one nested command have the same spacing
if "outer door" is open
turn on the "outer lights"
if the "inner door is open"
turn on the "inner lights"
turn on the generator
else
close the "outer door"
wait 1
wait 3
Print Command
The simplest thing you can do with EasyCommands is output text to the Programmable Block. This text will be visible when viewing the Programmable Block from the terminal menu.
Print "Hello World!"
You can print the result any Variable as well:
#The following would print "My Variable is: Hello World!"
set myVariable to "Hello World!"
Print "My Variable is: " + myVariable
The output from each Print command will appear on a new line in EasyCommands. The below script would yield 2 lines of output:
Print "Hello World!"
Print "How are you?"
Print statements are erased every tick. If you want to continually see a message on the Programmable Block screen you will need to make sure you are printing the message every tick.
The following would not end up showing anything useful since “Hello World!” would only show up for 1 tick and then be gone.
print "Hello World!"
wait until false
The below command would persist “Hello World!” on the screen forever:
until false
print "Hello World!"
Print statements are organized by Thread. By default, EasyCommands prints a few pieces of useful information in addition to things you specifically ask to print to the console. To change EasyCommands to only print what you want printed to the screen, edit the EasyCommands program (not your script, the actual program using “Edit”) and change logLevel to LogLevel.SCRIPT_ONLY:
public LogLevel logLevel = LogLevel.SCRIPT_ONLY;
Wait Command
Sometimes you just have to wait. The Wait command takes an optional variable (in seconds) for how long to take. You can also specify “ticks” instead of seconds if you want to wait an amount of ticks instead.
#Waits 1 tick
wait
#Waits 10 seconds
wait 10
#Waits 10 seconds
wait 10 seconds
#Waits 10 ticks, where 1 tick = 1/60 seconds (regardless of Update Frequency)
wait 10 ticks
#Waits until myCondition (could be a variable or an expression) is true
wait until myCondition is true
The Wait command waits for the requested amount of time regardless of the Update frequency. That said, the amount of time waited will become less accurate the lower the update frequency you set, so keep this in mind.
Also note that the Wait Command’s wait duration is not perfectly accurate. Don’t use it for trying to keep track of accurate amounts of time over long durations.
Interacting With Blocks
Probably the most important command type, and most often used, in EasyCommands is the Block Command. This command enables you to act on a given block, group of blocks, or all blocks of a given type. There are lots of variations of BlockCommand but the general form is to set or change the property of a block to a new value.
turn on the "Exterior Lights"
recharge the batteries
set the "Elevator Piston" velocity to 2
open the "Outer Hangar" doors
BlockCommands typically consist of an action to take, a Selector for which blocks to act on, the property for those blocks (see BlockHandlers), and a value to set the property to. Specifically:
<set/increase/decrease> the <selector> <property> to <value>
. Most BlockHandlers have default properties for value types. Default values (usually “true”) are also applied if you specify a property but not a value. So turn on the "Exterior Lights"
represents <set> the "Exterior Lights" <property?> on
The default boolean property for the “Light” block type is POWER, which for a Light enables the lights. Thus,
turn on the "Exterior Lights"
will set the “Exterior Lights” blocks to enabled. The following commands would also accomplish the same:
#All of these commands are equivalent
turn on the "Exterior Lights"
turn the "Exterior Lights" on
turn on the power to the "Exterior Lights"
"Exterior Lights" on
"Exterior Lights" power on
"Exterior Lights" true
power the "Exterior Lights"
power on the "Exterior Lights"
Again, check out BlockHandlers for a list of all available properties for each supported block type, what each property does, the default property/values, and the keywords for specifying selectors for those block types.
Incrementing Values
You can increase or decrease a block’s property value using increase/decrease
, such as increase the "Exterior Lights" range by 10
. The “by 10” is optional; EasyCommands will attempt to increase/decrease the property by a reasonable delta (usually 10-20% of the properties range).
Increasing a String property value appends the given string to the existing value. See Operations for a better description of what will happen whenever you add B to an A value type, as this is what will happen when increasing a block properties value.
Moving/Reversing Properties
You can also move some properties, which is handy for things like Rotors & Pistons:
#Rotors
turn the "test rotor" clockwise
turn the "test rotor" counterclockwise
reverse the "test rotor"
#Pistons
reverse the "test piston"
extend the "test piston"
retract the "test piston"
Creating a Variable
There are plenty of use cases where you need to store the result of something so that you can use it later. EasyCommands allows you to store Variables which can be looked up later using the following syntax:
#Either of these will work
set myVariable to "Some Value"
assign myVariable to "Some Value"
allocate myVarible to "Some Value"
designate myVariable to "Some Value"
Once a variable is created you can reference it later using it’s name:
set myValue to 2
print "My Value is: " + myValue
set myValue to myValue + 1
print "My Value is: " + myValue
# Results of above would be:
# My Value is: 2
# My Value is: 3
A variable can be set to any Primitive or the result of any expression, such as:
set myValue to 2
set myValue to otherValue + 3
set myValue to the count of my batteries
set myValue to (count of my batteries > 3)
The “global” Keyword
By default, any variable you create will be tied to the current Thread. This means that any other threads that are running won’t have access to this variable (except in some situations discussed in Threads). In order to share a variable across all threads you need to specify the global keyword when setting the variable.
set global myValue to 2
set global myValue to otherValue + 3
set global myValue to the count of my batteries
set global myValue to (count of my batteries > 3)
Binding a Variable
Typically when you set a variable, the value of that variable stays constant until you change it. So by default, the following script would generate the expected result below:
#set a to 1
set a to 1
#set b to a + 1 = (1 + 1) = 2
set b to a + 1
#set a to 2
set a to 2
print "a is: " + a
print "b is: " + b
#The Result would be:
#a is: 2
#b is: 2
However, you might find that in some instances you want to store a calculation, rather than a value at that particular time. You can do this using the “bind” keyword:
#set a to 1
set a to 1
#bind b to a + 1 (reference)
bind b to a + 1
#set a to 2
set a to 2
print "a is: " + a
print "b is: " + b
#The Result would be:
#a is: 2
#b is: 3
Incrementing a Variable
Once you have variables, you sometimes want to increment or decrement them (say, you’re iterating through a list of things and want to do an action on each). To increase a variable, use the following syntax
set i to 5
set myVariable to 0
#Increases the "myVariable" variable by 10
increase myVariable by 10
myVariable+=10
#Decrease the "myVariable" by the "i" variable amount
decrease myVariable by i
myVariable-=i
#Increase "myVariable" by 1
increase myVariable
myVariable++
myVariable+=1
#Decrease "myVariable" by 1
decrease myVariable
myVariable--
myVariable-=1
#Add "World" to myVariable
myVariable+="World"
Conditional Commands
Sometimes you might only want to run a command if or when a condition is met. For example, maybe I want to wait for the airlock to be pressurized before I open the interior doors. To do this, I could do the following:
if the "Airlock Vent" is pressurized
open the "Airlock Interior Door"
In general, the form of a Conditional Command is <if/until/when/while/unless> <condition> <command> <else/otherwise> <command>
A
if (outofAmmo)
if (a > b)
if (all of the lights are on)
if (the average "Elevator Piston" height > 5)
See the sections on Variables and Operations for a complete list of the things you can use to create a condition.
You can also specify
wait until the "Exterior Doors" are closed
Print "Not High Enough" until the average "Elevator Piston" height > 5
Here’s are all the types of conditional commands and how you might use them:
If Command
As the name suggests, this command checks for a condition and if the condition is true it begins running the requested command(s). If the condition is not met, the script proceeds to the next command outside the if statement
if the "Airlock Vent" is pressurized
open the "Interior Door"
set the "Base Screens" text to "Welcome Home!"
wait 10 seconds
print "This command would run immediately if the condition isn't met."
If/Else
If you want something else to happen when the command is not met, you can use the “else” or “otherwise” keywords.
if the "Airlock Vent" is pressurized
open the "Interior Door"
set the "Base Screens" text to "Welcome Home!"
otherwise
pressurize the "Airlock Vent"
set the "Airlock Screens" to "Please wait for airlock to pressurize"
You can chain conditional commands together in case there are more than two conditions to check:
set ammoRemaining to the "Gatling Turrets" inventory "ammo" amount
if ammoRemaining > 2
print "Locked and loaded!"
else if ammoRemaining > 0
print "Getting low on Ammo!"
else
print "No More Ammo!"
You can also perform conditional checks inside of conditional checks. Just make sure to keep your spaces consistent. The script will line up commands and if/else statements based on the spaces
if a > 5
if a > b
print "A is greater than b and more than 5"
else
print "A is not greater than b but more than 5"
else
print "A is not greater than 5
Unless
The “unless” keyword is similar to if but the command only executes if the condition is false.
#Turn off the lights when no one is in the base
unless the "Base Occupied" sensor is triggered
print "Turning off the lights to save power"
turn off the "Base Lights"
While
The “while” keyword is similar to “if”, however the given command(s) will continue to be executed until the condition is false. This allows you to effectively do something until a condition is met and then move on to do something else. For example:
while the "Airlock Vent" is not pressurized
turn off the "Warning Lights"
wait 1 second
turn on the "Warning Lights"
wait 1 second
Note that in the above command we are waiting for seconds as part of the requested command. If a condition is met for any conditional command, the entire command will finish executing before the condition is checked again. So the above script would always finish turning the “Warning Lights” off and on again before checking the condition again. In addition, the condition is not being checked while the command is running. So if the “Airlock Vent” happened to get pressurized, and then de-pressurized again, while the lights were blinking the while loop would not stop executing.
Until
The “until” keyword is the “while” equivalent for unless. It will execute the requested command if the given condition is false, and will continue exeucting the command until the condition is true.
#Keep trying to pressurize the Airlock Vent until it is pressurized!
until the "Airlock Vent" is pressurized
pressurize the "Airlock Vent"
Print "We did it! Airlock Vent is pressurized"
When
The “when” keyword is a cool conditional check. This conditional command will check for the given condition and wait until the condition is met. Once the condition is met, it will execute the given command once.
pressurize the "Airlock Vent"
when the "Airlock Vent" is pressurized
open the "Interior Airlock Door"
Print "Welcome Home!"
You can use the else/otherwise command with when to perform a given command until the condition is met. So putting this all together, you could write the Airlock Script we’ve been playing with previously as follows:
when the "Come Inside" sensor is triggered
close the "Exterior Doors"
wait until the "Exterior Doors" are closed
pressurize the "Airlock Vent"
when the "Airlock Vent" is pressurized
open the "Interior Doors"
Print "Welcome Home!"
otherwise
turn off the "Warning Lights"
wait 1 second
turn on the "Warning Lights"
wait 1 second
otherwise
Print "Waiting for a request to come inside..."
Breaking While/Until/When Command
You can break out of a While/Until/When Conditional Command using the break
keywords. This will immediately halt execution of the While/Until/When and proceed to the next command after the conditional command.
set myList to []
set i to 0
until i > 5
i++
if i % 4 is 0
break
increase myList by i
print "My List: " + myList
#My List: [1,2,3]
Breaking from a While/Until/When command only breaks out of the immediate While/Until/When command. If you perform a While/Until/When inside a While/Until/When, and break from the inner while/until/when, it will continue iterating through the outer While/Until/When.
set j to 0
until i > 3
set i to 0
until j > 3
if i = j
break
print "Continuing..."
Continuing a While/Until/When Command
Sometimes you might want to keep running a conditional command but restart the current command execution. You can do this with the continue
keywords.
set myList to []
set i to 0
until i > 5
i++
if i % 4 is 0
continue
increase myList by i
print "My List: " + myList
#My List: [1,2,3,5]
Continue acts similarly to break in that it only affects the immediate While/Until/When command.
For Each Command
When dealing with Collections, you may find that you need to iterate over them and perform a command on each one.
You can do this a few ways in EasyCommands. The simplest is to use the For Each Command.
set myList to ["one", "two", "three"]
for each item in myList
set my display text to "My item is: " + item
wait 1
# The results would be:
# "My Item is: 1"
# "My Item is: 2"
# "My Item is: 3"
This command will iterate over the given list of items. For each item, it will assign a variable to that item, and then execute the given sub command. The name of the variable is given by as part of the command. In the above command it is “item”. Be careful of using reserved keywords.
Note that when iterating, only 1 iteration is done per tick. So if you do a “Print” statement, don’t expect to get the whole list printed. Remember, Print statements are reset every tick. If you want all the items in a list, you’ll need to store a variable that adds them all together, or just print the whole list in one go: print myList
Using Indexes for iteration
Sometimes you might want to use the index of an item, rather than the item itself, as the thing to iterate over. Maybe you want to use the number for some additional purpose. You can still use a for each command to do this, though perhaps in a little round about way.
set myList to ["one", "two", "three"]
for each i in 0..count of myList[]
set my display text to "My item is: " + myList[i]
wait 1
There’s a few things going on here. First, we are using “count of myList[] - 1” to get the length of the collection. The “- 1” is because list indexes are “0” based (0,1,2) vs “1” based (1,2,3). Second, we are using the Range Operation to generate a list of numbers from 0 to count of myList[] - 1, inclusive. And lastly, we are using the iteration variable “i” to do a lookup of the list by numeric index, which returns the value of the list at that index.
Iterating a list in reverse
Iterating a list by item directly will always iterate from front to back. To iterate from back to front you will need to use index based list lookup. Thankfully, you can do this pretty easily.
set myList to ["one", "two", "three"]
for each i in count of myList[] - 1 .. 0
set my display text to "My item is: " + myList[i]
wait 1
Above we are also creating a collection of indexes to iterate over, but in reverse.
Alternative using until and incrementing variables
If, for some reason, you want to iterate a list in non-consecutive order, or there’s some other thing you want to do before iterating further, or you want to remove items from the list as you process them, then you’ll want to use an until or while conditional command instead of iteration. The following program removes all even values from the given list and then prints the remaining values on the screen.
set myList to [1, 2, 3, 4, 5]
set i to 0
while i < count of myList[]
if myList[i] % 2 is 0
#This command removes the value from myList at the ith index
set myList to myList - i
else i++
#Now print the remaining items
for each i in 0..count of myList[] - 1
set my display text to "myItem[" + i + "] = " + myList[i]
wait 1
Breaking a For Each Command
You can break out of the a For Each command using the break
keywords. This will immediately halt execution of the for each and proceed to the next command after the for each command.
set myList to []
for each i in 1..5
if i % 4 is 0
break
increase myList by i
print "My List: " + myList
#My List: [1,2,3]
Breaking from a for each command only breaks out of the immediate For Each command. If you perform a for each inside a for each, and break from the inner for each, it will continue iterating through the outer for each.
for each i in 0..3
for each j in 0..3
if i = j
break
print "Continuing..."
Continuing a For Each Command
Sometimes you might want to keep iterating over a for each, but “skip” the current item. You can do this with the continue
keywords.
set myList to []
for each i in 1..5
if i % 4 is 0
continue
increase myList by i
print "My List: " + myList
#My List: [1,2,3,5]
Continue acts similarly to break in that it only affects the immediate For Each command.
Function Command
Functions allow you to create re-usable and invokable pieces of script which can be called from within your script, and from external sources (see Invoking Your Script).
There are two ways to invoke a function in your script. The first is to simply call it using its name.
#Call myFunction
myFunction
:myFunction
print "You called me!"
Function names are case sensitive so make sure you match the case of your requested function. Optionally you can specify “call” to better indicate that you are calling a function: call myFunction
When you call a function, the script will wait until the function is completely finished before executing the next statement.
Function Parameters
You can also pass parameters to your functions. Parameters get assigned to local thread variables before the function executes, so be careful about your parameter names as these will override any other variables of the same name. See Functions for more information.
printSomething "Hello World!"
:printSomething something
print "Message: " + something
Goto Function
By default, a function call is a “sub-routine” which continues executing the calling function after completing the subroutine. The “goto” keywords enable you to immediately switch program execution without returning to the calling function. This effectively enables switching behavior, as well as short circuiting:
set outOfAmmo to true
attack
:attack
if outOfAmmo
goto flee
#This command will not be executed
print "Fire at will!"
:flee
print "Run away!"
If you load the above script into a new EasyCommands instance, you’ll notice “[Main] flee” in the list of running commands. This is because EasyCommands has completely switched execution to the flee program.
Queueing & Async Commands
EasyCommands supports a concept for Threads, which allows you to execute multiple programs in parallel, or to queue up commands to run after the initial command is finished executing.
The general structure of this command is <queue/async> <command>
Queuing Command
You can “queue” commands to run after we’ve finished running the current script. You might use this if you create a factory that builds different types of vehicles, and you want to queue up 2 of one type and 1 of another.
:myFactory
queue makeRover
queue makeShip
queue makeRover
queue idle
:makeRover
set my display to "Building a Rover"
wait 2
:makeShip
set my display to "Building a Ship"
wait 2
:idle
set my display text to "Idle..."
Async Command
An asynchronous command is a command that you spin off to run on its own Thread. Asynchronous commands run in the same tick as the main script, so effectively they run in parallel. Async commands allow a single program to run multiple scripts simultaneously so that 1 EasyCommands script can manage multiple things. This is useful and powerful, but be careful about trying to run too much in 1 script! Async commands will automatically end once they complete their task.
See Threads for more information on how parameters are passed from the caller to the asynchronous threads and other information.
:setup
#Calling functions asynchronously
async runTask "Task1"
async runTask "Task2"
#Async command without needing a function
async
set my display text to "Im running a background task!"
wait 5
set my display text to "Now Im done!"
exit
goto main
:main
Print "Main function..."
repeat
:runTask taskName
Print "Running task: " + taskName
repeat
Await Command
The Await Command compliments the Async command by allowing you to start one or more asynchronous Threads and then wait for all of them to complete before proceeding.
Specifically, the Await Command will wait for all async commands executed within its scope to complete before proceeding to the next command.
Keywords: await, blocking
The most common usage for this is to run multiple sub-commands in parallel but wait for all of them to finish before proceeding. You can also continue to run commands within the calling thread while waiting for the asynchronous commands to complete.
:main
#Await will not wait for this command to complete as it is outside the scope of the await command.
async runTask1
await
Print "Doing some thing in the main thread"
async runTask2
async runTask3
Print "Doing some other thing in the main thread"
print "Done Running Async Tasks!"
:runTask1
#Some long running task
Print "Running Task 1"
wait 1 second
:runTask2
Print "Running Task 2"
wait 2 seconds
:runTask3
Print "Running Task 3"
wait 3 seconds
Make sure that any async commands you execute within the await command always complete or else you’ll block the calling thread forever. However, you can also stop waiting for asynchronous threads using either the “break” or “continue” keywords.
await
async
wait 10 seconds
print "This Takes Forever!"
wait 2 seconds
print "Im done waiting!"
break
print "Done"
Control Commands
You might find that sometimes you want to pause, stop, or restart your script entirely.
Stopping or Restarting a script will clear all running & asynchronous threads, and clear all local and global variables.
#Restart Script from the beginning
restart
#Stop Script immediately
exit
#Pause your script and all asynchronous threads. When you run it again it will begin execution right where it left off (including asynchronous threads)
pause
The Repeat Command
The “repeat” command is a special command often used for asynchronous threads to keep executing the given function. The Repeat command effectively restarts the current “thread”, rather than the whole program.
set my display to "Tick"
wait 1
set my display to "Tock"
wait 1
repeat
If you specify a “goto functionName” somewhere before calling “repeat”, the script will repeat from the last “goto function”. Note that any previous parameters passed to that function are not passed when calling repeat.
Send/Listen/Forget Commands
Sometimes you might want to send or listen for events from other grids. You can do this from EasyCommands using the send
and listen
commands. If later you want to stop listening for those events, you can do so with the forget
command.
Send Command
To send a message to another grid, you broadcast it to a channel. The outline of the command is
send <message> to <channel>
You can also use this to send full commands to other EasyCommands scripts which are listening for commands.
send "Finished!" to myChannel
send 'open the "Outer Doors"' to myBaseChannel
Listen Command
To listen for commmands from your script, you can use the listen command. The outline of this command is
listen <channel>
.
Keywords: listen, channel, register, subscribe
Note that your script needs to be running in order to be triggered by an event from a channel. So it’s important to put the script in an “always listening” mode, which can be accomplished in several ways but the easiest would be as follows:
listen "myChannel"
wait until false
Forget Command
To stop listening to a channel and ignore future messages from that channel, you can use the ignore command. The outline of this command is
forget <channel>
.
Keywords: forget, dismiss, ignore, deregister, unsubscribe
#start listening
listen myChannel
#Do some stuff
#stop listening
forget myChannel
Using Send/Listen commands together
You can quickly do some fun cross-grid things with these two commands. Here’s a simple example that enables keyless entry to your garage from your rover:
Base Script:
listen "myBaseChannel"
wait until false
:openDoors
open the "Garage Doors"
Rover Script:
:main
Print "doing whatever..."
#Create an item in your hotbar to run "openDoors" on your EasyCommands Programmable Block
:openDoors
send "openDoors " to "myBaseChannel"
For more information on Cross Grid communication and handling input in your EasyCommands script, see Invoking Your Script
Transferring Items Across Inventories
You can transfer items between inventories using the Transfer Item Command. The general outline is:
<transfer/give> <items> from <source> to <destination>
<transfer/give> <variable> <items> from <source> to <destination>
The transfer command will attempt to transfer as many items as possible from the sources to the destionations. Be careful when using this command as it’s easy to hit the Script Too Complex limit. The transfer command is best effort and “fire & forget”. If you ask it to transfer 50 “gold ingot” and you only have 30, the command will transfer 30 ingots and complete successfully.
Here’s a full example of transferring items in small batches, just for fun
set sourceInventories to "My Rover Inventories"
set destinationInventories to "My Base Inventories"
#Bind this as a calculation, so it auto decrements as we pull ore
bind amountToTransfer to $sourceInventories "ore" amount
until amountToTransfer <= 0
transfer 100 "ore" from $sourceInventories to $destinationInventories
Transferring Custom Items
Custom Items can be transferred across items similarly to other supported item types. Just make sure to use the correct TypeId and/or SubTypeIds when transferring items.
#HackingChip is the SubTypeId. TypeId is not considered
transfer 100 "HackingChip" from $mySource cargo to $myDestination cargo
#MyObjectBuilder_Component is the TypeId (effectively the group of items).
transfer 100 "MyObjectBuilder_Component." from $mySource cargo to $myDestination cargo
#MyObjectBuilder_Component is the TypeId and HackingChip is the SubTypeId.
transfer 100 "MyObjectBuilder_Component.HackingChip" from $mySource cargo to $myDestination cargo
See Items for more information on dealing with custom item types.