Origins: Powerful Powers
» Lists and loopsPage 11 of 17

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.

IdKindWhat it does
listpower typedeclares one list
power_listpower typea read-only list of the holder's current power ids
item_listpower typea read-only list of the item ids in one of the holder's origins:inventory powers
for_eachentity and bi-entity actionruns code for each element, and can change value
for_each_readentity and bi-entity actionruns 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.

FieldTypeDefaultDescription
variableStringrequiredThe name code uses. Same rules as variable names, and unique on the entity across all variables and list powers.
element_typeStringrequiredint, long, float, double, boolean or String. Every element has this type.
initialArray[]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_sizeInteger1024The most elements the list can hold
when_fullString"error"What add does on a full list. "error" fails. "drop_oldest" removes element 0 first, for a rolling log.
json
{
  "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.

MethodReturnsNotes
history.size()int
history.isEmpty()boolean
history.get(i)the elementerror if i is outside 0 … size()-1
history.contains(v) / history.indexOf(v)boolean / intindexOf is -1 when missing
history.set(i, v)the old elementchanges the list
history.add(v)truechanges the list and follows when_full
history.add(i, v)nothinginserts at i
history.remove(i)the removed elementan int argument removes by position
history.remove(v)booleanany other argument removes the first equal element
history.clear()nothingchanges the list
java
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_variable values and ${…}, like assignments.
  • A list is only usable through its methods. x = history; or history + 1 fails 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:

NameType
valuethe element typethe current element
indexintits position, starting at 0
sizeintthe list's size
  • index and size are always read-only.
  • value can only be changed directly in a for_each code block. Assigning it anywhere else is a load error.
  • value, index and size are reserved names. Outside a loop they fail with "value only exists inside …".
  • While a loop runs, its list has a fixed size. add, remove and clear on it fail, like Java's ConcurrentModificationException. 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.

FieldTypeDefaultDescription
listStringrequiredThe list: cooldowns, or other.cooldowns in the bi-entity version
codeString or array of stringsrequiredStatements, run once per element

Example: count every ability cooldown down by one each tick.

json
{
  "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.

FieldTypeDefaultDescription
listStringrequiredThe list: history, or other.history in the bi-entity version
actionEntity action typerequired in the entity versionRun once per element
bientity_actionBi-entity action typerequired in the bi-entity versionRun 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.

json
{
  "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.

json
{
  "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.

FieldTypeDefaultDescription
variableStringrequiredThe name code uses. Same rules as list.
include_hiddenBooleanfalseAlso list hidden powers.
only_activeBooleanfalseLeave out powers whose own condition is false right now
tagStringnoneOnly powers in this power tag. Without a namespace it gets this power's own.
sourceIdentifiernoneOnly 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:

json
{
  "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:

json
{
  "type": "origins-powerful-powers:expression",
  "expression": "powers.contains(\"origins:elytra\")"
}
json
{
  "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.

FieldTypeDefaultDescription
variableStringrequiredThe name code uses. Same rules as list.
powerIdentifierrequiredThe origins:inventory power to read
countsBooleanfalsefalse lists item ids as Strings. true lists stack sizes as ints
json
{
  "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, remove and clear fail when the code runs. Use the actions on page 14 to change items.

Example: count how many slots hold logs.

json
{
  "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.

json
{
  "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;"
      }
    }
  ]
}
json
{
  "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 an int element by value, use remove(indexOf(v)).
  • add/set widen like =, so 3 goes into a double list as 3.0.
  • contains/indexOf compare like ==, so 3 matches 3L.
  • 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_each writes value back into the list.