Lists and loops
Typed, resizable lists you declare in a power, use from code with Java's List methods, and loop over with two "for each" actions. Lists are declared in their own powers, separate from variables.
| Id | Kind | What it does |
|---|---|---|
list | power type | declares one list |
power_list | power type | a read-only list of the holder's current power ids |
item_list | power type | a read-only list of the item ids in one of the holder's origins:inventory powers |
for_each | entity and bi-entity action | runs code for each element, and can change value |
for_each_read | entity and bi-entity action | runs any action for each element, with the list read-only |
List
origins-powerful-powers:listpower type
Declares one list. Each holder has their own list, saved with their powers and synced to the client when it changes.
| Field | Type | Default | Description |
|---|---|---|---|
variable | String | required | The name code uses. Same rules as variable names, and unique on the entity across all variables and list powers. |
element_type | String | required | int, long, float, double, boolean or String. Every element has this type. |
initial | Array | [] | Starting elements, such as [1, 2, 3] or ["a", "b"]. Numbers follow Java's rules (1 is an int, 1.5 a double) and convert like =. |
max_size | Integer | 1024 | The most elements the list can hold |
when_full | String | "error" | What add does on a full list. "error" fails. "drop_oldest" removes element 0 first, for a rolling log. |
{
"type": "origins-powerful-powers:list",
"variable": "history",
"element_type": "long",
"max_size": 12,
"when_full": "drop_oldest"
}This keeps the last 12 values added to history.
initial is checked on grant. An initial element that doesn't fit element_type (like 1.5 in an int list) is skipped with a warning in the log when the power is granted. The list name and the other fields are checked when the pack loads.
Using a list in code
Lists have the methods of Java's java.util.List. They work in execute code, expression conditions, modify_variable values and ${…}. Bi-entity versions can use other.history.
| Method | Returns | Notes |
|---|---|---|
history.size() | int | |
history.isEmpty() | boolean | |
history.get(i) | the element | error if i is outside 0 … size()-1 |
history.contains(v) / history.indexOf(v) | boolean / int | indexOf is -1 when missing |
history.set(i, v) | the old element | changes the list |
history.add(v) | true | changes the list and follows when_full |
history.add(i, v) | nothing | inserts at i |
history.remove(i) | the removed element | an int argument removes by position |
history.remove(v) | boolean | any other argument removes the first equal element |
history.clear() | nothing | changes the list |
history.add(coins - lastBalance);
best = history.isEmpty() ? 0 : Math.max(best, history.get(history.size() - 1));- Methods that change the list (
set,add,remove,clear) are load errors in conditions,modify_variablevalues and${…}, like assignments. - A list is only usable through its methods.
x = history;orhistory + 1fails with "history is a list". - Using a missing result (
x = history.clear();) fails, as in Java.
Loops
Inside a loop, three reserved names describe the innermost loop:
| Name | Type | |
|---|---|---|
value | the element type | the current element |
index | int | its position, starting at 0 |
size | int | the list's size |
indexandsizeare always read-only.valuecan only be changed directly in afor_eachcode block. Assigning it anywhere else is a load error.value,indexandsizeare reserved names. Outside a loop they fail with "value only exists inside …".- While a loop runs, its list has a fixed size.
add,removeandclearon it fail, like Java'sConcurrentModificationException. Other lists and variables can change freely.
For each
origins-powerful-powers:for_eachentity action and bi-entity action
Runs code once for each element of a list. Changing value changes that element.
| Field | Type | Default | Description |
|---|---|---|---|
list | String | required | The list: cooldowns, or other.cooldowns in the bi-entity version |
code | String or array of strings | required | Statements, run once per element |
Example: count every ability cooldown down by one each tick.
{
"type": "origins:action_over_time",
"interval": 1,
"entity_action": {
"type": "origins-powerful-powers:for_each",
"list": "cooldowns",
"code": "value = Math.max(value - 1, 0);"
}
}The code can also read and change ordinary variables, for example total += value;, or ready += value == 0 ? 1 : 0; to count how many are ready. An error in the code stops the loop. Changes made before it stay.
For each read
origins-powerful-powers:for_each_readentity action and bi-entity action
Runs any action once for each element of a list. The list is read-only inside.
| Field | Type | Default | Description |
|---|---|---|---|
list | String | required | The list: history, or other.history in the bi-entity version |
action | Entity action type | required in the entity version | Run once per element |
bientity_action | Bi-entity action type | required in the bi-entity version | Run once per element |
Inside the action, OPP's expression conditions, execute code, modify_variable values and ${…} in with_variables can read value, index and size. Other variables can change, so a running total works.
Example: a puff of particles per payout, green for big ones.
{
"type": "origins-powerful-powers:for_each_read",
"list": "history",
"action": {
"type": "origins:if_else",
"condition": {
"type": "origins-powerful-powers:expression",
"expression": "value >= 25"
},
"if_action": {
"type": "origins:execute_command",
"command": "particle minecraft:happy_villager ~ ~2.2 ~ 0.4 0.2 0.4 0 4"
},
"else_action": {
"type": "origins:execute_command",
"command": "particle minecraft:smoke ~ ~2.2 ~ 0.4 0.2 0.4 0 4"
}
}
}Example: print each element in chat.
{
"type": "origins-powerful-powers:for_each_read",
"list": "history",
"action": {
"type": "origins-powerful-powers:with_variables",
"action": {
"type": "origins:execute_command",
"command": "tellraw @s {\"text\":\"#${index + 1}: ${value}\"}"
}
}
}Nesting
A for_each_read action can contain another loop, over another list or the same one. value, index and size always mean the innermost loop. To use an outer element inside an inner loop, copy it into a variable first.
Power list
origins-powerful-powers:power_listpower type
A read-only list of String power ids: the powers the holder has at the moment it's read. Each code run takes a fresh snapshot, and each loop takes one when it starts. The ids are sorted alphabetically.
| Field | Type | Default | Description |
|---|---|---|---|
variable | String | required | The name code uses. Same rules as list. |
include_hidden | Boolean | false | Also list hidden powers. |
only_active | Boolean | false | Leave out powers whose own condition is false right now |
tag | String | none | Only powers in this power tag. Without a namespace it gets this power's own. |
source | Identifier | none | Only powers granted by this source. Origins' /power sources <player> <power> shows a power's sources. |
It includes its own power, so give it "hidden": true to leave itself out:
{
"type": "origins-powerful-powers:power_list",
"hidden": true,
"variable": "powers"
}The read-only methods from Using a list in code work on it, and so does for_each_read:
{
"type": "origins-powerful-powers:expression",
"expression": "powers.contains(\"origins:elytra\")"
}{
"type": "origins-powerful-powers:for_each_read",
"list": "powers",
"action": {
"type": "origins-powerful-powers:modify_variable",
"variable": "fromOrigins",
"operation": "add",
"value": "value.startsWith(\"origins:\") ? 1 : 0"
}
}Changing it (add, set, remove, clear, or a for_each that assigns value) fails with "powers is read-only" when the code runs.
Item list
origins-powerful-powers:item_listpower type
A read-only list of what's in one of the holder's origins:inventory powers, at the moment code reads it. One element per slot, so index is the slot number and get(3) is container.3.
| Field | Type | Default | Description |
|---|---|---|---|
variable | String | required | The name code uses. Same rules as list. |
power | Identifier | required | The origins:inventory power to read |
counts | Boolean | false | false lists item ids as Strings. true lists stack sizes as ints |
{
"type": "origins-powerful-powers:item_list",
"hidden": true,
"variable": "satchel",
"power": "mypack:satchel"
}- An empty slot is
minecraft:air, so the list length always matches the power's size. - If the holder lacks that power, or it's another power type, the list is empty and the reason is logged once.
- It's read-only.
add,set,removeandclearfail when the code runs. Use the actions on page 14 to change items.
Example: count how many slots hold logs.
{
"type": "origins-powerful-powers:for_each_read",
"list": "satchel",
"action": {
"type": "origins-powerful-powers:modify_variable",
"variable": "logs",
"operation": "add",
"value": "value.endsWith(\"_log\") ? 1 : 0"
}
}Example: is the satchel empty? Count the filled slots into an int filled variable, then check it.
{
"type": "origins:and",
"actions": [
{
"type": "origins-powerful-powers:execute",
"code": "filled = 0;"
},
{
"type": "origins-powerful-powers:for_each_read",
"list": "satchel",
"action": {
"type": "origins-powerful-powers:execute",
"code": "filled += value.equals(\"minecraft:air\") ? 0 : 1;"
}
}
]
}{
"type": "origins-powerful-powers:expression",
"expression": "filled == 0"
}For one slot, use Item.id.
Differences from Java
remove(int)always removes by position. To remove anintelement by value, useremove(indexOf(v)).add/setwiden like=, so3goes into adoublelist as3.0.contains/indexOfcompare like==, so3matches3L.- No generics syntax,
new ArrayList<>(), or lists of lists. Lists can't be assigned or passed around. A list only exists as a power. for_eachwritesvalueback into the list.