Skip to content

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.

Examples
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.

wait(5 * 20){"Time Unit" = "Ticks"};

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.

Examples
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.

FUNCTION getCoinAmount;
RETURNS num;

return num:Random(5,10);
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>.

Incremetors
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.

default:Teleport(global spawnLocation: loc + vec(1,10,1));

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.

Bad
global locations = {
    "spawn" = loc(10,50,10)
};

default:SendMessage("Teleporting to location", global locations["spawn"]);
default:Teleport(global locations["spawn"]);
Good
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.

global dict = {};
global dict["key"] = 100;

default:SendMessage(global dict["key"]); #sends "100"

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.

Example
if (default.Y + 2 > 100) {
    # code...
};
Example
select PlayersByCondition saved ("powerup %uuid") == "extraHealth";

Condition Actions

Condition actions syntax is identical to normal action syntax except it uses ? instead of :.

target?Action(arguments){tags}

Example
if (game?CommandEquals("spawn")) {
    default:Teleport(loc(5,50,5));
}
Example
select PlayersByCondition list?Contains(saved ("powerups %uuid"), "speedBoost"));

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.

Example
while (player?IsSneaking) {
    # code...
}
Example
select EntitiesByCondition entity?IsNearLocation(default.Location,5);

Not (!)

Condition actions can be inverted by prefixing them with a !. This is equivalent to clicking their codeblock with the NOT Arrow.

Example
if (!default?HasPlotPermission{"Permission" = "Developer"}) {
    default:SendMessage("&cYou do not have permission to use this command!");
    return;
}
Example
select EntitiesByCondition !entity?HasTag("owner","%default");

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 (==, !=, <, >, <=, >=)