Merge pull request #112 from sbrl/selection-tools-refactor

Selection tools refactor
This commit is contained in:
VorTechnix 2024-10-01 11:52:04 -07:00 committed by GitHub
commit f24ceffd2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 660 additions and 618 deletions

1
.gitignore vendored

@ -242,6 +242,7 @@ temp/
# VorTechnix stuff # VorTechnix stuff
.vdev/ .vdev/
*.old.lua
# Created by https://www.toptal.com/developers/gitignore/api/archives # Created by https://www.toptal.com/developers/gitignore/api/archives
# Edit at https://www.toptal.com/developers/gitignore?templates=archives # Edit at https://www.toptal.com/developers/gitignore?templates=archives

@ -96,6 +96,15 @@ describe("parse_axes", function()
assert.are.same(Vector3.new(40, 77, 99), maxv) assert.are.same(Vector3.new(40, 77, 99), maxv)
end) end)
it("should infer that directions before a value are connected to that value", function()
local minv, maxv = parse_axes({
"xy", "-x", "5"
}, facing_dirs.z_neg)
assert.is.truthy(minv)
assert.are.same(Vector3.new(-5, 0, 0), minv)
assert.are.same(Vector3.new(5, 5, 0), maxv)
end)
it("should return 2 0,0,0 vectors if no input", function() it("should return 2 0,0,0 vectors if no input", function()
local minv, maxv = parse_axes({ local minv, maxv = parse_axes({
-- No input -- No input

@ -4,7 +4,7 @@ It's about time I started a changelog! This will serve from now on as the main c
Note to self: See the bottom of this file for the release template text. Note to self: See the bottom of this file for the release template text.
## v1.15: The untitled update (unreleased) ## v1.15: The direction update (unreleased)
- Added the optional argument `all` to [`//unmark`](https://worldeditadditions.mooncarrot.space/Reference/#unmark) - Added the optional argument `all` to [`//unmark`](https://worldeditadditions.mooncarrot.space/Reference/#unmark)
- Added a (rather nuclear) fix (attempt 4) at finally exterminating all zombie region marker walls forever - Added a (rather nuclear) fix (attempt 4) at finally exterminating all zombie region marker walls forever
- This is not a hotfix to avoid endless small releases fixing the bug, as it's clear it's much more difficult to fix on all systems than initially expected - This is not a hotfix to avoid endless small releases fixing the bug, as it's clear it's much more difficult to fix on all systems than initially expected
@ -16,11 +16,21 @@ Note to self: See the bottom of this file for the release template text.
- Added [`//set+`](https://worldeditadditions.mooncarrot.space/Reference/#set) for setting nodes and param2/light levels quickly. - Added [`//set+`](https://worldeditadditions.mooncarrot.space/Reference/#set) for setting nodes and param2/light levels quickly.
- NOTE TO SELF: Setting light values doesn't appear to be working very well for some reason - NOTE TO SELF: Setting light values doesn't appear to be working very well for some reason
- Added [`//ndef`](https://worldeditadditions.mooncarrot.space/Reference/#ndef) to print a given node's definition table. This is for debugging and development purposes. - Added [`//ndef`](https://worldeditadditions.mooncarrot.space/Reference/#ndef) to print a given node's definition table. This is for debugging and development purposes.
- Added `//sgrow` and `//sshrink` commands to enlarge and shrink selection regions and aliased them over WorldEdit equivalents (`//expand`, `//outset` and `//contract`, `//inset` respectively).
- Added Unified Axis Syntax (UAS) parser. - Implementation by @VorTechnix
- See [UAS System reference] for details. (Note to self hook up hyperlink)
- Added `//uasparse` command to show the vectors produced by a given UAS expression. - Implementation by @VorTechnix
### Bugfixes and changes ### Bugfixes and changes
- Don't warn on failed registration of `//flora` → [`//bonemeal`](https://worldeditadditions.mooncarrot.space/Reference/#bonemeal) if the `bonemeal` mod isn't installed (e.g. in MineClone2) - thanks @VorTechnix in #106 - Don't warn on failed registration of `//flora` → [`//bonemeal`](https://worldeditadditions.mooncarrot.space/Reference/#bonemeal) if the `bonemeal` mod isn't installed (e.g. in MineClone2) - thanks @VorTechnix in #106
- Improve documentation of [`//noise2d`](https://worldeditadditions.mooncarrot.space/Reference/#noise2d). If it still doesn't make sense, please let me know. It's a complicated command that needs reworking a bit to be easier to use. - Improve documentation of [`//noise2d`](https://worldeditadditions.mooncarrot.space/Reference/#noise2d). If it still doesn't make sense, please let me know. It's a complicated command that needs reworking a bit to be easier to use.
- Alias `//napply` to [`//nodeapply`](https://worldeditadditions.mooncarrot.space/Reference/#nodeapply) - Alias `//napply` to [`//nodeapply`](https://worldeditadditions.mooncarrot.space/Reference/#nodeapply)
- Re-factored selection tools to all use WEA position system and UAS Parser if applicable. - Re-factor by @VorTechnix
- `//sshift` now overrides `//shift` from WorldEdit. - Re-factor by @VorTechnix
### Deprecations
- Deprecated `//scol`, `//srect` and `//scube`. Now that `//srel` is using UAS parser there is no need for them. - Deprecated by @VorTechnix
- Deprecated `//sfactor`. Now that `//sgrow` and `//sshrink` exist it is no longer needed. - Deprecated by @VorTechnix
### Lua API changes ### Lua API changes

@ -1201,6 +1201,16 @@ This command is intended for debugging and development purposes, but if you're i
//ndef glass //ndef glass
``` ```
### `//uasparse <unified axis syntax>`
Short for *Unified Axis Syntax Parse*. Parses the given UAS expression and prints the resulting vectors to the chat window.
```weacmd
//uasparse front right 5
//uasparse y 12 h -2
//uasparse left 3 up 5 -front 7
//uasparse -z 12 -y -2 x -2
```
## Selection ## Selection
@ -1212,6 +1222,58 @@ This command is intended for debugging and development purposes, but if you're i
███████ ███████ ███████ ███████ ██████ ██ ██ ██████ ██ ████ ███████ ███████ ███████ ███████ ██████ ██ ██ ██████ ██ ████
--> -->
### Unified Axis Syntax (UAS)
The Unified Axis Syntax system allows users to input direction and distance information in three dimensions using "natural" measurement syntax. The key features include axis clumping, double negatives, relative directions, mirroring and compass directions (more information below).
*Note: negatives can be applied to axes, directions **AND** distances*
#### Relative Directions
|Key Words | Interpretation|
|----------|---------------|
|`f[ront]\|facing\|?` | The direction the player is facing most toward in the world
|`b[ack]\|behind\|rear` | The opposite of the direction the player is facing most toward in the world
|`l[eft]` | The direction to the left of the player
|`r[ight]` | The direction to the right of the player
```weacmd
back 1
f r 4
-facing 13
```
#### Compass Directions
|Key Words | Interpretation|
|----------|---------------|
|`n[orth]` | z
|`s[outh]` | -z
|`e[ast]` | x
|`w[est]` | -x
|`u[p]` | y
|`d[own]` | -y
```weacmd
south 3
north west 5
e d -2
```
#### Axis Clumping
Supported axes are `x`, `y`, `z`, `h`, `v`. All horizontal axes are covered by `h` and both vertical ones are covered by `v`.
- `h 5` == `xz -xz 5` == `x 5 z 5 x -5 z -5`
- `v 5` == `up down 5` == `y -y 5`
- `vxz 5` == `xyz -y 5` == `xyz 5 y -5`
#### Inference and Omnidirectionality
The UAS parser takes command input that is split by whitespace and interprets it as a series of numbers preceded by directional cues. If a number is preceded by another number or nothing it assumes that the number is to be applied on all axes in both positive and negative directions.
- `10` == `hv 10` == `xyz -xyz 10`
- `x 3 6` == `x 3 hv 6`
From the above examples you can also see the principle of inference. All direction modifiers before a value are interpreted as belonging to that value. So `x v 5` is equivalent to `x 5 v 5` and `xv 5`.
Because UAS parses "natural" measurement syntax, there are many ways to express the same direction and distance. This caters to users with different ways of thinking and different play styles which will hopefully make the tools easier to use.
### `//unmark` ### `//unmark`
> First overridden in v1.14 > First overridden in v1.14
@ -1310,7 +1372,6 @@ Short for _select column_. Sets the pos2 at a set distance along 1 axis from pos
//scol x 3 //scol x 3
``` ```
### `//srect [<axis1> [<axis2>]] <length>` ### `//srect [<axis1> [<axis2>]] <length>`
> Added in v1.12; deprecated in favour of [`//srel`](#srel) in v1.15 > Added in v1.12; deprecated in favour of [`//srel`](#srel) in v1.15
@ -1365,17 +1426,43 @@ Short for _select center_. Sets pos1 and pos2 to the centre point(s) of the curr
//scentre //scentre
``` ```
### `//srel <axis1> <length1> [<axis2> <length2> [<axis3> <length3>]]` ### `//srel <unified axis syntax>`
Short for _select relative_. Sets the pos2 at set distances along 3 axes relative to pos1. If pos1 is not set it will default to the node directly under the player. The axis arguments accept `x, y, z` as well as `up, down, left, right, front, back`. Left, right, front and back are relative to player facing direction. Negative (`-`) can be applied to the axis, the length or both. Implementation thanks to @VorTechnix. Short for _select relative_. Sets the pos2 at set distances along 3 axes relative to pos1. If pos1 is not set it will default to the node directly under the player. The axis arguments accept `x, y, z` as well as `up, down, left, right, front, back`. Left, right, front and back are relative to player facing direction. Negative (`-`) can be applied to the axis, the length or both. Implementation thanks to @VorTechnix.
```weacmd ```weacmd
//srel front 5 //srel front 5
//srel y 12 right -2 //srel y 12 right -2
//srel left 3 up 5 -front 7 //srel left 3 up 5 -front 7
//srel -z 12 -y -2 x -2 //srel -z 12 -y -2 x -2
``` ```
### `//sshift <axis1> <length1> [<axis2> <length2> [<axis3> <length3>]]` ### `//sgrow <unified axis syntax>`
> Added in v1.15
Short for _selection grow_. Grows the current selection along specified axes/directions.
Aliases: `//extend`, `//outset`.
```weacmd
//sgrow back 4
//sgrow left -2 v r 2
//sgrow h 4
//sgrow -zy -2 x -2
```
### `//sshrink <unified axis syntax>`
> Added in v1.15
Short for _selection shrink_. Shrinks the current selection along specified axes/directions.
Aliases: `//contract`, `//inset`.
```weacmd
//sshrink left 4
//sshrink right -2 up 2
//sshrink v 4
//sshrink -hy 2 x -3 true
```
### `//sshift <unified axis syntax>`
> Added in v1.13 > Added in v1.13
Short for _selection shift_. Shifts the WorldEdit region along 3 axes. The axis arguments accept `x, y, z` as well as `up, down, left, right, front, back`. Left, right, front and back are relative to player facing direction. Negative (`-`) can be applied to the axis, the length or both. Implementation thanks to @VorTechnix. Short for _selection shift_. Shifts the WorldEdit region along 3 axes. The axis arguments accept `x, y, z` as well as `up, down, left, right, front, back`. Left, right, front and back are relative to player facing direction. Negative (`-`) can be applied to the axis, the length or both. Implementation thanks to @VorTechnix.

@ -80,20 +80,23 @@ The detailed explanations have moved! Check them out [here](https://worldeditadd
- [`//basename <name>`](https://worldeditadditions.mooncarrot.space/Reference/#basename) - [`//basename <name>`](https://worldeditadditions.mooncarrot.space/Reference/#basename)
- [`//ngroups <node_name> [v[erbose]]`](https://worldeditadditions.mooncarrot.space/Reference/#ngroups) _(new in v1.15)_ - [`//ngroups <node_name> [v[erbose]]`](https://worldeditadditions.mooncarrot.space/Reference/#ngroups) _(new in v1.15)_
- [`//ndef <node_name>`](https://worldeditadditions.mooncarrot.space/Reference/#ndef) _(new in v1.15)_ - [`//ndef <node_name>`](https://worldeditadditions.mooncarrot.space/Reference/#ndef) _(new in v1.15)_
- [`//uasparse <unified axis syntax>`](https://worldeditadditions.mooncarrot.space/Reference/#uasparse) _(new in v1.15)_
### Selection ### Selection
- [`//scol [<axis1> ] <length>`](https://worldeditadditions.mooncarrot.space/Reference/#scol) - [~~`//scol [<axis1> ] <length>`~~](https://worldeditadditions.mooncarrot.space/Reference/#scol) (REMOVED in v1.15)
- [`//srect [<axis1> [<axis2>]] <length>`](https://worldeditadditions.mooncarrot.space/Reference/#srect) - [~~`//srect [<axis1> [<axis2>]] <length>`~~](https://worldeditadditions.mooncarrot.space/Reference/#srect) (REMOVED in v1.15)
- [`//scube [<axis1> [<axis2> [<axis3>]]] <length>`](https://worldeditadditions.mooncarrot.space/Reference/#scube) - [~~`//scube [<axis1> [<axis2> [<axis3>]]] <length>`~~](https://worldeditadditions.mooncarrot.space/Reference/#scube) (REMOVED in v1.15)
- [`//scloud <0-6|stop|reset>`](https://worldeditadditions.mooncarrot.space/Reference/#scloud) - [`//scloud <0-6|stop|reset>`](https://worldeditadditions.mooncarrot.space/Reference/#scloud)
- [`//scentre`](https://worldeditadditions.mooncarrot.space/Reference/#scentre) - [`//scentre`](https://worldeditadditions.mooncarrot.space/Reference/#scentre)
- [`//srel <axis1> <length1> [<axis2> <length2> [<axis3> <length3>]]`](https://worldeditadditions.mooncarrot.space/Reference/#srel) - [`//sgrow <unified axis syntax>`](https://worldeditadditions.mooncarrot.space/Reference/#sgrow) _(new in v1.15)_
- [`//srel <unified axis syntax>`](https://worldeditadditions.mooncarrot.space/Reference/#srel)
- [`//smake <operation:odd|even|equal> <mode:grow|shrink|average> [<target=xz> [<base>]]`](https://worldeditadditions.mooncarrot.space/Reference/#smake) - [`//smake <operation:odd|even|equal> <mode:grow|shrink|average> [<target=xz> [<base>]]`](https://worldeditadditions.mooncarrot.space/Reference/#smake)
- [`//sshrink <unified axis syntax>`](https://worldeditadditions.mooncarrot.space/Reference/#sshrink) _(new in v1.15)_
- [`//sstack`](https://worldeditadditions.mooncarrot.space/Reference/#sstack) - [`//sstack`](https://worldeditadditions.mooncarrot.space/Reference/#sstack)
- [`//spush`](https://worldeditadditions.mooncarrot.space/Reference/#spush) - [`//spush`](https://worldeditadditions.mooncarrot.space/Reference/#spush)
- [`//spop`](https://worldeditadditions.mooncarrot.space/Reference/#spop) - [`//spop`](https://worldeditadditions.mooncarrot.space/Reference/#spop)
- [`//sshift <axis1> <length1> [<axis2> <length2> [<axis3> <length3>]]`](https://worldeditadditions.mooncarrot.space/Reference/#sshift) - [`//sshift <unified axis syntax>`](https://worldeditadditions.mooncarrot.space/Reference/#sshift)
- [`//sfactor <mode:grow|shrink|average> <factor> [<target=xz>]`](https://worldeditadditions.mooncarrot.space/Reference/#sfactor) - [~~`//sfactor <mode:grow|shrink|average> <factor> [<target=xz>]`~~](https://worldeditadditions.mooncarrot.space/Reference/#sfactor) (REMOVED in v1.15)
- [`//pos <index>`](https://worldeditadditions.mooncarrot.space/Reference/#pos) - [`//pos <index>`](https://worldeditadditions.mooncarrot.space/Reference/#pos)
- [`//pos1`](https://worldeditadditions.mooncarrot.space/Reference/#pos1) - [`//pos1`](https://worldeditadditions.mooncarrot.space/Reference/#pos1)
- [`//pos2`](https://worldeditadditions.mooncarrot.space/Reference/#pos2) - [`//pos2`](https://worldeditadditions.mooncarrot.space/Reference/#pos2)

@ -1,34 +1,46 @@
-- ███████ ███████ ██ ███████ ██████ ████████ ██████ ██████ ███████ -- ███████ ███████ ██ ███████ ██████ ████████ ██████ ██████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ █████ ██ █████ ██ ██ ██ ██ ██████ ███████ -- ███████ █████ ██ █████ ██ ██ ██ ██ ██████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ███████ ███████ ███████ ██████ ██ ██████ ██ ██ ███████ -- ███████ ███████ ███████ ███████ ██████ ██ ██████ ██ ██ ███████
-- Chat commands that operate on selections. -- Chat commands that operate on selections.
local wea_cmdpath = worldeditadditions_commands.modpath .. "/commands/selectors/" local wea_cmdpath = worldeditadditions_commands.modpath .. "/commands/selectors/"
local weac = worldeditadditions_core local weac = worldeditadditions_core
dofile(wea_cmdpath.."srel.lua") dofile(wea_cmdpath.."scentre.lua")
dofile(wea_cmdpath.."scentre.lua") dofile(wea_cmdpath.."scloud.lua")
dofile(wea_cmdpath.."scloud.lua") dofile(wea_cmdpath.."sgrow.lua")
dofile(wea_cmdpath.."scol.lua") dofile(wea_cmdpath.."smake.lua")
dofile(wea_cmdpath.."scube.lua") dofile(wea_cmdpath.."spop.lua")
dofile(wea_cmdpath.."sfactor.lua") dofile(wea_cmdpath.."spush.lua")
dofile(wea_cmdpath.."smake.lua") dofile(wea_cmdpath.."srel.lua")
dofile(wea_cmdpath.."spop.lua") dofile(wea_cmdpath.."sshift.lua")
dofile(wea_cmdpath.."spush.lua") dofile(wea_cmdpath.."sshrink.lua")
dofile(wea_cmdpath.."srect.lua") dofile(wea_cmdpath.."sstack.lua")
dofile(wea_cmdpath.."sshift.lua")
dofile(wea_cmdpath.."sstack.lua") --- DEPRECATED ---
dofile(wea_cmdpath.."scol.lua")
dofile(wea_cmdpath.."unmark.lua") dofile(wea_cmdpath.."scube.lua")
dofile(wea_cmdpath.."mark.lua") dofile(wea_cmdpath.."srect.lua")
dofile(wea_cmdpath.."pos1-2.lua") dofile(wea_cmdpath.."sfactor.lua")
dofile(wea_cmdpath.."reset.lua") --- END DEPRECATED ---
-- Aliases dofile(wea_cmdpath.."unmark.lua")
weac.register_alias("sfac", "sfactor") dofile(wea_cmdpath.."mark.lua")
dofile(wea_cmdpath.."pos1-2.lua")
weac.register_alias("1", "pos1", true) -- true = override target dofile(wea_cmdpath.."reset.lua")
weac.register_alias("2", "pos2", true) -- true = override target
-- Aliases
weac.register_alias("sfac", "sfactor")
weac.register_alias("sgrow", "expand", true) -- true = override target
weac.register_alias("sgrow", "outset", true) -- true = override target
weac.register_alias("sshrink", "contract", true) -- true = override target
weac.register_alias("sshrink", "inset", true) -- true = override target
weac.register_alias("sshift", "shift", true) -- true = override target
weac.register_alias("1", "pos1", true) -- true = override target
weac.register_alias("2", "pos2", true) -- true = override target

@ -1,35 +1,33 @@
local wea_c = worldeditadditions_core local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
-- ███████ ██████ ███████ ███ ██ ████████ ███████ ██████ -- ███████ ██████ ███████ ███ ██ ████████ ███████ ██████
-- ██ ██ ██ ████ ██ ██ ██ ██ ██ -- ██ ██ ██ ████ ██ ██ ██ ██ ██
-- ███████ ██ █████ ██ ██ ██ ██ █████ ██████ -- ███████ ██ █████ ██ ██ ██ ██ █████ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ██████ ███████ ██ ████ ██ ███████ ██ ██ -- ███████ ██████ ███████ ██ ████ ██ ███████ ██ ██
worldeditadditions_core.register_command("scentre", { worldeditadditions_core.register_command("scentre", {
params = "", params = "",
description = "Set WorldEdit region positions 1 and 2 to the centre of the current selection.", description = "Set WorldEdit region positions 1 and 2 to the centre of the current selection.",
privs = {worldedit=true}, privs = {worldedit=true},
require_pos = 2, require_pos = 2,
parse = function(params_text) parse = function(params_text)
return true return true
end, end,
func = function(name) func = function(name)
local mean = Vector3.mean( local mean = Vector3.mean(
Vector3.clone(worldedit.pos1[name]), Vector3.clone(wea_c.pos.get(name, 1)),
Vector3.clone(worldedit.pos2[name]) Vector3.clone(wea_c.pos.get(name, 2))
) )
local pos1, pos2 = Vector3.clone(mean), Vector3.clone(mean) local pos1, pos2 = Vector3.clone(mean), Vector3.clone(mean)
pos1 = pos1:floor() pos1 = pos1:floor()
pos2 = pos2:ceil() pos2 = pos2:ceil()
worldedit.pos1[name], worldedit.pos2[name] = pos1, pos2 wea_c.pos.set_all(name, {pos1, pos2})
worldedit.mark_pos1(name)
worldedit.mark_pos2(name) return true, "Position 1 to "..pos1..", Position 2 to "..pos2
end,
return true, "position 1 set to "..pos1..", position 2 set to "..pos2 })
end,
}) -- lua print(vecs.mean.x..", "..vecs.mean.y..", "..vecs.mean.z)
-- lua print(vecs.mean.x..", "..vecs.mean.y..", "..vecs.mean.z)

@ -7,37 +7,15 @@ local Vector3 = wea_c.Vector3
-- ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██
-- ███████ ██████ ██████ ███████ -- ███████ ██████ ██████ ███████
worldeditadditions_core.register_command("scol", { worldeditadditions_core.register_command("scol", {
params = "[<axis1>] <length>", params = "None",
description = "Set WorldEdit region position 2 at a set distance along 1 axis.", description = "DEPRECATED: please use //srel instead.",
privs = {worldedit=true}, privs = { worldedit = true },
require_pos = 1, require_pos = 1,
parse = function(params_text) parse = function(params_text)
local vec, tmp = Vector3.new(0, 0, 0), {} return params_text
local find = wea_c.split(params_text, "%s", false)
local ax1, sn1, len = (tostring(find[1]):match('[xyz]') or "g"):sub(1,1), wea_c.getsign(find[1]), find[table.maxn(find)]
tmp.len = tonumber(len)
-- If len == nil cancel the operation
if not tmp.len then return false, "No length specified." end
-- If ax1 is bad send "get" order
if ax1 == "g" then tmp.get = true
else vec[ax1] = sn1 * tmp.len end
return true, vec, tmp
-- tmp carries:
-- The length (len) arguement to the main function for use if "get" is invoked there
-- The bool value "get" to tell the main function if it needs to populate missing information in vec
end, end,
func = function(name, vec, tmp) func = function(name, paramtext)
if tmp.get then return false, "DEPRECATED: please use //srel instead..."
local ax, dir = wea_c.player_axis2d(name)
vec[ax] = tmp.len * dir
end
local pos2 = vec + Vector3.clone(worldedit.pos1[name])
worldedit.pos2[name] = pos2
worldedit.mark_pos2(name)
return true, "position 2 set to "..pos2
end, end,
}) })

@ -7,48 +7,15 @@ local Vector3 = wea_c.Vector3
-- ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██
-- ███████ ██████ ██████ ██████ ███████ -- ███████ ██████ ██████ ██████ ███████
worldeditadditions_core.register_command("scube", { worldeditadditions_core.register_command("scube", {
params = "[<axis1> [<axis2> [<axis3>]]] <length>", params = "None",
description = "Set WorldEdit region position 2 at a set distance along 3 axes.", description = "DEPRECATED: please use //srel instead.",
privs = { worldedit = true }, privs = { worldedit = true },
require_pos = 1, require_pos = 1,
parse = function(params_text) parse = function(params_text)
local vec, tmp = Vector3.new(0, 0, 0), {} return params_text
local find = wea_c.split(params_text, "%s", false)
local ax1, ax2, ax3 = (tostring(find[1]):match('[xyz]') or "g"):sub(1,1), (tostring(find[2]):match('[xyz]') or "g"):sub(1,1),
(tostring(find[3]):match('[xyz]') or "g"):sub(1,1)
local sn1, sn2, sn3, len = wea_c.getsign(find[1]), wea_c.getsign(find[2]), wea_c.getsign(find[3]), find[table.maxn(find)]
tmp.len = tonumber(len)
-- If len is nill cancel the operation
if not tmp.len then return false, "No length specified." end
-- If axis is bad send "get" order
if ax1 == "g" then tmp.get = true
else vec[ax1] = sn1 * tmp.len end
if ax2 == "g" then tmp.get = true
else vec[ax2] = sn2 * tmp.len end
if ax3 == "g" then tmp.get = true
else vec[ax3] = sn3 * tmp.len end
tmp.axes = ax1..","..ax2..","..ax3
return true, vec, tmp
-- tmp carries:
-- The length (len) arguement to the main function for use if "get" is invoked there
-- The bool value "get" to tell the main function if it needs to populate missing information in vec
-- The string "axes" to tell the main function what axes are and/or need to be populated if "get" is invoked
end, end,
func = function(name, vec, tmp) func = function(name, paramtext)
if tmp.get then return false, "DEPRECATED: please use //srel instead..."
local ax, dir = wea_c.player_axis2d(name)
local _, left, sn = wea_c.axis_left(ax,dir)
if not tmp.axes:find("x") then vec.x = tmp.len * (ax == "x" and dir or sn) end
if not tmp.axes:find("z") then vec.z = tmp.len * (ax == "z" and dir or sn) end
if not tmp.axes:find("y") then vec.y = tmp.len end
end
local pos2 = vec + Vector3.clone(worldedit.pos1[name])
worldedit.pos2[name] = pos2
worldedit.mark_pos2(name)
return true, "position 2 set to "..pos2
end, end,
}) })

@ -1,87 +1,22 @@
local wea = worldeditadditions local wea = worldeditadditions
local wea_c = worldeditadditions_core local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
-- ███████ ███████ █████ ██████ ████████ ██████ ██████ -- ███████ ███████ █████ ██████ ████████ ██████ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ █████ ███████ ██ ██ ██ ██ ██████ -- ███████ █████ ███████ ██ ██ ██ ██ ██████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ██ ██ ██ ██████ ██ ██████ ██ ██ -- ███████ ██ ██ ██ ██████ ██ ██████ ██ ██
worldeditadditions_core.register_command("sfactor", { worldeditadditions_core.register_command("sfactor", {
params = "<mode> <factor> [<target=xz>]", params = "None",
description = "Make the length of one or more target axes of the current selection to be multiple(s) of <factor>.", description = "DEPRECATED: please use //grow or //shrink instead.",
privs = { worldedit = true },
require_pos = 2, privs = { worldedit = true },
parse = function(params_text) parse = function(params_text)
local parts = wea_c.split(params_text, "%s+", false) return params_text
end,
if #parts < 2 then func = function(name, paramtext)
return false, "Error: Not enough arguments. Expected \"<mode> <factor> [<target>]\"." return false, "DEPRECATED: please use //grow or //shrink instead..."
end end
local mode, fac, targ = wea_c.table.unpack(parts) })
local modeSet = wea_c.table.makeset {"grow", "shrink", "avg"}
-- Mode parsing
if mode == "average" then -- If mode is average set to avg
mode = "avg"
elseif not modeSet[mode] then -- If mode is invalid throw error
return false, "Error: Invalid <mode> \""..mode.."\". Expected \"grow\", \"shrink\", or \"average\"/\"avg\"."
end
-- Factor parsing
local factest = tonumber(fac)
if not factest then
return false, "Error: Invalid <factor> \""..fac.."\". Expected a number."
elseif factest < 2 then
return false, "Error: <factor> is too low. Expected a number equal to or greater than 2."
else
fac = math.floor(factest+0.5)
end
-- Target parsing
if not targ then -- If no target set to default (xz)
targ = "xz"
elseif targ:match("[xyz]+") then -- ensure correct target syntax
targ = table.concat(wea_c.tochars(targ:match("[xyz]+"),true,true))
else
return false, "Error: Invalid <target> \""..targ.."\". Expected \"x\" and or \"y\" and or \"z\"."
end
return true, mode, fac, targ
end,
func = function(name, mode, fac, targ)
local pos1, pos2 = Vector3.clone(worldedit.pos1[name]), Vector3.clone(worldedit.pos2[name])
local delta = pos2 - pos1 -- local delta equation: Vd(a) = V2(a) - V1(a)
local _tl = #targ -- Get targ length as a variable incase mode is "average"/"avg"
local targ = wea_c.tocharset(targ) -- Break up targ string into set table
local _m = 0 -- _m is the container to hold the average of the axes in targ
-- set _m to the max, min or mean of the target axes depending on mode (_tl is the length of targ) or base if it exists
if mode == "avg" then
for k,v in pairs(targ) do _m = _m + math.abs(delta[k]) end
_m = _m / _tl
end
-- Equation: round(delta[<axis>] / factor) * factor
local eval = function(int,fac_inner)
local tmp, abs, neg = int / fac_inner, math.abs(int), int < 0
if mode == "avg" then
if int > _m then int = math.floor(abs / fac_inner) * fac_inner
else int = math.ceil(abs / fac_inner) * fac_inner end
elseif mode == "shrink" then int = math.floor(abs / fac_inner) * fac_inner
else int = math.ceil(abs / fac_inner) * fac_inner end
if int < fac_inner then int = fac_inner end -- Ensure selection doesn't collapse to 0
if neg then int = int * -1 end -- Ensure correct facing direction
return int
end
for k,v in pairs(targ) do delta[k] = eval(delta[k],fac) end
worldedit.pos2[name] = pos1 + delta
worldedit.mark_pos2(name)
return true, "position 2 set to "..pos2
end
})

@ -0,0 +1,42 @@
-- local wea = worldeditadditions
local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3
-- ███████ ██████ ██████ ██████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ██ ███ ██████ ██ ██ ██ █ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ███ ██
-- ███████ ██████ ██ ██ ██████ ███ ███
worldeditadditions_core.register_command("sgrow", {
params = "<unified axis syntax>",
description = "Grow selection region",
privs = { worldedit = true },
require_pos = 0,
parse = function(params_text)
local ret = wea_c.split(params_text)
if #ret < 1 then return false, "SGROW: No params found!"
else return true, ret end
end,
func = function(name, params_text)
local facing = wea_c.player_dir(name)
local min, max = wea_c.parse.directions(params_text, facing)
if not min then return false, max end
local pos1 = wea_c.pos.get(name, 1)
local pos2 = wea_c.pos.get(name, 2)
if not pos2 then wea_c.pos.set(name, 2, pos1)
else pos1, pos2 = Vector3.sort(pos1, pos2) end
pos1, pos2 = pos1:add(min), pos2:add(max)
wea_c.pos.set_all(name, {pos1, pos2})
return true, "Position 1 to "..pos1..", Position 2 to "..pos2
end,
})
-- Tests
-- //srel front 5 left 3 y 2

@ -1,133 +1,131 @@
local wea = worldeditadditions local wea_c = worldeditadditions_core
local wea_c = worldeditadditions_core local Vector3 = wea_c.Vector3
local Vector3 = wea_c.Vector3
-- ███████ ███ ███ █████ ██ ██ ███████
-- ███████ ███ ███ █████ ██ ██ ███████ -- ██ ████ ████ ██ ██ ██ ██ ██
-- ██ ████ ████ ██ ██ ██ ██ ██ -- ███████ ██ ████ ██ ███████ █████ █████
-- ███████ ██ ████ ██ ███████ █████ █████ -- ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ -- ███████ ██ ██ ██ ██ ██ ██ ███████
-- ███████ ██ ██ ██ ██ ██ ██ ███████ worldeditadditions_core.register_command("smake", {
worldeditadditions_core.register_command("smake", { params = "<operation:odd|even|equal> <mode:grow|shrink|average> [<target=xyz> [<base>]]",
params = "<operation:odd|even|equal> <mode:grow|shrink|average> [<target=xyz> [<base>]]", description = "Make one or more axes of the current selection odd, even, or equal to another.",
description = "Make one or more axes of the current selection odd, even, or equal to another.", privs = { worldedit = true },
privs = { worldedit = true }, require_pos = 2,
require_pos = 2, parse = function(params_text)
parse = function(params_text) -- Split params_text, check for missing arguments and fill in empty spots
-- Split params_text, check for missing arguments and fill in empty spots local parts = wea_c.split(params_text, "%s+", false)
local parts = wea_c.split(params_text, "%s+", false) if #parts < 2 then
if #parts < 2 then return false, "Error: Not enough arguments. Expected \"<operation> <mode> [<target=xyz> [<base>]]\"."
return false, "Error: Not enough arguments. Expected \"<operation> <mode> [<target=xyz> [<base>]]\"." else
else for i=3,4 do if not parts[i] then parts[i] = false end end
for i=3,4 do if not parts[i] then parts[i] = false end end end
end
-- Initialize local variables and sets
-- Initialize local variables and sets local oper, mode, targ, base = wea_c.table.unpack(parts)
local oper, mode, targ, base = wea_c.table.unpack(parts) local operSet, modeSet = wea_c.table.makeset {"equal", "odd", "even"}, wea_c.table.makeset {"grow", "shrink", "avg"}
local operSet, modeSet = wea_c.table.makeset {"equal", "odd", "even"}, wea_c.table.makeset {"grow", "shrink", "avg"}
-- Main Logic
-- Main Logic -- Check base if base is present and if so valid.
-- Check base if base is present and if so valid. if base then
if base then if base:match("[xyz]") then -- ensure correct base syntax
if base:match("[xyz]") then -- ensure correct base syntax base = base:match("[xyz]")
base = base:match("[xyz]") else
else return false, "Error: Invalid base \""..base.."\". Expected \"x\", \"y\" or \"z\"."
return false, "Error: Invalid base \""..base.."\". Expected \"x\", \"y\" or \"z\"." end
end end
end
-- Resolve target then mode (in that order incase mode is target).
-- Resolve target then mode (in that order incase mode is target). if not targ then -- If no target set to default (xz)
if not targ then -- If no target set to default (xz) targ = "xz"
targ = "xz" elseif targ:match("[xyz]+") then -- ensure correct target syntax
elseif targ:match("[xyz]+") then -- ensure correct target syntax targ = table.concat(wea_c.tochars(targ:match("[xyz]+"),true,true))
targ = table.concat(wea_c.tochars(targ:match("[xyz]+"),true,true)) else
else return false, "Error: Invalid <target> \""..targ.."\". Expected \"x\" and or \"y\" and or \"z\"."
return false, "Error: Invalid <target> \""..targ.."\". Expected \"x\" and or \"y\" and or \"z\"." end
end
if mode == "average" then -- If mode is average set to avg
if mode == "average" then -- If mode is average set to avg mode = "avg"
mode = "avg" elseif mode:match("[xyz]+") then -- If target is actually base set vars to correct values.
elseif mode:match("[xyz]+") then -- If target is actually base set vars to correct values. base, targ, mode = targ:sub(1,1), table.concat(wea_c.tochars(mode:match("[xyz]+"),true,true)), false
base, targ, mode = targ:sub(1,1), table.concat(wea_c.tochars(mode:match("[xyz]+"),true,true)), false elseif not modeSet[mode] and not base then -- If mode is invalid and base isn't present throw error
elseif not modeSet[mode] and not base then -- If mode is invalid and base isn't present throw error return false, "Error: Invalid <mode> \""..mode.."\". Expected \"grow\", \"shrink\", or \"average\"/\"avg\"."
return false, "Error: Invalid <mode> \""..mode.."\". Expected \"grow\", \"shrink\", or \"average\"/\"avg\"." end
end
if base then
if base then if oper ~= "equal" then base = false -- If operation isn't equalize we don't need <base>
if oper ~= "equal" then base = false -- If operation isn't equalize we don't need <base> elseif targ:match(base) then -- Else check that base is not in target and return error if it is
elseif targ:match(base) then -- Else check that base is not in target and return error if it is return false, "Error: <base> ("..base..") cannot be included in <target> ("..targ..")."
return false, "Error: <base> ("..base..") cannot be included in <target> ("..targ..")." end
end end
end
-- Check if operator is valid
-- Check if operator is valid if not operSet[oper] then
if not operSet[oper] then return false, "Error: Invalid operator \""..oper.."\". Expected \"odd\", \"even\" or \"equal\"."
return false, "Error: Invalid operator \""..oper.."\". Expected \"odd\", \"even\" or \"equal\"." end
end
return true, oper, mode, targ, base
return true, oper, mode, targ, base end,
end, func = function(name, oper, mode, targ, base)
func = function(name, oper, mode, targ, base) local pos1, pos2 = Vector3.clone(wea_c.pos.get(name, 1)), Vector3.clone(wea_c.pos.get(name, 2))
local pos1, pos2 = Vector3.clone(worldedit.pos1[name]), Vector3.clone(worldedit.pos2[name]) local eval -- Declare eval placeholder function to edit later
local eval -- Declare eval placeholder function to edit later
local delta = pos2 - pos1 -- local delta equation: Vd(a) = V2(a) - V1(a)
local delta = pos2 - pos1 -- local delta equation: Vd(a) = V2(a) - V1(a) local _tl = #targ -- Get targ length as a variable incase mode is "average"/"avg"
local _tl = #targ -- Get targ length as a variable incase mode is "average"/"avg" local targ = wea_c.tocharset(targ) -- Break up targ string into set table
local targ = wea_c.tocharset(targ) -- Break up targ string into set table local _m = 0 -- _m is the container to hold the max, min or average (depending on the mode) of the axes in targ
local _m = 0 -- _m is the container to hold the max, min or average (depending on the mode) of the axes in targ
-- set _m to the max, min or mean of the target axes depending on mode or base if it exists
-- set _m to the max, min or mean of the target axes depending on mode or base if it exists if base then _m = delta[base]
if base then _m = delta[base] elseif mode == "avg" then
elseif mode == "avg" then for k,v in pairs(targ) do _m = _m + math.abs(delta[k]) end
for k,v in pairs(targ) do _m = _m + math.abs(delta[k]) end _m = _m / _tl
_m = _m / _tl elseif mode == "grow" then
elseif mode == "grow" then for k,v in pairs(targ) do if math.abs(delta[k]) > _m then _m = math.abs(delta[k]) end end
for k,v in pairs(targ) do if math.abs(delta[k]) > _m then _m = math.abs(delta[k]) end end else
else -- Take output of next(targ), put it in a table, wrap the table in brackets to force evlauation so that
-- Take output of next(targ), put it in a table, wrap the table in brackets to force evlauation so that -- we can call the first element of that table to serve as the key for a call to delta.
-- we can call the first element of that table to serve as the key for a call to delta. _m = delta[({next(targ)})[1]]
_m = delta[({next(targ)})[1]] for k,v in pairs(targ) do if math.abs(delta[k]) < _m then _m = math.abs(delta[k]) end end
for k,v in pairs(targ) do if math.abs(delta[k]) < _m then _m = math.abs(delta[k]) end end end
end
if oper == "even" then
if oper == "even" then eval = function(int)
eval = function(int) local tmp, abs, neg = int / 2, math.abs(int), int < 0
local tmp, abs, neg = int / 2, math.abs(int), int < 0 if math.floor(tmp) ~= tmp then
if math.floor(tmp) ~= tmp then if mode == "avg" then
if mode == "avg" then if int > _m then int = abs - 1
if int > _m then int = abs - 1 else int = abs + 1 end
else int = abs + 1 end elseif mode == "shrink" and abs > 0 then int = abs - 1
elseif mode == "shrink" and abs > 0 then int = abs - 1 else int = abs + 1 end
else int = abs + 1 end end
end if neg then int = int * -1 end -- Ensure correct facing direction
if neg then int = int * -1 end -- Ensure correct facing direction return int
return int end
end elseif oper == "odd" then
elseif oper == "odd" then eval = function(int)
eval = function(int) local tmp, abs, neg = int / 2, math.abs(int), int < 0
local tmp, abs, neg = int / 2, math.abs(int), int < 0 if math.floor(tmp) == tmp then
if math.floor(tmp) == tmp then if mode == "avg" then
if mode == "avg" then if int > _m then int = abs - 1
if int > _m then int = abs - 1 else int = abs + 1 end
else int = abs + 1 end elseif mode == "shrink" and abs > 0 then int = abs - 1
elseif mode == "shrink" and abs > 0 then int = abs - 1 else int = abs + 1 end
else int = abs + 1 end end
end if neg then int = int * -1 end
if neg then int = int * -1 end return int
return int end
end else -- Case: oper == "equal"
else -- Case: oper == "equal" eval = function(int)
eval = function(int) -- Bug: shrink sets pos2 to pos1
-- Bug: shrink sets pos2 to pos1 if int < 0 then return _m * -1
if int < 0 then return _m * -1 else return _m end
else return _m end end
end end
end
for k,_ in pairs(targ) do delta[k] = eval(delta[k]) end
for k,v in pairs(targ) do delta[k] = eval(delta[k]) end
wea_c.pos.set(name, 2, pos1 + delta)
worldedit.pos2[name] = pos1 + delta return true, "Position 2 to "..pos2
worldedit.mark_pos2(name) end
return true, "position 2 set to "..pos2 })
end
})

@ -1,4 +1,3 @@
local wea = worldeditadditions
local wea_c = worldeditadditions_core local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
@ -8,43 +7,15 @@ local Vector3 = wea_c.Vector3
-- ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██
-- ███████ ██ ██ ███████ ██████ ██ -- ███████ ██ ██ ███████ ██████ ██
worldeditadditions_core.register_command("srect", { worldeditadditions_core.register_command("srect", {
params = "[<axis1> [<axis2>]] <length>", params = "None",
description = "Set WorldEdit region position 2 at a set distance along 2 axes.", description = "DEPRECATED: please use //srel instead.",
privs = { worldedit = true }, privs = { worldedit = true },
require_pos = 1, require_pos = 1,
parse = function(params_text) parse = function(params_text)
local vec, tmp = Vector3.new(0, 0, 0), {} return params_text
local find = wea_c.split(params_text, "%s", false)
local ax1, ax2 = (tostring(find[1]):match('[xyz]') or "g"):sub(1,1), (tostring(find[2]):match('[xyz]') or "g"):sub(1,1)
local sn1, sn2, len = wea_c.getsign(find[1]), wea_c.getsign(find[2]), find[table.maxn(find)]
tmp.len = tonumber(len)
-- If len == nill cancel the operation
if not tmp.len then return false, "No length specified." end
-- If axis is bad send "get" order
if ax1 == "g" then tmp.get = true
else vec[ax1] = sn1 * tmp.len end
if ax2 == "g" then tmp.get = true
else vec[ax2] = sn2 * tmp.len end
tmp.axes = ax1..","..ax2
return true, vec, tmp
-- tmp carries:
-- The length (len) arguement to the main function for use if "get" is invoked there
-- The bool value "get" to tell the main function if it needs to populate missing information in vec
-- The string "axes" to tell the main function what axes are and/or need to be populated if "get" is invoked
end, end,
func = function(name, vec, tmp) func = function(name, paramtext)
if tmp.get then return false, "DEPRECATED: please use //srel instead..."
local ax, dir = wea_c.player_axis2d(name)
if not tmp.axes:find("[xz]") then vec[ax] = tmp.len * dir end
if not tmp.axes:find("y") then vec.y = tmp.len end
end
local p2 = vec + Vector3.clone(worldedit.pos1[name])
worldedit.pos2[name] = p2
worldedit.mark_pos2(name)
return true, "position 2 set to "..p2
end, end,
}) })

@ -1,58 +1,37 @@
local wea = worldeditadditions -- local wea = worldeditadditions
local wea_c = worldeditadditions_core local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3 local Vector3 = wea_c.Vector3
-- ███████ ██████ ███████ ██ -- ███████ ██████ ███████ ██
-- ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██
-- ███████ ██████ █████ ██ -- ███████ ██████ █████ ██
-- ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██
-- ███████ ██ ██ ███████ ███████ -- ███████ ██ ██ ███████ ███████
local function parse_with_name(name,args)
local vec, tmp = Vector3.new(0, 0, 0), {}
local find, _, i = {}, 0, 0 worldeditadditions_core.register_command("srel", {
repeat params = "<unified axis syntax>",
_, i, tmp.proc = args:find("([%l%s+-]+%d+)%s*", i) description = "Set WorldEdit region position 2 relative to position 1 and player facing.",
if tmp.proc:match("[xyz]") then privs = { worldedit = true },
tmp.ax = tmp.proc:match("[xyz]") require_pos = 0,
tmp.dir = tonumber(tmp.proc:match("[+-]?%d+")) * (tmp.proc:match("-%l+") and -1 or 1) parse = function(params_text)
else local ret = wea_c.split(params_text)
tmp.ax, _ = wea_c.dir_to_xyz(name, tmp.proc:match("%l+")) if #ret < 1 then return false, "SREL: No params found!"
if not tmp.ax then return false, _ end else return true, ret end
tmp.dir = tonumber(tmp.proc:match("[+-]?%d+")) * (tmp.proc:match("-%l+") and -1 or 1) * _ end,
end func = function(name, params_text)
vec[tmp.ax] = tmp.dir local facing = wea_c.player_dir(name)
until not args:find("([%l%s+-]+%d+)%s*", i) local vec, err = wea_c.parse.directions(params_text, facing, true)
return true, vec if not vec then return false, err end
end
worldeditadditions_core.register_command("srel", { local pos1 = wea_c.pos.get(name, 1)
params = "<axis1> <length1> [<axis2> <length2> [<axis3> <length3>]]", local pos2 = pos1:add(vec)
description = "Set WorldEdit region position 2 at set distances along 3 axes.",
privs = { worldedit = true }, wea_c.pos.set_all(name, {pos1, pos2})
require_pos = 0, return true, "Position 1 to "..pos1..", Position 2 to "..pos2
parse = function(params_text) end,
if params_text:match("([%l%s+-]+%d+)") then return true, params_text })
else return false, "No acceptable params found" end
end, -- Tests
func = function(name, params_text) -- //srel front 5 left 3 y 2
local ret = ""
local _, vec = parse_with_name(name,params_text)
if not _ then return false, vec end
if not worldedit.pos1[name] then
local pos = wea_c.player_vector(name) + Vector3.new(0.5, -0.5, 0.5)
pos = pos:floor()
worldedit.pos1[name] = pos
worldedit.mark_pos1(name)
ret = "position 1 set to "..pos..", "
end
local p2 = vec + Vector3.clone(worldedit.pos1[name])
worldedit.pos2[name] = p2
worldedit.mark_pos2(name)
return true, ret.."position 2 set to "..p2
end,
})
-- Tests
-- //srel front 5 left 3 y 2

@ -1,4 +1,3 @@
local wea = worldeditadditions
local wea_c = worldeditadditions_core local wea_c = worldeditadditions_core
local Vector3 = worldeditadditions.Vector3 local Vector3 = worldeditadditions.Vector3
@ -8,46 +7,26 @@ local Vector3 = worldeditadditions.Vector3
-- ██ ██ ██ ██ ██ ██ ██ -- ██ ██ ██ ██ ██ ██ ██
-- ███████ ███████ ██ ██ ██ ██ ██ -- ███████ ███████ ██ ██ ██ ██ ██
local function parse_with_name(name,args)
local vec, tmp = Vector3.new(0, 0, 0), {}
local find, _, i = {}, 0, 0
repeat
_, i, tmp.proc = args:find("([%l%s+-]+%d+)%s*", i)
if tmp.proc:match("[xyz]") then
tmp.ax = tmp.proc:match("[xyz]")
tmp.dir = tonumber(tmp.proc:match("[+-]?%d+")) * (tmp.proc:match("-%l+") and -1 or 1)
else
tmp.ax, _ = wea_c.dir_to_xyz(name, tmp.proc:match("%l+"))
if not tmp.ax then return false, _ end
tmp.dir = tonumber(tmp.proc:match("[+-]?%d+")) * (tmp.proc:match("-%l+") and -1 or 1) * _
end
vec[tmp.ax] = tmp.dir
until not args:find("([%l%s+-]+%d+)%s*", i)
return true, vec
end
worldeditadditions_core.register_command("sshift", { worldeditadditions_core.register_command("sshift", {
params = "<axis1> <distance1> [<axis2> <distance2> [<axis3> <distance3>]]", params = "<unified axis syntax>",
description = "Shift the WorldEdit region in 3 dimensions.", description = "Shift the WorldEdit region in 3 dimensions.",
privs = { worldedit = true }, privs = { worldedit = true },
require_pos = 2, require_pos = 2,
parse = function(params_text) parse = function(params_text)
if params_text:match("([%l%s+-]+%d+)") then return true, params_text local ret = wea_c.split(params_text)
else return false, "No acceptable params found" end if #ret < 1 then return false, "Error: No params found!"
else return true, ret end
end, end,
func = function(name, params_text) func = function(name, params_text)
local _, vec = parse_with_name(name,params_text) local facing = wea_c.player_dir(name)
if not _ then return false, vec end local vec, err = wea_c.parse.directions(params_text, facing, true)
if not vec then return false, err end
local pos1 = vec:add(worldedit.pos1[name]) local pos1 = vec:add(wea_c.pos.get(name, 1))
worldedit.pos1[name] = pos1 local pos2 = vec:add(wea_c.pos.get(name, 2))
worldedit.mark_pos1(name)
local pos2 = vec:add(worldedit.pos2[name]) wea_c.pos.set_all(name, {pos1, pos2})
worldedit.pos2[name] = pos2 return true, "Position 1 to "..pos1..", Position 2 to "..pos2
worldedit.mark_pos2(name)
return true, "Region shifted by " .. (vec.x + vec.y + vec.z) .. " nodes."
end, end,
}) })

@ -0,0 +1,42 @@
-- local wea = worldeditadditions
local wea_c = worldeditadditions_core
local Vector3 = wea_c.Vector3
-- ███████ ███████ ██ ██ ██████ ██ ███ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██
-- ███████ ███████ ███████ ██████ ██ ██ ██ ██ █████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ███████ ███████ ██ ██ ██ ██ ██ ██ ████ ██ ██
worldeditadditions_core.register_command("sshrink", {
params = "<unified axis syntax>",
description = "Shrink selection region",
privs = { worldedit = true },
require_pos = 0,
parse = function(params_text)
local ret = wea_c.split(params_text)
if #ret < 1 then return false, "Error: No params found!"
else return true, ret end
end,
func = function(name, params_text)
local facing = wea_c.player_dir(name)
local min, max = wea_c.parse.directions(params_text, facing)
if not min then return false, max end
local pos1 = wea_c.pos.get(name, 1)
local pos2 = wea_c.pos.get(name, 2)
if not pos2 then wea_c.pos.set(name, 2, pos1)
else pos1, pos2 = Vector3.sort(pos1, pos2) end
pos1, pos2 = pos1:add(max), pos2:add(min)
wea_c.pos.set_all(name, {pos1, pos2})
return true, "Position 1 to "..pos1..", Position 2 to "..pos2
end,
})
-- Tests
-- //srel front 5 left 3 y 2

@ -0,0 +1,29 @@
local wea_c = worldeditadditions_core
-- local Vector3 = wea_c.Vector3
-- ██ ██ █████ ███████ ██████ █████ ██████ ███████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ███████ ███████ ██████ ███████ ██████ ███████ █████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██ ██ ███████ ██ ██ ██ ██ ██ ███████ ███████
worldeditadditions_core.register_command("uasparse", {
params = "<unified axis syntax>",
description = "Debug command. Returns min and max vectors for given inputs",
privs = { worldedit = true },
-- require_pos = 2,
parse = function(params_text)
local ret = wea_c.split(params_text)
if #ret < 1 then return false, "Error: No params found!"
else return true, ret end
end,
func = function(name, params_text)
local facing = wea_c.player_dir(name)
local min, max = wea_c.parse.directions(params_text, facing)
if not min then
return false, max
else
return true, "Min: "..min.." Max: "..max
end
end
})

@ -41,6 +41,7 @@ dofile(wea_cmd.modpath.."/commands/revolve.lua")
dofile(wea_cmd.modpath.."/commands/rotate.lua") dofile(wea_cmd.modpath.."/commands/rotate.lua")
dofile(wea_cmd.modpath.."/commands/orient.lua") dofile(wea_cmd.modpath.."/commands/orient.lua")
dofile(wea_cmd.modpath.."/commands/set.lua") dofile(wea_cmd.modpath.."/commands/set.lua")
dofile(wea_cmd.modpath.."/commands/uasparse.lua")
-- Meta Commands -- Meta Commands
dofile(wea_cmd.modpath .. "/commands/meta/init.lua") dofile(wea_cmd.modpath .. "/commands/meta/init.lua")

@ -23,7 +23,7 @@ else
end end
--- Unified Axis Keywords banks --- Unified Axis Syntax banks
local keywords = { local keywords = {
-- Compass keywords -- Compass keywords
compass = { compass = {
@ -36,7 +36,9 @@ local keywords = {
["w"] = "-x", ["west"] = "-x", ["w"] = "-x", ["west"] = "-x",
["-w"] = "x", ["-west"] = "x", ["-w"] = "x", ["-west"] = "x",
["u"] = "y", ["up"] = "y", ["u"] = "y", ["up"] = "y",
["-u"] = "-y", ["-up"] = "-y",
["d"] = "-y", ["down"] = "-y", ["d"] = "-y", ["down"] = "-y",
["-d"] = "y", ["-down"] = "y",
}, },
-- Direction keywords -- Direction keywords
@ -90,9 +92,9 @@ function parse.num(str)
else return false end else return false end
end end
--- Checks if a string is a valid Unified Axis Keyword. (Supports axis clumping) --- Checks if a string is a valid Unified Axis Syntax. (Supports axis clumping)
-- @param: str: String: Keyword instance to parse -- @param: str: String: Keyword to parse
-- @returns: Key Instance: returns keyword type, processed keyword content and signed number (or nil) -- @returns: keyword type, processed keyword content and signed number (or nil)
function parse.keyword(str) function parse.keyword(str)
if type(str) ~= "string" then if type(str) ~= "string" then
return "err", "Error: \""..tostring(str).."\" is not a string.", 404 return "err", "Error: \""..tostring(str).."\" is not a string.", 404
@ -135,68 +137,65 @@ end
--- Converts Unified Axis Keyword table into Vector3 instances. --- Converts Unified Axis Syntax table into Vector3 instances.
-- @param: tbl: Table: Keyword table to parse -- @param: tbl: Table: Keyword table to parse
-- @param: facing: Table: Output from worldeditadditions_core.player_dir(name) -- @param: facing: Table: Output from worldeditadditions_core.player_dir(name)
-- @param: sum: Bool | String | nil: Return a single vector by summing the 2 output vectors together -- @param: sum: Bool | String | nil: Return a single vector by summing the 2 output vectors together
-- @returns: Vector3, [Vector3]: returns min, max Vector3 or sum Vector3 (if @param: sum ~= nil) -- @returns: Vector3, [Vector3]: returns min, max Vector3 or sum Vector3 (if @param: sum ~= nil)
-- if error: @returns: false, String: error message -- if error: @returns: false, String: error message
function parse.keytable(tbl, facing, sum) function parse.keytable(tbl, facing, sum)
local min, max = Vector3.new(), Vector3.new() local min, max, mir= Vector3.new(), Vector3.new(), false
local expected = 1 local neg, pos, v0 = Vector3.new(), Vector3.new(), Vector3.new()
local tmp = {axes = {}, num = 0, sign = 1, mirror = false}
--- Processes a number and adds it to the min and max vectors. local function update(num) -- Update "min" and "max"
-- @param num The number to process. if num < 0 then
-- @param axes The axes to apply the number to. min, max = min:add(pos:mul(num)), max:add(neg:mul(num))
-- @param sign The sign of the number.
local function parseNumber(num, axes, sign)
if axes.rev then parseNumber(num, axes.rev, -sign) end
if num * sign >= 0 then
max = max:add(parse.vectorize(axes, num, sign))
else else
min = min:add(parse.vectorize(axes, num, sign)) min, max = min:add(neg:mul(num)), max:add(pos:mul(num))
end
neg, pos = v0:clone(), v0:clone()
end
local function udir(axes, sign) -- Update "neg" and "pos"
if axes.rev then udir(axes.rev, -sign) end
for _, v in ipairs(axes) do
if sign < 0 then neg[v] = -1
else pos[v] = 1 end
end end
end end
for i, v in ipairs(tbl) do for i,v in ipairs(tbl) do
if v:sub(1, 1) == "+" then if v:sub(1, 1) == "+" then
v = v:sub(2) v = v:sub(2)
end end
tmp.num = parse.num(v) local num = parse.num(v)
if expected == 1 then -- If we have a dimension add it to output
if tmp.num then -- Else gather direction statements
parseNumber(tmp.num, {"x", "y", "z", rev={"x", "y", "z"}}, tmp.sign) if num then
else -- Check for direction vectors
local key_type, key_entry, key_sign = parse.keyword(v) if neg == v0 and pos == v0 then
neg, pos = v0:add(-1), v0:add(1)
end
update(num)
else
local key_type, key_entry, key_sign = parse.keyword(v)
if key_type == "axis" then if key_type == "axis" then
tmp.axes = key_entry udir(key_entry, key_sign)
tmp.sign = key_sign
elseif key_type == "dir" then elseif key_type == "dir" then
tmp.axes = {facing[key_entry].axis} udir({facing[key_entry].axis},
tmp.sign = facing[key_entry].sign * key_sign facing[key_entry].sign * key_sign)
elseif key_type == "rev" then elseif key_type == "rev" then
tmp.mirror = true mir = true
else else
return false, key_entry return false, key_entry
end end
expected = 2
end
else
if tmp.num then
parseNumber(tmp.num, tmp.axes, tmp.sign)
expected = 1
else
return false, "Error: Expected number after \""..tostring(tbl[i-1]).."\". Got \""..tostring(v).."\"."
end
end end
end end
if tmp.mirror and not sum then if mir and not sum then
max = max:max(min:abs()) max = max:max(min:abs())
min = max:multiply(-1) min = max:multiply(-1)
end end
@ -209,6 +208,7 @@ function parse.keytable(tbl, facing, sum)
end end
return { return {
keyword = parse.keyword, keyword = parse.keyword,
keytable = parse.keytable, keytable = parse.keytable,

@ -1,89 +1,90 @@
-- Licence: GPLv2 (MPL-2.0 is compatible, so we can use this here) -- Licence: GPLv2 (MPL-2.0 is compatible, so we can use this here)
-- Source: https://stackoverflow.com/a/43582076/1460422 -- Source: https://stackoverflow.com/a/43582076/1460422
-- gsplit: iterate over substrings in a string separated by a pattern -- gsplit: iterate over substrings in a string separated by a pattern
-- --
-- Parameters: -- Parameters:
-- text (string) - the string to iterate over -- text (string) - the string to iterate over
-- pattern (string) - the separator pattern -- pattern (string) - the separator pattern
-- plain (boolean) - if true (or truthy), pattern is interpreted as a plain -- plain (boolean) - if true (or truthy), pattern is interpreted as a plain
-- string, not a Lua pattern -- string, not a Lua pattern
-- --
-- Returns: iterator -- Returns: iterator
-- --
-- Usage: -- Usage:
-- for substr in gsplit(text, pattern, plain) do -- for substr in gsplit(text, pattern, plain) do
-- doSomething(substr) -- doSomething(substr)
-- end -- end
local function gsplit(text, pattern, plain) local function gsplit(text, pattern, plain)
local splitStart, length = 1, #text local splitStart, length = 1, #text
return function () return function ()
if splitStart then if splitStart then
local sepStart, sepEnd = string.find(text, pattern, splitStart, plain) local sepStart, sepEnd = string.find(text, pattern, splitStart, plain)
local ret local ret
if not sepStart then if not sepStart then
ret = string.sub(text, splitStart) ret = string.sub(text, splitStart)
splitStart = nil splitStart = nil
elseif sepEnd < sepStart then elseif sepEnd < sepStart then
-- Empty separator! -- Empty separator!
ret = string.sub(text, splitStart, sepStart) ret = string.sub(text, splitStart, sepStart)
if sepStart < length then if sepStart < length then
splitStart = sepStart + 1 splitStart = sepStart + 1
else else
splitStart = nil splitStart = nil
end end
else else
ret = sepStart > splitStart and string.sub(text, splitStart, sepStart - 1) or '' ret = sepStart > splitStart and string.sub(text, splitStart, sepStart - 1) or ''
splitStart = sepEnd + 1 splitStart = sepEnd + 1
end end
return ret return ret
end end
end end
end end
--- Split a string into substrings separated by a pattern. -- Deprecated --- Split a string into substrings separated by a pattern. -- Deprecated
-- @param text string The string to iterate over -- @param text string The string to iterate over
-- @param pattern string The separator pattern -- @param pattern string The separator pattern
-- @param plain boolean If true (or truthy), pattern is interpreted as a -- @param plain boolean If true (or truthy), pattern is interpreted as a
-- plain string, not a Lua pattern -- plain string, not a Lua pattern
-- @returns table A sequence table containing the substrings -- @returns table A sequence table containing the substrings
local function dsplit(text, pattern, plain) local function dsplit(text, pattern, plain)
local ret = {} local ret = {}
for match in gsplit(text, pattern, plain) do for match in gsplit(text, pattern, plain) do
table.insert(ret, match) table.insert(ret, match)
end end
return ret return ret
end end
--- Split a string into substrings separated by a pattern. --- Split a string into substrings separated by a pattern.
-- @param str string The string to iterate over -- @param str string The string to iterate over
-- @param dlm string The delimiter (separator) pattern -- @param dlm="%s+" string The delimiter (separator) pattern. If a falsey value is passed, then the default value is used.
-- @param plain boolean If true (or truthy), pattern is interpreted as a -- @param plain boolean If true (or truthy), pattern is interpreted as a
-- plain string, not a Lua pattern -- plain string, not a Lua pattern
-- @returns table A sequence table containing the substrings -- @returns table A sequence table containing the substrings
local function split(str,dlm,plain) local function split(str,dlm,plain)
local pos, ret = 0, {} if not dlm then dlm = "%s+" end
local ins, i = str:find(dlm,pos,plain) local pos, ret = 0, {}
-- "if plain" shaves off some time in the while statement local ins, i = str:find(dlm,pos,plain)
if plain then -- "if plain" shaves off some time in the while statement
while ins do if plain then
table.insert(ret,str:sub(pos,ins - 1)) while ins do
pos = ins + #dlm table.insert(ret,str:sub(pos,ins - 1))
ins = str:find(dlm,pos,true) pos = ins + #dlm
end ins = str:find(dlm,pos,true)
else end
while ins do else
table.insert(ret,str:sub(pos,ins - 1)) while ins do
pos = i + 1 table.insert(ret,str:sub(pos,ins - 1))
ins, i = str:find(dlm,pos) pos = i + 1
end ins, i = str:find(dlm,pos)
end end
-- print(pos..","..#str) end
if str:sub(pos,#str) ~= "" then -- print(pos..","..#str)
table.insert(ret,str:sub(pos,#str)) if str:sub(pos,#str) ~= "" then
end table.insert(ret,str:sub(pos,#str))
return ret end
end return ret
end
return split return split