Scripting Expressions - MehVahdJukaar/polytone GitHub Wiki
Scripting Expressions
Many of the mod systems can be customized with Expression values, allowing for arbitrary dynamic queries of game parameters. This system is VERY powerful but at the same time it's an avanced concept.
Scripting Expressions are a 1.21.11+ concept (for now) which replace the old Math Expressions.
Struggling with scripting? Fret not, AI is just right for tasks like this, you can try to copy this wiki and the relevant Modifiers sections (i.e. particle modifiers) into an AI Chatbot and ask it what to write.
Introduction
This system is powered by the MVEL library, a Java scripting library which allows to create expressive scripting-like curly braced styled expressions. These will have a syntax very similar to other curly brackets scripting languages such as JavaScript and allow you to have things such as dynamic method invocations, local variable declarations, if statements and multiple line statements.
Example
Here is an example that uses the expression system to do something complex:
NOTE that this wiki page has new lines characters JUST for visibility sake. You have to REMOVE them for it to be valid json syntax unfortunately.
{
"some_expression_in_modifiers": "
t = global.getTime();
r = 0.5;
g = 0.5;
b = 0.5;
if (player.speed() > 0.3) {
r = 1.0;
} else if (player.speed > 0.1) {
r = 0.7;
}
if (object.canSeeSky()) {
g = clamp(object.skyLight / 15, 0, 1);
} else {
g = 0.2;
}
if (t % 2 < 1) {
b = 0.5 + 0.5 * sin(t);
} else {
b = 0.3 + 0.3 * cos(global.time());
}
if (p.inWater) {
g += 0.2;
}
color(r, g, b, 1.0);
}
"
}
Context Functions
Expression scripting exposes a number of context object, each varying depending on the current expression that you are using.
Each object can be accessed with the . operator to invoke one of its functions.
Here is an example invoking the getArmor function on the player context object and returns 1 if the helmet is iron helmet.
player.getArmor('head') == 'minecraft:iron_helmet' ? 1 : 0
Each function can either be called with its name (with or without brachets if it has no arguments) or with its getter.
Example for age, some of the valid syntaxes are.
player.agep.getAge()player.age()
Contexts Availability
Block Modifiers & Colormap Expressions:
object: Positional Contextplayer: Player Contextcamera: Camera Contextglobal: Global Contextrandom: Random Context
Particle Modifiers Expressions:
object: Particle Contextplayer: Player Contextcamera: Camera Contextglobal: Global Contextrandom: Random Context
Variant Animated Textures & Global Expressions Expressions:
player: Player Contextcamera: Camera Contextglobal: Global Contextrandom: Random Context
Entity Modifiers Expressions:
object: Entity Contextplayer: Entity Contextcamera: Camera Contextglobal: Global Contextrandom: Random Context
Global Context
This one can be accessed with global or g.
Available on all expressions.
| Method / Field | Return Type | Description / Notes |
|---|---|---|
| time() | double | Current game time in ticks |
| dayTime() | double | Current day time in ticks |
| season() | String | Current season name, if a season mod is present |
| seasonNumber() | double | Current season number. 0-1 |
| skyType() | int | |
| dimensionType() | String | Name of current dimension |
| rain() | double | Current rain + thunder value. 0-1 |
| environmentAttribute(attributeName: String) | String - number | Value of a global named environment attribute |
Positional Context
This one is shared between Player Object, Particle Object, Camera Object and more. It's also found alone in Colormap Expressions or Block Modifiers under the name object or o
Its simply reported here for brevity not to duplicate its content into each of those sections below.
The section itself contains a lot of functions that query the world for attributes that are relevant at a particular block position.
| Method | Return Type | Description / Notes |
|---|---|---|
| x() | double | Object X position |
| y() | double | Object Y position |
| z() | double | Object Z position |
| block() | String | Registered name of the block at the current position |
| blockState() | String | Full string representation of the block state |
| blockStateValue(input: Object) | String / number | Value of a specific block state property |
| biome() | String | Name of the biome at the current position |
| biomeIndex() | double | Legacy biome mapper |
| biomeIndex(biomeMapper: String) | double | Biome index using a specific mapper |
| temperature() | double | Climate temperature of the biome |
| downfall() | double | Climate rainfall of the biome |
| skyLight() | int | Sky light level at the current position position |
| blockLight() | int | Block light level at the current position position |
| canSeeSky() | boolean | True if the position can see the sky |
| hasEntitiesWithin() | boolean | True if any entities intersect the object's bounds |
| hasBlockTag(tag: String) | boolean | True if the block matches the given tag |
| hasAirAt() | boolean | True if the position is air |
| hasFluid() | boolean | True if the positon contains fluid |
| fluid() | String | Name of the fluid at the current position |
| hasBlockEntity() | boolean | True if the position has a block entity |
| blockEntity() | String | Name of the block entity, or "null" |
| environmentAttribute(attributeName: String) | String - number | Value of a named environment attribute at the current position |
Entity Context
This type is used for Entity Context, called objector o, just available in Entity Modifiers Expressions.
Here is its complete API.
The functions from Positional Context are also included in this but wont be reported below.
| Function | Return Type | Description / Notes |
|---|---|---|
| xd() | double | Player X velocity |
| yd() | double | Player Y velocity |
| zd() | double | Player Z velocity |
| inWater() | boolean | True if player is in water |
| swimming() | boolean | True if player is swimming |
| flying() | boolean | True if player is fall flying |
| crouching() | boolean | True if player is crouching |
| sleeping() | boolean | True if player is sleeping |
| onGround() | boolean | True if player is on the ground |
| health() | double | Current player health |
| maxHealth() | double | Maximum player health |
| hurtTime() | double | Time since last damage |
| age() | int | Player tick count (age) |
| speed() | double | Player movement speed |
| speedSq() | double | Player movement speed squared. Faster than calling speed |
| walkAnimation() | double | Walk animation position |
| walkAnimationSpeed() | double | Walk animation speed |
| mainHandItem() | String | Registered name of main hand item |
| offHandItem() | String | Registered name of offhand item |
| armor(slot: String) | String | Registered name of armor in the given slot (feet, legs, chest, head) |
| height() | double | Bounding box height |
| width() | double | Bounding box width |
| eyeHeight() | double | eye height |
| owner() | Entity Context / null | Another entity context of the entity that owns this entity, used for pets and projectiles. Can be null so you must check for that |
Player Context
Called player or shorthand p; available in every expression type.
It can use EVERYTHING described in Entity Context PLUS these:
| Function | Return Type | Description / Notes |
|---|---|---|
| itemUsedTicks() | int | Item use ticks |
| itemUsed() | string | Item used |
Particle Context
This one is used in Particle Modifiers or Custom particles. You will be able to reference it with they keyworld object or o.
The functions from Positional Context are also included in this but wont be reported below.
Here is its complete API.
| Method / Field | Return Type | Description / Notes |
|---|---|---|
| xd() | double | Particle X velocity |
| yd() | double | Particle Y velocity |
| zd() | double | Particle Z velocity |
| red() | double | Red color component (1.0 if not a quad particle) |
| green() | double | Green color component (1.0 if not a quad particle) |
| blue() | double | Blue color component (1.0 if not a quad particle) |
| alpha() | double | Alpha transparency (1.0 if not a quad particle) |
| roll() | double | Particle roll (0.0 if not a quad particle) |
| size() | double | Particle size (default 0.15F if not a quad particle) |
| age() | int | Current particle age |
| life() | int | Total particle lifetime |
| color() | int | rgb color |
| custom() | double | Particle Custom variable. You just have 1 of these |
ADVANCED:
Particle context also allows to SET values directly. This is useful as it allows you to run LESS expressions per particle and hence will have a performance boost. The syntax for this is simple as the variable names are the same as the getters above but can be prefixed by a set keyword. Additionally the setRemoved / removed and setColorFrom(colormapName: String) / colorFrom(colormapName: String) functions are available.
Additionally these can be used in a ticker or initializer(not supported yet) custom particle object without the need to have nested expressions, the ticker itself WILL BE an expression that does everything.
This MIGHT or MIGHT NOT improve performance.
{
"ticker": "
if(o.age>4){
o.setRemoved();
};
o.roll = 2 + o.roll;
o.alpha = 0.4;
o.setBlue(255);
o.setColor(colormap
"
}
Camera Context
This one can be accessed with camera or c in all expressions.
It includes all the functions from Positional Context.
| Method / Field | Return Type | Description / Notes |
|---|---|---|
| yaw() | double | Camera yaw rotation |
| pitch() | double | Camera pitch rotation |
| roll() | double | Camera roll rotation |
| detatched() | boolean | True if camera is detached |
| viewDistance() | int | Current render distance from game options |
| fov() | double | Fov angle in degrees |
| lookingToward(x: double, y: double, z: double) | Helper function that checks if a position is infront of the camera and within its FOV |
Random Context
Contrary to math functions, radom math functions are accessed by a random or r object. This is done since random invocations could be seeded and deterministic and hence isnt a global property. For instance a random call in a block colormap will always result in the same value at a particular position.
Accessible in all expressions.
| Method / Field | Return Type | Description / Notes |
|---|---|---|
| randInt() | int | Returns a random integer |
| randInt(bound: int) | int | Returns a random integer between 0 (inclusive) and bound (exclusive) |
| randInt(origin: int, bound: int) | int | Returns a random integer between origin (inclusive) and bound (exclusive) |
| rand() | double | Returns a random double between 0.0 and 1.0 |
| rand(bound: double) | double | Returns a random double between 0.0 and bound |
| rand(origin: double, bound: double) | double | Returns a random double between origin and bound |
| gaussian() | double | Returns a random number following standard Gaussian distribution |
| gaussian(mean: double, deviation: double) | double | Returns a random Gaussian with given mean and standard deviation |
| noise(name: String, x: double, y: double) | double | Returns the value of a registered noiseat (x, y). See the end of the Math Expression section |
| noise(x: double, y: double) | double | Returns the value of the default Perlin simplex noise at (x, y) |
Math Functions
These dont need a context and can be called directly. Also listed are some constant values you can use.
| Method / Field | Return Type | Description / Notes |
|---|---|---|
| PI | double | Constant π |
| TAU | double | Constant 2π |
| E | double | Constant e |
| PSI | double | Golden ratio |
| sin(x: double) | double | Sine of x (radians) |
| cos(x: double) | double | Cosine of x (radians) |
| tan(x: double) | double | Tangent of x (radians) |
| atan2(y: double, x: double) | double | Arc tangent of y/x |
| sqrt(x: double) | double | Square root of x |
| abs(x: double) | double | Absolute value of x |
| log(x: double) | double | Natural logarithm of x |
| exp(x: double) | double | Exponential e^x |
| pow(a: double, b: double) | double | a raised to the power of b |
| floor(x: double) | double | Largest integer ≤ x |
| ceil(x: double) | double | Smallest integer ≥ x |
| round(x: double) | double | Closest integer to x |
| fract(x: double) | double | Fractional part of x |
| sign(x: double) | double | Sign of x (-1, 0, 1) |
| radians(degrees: double) | double | Convert degrees to radians |
| degrees(radians: double) | double | Convert radians to degrees |
| mod(x: double, y: double) | double | Positive modulo of x by y |
| min(a: double, b: double) | double | Minimum of a and b |
| max(a: double, b: double) | double | Maximum of a and b |
| clamp(val: double, min: double, max: double) | double | Clamp val between min and max |
| square(x: double) | double | x squared |
| cube(x: double) | double | x cubed |
| lerp(a: double, b: double, t: double) | double | Linear interpolation between a and b |
| inverseLerp(a: double, b: double, v: double) | double | Normalized position of v between a and b |
| smoothstep(edge0: double, edge1: double, x: double) | double | Smoothstep interpolation between edges |
| distSquare(x1: double, y1: double, x2: double, y2: double) | double | Squared distance between two points |
| distSquare(p1: Vec3i, p2: Vec3i) | double | Squared distance between two Vec3i points |
| dist(x1: double, y1: double, x2: double, y2: double) | double | Distance between two points |
| dist(p1: Vec3i, p2: Vec3i) | double | Distance between two Vec3i points |
| red(color: double) | double | Red component of ARGB color (0–1) |
| green(color: double) | double | Green component of ARGB color (0–1) |
| blue(color: double) | double | Blue component of ARGB color (0–1) |
| alpha(color: double) | double | Alpha component of ARGB color (0–1) |
| color(r: double, g: double, b: double, a: double) | int | Packs RGBA floats into ARGB int |
Global Functions
These are accessible anywhere just like math ones
| Method / Field | Return Type | Description / Notes |
|---|---|---|
| colormap(colormapName: String, ?x: int, ?y: int, ?z: int, ?tintIndex: int) | int | Samples a custom colormap at the specific position and with the specific tint index provided. Last 4 parameters are optional |
| config(configId : string) | string / number | Config value of the custom Polytone Config |
| modOn(modId : string, ?modVersionrange : string) | boolean | Rerturns if a mod is loaded. Version range is optional. Can be single version or range |
| dateYear() | int | Current system year |
| dateMonth() | int | Current system month |
| dateDay() | int | Current system day |
| dateHour() | int | Current system hour |
| dateMinute() | int | Current system minute |
| dateTime() | int | Current system time |
| dateDayOfTheYear() | int | Current system day of year |
| modLoader() | string | Current Mod Loader. Can be Fabric or Neoforge |
Global Expressions
This is an advanced concept that allows you to run certain expressions every tick to set certain global variables that you can then reference in your Script Expressions.
To do so you'll have to place json files in the global_expressions folder. Syntax is very simple and fields are as in the following example.
{
"update_interval": 1,
"default_value": 0,
"expression": "g.time + p.age/2 + g.rain"
}
Given the name of the json file, a variable with the name [file namespace]_[file name] will be assigned every update_interval ticks according to your expression. You can then reference it in your other Scripting Expressions.
For instance imagine your pack namespace is my_pack and the json file is called some_calculation, then you can reference it like such:
{
"some_example_expression_field": "sin(my_pack_some_calculation + 3) /2.0)"
}
Disclaimer
All credits to MVEL library which powers this system.
If you need more functions addded to the API contact me.