moved scarpet docs to markdown files
This commit is contained in:
1 parent
c7603038da
commit
d93a0dbdf4
19 files changed
+3154
-2712
No files matched your search
@@ -5,7 +5,6 @@ build/
|
||||
out/
|
||||
classes/
|
||||
publish/
|
||||
docs/
|
||||
|
||||
# idea
|
||||
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
# Auxiliary aspects
|
||||
|
||||
Collection of other methods that control smaller, yet still important aspects of the game
|
||||
|
||||
## Sounds
|
||||
|
||||
### `sound(name, pos, volume?, pitch?)`
|
||||
|
||||
Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`. `pos` can be
|
||||
either a block, triple of coords, or a list of thee numbers. Uses the same options as a corresponding `playsound` command.
|
||||
|
||||
## Particles
|
||||
|
||||
### `particle(name, pos, count?. spread?, speed?, playername?)`
|
||||
|
||||
Renders a cloud of particles `name` centered around `pos` position, by default `count` 10 of them, default `speed`
|
||||
of 0, and to all players nearby, but these options can be changed via optional arguments. Follow vanilla `/particle`
|
||||
command on details on those options. Valid particle names are
|
||||
for example `'angry_villager', 'item diamond', 'block stone', 'dust 0.8 0.1 0.1 4'`.
|
||||
|
||||
### `particle_line(name, pos, pos2, density?)`
|
||||
|
||||
Renders a line of particles from point `pos` to `pos2` with supplied density (defaults to 1), which indicates how far
|
||||
apart you would want particles to appear, so `0.1` means one every 10cm.
|
||||
|
||||
### `particle_rect(name, pos, pos2, density?)`
|
||||
|
||||
Renders a cuboid of particles between point `pos` to `pos2` with supplied density.
|
||||
|
||||
## Markers
|
||||
|
||||
### `create_marker(text, pos, rotation?, block?)`
|
||||
|
||||
Spawns a (permanent) marker entity with text or block at position. Returns that entity for further manipulations.
|
||||
Unloading the app that spawned them will cause all the markers from the loaded portion of the world to be removed.
|
||||
Also - if the game loads that marker in the future and the app is not loaded, it will be removed as well.
|
||||
|
||||
### `remove_all_markers()`
|
||||
|
||||
Removes all scarpet markers from the loaded portion of the world created by this app, in case you didn't want to do
|
||||
the proper cleanup.
|
||||
|
||||
## System function
|
||||
|
||||
### `nbt(expr)`
|
||||
|
||||
Treats the argument as a nbt serializable string and returns its nbt value. In case nbt is not in a correct nbt
|
||||
compound tag format, it will return `null` value.
|
||||
|
||||
Consult section about container operations in `Expression` to learn about possible operations on nbt values.
|
||||
|
||||
### `escape_nbt(expr)`
|
||||
|
||||
Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly
|
||||
as a string value.
|
||||
|
||||
### `print(expr)`
|
||||
|
||||
Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr.
|
||||
|
||||
### `logger(expr)`
|
||||
|
||||
Prints the message to system logs, and not to chat.
|
||||
|
||||
### `run(expr)`
|
||||
|
||||
Runs a vanilla command from the string result of the `expr` and returns its success count
|
||||
|
||||
<pre>
|
||||
run('fill 1 1 1 10 10 10 air') -> 123 // 123 block were filled, this operation was successful 123 times out of a possible 1000 blocks volume
|
||||
run('give @s stone 4') -> 1 // this operation was successful once
|
||||
</pre>
|
||||
|
||||
### `save()`
|
||||
|
||||
Performs autosave, saves all chunks, player data, etc. Useful for programs where autosave is disabled due to
|
||||
performance reasons and saves the world only on demand.
|
||||
|
||||
### `load_app_data(), load_app_data(file)`
|
||||
|
||||
Loads the app data associated with the app from the world /scripts folder. Without argument returns the memory
|
||||
managed and buffered / throttled NBT tag. With a file name - reads explicitly a file with that name from the
|
||||
scripts folder.
|
||||
|
||||
You can use app data to save non-vanilla information separately from the world and other scripts.
|
||||
|
||||
### `store_app_data(tag), store_app_data(tag, file)`
|
||||
|
||||
Stores the app data associated with the app from the world /scripts folder. With the `file` parameter saves
|
||||
immediately and with every call, without `file` parameter, it may take up to 10 seconds for the output file
|
||||
to sync preventing flickering in case this tag changes frequently. It will be synced when server closes.
|
||||
|
||||
### `tick_time()`
|
||||
|
||||
Returns game tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time
|
||||
|
||||
### `game_tick(mstime?)`
|
||||
|
||||
Causes game to run for one tick. By default runs it and returns control to the program, but can optionally
|
||||
accept expected tick length, in milliseconds. You can't use it to permanently change the game speed, but setting
|
||||
longer commands with custom tick speeds can be interrupted via `/script stop` command
|
||||
|
||||
<pre>
|
||||
loop(1000,game_tick()) // runs the game as fast as it can for 1000 ticks
|
||||
loop(1000,game_tick(100)) // runs the game twice as slow for 1000 ticks
|
||||
</pre>
|
||||
|
||||
### `seed()`
|
||||
|
||||
Returns current world seed.
|
||||
|
||||
### `current_dimension()`
|
||||
|
||||
Returns current dimension that the script runs in.
|
||||
|
||||
### `in_dimension(smth, expr)`
|
||||
|
||||
Evaluates the expression `expr` with different dimension execution context. `smth` can be an entity,
|
||||
world-localized block, so not `block('stone')`, or a string representing a dimension like:
|
||||
`'nether'`, `'the_nether'`, `'end'` or `'overworld'`, etc.
|
||||
|
||||
### `schedule(delay, function, args...)`
|
||||
|
||||
Schedules a user defined function to run with a specified `delay` ticks of delay. Scheduled functions run at the end
|
||||
of the tick, and they will run in order they were scheduled.
|
||||
|
||||
### `statistic(player, category, entry)`
|
||||
|
||||
Queries in-game statistics for certain values. Categories include:
|
||||
|
||||
* `mined`: blocks mined
|
||||
* `crafted`: items crafted
|
||||
* `used`: items used
|
||||
* `broken`: items broken
|
||||
* `picked_up`: items picked up
|
||||
* `dropped`: items dropped
|
||||
* `killed`: mobs killed
|
||||
* `killed_by`: blocks mined
|
||||
* `custom`: various random stats
|
||||
|
||||
For the options of `entry`, consult your statistics page, or give it a guess.
|
||||
|
||||
The call will return `null` if the statistics options are incorrect, or player didn't get these in their history.
|
||||
If the player encountered the statistic, or game created for him empty one, it will return a number.
|
||||
Scarpet will not affect the entries of the statistics, even if it is just creating empty ones. With `null` response
|
||||
it could either mean your input is wrong, or statistic has effectively a value of `0`.
|
||||
|
||||
### `plop(pos, what)`
|
||||
|
||||
Plops a structure or a feature at a given `pos`, so block, triple position coordinates or a list of coordinates.
|
||||
To `what` gets plopped and exactly where it often depends on the feature or structure itself. For example, all
|
||||
structures are chunk aligned, and often span multiple chunks. Repeated calls to plop a structure in the same chunk
|
||||
would result either in the same structure generated on top of each other, or with different state, but same position.
|
||||
Most structures generate at specific altitudes, which are hardcoded, or with certain blocks around them. API will
|
||||
cancel all extra position / biome / random requirements for structure / feature placement, but some hardcoded
|
||||
limitations may still cause some of structures/features not to place. Some features require special blocks to be
|
||||
present, like coral -> water or ice spikes -> snow block, and for some features, like fossils, placement is all sorts
|
||||
of messed up. This can be partially avoided for structures by setting their structure information via `set_structure`,
|
||||
which sets it without looking into world blocks, and then use `plop` to fill it with blocks. This may, or may not work.
|
||||
|
||||
All generated structures will retain their properties, like mob spawning, however in many cases the world / dimension
|
||||
itself has certain rules to spawn mobs, like plopping a nether fortress in the overworld will not spawn nether mobs,
|
||||
because nether mobs can spawn only in the nether, but plopped in the nether - will behave like a valid nether fortress.
|
||||
|
||||
`plop` will not use world random number generator to generate structures and features, but its own. This has a benefit
|
||||
that they will generate properly randomly, not the same time every time.
|
||||
|
||||
Structure list:
|
||||
|
||||
* `monument`: Ocean Monument. Generates at fixed Y coordinate, surrounds itself with water.
|
||||
* `fortress`: Nether Fortress. Altitude varies, but its bounded by the code.
|
||||
* `mansion`: Woodland Mansion
|
||||
* `jungle_temple`: Jungle Temple
|
||||
* `desert_temple`: Desert Temple. Generates at fixed Y altitude.
|
||||
* `end_city`: End City with Shulkers
|
||||
* `igloo`: Igloo
|
||||
* `shipwreck`: Shipwreck, version1?
|
||||
* `shipwreck2`: Shipwreck, version2?
|
||||
* `witch_hut`
|
||||
* `ocean_ruin`, `ocean_ruin_small`, `ocean_ruin_tall`: Stone variants of ocean ruins.
|
||||
* `ocean_ruin_warm`, `ocean_ruin_warm_small`, `ocean_ruin_warm_tall`: Sandstone variants of ocean ruins.
|
||||
* `treasure`: A treasure chest. Yes, its a whole structure.
|
||||
* `pillager_outpost`: A pillager outpost.
|
||||
* `mineshaft`: A mineshaft.
|
||||
* `mineshaft_mesa`: A Mesa (Badlands) version of a mineshaft.
|
||||
* `village`: Plains, oak village.
|
||||
* `village_desert`: Desert, sandstone village.
|
||||
* `village_savanna`: Savanna, acacia village.
|
||||
* `village_taiga`: Taiga, spruce village.
|
||||
* `village_snowy`: Resolute, Canada.
|
||||
|
||||
Feature list:
|
||||
|
||||
* `oak`
|
||||
* `oak_beehive`: oak with a hive (1.15+).
|
||||
* `oak_large`: oak with branches.
|
||||
* `oak_large_beehive`: oak with branches and a beehive (1.15+).
|
||||
* `birch`
|
||||
* `birch_large`: tall variant of birch tree.
|
||||
* `shrub`: low bushes that grow in jungles.
|
||||
* `shrub_acacia`: low bush but configured with acacia (1.14 only)
|
||||
* `shrub_snowy`: low bush with white blocks (1.14 only)
|
||||
* `jungle`: a tree
|
||||
* `jungle_large`: 2x2 jungle tree
|
||||
* `spruce`
|
||||
* `spruce_large`: 2x2 spruce tree
|
||||
* `pine`: spruce with minimal leafage (1.15+)
|
||||
* `pine_large`: 2x2 spruce with minimal leafage (1.15+)
|
||||
* `spruce_matchstick`: see 1.15 pine (1.14 only).
|
||||
* `spruce_matchstick_large`: see 1.15 pine_large (1.14 only).
|
||||
* `dark_oak`
|
||||
* `acacia`
|
||||
* `oak_swamp`: oak with more leaves and vines.
|
||||
* `well`: desert well
|
||||
* `grass`: a few spots of tall grass
|
||||
* `grass_jungle`: little bushier grass feature (1.14 only)
|
||||
* `lush_grass`: grass with patchy ferns (1.15+)
|
||||
* `tall_grass`: 2-high grass patch (1.15+)
|
||||
* `fern`: a few random 2-high ferns
|
||||
* `cactus`: random cacti
|
||||
* `dead_bush`: a few random dead bushi
|
||||
* `fossils`: underground fossils, placement little wonky
|
||||
* `mushroom_brown`: large brown mushroom.
|
||||
* `mushroom_red`: large red mushroom.
|
||||
* `ice_spike`: ice spike. Require snow block below to place.
|
||||
* `glowstone`: glowstone cluster. Required netherrack above it.
|
||||
* `melon`: a patch of melons
|
||||
* `melon_pile`: a pile of melons (1.15+)
|
||||
* `pumpkin`: a patch of pumpkins
|
||||
* `pumpkin_pile`: a pile of pumpkins (1.15+)
|
||||
* `sugarcane`
|
||||
* `lilypad`
|
||||
* `dungeon`: Dungeon. These are hard to place, and fail often.
|
||||
* `iceberg`: Iceberg. Generate at sea level.
|
||||
* `iceberg_blue`: Blue ice iceberg.
|
||||
* `lake`
|
||||
* `lava_lake`
|
||||
* `end_island`
|
||||
* `chorus`: Chorus plant. Require endstone to place.
|
||||
* `sea_grass`: a patch of sea grass. Require water.
|
||||
* `sea_grass_river`: a variant.
|
||||
* `kelp`
|
||||
* `coral_tree, coral_mushroom, coral_claw`: various coral types, random color.
|
||||
* `coral`: random coral structure. Require water to spawn.
|
||||
* `sea_pickle`
|
||||
* `boulder`: A rocky, mossy formation from a giant taiga biome. Doesn't update client properly, needs relogging.
|
||||
@@ -0,0 +1,34 @@
|
||||
# Iterating over larger areas of blocks
|
||||
|
||||
These functions help scan larger areas of blocks without using generic loop functions, like nested `loop`.
|
||||
|
||||
### `scan(cx, cy, cz, dx, dy, dz, px?, py?, pz?, expr)`
|
||||
|
||||
Evaluates expression over area of blocks defined by its center (`cx, cy, cz`), expanded in all directions
|
||||
by `dx, dy, dz` blocks, or optionally in negative with `d` coords, and `p` coords in positive values. `expr`
|
||||
receives `_x, _y, _z` as coords of current analyzed block and `_`, which represents the block itself.
|
||||
|
||||
### `volume(x1, y1, z1, x2, y2, z2, expr)`
|
||||
|
||||
Evaluates expression for each block in the area, the same as the `scan`function, but using two opposite corners of
|
||||
the rectangular cuboid. Any corners can be specified, its like you would do with `/fill` command.
|
||||
|
||||
### `neighbours(x, y, z), neighbours(block), neighbours(l(x,y,z))`
|
||||
|
||||
Returns the list of 6 neighbouring blocks to the argument. Commonly used with other loop functions like `for`.
|
||||
|
||||
<pre>
|
||||
for(neighbours(x,y,z),air(_)) => 4 // number of air blocks around a block
|
||||
</pre>
|
||||
|
||||
### `rect(cx, cy, cz, dx?, dy?, dz?, px?, py?, pz?)`
|
||||
|
||||
returns an iterator, just like `range` function that iterates over rectangular cubarea of blocks. If only center
|
||||
point is specified, it iterates over 27 blocks. If `d` arguments are specified, expands selection of respective
|
||||
number of blocks in each direction. If `p` arguments are specified, it uses `d` for negative offset, and `p` for positive.
|
||||
|
||||
### `diamond(cx, cy, cz, radius?, height?)`
|
||||
|
||||
Iterates over a diamond like area of blocks. With no radius and height, its 7 blocks centered around the middle
|
||||
(block + neighbours). With a radius specified, it expands shape on x and z coords, and with a custom height, on y.
|
||||
Any of these can be zero as well. radius of 0 makes a stick, height of 0 makes a diamond shape pad.
|
||||
@@ -0,0 +1,530 @@
|
||||
# Blocks / World API
|
||||
|
||||
## Specifying blocks
|
||||
|
||||
### `block(x, y, z)`, `block(l(x,y,z))`, `block(state)`
|
||||
|
||||
Returns either a block from specified location, or block with a specific state (as used by `/setblock` command),
|
||||
so allowing for block properties, block entity data etc. Blocks otherwise can be referenced everywhere by its simple
|
||||
string name, but its only used in its default state.
|
||||
|
||||
<pre>
|
||||
block('air') => air
|
||||
block('iron_trapdoor[half=top]') => iron_trapdoor
|
||||
block(0,0,0) == block('bedrock') => 1
|
||||
block('hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') => hopper
|
||||
</pre>
|
||||
|
||||
Retrieving a block with `block` function has also a side-effect of evaluating its current state and data.
|
||||
So if you use it later it will reflect block state and data of the block that was when block was called, rather than
|
||||
when it was used. Block values passed in various places like `scan` functions, etc, are not fully evaluated unless
|
||||
its properties are needed. This means that if the block at the location changes before its queried in the program this
|
||||
might result in getting the later state, which might not be desired. Consider the following example:
|
||||
|
||||
<pre>set(10,10,10,'stone');
|
||||
scan(10,10,10,0,0,0, b = _);
|
||||
set(10,10,10,'air');
|
||||
print(b); // 'air', block was remembered 'lazily', and evaluated by `print`, when it was already set to air
|
||||
set(10,10,10,'stone');
|
||||
scan(10,10,10,0,0,0, b = block(_));
|
||||
set(10,10,10,'air');
|
||||
print(b); // 'stone', block was evaluated 'eagerly' but call to `block`
|
||||
</pre>
|
||||
|
||||
## World Manipulation
|
||||
|
||||
All the functions below can be used with block value, queried with coord triple, or 3-long list. All `pos` in the
|
||||
functions referenced below refer to either method of passing block position.
|
||||
|
||||
### `set(pos, block, property?, value?, ...)`
|
||||
|
||||
First part of the `set` function is either a coord triple, list of three numbers, or other block with coordinates.
|
||||
Second part, `block` is either block value as a result of `block()` function string value indicating the block name,
|
||||
and optional `property - value` pairs for extra block properties. If `block` is specified only by name, then if a
|
||||
destination block is the same the `set` operation is skipped, otherwise is executed, for other potential extra
|
||||
properties.
|
||||
|
||||
The returned value is either the block state that has been set, or `false` if block setting was skipped
|
||||
|
||||
<pre>
|
||||
set(0,5,0,'bedrock') => bedrock
|
||||
set(l(0,5,0), 'bedrock') => bedrock
|
||||
set(block(0,5,0), 'bedrock') => bedrock
|
||||
scan(0,5,0,0,0,0,set(_,'bedrock')) => 1
|
||||
set(pos(players()), 'bedrock') => bedrock
|
||||
set(0,0,0,'bedrock') => 0 // or 1 in overworlds generated in 1.8 and before
|
||||
scan(0,100,0,20,20,20,set(_,'glass'))
|
||||
// filling the area with glass
|
||||
scan(0,100,0,20,20,20,set(_,block('glass')))
|
||||
// little bit faster due to internal caching of block state selectors
|
||||
b = block('glass'); scan(0,100,0,20,20,20,set(_,b))
|
||||
// yet another option, skips all parsing
|
||||
set(x,y,z,'iron_trapdoor') // sets bottom iron trapdoor
|
||||
set(x,y,z,'iron_trapdoor[half=top]') // Incorrect. sets bottom iron trapdoor - no parsing of properties
|
||||
set(x,y,z,'iron_trapdoor','half','top') // correct - top trapdoor
|
||||
set(x,y,z, block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
|
||||
set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') // extra block data
|
||||
</pre>
|
||||
|
||||
### `without_updates(expr)`
|
||||
|
||||
Evaluates subexpression without causing updates when blocks change in the world
|
||||
|
||||
Consider following scenario: We would like to generate a bunch of terrain in a flat world following a perling noise
|
||||
generator. The following code causes cascading effect as blocks placed on chunk borders will cause other chunks to get
|
||||
loaded to full, thus generated:
|
||||
|
||||
<pre>
|
||||
__config() -> m(l('scope', 'global'));
|
||||
__on_chunk_generated(x, z) -> (
|
||||
scan(x,0,z,0,0,0,15,15,15,
|
||||
if (perlin(_x/16, _y/8, _z/16) > _y/16,
|
||||
set(_, 'black_stained_glass');
|
||||
)
|
||||
)
|
||||
)
|
||||
</pre>
|
||||
|
||||
The following addition resolves this issue, by not allowing block updates pass chunk borders:
|
||||
|
||||
<pre>
|
||||
__config() -> m(l('scope', 'global'));
|
||||
__on_chunk_generated(x, z) -> (
|
||||
without_updates(
|
||||
scan(x,0,z,0,0,0,15,15,15,
|
||||
if (perlin(_x/16, _y/8, _z/16) > _y/16,
|
||||
set(_, 'black_stained_glass');
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
</pre>
|
||||
|
||||
### `place_item(item, pos, facing?, sneak?)`
|
||||
|
||||
Places a given item in the world like it was placed by a player. Item names are default minecraft item name,
|
||||
less the minecraft prefix. Default facing is 'up', but there are other options: 'down', 'north', 'east', 'south',
|
||||
'west', but also there are other secondary directions important for placement of blocks like stairs, doors, etc.
|
||||
Try experiment with options like 'north-up' which is placed facing north with cursor pointing to the upper part of the
|
||||
block, or 'up-north', which means a block placed facing up (player looking down) and placed smidge away of the block
|
||||
center towards north. Optional sneak is a boolean indicating if a player would be sneaking while placing the
|
||||
block - this option only affects placement of chests and scaffolding at the moment. Returns true if placement was
|
||||
successful, false otherwise.
|
||||
|
||||
<pre>
|
||||
place_item('stone',x,y,z) // places a stone block on x,y,z block
|
||||
place_item('piston,x,y,z,'down') // places a piston facing down
|
||||
place_item('carrot',x,y,z) // attempts to plant a carrot plant. Returns true if could place carrots at that position.
|
||||
</pre>
|
||||
|
||||
### `set_poi(pos, type, occupancy?)`
|
||||
|
||||
Sets a Point of Interest (POI) of a specified type with optional custom occupancy. By default new POIs are not occupied.
|
||||
If type is `null`, POI at position is removed. In any case, previous POI is also removed. Available POI types are:
|
||||
|
||||
* `'unemployed', 'armorer', 'butcher', 'cartographer', 'cleric', 'farmer', 'fisherman', 'fletcher', 'leatherworker', 'librarian', 'mason', 'nitwit', 'shepherd', 'toolsmith', 'weaponsmith', 'home', 'meeting', 'beehive', 'bee_nest', 'nether_portal'`
|
||||
|
||||
Interestingly, `unemployed`, and `nitwit` are not used in the game, meaning, they could be used as permanent spatial
|
||||
markers for scarpet apps. `meeting` is the only one with increased max occupancy of 32.
|
||||
|
||||
### `set_biome(pos, biome_name)`
|
||||
|
||||
changes biome at that block position.
|
||||
|
||||
### `update(pos)`
|
||||
|
||||
Causes a block update at position.
|
||||
|
||||
### `block_tick(pos)`
|
||||
|
||||
Causes a block to tick at position.
|
||||
|
||||
### `random_tick(pos)`
|
||||
|
||||
Causes a random tick at position.
|
||||
|
||||
### `destroy(pos), destroy(pos, -1), destroy(pos, <N>), destroy(pos, tool, nbt?)`
|
||||
|
||||
Destroys the block like it was mined by a player. Add -1 for silk touch, and positive number for fortune level.
|
||||
If tool is specified, and optionally its nbt, it will use that tool and will attempt to mine the block with this tool.
|
||||
If called without item context, this function, unlike harvest, will affect all kinds of blocks. If called with item
|
||||
in context, it will fail to break blocks that cannot be broken by a survival player.
|
||||
|
||||
Without item context it returns `false` if failed to destroy the block and `true` if block breaking was successful.
|
||||
In item context, `true` means that breaking item has no nbt to use, `null` indicating that the tool should be
|
||||
considered broken in process, and `nbt` type value, for a resulting NBT tag on a hypothetical tool. Its up to the
|
||||
programmer to use that nbt to apply it where it belong
|
||||
|
||||
Here is a sample code that can be used to mine blocks using items in player inventory, without using player context
|
||||
for mining. Obviously, in this case the use of `harvest` would be much more applicable:
|
||||
|
||||
<pre>
|
||||
mine(x,y,z) ->
|
||||
(
|
||||
p = player();
|
||||
slot = p~'selected_slot';
|
||||
item_tuple = inventory_get(p, slot);
|
||||
if (!item_tuple, destroy(x,y,z,'air'); return()); // empty hand, just break with 'air'
|
||||
l(item, count, tag) = item_tuple;
|
||||
tag_back = destroy(x,y,z, item, tag);
|
||||
if (tag_back == false, // failed to break the item
|
||||
return(tag_back)
|
||||
);
|
||||
if (tag_back == true, // block broke, tool has no tag
|
||||
return(tag_back)
|
||||
);
|
||||
if (tag_back == null, //item broke
|
||||
delete(tag:'Damage');
|
||||
inventory_set(p, slot, count-1, item, tag);
|
||||
return(tag_back)
|
||||
);
|
||||
if (type(tag_back) == 'nbt', // item didn't break, here is the effective nbt
|
||||
inventory_set(p, slot, count, item, tag_back);
|
||||
return(tag_back)
|
||||
);
|
||||
print('How did we get here?');
|
||||
)
|
||||
</pre>
|
||||
|
||||
### `harvest(player, pos)`
|
||||
|
||||
Causes a block to be harvested by a specified player entity. Honors player item enchantments, as well as damages the
|
||||
tool if applicable. If the entity is not a valid player, no block gets destroyed. If a player is not allowed to break
|
||||
that block, a block doesn't get destroyed either.
|
||||
|
||||
## Block and World querying
|
||||
|
||||
### `pos(block), pos(entity)`
|
||||
|
||||
Returns a triple of coordinates of a specified block or entity. Technically entities are queried with `query` function
|
||||
and the same can be achieved with `query(entity,'pos')`, but for simplicity `pos` allows to pass all positional objects.
|
||||
|
||||
<pre>
|
||||
pos(block(0,5,0)) => l(0,5,0)
|
||||
pos(players()) => l(12.3, 45.6, 32.05)
|
||||
pos(block('stone')) => Error: Cannot fetch position of an unrealized block
|
||||
</pre>
|
||||
|
||||
### `pos_offset(pos, direction, amount?)`
|
||||
|
||||
Returns a coords triple that is offset in a specified `direction` by `amount` of blocks. The default offset amount is
|
||||
1 block. To offset into opposite facing, use negative numbers for the `amount`.
|
||||
|
||||
<pre>
|
||||
pos_offset(block(0,5,0), 'up', 2) => l(0,7,0)
|
||||
pos_offset(l(0,5,0), 'up', -2 ) => l(0,3,0)
|
||||
</pre>
|
||||
|
||||
### `block_properties(pos)`
|
||||
|
||||
Returns a list of available block properties for a particular block. If a block has no properties, returns an empty list.
|
||||
|
||||
### `property(pos, name)`
|
||||
|
||||
Returns property of block at `pos`, or specified by `block` argument. If a block doesn't have that property, `null`
|
||||
value is returned. Returned values are always strings. It is expected from the user to know what to expect and convert
|
||||
values to numbers using `number()` function or booleans using `bool()` function.
|
||||
|
||||
<pre>
|
||||
set(x,y,z,'iron_trapdoor','half','top'); property(x,y,z,'half') => top
|
||||
set(x,y,z,'air'); property(x,y,z,'half') => null
|
||||
property(block('iron_trapdoor[half=top]'),'half') => top
|
||||
property(block('iron_trapdoor[half=top]'),'powered') => false
|
||||
bool(property(block('iron_trapdoor[half=top]'),'powered')) => 0
|
||||
</pre>
|
||||
|
||||
### `block_data(pos)`
|
||||
|
||||
Return NBT string associated with specific location, or null if the block does not carry block data. Can be currently
|
||||
used to match specific information from it, or use it to copy to another block
|
||||
|
||||
<pre> block_data(x,y,z) => '{TransferCooldown:0,x:450,y:68, ... }'
|
||||
</pre>
|
||||
|
||||
### `poi(pos), poi(pos, radius), poi(pos, radius, status)`
|
||||
|
||||
Queries a POI (Point of Interest) at a given position, returning `null` if none is found, or tuple of poi type and its
|
||||
occupancy load. With optional `radius` and `status`, returns a list of POIs around `pos` within a given `radius`.
|
||||
If `status` is specified (either 'available', or 'occupied') returns only POIs with that status. The return format is
|
||||
again, poi type, occupancy load, and extra tripple of coordinates.
|
||||
|
||||
Querying for POIs using the radius is intended use of POI mechanics and ability of accessing individual POIs
|
||||
via `poi(pos)` in only provided for completness.
|
||||
|
||||
<pre>
|
||||
poi(x,y,z) => null // nothing set at position
|
||||
poi(x,y,z) => ['meeting',3] // its a bell-type meeting point occupied by 3 villagers
|
||||
poi(x,y,z,5) => [] // nothing around
|
||||
poi(x,y,z,5) => [['nether_portal',0,[7,8,9]],['nether_portal',0,[7,9,9]]] // two portal blocks in the range
|
||||
</pre>
|
||||
|
||||
### `biome(pos)`
|
||||
|
||||
returns biome at that block position.
|
||||
|
||||
### `solid(pos)`
|
||||
|
||||
Boolean function, true if the block is solid
|
||||
|
||||
### `air(pos)`
|
||||
|
||||
Boolean function, true if a block is air.... or cave air... or void air.... or any other air they come up with.
|
||||
|
||||
### `liquid(pos)`
|
||||
|
||||
Boolean function, true if the block is liquid, or liquidlogged
|
||||
|
||||
### `flammable(pos)`
|
||||
|
||||
Boolean function, true if the block is flammable
|
||||
|
||||
### `transparent(pos)`
|
||||
|
||||
Boolean function, true if the block is transparent
|
||||
|
||||
### `opacity(pos)`
|
||||
|
||||
Numeric, returning opacity level of a block
|
||||
|
||||
### `blocks_daylight(pos)`
|
||||
|
||||
Boolean function, true if the block blocks daylight
|
||||
|
||||
### `emitted_light(pos)`
|
||||
|
||||
Numeric, returning light level emitted from block
|
||||
|
||||
### `light(pos)`
|
||||
|
||||
Integer function, returning total light level at position
|
||||
|
||||
### `block_light(pos)`
|
||||
|
||||
Integer function, returning block light at position. From torches and other light sources.
|
||||
|
||||
### `sky_light(pos)`
|
||||
|
||||
Numeric function, returning sky light at position. From the sky access.
|
||||
|
||||
### `see_sky(pos)`
|
||||
|
||||
Boolean function, returning true if the block can see sky.
|
||||
|
||||
### `hardness(pos)`
|
||||
|
||||
Numeric function, indicating hardness of a block.
|
||||
|
||||
### `blast_resistance(pos)`
|
||||
|
||||
Numeric function, indicating blast_resistance of a block.
|
||||
|
||||
### `in_slime_chunk(pos)`
|
||||
|
||||
Boolean indicating if the given block position is in a slime chunk.
|
||||
|
||||
### `top(type, pos)`
|
||||
|
||||
Returns the Y value of the topmost block at given x, z coords (y value of a block is not important), according to the
|
||||
heightmap specified by `type`. Valid options are:
|
||||
|
||||
* `light`: topmost light blocking block (1.13 only)
|
||||
* `motion`: topmost motion blocking block
|
||||
* `terrain`: topmost motion blocking block except leaves
|
||||
* `ocean_floor`: topmost non-water block
|
||||
* `surface`: topmost surface block
|
||||
|
||||
<pre>
|
||||
top('motion', x, y, z) => 63
|
||||
top('ocean_floor', x, y, z) => 41
|
||||
</pre>
|
||||
|
||||
### `loaded(pos)`
|
||||
|
||||
Boolean function, true if the block is accessible for the game mechanics. Normally `scarpet` doesn't check if operates
|
||||
on loaded area - the game will automatically load missing blocks. We see this as advantage. Vanilla `fill/clone`
|
||||
commands only check the specified corners for loadness.
|
||||
|
||||
To check if block is truly loaded, I mean in memory, use `generation_status(x) != null`, as chunks can still be loaded
|
||||
outside of the playable area, just are not used any of the game mechanics processes.
|
||||
|
||||
<pre>
|
||||
loaded(pos(players())) => 1
|
||||
loaded(100000,100,1000000) => 0
|
||||
</pre>
|
||||
|
||||
### `(Deprecated) loaded_ep(pos)`
|
||||
|
||||
Boolean function, true if the block is loaded and entity processing, as per 1.13.2
|
||||
|
||||
Deprecated as of scarpet 1.6, use `loaded_status(x) > 0`, or just `loaded(x)` with the same effect
|
||||
|
||||
### `loaded_status(pos)`
|
||||
|
||||
Returns loaded status as per new 1.14 chunk ticket system, 0 for inaccessible, 1 for border chunk, 2 for ticking,
|
||||
3 for entity ticking
|
||||
|
||||
### `generation_status(pos), generation_status(pos, true)`
|
||||
|
||||
Returns generation status as per new 1.14 chunk ticket system. Can return any value from several available but chunks
|
||||
can only be valid in a few states: `full`, `features`, `liquid_carvers`, and `structure_starts`. Returns `null`
|
||||
if the chunk is not in memory unless called with optional `true`.
|
||||
|
||||
### `structures(pos), structures(pos, structure_name)`
|
||||
|
||||
Returns structure information for a given block position. Note that structure information is the same for all the
|
||||
blocks from the same chunk. `structures` function can be called with a block, or a block and a structure name. In
|
||||
the first case it returns a map of structures at a given position, keyed by structure name, with values indicating
|
||||
the bounding box of the structure - a pair of two 3-value coords (see examples). When called with an extra structure
|
||||
name, returns list of components for that structure, with their name, direction and two sets of coordinates
|
||||
indicating the bounding box of the structure piece.
|
||||
|
||||
### `structure_references(pos), structure_references(pos, structure_name)`
|
||||
|
||||
Returns structure information that a chunk with a given block position is part of. `structure_references` function
|
||||
can be called with a block, or a block and a structure name. In the first case it returns a list of structure names
|
||||
that give chunk belongs to. When called with an extra structure name, returns list of positions pointing to the
|
||||
lowest block position in chunks that hold structure starts for these structures. You can query that chunk structures
|
||||
then to get its bounding boxes.
|
||||
|
||||
### `set_structure(pos, structure_name), set_structure(pos, structure_name, null)`
|
||||
|
||||
Creates or removes structure information of a structure associated with a chunk of `pos`. Unlike `plop`, blocks are
|
||||
not placed in the world, only structure information is set. For the game this is a fully functional structure even
|
||||
if blocks are not set. To remove structure a given point is in, use `structure_references` to find where current
|
||||
structure starts.
|
||||
|
||||
### `suffocates(pos)`
|
||||
|
||||
Boolean function, true if the block causes suffocation.
|
||||
|
||||
### `power(pos)`
|
||||
|
||||
Numeric function, returning redstone power level at position.
|
||||
|
||||
### `ticks_randomly(pos)`
|
||||
|
||||
Boolean function, true if the block ticks randomly.
|
||||
|
||||
### `blocks_movement(pos)`
|
||||
|
||||
Boolean function, true if block at position blocks movement.
|
||||
|
||||
### `block_sound(pos)`
|
||||
|
||||
Returns the name of sound type made by the block at position. One of:
|
||||
|
||||
* `wood`
|
||||
* `gravel`
|
||||
* `grass`
|
||||
* `stone`
|
||||
* `metal`
|
||||
* `glass`
|
||||
* `wool`
|
||||
* `sand`
|
||||
* `snow`
|
||||
* `ladder`
|
||||
* `anvil`
|
||||
* `slime`
|
||||
* `sea_grass`
|
||||
* `coral`
|
||||
|
||||
### `material(pos)`
|
||||
|
||||
Returns the name of material of the block at position. very useful to target a group of blocks. One of:
|
||||
|
||||
* `air`
|
||||
* `void`
|
||||
* `portal`
|
||||
* `carpet`
|
||||
* `plant`
|
||||
* `water_plant`
|
||||
* `vine`
|
||||
* `sea_grass`
|
||||
* `water`
|
||||
* `bubble_column`
|
||||
* `lava`
|
||||
* `snow_layer`
|
||||
* `fire`
|
||||
* `redstone_bits`
|
||||
* `cobweb`
|
||||
* `redstone_lamp`
|
||||
* `clay`
|
||||
* `dirt`
|
||||
* `grass`
|
||||
* `packed_ice`
|
||||
* `sand`
|
||||
* `sponge`
|
||||
* `wood`
|
||||
* `wool`
|
||||
* `tnt`
|
||||
* `leaves`
|
||||
* `glass`
|
||||
* `ice`
|
||||
* `cactus`
|
||||
* `stone`
|
||||
* `iron`
|
||||
* `snow`
|
||||
* `anvil`
|
||||
* `barrier`
|
||||
* `piston`
|
||||
* `coral`
|
||||
* `gourd`
|
||||
* `dragon_egg`
|
||||
* `cake`
|
||||
|
||||
### `map_colour(pos)`
|
||||
|
||||
Returns the map colour of a block at position. One of:
|
||||
|
||||
* `air`
|
||||
* `grass`
|
||||
* `sand`
|
||||
* `wool`
|
||||
* `tnt`
|
||||
* `ice`
|
||||
* `iron`
|
||||
* `foliage`
|
||||
* `snow`
|
||||
* `clay`
|
||||
* `dirt`
|
||||
* `stone`
|
||||
* `water`
|
||||
* `wood`
|
||||
* `quartz`
|
||||
* `adobe`
|
||||
* `magenta`
|
||||
* `light_blue`
|
||||
* `yellow`
|
||||
* `lime`
|
||||
* `pink`
|
||||
* `gray`
|
||||
* `light_gray`
|
||||
* `cyan`
|
||||
* `purple`
|
||||
* `blue`
|
||||
* `brown`
|
||||
* `green`
|
||||
* `red`
|
||||
* `black`
|
||||
* `gold`
|
||||
* `diamond`
|
||||
* `lapis`
|
||||
* `emerald`
|
||||
* `obsidian`
|
||||
* `netherrack`
|
||||
* `white_terracotta`
|
||||
* `orange_terracotta`
|
||||
* `magenta_terracotta`
|
||||
* `light_blue_terracotta`
|
||||
* `yellow_terracotta`
|
||||
* `lime_terracotta`
|
||||
* `pink_terracotta`
|
||||
* `gray_terracotta`
|
||||
* `light_gray_terracotta`
|
||||
* `cyan_terracotta`
|
||||
* `purple_terracotta`
|
||||
* `blue_terracotta`
|
||||
* `brown_terracotta`
|
||||
* `green_terracotta`
|
||||
* `red_terracotta`
|
||||
* `black_terracotta`
|
||||
@@ -0,0 +1,471 @@
|
||||
# Entity API
|
||||
|
||||
## Entity Selection
|
||||
|
||||
Entities have to be fetched before using them. Entities can also change their state between calls to the script if
|
||||
game happens either in between separate calls to the programs, or if the program calls `game_tick` on its own.
|
||||
In this case - entities would need to be re-fetched, or the code should account for entities getting dead.
|
||||
|
||||
### `player(), player(type), player(name)`
|
||||
|
||||
With no arguments, it returns the calling player or the player closest to the caller. Note that the main context
|
||||
will receive `p` variable pointing to this player. With `type` or `name` specified it will try first to match a type,
|
||||
returning a list of players matching a type, and if this fails, will assume its player name query retuning player with
|
||||
that name, or `null` if no player was found. With `'all'`, list of all players in the game, in all dimensions, so end
|
||||
user needs to be cautious, that you might be referring to wrong blocks and entities around the player in question.
|
||||
With `type = '*'` it returns all players in caller dimension, `'survival'` returns all survival and adventure players,
|
||||
`'creative'` returns all creative players, `'spectating'` returns all spectating players, and `'!spectating'`,
|
||||
all not-spectating players. If all fails, with `name`, the player in question, if is logged in.
|
||||
|
||||
### `entity_id(uuid), entity_id(id)`
|
||||
|
||||
Fetching entities wither by their ID obtained via `entity ~ 'id'`, which is unique for a dimension and current world
|
||||
run, or by UUID, obtained via `entity ~ 'uuid'`. It returns null if no such entity is found. Safer way to 'store'
|
||||
entities between calls, as missing entities will be returning `null`. Both calls using UUID or numerical ID are `O(1)`,
|
||||
but obviously using UUIDs takes more memory and compute.
|
||||
|
||||
### `entity_list(type)`
|
||||
|
||||
Returns global lists of entities in the current dimension of a specified type. Currently the following
|
||||
selectors are available:
|
||||
|
||||
* `*`: all entites
|
||||
* `living`
|
||||
* `items`
|
||||
* `players`
|
||||
* `!players`
|
||||
|
||||
### `entity_area(type, cx, cy, cz, dx, dy, dz)`
|
||||
|
||||
Returns entities of a specified type in an area centered on `cx, cy, cz` and at most `dx, dy, dz` blocks away from
|
||||
the center point. Uses same selectors as `entities_list`
|
||||
|
||||
### `entity_selector(selector)`
|
||||
|
||||
Returns entities satisfying given vanilla entity selector. Most complex among all the methods of selecting entities,
|
||||
but the most capable. Selectors are cached so should be as fast as other methods of selecting entities.
|
||||
|
||||
### `spawn(name, pos, nbt?)`
|
||||
|
||||
Spawns and places an entity in world, like `/summon` vanilla command. Requires a position to spawn, and optional
|
||||
extra nbt data to merge with the entity. What makes it different from calling `run('summon ...')`, is the fact that
|
||||
you get the entity back as a return value, which is swell.
|
||||
|
||||
## Entity Manipulation
|
||||
|
||||
Unlike with blocks, that use plethora of vastly different querying functions, entities are queried with `query`
|
||||
function and altered via `modify` function. Type of information needed or values to be modified are different for
|
||||
each call.
|
||||
|
||||
Using `~` (in) operator is an alias for `query`. Especially useful if a statement has no arguments,
|
||||
which in this case can be radically simplified
|
||||
|
||||
<pre>
|
||||
query(p, 'name') <=> p ~ 'name' // much shorter and cleaner
|
||||
query(p, 'holds', 'offhand') <=> p ~ l('holds', 'offhand') // not really but can be done
|
||||
</pre>
|
||||
|
||||
### `query(e,'removed')`
|
||||
|
||||
Boolean. True if the entity is removed
|
||||
|
||||
### `query(e,'id')`
|
||||
|
||||
Returns numerical id of the entity. Most efficient way to keep track of entites in a script.
|
||||
Ids are only unique within current game session (ids are not preserved between restarts),
|
||||
and dimension (each dimension has its own ids which can overlap.
|
||||
|
||||
### `query(e,'uuid')`
|
||||
|
||||
Returns UUID (unique id) of the entity. Can be used to access entities with the other vanilla commands and
|
||||
remains unique regardless of the dimension, and is preserved between game restarts. Apparently players cannot be
|
||||
accessed via UUID, but name instead.
|
||||
|
||||
<pre>
|
||||
map(entities_area('*',x,y,z,30,30,30),run('kill '+query(_,'id'))) // doesn't kill the player
|
||||
</pre>
|
||||
|
||||
### `query(e,'pos')`
|
||||
|
||||
Triple of entity position
|
||||
|
||||
### `query(e,'location')`
|
||||
|
||||
Quin-tuple of entity position (x, y, and z coords), and rotation (yaw, pitch)
|
||||
|
||||
### `query(e,'x'), query(e,'y'), query(e,'z')`
|
||||
|
||||
Respective entity coordinate
|
||||
|
||||
### `query(e,'pitch'), query(e,'yaw')`
|
||||
|
||||
Pitch and Yaw or where entity is looking.
|
||||
|
||||
### `query(e,'look')`
|
||||
|
||||
Returns a 3d vector where the entity is looking.
|
||||
|
||||
### `query(e,'motion')`
|
||||
|
||||
Triple of entity motion vector, `l(motion_x, motion_y, motion_z)`
|
||||
|
||||
### `query(e,'motion_x'), query(e,'motion_y'), query(e,'motion_z')`
|
||||
|
||||
Respective component of the motion vector
|
||||
|
||||
### `query(e,'name'), query(e,'custom_name'), query(e,'type')`
|
||||
|
||||
String of entity name
|
||||
|
||||
<pre>query(e,'name') => Leatherworker
|
||||
query(e,'custom_name') => null
|
||||
query(e,'type') => villager
|
||||
</pre>
|
||||
|
||||
### `query(e,'is_riding')`
|
||||
|
||||
Boolean. True if riding another entity.
|
||||
|
||||
### `query(e,'is_ridden')`
|
||||
|
||||
Boolean. True if another entity is riding it.
|
||||
|
||||
### `query(e,'passengers')`
|
||||
|
||||
List of entities riding the entity.
|
||||
|
||||
### `query(e,'mount')`
|
||||
|
||||
Entity that `e` rides.
|
||||
|
||||
### `query(e,'tags')`
|
||||
|
||||
List of entity tags.
|
||||
|
||||
### `query(e,'has_tags',tag)`
|
||||
|
||||
Boolean, True if the entity is marked with `tag`.
|
||||
|
||||
### `query(e,'is_burning')`
|
||||
|
||||
Boolean, True if the entity is burning.
|
||||
|
||||
### `query(e,'fire')`
|
||||
|
||||
Number of remaining ticks of being on fire.
|
||||
|
||||
### `query(e,'silent')`
|
||||
|
||||
Boolean, True if the entity is silent.
|
||||
|
||||
### `query(e,'gravity')`
|
||||
|
||||
Boolean, True if the entity is affected by gravity, like most entities do.
|
||||
|
||||
### `query(e,'immune_to_fire')`
|
||||
|
||||
Boolean, True if the entity is immune to fire.
|
||||
|
||||
### `query(e,'dimension')`
|
||||
|
||||
Name of the dimension entity is in.
|
||||
|
||||
### `query(e,'height')`
|
||||
|
||||
Height of the entity.
|
||||
|
||||
### `query(e,'width')`
|
||||
|
||||
Width of the entity.
|
||||
|
||||
### `query(e,'eye_height')`
|
||||
|
||||
Eye height of the entity.
|
||||
|
||||
### `query(e,'age')`
|
||||
|
||||
Age, in ticks, of the entity, i.e. how long it existed.
|
||||
|
||||
### `query(e,'breeding_age')`
|
||||
|
||||
Breeding age of passive entity, in ticks. If negative it it time to adulthood, if positive, breeding cooldown
|
||||
|
||||
### `query(e,'despawn_timer')`
|
||||
|
||||
For living entities - the number of ticks they fall outside of immediate player presence.
|
||||
|
||||
### `query(e,'item')`
|
||||
|
||||
The item triple (name, count, nbt) if its an item entity, `null` otherwise
|
||||
|
||||
### `query(e,'count')`
|
||||
|
||||
Number of items in a stack from item entity, `null` otherwise
|
||||
|
||||
### `query(e,'pickup_delay')`
|
||||
|
||||
Retrieves pickup delay timeout for an item entity, `null` otherwise
|
||||
|
||||
### `query(e,'is_baby')`
|
||||
|
||||
Boolean, true if its a baby.
|
||||
|
||||
### `query(e,'target')`
|
||||
|
||||
Returns mob's attack target or null if none or not applicable.
|
||||
|
||||
### `query(e,'home')`
|
||||
|
||||
Returns creature's home position or null if none or not applicable.
|
||||
|
||||
### `query(e,'sneaking')`
|
||||
|
||||
Boolean, true if entity is sneaking.
|
||||
|
||||
### `query(e,'sprinting')`
|
||||
|
||||
Boolean, true if entity is sprinting.
|
||||
|
||||
### `query(e,'swimming')`
|
||||
|
||||
Boolean, true if entity is swimming.
|
||||
|
||||
### `query(e,'jumping')`
|
||||
|
||||
Boolean, true if entity is jumping.
|
||||
|
||||
### `query(e,'gamemode')`
|
||||
|
||||
String with gamemode, or `null` if not a player.
|
||||
|
||||
### `query(e,'gamemode_id')`
|
||||
|
||||
Good'ol gamemode id, or null if not a player.
|
||||
|
||||
### `query(e,'player_type')`
|
||||
|
||||
Returns `null` if the argument is not a player, otherwise:
|
||||
|
||||
* `singleplayer`: for singleplayer game
|
||||
* `multiplayer`: for players on a dedicated server
|
||||
* `lan_host`: for singleplayer owner that opened the game to LAN
|
||||
* `lan_player`: for all other players that connected to a LAN host
|
||||
* `fake`: any carpet-spanwed fake player
|
||||
* `shadow`: any carpet-shadowed real player
|
||||
* `realms`: ?
|
||||
|
||||
### `query(e,'permission_level')`
|
||||
|
||||
Player's permission level, or `null` if not applicable for this entity.
|
||||
|
||||
### `query(e,'effect',name?)`
|
||||
|
||||
Without extra arguments, it returns list of effect active on a living entity. Each entry is a triple of short
|
||||
effect name, amplifier, and remaining duration. With an argument, if the living entity has not that potion active,
|
||||
returns `null`, otherwise return a tuple of amplifier and remaining duration.
|
||||
|
||||
<pre>
|
||||
query(p,'effect') => [[haste, 0, 177], [speed, 0, 177]]
|
||||
query(p,'effect','haste') => [0, 177]
|
||||
query(p,'effect','resistance') => null
|
||||
</pre>
|
||||
|
||||
### `query(e,'health')`
|
||||
|
||||
Number indicating remaining entity health, or `null` if not applicable.
|
||||
|
||||
### `query(e,'holds',slot?)`
|
||||
|
||||
Returns triple of short name, stack count, and NBT of item held in `slot`. Available options for `slot` are:
|
||||
|
||||
* `mainhand`
|
||||
* `offhand`
|
||||
* `head`
|
||||
* `chest`
|
||||
* `legs`
|
||||
* `feet`
|
||||
|
||||
If `slot` is not specified, it defaults to the main hand.
|
||||
|
||||
### `query(e,'selected_slot')`
|
||||
|
||||
Number indicating the selected slot of entity inventory. Currently only applicable to players.
|
||||
|
||||
### `query(e,'facing', order?)`
|
||||
|
||||
Returns where the entity is facing. optional order (number from 0 to 5, and negative), indicating primary directions
|
||||
where entity is looking at. From most prominent (order 0) to opposite (order 5, or -1)
|
||||
|
||||
### `query(e,'trace', reach?, options?...)`
|
||||
|
||||
Returns the result of ray tracing from entity perspective, indicating what it is looking at. Default reach is 4.5
|
||||
blocks (5 for creative players), and by default it traces for blocks and entities, identical to player attack tracing
|
||||
action. This can be customized with `options`, use 'blocks' to trace for blocks, 'liquids' to include liquid blocks
|
||||
as possible results, and 'entities' to trace entities. Any combination of the above is possible. When tracing
|
||||
entities and blocks, blocks will take over the priority even if transparent or non-colliding
|
||||
(aka fighting chickens in tall grass).
|
||||
|
||||
Regardless of the options selected, the result could be `null` if nothing is in reach, entity, if look targets an
|
||||
entity, and block value if block is in reach.
|
||||
|
||||
### `query(e,'nbt',path?)`
|
||||
|
||||
Returns full NBT of the entity. If path is specified, it fetches only that portion of the NBT, that corresponds to the
|
||||
path. For specification of `path` attribute, consult vanilla `/data get entity` command.
|
||||
|
||||
Note that calls to `nbt` are considerably more expensive comparing to other calls in Minecraft API, and should only
|
||||
be used when there is no other option. Returned value is of type `nbt`, which can be further manipulated with nbt
|
||||
type objects via `get, put, has, delete`, so try to use API calls first for that.
|
||||
|
||||
## Entity Modification
|
||||
|
||||
Like with entity querying, entity modifications happen through one function. Most position and movements modifications
|
||||
don't work currently on players as their position is controlled by clients.
|
||||
|
||||
Currently there is no ability to modify NBT directly, but you could always use `run('data modify entity`
|
||||
|
||||
### `modify(e,'remove')`
|
||||
|
||||
Removes (not kills) entity from the game.
|
||||
|
||||
### `modify(e,'kill')`
|
||||
|
||||
Kills the entity.
|
||||
|
||||
### `modify(e, 'pos', x, y, z), modify(e, 'pos', l(x,y,z) )`
|
||||
|
||||
Moves the entity to a specified coords.
|
||||
|
||||
### `modify(e, 'location', x, y, z, yaw, pitch), modify(e, 'location', l(x, y, z, yaw, pitch) )`
|
||||
|
||||
Changes full location vector all at once.
|
||||
|
||||
### `modify(e, 'x', x), modify(e, 'y', y), modify(e, 'z', z)`
|
||||
|
||||
Moves the entity in.... one direction.
|
||||
|
||||
### `modify(e, 'pitch', pitch), modify(e, 'yaw', yaw)`
|
||||
|
||||
Changes entity's pitch or yaw.
|
||||
|
||||
### `modify(e, 'move', x, y, z), modify(e, 'move', l(x,y,z) )`
|
||||
|
||||
Moves the entity by a vector from its current location.
|
||||
|
||||
### `modify(e, 'motion', x, y, z), modify(e, 'motion', l(x,y,z) )`
|
||||
|
||||
Sets the motion vector (where and how much entity is moving).
|
||||
|
||||
### `modify(e, 'motion_z', x), modify(e, 'motion_y', y), modify(e, 'motion_z', z)`
|
||||
|
||||
Sets the corresponding component of the motion vector.
|
||||
|
||||
### `modify(e, 'accelerate', x, y, z), modify(e, 'accelerate', l(x, y, z) )`
|
||||
|
||||
Adds a vector to the motion vector. Most realistic way to apply a force to an entity.
|
||||
|
||||
### `modify(e, 'custom_name'), modify(e, 'custom_name', name )`
|
||||
|
||||
### `modify(e, 'age', number)`
|
||||
|
||||
Modifies entity's internal age counter. Fiddling with this will affect directly behaviours of complex
|
||||
entities, so use it with caution.
|
||||
|
||||
### `modify(e, 'pickup_delay', number)`
|
||||
|
||||
### `modify(e, 'breeding_age', number)`
|
||||
|
||||
### `modify(e, 'despawn_timer', number)`
|
||||
|
||||
Sets a custom despawn timer value.
|
||||
|
||||
### `modify(e, 'dismount')`
|
||||
|
||||
Dismounts riding entity.
|
||||
|
||||
### `modify(e, 'mount', other)`
|
||||
|
||||
Mounts the entity to the `other`.
|
||||
|
||||
### `modify(e, 'drop_passengers')`
|
||||
|
||||
Shakes off all passengers.
|
||||
|
||||
### `modify(e, 'mount_passengers', passenger, ? ...), modify(e, 'mount_passengers', l(passengers) )`
|
||||
|
||||
Mounts on all listed entities on `e`.
|
||||
|
||||
### `modify(e, 'tag', tag, ? ...), modify(e, 'tag', l(tags) )`
|
||||
|
||||
Adds tag / tags to the entity.
|
||||
|
||||
### `modify(e, 'clear_tag', tag, ? ...), modify(e, 'clear_tag', l(tags) )`
|
||||
|
||||
Removes tag from entity.
|
||||
|
||||
### `modify(e, 'talk')`
|
||||
|
||||
Make noises.
|
||||
|
||||
### `modify(e, 'ai', boolean)`
|
||||
|
||||
If called with `false` value, it will disable AI in the mob. `true` will enable it again.
|
||||
|
||||
### `modify(e, 'no_clip', boolean)`
|
||||
|
||||
Sets if the entity obeys any collisions, including collisions with the terrain and basic physics. Not affecting
|
||||
players, since they are controlled client side
|
||||
|
||||
### `modify(e, 'effect', name, duration?, amplifier?, show_particles?, show_icon?)`
|
||||
|
||||
Applies status effect to the living entity. Takes several optional parameters, which default to `0`, `true`
|
||||
and `true`. If no duration is specified, or it is null or 0, the effect is removed.
|
||||
|
||||
### `modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)`
|
||||
|
||||
Sets AI to stay around the home position, within `distance` blocks from it. `distance` defaults to 16 blocks.
|
||||
`null` removes it. _May_ not work fully with mobs that have this AI built in, like Villagers.
|
||||
|
||||
## Entity Events
|
||||
|
||||
There is a number of events that happen to entities that you can attach your own code to in the form of event handlers.
|
||||
The event handler is any function that runs in your package that accepts certain expected parameters, which you can
|
||||
expand with your own arguments. When it comes to the moment when the given command needs to be executed, it does so
|
||||
providing that number of arguments it accepts is equal number of event arguments, and extra arguments passed when
|
||||
defining the callback with `entity_event`
|
||||
|
||||
The following events can be handled by entities.
|
||||
|
||||
* `'on_tick'`: executes every tick right before entity is ticked in the game. Required arguments: `entity`
|
||||
* `'on_death'`: executes once when a living entity dies. Required arguments: `entity, reason`
|
||||
* `'on_removed'`: execute once when an entity is removed. Required arguments: `entity`
|
||||
* `'on_damaged'`: executed every time a living entity is about to receive damage.
|
||||
Required arguments: `entity, amount, source, attacking_entity`
|
||||
|
||||
It doesn't mean that all entity types will have a chance to execute a given event, but entities will not error
|
||||
when you will attach inapplicable event to it.
|
||||
|
||||
### `entity_event(e, event, call_name, args...)`
|
||||
|
||||
Attaches specific function from the current package to be called upon the `event`, with extra `args` curried to the
|
||||
original required arguments for the event handler
|
||||
|
||||
<pre>
|
||||
protect_villager(entity, amount, source, source_entity, healing_player) ->
|
||||
(
|
||||
if(source_entity && source_entity~'type' != 'player',
|
||||
modify(entity, 'health', amount + entity~'health' );
|
||||
particle('end_rod', pos(entity)+l(0,3,0));
|
||||
print(str('%s healed thanks to %s', entity, healing_player))
|
||||
)
|
||||
);
|
||||
__on_player_interacts_with_entity(player, entity, hand) ->
|
||||
(
|
||||
if (entity~'type' == 'villager',
|
||||
entity_event(entity, 'on_damage', 'protect_villager', player~'name')
|
||||
)
|
||||
)
|
||||
</pre>
|
||||
|
||||
In this case this will protect a villager from entity damage (zombies, etc.) except players by granting all the
|
||||
health back to the villager after being harmed.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Scarpet events system
|
||||
|
||||
Provides the ability to execute specific function whenever an event occurs. The functions to be registered need to
|
||||
conform with the arguments to the event specification. When loading module functions, each function that starts
|
||||
with `__on_...` and has the required arguments, will be bound automatically. In case of player specific modules,
|
||||
all player action events will be directed to the appropriate player space, and all tick events will be executed in
|
||||
the global context, so its not a good idea to mix these two, so use either of these, or use commands to call tick
|
||||
events directly, or handle player specific data inside a module. In near future using global events
|
||||
(on_tick, on_chunk_generated) will be not allowed in player scoped apps.
|
||||
|
||||
### Event list
|
||||
|
||||
Here is a list of events that can be handled by scarpet. This list includes prefixes required by modules to autoload
|
||||
them, but you can add any function to any event if it accepts required number of parameters:
|
||||
|
||||
<pre>
|
||||
__on_tick() // can access blocks and entities in the overworld
|
||||
__on_tick_nether() // can access blocks and entities in the nether
|
||||
__on_tick_ender() // can access blocks and entities in the end
|
||||
__on_chunk_generated(x,z) // called after a chunk is promoted to the full chunk,
|
||||
// prodiving lowest x and z coords in the chunk
|
||||
// event will not work with optifine installed in the game
|
||||
// player specific callbacks
|
||||
__on_player_uses_item(player, item_tuple, hand) // right click action
|
||||
__on_player_releases_item(player, item_tuple, hand) // client action (e.g. bow)
|
||||
__on_player_finishes_using_item(player, item_tuple, hand)) // server action (e.g. food), called item is from before it is used.
|
||||
__on_player_clicks_block(player, block, face) // left click attack on a block
|
||||
__on_player_breaks_block(player, block) // called after block is broken (the caller receives previous blockstate)
|
||||
__on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) // player right clicks block with anything
|
||||
__on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block
|
||||
__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
|
||||
__on_player_interacts_with_entity(player, entity, hand)
|
||||
__on_player_attacks_entity(player, entity)
|
||||
__on_player_rides(player, forward, strafe, jumping, sneaking)
|
||||
__on_player_jumps(player)
|
||||
__on_player_deploys_elytra(player)
|
||||
__on_player_wakes_up(player)
|
||||
__on_player_starts_sneaking(player)
|
||||
__on_player_stops_sneaking(player)
|
||||
__on_player_starts_sprinting(player)
|
||||
__on_player_stops_sprinting(player)
|
||||
__on_player_drops_item(player)
|
||||
__on_player_drops_stack(player)
|
||||
__on_statistic(player, category, event, value) // player statistic changes
|
||||
</pre>
|
||||
|
||||
### `/script event` command
|
||||
|
||||
used to display current events and bounded functions. use `add_to` ro register new event, or `remove_from` to
|
||||
unbind a specific function from an event. Function to be bounded to an event needs to have the same number of
|
||||
parameters as the action is attempting to bind to (see list above). All calls in modules loaded via `/script load`
|
||||
that have functions listed above will be automatically bounded and unbounded when script is unloaded.
|
||||
@@ -0,0 +1,116 @@
|
||||
# Inventory and Items API
|
||||
|
||||
## Manipulating inventories of blocks and entities
|
||||
|
||||
Most functions in this category require inventory as the first argument. Inventory could be specified by an entity,
|
||||
or a block, or position (three coordinates) of a potential block with inventory. Player enderchest inventory require
|
||||
two arguments, keyword `'enderchest'`, followed by the player entity argument. If the entity or a block doesn't have
|
||||
an inventory, they typically do nothing and return null.
|
||||
|
||||
Most items returned are in the form of a triple of item name, count, and nbt or the extra data associated with an item.
|
||||
Manipulating of the nbt data can be costly, but retrieving them from the tuple to match other aspects is cheap
|
||||
|
||||
### `stack_limit(item)`
|
||||
|
||||
Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable), 16 (like buckets),
|
||||
or 64 - rest. It is recommended to consult this, as other inventory API functions ignore normal stack limits, and
|
||||
it is up to the programmer to keep it at bay. As of 1.13, game checks for negative numbers and setting an item to
|
||||
negative is the same as empty.
|
||||
|
||||
<pre>
|
||||
stack_limit('wooden_axe') => 1
|
||||
stack_limit('ender_pearl') => 16
|
||||
stack_limit('stone') => 64
|
||||
</pre>
|
||||
|
||||
### `item_category(item)`
|
||||
|
||||
Returns the string representing the category of a given item, like `building_blocks`, `combat`, or `tools`.
|
||||
|
||||
<pre>
|
||||
item_category('wooden_axe') => tools
|
||||
item_category('ender_pearl') => misc
|
||||
item_category('stone') => building_blocks
|
||||
</pre>
|
||||
|
||||
### `inventory_size(inventory)`
|
||||
|
||||
Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't
|
||||
have an inventory
|
||||
|
||||
<pre>
|
||||
inventory_size(player()) => 41
|
||||
inventory_size('enderchest', player()) => 27 // enderchest
|
||||
inventory_size(x,y,z) => 27 // chest
|
||||
inventory_size(block(pos)) => 5 // hopper
|
||||
</pre>
|
||||
|
||||
### `inventory_has_items(inventory)`
|
||||
|
||||
Returns true, if the inventory is not empty, false if it is empty, and null, if its not an inventory.
|
||||
|
||||
<pre> inventory_has_items(player()) => true
|
||||
inventory_has_items(x,y,z) => false // empty chest
|
||||
inventory_has_items(block(pos)) => null // stone
|
||||
</pre>
|
||||
|
||||
### `inventory_get(inventory, slot)`
|
||||
|
||||
Returns the item in the corresponding inventory slot, or null if slot empty or inventory is invalid. You can use
|
||||
negative numbers to indicate slots counted from 'the back'.
|
||||
|
||||
<pre>
|
||||
inventory_get(player(), 0) => null // nothing in first hotbar slot
|
||||
inventory_get(x,y,z, 5) => ['stone', 1, {}]
|
||||
inventory_get(player(), -1) => ['diamond_pickaxe', 1, {Damage:4}] // slightly damaged diamond pick in the offhand
|
||||
</pre>
|
||||
|
||||
### `inventory_set(inventory, slot, count, item?, nbt?)`
|
||||
|
||||
Modifies or sets a stack in inventory. specify count 0 to empty the slot. If item is not specified, keeps existing
|
||||
item, just modifies the count. If item is provided - replaces current item. If nbt is provided - adds a tag to the
|
||||
stack at slot. Returns previous stack in that slot.
|
||||
|
||||
<pre>
|
||||
inventory_set(player(), 0, 0) => ['stone', 64, {}] // player had a stack of stone in first hotbar slot
|
||||
inventory_set(player(), 0, 6) => ['diamond', 64, {}] // changed stack of diamonds in player slot to 6
|
||||
inventory_set(player(), 0, 1, 'diamond_axe','{Damage:5}') => null //added slightly damaged pick to first player slot
|
||||
</pre>
|
||||
|
||||
### `inventory_find(inventory, item, start_slot?, ), inventory_find(inventory, null, start_slot?)`
|
||||
|
||||
Finds the first slot with a corresponding item in the inventory, or if queried with null: the first empty slot.
|
||||
Returns slot number if found, or null otherwise. Optional start_slot argument allows to skip all preceeding slots
|
||||
allowing for efficient (so not slot-by-slot) inventory search for items.
|
||||
|
||||
<pre>
|
||||
inventory_find(player(), 'stone') => 0 // player has stone in first hotbar slot
|
||||
inventory_find(player(), null) => null // player's inventory has no empty spot
|
||||
while( (slot = inventory_find(p, 'diamond', slot)) != null, 41, drop_item(p, slot) )
|
||||
// spits all diamonds from player inventory wherever they are
|
||||
inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
|
||||
</pre>
|
||||
|
||||
### `inventory_remove(inventory, item, amount?)`
|
||||
|
||||
Removes amount (defaults to 1) of item from inventory. If the inventory doesn't have the defined amount, nothing
|
||||
happens, otherwise the given amount of items is removed wherever they are in the inventory. Returns boolean
|
||||
whether the removal operation was successful. Easiest way to remove a specific item from player inventory
|
||||
without specifying the slot.
|
||||
|
||||
<pre>
|
||||
inventory_remove(player(), 'diamond') => 1 // removed diamond from player inventory
|
||||
inventory_remove(player(), 'diamond', 100) => 0 // player doesn't have 100 diamonds, nothing happened
|
||||
</pre>
|
||||
|
||||
### `drop_item(inventory, slot, amount?, )`
|
||||
|
||||
Drops the items from indicated inventory slot, like player that Q's an item or villager, that exchanges food.
|
||||
You can Q items from block inventories as well. default amount is 0 - which is all from the slot.
|
||||
NOTE: hoppers are quick enough to pick all the queued items from their inventory anyways.
|
||||
Returns size of the actual dropped items.
|
||||
|
||||
<pre>
|
||||
inventory_drop(player(), 0, 1) => 1 // Q's one item on the ground
|
||||
inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
|
||||
</pre>
|
||||
@@ -0,0 +1,13 @@
|
||||
# Minecraft specific API and `scarpet` language add-ons and commands
|
||||
|
||||
Here is the gist of the Minecraft related functions. Otherwise the CarpetScript could live without Minecraft.
|
||||
|
||||
## Dimension warning
|
||||
|
||||
One note, which is important is that most of the calls for entities and blocks would refer to the current
|
||||
dimension of the caller, meaning, that if we for example list all the players using `player('all')` function,
|
||||
if a player is in the other dimension, calls to entities and blocks around that player would point incorrectly.
|
||||
Moreover, running commandblocks in the spawn chunks would mean that commands will always refer to the overworld
|
||||
blocks and entities. In case you would want to run commands across all dimensions, just run three of them,
|
||||
using `/execute in overworld/the_nether/the_end run script run ...` and query players using `player('*')`,
|
||||
which only returns players in current dimension, or use `in_dimension(expr)` function.
|
||||
@@ -0,0 +1,185 @@
|
||||
# `/script run` command
|
||||
|
||||
Primary way to input commands. The command executes in the context, position, and dimension of the executing player,
|
||||
commandblock, etc... The command receives 4 variables, `x`, `y`, `z` and `p` indicating position and
|
||||
the executing entity of the command. You will receive tab completion suggestions as you type your code suggesting
|
||||
functions and global variables. It is advisable to use `/execute in ... at ... as ... run script run ...` or similar,
|
||||
to simulate running commands in a different scope.
|
||||
|
||||
# `/script load / unload <app> (global?)`, `/script in <app>` commands
|
||||
|
||||
`load / unload` commands allow for very conventient way of writing your code, providing it to the game and
|
||||
distribute with your worlds without the need of use of commandblocks. Just place your scarpet code in the
|
||||
/scripts folder of your world files and make sure it ends with `.sc` extension. The good thing about editing that
|
||||
code is that you can no only use normal editing without the need of marking of newlines,
|
||||
but you can also use comments in your code.
|
||||
|
||||
a comment is anything that starts with a double slash, and continues to the end of the line:
|
||||
|
||||
<pre>
|
||||
foo = 1;
|
||||
//This is a comment
|
||||
bar = 2;
|
||||
// This never worked, so I commented it out
|
||||
// baz = foo()
|
||||
</pre>
|
||||
|
||||
### `/script load/unload <app> (?global)`
|
||||
|
||||
Loading operation will load that script code from disk and execute it right away. You would probably use it to load
|
||||
some stored procedures to be used for later. To reload the module, just type `/script load` again. Reloading removes
|
||||
all the current global state (globals and functions) that were added later by the module.
|
||||
|
||||
Loading a module, if it contains a `__command()` method, will attempt to registed a command with that app name,
|
||||
and register all public (no underscore) functions available in the module as subcommands. It will also bind specific
|
||||
events to the event system (check Events section for details).
|
||||
|
||||
Loaded apps have the ability to store and load external files, especially their persistent tag state. For that
|
||||
check `load_app_data` and `store_app_data` functions.
|
||||
|
||||
If an app defines `__config` method, and that method returns a map, it will be used to apply custom settings
|
||||
for this app. Currently the following options are supported:
|
||||
|
||||
* `scope`: default scope for global variables for the app, Default is 'player' which means that globals and defined
|
||||
functions will be unique for each player so that apps for each player will run in isolation. This is useful in
|
||||
tool-like applications. Another option is 'global' which shares global state for all runs on the server - applicable
|
||||
to 'block' like solutions, where custom behaviours are applied to blocks.
|
||||
* `stay_loaded`: defaults to false. If true, and `/carpet scriptsAutoload` is turned on, the following apps will
|
||||
stay loaded after startup. Otherwise, after reading a code, and fetching the config, server will drop them down.
|
||||
This is to allow to store multiple apps on the server/world and selectively decide which one should be running at
|
||||
startup. WARNING: all apps will run once at startup anyways, so be aware that their actions that are called
|
||||
statically, will be performed once anyways.
|
||||
|
||||
Unloading an app removes all of its state from the game, disables commands, and removes bounded events, and
|
||||
saves its global state. If more cleanup is needed, one can define an `__on_close()` function which will be
|
||||
executed when the module is unloaded, or server is closing, or crashing. However, there is no need to do that
|
||||
explicitly for the things mentioned in the previous statement.
|
||||
|
||||
Scripts can be loaded in shared(global) and player mode. Default is player, so all globals and stored functions are
|
||||
individual for each player, meaning scripts don't need to worry about making sure they store some intermittent
|
||||
data for each player independently. In global mode - all global values and stored functions are shared among all
|
||||
players. To access specific player data with commandblocks, use `/execute as (player) run script in (app) run ...`.
|
||||
To access global/server state, you need to disown the command from any player, so use a commandblock, or any
|
||||
arbitrary entity: `/execute as @e[type=bat,limit=1] run script in (module) globals` for instance.
|
||||
|
||||
### `/script in <app> ...`
|
||||
|
||||
Allows to run normal /script commands in a specific app, like `run, invoke,..., globals` etc...
|
||||
|
||||
# `/script invoke / invokepoint / invokearea`, `/script globals` commands
|
||||
|
||||
`invoke` family of commands provide convenient way to invoke stored procedures (i.e. functions that has been
|
||||
defined previously by any running script. To view current stored procedure set,
|
||||
run `/script globals`(or `/script globals all` to display all functions even hidden ones), to define a new stored
|
||||
procedure, just run a `/script run function(a,b) -> ( ... )` command with your procedure once, and to forget a
|
||||
procedure, use `undef` function: `/script run undef('function')`
|
||||
|
||||
### `/script invoke <fun> <args?> ...`
|
||||
|
||||
Equivalent of running `/script run fun(args, ...)`, but you get the benefit of getting the tab completion of the
|
||||
command name, and lower permission level required to run these (since player is not capable of running any custom
|
||||
code in this case, only this that has been executed before by an operator). Arguments will be checked for validity,
|
||||
and you can only pass simple values as arguments (strings, numbers, or `null` value). Use quotes to include
|
||||
whitespaces in argument strings.
|
||||
|
||||
Command will check provided arguments with required arguments (count) and fail if not enough or too much
|
||||
arguments are provided. Operators defining functions are advised to use descriptive arguments names, as these
|
||||
will be visible for invokers and form the base of understanding what each argument does.
|
||||
|
||||
`invoke` family of commands will tab complete any stored function that does not start with `'_'`, it will still
|
||||
allow to run procedures starting with `'_'` but not suggest them, and ban execution of any hidden stored procedures,
|
||||
so ones that start with `'__'`. In case operator needs to use subroutines for convenience and don't want to expose
|
||||
them to the `invoke` callers, they can use this mechanic.
|
||||
|
||||
<pre>
|
||||
/script run example_function(const, phrase, price) -> print(const+' '+phrase+' '+price)
|
||||
/script invoke example_function pi costs 5
|
||||
</pre>
|
||||
|
||||
### `/script invokepoint <fun> <coords x y z> <args?> ...`
|
||||
|
||||
It is equivalent to `invoke` except it assumes that the first three arguments are coordinates, and provides
|
||||
coordinates tab completion, with `looking at...` mechanics for convenience. All other arguments are expected
|
||||
at the end
|
||||
|
||||
### `/script invokearea <fun> <coords x y z> <coords x y z> <args?> ...`
|
||||
|
||||
It is equivalent to `invoke` except it assumes that the first three arguments are one set of ccordinates,
|
||||
followed by the second set of coordinates, providing tab completion, with `looking at...` mechanics for convenience,
|
||||
followed by any other required arguments
|
||||
|
||||
# `/script scan`, `/script fill` and `/script outline` commands
|
||||
|
||||
These commands can be used to evaluate an expression over an area of blocks. They all need to have specified the
|
||||
origin of the analyzed area (which is used as referenced (0,0,0), and two corners of an area to analyzed. If you
|
||||
would want that the script block coordinates refer to the actual world coordinates, use origin of `0 0 0`, or if
|
||||
it doesn't matter, duplicating coordinates of one of the corners is the easiest way.
|
||||
|
||||
These commands, unlike raw `/script run` are limited by vanilla fill / clone command limit of 32k blocks, which can
|
||||
be altered with carpet mod's own `/carpet fillLimit` command.
|
||||
|
||||
### `/script scan origin<x y z> corner<x y z> corner<x y z> expr`
|
||||
|
||||
Evaluates expression for each point in the area and returns number of successes (result was positive). Since the
|
||||
command by itself doesn't affect the area, the effects would be in side effects.
|
||||
|
||||
### `/script fill origin<x y z> corner<x y z> corner<x y z> "expr" <block> (? replace <replacement>)`
|
||||
|
||||
Think of it as a regular fill command, that sets blocks based on whether a result of the command was successful.
|
||||
Note that the expression is in quotes. Thankfully string constants in `scarpet` use single quotes. Can be used to
|
||||
fill complex geometric shapes.
|
||||
|
||||
### `/script outline origin<x y z> corner<x y z> corner<x y z> "expr" <block> (? replace <replacement>)`
|
||||
|
||||
Similar to `fill` command it evaluates an expression for each block in the area, but in this case setting blocks
|
||||
where condition was true and any of the neighbouring blocks were evaluated negatively. This allows to create surface
|
||||
areas, like sphere for example, without resorting to various rounding modes and tricks.
|
||||
|
||||
Here is an example of seven ways to draw a sphere of radius of 32 blocks around 0 100 0:
|
||||
|
||||
<pre>
|
||||
/script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z < 32*32" white_stained_glass replace air
|
||||
/script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z <= 32*32" white_stained_glass replace air
|
||||
/script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z < 32.5*32.5" white_stained_glass replace air
|
||||
/script fill 0 100 0 -40 60 -40 40 140 40 "floor(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air
|
||||
/script fill 0 100 0 -40 60 -40 40 140 40 "round(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air
|
||||
/script fill 0 100 0 -40 60 -40 40 140 40 "ceil(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air
|
||||
/draw sphere 0 100 0 32 white_stained_glass replace air // fluffy ball round(sqrt(x*x+y*y+z*z)-rand(abs(y)))==32
|
||||
</pre>
|
||||
|
||||
The last method is the one that world edit is using (part of carpet mod). It turns out that the outline method
|
||||
with `32.5` radius, fill method with `round` function and draw command are equivalent
|
||||
|
||||
# `script stop/script resume` command
|
||||
|
||||
`/script stop` allows to stop execution of any script currently running that calls the `game_tick()` function which
|
||||
allows the game loop to regain control of the game and process other commands. This will also make sure that all
|
||||
current and future programs will stop their execution. Execution of all programs will be prevented
|
||||
until `/script resume` command is called.
|
||||
|
||||
Lets look at the following example. This is a program computes Fibonacci number in a recursive manner:
|
||||
|
||||
<pre>
|
||||
fib(n) -> if(n<3, 1, fib(n-1)+fib(n-2) ); fib(8)
|
||||
</pre>
|
||||
|
||||
That's really bad way of doing it, because the higher number we need to compute the compute requirements will
|
||||
rise exponentially with `n`. It takes a little over 50 milliseconds to do fib(24), so above one tick, but about
|
||||
a minute to do fib(40). Calling fib(40) will not only freeze the game, but also you woudn't be able to interrupt
|
||||
its execution. We can modify the script as follows
|
||||
|
||||
<pre>
|
||||
fib(n) -> ( game_tick(50); if(n<3, 1, fib(n-1)+fib(n-2) ) ); fib(40)
|
||||
</pre>
|
||||
|
||||
But this would never finish as such call would finish after `~ 2^40` ticks. To make our computations responsive,
|
||||
yet able to respond to user interactions, other commands, as well as interrupt execution, we could do the following:
|
||||
|
||||
<pre>
|
||||
fib(n) -> ( if(n==23, game_tick(50) ); if(n<3, 1, fib(n-1)+fib(n-2) ) ); fib(40)
|
||||
</pre>
|
||||
|
||||
This would slow down the computation of fib(40) from a minute to two, but allows the game to keep continue running
|
||||
and be responsive to commands, using about half of each tick to advance the computation. Obviously depending on the
|
||||
problem, and available hardware, certain things can take more or less time to execute, so portioning of work with
|
||||
calling `gametick` should be balanced in each case separately
|
||||
@@ -0,0 +1,241 @@
|
||||
# Lists, Maps and API support for Containers
|
||||
|
||||
Scarpet supports basic container types: lists and maps (aka hashmaps, dicts etc..)
|
||||
|
||||
## Container manipulation
|
||||
|
||||
Here is a list of operations that work on all types of containers: lists, maps, as well as other Minecraft specific
|
||||
modifyable containers, like NBTs
|
||||
|
||||
### `get(container, address, ...), get(lvalue), ':' operator`
|
||||
|
||||
Returns the value at `address` element from the `value`. For lists it indicates an index, use negative numbers to
|
||||
reach elements from the end of the list. `get` call will always be able to find the index. In case there is few
|
||||
items, it will loop over
|
||||
|
||||
for maps, retrieves the value under the key specified in the `address` or null otherwise
|
||||
|
||||
[Minecraft specific usecase]: In case `value` is of `nbt` type, uses address as the nbt path to query, returning null,
|
||||
if path is not found, one value if there was one match, or list of values if result is a list. Returned elements can
|
||||
be of numerical type, string texts, or another compound nbt tags
|
||||
|
||||
In case to simplify the access with nested objects, you can add chain of addresses to the arguments of `get` rather
|
||||
than calling it multiple times. `get(get(foo,a),b)` is equivalent to `get(foo, a, b)`, or `foo:a:b`.
|
||||
|
||||
<pre>
|
||||
get(l(range(10)), 5) => 5
|
||||
get(l(range(10)), -1) => 9
|
||||
get(l(range(10)), 10) => 0
|
||||
l(range(10)):93 => 3
|
||||
get(player() ~ 'nbt', 'Health') => 20 // inefficient way to get player health, use player() ~ 'health' instead
|
||||
get(m( l('foo',2), l('bar',3), l('baz',4) ), 'bar') => 3
|
||||
</pre>
|
||||
|
||||
### `has(container, address, ...), has(lvalue)`
|
||||
|
||||
Similar to `get`, but returns boolean value indicating if the given index / key / path is in the container.
|
||||
Can be used to determine if `get(...)==null` means the element doesn't exist, or the stored value for this
|
||||
address is `null`, and is cheaper to run than `get`.
|
||||
|
||||
Like get, it can accept multiple addresses for chains in nested containers. In this case `has(foo:a:b)` is
|
||||
equivalent to `has(get(foo,a), b)` or `has(foo, a, b)`
|
||||
|
||||
### `delete(container, address, ...), delete(lvalue)`
|
||||
|
||||
Removes specific entry from the container. For the lists - removes the element and shrinks it. For maps, it
|
||||
removes the key from the map, and for nbt - removes content from a given path. For lists and maps returns previous
|
||||
entry at the address, for nbt's - number of removed objects, with 0 indicating that the original value was unaffected.
|
||||
|
||||
Like with the `get` and `has`, `delete` can accept chained addresses, as well as l-value container access, removing
|
||||
the value from the leaf of the path provided, so `delete(foo, a, b)` is the
|
||||
same as `delete(get(foo,a),b)` or `delete(foo:a:b)`
|
||||
|
||||
Returns true, if container was changed, false, if it was left unchanged, and null if operation was invalid.
|
||||
|
||||
### `put(container, address, value), put(container, address, value, mode), put(lvalue, value)`
|
||||
|
||||
<u>**Lists**</u>
|
||||
|
||||
Modifies the container by replacing the value under the address with the supplied `value`. For lists, a valid
|
||||
index is required, but can be negative as well to indicate positions from the end of the list. If `null` is
|
||||
supplied as the address, it always means - add to the end of the list.
|
||||
|
||||
There are three modes that lists can have items added to them:
|
||||
|
||||
* `replace`(default): Replaces item under given index(address). Doesn't change the size of the array
|
||||
unless `null` address is used, which is an exception and then it appends to the end
|
||||
* `insert`: Inserts given element at a specified index, shifting the rest of the array to make space for the item.
|
||||
Note that index of -1 points to the last element of the list, thus inserting at that position and moving the previous
|
||||
last element to the new last element position. To insert at the end, use `+=` operator, or `null` address in put
|
||||
* `extend`: treats the supplied value as an iterable set of values to insert at a given index, extending the list
|
||||
by this amount of items. Again use `null` address/index to point to the end of the list
|
||||
|
||||
Due to the extra mode parameter, there is no chaining for `put`, but you can still use l-value container access to
|
||||
indicate container and address, so `put(foo, key, value)` is the same as `put(foo:key, value)` or `foo:key=value`
|
||||
|
||||
Returns true, if container got modified, false otherwise, and null if operation was invalid.
|
||||
|
||||
<u>**Maps**</u>
|
||||
|
||||
For maps there are no modes available (yet, seems there is no reason to). It replaces the value under the supplied
|
||||
key (address), or sets it if not currently present.
|
||||
|
||||
<u>**NBT Tags**</u>
|
||||
|
||||
The address for nbt values is a valid nbt path that you would use with `/data` command, and tag is any tag that
|
||||
would be applicable for a given insert operation. Note that to distinguish between proper types (like integer types,
|
||||
you need to use command notation, i.e. regular ints is `123`, while byte size int would be `123b` and an explicit
|
||||
string would be `"5"`, so it helps that scarpet uses single quotes in his strings. Unlike for lists and maps, it
|
||||
returns the number of affected nodes, or 0 if none were affected.
|
||||
|
||||
There are three modes that NBT tags can have items added to them:
|
||||
|
||||
* `replace`(default): Replaces item under given path(address). Removes them first if possible, and then adds given
|
||||
element to the supplied position. The target path can indicate compound tag keys, lists, or individual elements
|
||||
of the lists.
|
||||
* `<N>`: Index for list insertions. Inserts given element at a specified index, inside a list specified with the
|
||||
path address. Fails if list is not specified. It behaves like `insert` mode for lists, i.e. it is not removing any
|
||||
of the existing elements. Use `replace` to remove and replace existing element.
|
||||
* `merge`: assumes that both path and replacement target are of compound type (dictionaries, maps, `{}` types),
|
||||
and merges keys from `value` with the compound tag under the path
|
||||
|
||||
<pre>
|
||||
a = l(1, 2, 3); put(a, 1, 4); a => [1, 4, 3]
|
||||
a = l(1, 2, 3); put(a, null, 4); a => [1, 2, 3, 4]
|
||||
a = l(1, 2, 3); put(a, 1, 4, 'insert'); a => [1, 4, 2, 3]
|
||||
a = l(1, 2, 3); put(a, null, l(4, 5, 6), 'extend'); a => [1, 2, 3, 4, 5, 6]
|
||||
a = l(1, 2, 3); put(a, 1, l(4, 5, 6), 'extend'); a => [1, 4, 5, 6, 2, 3]
|
||||
a = l(l(0,0,0),l(0,0,0),l(0,0,0)); put(a.1, 1, 1); a => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
|
||||
a = m(1,2,3,4); put(a, 5, null); a => {1: null, 2: null, 3: null, 4: null, 5: null}
|
||||
tag = nbt('{}'); put(tag, 'BlockData.Properties', '[1,2,3,4]'); tag => {BlockData:{Properties:[1,2,3,4]}}
|
||||
tag = nbt('{a:[{lvl:3},{lvl:5},{lvl:2}]}'); put(tag, 'a[].lvl', 1); tag => {a:[{lvl:1},{lvl:1},{lvl:1}]}
|
||||
tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl', 1, 2); tag
|
||||
=> {a:[{lvl:[1,2,1,3]},{lvl:[3,2,1,1]},{lvl:[4,5,1,6]}]}
|
||||
tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl[1]', 1); tag
|
||||
=> {a:[{lvl:[1,1,3]},{lvl:[3,1,1]},{lvl:[4,1,6]}]}
|
||||
</pre>
|
||||
|
||||
## List operations
|
||||
|
||||
### `l(values ...), l(iterator)`
|
||||
|
||||
Creates a list of values of the expressions passed as parameters. It can be used as an L-value and if all
|
||||
elements are variables, you coujld use it to return multiple results from one function call, if that
|
||||
function returns a list of results with the same size as the `l` call uses. In case there is only one
|
||||
argument and it is an iterator (vanilla expression specification has `range`, but Minecraft API implements
|
||||
a bunch of them, like `diamond`), it will convert it to a proper list. Iterators can only be used in high order
|
||||
functions, and are treated as empty lists, unless unrolled with `l`
|
||||
|
||||
<pre>
|
||||
l(1,2,'foo') => [1, 2, foo]
|
||||
l() => [] (empty list)
|
||||
l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
l(1, 2) = l(3, 4) => Error: l is not a variable
|
||||
l(foo, bar) = l(3,4); foo==3 && bar==4 => 1
|
||||
l(foo, bar, baz) = l(2, 4, 6); l(min(foo, bar), baz) = l(3, 5); l(foo, bar, baz) => [3, 4, 5]
|
||||
</pre>
|
||||
|
||||
In the last example `l(min(foo, bar), baz)` creates a valid L-value, as min(foo, bar) finds the lower of the
|
||||
variables (in this case `foo`) creating a valid assignable L-list of [foo, baz], and these values
|
||||
will be assigned new values
|
||||
|
||||
### `join(delim, list), join(delim, values ...)`
|
||||
|
||||
Returns a string that contains joined elements of the list, iterator, or all values,
|
||||
concatenated with `delim` delimiter
|
||||
|
||||
<pre>
|
||||
join('-',range(10)) => 0-1-2-3-4-5-6-7-8-9
|
||||
join('-','foo') => foo
|
||||
join('-', 'foo', 'bar') => foo-bar
|
||||
</pre>
|
||||
|
||||
### `split(delim, expr)`
|
||||
|
||||
Splits a string undr `expr` by `delim` which can be a regular expression
|
||||
|
||||
<pre>
|
||||
split('',foo) => [f, o, o]
|
||||
split('.','foo.bar') => []
|
||||
split('\\.','foo.bar') => [foo, bar]
|
||||
</pre>
|
||||
|
||||
### `slice(expr, from, to?)`
|
||||
|
||||
extracts a substring, or sublist (based on the type of the result of the expression under expr with
|
||||
starting index of `from`, and ending at `to` if provided, or the end, if omitted
|
||||
|
||||
<pre>
|
||||
slice(l(0,1,2,3,4,5), 1, 3) => [1, 2, 3]
|
||||
slice('foobar', 0, 1) => 'f'
|
||||
slice('foobar', 3) => 'bar'
|
||||
slice(range(10), 3, 5) => [3, 4, 5]
|
||||
slice(range(10), 5) => [5, 6, 7, 8, 9]
|
||||
</pre>
|
||||
|
||||
### `sort(list), sort(values ...)`
|
||||
|
||||
Sorts in the default sortographical order either all arguments, or a list if its the only argument.
|
||||
It returns a new sorted list, not affecting the list passed to the argument
|
||||
|
||||
<pre>sort(3,2,1) => [1, 2, 3]
|
||||
sort('a',3,11,1) => [1, 3, 11, 'a']
|
||||
list = l(4,3,2,1); sort(list) => [1, 2, 3, 4]
|
||||
</pre>
|
||||
|
||||
### `sort_key(list, key_expr)`
|
||||
|
||||
Sorts a copy of the list in the order or keys as defined by the `key_expr` for each element
|
||||
|
||||
<pre>
|
||||
sort_key([1,3,2],_) => [1, 2, 3]
|
||||
sort_key([1,3,2],-_) => [3, 2, 1]
|
||||
sort_key(l(range(10)),rand(1)) => [1, 0, 9, 6, 8, 2, 4, 5, 7, 3]
|
||||
sort_key(l(range(20)),str(_)) => [0, 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
</pre>
|
||||
|
||||
### `range(to), range(from, to), range(from, to, step)`
|
||||
|
||||
Creates a range of numbers from `from`, no greater/larger than `to`. The `step` parameter dictates not only the
|
||||
increment size, but also direction (can be negative). The returned value is not a proper list, just the iterator
|
||||
but if for whatever reason you need a proper list with all items evaluated, use `l(range(to))`.
|
||||
Primarily to be used in higher order functions
|
||||
|
||||
<pre>
|
||||
range(10) => [...]
|
||||
l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
map(range(10),_*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
|
||||
reduce(range(10),_a+_, 0) => 45
|
||||
range(5,10) => [5, 6, 7, 8, 9]
|
||||
range(20, 10, -2) => [20, 18, 16, 14, 12]
|
||||
</pre>
|
||||
|
||||
### `element(list, index)(deprecated)`
|
||||
|
||||
Legacy support for older method that worked only on lists. Please use `get(...)` for equivalent support, or `.`
|
||||
operator. Also previous unique behaviours with `put` on lists has been removed to support all type of
|
||||
container types.
|
||||
|
||||
## Map operations
|
||||
|
||||
Scarpet supports map structures, aka hashmaps, dicts etc. Map structure can also be used, with `null` values as sets.
|
||||
Apart from container access functions, (`. , get, put, has, delete`), the following functions:
|
||||
|
||||
### `m(values ...), m(iterator), m(key_value_pairs)`
|
||||
|
||||
creates and initializes a map with supplied keys, and values. If the arguments contains a flat list, these are all
|
||||
treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. If the
|
||||
arguments is a list of lists, they have to have two elements each, and then first is a key, and second, a value
|
||||
|
||||
<pre>
|
||||
m(1,2,'foo') => {1: null, 2: null, foo: null}
|
||||
m() => {} (empty map)
|
||||
m(range(10)) => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
|
||||
m(l(1, 2), l(3, 4)) => {1: 2, 3: 4}
|
||||
reduce(range(10), put(_a, _, _*_); _a, m())
|
||||
=> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
|
||||
</pre>
|
||||
|
||||
### `keys(map), values(map), pairs(map)`
|
||||
|
||||
Returns full lists of keys, values and key-value pairs (2-element lists) for all the entries in the map
|
||||
@@ -0,0 +1,170 @@
|
||||
# User-defined functions and program control flow
|
||||
|
||||
## Writing programs with more than 1 line
|
||||
|
||||
### `Operator ;`
|
||||
|
||||
To effectively write programs that have more than one line, a programmer needs way to specify a sequence of commands
|
||||
that execute one after another. In `scarpet` this can be achieved with `;`. Its an operator, and by separating
|
||||
statements with semicolons. And since whitespaces and
|
||||
`$` (commandline visible newline separator)
|
||||
sign are all treats as whitespaces, how you layout your code doesn't matter, as long as it is readable to everyone involved.
|
||||
|
||||
<pre>
|
||||
expr;
|
||||
expr;
|
||||
expr;
|
||||
expr
|
||||
</pre>
|
||||
|
||||
Notice that the last expression is not followed by a semicolon. Since instruction separation is functional
|
||||
in `scarpet`, and not barely an instruction delimiter, terminating the code with a dangling operator wouldn't
|
||||
be valid. Having said that, since many programming languages don't care about the number of op terminators
|
||||
programmers use, carpet preprocessor will remove all unnecessary semicolons from scripts when compiled.
|
||||
|
||||
In general `expr; expr; expr; expr` is equivalent to `(((expr ; expr) ; expr) ; expr)`.
|
||||
|
||||
Result of the evaluated expression is the same as the result of the second expression, but first expression
|
||||
is also evaluated for side-effects
|
||||
|
||||
<pre>
|
||||
expr1 ; expr2 => expr2 // with expr1 as a side-effect
|
||||
</pre>
|
||||
|
||||
## Global variables
|
||||
|
||||
All defined functions are compiled, stored persistently, and available globally - accessible to all other scripts.
|
||||
Functions can only be undefined via call to `undef('fun')`, which would erase global entry for function `fun`.
|
||||
Since all variables have local scope inside each function, one way to share large objects is via global variables
|
||||
|
||||
Any variable that is used with a name that starts with `'global_'` will be stored and accessible globally, not,
|
||||
inside current scope. It will also persist across scripts, so if a procedure needs to use its own construct, it
|
||||
needs to define it, or initialize it explicitly, or undefine it via `undef`
|
||||
|
||||
<pre>
|
||||
a() -> global_list+=1; global_list = l(1,2,3); a(); a(); global_list // => [1,2,3,1,1]
|
||||
</pre>
|
||||
|
||||
### `Operator ->`
|
||||
|
||||
To organize code better than a flat sequence of operations, one can define functions. Definition is correct
|
||||
if has the following form
|
||||
|
||||
<pre>
|
||||
fun(args, ...) -> expr
|
||||
</pre>
|
||||
|
||||
Where `fun(args, ...)` is a function signature indicating function name, number of arguments, and their names,
|
||||
and expr is an expression (can be complex) that is evaluated when `fun` is called. Names in the signature don't
|
||||
need to be used anywhere else, other occurrences of these names will be masked in this function scope. Function
|
||||
call creates new scope for variables inside `expr`, so all non-global variables are not visible from the caller
|
||||
scope. All parameters are passed by value to the new scope, including lists and other containers, however their
|
||||
copy will be shallow.
|
||||
|
||||
The function returns its name as a string, which means it can be used to call it later with the `call` function
|
||||
|
||||
Using `_` as the function name creates anonymous function, so each time `_` function is defined, it will be given
|
||||
a unique name, which you can pass somewhere else to get this function `call`ed.
|
||||
|
||||
<pre>
|
||||
a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list // => [1,2,3]
|
||||
</pre>
|
||||
|
||||
In case the inner function wants to operate and modify larger objects, lists from the outer scope, but not global,
|
||||
it needs to use `outer` function in function signature
|
||||
|
||||
### `outer(arg)`
|
||||
|
||||
`outer` function can only be used in the function signature, and it will cause an error everywhere else. It
|
||||
saves the value of that variable from the outer scope and allows its use in the inner scope. This is a similar
|
||||
behaviour to using outer variables in lambda function definitions from Java, except here you have to specify
|
||||
which variables you want to use, and borrow
|
||||
|
||||
This mechanism can be used to use static mutable objects without the need of using `global_...` variables
|
||||
|
||||
<pre>
|
||||
list = l(1,2,3); a(outer(list)) -> list+=1; a(); a(); list // => [1,2,3,1,1]
|
||||
</pre>
|
||||
|
||||
The return value of a function is the value of the last expression. This as the same effect as using outer or
|
||||
global lists, but is more expensive
|
||||
|
||||
<pre>
|
||||
a(lst) -> lst+=1; list = l(1,2,3); list=a(list); list=a(list); list // => [1,2,3,1,1]
|
||||
</pre>
|
||||
|
||||
Ability to combine more statements into one expression, with functions, passing parameters, and global and outer
|
||||
scoping allow to organize even larger scripts
|
||||
|
||||
### `import(module_name, symbols ...)`
|
||||
|
||||
Imports symbols from other apps and libraries into the current one: global variables or functions, allowing to use
|
||||
them in the current app. This include other symbols imported by these modules. Scarpet supports cicular dependencies,
|
||||
but if symbols are used directly in the module body rather than functions, it may not be able to retrieve them.
|
||||
Returns full list of available symbols that could be imported from this module, which can be used to debug import
|
||||
issues, and list contents of libraries.
|
||||
|
||||
### `call(function, args.....)`
|
||||
|
||||
calls a user defined function with specified arguments. It is equivalent to calling `function(args...)` directly
|
||||
except you can use it with function value, or name instead. This means you can pass functions to other user defined
|
||||
functions as arguments and call them with `call` internally. And since function definitions return the defined
|
||||
function, they can be defined in place as anonymous functions.
|
||||
|
||||
Little technical note: the use of `_` in expression passed to built in functions is much more efficient due to not
|
||||
creating new call stacks for each invoked function, but anonymous functions is the only mechanism available for
|
||||
programmers with their own lambda arguments
|
||||
|
||||
<pre>
|
||||
my_map(list, function) -> map(list, call(function, _));
|
||||
my_map(l(1,2,3), _(x) -> x*x); // => [1,4,9]
|
||||
profile_expr(my_map(l(1,2,3), _(x) -> x*x)); // => ~32000
|
||||
sq(x) -> x*x; profile_expr(my_map(l(1,2,3), 'sq')); // => ~36000
|
||||
sq = (_(x) -> x*x); profile_expr(my_map(l(1,2,3), sq)); // => ~36000
|
||||
profile_expr(map(l(1,2,3), _*_)); // => ~80000
|
||||
</pre>
|
||||
|
||||
## Control flow
|
||||
|
||||
### `return(expr?)`
|
||||
|
||||
Sometimes its convenient to break the organized control flow, or it is not practical to pass the final result value of
|
||||
a function to the last statement, in this case a return statement can be used
|
||||
|
||||
If no argument is provided - returns null value.
|
||||
|
||||
<pre>
|
||||
def() -> (
|
||||
expr1;
|
||||
expr2;
|
||||
return(expr3); // function terminates returning expr3
|
||||
expr4; // skipped
|
||||
expr5 // skipped
|
||||
)
|
||||
</pre>
|
||||
|
||||
In general its cheaper to leave the last expression as a return value, rather than calling
|
||||
returns everywhere, but it would often lead to a messy code.
|
||||
|
||||
### `exit(expr?)`
|
||||
|
||||
It terminates entire program passing `expr` as the result of the program execution, or null if omitted.
|
||||
|
||||
### `try(expr, catch_expr(_)?) ... throw(value?)`
|
||||
|
||||
`try` function evaluates expression, and continues further unless `throw` function is called anywhere
|
||||
inside `expr`. In that case the `catch_expr` is evaluates with `_` set to the argument `throw` was called with.
|
||||
This mechanic accepts skipping thrown value - it throws null instead, and catch expression - then try returns
|
||||
null as well This mechanism allows to terminate large portion of a convoluted call stack and continue program
|
||||
execution. There is only one level of exceptions currently in carpet, so if the inner function also defines
|
||||
the `try` catchment area, it will received the exception first, but it can technically rethrow the value its
|
||||
getting for the outer scope. Unhandled throw acts like an exit statement.
|
||||
|
||||
### `if(cond, expr, cond?, expr?, ..., default?)`
|
||||
|
||||
If statement is a function that takes a number of conditions that are evaluated one after another and if any of
|
||||
them turns out true, its `expr` gets returned, otherwise, if all conditions fail, the return value is `default`
|
||||
expression, or `null` if default is skipped
|
||||
|
||||
`if` function is equivalent to `if (cond) expr; else if (cond) expr; else default;` from Java,
|
||||
just in a functional form
|
||||
@@ -0,0 +1,132 @@
|
||||
# Loops, and higher order functions
|
||||
|
||||
Efficient use of these functions can greatly simplify your programs and speed them up, as these functions will
|
||||
internalize most of the operations that need to be applied on multiple values at the same time. Most of them take
|
||||
a `list` argument which can be any iterable structure in scarpet, including generators, like `rect`, or `range`,
|
||||
and maps, where the iterator returns all the map keys
|
||||
|
||||
## Loops
|
||||
|
||||
### `break(), break(expr), continue(), continue(expr)`
|
||||
|
||||
These allow to control execution of a loop either skipping current iteration code, using `continue`, or finishing the
|
||||
current loop, using `break`. `break` and `continue` can only be used inside `for, while, loop, map, filter and reduce`
|
||||
functions, while `break` can be used in `first` as well. Outside of the internal expressions of these functions,
|
||||
calling `break` or `continue` will cause an error. In case of the nexted loops, and more complex setups, use
|
||||
custom `try` and `throw` setup.
|
||||
|
||||
Please check corresponding loop function description what `continue` and `break` do in their contexts, but in
|
||||
general case, passed values to `break` and `continue` will be used in place of the return value of the internal
|
||||
iteration expression.
|
||||
|
||||
### `for(list,expr(_,_i))`
|
||||
|
||||
Evaluates expression over list of items from the `list`. Supplies `_`(value) and `_i`(iteration number) to the `expr`.
|
||||
|
||||
Returns the number of times `expr` was successful. Uses `continue` and `break` argument in place of the returned
|
||||
value from the `expr`(if supplied), to determine if the iteration was successful.
|
||||
|
||||
<pre>
|
||||
check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) );
|
||||
for(range(1000000,1100000),check_prime(_)) => 7216
|
||||
</pre>
|
||||
|
||||
From which we can learn that there is 7216 primes between 1M and 1.1M
|
||||
|
||||
### `while(cond, limit)`
|
||||
|
||||
Evaluates expression `expr` repeatedly until condition `cond` becomes false, but not more than `limit` times.
|
||||
Returns the result of the last `expr` evaluation, or `null` if nothing was successful. Both `expr` and `cond` will
|
||||
recveived a bound variable `_` indicating current iteration, so its a number.
|
||||
|
||||
<pre>
|
||||
while(a<100,10,a=_*_) => 81 // loop exhausted via limit
|
||||
while(a<100,20,a=_*_) => 100 // loop stopped at condition, but a has already been assigned
|
||||
while(_*_<100,20,a=_*_) => 81 // loop stopped at condition, before a was assigned a value
|
||||
</pre>
|
||||
|
||||
### `loop(num,expr(_),exit(_)?)`
|
||||
|
||||
Evaluates expression `expr`, `num` number of times. code>expr receives `_` system variable indicating the iteration.
|
||||
|
||||
<pre>
|
||||
loop(5, game_tick()) => repeat tick 5 times
|
||||
list = l(); loop(5, x = _; loop(5, list += l(x, _) ) ); list
|
||||
// double loop, produces: [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], ... , [4, 2], [4, 3], [4, 4]]
|
||||
</pre>
|
||||
|
||||
In this small example we will search for first 10 primes, apparently including 0:
|
||||
|
||||
<pre>
|
||||
check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) );
|
||||
primes = l();
|
||||
loop(10000, if(check_prime(_), primes += _ ; if (length(primes) >= 10, break())));
|
||||
primes
|
||||
// outputs: [0, 1, 2, 3, 5, 7, 11, 13, 17, 19]
|
||||
</pre>
|
||||
|
||||
## Higher Order Functions
|
||||
|
||||
### `map(list,expr(_,_i))`
|
||||
|
||||
Converts a `list` of values, to another list where each value is result of an expression `v = expr(_, _i)`
|
||||
where `_` is passed as each element of the list, and `_i` is the index of such element. If `break` is called the
|
||||
map returns whatever collected thus far. If `continue` and `break` are used with supplied argument, it is used in
|
||||
place of the resulting map element, otherwise current element is skipped.
|
||||
|
||||
<pre>
|
||||
map(range(10), _*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
|
||||
map(players('*'), _+' is stoopid') [gnembon is stoopid, herobrine is stoopid]
|
||||
</pre>
|
||||
|
||||
### `filter(list,expr(_,_i))`
|
||||
|
||||
filters `list` elements returning only these that return positive result of the `expr`. With `break` and `continue`
|
||||
statements, the supplied value can be used as a boolean check instead.
|
||||
|
||||
<pre>
|
||||
filter(range(100), !(_%5), _*_>1000) => [0, 5, 10, 15, 20, 25, 30]
|
||||
map(filter(entity_list('*'),_=='Witch'), query(_,'pos') ) => [[1082.5, 57, 1243.5]]
|
||||
</pre>
|
||||
|
||||
### `first(list,expr(_,_i))`
|
||||
|
||||
Finds and returns the first item in the list that satisfies `expr`. It sets `_` for current element value,
|
||||
and `_i` for index of that element. `break` can be called inside the iteration code, using its argument value
|
||||
instead of the current item. `continue` has no sense and cannot be called inside `first` call.
|
||||
|
||||
<pre>
|
||||
first(range(1000,10000), n=_; !first( range(2, sqrt(n)+1), !(n % _) ) ) => 1009 // first prime after 1000
|
||||
</pre>
|
||||
|
||||
Notice in the example above, that we needed to rename the outer `_` to be albe to use in in the inner `first` call
|
||||
|
||||
### `all(list,expr(_,_i))`
|
||||
|
||||
Returns `true` if all elements on the list satisfy the condition. Its roughly equivalent
|
||||
to `all(list,expr) <=> for(list,expr)==length(list)`. `expr` also receives bound `_` and `_i` variables. `break`
|
||||
and `continue` have no sense and cannot be used inside of `expr` body.
|
||||
|
||||
<pre>
|
||||
all([1,2,3], check_prime(_)) => 1
|
||||
all(neighbours(x,y,z), _=='stone') => 1 // if all neighbours of [x, y, z] are stone
|
||||
map(filter(rect(0,4,0,1000,0,1000), l(x,y,z)=pos(_); all(rect(x,y,z,1,0,1),_=='bedrock') ), pos(_) )
|
||||
=> [[-298, 4, -703], [-287, 4, -156], [-269, 4, 104], [242, 4, 250], [-159, 4, 335], [-208, 4, 416], [-510, 4, 546], [376, 4, 806]]
|
||||
// find all 3x3 bedrock structures in the top bedrock layer
|
||||
map( filter( rect(0,4,0,1000,1,1000,1000,0,1000), l(x,y,z)=pos(_);
|
||||
all(rect(x,y,z,1,0,1),_=='bedrock') && for(rect(x,y-1,z,1,1,1,1,0,1),_=='bedrock')<8),
|
||||
pos(_) ) => [[343, 3, -642], [153, 3, -285], [674, 3, 167], [-710, 3, 398]]
|
||||
// ditto, but requiring at most 7 bedrock block in the 18 blocks below them
|
||||
</pre>
|
||||
|
||||
### `reduce(list,expr(_a,_,_i), initial)`
|
||||
|
||||
Applies `expr` for each element of the list and saves the result in `_a` accumulator. Consecutive calls to `expr`
|
||||
can access that value to apply more values. You also need to specify the initial value to apply for the
|
||||
accumulator. `break` can be used to terminate reduction prematurely. If a value is provided to `break` or `continue`,
|
||||
it will be used from now on as a new value for the accumulator.
|
||||
|
||||
<pre>
|
||||
reduce([1,2,3,4],_a+_,0) => 10
|
||||
reduce([1,2,3,4],_a*_,1) => 24
|
||||
</pre>
|
||||
@@ -0,0 +1,120 @@
|
||||
# Arithmetic operations
|
||||
|
||||
## Basic Arithmetic Functions
|
||||
|
||||
There is bunch of them - they require a number and spit out a number, doing what you would expect them to do.
|
||||
|
||||
### `fact(n)`
|
||||
|
||||
Factorial of a number, a.k.a `n!`, just not in `scarpet`. Gets big... quick...
|
||||
|
||||
### `sqrt(n)`
|
||||
|
||||
Square root. For other fancy roots, use `^`, math and yo noggin. Imagine square roots on a tree...
|
||||
|
||||
### `abs(n)`
|
||||
|
||||
Absolut value.
|
||||
|
||||
### `round(n)`
|
||||
|
||||
Closest integer value. Did you know the earth is also round?
|
||||
|
||||
### `floor(n)`
|
||||
|
||||
Highest integer that is still no larger then `n`. Insert a floor pun here.
|
||||
|
||||
### `ceil(n)`
|
||||
|
||||
First lucky integer that is not smalller than `n`. As you would expect, ceiling is typically right above the floor.
|
||||
|
||||
### `ln(n)`
|
||||
|
||||
Natural logarithm of `n`. Naturally.
|
||||
|
||||
### `ln1p(n)`
|
||||
|
||||
Natural logarithm of `n+1`. Very optimistic.
|
||||
|
||||
### `log10(n)`
|
||||
|
||||
Decimal logarithm of `n`. Its ceiling is the length of its floor.
|
||||
|
||||
### `log(n)`
|
||||
|
||||
Binary logarithm of `n`. Finally, a proper one, not like the previous 11.
|
||||
|
||||
### `log1p(n)`
|
||||
|
||||
Binary logarithm of `n+1`. Also always positive.
|
||||
|
||||
### `mandelbrot(a, b, limit)`
|
||||
|
||||
Computes the value of the mandelbrot set, for set `a` and `b` spot. Spot the beetle. Why not.
|
||||
|
||||
### `min(arg, ...), min(list), max(arg, ...), max(list)`
|
||||
|
||||
Compute minimum or maximum of supplied arguments assuming default sorthoraphical order.
|
||||
In case you are missing `argmax`, just use `a ~ max(a)`, little less efficient, but still fun.
|
||||
|
||||
Interesting bit - `min` and `max` don't remove variable associations from arguments, which means can be used as
|
||||
LHS of assignments (obvious case), or argument spec in function definitions (far less obvious).
|
||||
|
||||
<pre>
|
||||
a = 1; b = 2; min(a,b) = 3; l(a,b) => [3, 2]
|
||||
a = 1; b = 2; fun(x, min(a,b)) -> l(a,b); fun(3,5) => [5, 0]
|
||||
</pre>
|
||||
|
||||
Absolutely no idea, how the latter might be useful in practice. But since it compiles, can ship it.
|
||||
|
||||
### `relu(n)`
|
||||
|
||||
Linear rectifier of `n`. 0 below 0, n above. Why not. `max(0,n)` with less moral repercussions.
|
||||
|
||||
## Trigonometric / Geometric Functions
|
||||
|
||||
### `sin(x)`
|
||||
|
||||
### `cos(x)`
|
||||
|
||||
### `tan(x)`
|
||||
|
||||
### `asin(x)`
|
||||
|
||||
### `acos(x)`
|
||||
|
||||
### `atan(x)`
|
||||
|
||||
### `atan2(x,y)`
|
||||
|
||||
### `sinh(x)`
|
||||
|
||||
### `cosh(x)`
|
||||
|
||||
### `tanh(x)`
|
||||
|
||||
### `sec(x)`
|
||||
|
||||
### `csc(x)`
|
||||
|
||||
### `sech(x)`
|
||||
|
||||
### `csch(x)`
|
||||
|
||||
### `cot(x)`
|
||||
|
||||
### `acot(x)`
|
||||
|
||||
### `coth(x)`
|
||||
|
||||
### `asinh(x)`
|
||||
|
||||
### `acosh(x)`
|
||||
|
||||
### `atanh(x)`
|
||||
|
||||
### `rad(deg)`
|
||||
|
||||
### `deg(rad)`
|
||||
|
||||
Use as you wish
|
||||
@@ -0,0 +1,215 @@
|
||||
# Operators
|
||||
|
||||
There is a number of operators you can use inside the expressions. Those could be considered generic type operators
|
||||
that apply to most data types. They also follow standard operator precedence, i.e. `2+2*2` is understood
|
||||
as `2+(2*2)`, not `(2+2)*2`, otherwise they are applied from left to right, i.e. `2+4-3` is interpreted
|
||||
as `(2+4)-3`, which in case of numbers doesn't matter, but since `scarpet` allows for mixing all value types
|
||||
the associativity would matter, and may lead to unintended effects:
|
||||
|
||||
Important operator is function definition `->` operator. It will be covered
|
||||
in {@link carpet.script.Expression#UserDefinedFunctionsAndControlFlow}
|
||||
|
||||
<pre>
|
||||
'123'+4-2 => ('123'+4)-2 => '1234'-2 => '134'
|
||||
'123'+(4-2) => '123'+2 => '1232'
|
||||
3*'foo' => 'foofoofoo'
|
||||
1357-5 => 1352
|
||||
1357-'5' => 137
|
||||
3*'foo'-'o' => 'fff'
|
||||
l(1,3,5)+7 => l(8,10,12)
|
||||
</pre>
|
||||
|
||||
As you can see, values can behave differently when mixed with other types in the same expression.
|
||||
In case values are of the same types, the result tends to be obvious, but `Scarpet` tries to make sense of whatever
|
||||
it has to deal with
|
||||
|
||||
## Operator Precedence
|
||||
|
||||
Here is the complete list of operators in `scarpet` including control flow operators. Note, that commas and brackets
|
||||
are not technically operators, but part of the language, even if they look like them:
|
||||
|
||||
* Match, Get `~ :`
|
||||
* Unary `+ - !`
|
||||
* Exponent `^`
|
||||
* Multiplication `* / %`
|
||||
* Addition `+ -`
|
||||
* Comparison `== != > >= <= <`
|
||||
* Logical And`&&`
|
||||
* Logical Or `||`
|
||||
* Assignment `= += <>`
|
||||
* Definition `->`
|
||||
* Next statement`;`
|
||||
* Comma `,`
|
||||
* Bracket `( )`
|
||||
|
||||
### `Get, Accessor Operator :`
|
||||
|
||||
Operator version of the `get(...)` function to access elements of lists, maps, and potentially other containers
|
||||
(i.e. NBTs). It is important to distinguish from `~` operator, which is a matching operator, which is expected to
|
||||
perform some extra computations to retrieve the result, while `:` should be straightforward and immediate, and
|
||||
the source object should behave like a container and support full container API,
|
||||
meaning `get(...)`, `put(...)`, `delete(...)`, and `has(...)` functions
|
||||
|
||||
For certain operators and functions (get, put, delete, has, =, +=) objects can use `:` annotated fields as l-values,
|
||||
meaning construct like `foo:0 = 5`, would act like `put(foo, 0, 5)`, rather than `get(foo, 0) = 5`,
|
||||
which would result in an error.
|
||||
|
||||
TODO: add more information about l-value behaviour.
|
||||
|
||||
### `Matching Operator ~`
|
||||
|
||||
This operator should be understood as 'matches', 'contains', 'is_in', or 'find me some stuff about something else.
|
||||
For strings it matches the right operand as a regular expression to the left, returning the first match.
|
||||
This can be used to extract information from unparsed nbt's in a more convoluted way (use `get(...)` for
|
||||
more appropriate way of doing it). For lists it checks if an element is in the list, and returns the
|
||||
index of that element, or `null` if no such element was found, especially that the use of `first(...)`
|
||||
function will not return the index. Currently it doesn't have any special behaviour for numbers - it checks for
|
||||
existence of characters in string representation of the left operand with respect of the regular expression on
|
||||
the right hand side.
|
||||
|
||||
In Minecraft API portion `entity ~ feature` is a shortcode for `query(entity,feature)` for queries that do not take
|
||||
any extra arguments.
|
||||
|
||||
<pre>
|
||||
l(1,2,3) ~ 2 => 1
|
||||
l(1,2,3) ~ 4 => null
|
||||
'foobar' ~ '.b' => 'ob'
|
||||
player('*') ~ 'gnembon' // null unless player gnembon is logged in (better to use player('gnembon') instead
|
||||
p ~ 'sneaking' // if p is an entity returns whether p is sneaking
|
||||
</pre>
|
||||
|
||||
Or a longer example of an ineffective way to searching for a squid
|
||||
|
||||
<pre>
|
||||
entities = entities_area('all',x,y,z,100,10,100);
|
||||
sid = entities ~ 'Squid';
|
||||
if(sid != null, run('execute as '+query(get(entities,sid),'id')+' run say I am here '+query(get(entities,sid),'pos') ) )
|
||||
</pre>
|
||||
|
||||
Or an example to find if a player has specific enchantment on a held axe (either hand) and get its level
|
||||
(not using proper NBTs query support via `get(...)`):
|
||||
|
||||
<pre>
|
||||
global_get_enchantment(p, ench) -> (
|
||||
$ for(l('main','offhand'),
|
||||
$ holds = query(p, 'holds', _);
|
||||
$ if( holds,
|
||||
$ l(what, count, nbt) = holds;
|
||||
$ if( what ~ '_axe' && nbt ~ ench,
|
||||
$ lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
|
||||
$ )
|
||||
$ )
|
||||
$ );
|
||||
$ lvl
|
||||
$);
|
||||
/script run global_get_enchantment(players(), 'sharpness')
|
||||
</pre>
|
||||
|
||||
### `Basic Arithmetic Operators + - * /`
|
||||
|
||||
Allows to add the results of two expressions. If the operands resolve to numbers, the result is arithmetic operation.
|
||||
In case of strings, adding or subtracting from a string results in string concatenation and removal of substrings
|
||||
from that string. Multiplication of strings and numbers results in repeating the string N times and division results
|
||||
in taking the first k'th part of the string, so that `str*n/n ~ str` In case first operand is a list, either it
|
||||
results in a new list with all elements modified one by one with the other operand, or if the operand is a list
|
||||
with the same number of items - element-wise addition/subtraction
|
||||
|
||||
Examples:
|
||||
|
||||
<pre>
|
||||
2+3 => 5
|
||||
'foo'+3+2 => 'foo32'
|
||||
'foo'+(3+2) => 'foo5'
|
||||
3+2+'bar' => '5bar'
|
||||
'foo'*3 => 'foofoofoo'
|
||||
'foofoofoo' / 3 => 'foo'
|
||||
'foofoofoo'-'o' => 'fff'
|
||||
l(1,2,3)+1 => l(2,3,4)
|
||||
b = l(100,63,100); b+l(10,0,10) => l(110,63,110)
|
||||
</pre>
|
||||
|
||||
### `Just Operators % ^`
|
||||
|
||||
The modulo and exponent (power) operators work only if both operands are numbers
|
||||
|
||||
<pre>pi^pi%euler => 1.124....
|
||||
-9 % 4 => -1
|
||||
9 % -4 => 0 ¯\_(ツ)_/¯ Java
|
||||
-3 ^ 2 => 9
|
||||
-3 ^ pi => // Error
|
||||
</pre>
|
||||
|
||||
### `Comparison Operators == != < > <= >=`
|
||||
|
||||
Allows to compare the results of two expressions. For numbers it is considers arithmetic order of numbers, for
|
||||
strings - lexicographical, nulls are always 'less' than everything else, and lists check their elements -
|
||||
if the sizes are different, the size matters, otherwise, pairwise comparisons for each elements are performed.
|
||||
The same order rules than with all these operators are used with the default sortographical order as used by `sort`
|
||||
function. All of these are true:
|
||||
|
||||
<pre>
|
||||
null == null
|
||||
null != false
|
||||
0 == false
|
||||
1 == true
|
||||
null < 0
|
||||
null < -1000
|
||||
1000 < 'a'
|
||||
'bar' < 'foo'
|
||||
3 == 3.0
|
||||
</pre>
|
||||
|
||||
### `Logical Operators && ||`
|
||||
|
||||
These operator compute respective boolean operation on the operands. What it important is that if calculating of the
|
||||
second operand is not necessary, it won't be evaluated, which means one can use them as conditional statements. In
|
||||
case of success returns first positive operand (`||`) or last one (`&&`).
|
||||
|
||||
<pre>
|
||||
true || false => 1
|
||||
null || false => 0
|
||||
null != false || run('kill gnembon') => 1 // gnembon survives
|
||||
null != false && run('kill gnembon') => 0 // when cheats not allowed
|
||||
null != false && run('kill gnembon') => 1 // gnembon dies, cheats allowed
|
||||
</pre>
|
||||
|
||||
### `Assignment Operators = <> +=`
|
||||
|
||||
A set of assignment operators. All require bounded variable on the LHS, `<>` requires bounded arguments on the
|
||||
right hand side as well (bounded, meaning being variables). Additionally they can also handle list constructors
|
||||
with all bounded variables, and work then as list assignment operators. When `+=` is used on a list, it extends
|
||||
that list of that element, and returns the list (old == new). `scarpet` doesn't support currently removal of items.
|
||||
Removal of items can be obtaine via `filter` command, and reassigning it fo the same variable. Both operations would
|
||||
require rewriting of the array anyways.
|
||||
|
||||
<pre>
|
||||
a = 5 => a == 5
|
||||
l(a,b,c) = l(3,4,5) => a==3, b==4, c==5
|
||||
l(minx,maxx) = sort(xi,xj); // minx assumes min(xi, xj) and maxx, max(xi, xj)
|
||||
l(a,b,c,d,e,f) = l(range(6)); l(a,b,c) <> l(d,e,f); l(a,b,c,d,e,f) => [3,4,5,0,1,2]
|
||||
a = l(1,2,3); a += 4 => [1,2,3,4]
|
||||
a = l(1,2,3,4); a = filter(a,_!=2) => [1,3,4]
|
||||
</pre>
|
||||
|
||||
### `Unary Operators - +`
|
||||
|
||||
Require a number, flips the sign. One way to assert it's a number is by crashing the script. gg.
|
||||
|
||||
<pre>
|
||||
-4 => -4
|
||||
+4 => 4
|
||||
+'4' // Error message
|
||||
</pre>
|
||||
|
||||
### `Negation Operator !`
|
||||
|
||||
flips boolean condition of the expression. Equivalent of `bool(expr)==false`
|
||||
|
||||
<pre>
|
||||
!true => 0
|
||||
!false => 1
|
||||
!null => 1
|
||||
!5 => 0
|
||||
!l() => 1
|
||||
!l(null) => 0
|
||||
</pre>
|
||||
@@ -0,0 +1,276 @@
|
||||
# Fundamental components of `scarpet` programming language (towards version 1.7).
|
||||
|
||||
Scarpet (a.k.a. Carpet Script, or Script for Carpet) is a programming language
|
||||
designed to provide the ability to write custom programs to run within Minecraft
|
||||
and interact with the world.
|
||||
|
||||
The project was initially built based on the EvalEx project, however it now diverged
|
||||
so far away from the original implementation, it would be hard to tell it without
|
||||
this mention. EvalEx is a handy expression evaluator for Java, that allows to evaluate
|
||||
simple mathematical and boolean expressions. For more information,
|
||||
see: [EvalEx GitHub repository](https://github.com/uklimaschewski/EvalEx)
|
||||
|
||||
This specification is divided into two sections: this one is agnostic to any
|
||||
Minecraft related features and could function on its own, and CarpetExpression
|
||||
for Minecraft specific routines and world manipulation functions.
|
||||
|
||||
# Synopsis
|
||||
|
||||
<pre>
|
||||
script run print('Hello World!')
|
||||
</pre>
|
||||
|
||||
or an OVERLY complex example:
|
||||
|
||||
<pre>
|
||||
/script run
|
||||
block_check(x1, y1, z1, x2, y2, z2, block_to_check) ->
|
||||
(
|
||||
l(minx, maxx) = sort(l(x1, x2));
|
||||
l(miny, maxy) = sort(l(y1, y2));
|
||||
l(minz, maxz) = sort(l(z1, z2));
|
||||
'Need to compute the size of the area of course';
|
||||
'Cause this language doesn\'t support comments in the command mode';
|
||||
xsize = maxx - minx + 1;
|
||||
ysize = maxy - miny + 1;
|
||||
zsize = maxz - minz + 1;
|
||||
total_count = 0;
|
||||
loop(xsize,
|
||||
xx = minx + _ ;
|
||||
loop(ysize,
|
||||
yy = miny + _ ;
|
||||
loop(zsize,
|
||||
zz = minz + _ ;
|
||||
if ( block(xx,yy,zz) == block_to_check,
|
||||
total_count += ceil(rand(1))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
total_count
|
||||
);
|
||||
check_area_around_closest_player_for_block(block_to_check) ->
|
||||
(
|
||||
closest_player = player();
|
||||
l(posx, posy, posz) = query(closest_player, 'pos');
|
||||
total_count = block_check( posx-8,1,posz-8, posx+8,17,posz+8, block_to_check);
|
||||
print('There is '+total_count+' of '+block_to_check+' around you')
|
||||
)
|
||||
/script invoke check_area_around_closest_player_for_block 'diamond_ore'
|
||||
</pre>
|
||||
|
||||
or simply
|
||||
|
||||
<pre>
|
||||
/script run print('There is'+for(rect(x,9,z,8,8,8), _ == 'diamond_ore')+' diamond ore around you')
|
||||
</pre>
|
||||
|
||||
It definitely pays to check what higher level `scarpet` functions have to offer.
|
||||
|
||||
# Programs
|
||||
|
||||
You can think of an program like a mathematical expression, like `"2.4*sin(45)/(2-4)"` or `"sin(y)>0 & max(z, 3)>3"`.
|
||||
Writing a program, is like writing a `2+3`, just a bit longer.
|
||||
|
||||
## Basic language components
|
||||
|
||||
Programs consist of constants, like `2`, `3.14`, `pi`, or `'foo'`, operators like `+`, `/`, `->`, variables which you
|
||||
can define, like `foo` or special ones that will be defined for you, like `_x`, or `_` , which I specific to a each
|
||||
built in function, and functions with name, and arguments in the form of `f(a,b,c)`, where `f` is the function name,
|
||||
and `a, b, c` are the arguments which can be any other expression. And that's all the parts of the language, so all
|
||||
in all - sounds quite simple.
|
||||
|
||||
## Code flow
|
||||
|
||||
Like any other proper programming language, `scarpet` needs brackets, basically to identify where stuff begins and
|
||||
where it ends. In the languages that uses much more complicated constructs, like Java, they tend to use all sort of
|
||||
them, round ones to indicate function calls, curly to indicate section of code, square to access lists, pointy for
|
||||
generic types etc... I mean - there is no etc, cause they have exhausted all the bracket options...
|
||||
|
||||
`Scarpet` is different, since it runs everything based on functions (although its not per se a functional
|
||||
language like lisp) only needs the round brackets for everything, and it is up to the programmer to organize
|
||||
its code so its readable, as adding more brackets does not have any effect on the performance of the programs
|
||||
as they are compiled before they are executed. Look at the following example usage of `if()` function:
|
||||
|
||||
<pre>
|
||||
if(x<y+6,set(x,8+y,z,'air');plop(x,top('surface',x,z),z,'birch'),sin(query(player(),'yaw'))>0.5,plop(0,0,0,'boulder'),particle('fire',x,y,z))
|
||||
</pre>
|
||||
|
||||
Would you prefer to read
|
||||
|
||||
<pre>
|
||||
if( x<y+6,
|
||||
set(x,8+y,z,'air');
|
||||
plop(x,top('surface',x,z),z,'birch'),
|
||||
sin(query(player(),'yaw'))>0.5,
|
||||
plop(0,0,0,'boulder'),
|
||||
particle('fire',x,y,z)
|
||||
)
|
||||
</pre>
|
||||
|
||||
Or rather:
|
||||
|
||||
<pre>
|
||||
if
|
||||
(
|
||||
x<y+6,
|
||||
(
|
||||
set(x,8+y,z,'air');
|
||||
plop(x,top('surface',x,z),z,'birch')
|
||||
),
|
||||
sin(query(player(),'yaw'))>0.5,
|
||||
(
|
||||
plop(0,0,0,'boulder')
|
||||
),
|
||||
particle('fire',x,y,z)
|
||||
)
|
||||
</pre>
|
||||
|
||||
Whichever style you prefer it doesn't matter. It typically depends on the situation and the complexity of the
|
||||
subcomponents. No matter how many whitespaces and extra brackets you add - the code will evaluate to exactly the
|
||||
same expression, and will run exactly the same, so make sure your programs are nice and clean so others don't
|
||||
have problems with them
|
||||
|
||||
## Functions and scoping
|
||||
|
||||
Users can define functions in the form `fun(args....) -> expression` and they are compiled and saved for further
|
||||
execution in this but also subsequent calls of /script command. Functions can also be assigned to variables,
|
||||
passed as arguments, called with `call` function, but in most cases you would want to call them directly by
|
||||
name, in the form of `fun(args...)`. This means that once defined functions are saved with the world for
|
||||
further use. For variables, there are two types of them, global - which are shared anywhere in the code,
|
||||
and those are all which name starts with 'global_', and local variables which is everything else and those
|
||||
are only visible inside each function. This also means that all the parameters in functions are
|
||||
passed 'by value', not 'by reference'.
|
||||
|
||||
## Outer variables
|
||||
|
||||
Functions can still 'borrow' variables from the outer scope, by adding them to the function signature wrapped
|
||||
around built-in function `outer`. It adds the specified value to the function call stack so they behave exactly
|
||||
like capturing lambdas in Java, but unlike java captured variables don't need to be final. Scarpet will just
|
||||
attach their new values at the time of the function definition, even if they change later. Most value will be
|
||||
copied, but mutable values, like maps or lists, allow to keep the 'state' with the function, allowing them to
|
||||
have memory and act like objects so to speak. . Check `outer(var)` for details.
|
||||
|
||||
## Code delivery, line indicators
|
||||
|
||||
Note that this should only apply to pasting your code to execute with commandblock. Scarpet recommends placing
|
||||
your code in apps (files with `.sc` extension that can be placed inside "/scripts" folder in the world files and
|
||||
loaded as a scarpet app with command `/script load [app_name]`. Scarpet apps loaded from disk should only
|
||||
contain code, no need to start with "/script run" prefix
|
||||
|
||||
The following is the code that could be provided in a `foo.sc` app file located in world `/scripts` folder
|
||||
|
||||
<pre>
|
||||
run_program() -> (
|
||||
loop( 10,
|
||||
// looping 10 times
|
||||
// comments are allowed in scripts located in world files
|
||||
// since we can tell where that line ends
|
||||
foo = floor(rand(10));
|
||||
check_not_zero(foo);
|
||||
print(_+' - foo: '+foo);
|
||||
print(' reciprocal: '+ _/foo )
|
||||
)
|
||||
);
|
||||
check_not_zero(foo) -> (
|
||||
if (foo==0, foo = 1)
|
||||
)
|
||||
</pre>
|
||||
|
||||
Which we then call in-game with:
|
||||
|
||||
<pre>
|
||||
/script load foo
|
||||
/script in foo invoke run_program
|
||||
</pre>
|
||||
|
||||
However the following code can also be input as a command, or in a command block.
|
||||
|
||||
Since the maximum command that can be input to the chat is limited in length, you will be probably inserting your
|
||||
programs by pasting them to command blocks or reading from world files, however pasting to command blocks will
|
||||
remove some whitespaces and squish your newlines making the code not readable. If you are pasting a program that
|
||||
is perfect and will never cause an error, I salute you, but for the most part it is quite likely that your program
|
||||
might break, either at compile time, when its initially analyzed, or at execute time, when you suddenly attempt to
|
||||
divide something by zero. In these cases you would want to get a meaningful error message, but for that you would
|
||||
need to indicate for the compiler where did you put these new lines, since command block would squish them. For that,
|
||||
place at the beginning of the line to let the compiler know where are you. This makes so that `$` is the only
|
||||
character that is illegal in programs, since it will be replaced with new lines. As far as I know, `$` is not
|
||||
used anywhere inside Minecraft identifiers, so this shouldn't hinder the abilities of your programs.
|
||||
|
||||
Consider the following program executed as command block command:
|
||||
|
||||
<pre>
|
||||
/script run
|
||||
run_program() -> (
|
||||
loop( 10,
|
||||
foo = floor(rand(_));
|
||||
check_not_zero(foo);
|
||||
print(_+' - foo: '+foo);
|
||||
print(' reciprocal: '+ _/foo )
|
||||
)
|
||||
);
|
||||
check_not_zero(foo) -> (
|
||||
if (foo==0, foo = 1)
|
||||
)
|
||||
</pre>
|
||||
|
||||
Lets say that the intention was to check if the bar is zero and prevent division by zero in print, but because
|
||||
the `foo` is passed as a variable, it never changes the original foo value. Because of the inevitable division
|
||||
by zero, we get the following message:
|
||||
|
||||
<pre>
|
||||
Your math is wrong, Incorrect number format for NaN at pos 98
|
||||
run_program() -> ( loop( 10, foo = floor(rand(_)); check_not_zero(foo); print(_+' - foo: '+foo);
|
||||
HERE>> print(' reciprocal: '+ _/foo ) ));check_not_zero(foo) -> ( if (foo==0, foo = 1))
|
||||
</pre>
|
||||
|
||||
As we can see, we got our problem where the result of the mathematical operation was not a number
|
||||
(infinity, so not a number), however by pasting our program into the command made it squish the newlines so
|
||||
while it is clear where the error happened and we still can track the error down, the position of the error (98)
|
||||
is not very helpful and wouldn't be useful if the program gets significantly longer. To combat this issue we can
|
||||
precede every line of the script with dollar signs `$`:
|
||||
|
||||
<pre>
|
||||
/script run
|
||||
$run_program() -> (
|
||||
$ loop( 10,
|
||||
$ foo = floor(rand(_));
|
||||
$ check_not_zero(foo);
|
||||
$ print(_+' - foo: '+foo);
|
||||
$ print(' reciprocal: '+ _/foo )
|
||||
$ )
|
||||
$);
|
||||
$check_not_zero(foo) -> (
|
||||
$ if (foo==0, foo = 1)
|
||||
$)
|
||||
</pre>
|
||||
|
||||
Then we get the following error message
|
||||
|
||||
<pre>
|
||||
Your math is wrong, Incorrect number format for NaN at line 7, pos 2
|
||||
print(_+' - foo: '+foo);
|
||||
HERE>> print(' reciprocal: '+ _/foo )
|
||||
)
|
||||
</pre>
|
||||
|
||||
As we can note not only we get much more concise snippet, but also information about the line number and position,
|
||||
so means its way easier to locate the potential problems problem
|
||||
|
||||
Obviously that's not the way we intended this program to work. To get it `foo` modified via a function call,
|
||||
we would either return it as a result and assign it to the new variable:
|
||||
|
||||
<pre>
|
||||
foo = check_not_zero(foo);
|
||||
...
|
||||
check_not_zero(foo) -> if(foo == 0, 1, foo)
|
||||
</pre>
|
||||
|
||||
.. or convert it to a global variable, which in this case passing as an argument is not required
|
||||
|
||||
<pre>
|
||||
global_foo = floor(rand(10));
|
||||
check_foo_not_zero();
|
||||
...
|
||||
check_foo_not_zero() -> if(global_foo == 0, global_foo = 1)
|
||||
</pre>
|
||||
@@ -0,0 +1,324 @@
|
||||
# System functions
|
||||
|
||||
## Type conversion functions
|
||||
|
||||
### `copy(expr)`
|
||||
|
||||
Returns the deep copy of the expression. Can be used to copy mutable objects, like maps and lists
|
||||
|
||||
### `type(expr)`
|
||||
|
||||
Returns the string value indicating type of the expression. Possible outcomes
|
||||
are `null, number, string, list, iterator`, as well as minecraft related concepts like `block, entity, nbt`
|
||||
|
||||
### `bool(expr)`
|
||||
|
||||
Returns a boolean context of the expression. Note that there are no true/false values in scarpet. `true` is
|
||||
alias of 1, and `false` is 0\. Bool is also interpreting string values as boolean, which is different from other
|
||||
places where boolean context can be used. This can be used in places where API functions return string values to
|
||||
represent binary values
|
||||
|
||||
<pre>
|
||||
bool(pi) => 1
|
||||
bool(false) => 0
|
||||
bool('') => 0
|
||||
bool(l()) => 0
|
||||
bool(l('')) => 1
|
||||
bool('foo') => 1
|
||||
bool('false') => 0
|
||||
bool('nulL') => 0
|
||||
if('false',1,0) => 1
|
||||
</pre>
|
||||
|
||||
### `number(expr)`
|
||||
|
||||
Returns a numeric context of the expression. Can be used to read numbers from strings
|
||||
|
||||
<pre>
|
||||
number(null) => null
|
||||
number(false) => 0
|
||||
number('') => null
|
||||
number('3.14') => 3.14
|
||||
number(l()) => 0
|
||||
number(l('')) => 1
|
||||
number('foo') => null
|
||||
number('3bar') => null
|
||||
number('2')+number('2') => 4
|
||||
</pre>
|
||||
|
||||
### `str(expr, params? ... ), str(expr, param_list)`
|
||||
|
||||
Returns a formatted string representing expression. Accepts formatting style accepted by `String.format`.
|
||||
Supported types (with `"%?"` syntax):
|
||||
|
||||
* d, o, x: integers
|
||||
* a, e, f, g: floats
|
||||
* b: booleans
|
||||
* s: strings
|
||||
|
||||
<pre>
|
||||
str(null) => null
|
||||
str(false) => 0
|
||||
str('') => null
|
||||
str('3.14') => 3.14
|
||||
str(l()) => 0
|
||||
str(l('')) => 1
|
||||
str('foo') => null
|
||||
str('3bar') => null
|
||||
str(2)+str(2) => 22
|
||||
str('pi: %.2f',pi) => 'pi: 3.14'
|
||||
str('player at: %d %d %d',pos(player())) => 'player at: 567, -2423, 124'
|
||||
</pre>
|
||||
|
||||
* * *
|
||||
## Threading and Parallel Execution
|
||||
|
||||
Scarpet allows to run threads of execution in parallel to the main script execution thread. In Minecraft, the main
|
||||
thread scripts are executed on the main server thread. Since Minecraft is inherently NOT thread safe, it is not that
|
||||
beneficial to parallel execution in order to access world resources faster. Both `GetBlockState` and `setBlockState`
|
||||
are not thread safe and require the execution to park on the server thread, where these requests can be executed in
|
||||
the off-tick time in between ticks that didn't take 50ms. There are however benefits of running things in parallel,
|
||||
like fine time control not relying on the tick clock, or running things independent on each other. You can still run
|
||||
your actions on tick-by-tick basis, either taking control of the execution using `game_tick()` API function
|
||||
(nasty solution), or scheduling tick using `schedule()` function (much nicer solution), but threading often gives
|
||||
the neatest solution to solve problems in parallel (see scarpet camera).
|
||||
|
||||
Due to limitations with the game, there are some limits to the threading as well. You cannot for
|
||||
instance `join_task()` at all from the main script and server thread, because any use of Minecraft specific
|
||||
function that require any world access, will require to park and join on the main thread to get world access,
|
||||
meaning that calling join on that task would inevitably lead to a typical deadlock. You can still join tasks
|
||||
from other threads, just because the only possibility of a deadlock in this case would come explicitly from your
|
||||
bad code, not the internal world access behaviour. Some things tough like player or entity manipulation, can be
|
||||
effectively parallelized.
|
||||
|
||||
### `task(function, ?args...., ?executor)`
|
||||
|
||||
Creates and runs a parallel task, returning the handle to the task object. Task will return the return value of the
|
||||
function when its completed, or will return `null` immediately if task is still in progress, so grabbing a value of
|
||||
a task object is non-blocking. Function can be either function value, or function lambda, or a name of an existing
|
||||
defined function. In case function needs arguments to be called with, they should be supplied after the function
|
||||
name, or value. Optional `executor` identifier is to place the task in a specific queue identified by this value.
|
||||
The default thread value is the `null` thread. There is no limits on number of parallel tasks for any executor,
|
||||
so using different queues is solely for synchronization purposes. Also, since `task` function knows how many extra
|
||||
arguments it requires, the use of tailing optional parameter for the custom executor thread pool is not ambiguous.
|
||||
|
||||
<pre>
|
||||
task( _() -> print('Hello Other World') ) => Runs print command on a separate thread
|
||||
foo(a, b) -> print(a+b); task('foo',2,2) => Uses existing function definition to start a task
|
||||
task('foo',3,5,'temp'); => runs function foo with a different thread executor, identified as 'temp'
|
||||
a = 3; task( _(outer(a), b) -> foo(a,b), 5, 'temp')
|
||||
=> Another example of running the same thing passing arguments using closure over anonymous function as well as passing a parameter.
|
||||
</pre>
|
||||
|
||||
### `task_count(executor?)`
|
||||
|
||||
If no argument provided, returns total number of tasks being executed in parallel at this moment using scarpet
|
||||
threading system. If the executor is provided, returns number of active tasks for that provider. Use `task_count(null)`
|
||||
to get the task count of the default executor only.
|
||||
|
||||
### `task_value(task)`
|
||||
|
||||
Returns the task return value, or `null` if task hasn't finished yet. Its a non-blocking operation. Unlike `join_task`,
|
||||
can be called on any task at any point
|
||||
|
||||
### `task_join(task)`
|
||||
|
||||
Waits for the task completion and returns its computed value. If the task has already finished returns it immediately.
|
||||
Unless taking the task value directly, i.e. via `task_value`, this operation is blocking. Since Minecraft has a
|
||||
limitation that all world access operations have to be performed on the main game thread in the off-tick time,
|
||||
joining any tasks that use Minecraft API from the main thread would mean automatic lock, so joining from the main
|
||||
thread is not allowed. Join tasks from other threads, if you really need to, or communicate asynchronously with
|
||||
the task via globals or function data / arguments to monitor its progress, communicate, get partial results,
|
||||
or signal termination.
|
||||
|
||||
### `task_completed(task)`
|
||||
|
||||
Returns true if task has completed, or false otherwise.
|
||||
|
||||
### `synchronize(lock, expression)`
|
||||
|
||||
Evaluates `expression` synchronized with respect to the lock `lock`. Returns the value of the expression.
|
||||
|
||||
* * *
|
||||
|
||||
## Auxiliary functions
|
||||
|
||||
### `lower(expr), upper(expr), title(expr)`
|
||||
|
||||
Returns lowercase, uppercase or titlecase representation of a string representation of the passed expression
|
||||
|
||||
<pre>
|
||||
lower('aBc') => 'abc'
|
||||
upper('aBc') => 'ABC'
|
||||
title('aBc') => 'Abc'
|
||||
</pre>
|
||||
|
||||
### `replace(string, regex, repl?); replace_first(string, regex, repl?)`
|
||||
|
||||
Replaces all, or first occurence of a regular expression in the string with `repl` expression,
|
||||
or nothing, if not specified
|
||||
|
||||
<pre>
|
||||
replace('abbccddebfg','b+','z') // => azccddezfg
|
||||
replace_first('abbccddebfg','b+','z') // => azccddebfg
|
||||
</pre>
|
||||
|
||||
### `length(expr)`
|
||||
|
||||
Returns length of the expression, the length of the string, the length of the integer part of the number,
|
||||
or length of the list
|
||||
|
||||
<pre>
|
||||
length(pi) => 1
|
||||
length(pi*pi) => 1
|
||||
length(pi^pi) => 2
|
||||
length(l()) => 0
|
||||
length(l(1,2,3)) => 3
|
||||
length('') => 0
|
||||
length('foo') => 3
|
||||
</pre>
|
||||
|
||||
### `rand(expr), rand(expr, seed)`
|
||||
|
||||
returns a random number from `0.0` (inclusive) to `expr` (exclusive). In boolean context (in conditions,
|
||||
boolean functions, or `bool`), returns false if the randomly selected value is less than 1\. This means
|
||||
that `rand(2)` returns true half of the time and `rand(5)` returns true for 80% (4/5) of the time. If seed is not
|
||||
provided, uses a random seed. If seed is provided, each consecutive call to rand() will act like 'next' call to the
|
||||
same random object. Scarpet keeps track of up to 1024 custom random number generators, so if you exceed this number
|
||||
(per app), then your random sequence will revert to the beginning.
|
||||
|
||||
<pre>
|
||||
map(range(10), floor(rand(10))) => [5, 8, 0, 6, 9, 3, 9, 9, 1, 8]
|
||||
map(range(10), bool(rand(2))) => [1, 1, 1, 0, 0, 1, 1, 0, 0, 0]
|
||||
map(range(10), str('%.1f',rand(_))) => [0.0, 0.4, 0.6, 1.9, 2.8, 3.8, 5.3, 2.2, 1.6, 5.6]
|
||||
</pre>
|
||||
|
||||
### `perlin(x), perlin(x, y), perlin(x, y, z), perlin(x, y, z, seed)`
|
||||
|
||||
returns a noise value from `0.0` to `1.0` (roughly) for 1, 2 or 3 dimensional coordinate. The default seed it samples
|
||||
from is `0`, but seed can be specified as a 4th argument as well. In case you need 1D or 2D noise values with custom
|
||||
seed, use `null` for `z`, or `y` and `z` arguments respectively.
|
||||
|
||||
Perlin noise is based on a square grid and generates rougher maps comparing to Simplex, which is creamier.
|
||||
Querying for lower-dimensional result, rather than affixing unused dimensions to constants has a speed benefit,
|
||||
|
||||
Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 perlin seeds
|
||||
used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more
|
||||
than 256 seeds at the same time, switching between them can get much more expensive.
|
||||
|
||||
### `simplex(x, y), simplex(x, y, z), simplex(x, y, z, seed)`
|
||||
|
||||
returns a noise value from `0.0` to `1.0` (roughly) for 2 or 3 dimensional coordinate. The default seed it samples
|
||||
from is `0`, but seed can be specified as a 4th argument as well. In case you need 2D noise values with custom seed,
|
||||
use `null` for `z` argument.
|
||||
|
||||
Simplex noise is based on a triangular grid and generates smoother maps comparing to Perlin. To sample 1D simplex
|
||||
noise, affix other coordinate to a constant.
|
||||
|
||||
Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 simplex seeds
|
||||
used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more
|
||||
than 256 seeds at the same time, switching between them can get much more expensive.
|
||||
|
||||
### `print(expr)`
|
||||
|
||||
prints the value of the expression to chat. Passes the result of the argument to the output unchanged,
|
||||
so `print`-statements can be weaved in code to debug programming issues
|
||||
|
||||
<pre>
|
||||
print('foo') => results in foo, prints: foo
|
||||
a = 1; print(a = 5) => results in 5, prints: 5
|
||||
a = 1; print(a) = 5 => results in 5, prints: 1
|
||||
print('pi = '+pi) => prints: pi = 3.141592653589793
|
||||
print(str('pi = %.2f',pi)) => prints: pi = 3.14
|
||||
</pre>
|
||||
|
||||
### `sleep(expr)`
|
||||
|
||||
Halts the execution of the program (and the game itself) for `expr` milliseconds. All in all, its better to
|
||||
use `game_tick(expr)` to let the game do its job while the program waits
|
||||
|
||||
<pre>
|
||||
sleep(50)
|
||||
</pre>
|
||||
|
||||
### `time()`
|
||||
|
||||
Returns the number of milliseconds since 'some point', like Java's `System.nanoTime()`.
|
||||
It returns a float, which has 1 microsecond precision (0.001 ms)
|
||||
|
||||
<pre>
|
||||
start_time = time();
|
||||
flip_my_world_upside_down();
|
||||
print(str('this took %d milliseconds',time()-start_time))
|
||||
</pre>
|
||||
|
||||
### `profile_expr(expression)`
|
||||
|
||||
Returns number of times given expression can be run in 50ms time. Useful to profile and optimize your code.
|
||||
Note that, even if its only a number, it WILL run these commands, so if they are destructive, you need to be careful.
|
||||
|
||||
* * *
|
||||
|
||||
## Access to variables and stored functions (use with caution)
|
||||
|
||||
### `var(expr)`
|
||||
|
||||
Returns the variable under the name of the string value of the expression. Allows to manipulate variables in more
|
||||
programmatic manner, which allows to use local variable set with a hash map type key-value access,
|
||||
can also be used with global variables
|
||||
|
||||
<pre>
|
||||
a = 1; var('a') = 'foo'; a => a == 'foo'
|
||||
</pre>
|
||||
|
||||
### `undef(expr)`
|
||||
|
||||
Removes all bindings of a variable with a name of `expr`. Removes also all function definitions with that name.
|
||||
It can affect global variable pool, and local variable set for a particular function.
|
||||
|
||||
<pre>
|
||||
inc(i) -> i+1; foo = 5; inc(foo) => 6
|
||||
inc(i) -> i+1; foo = 5; undef('foo'); inc(foo) => 1
|
||||
inc(i) -> i+1; foo = 5; undef('inc'); undef('foo'); inc(foo) => Error: Function inc is not defined yet at pos 53
|
||||
undef('pi') => bad idea - removes hidden variable holding the pi value
|
||||
undef('true') => even worse idea, unbinds global true value, all references to true would now refer to the default 0
|
||||
</pre>
|
||||
|
||||
### `vars(prefix)`
|
||||
|
||||
It returns all names of variables from local scope (if prefix does not start with 'global') or global variables
|
||||
(otherwise). Here is a larger example that uses combination of `vars` and `var` functions to be
|
||||
used for object counting
|
||||
|
||||
<pre>
|
||||
/script run
|
||||
$ count_blocks(ent) -> (
|
||||
$ l(cx, cy, cz) = query(ent, 'pos');
|
||||
$ scan(cx, cy, cz, 16, 16, 16, var('count_'+_) += 1);
|
||||
$ for ( sort_key( vars('count_'), -var(_)),
|
||||
$ print(str( '%s: %d', slice(_,6), var(_) ))
|
||||
$ )
|
||||
$ )
|
||||
/script run count_blocks(player())
|
||||
</pre>
|
||||
|
||||
* * *
|
||||
|
||||
## System key-value storage
|
||||
|
||||
Scarpet runs apps in isolation. The can share code via use of shared libraries, but each library that is imported to
|
||||
each app is specific to that app. Apps can store and fetch state from disk, but its restricted to specific locations
|
||||
meaning apps cannot interact via disk either. To facilitate communication for interappperability, scarpet hosts its
|
||||
own key-value storage that is shared between all apps currently running on the host, providing methods for getting an
|
||||
associated value with optional setting it if not present, and an operation of modifying a content of a system
|
||||
global value.
|
||||
|
||||
### `system_variable_get(key, default_value ?)`
|
||||
|
||||
Returns the variable from the system shared key-value storage keyed with a `key` value, optionally if value is
|
||||
not present, and default expression is provided, sets a new value to be associated with that key
|
||||
|
||||
### `system_variable_set(key, new_value)`
|
||||
|
||||
Returns the variable from the system shared key-value storage keyed with a `key` value, and sets a new
|
||||
mapping for the key
|
||||
@@ -0,0 +1,29 @@
|
||||
# Variables and Constants
|
||||
|
||||
`scarpet` provides a number of constants that can be used literally in scripts
|
||||
|
||||
* `null`: nothing, zilch, not even false
|
||||
* `true`: pure true, or just 1 (one)
|
||||
* `false`: false truth, or true falsth, 0 (zero) actually
|
||||
* `pi`: for the fans of perimeters, its a perimeter of an apple pi of diameter 1\. About 3.14
|
||||
* `euler`: clever guy. Derivative of its exponent is goto 1\. About 2.72
|
||||
|
||||
Apart from that, there is a bunch of system variables, that start with `_` that are set by `scarpet` built-ins,
|
||||
like `_`, typically each consecutive value in loops, `_i` indicating iteration, or `_a` like an accumulator
|
||||
for `reduce` function. Certain calls to Minecraft specific calls would also set `_x`, `_y`, `_z`, indicating
|
||||
block positions. All variables starting with `_` are read-only, and cannot be declared and modified in client code.
|
||||
|
||||
## Literals
|
||||
|
||||
`scarpet` accepts numeric and string liters constants. Numbers look like `1, 2.5, -3e-7, 0xff,` and are internally
|
||||
represented as Java's `double` but `scarpet` will try to trim trailing zeros as much as possible so if you need to
|
||||
use them as intergers, you can. Strings using single quoting, for multiple reasons, but primarily to allow for
|
||||
easier use of strings inside doubly quoted command arguments (when passing a script as a parameter of `/script fill`
|
||||
for example, or when typing in jsons inside scarpet to feed back into a `/data merge` command for example.
|
||||
Strings also use backslashes `\` for quoting special characters, in both plain strings and regular expressions
|
||||
|
||||
<pre>
|
||||
'foo'
|
||||
print('This doesn\'t work')
|
||||
nbt ~ '\\.foo' // matching '.' as a '.', not 'any character match'
|
||||
</pre>
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user