Expressions¶
For uses of the Set Variable block not covered by the operators listed below, see Set Variable.
You can write an expression almost anywhere you can put a value.
line reward = num:Round(game.PlayerCount * global coinBonus * (global ("%default killstreak") + 10));
default:Teleport(default.Location + (default.Direction * global ("%default teleportRange")));
default:GivePotionEffect(pot("Speed", 1, (10*20) + num:Random(0,global maxPotionBonus)));
Value Operators¶
Info
Many operators work on more types of code items than just numbers. For more info on which operators work with which code items, check out the code items' respective pages under the Code Items category.
Terracotta supports the following operators:
+
- Addition-
- Subtraction*
- Multiplication/
- Division^
- Exponentiation%
- Modulo
Operations between constants are evaluated at compile-time, meaning you can use them safely for convenience without having to worry about using CPU. In the below example, 5 * 20
is directly added to the template as the number 100
and never creates any codeblocks. This applies to all code items, not just numbers.
Inlined Functions¶
Any function that returns a value can be used in expressions. Some actions like Set Location Coordinate still return a value even if their description doesn't say so. Generally, if an action has Variable - Variable to set
as its first parameter, it can be inlined.
default:SendMessage("You rolled a " + num:Random(1,6) + "!");
default:GiveItems(item(var:SetToRandom("cooked_porkchop","cooked_beef","golden_carrot"),16));
Custom function calls can be inlined as long as the function being called specifies a return value.
PLAYER_EVENT KillPlayer;
global ("%uuid coins") += call getCoinAmount() * global ("%uuid coinMultiplier");
Incrementors¶
Incrementors do an operation to a variable without having to write out variablename = variablename <operation> <value>
.
global added += 10;
global subtracted -= 2389;
global multiplied *= 100;
global divided /= 10;
global exponented ^= 3;
global moduloed %= 2;
Type Overrides¶
Terracotta has some type inference built in, so for many situations (especially variables that are declared inside the file you're working in) you won't have to worry about types. Sometimes though, the type of a value is unknown and must be specified manually in order to use it with operations. This can be done by adding : <type>
after the value.
In the below case, spawnLocation
's type is unknown. For the compiler to know what to do when adding the vector to it, you have to manually specify that it's a location.
Specifying the type of a variable every time you use it would suck, so you can also assign a type to variables outside of expressions.
global spawnLocation: loc;
# compiler now knows for both of these lines that `spawnLocation` is a location
default:Teleport(global spawnLocation + vec(0,10,0));
wait(1){"Time Unit" = "Seconds"};
default:Teleport(global spawnLocation + vec(0,20,0));
Type overrides can also be applied to indexing operations and actions/functions that return multiple types.
default:Teleport(global spawnLocationDict["main"]: loc + vec(0,10,0));
line newTag = item:GetTag(global item,"cooltagname"): num + 10;
Indexing Operation¶
Values in lists and dicts can be accessed via square bracket syntax from within expressions.
line dict = {
"very awesome key" = "even more awesome value"
};
default:SendMessage(line dict["very awesome key"]);
line list = [1,2,"buckle my shoe"];
default:SendMessage(line list[3]);
Warning
Even though you can easily do the same index operation in multiple places, it's not recommended. Every index operation creates more codeblocks, which uses more CPU. For this reason, if you know a value is not going to change, it's best to only index once and store the result in a variable.
global locations = {
"spawn" = loc(10,50,10)
};
default:SendMessage("Teleporting to location", global locations["spawn"]);
default:Teleport(global locations["spawn"]);
global locations = {
"spawn" = loc(10,50,10)
};
line selectedLocation = global locations["spawn"];
default:SendMessage("Teleporting to location", line selectedLocation);
default:Teleport(line selectedLocation);
It's true that the above example is a bit unnecessary, but in loops or when using indexing operations that traverse multiple levels the saved CPU can really add up.
Indexes can themselves be expressions.
line scores = [23,925,78,873];
default:SendMessage(line teamScores[num:Random(1,4)]);
line teams = {
"red" = {
"points" = 12
},
"blue" = {
"points" = 15
}
};
line teamData = line teams[var:SetToRandom("red","blue")];
If the type of a value is unknown, it must be manually specified in order to index into it. The indexing operation can appear directly after the type override; no extra parentheses are needed.
default:SendMessage(global dict_declared_elsewhere:dict["cool_key"]);
default:SendMessage(global list_declared_elsewhere:list[5]);
Multiple levels can be traversed, however you will have to manually specify the type of each level.
line gameState = {
"redTeam" = {
"unlocks" = ["damageBoost","healthBoost"]
}
};
line firstUnlock = line gameState["redTeam"]:dict["unlocks"]:list[1];
You can set dictionary values by indexing into them and using an assignment operator.
Assigning values to keys multiple levels deep will change the value in the original dictionary.
global dict = {
"words" = ["random value"]
};
global dict["words"]:list[0] = "jeremaster";
default:SendMessage(global dict["words"][0]); #sends "jeremaster"
Conditional Expressions¶
Conditions can take the form of either an expression with a comparison operator or an IF PLAYER/ENTITY/GAME/VARIABLE action.
The following are both valid ways to check which item a player is holding:
if (default?IsHoldingItem(item("emerald")){"Hand Slot" = "Main hand"}) {
default:SendMessage("Accepted!");
}
if (default.MainHandItem == item("emerald")) {
default:SendMessage("Accepted!");
}
As of now, there are no logic operators, meaning there is no way to combine multiple conditions. This will likely change in the future.
Comparison Operators¶
Comparison operators compare their left expression and right expression. The following comparison operators are available:
==
!=
<
>
<=
>=
==
and !=
work with any types of values. <
, >
, <=
, and >=
only work with numbers.
Condition Actions¶
Condition actions syntax is identical to normal action syntax except it uses ?
instead of :
.
Generic Targets¶
Due to DiamondFire limitations, condition actions used outside of IF blocks (e.g. in repeats or select actions) cannot differentiate between targets. To access IF PLAYER and IF ENTITY conditions in these contexts, the generic targets player
and entity
must be used.
Not (!
)¶
Condition actions can be inverted by prefixing them with a !
. This is equivalent to clicking their codeblock with the NOT Arrow.
if (!default?HasPlotPermission{"Permission" = "Developer"}) {
default:SendMessage("&cYou do not have permission to use this command!");
return;
}
Order of Operations¶
Unlike a certain expression system used by DiamondFire that shall not be named, Terracotta expressions follow a sane order of operations.
Things closer to the top of this list are evaluated before things closer to the bottom. Things on the same line of this list are evaluated from left to right.
- Nested expressions (parentheses), indexing into dicts/lists, and function calls (actions, constructors)
- Exponentiation (
^
) - Multiplication, division, and modulo (
*
,/
,%
) - Addition and subtraction (
+
,-
) - Comparisons (
==
,!=
,<
,>
,<=
,>=
)