1
0
mirror of https://github.com/Uberi/Minetest-WorldEdit.git synced 2025-06-30 07:01:03 +02:00

1 Commits

Author SHA1 Message Date
f10da8c9f6 Version MFF 2018-09-05 00:44:21 +02:00
40 changed files with 1703 additions and 2705 deletions

View File

@ -17,7 +17,6 @@ Many commands also have shorter names that can be typed faster. For example, if
| `//s` | `//set` | | `//s` | `//set` |
| `//r` | `//replace` | | `//r` | `//replace` |
| `//ri` | `//replaceinverse` | | `//ri` | `//replaceinverse` |
| `//hcube` | `//hollowcube` |
| `//hspr` | `//hollowsphere` | | `//hspr` | `//hollowsphere` |
| `//spr` | `//sphere` | | `//spr` | `//sphere` |
| `//hdo` | `//hollowdome` | | `//hdo` | `//hollowdome` |
@ -29,26 +28,24 @@ Many commands also have shorter names that can be typed faster. For example, if
### `//about` ### `//about`
Get information about the WorldEdit mod. Get information about the mod.
//about //about
### `//help [all/<cmd>]` ### `//inspect on/off/1/0/true/false/yes/no/enable/disable/<blank>`
Get help for WorldEdit commands. `all` shows all WorldEdit commands, `<cmd>`
the help text for the given command.
//help
//help all
//help hollowpyramid
### `//inspect [on/off/1/0/true/false/yes/no/enable/disable]`
Enable or disable node inspection. Enable or disable node inspection.
//inspect on //inspect on
//inspect off //inspect off
//inspect 1
//inspect 0
//inspect true
//inspect false
//inspect yes
//inspect no
//inspect enable
//inspect disable
//inspect //inspect
### `//reset` ### `//reset`
@ -83,17 +80,16 @@ Set WorldEdit region position 2 to the player's location.
### `//p set/set1/set2/get` ### `//p set/set1/set2/get`
Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region.
punching nodes, or print the current WorldEdit region.
//p set //p set
//p set1 //p set1
//p set2 //p set2
//p get //p get
### `//fixedpos set1/set2 <x> <y> <z>` ### `//fixedpos set1 x y z`
Set the WorldEdit region position 1 or 2 to the position (`<x>`, `<y>`, `<z>`). Set a WorldEdit region position to the position at (`<x>`, `<y>`, `<z>`).
//fixedpos set1 0 0 0 //fixedpos set1 0 0 0
//fixedpos set1 -30 5 28 //fixedpos set1 -30 5 28
@ -120,23 +116,14 @@ Set the current WorldEdit region to `<node>`.
//set Blue Lightstone //set Blue Lightstone
//set dirt with grass //set dirt with grass
### `//param2 <param2>` ### `//mix <node1> ...`
Set the param2 value of all nodes in the current WorldEdit region to `<param2>`. Fill the current WorldEdit region with a random mix of `<node1>`, `...`.
//param2 8
### `//mix <node1> [count1] <node2> [count2] ...`
Fill the current WorldEdit region with a random mix of `<node1>`, `<node2>`, `...`.
Weightings can be optionally specified via the `[count1]`, `[count2]`, `...` parameters after a node name.
//mix air //mix air
//mix cactus stone glass sandstone //mix cactus stone glass sandstone
//mix Bronze //mix Bronze
//mix default:cobble air //mix default:cobble air
//mix stone 3 dirt 2
//mix cobblestone 8 stoneblock 2 stonebrick
### `//replace <search node> <replace node>` ### `//replace <search node> <replace node>`
@ -156,21 +143,6 @@ Replace all nodes other than `<search node>` with `<replace node>` in the curren
//replaceinverse dirt Bronze Block //replaceinverse dirt Bronze Block
//replaceinverse mesecons:wire_00000000_off flowers:flower_tulip //replaceinverse mesecons:wire_00000000_off flowers:flower_tulip
### `//hollowcube <width> <height> <length> <node>`
Adds a hollow cube with its ground level centered at WorldEdit position 1 with
dimensions `<width>` x `<height>` x `<length>`, composed of `<node>`.
//hollowcube 6 5 6 Diamond Block
### `//cube <width> <height> <length> <node>`
Adds a cube with its ground level centered at WorldEdit position 1 with
dimensions `<width>` x `<height>` x `<length>`, composed of `<node>`.
//cube 6 5 6 Diamond Block
//cube 7 2 1 default:cobble
### `//hollowsphere <radius> <node>` ### `//hollowsphere <radius> <node>`
Add hollow sphere centered at WorldEdit position 1 with radius `<radius>`, composed of `<node>`. Add hollow sphere centered at WorldEdit position 1 with radius `<radius>`, composed of `<node>`.
@ -203,50 +175,36 @@ Add dome centered at WorldEdit position 1 with radius `<radius>`, composed of `<
//dome -12 glass //dome -12 glass
//dome 17 mesecons:wire_00000000_off //dome 17 mesecons:wire_00000000_off
### `//hollowcylinder x/y/z/? <length> <radius1> [radius2] <node>` ### `//hollowcylinder x/y/z/? <length> <radius> <node>`
Add hollow cylinder at WorldEdit position 1 along the given axis with length `<length>`, Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length `<length>` and radius `<radius>`, composed of `<node>`.
base radius `<radius1>` (and top radius `[radius2]`), composed of `<node>`.
Despite its name this command allows you to create cones (`radius2` = 0) as well as any shapes inbetween (0 < `radius2` < `radius1`).
Swapping `radius1` and `radius2` will create the same object but upside-down.
//hollowcylinder x +5 8 Bronze Block //hollowcylinder x +5 8 Bronze Block
//hollowcylinder y 28 10 glass //hollowcylinder y 28 10 glass
//hollowcylinder z -12 3 mesecons:wire_00000000_off //hollowcylinder z -12 3 mesecons:wire_00000000_off
//hollowcylinder ? 2 4 default:stone //hollowcylinder ? 2 4 default:stone
//hollowcylinder y 10 10 0 walls:cobble ### `//cylinder x/y/z/? <length> <radius> <node>`
//hollowcylinder x 6 0 5 Dirt
//hollowcylinder z 20 10 20 default:desert_stone
### `//cylinder x/y/z/? <length> <radius1> [radius2] <node>` Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length `<length>` and radius `<radius>`, composed of `<node>`.
Add cylinder at WorldEdit position 1 along the given axis with length `<length>`,
base radius `<radius1>` (and top radius `[radius2]`), composed of `<node>`.
Can also create shapes other than cylinders, e.g. cones (see documentation above).
//cylinder x +5 8 Bronze Block //cylinder x +5 8 Bronze Block
//cylinder y 28 10 glass //cylinder y 28 10 glass
//cylinder z -12 3 mesecons:wire_00000000_off //cylinder z -12 3 mesecons:wire_00000000_off
//cylinder ? 2 4 default:stone //cylinder ? 2 4 default:stone
//cylinder y 10 10 0 walls:cobble ### `//hollowpyramid x/y/z? <height> <node>`
//cylinder x 6 0 5 Dirt
//cylinder z 20 10 20 default:desert_stone
### `//hollowpyramid x/y/z/? <height> <node>` Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height `<height>`, composed of `<node>`.
Add hollow pyramid centered at WorldEdit position 1 along the given axis with height `<height>` composed of `<node>`.
//hollowpyramid x 8 Diamond Block //hollowpyramid x 8 Diamond Block
//hollowpyramid y -5 glass //hollowpyramid y -5 glass
//hollowpyramid z 2 mesecons:wire_00000000_off //hollowpyramid z 2 mesecons:wire_00000000_off
//hollowpyramid ? 12 mesecons:wire_00000000_off //hollowpyramid ? 12 mesecons:wire_00000000_off
### `//pyramid x/y/z/? <height> <node>` ### `//pyramid x/y/z? <height> <node>`
Add pyramid centered at WorldEdit position 1 along the given axis with height `<height>` composed of `<node>`. Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height `<height>`, composed of `<node>`.
//pyramid x 8 Diamond Block //pyramid x 8 Diamond Block
//pyramid y -5 glass //pyramid y -5 glass
@ -255,8 +213,7 @@ Add pyramid centered at WorldEdit position 1 along the given axis with height `<
### `//spiral <length> <height> <spacer> <node>` ### `//spiral <length> <height> <spacer> <node>`
Add spiral centered at WorldEdit position 1 with side length `<length>`, Add spiral centered at WorldEdit position 1 with side length `<length>`, height `<height>`, space between walls `<spacer>`, composed of `<node>`.
height `<height>`, space between walls `<spacer>`, composed of `<node>`.
//spiral 20 5 3 Diamond Block //spiral 20 5 3 Diamond Block
//spiral 5 2 1 glass //spiral 5 2 1 glass
@ -264,7 +221,7 @@ height `<height>`, space between walls `<spacer>`, composed of `<node>`.
### `//copy x/y/z/? <amount>` ### `//copy x/y/z/? <amount>`
Copy the current WorldEdit region along the given axis by `<amount>` nodes. Copy the current WorldEdit region along the x/y/z/? axis by `<amount>` nodes.
//copy x 15 //copy x 15
//copy y -7 //copy y -7
@ -273,7 +230,7 @@ Copy the current WorldEdit region along the given axis by `<amount>` nodes.
### `//move x/y/z/? <amount>` ### `//move x/y/z/? <amount>`
Move the current WorldEdit positions and region along the given axis by `<amount>` nodes. Move the current WorldEdit positions and region along the x/y/z/? axis by `<amount>` nodes.
//move x 15 //move x 15
//move y -7 //move y -7
@ -282,7 +239,7 @@ Move the current WorldEdit positions and region along the given axis by `<amount
### `//stack x/y/z/? <count>` ### `//stack x/y/z/? <count>`
Stack the current WorldEdit region along the given axis `<count>` times. Stack the current WorldEdit region along the x/y/z/? axis `<count>` times.
//stack x 3 //stack x 3
//stack y -1 //stack y -1
@ -296,34 +253,35 @@ Stack the current WorldEdit region `<count>` times by offset `<x>`, `<y>`, `<z>`
//stack2 5 3 8 2 //stack2 5 3 8 2
//stack2 1 -1 -1 -1 //stack2 1 -1 -1 -1
### `//stretch <stretchx> <stretchy> <stretchz>` ### `//scale <factor>`
Scale the current WorldEdit positions and region by a factor of Scale the current WorldEdit positions and region by a factor of positive integer `<factor>` with position 1 as the origin.
`<stretchx>`, `<stretchy>`, `<stretchz>` along the X, Y, and Z axes,
respectively, with position 1 as the origin.
//stretch 2 2 2 //scale 2
//stretch 1 2 1 //scale 1
//stretch 10 20 1 //scale 10
### `//transpose x/y/z/? x/y/z/?` ### `//transpose x/y/z/? x/y/z/?`
Transpose the current WorldEdit positions and region along given axes. Transpose the current WorldEdit positions and region along the x/y/z/? and x/y/z/? axes.
//transpose x y //transpose x y
//transpose x z
//transpose y z //transpose y z
//transpose ? y //transpose ? y
### `//flip x/y/z/?` ### `//flip x/y/z/?`
Flip the current WorldEdit region along the given axis. Flip the current WorldEdit region along the x/y/z/? axis.
//flip x //flip x
//flip y
//flip z
//flip ? //flip ?
### `//rotate x/y/z/? <angle>` ### `//rotate x/y/z/? <angle>`
Rotate the current WorldEdit positions and region along the given axis by angle `<angle>` (90 degree increment). Rotate the current WorldEdit positions and region along the x/y/z/? axis by angle `<angle>` (90 degree increment).
//rotate x 90 //rotate x 90
//rotate y 180 //rotate y 180
@ -351,13 +309,6 @@ Removes any fluid node within the current WorldEdit region.
//drain //drain
### `//clearcut`
Removes any plant, tree or foilage-like nodes in the selected region.
The idea is to remove anything that isn't part of the terrain, leaving a "natural" empty space ready for building.
//clearcut
### `//hide` ### `//hide`
Hide all nodes in the current WorldEdit region non-destructively. Hide all nodes in the current WorldEdit region non-destructively.
@ -366,7 +317,7 @@ Hide all nodes in the current WorldEdit region non-destructively.
### `//suppress <node>` ### `//suppress <node>`
Suppress all `<node>` in the current WorldEdit region non-destructively. Suppress all <node> in the current WorldEdit region non-destructively.
//suppress Diamond Block //suppress Diamond Block
//suppress glass //suppress glass
@ -374,7 +325,7 @@ Suppress all `<node>` in the current WorldEdit region non-destructively.
### `//highlight <node>` ### `//highlight <node>`
Highlight `<node>` in the current WorldEdit region by hiding everything else non-destructively. Highlight <node> in the current WorldEdit region by hiding everything else non-destructively.
//highlight Diamond Block //highlight Diamond Block
//highlight glass //highlight glass
@ -418,8 +369,8 @@ Executes `<code>` as a Lua chunk in the global namespace.
Executes `<code>` as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region. Executes `<code>` as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region.
//luatransform minetest.swap_node(pos, {name="default:stone"}) //luatransform minetest.add_node(pos, {name="default:stone"})
//luatransform if minetest.get_node(pos).name == "air" then minetest.add_node(pos, {name="default:water_source"}) end //luatransform if minetest.get_node(pos).name == "air" then minetest.add_node(pos, {name="default:water_source"})
### `//mtschemcreate <file>` ### `//mtschemcreate <file>`
@ -448,61 +399,37 @@ Clears all objects within the WorldEdit region.
//clearobjects //clearobjects
### `//shift x/y/z/?/up/down/left/right/front/back [+/-]<amount>` ### `//shift x/y/z/?/up/down/left/right/front/back [+|-]<amount>`
Shifts the selection area by `[+|-]<amount>` without moving its contents. Shifts the selection area by `[+|-]<amount>` without touching its contents. The shifting axis can be absolute (`x/y/z`) or
The shifting axis can be absolute (`x/y/z`) or relative (`up/down/left/right/front/back`). relative (`up/down/left/right/front/back`).
//shift left 5 //shift left 5
### `//expand [+/-]x/y/z/?/up/down/left/right/front/back <amount> [reverse amount]` ### `//expand [+|-]x/y/z/?/up/down/left/right/front/back <amount> [reverse-amount]`
Expands the selection by `<amount>` in the selected absolute or relative axis. Expands the selection by `<amount>` in the selected absolute or relative axis. If specified, the selection can be expanded in the
If specified, the selection can be expanded in the opposite direction over the same axis by `[reverse amount]`. opposite direction over the same axis by `[reverse-amount]`.
//expand right 7 5 //expand right 7 5
### `//contract [+/-]x/y/z/?/up/down/left/right/front/back <amount> [reverse amount]` ### `//contract [+|-]x/y/z/?/up/down/left/right/front/back <amount> [reverse-amount]`
Contracts the selection by `<amount>` in the selected absolute or relative axis. Contracts the selection by `<amount>` in the selected absolute or relative axis. If specified, the selection can be contracted in the
If specified, the selection can be contracted in the opposite direction over the same axis by `[reverse amount]`. opposite direction over the same axis by `[reverse-amount]`.
//expand right 7 5 //expand right 7 5
### `//outset [h/v] <amount>` ### `//outset [hv] <amount>`
Expands the selection in all directions by `<amount>`. If specified, Expands the selection in all directions by `<amount>`. If specified, the selection can be expanded horizontally in the x and z axes `[h]`
the selection can be expanded horizontally in the x and z axes using `h` or vertically in the y axis `[v]`.
or vertically in the y axis using `v`.
//outset v 5 //outset v 5
### `//inset [h/v] <amount>` ### `//inset [hv] <amount>`
Contracts the selection in all directions by `<amount>`. If specified, Contracts the selection in all directions by `<amount>`. If specified, the selection can be contracted horizontally in the x and z axes `[h]`
the selection can be contracted horizontally in the x and z axes using `h` or vertically in the y axis `[v]`.
or vertically in the y axis using `v`.
//inset h 5 //outset v 5
### `//brush none/(<command> [parameters])`
Assigns the given `<command>` to the currently held brush item, it will be ran with the first pointed solid node (as determined via raycast) as
WorldEdit position 1 when using that specific brush item.
Passing `none` instead clears the command assigned to the currently held brush item.
Note that this functionality requires the `worldedit_brush` mod enabled.
//brush cube 8 8 8 Cobblestone
//brush spr 12 glass
//brush none
### `//cubeapply <size>/(<sizex> <sizey> <sizez>) <command> [parameters]`
Selects a cube with side length of `<size>` around the WorldEdit position 1 and runs the given `<command>` on the newly selected region.
If `<sizex>`, `<sizey>` and `<sizez>` are given, they instead specify the length of the cuboid in X, Y, Z direction.
This is mostly useful for brushes since it allows commands such as `//replace` to be ran, but it can also be used standalone.
//cubeapply 10 replaceinverse air default:water_source
//brush cubeapply 15 drain
//brush cubeapply 12 3 12 drain
//brush cubeapply 1 deleteblocks

0
LICENSE.txt Normal file → Executable file
View File

108
README.md Normal file → Executable file
View File

@ -1,5 +1,5 @@
WorldEdit v1.3 WorldEdit v1.1 for Minetest 0.4.8+
============== ==================================
The ultimate in-game world editing tool for [Minetest](http://minetest.net/)! Tons of functionality to help with building, fixing, and more. The ultimate in-game world editing tool for [Minetest](http://minetest.net/)! Tons of functionality to help with building, fixing, and more.
For more information, see the [forum topic](https://forum.minetest.net/viewtopic.php?id=572) at the Minetest forums. For more information, see the [forum topic](https://forum.minetest.net/viewtopic.php?id=572) at the Minetest forums.
@ -11,64 +11,61 @@ For more information, see the [forum topic](https://forum.minetest.net/viewtopic
Installing Installing
---------- ----------
If you are using Windows, consider installing this mod using [MODSTER](https://forum.minetest.net/viewtopic.php?id=6497), a super simple mod installer that will take care of everything for you. If you are using MODSTER, skip directly to step 6 in the instructions below.
There is a nice installation guide over at the [Minetest Wiki](http://wiki.minetest.com/wiki/Installing_mods). Here is a short summary: There is a nice installation guide over at the [Minetest Wiki](http://wiki.minetest.com/wiki/Installing_mods). Here is a short summary:
1. Download the mod from the [official releases page](https://github.com/Uberi/Minetest-WorldEdit/releases). The download links are labelled "Source Code". If you are using Windows, you'll want to download the ZIP version. 1. Download the mod from the [official releases page](https://github.com/Uberi/Minetest-WorldEdit/releases). The download links are labelled "Source Code". If you are using Windows, you will probably want to download the ZIP version.
2. You should have a file named `Minetest-WorldEdit-x.x.zip`. 2. You should have a file named `SOMETHING.zip` or `SOMETHING.tar.gz`.
3. Extract this file using your archiver of choice. If you are using Windows, open the ZIP file and move the folder inside to a safe place outside of the ZIP file. 3. Extract this file using your archiver of choice. If you are using Windows, open the ZIP file and move the folder inside to a safe place outside of the ZIP file.
4. Make sure that you now have a folder with a file named README.md inside it. If you just have another folder inside this folder, use the nested folder instead. 4. Make sure that you now have a folder with a file named README.md inside it. If you just have another folder inside this folder, use this nested folder instead.
5. Move this folder into the `MINETEST_FOLDER/mods` folder, where `MINETEST_FOLDER` is the folder Minetest is located in. 5. Move this folder into the `MINETEST_FOLDER/mods` folder, where `MINETEST_FOLDER` is the folder Minetest is located in.
6. Open Minetest to a world selection screen. 6. Open Minetest to a world selection screen.
7. Select a world you want to use WorldEdit in by left clicking on it once and press the **Configure** button. 7. Select a world you want to use WorldEdit in by left clicking on it once, and press the **Configure** button.
8. You should have a mod selection screen. Select the one named something like `Minetest-WorldEdit` by left clicking once and press the **Enable Modpack** button. 8. You should have a mod selection screen. Select the one named something like `Minetest-WorldEdit` by left clicking once and press the **Enable MP** button.
9. Press the **Save** button. You can now use WorldEdit in that world. Repeat steps 7 to 9 to enable WorldEdit for other worlds too. 9. Press the **Save** button. You can now use WorldEdit in that world. Repeat steps 7 to 9 to enable WorldEdit for other worlds too.
If you are having trouble, try asking for help in the [IRC channel](https://webchat.freenode.net/?channels=#minetest) (faster but may not always have helpers online) If you are having trouble, try asking for help in the [IRC channel](http://webchat.freenode.net/?channels=#minetest) (faster but may not always have helpers online) or ask on the [forum topic](https://forum.minetest.net/viewtopic.php?id=572) (slower but more likely to get help).
or ask on the [forum topic](https://forum.minetest.net/viewtopic.php?id=572) (slower but more likely to get help).
Usage Usage
----- -----
WorldEdit works primarily through the WorldEdit GUI and chat commands. Depending on your key bindings, you can invoke chat entry with the "T" key and open the chat console with the "F10" key. WorldEdit works primarily through the WorldEdit GUI and chat commands. Depending on your key bindings, you can invoke chat entry with the "t" key, and open the chat console with the "F10" key.
WorldEdit has a huge potential for abuse by untrusted players. Therefore, users will not be able to use WorldEdit unless they have the `worldedit` privilege. WorldEdit has a huge potential for abuse by untrusted players. Therefore, users will not be able to use WorldEdit unless they have the `worldedit` privelege. This is available by default in single player, but in multiplayer the permission must be explicitly given by someone with the right credentials, using the follwoing chat command: `/grant <player name> worldedit`. This privelege can later be removed using the following chat command: `/revoke <player name> worldedit`.
This is available by default in singleplayer, but in multiplayer the permission must be explicitly given by someone with the right credentials,
using the following chat command: `/grant <player name> worldedit`. This privilege can later be removed using the following chat command: `/revoke <player name> worldedit`.
Certain functions/commands such as WorldEdit `//lua` and `//luatransform` chat commands additionally require the `server` privilege. Certain functions/commands such as WorldEdit GUI's "Run Lua" function (equivalent to the `//lua` and `//luatransform` chat command) additionally require the `server` privilege. This is because it is extremely dangerous to give access to these commands to untrusted players, since they essentially are able to control the computer the server is running on. Give this privilege only to people you trust with your computer.
This is because it is extremely dangerous to give access to these commands to untrusted players, since they essentially are able to control the computer the server is running on.
Give this privilege only to people you trust with your computer.
For in-game information about these commands, type `//help <command name>` in the chat. For example, to learn more about the `//copy` command, simply type `//help copy` to display information relevant to copying a region. For in-game information about these commands, type `/help <command name>` in the chat. For example, to learn more about the `//copy` command, simply type `/help /copy` to display information relevant to copying a region.
Interface Interface
--------- ---------
WorldEdit is accessed in-game in two main ways. WorldEdit is accessed in-game in two main ways.
The GUI adds a screen to each player's inventory that gives access to various WorldEdit functions. The [tutorial](Tutorial.md) may be helpful in learning to use it. The GUI adds a screen to each player's inventory that gives access to various WorldEdit functions. The [tutorial](Tutorial.md) and the [Chat Commands Reference](ChatCommands.md) may be helpful in learning to use it.
The chat interface adds many chat commands that perform various WorldEdit powered tasks. It is documented in the [Chat Commands Reference](ChatCommands.md). The chat interface adds many chat commands that perform various WorldEdit powered tasks. It is documented in the [Chat Commands Reference](ChatCommands.md).
Compatibility Compatibility
------------- -------------
This mod supports Minetest versions 5.0 and newer. Older versions of WorldEdit may work with older versions of Minetest, but are not recommended or supported. This mod supports Minetest versions 0.4.8 and newer. Older versions of WorldEdit may work with older versions of Minetest, but are not recommended or supported.
WorldEdit works quite well with other mods and does not have any known mod conflicts. WorldEdit works quite well with other mods, and does not have any known mod conflicts.
WorldEdit GUI requires one of [sfinv](https://github.com/minetest/minetest_game/tree/master/mods/sfinv) (included in minetest_game), WorldEdit GUI works with [Unified Inventory](https://forum.minetest.net/viewtopic.php?id=3933) and [Inventory++](https://forum.minetest.net/viewtopic.php?id=6204), but these are not required to use the mod.
[Unified Inventory](https://forum.minetest.net/viewtopic.php?t=12767),
[Inventory++](https://forum.minetest.net/viewtopic.php?id=6204) or [Smart Inventory](https://forum.minetest.net/viewtopic.php?t=16597).
If you use any other inventory manager mods, note that they may conflict with the WorldEdit GUI. If this is the case, it may be necessary to disable them. If you use any other inventory manager mods, note that they may conflict with the WorldEdit GUI. If this is the case, it may be necessary to disable them.
WorldEdit API WorldEdit API
------------- -------------
WorldEdit exposes all significant functionality in a simple Lua interface. WorldEdit exposes all significant functionality in a simple Lua interface. Adding WorldEdit to the file "depends.txt" in your mod gives you access to all of the `worldedit` functions. The API is useful for tasks such as high-performance node manipulation, alternative interfaces, and map creation.
Adding WorldEdit as a dependency to your mod gives you access to all of the `worldedit` functions. The API is useful for tasks such as high-performance node manipulation, alternative interfaces and map creation. If you don't add WorldEdit to your "depends.txt" file, each file in the WorldEdit mod is also independent. For example, one may import the WorldEdit primitives API using the following code:
AGPLv3 compatible mods may further include WorldEdit files in their own mods. This can be useful if a modder wishes to completely avoid any dependency on WorldEdit. Note that it is required to give credit to the authors in this case. dofile(minetest.get_modpath("worldedit").."/primitives.lua")
This API is documented in the [WorldEdit API Reference](WorldEdit%20API.md). AGPLv3 compatible mods may further include WorldEdit files in their own mods. This may be useful if a modder wishes to completely avoid any dependencies on WorldEdit. Note that it is required to give credit to the authors.
This API is documented in the [WorldEdit API Reference](WorldEdit API.md).
Axes Axes
---- ----
@ -84,16 +81,13 @@ Nodes
----- -----
Node names are required for many types of commands that identify or modify specific types of nodes. They can be specified in a number of ways. Node names are required for many types of commands that identify or modify specific types of nodes. They can be specified in a number of ways.
First, by description - the tooltip that appears when hovering over the item in an inventory. This is case insensitive and includes values such as "Cobblestone" and "bronze block". First, by description - the tooltip that appears when hovering over the item in an inventory. This is case insensitive and includes values such as "Cobblestone" and "bronze block". Note that certain commands (namely, `//replace` and `//replaceinverse`) do not support descriptions that contain spaces in the `<searchnode>` field.
Note that certain commands (namely, `//replace` and `//replaceinverse`) do not support descriptions that contain spaces in the `<searchnode>` field.
Second, by name - the node name that is defined by code, but without the mod name prefix. This is case sensitive and includes values such as "piston_normal_off" and "cactus". Second, by name - the node name that is defined by code, but without the mod name prefix. This is case sensitive and includes values such as "piston_normal_off" and "cactus". Nodes defined in the `default` mod always take precedence over other nodes when searching for the correct one, and if there are multiple possible nodes (such as "a:celery" and "b:celery"), one is chosen in no particular order.
If there are multiple possible nodes (such as "a:celery" and "b:celery"), one is chosen in no particular order.
Finally, by full name - the unambiguous identifier of the node, prefixes and all. This is case sensitive and includes values such as "default:stone" and "mesecons:wire_00000000_off". Finally, by full name - the unambiguous identifier of the node, prefixes and all. This is case sensitive and includes values such as "default:stone" and "mesecons:wire_00000000_off".
The node name "air" can be used anywhere a normal node name can and acts as a blank node. This is useful for clearing or removing nodes. The node name "air" can be used anywhere a normal node name can, and acts as a blank node. This is useful for clearing or removing nodes. For example, `//set air` would remove all the nodes in the current WorldEdit region. Similarly, `//sphere 10 air`, when WorldEdit position 1 underground, would dig a large sphere out of the ground.
For example, `//set air` would remove all the nodes in the current WorldEdit region. Similarly, `//sphere 10 air`, when WorldEdit position 1 underground, would dig a large sphere out of the ground.
Regions Regions
------- -------
@ -101,11 +95,11 @@ Most WorldEdit commands operate on regions. Regions are a set of two positions t
Each positions together define two opposing corners of the cube. With two opposing corners it is possible to determine both the location and dimensions of the region. Each positions together define two opposing corners of the cube. With two opposing corners it is possible to determine both the location and dimensions of the region.
Regions are not saved between server restarts. They start off as empty regions and cannot be used with most WorldEdit commands until they are set to valid values. Regions are not saved between server restarts. They start off as empty regions, and cannot be used with most WorldEdit commands until they are set to valid values.
Markers Markers
------- -------
Entities are used to mark the location of the WorldEdit regions. They appear as boxes containing the number 1 or 2 and represent the first and second position of the WorldEdit region, respectively. Entities are used to mark the location of the WorldEdit regions. They appear as boxes containing the number 1 or 2, and represent position 1 and 2 of the WorldEdit region, respectively.
To remove the entities, simply punch them. This does not reset the positions themselves. To remove the entities, simply punch them. This does not reset the positions themselves.
@ -113,11 +107,9 @@ Schematics
---------- ----------
WorldEdit supports two different types of schematics. WorldEdit supports two different types of schematics.
The first is the WorldEdit Schematic format, with the file extension ".we", and in some older versions, ".wem". The first is the WorldEdit Schematic format, with the file extension ".we", and in some older versions, ".wem". There have been several previous versions of the WorldEdit Schematic format, but WorldEdit is capable of loading any past versions, and will always support them - there is no need to worry about schematics becoming obselete.
There have been several previous versions of the WorldEdit Schematic format, but WorldEdit is capable of loading any past versions, and will always support them - there is no need to worry about schematics becoming obsolete.
As of version 5, WorldEdit schematics include a header. The header is seperated from the content by a colon (`:`). It may contain fields seperated by commas (`,`). As of version 5, WorldEdit schematics include a header. The header is seperated from the content by a colon (`:`). It contains fields seperated by commas (`,`). Currently only one field is used, which contains the version in ASCII decimal.
Currently only one field is used, which contains the version as an ASCII decimal.
The current version of the WorldEdit Schematic format is essentially an array of node data tables in Lua 5.1 table syntax preceded by a header. The current version of the WorldEdit Schematic format is essentially an array of node data tables in Lua 5.1 table syntax preceded by a header.
Specifically it looks like this: Specifically it looks like this:
@ -140,53 +132,29 @@ The ordering of the values and minor aspects of the syntax, such as trailing com
The WorldEdit Schematic format is accessed via the WorldEdit API, or WorldEdit serialization chat commands such as `//serialize` and `//deserialize`. The WorldEdit Schematic format is accessed via the WorldEdit API, or WorldEdit serialization chat commands such as `//serialize` and `//deserialize`.
The second is the Minetest Schematic format (MTS). The details of this format may be found in the Minetest documentation and are out of the scope of this document. The second is the Minetest Schematic format (MTS). The details of this format may be found in the Minetest documentation and are out of the scope of this document. Access to this format is done via specialized MTS commands such as `//mtschemcreate` and `//mtschemplace`.
Access to this format is done via specialized MTS commands such as `//mtschemcreate` and `//mtschemplace`.
Authors Authors
------- -------
WorldEdit would not be possible without the contributions of many developers and designers. Below, they are listed alphabetically: WorldEdit would not be possible without the contributions of many developers and designers. Below, they are listed alphabetically:
Alexander Weber cheapie
ANAND
beyondlimits
Carter Kolwey
cornernote cornernote
Cy cyisfor
Daniel Sosa
electricface electricface
est31
Eugen Wesseloh
h3ndrik
HybridDog
Isidor Zeuner
Jean-Patrick Guerrero
Joseph Pickard
kaeza kaeza
kilbith khonkhortisan
KodexKy
Kyle
MT-Modder
Niwla23
Panquesito7
Pedro Gimeno
Rui
Sebastien Ponce
sfan5 sfan5
ShadowNinja ShadowNinja
shivajiva101
spillz spillz
Starbeamrainbowlabs
TalkLounge
tenplus1
Uberi/Temperest Uberi/Temperest
Wuzzy
License License
------- -------
Copyright (c) 2012 sfan5, Anthony Zhang (Uberi/Temperest), and Brett O'Donnell (cornernote). Copyright 2013 sfan5, Anthony Zhang (Uberi/Temperest), and Brett O'Donnell (cornernote).
This mod is licensed under the [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.html). This mod is licensed under the [GNU Affero General Public License](http://www.gnu.org/licenses/agpl-3.0.html).
Basically, this means everyone is free to use, modify, and distribute the files, as long as these modifications are also licensed the same way. Basically, this means everyone is free to use, modify, and distribute the files, as long as these modifications are also licensed the same way.
Most importantly, the Affero variant of the GPL requires you to publish your modifications in source form, even if the mod is run only on the server, and not distributed. Most importantly, the Affero variant of the GPL requires you to publish your modifications in source form, even if the mod is run only on the server, and not distributed.

10
Tutorial.md Normal file → Executable file
View File

@ -1,11 +1,13 @@
WorldEdit Tutorial WorldEdit Tutorial
================== ==================
This is a step-by-step tutorial outlining the basic usage of WorldEdit. This is a step-by-step tutorial outlining the basic usage of WorldEdit. For more information, see the [README](README.md).
Let's start with a few assumptions: Let's start with a few assumptions:
* You have a compatible version of Minetest working, that is 5.0 or later. * You have a compatible version of Minetest working.
* See the [README](README.md) for compatibility information.
* You have WorldEdit installed as a mod. * You have WorldEdit installed as a mod.
* If using Windows, [MODSTER](https://forum.minetest.net/viewtopic.php?pid=101463) makes installing mods totally painless.
* Simply download the file, extract the archive, and move it to the correct mod folder for Minetest. * Simply download the file, extract the archive, and move it to the correct mod folder for Minetest.
* See the installation instructions in [README](README.md) if you need more details. * See the installation instructions in [README](README.md) if you need more details.
* You are familiar with the basics of the game. * You are familiar with the basics of the game.
@ -57,7 +59,7 @@ Look at the place between the two markers: it is now filled with MESE blocks!
The `//set <node>` command fills the region with whatever node you want. It is a region-oriented command, which means it works inside the WorldEdit region only. The `//set <node>` command fills the region with whatever node you want. It is a region-oriented command, which means it works inside the WorldEdit region only.
Now, try a few different variations, such as `//set torch`, `//set cobble`, and `//set water source`. Now, try a few different variations, such as `//set torch`, `//set cobble`, and `//set water`.
### WorldEdit GUI ### WorldEdit GUI
@ -73,7 +75,7 @@ Look at the place between the two markers: it is now filled with MESE blocks!
The "Set Nodes" function fills the region with whatever node you want. It is a region-oriented command, which means it works inside the WorldEdit region only. The "Set Nodes" function fills the region with whatever node you want. It is a region-oriented command, which means it works inside the WorldEdit region only.
Now, try a few different variations on the node name, such as "torch", "cobble", and "water source". Now, try a few different variations on the node name, such as "torch", "cobble", and "water".
Step 3: Position commands Step 3: Position commands
------------------------- -------------------------

View File

@ -24,13 +24,6 @@ Contained in manipulations.lua, this module allows several node operations to be
### count = worldedit.set(pos1, pos2, node_name) ### count = worldedit.set(pos1, pos2, node_name)
Sets a region defined by positions `pos1` and `pos2` to `node_name`. To clear a region, use "air" as the value of `node_name`. Sets a region defined by positions `pos1` and `pos2` to `node_name`. To clear a region, use "air" as the value of `node_name`.
If `node_name` is a list of nodes, each set node is randomly picked from it.
Returns the number of nodes set.
### `count = worldedit.set_param2(pos1, pos2, param2)`
Sets the param2 values of all nodes in a region defined by positions `pos1` and `pos2` to `param2`.
Returns the number of nodes set. Returns the number of nodes set.
@ -55,7 +48,6 @@ Returns the number of nodes copied.
### count = worldedit.copy2(pos1, pos2, off) ### count = worldedit.copy2(pos1, pos2, off)
Copies the region defined by positions `pos1` and `pos2` by the offset vector `off`. Copies the region defined by positions `pos1` and `pos2` by the offset vector `off`.
Note that the offset needs to be big enough that there is no overlap.
Returns the number of nodes copied. Returns the number of nodes copied.
@ -74,7 +66,6 @@ Returns the number of nodes stacked.
### count = worldedit.stack2(pos1, pos2, direction, amount) ### count = worldedit.stack2(pos1, pos2, direction, amount)
Duplicates the region defined by positions `pos1` and `pos2` `amount` times with offset vector `direction`. Duplicates the region defined by positions `pos1` and `pos2` `amount` times with offset vector `direction`.
Note that the offset vector needs to be big enough that there is no overlap.
Returns the number of nodes stacked. Returns the number of nodes stacked.
@ -124,12 +115,6 @@ Primitives
---------- ----------
Contained in primitives.lua, this module allows the creation of several geometric primitives. Contained in primitives.lua, this module allows the creation of several geometric primitives.
### count = worldedit.cube(pos, width, height, length, node_name, hollow)
Adds a cube with its ground level centered at `pos`, the dimensions `width` x `height` x `length`, composed of `node_name`.
Returns the number of nodes added.
### count = worldedit.sphere(pos, radius, node_name, hollow) ### count = worldedit.sphere(pos, radius, node_name, hollow)
Adds a sphere centered at `pos` with radius `radius`, composed of `node_name`. Adds a sphere centered at `pos` with radius `radius`, composed of `node_name`.
@ -142,9 +127,9 @@ Adds a dome centered at `pos` with radius `radius`, composed of `node_name`.
Returns the number of nodes added. Returns the number of nodes added.
### count = worldedit.cylinder(pos, axis, length, radius1, radius2, node_name, hollow) ### count = worldedit.cylinder(pos, axis, length, radius, node_name, hollow)
Adds a cylinder-like at `pos` along the `axis` axis ("x" or "y" or "z") with length `length`, base radius `radius1` and top radius `radius2`, composed of `node_name`. Adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `node_name`.
Returns the number of nodes added. Returns the number of nodes added.
@ -196,7 +181,7 @@ Returns the number of nodes restored.
Serialization Serialization
------------- -------------
Contained in serialization.lua, this module allows regions of nodes to be serialized and deserialized to formats suitable for use outside Minetest. Contained in serialization.lua, this module allows regions of nodes to be serialized and deserialized to formats suitable for use outside MineTest.
### version, extra_fields, content = worldedit.read_header(value) ### version, extra_fields, content = worldedit.read_header(value)

0
config.ld Normal file → Executable file
View File

View File

@ -1,2 +0,0 @@
name = Minetest-WorldEdit
description = WorldEdit is an in-game world editor. Use it to repair griefing, or just create awesome buildings in seconds.

0
modpack.txt Executable file
View File

0
worldedit/code.lua Normal file → Executable file
View File

5
worldedit/common.lua Normal file → Executable file
View File

@ -46,9 +46,6 @@ end
function worldedit.keep_loaded(pos1, pos2) function worldedit.keep_loaded(pos1, pos2)
-- Create a vmanip and read the area from map, this
-- causes all MapBlocks to be loaded into memory.
-- This doesn't actually *keep* them loaded, unlike the name implies.
local manip = minetest.get_voxel_manip() local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2) manip:read_from_map(pos1, pos2)
end end
@ -110,9 +107,7 @@ end
function mh.finish(manip, data) function mh.finish(manip, data)
-- Update map -- Update map
if data ~= nil then
manip:set_data(data) manip:set_data(data)
end
manip:write_to_map() manip:write_to_map()
manip:update_map() manip:update_map()
end end

0
worldedit/compatibility.lua Normal file → Executable file
View File

View File

@ -98,6 +98,21 @@ worldedit.marker_move = function(name, marker, deltavector)
return true return true
end end
-- Updates the location ingame of the markers
worldedit.marker_update = function(name, marker)
if marker == nil then
worldedit.mark_pos1(name)
worldedit.mark_pos2(name)
elseif marker == 1 then
worldedit.mark_pos1(name)
elseif marker == 2 then
worldedit.mark_pos2(name)
else
minetest.debug(
"worldedit: Invalid execution of function update_markers")
end
end
-- Returns two vectors with the directions for volumetric expansion -- Returns two vectors with the directions for volumetric expansion
worldedit.get_expansion_directions = function(mark1, mark2) worldedit.get_expansion_directions = function(mark1, mark2)
@ -134,7 +149,7 @@ end
-- Return the marker that is closest to the player -- Return the marker that is closest to the player
worldedit.marker_get_closest_to_player = function(name) worldedit.marker_get_closest_to_player = function(name)
local playerpos = minetest.get_player_by_name(name):get_pos() local playerpos = minetest.get_player_by_name(name):getpos()
local dist1 = vector.distance(playerpos, worldedit.pos1[name]) local dist1 = vector.distance(playerpos, worldedit.pos1[name])
local dist2 = vector.distance(playerpos, worldedit.pos2[name]) local dist2 = vector.distance(playerpos, worldedit.pos2[name])

21
worldedit/init.lua Normal file → Executable file
View File

@ -1,19 +1,24 @@
--- WorldEdit mod for the Minetest engine --- Worldedit.
-- @module worldedit -- @module worldedit
-- @release 1.3 -- @release 1.1
-- @copyright 2012 sfan5, Anthony Zhang (Uberi/Temperest), and Brett O'Donnell (cornernote) -- @copyright 2013 sfan5, Anthony Zhang (Uberi/Temperest), and Brett O'Donnell (cornernote).
-- @license GNU Affero General Public License version 3 (AGPLv3) -- @license GNU Affero General Public License version 3 (AGPLv3)
-- @author sfan5 -- @author sfan5
-- @author Anthony Zang (Uberi/Temperest) -- @author Anthony Zang (Uberi/Temperest)
-- @author Bret O'Donnel (cornernote) -- @author Bret O'Donnel (cornernote)
-- @author ShadowNinja -- @author ShadowNinja
worldedit = {} worldedit = {}
worldedit.version = {1, 1, major=1, minor=1}
worldedit.version_string = table.concat(worldedit.version, ".")
local ver = {major=1, minor=3} if not minetest.get_voxel_manip then
worldedit.version = ver local err_msg = "This version of WorldEdit requires Minetest 0.4.8 or later! You have an old version."
worldedit.version_string = string.format("%d.%d", ver.major, ver.minor) minetest.log("error", string.rep("#", 128))
minetest.log("error", err_msg)
minetest.log("error", string.rep("#", 128))
error(err_msg)
end
local path = minetest.get_modpath(minetest.get_current_modname()) local path = minetest.get_modpath(minetest.get_current_modname())
@ -34,7 +39,7 @@ load_module(path .. "/compatibility.lua")
load_module(path .. "/cuboid.lua") load_module(path .. "/cuboid.lua")
if minetest.settings:get_bool("log_mods") then if minetest.setting_getbool("log_mods") then
print("[WorldEdit] Loaded!") print("[WorldEdit] Loaded!")
end end

View File

@ -38,29 +38,6 @@ function worldedit.set(pos1, pos2, node_names)
return worldedit.volume(pos1, pos2) return worldedit.volume(pos1, pos2)
end end
--- Sets param2 of a region.
-- @param pos1
-- @param pos2
-- @param param2 Value of param2 to set
-- @return The number of nodes set.
function worldedit.set_param2(pos1, pos2, param2)
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local manip, area = mh.init(pos1, pos2)
local param2_data = manip:get_param2_data()
-- Set param2 for every node
for i in area:iterp(pos1, pos2) do
param2_data[i] = param2
end
-- Update map
manip:set_param2_data(param2_data)
manip:write_to_map()
manip:update_map()
return worldedit.volume(pos1, pos2)
end
--- Replaces all instances of `search_node` with `replace_node` in a region. --- Replaces all instances of `search_node` with `replace_node` in a region.
-- When `inverse` is `true`, replaces all instances that are NOT `search_node`. -- When `inverse` is `true`, replaces all instances that are NOT `search_node`.
@ -76,6 +53,9 @@ function worldedit.replace(pos1, pos2, search_node, replace_node, inverse)
local count = 0 local count = 0
--- TODO: This could be shortened by checking `inverse` in the loop,
-- but that would have a speed penalty. Is the penalty big enough
-- to matter?
if not inverse then if not inverse then
for i in area:iterp(pos1, pos2) do for i in area:iterp(pos1, pos2) do
if data[i] == search_id then if data[i] == search_id then
@ -98,47 +78,27 @@ function worldedit.replace(pos1, pos2, search_node, replace_node, inverse)
end end
local function deferred_execution(next_one, finished)
-- Allocate 100% of server step for execution (might lag a little)
local allocated_usecs =
tonumber(minetest.settings:get("dedicated_server_step")) * 1000000
local function f()
local deadline = minetest.get_us_time() + allocated_usecs
repeat
local is_done = next_one()
if is_done then
if finished then
finished()
end
return
end
until minetest.get_us_time() >= deadline
minetest.after(0, f)
end
f()
end
--- Duplicates a region `amount` times with offset vector `direction`. --- Duplicates a region `amount` times with offset vector `direction`.
-- Stacking is spread across server steps. -- Stacking is spread across server steps, one copy per step.
-- @return The number of nodes stacked. -- @return The number of nodes stacked.
function worldedit.stack2(pos1, pos2, direction, amount, finished) function worldedit.stack2(pos1, pos2, direction, amount, finished)
-- Protect arguments from external changes during execution
pos1 = table.copy(pos1)
pos2 = table.copy(pos2)
direction = table.copy(direction)
local i = 0 local i = 0
local translated = {x=0, y=0, z=0} local translated = {x=0, y=0, z=0}
local function step() local function next_one()
if i < amount then
i = i + 1
translated.x = translated.x + direction.x translated.x = translated.x + direction.x
translated.y = translated.y + direction.y translated.y = translated.y + direction.y
translated.z = translated.z + direction.z translated.z = translated.z + direction.z
worldedit.copy2(pos1, pos2, translated) worldedit.copy2(pos1, pos2, translated)
i = i + 1 minetest.after(0, next_one)
return i >= amount else
if finished then
finished()
end end
deferred_execution(step, finished) end
end
next_one()
return worldedit.volume(pos1, pos2) * amount return worldedit.volume(pos1, pos2) * amount
end end
@ -152,187 +112,181 @@ end
function worldedit.copy(pos1, pos2, axis, amount) function worldedit.copy(pos1, pos2, axis, amount)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
-- Decide if we need to copy stuff backwards (only applies to metadata) worldedit.keep_loaded(pos1, pos2)
local backwards = amount > 0 and amount < (pos2[axis] - pos1[axis] + 1)
local off = {x=0, y=0, z=0} local get_node, get_meta, set_node = minetest.get_node,
off[axis] = amount minetest.get_meta, minetest.set_node
return worldedit.copy2(pos1, pos2, off, backwards) -- Copy things backwards when negative to avoid corruption.
-- FIXME: Lots of code duplication here.
if amount < 0 then
local pos = {}
pos.x = pos1.x
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = get_node(pos) -- Obtain current node
local meta = get_meta(pos):to_table() -- Get meta of current node
local value = pos[axis] -- Store current position
pos[axis] = value + amount -- Move along axis
set_node(pos, node) -- Copy node to new position
get_meta(pos):from_table(meta) -- Set metadata of new node
pos[axis] = value -- Restore old position
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
else
local pos = {}
pos.x = pos2.x
while pos.x >= pos1.x do
pos.y = pos2.y
while pos.y >= pos1.y do
pos.z = pos2.z
while pos.z >= pos1.z do
local node = get_node(pos) -- Obtain current node
local meta = get_meta(pos):to_table() -- Get meta of current node
local value = pos[axis] -- Store current position
pos[axis] = value + amount -- Move along axis
set_node(pos, node) -- Copy node to new position
get_meta(pos):from_table(meta) -- Set metadata of new node
pos[axis] = value -- Restore old position
pos.z = pos.z - 1
end
pos.y = pos.y - 1
end
pos.x = pos.x - 1
end
end
return worldedit.volume(pos1, pos2)
end end
--- Copies a region by offset vector `off`. --- Copies a region by offset vector `off`.
-- @param pos1 -- @param pos1
-- @param pos2 -- @param pos2
-- @param off -- @param off
-- @param meta_backwards (not officially part of API)
-- @return The number of nodes copied. -- @return The number of nodes copied.
function worldedit.copy2(pos1, pos2, off, meta_backwards) function worldedit.copy2(pos1, pos2, off)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local src_manip, src_area = mh.init(pos1, pos2) worldedit.keep_loaded(pos1, pos2)
local src_stride = {x=1, y=src_area.ystride, z=src_area.zstride}
local src_offset = vector.subtract(pos1, src_area.MinEdge)
local dpos1 = vector.add(pos1, off) local get_node, get_meta, set_node = minetest.get_node,
local dpos2 = vector.add(pos2, off) minetest.get_meta, minetest.set_node
local dim = vector.add(vector.subtract(pos2, pos1), 1) local pos = {}
pos.x = pos2.x
local dst_manip, dst_area = mh.init(dpos1, dpos2) while pos.x >= pos1.x do
local dst_stride = {x=1, y=dst_area.ystride, z=dst_area.zstride} pos.y = pos2.y
local dst_offset = vector.subtract(dpos1, dst_area.MinEdge) while pos.y >= pos1.y do
pos.z = pos2.z
local function do_copy(src_data, dst_data) while pos.z >= pos1.z do
for z = 0, dim.z-1 do local node = get_node(pos) -- Obtain current node
local src_index_z = (src_offset.z + z) * src_stride.z + 1 -- +1 for 1-based indexing local meta = get_meta(pos):to_table() -- Get meta of current node
local dst_index_z = (dst_offset.z + z) * dst_stride.z + 1 local newpos = vector.add(pos, off) -- Calculate new position
for y = 0, dim.y-1 do set_node(newpos, node) -- Copy node to new position
local src_index_y = src_index_z + (src_offset.y + y) * src_stride.y get_meta(newpos):from_table(meta) -- Set metadata of new node
local dst_index_y = dst_index_z + (dst_offset.y + y) * dst_stride.y pos.z = pos.z - 1
-- Copy entire row at once
local src_index_x = src_index_y + src_offset.x
local dst_index_x = dst_index_y + dst_offset.x
for x = 0, dim.x-1 do
dst_data[dst_index_x + x] = src_data[src_index_x + x]
end end
pos.y = pos.y - 1
end end
pos.x = pos.x - 1
end end
end
-- Copy node data
local src_data = src_manip:get_data()
local dst_data = dst_manip:get_data()
do_copy(src_data, dst_data)
dst_manip:set_data(dst_data)
-- Copy param1
src_manip:get_light_data(src_data)
dst_manip:get_light_data(dst_data)
do_copy(src_data, dst_data)
dst_manip:set_light_data(dst_data)
-- Copy param2
src_manip:get_param2_data(src_data)
dst_manip:get_param2_data(dst_data)
do_copy(src_data, dst_data)
dst_manip:set_param2_data(dst_data)
mh.finish(dst_manip)
src_data = nil
dst_data = nil
-- Copy metadata
local get_meta = minetest.get_meta
if meta_backwards then
for z = dim.z-1, 0, -1 do
for y = dim.y-1, 0, -1 do
for x = dim.x-1, 0, -1 do
local pos = {x=pos1.x+x, y=pos1.y+y, z=pos1.z+z}
local meta = get_meta(pos):to_table()
pos = vector.add(pos, off)
get_meta(pos):from_table(meta)
end
end
end
else
for z = 0, dim.z-1 do
for y = 0, dim.y-1 do
for x = 0, dim.x-1 do
local pos = {x=pos1.x+x, y=pos1.y+y, z=pos1.z+z}
local meta = get_meta(pos):to_table()
pos = vector.add(pos, off)
get_meta(pos):from_table(meta)
end
end
end
end
return worldedit.volume(pos1, pos2) return worldedit.volume(pos1, pos2)
end end
--- Deletes all node metadata in the region
-- @param pos1
-- @param pos2
-- @return The number of nodes that had their meta deleted.
function worldedit.delete_meta(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local meta_positions = minetest.find_nodes_with_meta(pos1, pos2)
local get_meta = minetest.get_meta
for _, pos in ipairs(meta_positions) do
get_meta(pos):from_table(nil)
end
return #meta_positions
end
--- Moves a region along `axis` by `amount` nodes. --- Moves a region along `axis` by `amount` nodes.
-- @return The number of nodes moved. -- @return The number of nodes moved.
function worldedit.move(pos1, pos2, axis, amount) function worldedit.move(pos1, pos2, axis, amount)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local dim = vector.add(vector.subtract(pos2, pos1), 1) worldedit.keep_loaded(pos1, pos2)
local overlap = math.abs(amount) < dim[axis]
-- Decide if we need to copy metadata backwards
local backwards = overlap and amount > 0
local function nuke_area(my_off, my_dim) --- TODO: Move slice by slice using schematic method in the move axis
if my_dim.x == 0 or my_dim.y == 0 or my_dim.z == 0 then -- and transfer metadata in separate loop (and if the amount is
return -- greater than the length in the axis, copy whole thing at a time and
-- erase original after, using schematic method).
local get_node, get_meta, set_node, remove_node = minetest.get_node,
minetest.get_meta, minetest.set_node, minetest.remove_node
-- Copy things backwards when negative to avoid corruption.
--- FIXME: Lots of code duplication here.
if amount < 0 then
local pos = {}
pos.x = pos1.x
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = get_node(pos) -- Obtain current node
local meta = get_meta(pos):to_table() -- Get metadata of current node
remove_node(pos) -- Remove current node
local value = pos[axis] -- Store current position
pos[axis] = value + amount -- Move along axis
set_node(pos, node) -- Move node to new position
get_meta(pos):from_table(meta) -- Set metadata of new node
pos[axis] = value -- Restore old position
pos.z = pos.z + 1
end end
local my_pos1 = vector.add(pos1, my_off) pos.y = pos.y + 1
local my_pos2 = vector.subtract(vector.add(my_pos1, my_dim), 1) end
worldedit.set(my_pos1, my_pos2, "air") pos.x = pos.x + 1
worldedit.delete_meta(my_pos1, my_pos2)
end end
-- Copy stuff to new location
local off = {x=0, y=0, z=0}
off[axis] = amount
worldedit.copy2(pos1, pos2, off, backwards)
-- Nuke old area
if not overlap then
nuke_area({x=0, y=0, z=0}, dim)
else else
-- Source and destination region are overlapping, which means we can't local pos = {}
-- blindly delete the [pos1, pos2] area pos.x = pos2.x
local leftover = vector.new(dim) -- size of the leftover slice while pos.x >= pos1.x do
leftover[axis] = math.abs(amount) pos.y = pos2.y
if amount > 0 then while pos.y >= pos1.y do
nuke_area({x=0, y=0, z=0}, leftover) pos.z = pos2.z
else while pos.z >= pos1.z do
local top = {x=0, y=0, z=0} -- offset of the leftover slice from pos1 local node = get_node(pos) -- Obtain current node
top[axis] = dim[axis] - math.abs(amount) local meta = get_meta(pos):to_table() -- Get metadata of current node
nuke_area(top, leftover) remove_node(pos) -- Remove current node
local value = pos[axis] -- Store current position
pos[axis] = value + amount -- Move along axis
set_node(pos, node) -- Move node to new position
get_meta(pos):from_table(meta) -- Set metadata of new node
pos[axis] = value -- Restore old position
pos.z = pos.z - 1
end
pos.y = pos.y - 1
end
pos.x = pos.x - 1
end end
end end
return worldedit.volume(pos1, pos2) return worldedit.volume(pos1, pos2)
end end
--- Duplicates a region along `axis` `amount` times. --- Duplicates a region along `axis` `amount` times.
-- Stacking is spread across server steps. -- Stacking is spread across server steps, one copy per step.
-- @param pos1 -- @param pos1
-- @param pos2 -- @param pos2
-- @param axis Axis direction, "x", "y", or "z". -- @param axis Axis direction, "x", "y", or "z".
-- @param count -- @param count
-- @return The number of nodes stacked. -- @return The number of nodes stacked.
function worldedit.stack(pos1, pos2, axis, count, finished) function worldedit.stack(pos1, pos2, axis, count)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local length = pos2[axis] - pos1[axis] + 1 local length = pos2[axis] - pos1[axis] + 1
if count < 0 then if count < 0 then
count = -count count = -count
length = -length length = -length
end end
local amount = 0
local i, distance = 0, 0 local copy = worldedit.copy
local function step() local i = 1
distance = distance + length function next_one()
worldedit.copy(pos1, pos2, axis, distance) if i <= count then
i = i + 1 i = i + 1
return i >= count amount = amount + length
copy(pos1, pos2, axis, amount)
minetest.after(0, next_one)
end end
deferred_execution(step, finished) end
next_one()
return worldedit.volume(pos1, pos2) * count return worldedit.volume(pos1, pos2) * count
end end
@ -552,22 +506,20 @@ end
-- @param pos2 -- @param pos2
-- @param angle Angle in degrees (90 degree increments only). -- @param angle Angle in degrees (90 degree increments only).
-- @return The number of nodes oriented. -- @return The number of nodes oriented.
-- TODO: Support 6D facedir rotation along arbitrary axis.
function worldedit.orient(pos1, pos2, angle) function worldedit.orient(pos1, pos2, angle)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local registered_nodes = minetest.registered_nodes local registered_nodes = minetest.registered_nodes
local wallmounted = { local wallmounted = {
[90] = {0, 1, 5, 4, 2, 3, 0, 0}, [90] = {[0]=0, 1, 5, 4, 2, 3},
[180] = {0, 1, 3, 2, 5, 4, 0, 0}, [180] = {[0]=0, 1, 3, 2, 5, 4},
[270] = {0, 1, 4, 5, 3, 2, 0, 0} [270] = {[0]=0, 1, 4, 5, 3, 2}
} }
local facedir = { local facedir = {
[90] = { 1, 2, 3, 0, 13, 14, 15, 12, 17, 18, 19, 16, [90] = {[0]=1, 2, 3, 0},
9, 10, 11, 8, 5, 6, 7, 4, 23, 20, 21, 22}, [180] = {[0]=2, 3, 0, 1},
[180] = { 2, 3, 0, 1, 10, 11, 8, 9, 6, 7, 4, 5, [270] = {[0]=3, 0, 1, 2}
18, 19, 16, 17, 14, 15, 12, 13, 22, 23, 20, 21},
[270] = { 3, 0, 1, 2, 19, 16, 17, 18, 15, 12, 13, 14,
7, 4, 5, 6, 11, 8, 9, 10, 21, 22, 23, 20}
} }
angle = angle % 360 angle = angle % 360
@ -583,7 +535,8 @@ function worldedit.orient(pos1, pos2, angle)
worldedit.keep_loaded(pos1, pos2) worldedit.keep_loaded(pos1, pos2)
local count = 0 local count = 0
local get_node, swap_node = minetest.get_node, minetest.swap_node local set_node, get_node, get_meta, swap_node = minetest.set_node,
minetest.get_node, minetest.get_meta, minetest.swap_node
local pos = {x=pos1.x, y=0, z=0} local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do while pos.x <= pos2.x do
pos.y = pos1.y pos.y = pos1.y
@ -593,20 +546,17 @@ function worldedit.orient(pos1, pos2, angle)
local node = get_node(pos) local node = get_node(pos)
local def = registered_nodes[node.name] local def = registered_nodes[node.name]
if def then if def then
local paramtype2 = def.paramtype2 if def.paramtype2 == "wallmounted" then
if paramtype2 == "wallmounted" or node.param2 = wallmounted_substitution[node.param2]
paramtype2 == "colorwallmounted" then local meta = get_meta(pos):to_table()
local orient = node.param2 % 8 set_node(pos, node)
node.param2 = node.param2 - orient + get_meta(pos):from_table(meta)
wallmounted_substitution[orient + 1]
swap_node(pos, node)
count = count + 1 count = count + 1
elseif paramtype2 == "facedir" or elseif def.paramtype2 == "facedir" then
paramtype2 == "colorfacedir" then node.param2 = facedir_substitution[node.param2]
local orient = node.param2 % 32 local meta = get_meta(pos):to_table()
node.param2 = node.param2 - orient + set_node(pos, node)
facedir_substitution[orient + 1] get_meta(pos):from_table(meta)
swap_node(pos, node)
count = count + 1 count = count + 1
end end
end end
@ -640,34 +590,10 @@ function worldedit.clear_objects(pos1, pos2)
worldedit.keep_loaded(pos1, pos2) worldedit.keep_loaded(pos1, pos2)
local function should_delete(obj)
-- Avoid players and WorldEdit entities
if obj:is_player() then
return false
end
local entity = obj:get_luaentity()
return not entity or not entity.name:find("^worldedit:")
end
-- Offset positions to include full nodes (positions are in the center of nodes) -- Offset positions to include full nodes (positions are in the center of nodes)
local pos1x, pos1y, pos1z = pos1.x - 0.5, pos1.y - 0.5, pos1.z - 0.5 local pos1x, pos1y, pos1z = pos1.x - 0.5, pos1.y - 0.5, pos1.z - 0.5
local pos2x, pos2y, pos2z = pos2.x + 0.5, pos2.y + 0.5, pos2.z + 0.5 local pos2x, pos2y, pos2z = pos2.x + 0.5, pos2.y + 0.5, pos2.z + 0.5
local count = 0
if minetest.get_objects_in_area then
local objects = minetest.get_objects_in_area({x=pos1x, y=pos1y, z=pos1z},
{x=pos2x, y=pos2y, z=pos2z})
for _, obj in pairs(objects) do
if should_delete(obj) then
obj:remove()
count = count + 1
end
end
return count
end
-- Fallback implementation via get_objects_inside_radius
-- Center of region -- Center of region
local center = { local center = {
x = pos1x + ((pos2x - pos1x) / 2), x = pos1x + ((pos2x - pos1x) / 2),
@ -679,9 +605,13 @@ function worldedit.clear_objects(pos1, pos2)
(center.x - pos1x) ^ 2 + (center.x - pos1x) ^ 2 +
(center.y - pos1y) ^ 2 + (center.y - pos1y) ^ 2 +
(center.z - pos1z) ^ 2) (center.z - pos1z) ^ 2)
local count = 0
for _, obj in pairs(minetest.get_objects_inside_radius(center, radius)) do for _, obj in pairs(minetest.get_objects_inside_radius(center, radius)) do
if should_delete(obj) then local entity = obj:get_luaentity()
local pos = obj:get_pos() -- Avoid players and WorldEdit entities
if not obj:is_player() and (not entity or
not entity.name:find("^worldedit:")) then
local pos = obj:getpos()
if pos.x >= pos1x and pos.x <= pos2x and if pos.x >= pos1x and pos.x <= pos2x and
pos.y >= pos1y and pos.y <= pos2y and pos.y >= pos1y and pos.y <= pos2y and
pos.z >= pos1z and pos.z <= pos2z then pos.z >= pos1z and pos.z <= pos2z then

View File

@ -1,2 +0,0 @@
name = worldedit
description = WorldEdit main functionality & API

View File

@ -4,47 +4,6 @@
local mh = worldedit.manip_helpers local mh = worldedit.manip_helpers
--- Adds a cube
-- @param pos Position of ground level center of cube
-- @param width Cube width. (x)
-- @param height Cube height. (y)
-- @param length Cube length. (z)
-- @param node_name Name of node to make cube of.
-- @param hollow Whether the cube should be hollow.
-- @return The number of nodes added.
function worldedit.cube(pos, width, height, length, node_name, hollow)
-- Set up voxel manipulator
local basepos = vector.subtract(pos, {x=math.floor(width/2), y=0, z=math.floor(length/2)})
local manip, area = mh.init(basepos, vector.add(basepos, {x=width, y=height, z=length}))
local data = mh.get_empty_data(area)
-- Add cube
local node_id = minetest.get_content_id(node_name)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = vector.subtract(basepos, area.MinEdge)
local count = 0
for z = 0, length-1 do
local index_z = (offset.z + z) * stride.z + 1 -- +1 for 1-based indexing
for y = 0, height-1 do
local index_y = index_z + (offset.y + y) * stride.y
for x = 0, width-1 do
local is_wall = z == 0 or z == length-1
or y == 0 or y == height-1
or x == 0 or x == width-1
if not hollow or is_wall then
local i = index_y + (offset.x + x)
data[i] = node_id
count = count + 1
end
end
end
end
mh.finish(manip, data)
return count
end
--- Adds a sphere of `node_name` centered at `pos`. --- Adds a sphere of `node_name` centered at `pos`.
-- @param pos Position to center sphere at. -- @param pos Position to center sphere at.
-- @param radius Sphere radius. -- @param radius Sphere radius.
@ -133,48 +92,35 @@ end
-- @param pos Position to center base of cylinder at. -- @param pos Position to center base of cylinder at.
-- @param axis Axis ("x", "y", or "z") -- @param axis Axis ("x", "y", or "z")
-- @param length Cylinder length. -- @param length Cylinder length.
-- @param radius1 Cylinder base radius. -- @param radius Cylinder radius.
-- @param radius2 Cylinder top radius.
-- @param node_name Name of node to make cylinder of. -- @param node_name Name of node to make cylinder of.
-- @param hollow Whether the cylinder should be hollow. -- @param hollow Whether the cylinder should be hollow.
-- @return The number of nodes added. -- @return The number of nodes added.
function worldedit.cylinder(pos, axis, length, radius1, radius2, node_name, hollow) function worldedit.cylinder(pos, axis, length, radius, node_name, hollow)
local other1, other2 = worldedit.get_axis_others(axis) local other1, other2 = worldedit.get_axis_others(axis)
-- Backwards compatibility
if type(radius2) == "string" then
hollow = node_name
node_name = radius2
radius2 = radius1 -- straight cylinder
end
-- Handle negative lengths -- Handle negative lengths
local current_pos = {x=pos.x, y=pos.y, z=pos.z} local current_pos = {x=pos.x, y=pos.y, z=pos.z}
if length < 0 then if length < 0 then
length = -length length = -length
current_pos[axis] = current_pos[axis] - length current_pos[axis] = current_pos[axis] - length
radius1, radius2 = radius2, radius1
end end
-- Set up voxel manipulator -- Set up voxel manipulator
local manip, area = mh.init_axis_radius_length(current_pos, axis, math.max(radius1, radius2), length) local manip, area = mh.init_axis_radius_length(current_pos, axis, radius, length)
local data = mh.get_empty_data(area) local data = mh.get_empty_data(area)
-- Add desired shape (anything inbetween cylinder & cone) -- Add cylinder
local node_id = minetest.get_content_id(node_name) local node_id = minetest.get_content_id(node_name)
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local stride = {x=1, y=area.ystride, z=area.zstride} local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = { local offset = {
x = current_pos.x - area.MinEdge.x, x = current_pos.x - area.MinEdge.x,
y = current_pos.y - area.MinEdge.y, y = current_pos.y - area.MinEdge.y,
z = current_pos.z - area.MinEdge.z, z = current_pos.z - area.MinEdge.z,
} }
local min_slice, max_slice = offset[axis], offset[axis] + length - 1
local count = 0 local count = 0
for i = 0, length - 1 do
-- Calulate radius for this "height" in the cylinder
local radius = radius1 + (radius2 - radius1) * (i + 1) / length
radius = math.floor(radius + 0.5) -- round
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
for index2 = -radius, radius do for index2 = -radius, radius do
-- Offset contributed by other axis 1 plus 1 to make it 1-indexed -- Offset contributed by other axis 1 plus 1 to make it 1-indexed
local new_index2 = (index2 + offset[other1]) * stride[other1] + 1 local new_index2 = (index2 + offset[other1]) * stride[other1] + 1
@ -182,11 +128,13 @@ function worldedit.cylinder(pos, axis, length, radius1, radius2, node_name, holl
local new_index3 = new_index2 + (index3 + offset[other2]) * stride[other2] local new_index3 = new_index2 + (index3 + offset[other2]) * stride[other2]
local squared = index2 * index2 + index3 * index3 local squared = index2 * index2 + index3 * index3
if squared <= max_radius and (not hollow or squared >= min_radius) then if squared <= max_radius and (not hollow or squared >= min_radius) then
-- Position is in cylinder, add node here -- Position is in cylinder
local vi = new_index3 + (offset[axis] + i) * stride[axis] -- Add column along axis
for index1 = min_slice, max_slice do
local vi = new_index3 + index1 * stride[axis]
data[vi] = node_id data[vi] = node_id
count = count + 1
end end
count = count + length
end end
end end
end end
@ -208,13 +156,12 @@ function worldedit.pyramid(pos, axis, height, node_name, hollow)
local other1, other2 = worldedit.get_axis_others(axis) local other1, other2 = worldedit.get_axis_others(axis)
-- Set up voxel manipulator -- Set up voxel manipulator
-- FIXME: passing negative <radius> causes mis-sorted pos to be passed local manip, area = mh.init_axis_radius(pos, axis,
-- into mh.init() which is technically not allowed but works height >= 0 and height or -height)
local manip, area = mh.init_axis_radius(pos, axis, height)
local data = mh.get_empty_data(area) local data = mh.get_empty_data(area)
-- Handle inverted pyramids -- Handle inverted pyramids
local step local start_axis, end_axis, step
if height > 0 then if height > 0 then
height = height - 1 height = height - 1
step = 1 step = 1

View File

@ -1,7 +1,9 @@
--- Schematic serialization and deserialiation. --- Schematic serialization and deserialiation.
-- @module worldedit.serialization -- @module worldedit.serialization
worldedit.LATEST_SERIALIZATION_VERSION = 6 worldedit.LATEST_SERIALIZATION_VERSION = 5
local LATEST_SERIALIZATION_HEADER = worldedit.LATEST_SERIALIZATION_VERSION .. ":"
--[[ --[[
Serialization version history: Serialization version history:
@ -13,7 +15,6 @@ Serialization version history:
`name`, `param1`, `param2`, and `meta` fields. `name`, `param1`, `param2`, and `meta` fields.
5: Added header and made `param1`, `param2`, and `meta` fields optional. 5: Added header and made `param1`, `param2`, and `meta` fields optional.
Header format: <Version>,<ExtraHeaderField1>,...:<Content> Header format: <Version>,<ExtraHeaderField1>,...:<Content>
6: Much more complicated but also better format
--]] --]]
@ -23,7 +24,7 @@ Serialization version history:
-- @return Extra header fields as a list of strings, or nil if not supported. -- @return Extra header fields as a list of strings, or nil if not supported.
-- @return Content (data after header). -- @return Content (data after header).
function worldedit.read_header(value) function worldedit.read_header(value)
if value:find("^[0-9]+[,:]") then if value:find("^[0-9]+[%-:]") then
local header_end = value:find(":", 1, true) local header_end = value:find(":", 1, true)
local header = value:sub(1, header_end - 1):split(",") local header = value:sub(1, header_end - 1):split(",")
local version = tonumber(header[1]) local version = tonumber(header[1])
@ -55,232 +56,66 @@ function worldedit.serialize(pos1, pos2)
worldedit.keep_loaded(pos1, pos2) worldedit.keep_loaded(pos1, pos2)
local get_node, get_meta, hash_node_position = local pos = {x=pos1.x, y=0, z=0}
minetest.get_node, minetest.get_meta, minetest.hash_node_position
-- Find the positions which have metadata
local has_meta = {}
local meta_positions = minetest.find_nodes_with_meta(pos1, pos2)
for i = 1, #meta_positions do
has_meta[hash_node_position(meta_positions[i])] = true
end
-- Decide axis of saved rows
local dim = vector.add(vector.subtract(pos2, pos1), 1)
local axis
if dim.x * dim.y < math.min(dim.y * dim.z, dim.x * dim.z) then
axis = "z"
elseif dim.x * dim.z < math.min(dim.x * dim.y, dim.y * dim.z) then
axis = "y"
elseif dim.y * dim.z < math.min(dim.x * dim.y, dim.x * dim.z) then
axis = "x"
else
axis = "x" -- X or Z are usually most efficient
end
local other1, other2 = worldedit.get_axis_others(axis)
-- Helper functions (1)
local MATCH_DIST = 8
local function match_init(array, first_value)
array[1] = first_value
return {first_value}
end
local function match_try(cache, prev_pushed, value)
local i = #cache
while i >= 1 do
if cache[i] == value then
local ret = -(#cache - i + 1)
local was_value = type(prev_pushed) ~= "number" or prev_pushed >= 0
return ret, (was_value and ret == -1) or prev_pushed == ret
end
i = i - 1
end
return nil, false
end
local function match_push(cache, match, value)
if match ~= nil then -- don't advance cache
return match
end
local idx = #cache + 1
cache[idx] = value
if idx > MATCH_DIST then
table.remove(cache, 1)
end
return value
end
-- Helper functions (2)
local function cur_new(pos, pos1)
return {
a = axis,
p = {pos.x - pos1.x, pos.y - pos1.y, pos.z - pos1.z},
c = 1,
data = {},
param1 = {},
param2 = {},
meta = {},
}
end
local function is_emptyish(t)
-- returns true if <t> contains only one element and that one element is == 0
local seen = false
for _, value in pairs(t) do
if not seen then
if value ~= 0 then
return false
end
seen = true
else
return false
end
end
return true
end
local function cur_finish(result, cur)
if is_emptyish(cur.param1) then
cur.param1 = nil
end
if is_emptyish(cur.param2) then
cur.param2 = nil
end
if next(cur.meta) == nil then
cur.meta = nil
end
result[#result + 1] = cur
end
-- Serialize stuff
local pos = {}
local count = 0 local count = 0
local result = {} local result = {}
local cur local get_node, get_meta = minetest.get_node, minetest.get_meta
local cache_data, cache_param1, cache_param2 while pos.x <= pos2.x do
local prev_data, prev_param1, prev_param2 pos.y = pos1.y
pos[other1] = pos1[other1] while pos.y <= pos2.y do
while pos[other1] <= pos2[other1] do pos.z = pos1.z
pos[other2] = pos1[other2] while pos.z <= pos2.z do
while pos[other2] <= pos2[other2] do
pos[axis] = pos1[axis]
while pos[axis] <= pos2[axis] do
local node = get_node(pos) local node = get_node(pos)
if node.name ~= "air" and node.name ~= "ignore" then if node.name ~= "air" and node.name ~= "ignore" then
local meta
if has_meta[hash_node_position(pos)] then
meta = get_meta(pos):to_table()
-- Convert metadata item stacks to item strings
for _, invlist in pairs(meta.inventory) do
for index = 1, #invlist do
local itemstack = invlist[index]
if itemstack.to_string then
invlist[index] = itemstack:to_string()
end
end
end
end
if cur == nil then -- Start a new row
cur = cur_new(pos, pos1, axis, other1, other2)
cache_data = match_init(cur.data, node.name)
cache_param1 = match_init(cur.param1, node.param1)
cache_param2 = match_init(cur.param2, node.param2)
prev_data = cur.data[1]
prev_param1 = cur.param1[1]
prev_param2 = cur.param2[1]
cur.meta[1] = meta
else -- Append to existing row
local next_c = cur.c + 1
cur.c = next_c
local value, m, can_omit
value = node.name
m, can_omit = match_try(cache_data, prev_data, node.name)
if not can_omit then
prev_data = match_push(cache_data, m, value)
cur.data[next_c] = prev_data
end
value = node.param1
m, can_omit = match_try(cache_param1, prev_param1, value)
if not can_omit then
prev_param1 = match_push(cache_param1, m, value)
cur.param1[next_c] = prev_param1
end
value = node.param2
m, can_omit = match_try(cache_param2, prev_param2, value)
if not can_omit then
prev_param2 = match_push(cache_param2, m, value)
cur.param2[next_c] = prev_param2
end
cur.meta[next_c] = meta
end
count = count + 1 count = count + 1
else local meta = get_meta(pos):to_table()
if cur ~= nil then -- Finish row
cur_finish(result, cur)
cur = nil
end
end
pos[axis] = pos[axis] + 1
local meta_empty = true
-- Convert metadata item stacks to item strings
for name, inventory in pairs(meta.inventory) do
for index, stack in ipairs(inventory) do
meta_empty = false
inventory[index] = stack.to_string and stack:to_string() or stack
end end
if cur ~= nil then -- Finish leftover row
cur_finish(result, cur)
cur = nil
end end
pos[other2] = pos[other2] + 1 for k in pairs(meta) do
end if k ~= "inventory" then
pos[other1] = pos[other1] + 1 meta_empty = false
end
-- Serialize entries
result = minetest.serialize(result)
return tonumber(worldedit.LATEST_SERIALIZATION_VERSION) .. "," ..
string.format("%d,%d,%d:", dim.x, dim.y, dim.z) .. result, count
end
-- Contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile)
-- by ChillCode, available under the MIT license.
local function deserialize_workaround(content)
local nodes
if not minetest.global_exists("jit") then
nodes = minetest.deserialize(content, true)
else
-- XXX: This is a filthy hack that works surprisingly well
-- in LuaJIT, `minetest.deserialize` will fail due to the register limit
nodes = {}
content = content:gsub("^%s*return%s*{", "", 1):gsub("}%s*$", "", 1) -- remove the starting and ending values to leave only the node data
-- remove string contents strings while preserving their length
local escaped = content:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
local startpos, startpos1 = 1, 1
local endpos
while true do -- go through each individual node entry (except the last)
startpos, endpos = escaped:find("},%s*{", startpos)
if not startpos then
break break
end end
local current = content:sub(startpos1, startpos)
local entry = minetest.deserialize("return " .. current, true)
table.insert(nodes, entry)
startpos, startpos1 = endpos, endpos
end
local entry = minetest.deserialize("return " .. content:sub(startpos1), true) -- process the last entry
table.insert(nodes, entry)
end
return nodes
end end
result[count] = {
x = pos.x - pos1.x,
y = pos.y - pos1.y,
z = pos.z - pos1.z,
name = node.name,
param1 = node.param1 ~= 0 and node.param1 or nil,
param2 = node.param2 ~= 0 and node.param2 or nil,
meta = not meta_empty and meta or nil,
}
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
-- Serialize entries
result = minetest.serialize(result)
return LATEST_SERIALIZATION_HEADER .. result, count
end
--- Loads the schematic in `value` into a node list in the latest format. --- Loads the schematic in `value` into a node list in the latest format.
-- Contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile)
-- by ChillCode, available under the MIT license.
-- @return A node list in the latest format, or nil on failure. -- @return A node list in the latest format, or nil on failure.
local function legacy_load_schematic(version, header, content) local function load_schematic(value)
local version, header, content = worldedit.read_header(value)
local nodes = {} local nodes = {}
if version == 1 or version == 2 then -- Original flat table format if version == 1 or version == 2 then -- Original flat table format
local tables = minetest.deserialize(content, true) local tables = minetest.deserialize(content)
if not tables then return nil end if not tables then return nil end
-- Transform the node table into an array of nodes -- Transform the node table into an array of nodes
@ -318,9 +153,28 @@ local function legacy_load_schematic(version, header, content)
}) })
end end
elseif version == 4 or version == 5 then -- Nested table format elseif version == 4 or version == 5 then -- Nested table format
nodes = deserialize_workaround(content) if not jit then
elseif version >= 6 then -- This is broken for larger tables in the current version of LuaJIT
error("legacy_load_schematic called for non-legacy schematic") nodes = minetest.deserialize(content)
else
-- XXX: This is a filthy hack that works surprisingly well - in LuaJIT, `minetest.deserialize` will fail due to the register limit
nodes = {}
content = content:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1) -- remove the starting and ending values to leave only the node data
local escaped = content:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
local startpos, startpos1, endpos = 1, 1
while true do -- go through each individual node entry (except the last)
startpos, endpos = escaped:find("},%s*{", startpos)
if not startpos then
break
end
local current = content:sub(startpos1, startpos)
local entry = minetest.deserialize("return " .. current)
table.insert(nodes, entry)
startpos, startpos1 = endpos, endpos
end
local entry = minetest.deserialize("return " .. content:sub(startpos1)) -- process the last entry
table.insert(nodes, entry)
end
else else
return nil return nil
end end
@ -333,29 +187,14 @@ end
-- @return High corner position. -- @return High corner position.
-- @return The number of nodes. -- @return The number of nodes.
function worldedit.allocate(origin_pos, value) function worldedit.allocate(origin_pos, value)
local version, header, content = worldedit.read_header(value) local nodes = load_schematic(value)
if version == 6 then if not nodes then return nil end
local content = deserialize_workaround(content) return worldedit.allocate_with_nodes(origin_pos, nodes)
local pos2 = {
x = origin_pos.x + tonumber(header[1]) - 1,
y = origin_pos.y + tonumber(header[2]) - 1,
z = origin_pos.z + tonumber(header[3]) - 1,
}
local count = 0
for _, row in ipairs(content) do
count = count + row.c
end
return origin_pos, pos2, count
else
local nodes = legacy_load_schematic(version, header, content)
if not nodes or #nodes == 0 then return nil end
return worldedit.legacy_allocate_with_nodes(origin_pos, nodes)
end
end end
-- Internal -- Internal
function worldedit.legacy_allocate_with_nodes(origin_pos, nodes) function worldedit.allocate_with_nodes(origin_pos, nodes)
local huge = math.huge local huge = math.huge
local pos1x, pos1y, pos1z = huge, huge, huge local pos1x, pos1y, pos1z = huge, huge, huge
local pos2x, pos2y, pos2z = -huge, -huge, -huge local pos2x, pos2y, pos2z = -huge, -huge, -huge
@ -378,22 +217,10 @@ end
--- Loads the nodes represented by string `value` at position `origin_pos`. --- Loads the nodes represented by string `value` at position `origin_pos`.
-- @return The number of nodes deserialized. -- @return The number of nodes deserialized.
function worldedit.deserialize(origin_pos, value) function worldedit.deserialize(origin_pos, value)
local version, header, content = worldedit.read_header(value) local nodes = load_schematic(value)
if version == 6 then if not nodes then return nil end
local content = deserialize_workaround(content)
local pos2 = {
x = origin_pos.x + tonumber(header[1]) - 1,
y = origin_pos.y + tonumber(header[2]) - 1,
z = origin_pos.z + tonumber(header[3]) - 1,
}
worldedit.keep_loaded(origin_pos, pos2)
return worldedit.deserialize_with_content(origin_pos, content) local pos1, pos2 = worldedit.allocate_with_nodes(origin_pos, nodes)
else
local nodes = legacy_load_schematic(version, header, content)
if not nodes or #nodes == 0 then return nil end
local pos1, pos2 = worldedit.legacy_allocate_with_nodes(origin_pos, nodes)
worldedit.keep_loaded(pos1, pos2) worldedit.keep_loaded(pos1, pos2)
local origin_x, origin_y, origin_z = origin_pos.x, origin_pos.y, origin_pos.z local origin_x, origin_y, origin_z = origin_pos.x, origin_pos.y, origin_pos.z
@ -409,79 +236,4 @@ function worldedit.deserialize(origin_pos, value)
end end
return #nodes return #nodes
end end
end
-- Internal
function worldedit.deserialize_with_content(origin_pos, content)
-- Helper functions
local function resolve_refs(array)
-- find (and cache) highest index
local max_i = 1
for i, _ in pairs(array) do
if i > max_i then max_i = i end
end
array.max_i = max_i
-- resolve references
local cache = {}
for i = 1, max_i do
local v = array[i]
if v ~= nil then
if type(v) == "number" and v < 0 then -- is a reference
array[i] = cache[#cache + v + 1]
else
cache[#cache + 1] = v
end
end
end
end
local function read_in_array(array, idx)
if idx > array.max_i then
return array[array.max_i]
end
-- go backwards until we find something
repeat
local v = array[idx]
if v ~= nil then
return v
end
idx = idx - 1
until idx == 0
assert(false)
end
-- Actually deserialize
local count = 0
local entry = {}
local add_node, get_meta = minetest.add_node, minetest.get_meta
for _, row in ipairs(content) do
local axis = row.a
local pos = {
x = origin_pos.x + row.p[1],
y = origin_pos.y + row.p[2],
z = origin_pos.z + row.p[3],
}
if row.param1 == nil then row.param1 = {0} end
if row.param2 == nil then row.param2 = {0} end
if row.meta == nil then row.meta = {} end
resolve_refs(row.data)
resolve_refs(row.param1)
resolve_refs(row.param2)
for i = 1, row.c do
entry.name = read_in_array(row.data, i)
entry.param1 = read_in_array(row.param1, i)
entry.param2 = read_in_array(row.param2, i)
add_node(pos, entry)
local meta = row.meta[i]
if meta then
get_meta(pos):from_table(meta)
end
pos[axis] = pos[axis] + 1
end
count = count + row.c
end
return count
end

1
worldedit/visualization.lua Normal file → Executable file
View File

@ -6,7 +6,6 @@ minetest.register_node("worldedit:placeholder", {
paramtype = "light", paramtype = "light",
sunlight_propagates = true, sunlight_propagates = true,
diggable = false, diggable = false,
pointable = false,
walkable = false, walkable = false,
groups = {not_in_creative_inventory=1}, groups = {not_in_creative_inventory=1},
}) })

View File

@ -1,125 +0,0 @@
if minetest.raycast == nil then
error(
"worldedit_brush requires at least Minetest 5.0"
)
end
local BRUSH_MAX_DIST = 150
local brush_on_use = function(itemstack, placer)
local meta = itemstack:get_meta()
local name = placer:get_player_name()
local cmd = meta:get_string("command")
if cmd == "" then
worldedit.player_notify(name,
"This brush is not bound, use //brush to bind a command to it.")
return false
end
local cmddef = worldedit.registered_commands[cmd]
if cmddef == nil then return false end -- shouldn't happen as //brush checks this
local has_privs, missing_privs = minetest.check_player_privs(name, cmddef.privs)
if not has_privs then
worldedit.player_notify(name,
"Missing privileges: " .. table.concat(missing_privs, ", "))
return false
end
local raybegin = vector.add(placer:get_pos(),
{x=0, y=placer:get_properties().eye_height, z=0})
local rayend = vector.add(raybegin, vector.multiply(placer:get_look_dir(), BRUSH_MAX_DIST))
local ray = minetest.raycast(raybegin, rayend, false, true)
local pointed_thing = ray:next()
if pointed_thing == nil then
worldedit.player_notify(name, "Too far away.")
return false
end
assert(pointed_thing.type == "node")
worldedit.pos1[name] = pointed_thing.under
worldedit.pos2[name] = nil
worldedit.marker_update(name)
-- this isn't really clean...
local player_notify_old = worldedit.player_notify
worldedit.player_notify = function(name, msg)
if string.match(msg, "^%d") then return end -- discard "1234 nodes added."
return player_notify_old(name, msg)
end
assert(cmddef.require_pos < 2)
local parsed = {cmddef.parse(meta:get_string("params"))}
if not table.remove(parsed, 1) then return false end -- shouldn't happen
minetest.log("action", string.format("%s uses WorldEdit brush (//%s) at %s",
name, cmd, minetest.pos_to_string(pointed_thing.under)))
cmddef.func(name, unpack(parsed))
worldedit.player_notify = player_notify_old
return true
end
minetest.register_tool(":worldedit:brush", {
description = "WorldEdit Brush",
inventory_image = "worldedit_brush.png",
stack_max = 1, -- no need to stack these (metadata prevents this anyway)
range = 0,
on_use = function(itemstack, placer, pointed_thing)
brush_on_use(itemstack, placer)
return itemstack -- nothing consumed, nothing changed
end,
})
worldedit.register_command("brush", {
privs = {worldedit=true},
params = "none/<cmd> [parameters]",
description = "Assign command to WorldEdit brush item",
parse = function(param)
local found, _, cmd, params = param:find("^([^%s]+)%s+(.+)$")
if not found then
params = ""
found, _, cmd = param:find("^(.+)$")
end
if not found then
return false
end
return true, cmd, params
end,
func = function(name, cmd, params)
local itemstack = minetest.get_player_by_name(name):get_wielded_item()
if itemstack == nil or itemstack:get_name() ~= "worldedit:brush" then
worldedit.player_notify(name, "Not holding brush item.")
return
end
cmd = cmd:lower()
local meta = itemstack:get_meta()
if cmd == "none" then
meta:from_table(nil)
worldedit.player_notify(name, "Brush assignment cleared.")
else
local cmddef = worldedit.registered_commands[cmd]
if cmddef == nil or cmddef.require_pos ~= 1 then
worldedit.player_notify(name, "//" .. cmd .. " cannot be used with brushes")
return
end
-- Try parsing command params so we can give the user feedback
local ok, err = cmddef.parse(params)
if not ok then
err = err or "invalid usage"
worldedit.player_notify(name, "Error with brush command: " .. err)
return
end
meta:set_string("command", cmd)
meta:set_string("params", params)
local fullcmd = "//" .. cmd .. " " .. params
meta:set_string("description",
minetest.registered_tools["worldedit:brush"].description .. ": " .. fullcmd)
worldedit.player_notify(name, "Brush assigned to command: " .. fullcmd)
end
minetest.get_player_by_name(name):set_wielded_item(itemstack)
end,
})

View File

@ -1,3 +0,0 @@
name = worldedit_brush
description = WorldEdit brush
depends = worldedit, worldedit_commands

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 B

View File

@ -1,22 +1,28 @@
worldedit.register_command("outset", { minetest.register_chatcommand("/outset", {
params = "[h/v] <amount>", params = "[h|v] <amount>",
description = "Outset the selected region.", description = "outset the selection",
privs = {worldedit=true}, privs = {worldedit=true},
require_pos = 2, func = function(name, param)
parse = function(param)
local find, _, dir, amount = param:find("(%a*)%s*([+-]?%d+)") local find, _, dir, amount = param:find("(%a*)%s*([+-]?%d+)")
if find == nil then if find == nil then
return false return false, "invalid usage: " .. param
end
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
return false,
"Undefined region. Region must be defined beforehand."
end end
local hv_test = dir:find("[^hv]+") local hv_test = dir:find("[^hv]+")
if hv_test ~= nil then if hv_test ~= nil then
return false, "Invalid direction." return false, "Invalid direction."
end end
return true, dir, tonumber(amount)
end,
func = function(name, dir, amount)
if dir == "" or dir == "hv" or dir == "vh" then if dir == "" or dir == "hv" or dir == "vh" then
assert(worldedit.cuboid_volumetric_expand(name, amount)) assert(worldedit.cuboid_volumetric_expand(name, amount))
elseif dir == "h" then elseif dir == "h" then
@ -34,26 +40,35 @@ worldedit.register_command("outset", {
worldedit.marker_update(name) worldedit.marker_update(name)
return true, "Region outset by " .. amount .. " blocks" return true, "Region outset by " .. amount .. " blocks"
end, end,
}) }
)
worldedit.register_command("inset", { minetest.register_chatcommand("/inset", {
params = "[h/v] <amount>", params = "[h|v] <amount>",
description = "Inset the selected region.", description = "inset the selection",
privs = {worldedit=true}, privs = {worldedit=true},
require_pos = 2, func = function(name, param)
parse = function(param)
local find, _, dir, amount = param:find("(%a*)%s*([+-]?%d+)") local find, _, dir, amount = param:find("(%a*)%s*([+-]?%d+)")
if find == nil then if find == nil then
return false return false, "invalid usage: " .. param
end end
if dir:find("[^hv]") ~= nil then
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
return false,
"Undefined region. Region must be defined beforehand."
end
local hv_test = dir:find("[^hv]+")
if hv_test ~= nil then
return false, "Invalid direction." return false, "Invalid direction."
end end
return true, dir, tonumber(amount)
end,
func = function(name, dir, amount)
if dir == "" or dir == "vh" or dir == "hv" then if dir == "" or dir == "vh" or dir == "hv" then
assert(worldedit.cuboid_volumetric_expand(name, -amount)) assert(worldedit.cuboid_volumetric_expand(name, -amount))
elseif dir == "h" then elseif dir == "h" then
@ -71,23 +86,30 @@ worldedit.register_command("inset", {
worldedit.marker_update(name) worldedit.marker_update(name)
return true, "Region inset by " .. amount .. " blocks" return true, "Region inset by " .. amount .. " blocks"
end, end,
}) }
)
worldedit.register_command("shift", { minetest.register_chatcommand("/shift", {
params = "x/y/z/?/up/down/left/right/front/back [+/-]<amount>", params = "[x|y|z|?|up|down|left|right|front|back] [+|-]<amount>",
description = "Shifts the selection area without moving its contents", description = "Moves the selection region. Does not move contents.",
privs = {worldedit=true}, privs = {worldedit=true},
require_pos = 2, func = function(name, param)
parse = function(param) local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
local find, _, direction, amount = param:find("([%?%l]+)%s*([+-]?%d+)") local find, _, direction, amount = param:find("([%?%l]+)%s*([+-]?%d+)")
if find == nil then if find == nil then
return false worldedit.player_notify(name, "invalid usage: " .. param)
return
end
if pos1 == nil or pos2 == nil then
worldedit.player_notify(name,
"Undefined region. Region must be defined beforehand.")
return
end end
return true, direction, tonumber(amount)
end,
func = function(name, direction, amount)
local axis, dir local axis, dir
if direction == "x" or direction == "y" or direction == "z" then if direction == "x" or direction == "y" or direction == "z" then
axis, dir = direction, 1 axis, dir = direction, 1
@ -106,31 +128,36 @@ worldedit.register_command("shift", {
return true, "Region shifted by " .. amount .. " nodes" return true, "Region shifted by " .. amount .. " nodes"
end, end,
}) }
)
worldedit.register_command("expand", { minetest.register_chatcommand("/expand", {
params = "[+/-]x/y/z/?/up/down/left/right/front/back <amount> [reverse amount]", params = "[+|-]<x|y|z|?|up|down|left|right|front|back> <amount> [reverse-amount]",
description = "Expands the selection in the selected absolute or relative axis", description = "expand the selection in one or two directions at once",
privs = {worldedit=true}, privs = {worldedit=true},
require_pos = 2, func = function(name, param)
parse = function(param)
local find, _, sign, direction, amount, local find, _, sign, direction, amount,
rev_amount = param:find("([+-]?)([%?%l]+)%s*(%d+)%s*(%d*)") rev_amount = param:find("([+-]?)([%?%l]+)%s*(%d+)%s*(%d*)")
if find == nil then if find == nil then
return false worldedit.player_notify(name, "invalid use: " .. param)
return
end end
if rev_amount == "" then if worldedit.pos1[name] == nil or worldedit.pos2[name] == nil then
rev_amount = "0" worldedit.player_notify(name,
"Undefined region. Region must be defined beforehand.")
return
end end
return true, sign, direction, tonumber(amount), tonumber(rev_amount)
end,
func = function(name, sign, direction, amount, rev_amount)
local absolute = direction:find("[xyz?]") local absolute = direction:find("[xyz?]")
local dir, axis local dir, axis
if rev_amount == "" then
rev_amount = 0
end
if absolute == nil then if absolute == nil then
axis, dir = worldedit.translate_direction(name, direction) axis, dir = worldedit.translate_direction(name, direction)
@ -155,31 +182,36 @@ worldedit.register_command("expand", {
worldedit.marker_update(name) worldedit.marker_update(name)
return true, "Region expanded by " .. (amount + rev_amount) .. " nodes" return true, "Region expanded by " .. (amount + rev_amount) .. " nodes"
end, end,
}) }
)
worldedit.register_command("contract", { minetest.register_chatcommand("/contract", {
params = "[+/-]x/y/z/?/up/down/left/right/front/back <amount> [reverse amount]", params = "[+|-]<x|y|z|?|up|down|left|right|front|back> <amount> [reverse-amount]",
description = "Contracts the selection in the selected absolute or relative axis", description = "contract the selection in one or two directions at once",
privs = {worldedit=true}, privs = {worldedit=true},
require_pos = 2, func = function(name, param)
parse = function(param)
local find, _, sign, direction, amount, local find, _, sign, direction, amount,
rev_amount = param:find("([+-]?)([%?%l]+)%s*(%d+)%s*(%d*)") rev_amount = param:find("([+-]?)([%?%l]+)%s*(%d+)%s*(%d*)")
if find == nil then if find == nil then
return false worldedit.player_notify(name, "invalid use: " .. param)
return
end end
if rev_amount == "" then if worldedit.pos1[name] == nil or worldedit.pos2[name] == nil then
rev_amount = "0" worldedit.player_notify(name,
"Undefined region. Region must be defined beforehand.")
return
end end
return true, sign, direction, tonumber(amount), tonumber(rev_amount)
end,
func = function(name, sign, direction, amount, rev_amount)
local absolute = direction:find("[xyz?]") local absolute = direction:find("[xyz?]")
local dir, axis local dir, axis
if rev_amount == "" then
rev_amount = 0
end
if absolute == nil then if absolute == nil then
axis, dir = worldedit.translate_direction(name, direction) axis, dir = worldedit.translate_direction(name, direction)
@ -204,63 +236,5 @@ worldedit.register_command("contract", {
worldedit.marker_update(name) worldedit.marker_update(name)
return true, "Region contracted by " .. (amount + rev_amount) .. " nodes" return true, "Region contracted by " .. (amount + rev_amount) .. " nodes"
end, end,
}) }
)
worldedit.register_command("cubeapply", {
params = "<size>/(<sizex> <sizey> <sizez>) <command> [parameters]",
description = "Select a cube with side length <size> around position 1 and run <command> on region",
privs = {worldedit=true},
require_pos = 1,
parse = function(param)
local found, _, sidex, sidey, sidez, cmd, args =
param:find("^(%d+)%s+(%d+)%s+(%d+)%s+([^%s]+)%s*(.*)$")
if found == nil then
found, _, sidex, cmd, args = param:find("^(%d+)%s+([^%s]+)%s*(.*)$")
if found == nil then
return false
end
sidey = sidex
sidez = sidex
end
sidex = tonumber(sidex)
sidey = tonumber(sidey)
sidez = tonumber(sidez)
if sidex < 1 or sidey < 1 or sidez < 1 then
return false
end
local cmddef = worldedit.registered_commands[cmd]
if cmddef == nil or cmddef.require_pos ~= 2 then
return false, "invalid usage: //" .. cmd .. " cannot be used with cubeapply"
end
-- run parsing of target command
local parsed = {cmddef.parse(args)}
if not table.remove(parsed, 1) then
return false, parsed[1]
end
return true, sidex, sidey, sidez, cmd, parsed
end,
nodes_needed = function(name, sidex, sidey, sidez, cmd, parsed)
-- its not possible to defer to the target command at this point
return sidex * sidey * sidez
end,
func = function(name, sidex, sidey, sidez, cmd, parsed)
local cmddef = assert(worldedit.registered_commands[cmd])
local success, missing_privs = minetest.check_player_privs(name, cmddef.privs)
if not success then
worldedit.player_notify(name, "Missing privileges: " ..
table.concat(missing_privs, ", "))
return
end
-- update region to be the cuboid the user wanted
local half = vector.divide(vector.new(sidex, sidey, sidez), 2)
local sizea, sizeb = vector.apply(half, math.floor), vector.apply(half, math.ceil)
local center = worldedit.pos1[name]
worldedit.pos1[name] = vector.subtract(center, sizea)
worldedit.pos2[name] = vector.add(center, vector.subtract(sizeb, 1))
worldedit.marker_update(name)
-- actually run target command
return cmddef.func(name, unpack(parsed))
end,
})

1
worldedit_commands/depends.txt Executable file
View File

@ -0,0 +1 @@
worldedit

File diff suppressed because it is too large Load Diff

View File

@ -2,60 +2,57 @@ worldedit.marker1 = {}
worldedit.marker2 = {} worldedit.marker2 = {}
worldedit.marker_region = {} worldedit.marker_region = {}
local init_sentinel = "new" .. tostring(math.random(99999))
--marks worldedit region position 1 --marks worldedit region position 1
worldedit.mark_pos1 = function(name, region_too) worldedit.mark_pos1 = function(name)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 ~= nil then
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos1)
end
if worldedit.marker1[name] ~= nil then --marker already exists if worldedit.marker1[name] ~= nil then --marker already exists
worldedit.marker1[name]:remove() --remove marker worldedit.marker1[name]:remove() --remove marker
worldedit.marker1[name] = nil worldedit.marker1[name] = nil
end end
if pos1 ~= nil then if pos1 ~= nil then
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos1)
--add marker --add marker
worldedit.marker1[name] = minetest.add_entity(pos1, "worldedit:pos1", init_sentinel) worldedit.marker1[name] = minetest.add_entity(pos1, "worldedit:pos1")
if worldedit.marker1[name] ~= nil then if worldedit.marker1[name] ~= nil then
worldedit.marker1[name]:get_luaentity().player_name = name worldedit.marker1[name]:get_luaentity().player_name = name
end end
end end
if region_too == nil or region_too then
worldedit.mark_region(name) worldedit.mark_region(name)
end end
end
--marks worldedit region position 2 --marks worldedit region position 2
worldedit.mark_pos2 = function(name, region_too) worldedit.mark_pos2 = function(name)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos2 ~= nil then
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos2, pos2)
end
if worldedit.marker2[name] ~= nil then --marker already exists if worldedit.marker2[name] ~= nil then --marker already exists
worldedit.marker2[name]:remove() --remove marker worldedit.marker2[name]:remove() --remove marker
worldedit.marker2[name] = nil worldedit.marker2[name] = nil
end end
if pos2 ~= nil then if pos2 ~= nil then
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos2, pos2)
--add marker --add marker
worldedit.marker2[name] = minetest.add_entity(pos2, "worldedit:pos2", init_sentinel) worldedit.marker2[name] = minetest.add_entity(pos2, "worldedit:pos2")
if worldedit.marker2[name] ~= nil then if worldedit.marker2[name] ~= nil then
worldedit.marker2[name]:get_luaentity().player_name = name worldedit.marker2[name]:get_luaentity().player_name = name
end end
end end
if region_too == nil or region_too then
worldedit.mark_region(name) worldedit.mark_region(name)
end end
end
worldedit.mark_region = function(name) worldedit.mark_region = function(name)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if worldedit.marker_region[name] ~= nil then --marker already exists if worldedit.marker_region[name] ~= nil then --marker already exists
--wip: make the area stay loaded somehow
for _, entity in ipairs(worldedit.marker_region[name]) do for _, entity in ipairs(worldedit.marker_region[name]) do
entity:remove() entity:remove()
end end
@ -67,7 +64,7 @@ worldedit.mark_region = function(name)
local vec = vector.subtract(pos2, pos1) local vec = vector.subtract(pos2, pos1)
local maxside = math.max(vec.x, math.max(vec.y, vec.z)) local maxside = math.max(vec.x, math.max(vec.y, vec.z))
local limit = tonumber(minetest.settings:get("active_object_send_range_blocks")) * 16 local limit = tonumber(minetest.setting_get("active_object_send_range_blocks")) * 16
if maxside > limit * 1.5 then if maxside > limit * 1.5 then
-- The client likely won't be able to see the plane markers as intended anyway, -- The client likely won't be able to see the plane markers as intended anyway,
-- thus don't place them and also don't load the area into memory -- thus don't place them and also don't load the area into memory
@ -85,8 +82,7 @@ worldedit.mark_region = function(name)
--XY plane markers --XY plane markers
for _, z in ipairs({pos1.z - 0.5, pos2.z + 0.5}) do for _, z in ipairs({pos1.z - 0.5, pos2.z + 0.5}) do
local entpos = {x=pos1.x + sizex - 0.5, y=pos1.y + sizey - 0.5, z=z} local marker = minetest.add_entity({x=pos1.x + sizex - 0.5, y=pos1.y + sizey - 0.5, z=z}, "worldedit:region_cube")
local marker = minetest.add_entity(entpos, "worldedit:region_cube", init_sentinel)
if marker ~= nil then if marker ~= nil then
marker:set_properties({ marker:set_properties({
visual_size={x=sizex * 2, y=sizey * 2}, visual_size={x=sizex * 2, y=sizey * 2},
@ -99,14 +95,13 @@ worldedit.mark_region = function(name)
--YZ plane markers --YZ plane markers
for _, x in ipairs({pos1.x - 0.5, pos2.x + 0.5}) do for _, x in ipairs({pos1.x - 0.5, pos2.x + 0.5}) do
local entpos = {x=x, y=pos1.y + sizey - 0.5, z=pos1.z + sizez - 0.5} local marker = minetest.add_entity({x=x, y=pos1.y + sizey - 0.5, z=pos1.z + sizez - 0.5}, "worldedit:region_cube")
local marker = minetest.add_entity(entpos, "worldedit:region_cube", init_sentinel)
if marker ~= nil then if marker ~= nil then
marker:set_properties({ marker:set_properties({
visual_size={x=sizez * 2, y=sizey * 2}, visual_size={x=sizez * 2, y=sizey * 2},
collisionbox = {-thickness, -sizey, -sizez, thickness, sizey, sizez}, collisionbox = {-thickness, -sizey, -sizez, thickness, sizey, sizez},
}) })
marker:set_yaw(math.pi / 2) marker:setyaw(math.pi / 2)
marker:get_luaentity().player_name = name marker:get_luaentity().player_name = name
table.insert(markers, marker) table.insert(markers, marker)
end end
@ -116,13 +111,6 @@ worldedit.mark_region = function(name)
end end
end end
--convenience function that calls everything
worldedit.marker_update = function(name)
worldedit.mark_pos1(name, false)
worldedit.mark_pos2(name, false)
worldedit.mark_region(name)
end
minetest.register_entity(":worldedit:pos1", { minetest.register_entity(":worldedit:pos1", {
initial_properties = { initial_properties = {
visual = "cube", visual = "cube",
@ -132,11 +120,9 @@ minetest.register_entity(":worldedit:pos1", {
"worldedit_pos1.png", "worldedit_pos1.png"}, "worldedit_pos1.png", "worldedit_pos1.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55}, collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
physical = false, physical = false,
static_save = false,
}, },
on_activate = function(self, staticdata, dtime_s) on_step = function(self, dtime)
if staticdata ~= init_sentinel then if worldedit.marker1[self.player_name] == nil then
-- we were loaded from before static_save = false was added
self.object:remove() self.object:remove()
end end
end, end,
@ -144,9 +130,6 @@ minetest.register_entity(":worldedit:pos1", {
self.object:remove() self.object:remove()
worldedit.marker1[self.player_name] = nil worldedit.marker1[self.player_name] = nil
end, end,
on_blast = function(self, damage)
return false, false, {} -- don't damage or knockback
end,
}) })
minetest.register_entity(":worldedit:pos2", { minetest.register_entity(":worldedit:pos2", {
@ -158,11 +141,9 @@ minetest.register_entity(":worldedit:pos2", {
"worldedit_pos2.png", "worldedit_pos2.png"}, "worldedit_pos2.png", "worldedit_pos2.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55}, collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
physical = false, physical = false,
static_save = false,
}, },
on_activate = function(self, staticdata, dtime_s) on_step = function(self, dtime)
if staticdata ~= init_sentinel then if worldedit.marker2[self.player_name] == nil then
-- we were loaded from before static_save = false was added
self.object:remove() self.object:remove()
end end
end, end,
@ -170,23 +151,20 @@ minetest.register_entity(":worldedit:pos2", {
self.object:remove() self.object:remove()
worldedit.marker2[self.player_name] = nil worldedit.marker2[self.player_name] = nil
end, end,
on_blast = function(self, damage)
return false, false, {} -- don't damage or knockback
end,
}) })
minetest.register_entity(":worldedit:region_cube", { minetest.register_entity(":worldedit:region_cube", {
initial_properties = { initial_properties = {
visual = "upright_sprite", visual = "upright_sprite",
visual_size = {x=1.1, y=1.1},
textures = {"worldedit_cube.png"}, textures = {"worldedit_cube.png"},
visual_size = {x=10, y=10}, visual_size = {x=10, y=10},
physical = false, physical = false,
static_save = false,
}, },
on_activate = function(self, staticdata, dtime_s) on_step = function(self, dtime)
if staticdata ~= init_sentinel then if worldedit.marker_region[self.player_name] == nil then
-- we were loaded from before static_save = false was added
self.object:remove() self.object:remove()
return
end end
end, end,
on_punch = function(self, hitter) on_punch = function(self, hitter)
@ -199,8 +177,5 @@ minetest.register_entity(":worldedit:region_cube", {
end end
worldedit.marker_region[self.player_name] = nil worldedit.marker_region[self.player_name] = nil
end, end,
on_blast = function(self, damage)
return false, false, {} -- don't damage or knockback
end,
}) })

View File

@ -1,3 +0,0 @@
name = worldedit_commands
description = WorldEdit chat commands
depends = worldedit

View File

@ -1,48 +1,65 @@
local safe_region_callback = {} local safe_region_callback = {}
local safe_region_param = {}
local function check_region(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] --obtain positions
if pos1 == nil or pos2 == nil then
worldedit.player_notify(name, "no region selected")
return nil
end
return worldedit.volume(pos1, pos2)
end
--`count` is the number of nodes that would possibly be modified
--`callback` is a callback to run when the user confirms --`callback` is a callback to run when the user confirms
local function safe_region(name, count, callback) --`nodes_needed` is a function accepting `param`, `pos1`, and `pos2` to calculate the number of nodes needed
if count < 20000 then local function safe_region(callback, nodes_needed)
return callback() --default node volume calculation
nodes_needed = nodes_needed or check_region
return function(name, param)
--check if the operation applies to a safe number of nodes
local count = nodes_needed(name, param)
if count == nil then return end --invalid command
if count < 10000 then
return callback(name, param)
end end
--save callback to call later --save callback to call later
safe_region_callback[name] = callback safe_region_callback[name], safe_region_param[name] = callback, param
worldedit.player_notify(name, "WARNING: this operation could affect up to " .. count .. " nodes; type //y to continue or //n to cancel") worldedit.player_notify(name, "WARNING: this operation could affect up to " .. count .. " nodes; type //y to continue or //n to cancel")
end end
end
local function reset_pending(name) local function reset_pending(name)
safe_region_callback[name] = nil safe_region_callback[name], safe_region_param[name] = nil, nil
end end
minetest.register_chatcommand("/y", { minetest.register_chatcommand("/y", {
params = "", params = "",
description = "Confirm a pending operation", description = "Confirm a pending operation",
func = function(name) func = function(name)
local callback = safe_region_callback[name] local callback, param = safe_region_callback[name], safe_region_param[name]
if not callback then if not callback then
worldedit.player_notify(name, "no operation pending") worldedit.player_notify(name, "no operation pending")
return return
end end
reset_pending(name) safe_region_callback[name], safe_region_param[name] = nil, nil --reset pending operation
callback(name) callback(name, param)
end, end,
}) })
minetest.register_chatcommand("/n", { minetest.register_chatcommand("/n", {
params = "", params = "",
description = "Abort a pending operation", description = "Confirm a pending operation",
func = function(name) func = function(name)
if not safe_region_callback[name] then if not safe_region_callback[name] then
worldedit.player_notify(name, "no operation pending") worldedit.player_notify(name, "no operation pending")
return return
end end
safe_region_callback[name], safe_region_param[name] = nil, nil
reset_pending(name)
end, end,
}) })
return safe_region, reset_pending return safe_region, check_region, reset_pending

View File

@ -1,63 +1,22 @@
local function above_or_under(placer, pointed_thing)
if placer:get_player_control().sneak then
return pointed_thing.above
else
return pointed_thing.under
end
end
local punched_air_time = {}
minetest.register_tool(":worldedit:wand", { minetest.register_tool(":worldedit:wand", {
description = "WorldEdit Wand tool\nLeft-click to set 1st position, right-click to set 2nd", description = "WorldEdit Wand tool, Left-click to set 1st position, right-click to set 2nd",
inventory_image = "worldedit_wand.png", inventory_image = "worldedit_wand.png",
stack_max = 1, -- there is no need to have more than one stack_max = 1, -- there is no need to have more than one
liquids_pointable = true, -- ground with only water on can be selected as well liquids_pointable = true, -- ground with only water on can be selected as well
on_use = function(itemstack, placer, pointed_thing) on_use = function(itemstack, placer, pointed_thing)
if placer == nil or pointed_thing == nil then return end if placer ~= nil and pointed_thing ~= nil and pointed_thing.type == "node" then
local name = placer:get_player_name() local name = placer:get_player_name()
if pointed_thing.type == "node" then worldedit.pos1[name] = pointed_thing.under
-- set and mark pos1
worldedit.pos1[name] = above_or_under(placer, pointed_thing)
worldedit.mark_pos1(name)
elseif pointed_thing.type == "nothing" then
local now = minetest.get_us_time()
if now - (punched_air_time[name] or 0) < 1000 * 1000 then
-- reset markers
worldedit.registered_commands["reset"].func(name)
end
punched_air_time[name] = now
elseif pointed_thing.type == "object" then
local entity = pointed_thing.ref:get_luaentity()
if entity and entity.name == "worldedit:pos2" then
-- set pos1 = pos2
worldedit.pos1[name] = worldedit.pos2[name]
worldedit.mark_pos1(name) worldedit.mark_pos1(name)
end end
end
end,
on_place = function(itemstack, placer, pointed_thing)
if placer == nil or (pointed_thing or {}).type ~= "node" then
return itemstack
end
local name = placer:get_player_name()
-- set and mark pos2
worldedit.pos2[name] = above_or_under(placer, pointed_thing)
worldedit.mark_pos2(name)
return itemstack -- nothing consumed, nothing changed return itemstack -- nothing consumed, nothing changed
end, end,
on_secondary_use = function(itemstack, user, pointed_thing) on_place = function(itemstack, placer, pointed_thing) -- Left Click
if user == nil or (pointed_thing or {}).type ~= "object" then if placer ~= nil and pointed_thing ~= nil and pointed_thing.type == "node" then
return itemstack local name = placer:get_player_name()
end worldedit.pos2[name] = pointed_thing.under
local name = user:get_player_name()
local entity = pointed_thing.ref:get_luaentity()
if entity and entity.name == "worldedit:pos1" then
-- set pos2 = pos1
worldedit.pos2[name] = worldedit.pos1[name]
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
end end
return itemstack -- nothing consumed, nothing changed return itemstack -- nothing consumed, nothing changed

View File

@ -0,0 +1,6 @@
worldedit
worldedit_commands
unified_inventory?
inventory_plus?
sfinv?
creative?

586
worldedit_gui/functionality.lua Normal file → Executable file
View File

@ -1,6 +1,6 @@
--saved state for each player --saved state for each player
local gui_nodename1 = {} --mapping of player names to node names local gui_nodename1 = {} --mapping of player names to node names (arbitrary strings may also appear as values)
local gui_nodename2 = {} --mapping of player names to node names local gui_nodename2 = {} --mapping of player names to node names (arbitrary strings may also appear as values)
local gui_axis1 = {} --mapping of player names to axes (one of 1, 2, 3, or 4, representing the axes in the `axis_indices` table below) local gui_axis1 = {} --mapping of player names to axes (one of 1, 2, 3, or 4, representing the axes in the `axis_indices` table below)
local gui_axis2 = {} --mapping of player names to axes (one of 1, 2, 3, or 4, representing the axes in the `axis_indices` table below) local gui_axis2 = {} --mapping of player names to axes (one of 1, 2, 3, or 4, representing the axes in the `axis_indices` table below)
local gui_distance1 = {} --mapping of player names to a distance (arbitrary strings may also appear as values) local gui_distance1 = {} --mapping of player names to a distance (arbitrary strings may also appear as values)
@ -10,8 +10,9 @@ local gui_count1 = {} --mapping of player names to a quantity (arbitrary strings
local gui_count2 = {} --mapping of player names to a quantity (arbitrary strings may also appear as values) local gui_count2 = {} --mapping of player names to a quantity (arbitrary strings may also appear as values)
local gui_count3 = {} --mapping of player names to a quantity (arbitrary strings may also appear as values) local gui_count3 = {} --mapping of player names to a quantity (arbitrary strings may also appear as values)
local gui_angle = {} --mapping of player names to an angle (one of 90, 180, 270, representing the angle in degrees clockwise) local gui_angle = {} --mapping of player names to an angle (one of 90, 180, 270, representing the angle in degrees clockwise)
local gui_filename = {} --mapping of player names to file names local gui_filename = {} --mapping of player names to file names (arbitrary strings may also appear as values)
local gui_param2 = {} --mapping of player names to param2 values local gui_formspec = {} --mapping of player names to formspecs
local gui_code = {} --mapping of player names to formspecs
--set default values --set default values
setmetatable(gui_nodename1, {__index = function() return "Cobblestone" end}) setmetatable(gui_nodename1, {__index = function() return "Cobblestone" end})
@ -26,7 +27,8 @@ setmetatable(gui_count2, {__index = function() return "6" end})
setmetatable(gui_count3, {__index = function() return "4" end}) setmetatable(gui_count3, {__index = function() return "4" end})
setmetatable(gui_angle, {__index = function() return 90 end}) setmetatable(gui_angle, {__index = function() return 90 end})
setmetatable(gui_filename, {__index = function() return "building" end}) setmetatable(gui_filename, {__index = function() return "building" end})
setmetatable(gui_param2, {__index = function() return "0" end}) setmetatable(gui_formspec, {__index = function() return "size[5,5]\nlabel[0,0;Hello, world!]" end})
setmetatable(gui_code, {__index = function() return "minetest.chat_send_player(\"singleplayer\", \"Hello, world!\")" end})
local axis_indices = {["X axis"]=1, ["Y axis"]=2, ["Z axis"]=3, ["Look direction"]=4} local axis_indices = {["X axis"]=1, ["Y axis"]=2, ["Z axis"]=3, ["Look direction"]=4}
local axis_values = {"x", "y", "z", "?"} local axis_values = {"x", "y", "z", "?"}
@ -54,18 +56,13 @@ end
-- display node (or unknown_node image otherwise) at specified pos in formspec -- display node (or unknown_node image otherwise) at specified pos in formspec
local formspec_node = function(pos, nodename) local formspec_node = function(pos, nodename)
local ndef = nodename and minetest.registered_nodes[nodename] return nodename and string.format("item_image[%s;1,1;%s]", pos, nodename)
if nodename and ndef then or string.format("image[%s;1,1;worldedit_gui_unknown.png]", pos)
return string.format("item_image[%s;1,1;%s]", pos, nodename) ..
string.format("tooltip[%s;1,1;%s]", pos, minetest.formspec_escape(ndef.description))
else
return string.format("image[%s;1,1;worldedit_gui_unknown.png]", pos)
end
end end
-- two further priv helpers -- two further priv helpers
local function we_privs(command) local function we_privs(command)
return worldedit.registered_commands[command].privs return minetest.chatcommands["/" .. command].privs
end end
local function combine_we_privs(list) local function combine_we_privs(list)
@ -76,79 +73,11 @@ local function combine_we_privs(list)
return combine_privs(unpack(args)) return combine_privs(unpack(args))
end end
-- functions that handle value changing & page reshowing (without submitting)
local function copy_changes(name, fields, def)
for field, into in pairs(def) do
if into ~= true and fields[field] then
local value = tostring(fields[field])
if into == gui_axis1 or into == gui_axis2 then
into[name] = axis_indices[value]
elseif into == gui_angle then
into[name] = angle_indices[value]
else
into[name] = value
end
end
end
end
local function handle_changes(name, identifier, fields, def)
local any = false
for field, into in pairs(def) do
if fields.key_enter_field == field then
any = true
end
-- first condition: buttons (value not saved)
-- others: dropdowns which will be sent when their value changes
if into == true or into == gui_axis1 or into == gui_axis2 or into == gui_angle then
if fields[field] then
any = true
end
end
end
if not any then
return false
end
any = false
for field, into in pairs(def) do
if into ~= true and fields[field] then
local value = tostring(fields[field])
if into == gui_axis1 or into == gui_axis2 then
into[name] = axis_indices[value]
elseif into == gui_angle then
into[name] = angle_indices[value]
else
into[name] = value
end
if into == gui_nodename1 or into == gui_nodename2 then
any = true
end
end
end
-- Only nodename fields change based on the value, so only re-show the page if necessary
if any then
worldedit.show_page(name, identifier)
end
return true
end
-- This has the same behaviour as the player invoking the chat command
local function execute_worldedit_command(command_name, player_name, params)
local chatcmd = minetest.registered_chatcommands["/" .. command_name]
assert(chatcmd, "unknown command: " .. command_name)
local _, msg = chatcmd.func(player_name, params)
if msg then
worldedit.player_notify(player_name, msg)
end
end
worldedit.register_gui_function("worldedit_gui_about", { worldedit.register_gui_function("worldedit_gui_about", {
name = "About", name = "About",
privs = {interact=true}, privs = {interact=true},
on_select = function(name) on_select = function(name)
execute_worldedit_command("about", name, "") minetest.chatcommands["/about"].func(name, "")
end, end,
}) })
@ -156,8 +85,7 @@ worldedit.register_gui_function("worldedit_gui_inspect", {
name = "Toggle Inspect", name = "Toggle Inspect",
privs = we_privs("inspect"), privs = we_privs("inspect"),
on_select = function(name) on_select = function(name)
execute_worldedit_command("inspect", name, minetest.chatcommands["/inspect"].func(name, worldedit.inspect[name] and "disable" or "enable")
worldedit.inspect[name] and "disable" or "enable")
end, end,
}) })
@ -191,47 +119,47 @@ worldedit.register_gui_function("worldedit_gui_region", {
worldedit.register_gui_handler("worldedit_gui_region", function(name, fields) worldedit.register_gui_handler("worldedit_gui_region", function(name, fields)
if fields.worldedit_gui_p_get then if fields.worldedit_gui_p_get then
execute_worldedit_command("p", name, "get") minetest.chatcommands["/p"].func(name, "get")
return true return true
elseif fields.worldedit_gui_p_set1 then elseif fields.worldedit_gui_p_set1 then
execute_worldedit_command("p", name, "set1") minetest.chatcommands["/p"].func(name, "set1")
return true return true
elseif fields.worldedit_gui_p_set2 then elseif fields.worldedit_gui_p_set2 then
execute_worldedit_command("p", name, "set2") minetest.chatcommands["/p"].func(name, "set2")
return true return true
elseif fields.worldedit_gui_pos1 then elseif fields.worldedit_gui_pos1 then
execute_worldedit_command("pos1", name, "") minetest.chatcommands["/pos1"].func(name, "")
worldedit.show_page(name, "worldedit_gui_region") worldedit.show_page(name, "worldedit_gui_region")
return true return true
elseif fields.worldedit_gui_pos2 then elseif fields.worldedit_gui_pos2 then
execute_worldedit_command("pos2", name, "") minetest.chatcommands["/pos2"].func(name, "")
worldedit.show_page(name, "worldedit_gui_region") worldedit.show_page(name, "worldedit_gui_region")
return true return true
elseif fields.worldedit_gui_reset then elseif fields.worldedit_gui_reset then
execute_worldedit_command("reset", name, "") minetest.chatcommands["/reset"].func(name, "")
worldedit.show_page(name, "worldedit_gui_region") worldedit.show_page(name, "worldedit_gui_region")
return true return true
elseif fields.worldedit_gui_mark then elseif fields.worldedit_gui_mark then
execute_worldedit_command("mark", name, "") minetest.chatcommands["/mark"].func(name, "")
worldedit.show_page(name, "worldedit_gui_region") worldedit.show_page(name, "worldedit_gui_region")
return true return true
elseif fields.worldedit_gui_unmark then elseif fields.worldedit_gui_unmark then
execute_worldedit_command("unmark", name, "") minetest.chatcommands["/unmark"].func(name, "")
worldedit.show_page(name, "worldedit_gui_region") worldedit.show_page(name, "worldedit_gui_region")
return true return true
elseif fields.worldedit_gui_volume then elseif fields.worldedit_gui_volume then
execute_worldedit_command("volume", name, "") minetest.chatcommands["/volume"].func(name, "")
worldedit.show_page(name, "worldedit_gui_region") worldedit.show_page(name, "worldedit_gui_region")
return true return true
elseif fields.worldedit_gui_fixedpos_pos1_submit then elseif fields.worldedit_gui_fixedpos_pos1_submit then
execute_worldedit_command("fixedpos", name, ("set1 %s %s %s"):format( minetest.chatcommands["/fixedpos"].func(name, string.format("set1 %s %s %s",
tostring(fields.worldedit_gui_fixedpos_pos1x), tostring(fields.worldedit_gui_fixedpos_pos1x),
tostring(fields.worldedit_gui_fixedpos_pos1y), tostring(fields.worldedit_gui_fixedpos_pos1y),
tostring(fields.worldedit_gui_fixedpos_pos1z))) tostring(fields.worldedit_gui_fixedpos_pos1z)))
worldedit.show_page(name, "worldedit_gui_region") worldedit.show_page(name, "worldedit_gui_region")
return true return true
elseif fields.worldedit_gui_fixedpos_pos2_submit then elseif fields.worldedit_gui_fixedpos_pos2_submit then
execute_worldedit_command("fixedpos", name, ("set2 %s %s %s"):format( minetest.chatcommands["/fixedpos"].func(name, string.format("set2 %s %s %s",
tostring(fields.worldedit_gui_fixedpos_pos2x), tostring(fields.worldedit_gui_fixedpos_pos2x),
tostring(fields.worldedit_gui_fixedpos_pos2y), tostring(fields.worldedit_gui_fixedpos_pos2y),
tostring(fields.worldedit_gui_fixedpos_pos2z))) tostring(fields.worldedit_gui_fixedpos_pos2z)))
@ -249,7 +177,6 @@ worldedit.register_gui_function("worldedit_gui_set", {
local nodename = worldedit.normalize_nodename(node) local nodename = worldedit.normalize_nodename(node)
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_set") .. return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_set") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_set_node;Name;%s]", minetest.formspec_escape(node)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_set_node;Name;%s]", minetest.formspec_escape(node)) ..
"field_close_on_enter[worldedit_gui_set_node;false]" ..
"button[4,1.18;1.5,0.8;worldedit_gui_set_search;Search]" .. "button[4,1.18;1.5,0.8;worldedit_gui_set_search;Search]" ..
formspec_node("5.5,1.1", nodename) .. formspec_node("5.5,1.1", nodename) ..
"button_exit[0,2.5;3,0.8;worldedit_gui_set_submit;Set Nodes]" "button_exit[0,2.5;3,0.8;worldedit_gui_set_submit;Set Nodes]"
@ -257,22 +184,18 @@ worldedit.register_gui_function("worldedit_gui_set", {
}) })
worldedit.register_gui_handler("worldedit_gui_set", function(name, fields) worldedit.register_gui_handler("worldedit_gui_set", function(name, fields)
local cg = { if fields.worldedit_gui_set_search or fields.worldedit_gui_set_submit then
worldedit_gui_set_search = true, gui_nodename1[name] = tostring(fields.worldedit_gui_set_node)
worldedit_gui_set_node = gui_nodename1,
}
local ret = handle_changes(name, "worldedit_gui_set", fields, cg)
if fields.worldedit_gui_set_submit then
copy_changes(name, fields, cg)
worldedit.show_page(name, "worldedit_gui_set") worldedit.show_page(name, "worldedit_gui_set")
if fields.worldedit_gui_set_submit then
local n = worldedit.normalize_nodename(gui_nodename1[name]) local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then if n then
execute_worldedit_command("set", name, n) minetest.chatcommands["/set"].func(name, n)
end
end end
return true return true
end end
return ret return false
end) end)
worldedit.register_gui_function("worldedit_gui_replace", { worldedit.register_gui_function("worldedit_gui_replace", {
@ -283,11 +206,9 @@ worldedit.register_gui_function("worldedit_gui_replace", {
local search_nodename, replace_nodename = worldedit.normalize_nodename(search), worldedit.normalize_nodename(replace) local search_nodename, replace_nodename = worldedit.normalize_nodename(search), worldedit.normalize_nodename(replace)
return "size[6.5,4]" .. worldedit.get_formspec_header("worldedit_gui_replace") .. return "size[6.5,4]" .. worldedit.get_formspec_header("worldedit_gui_replace") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_replace_search;Name;%s]", minetest.formspec_escape(search)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_replace_search;Name;%s]", minetest.formspec_escape(search)) ..
"field_close_on_enter[worldedit_gui_replace_search;false]" ..
"button[4,1.18;1.5,0.8;worldedit_gui_replace_search_search;Search]" .. "button[4,1.18;1.5,0.8;worldedit_gui_replace_search_search;Search]" ..
formspec_node("5.5,1.1", search_nodename) .. formspec_node("5.5,1.1", search_nodename) ..
string.format("field[0.5,2.5;4,0.8;worldedit_gui_replace_replace;Name;%s]", minetest.formspec_escape(replace)) .. string.format("field[0.5,2.5;4,0.8;worldedit_gui_replace_replace;Name;%s]", minetest.formspec_escape(replace)) ..
"field_close_on_enter[worldedit_gui_replace_replace;false]" ..
"button[4,2.18;1.5,0.8;worldedit_gui_replace_replace_search;Search]" .. "button[4,2.18;1.5,0.8;worldedit_gui_replace_replace_search;Search]" ..
formspec_node("5.5,2.1", replace_nodename) .. formspec_node("5.5,2.1", replace_nodename) ..
"button_exit[0,3.5;3,0.8;worldedit_gui_replace_submit;Replace Nodes]" .. "button_exit[0,3.5;3,0.8;worldedit_gui_replace_submit;Replace Nodes]" ..
@ -296,29 +217,28 @@ worldedit.register_gui_function("worldedit_gui_replace", {
}) })
worldedit.register_gui_handler("worldedit_gui_replace", function(name, fields) worldedit.register_gui_handler("worldedit_gui_replace", function(name, fields)
local cg = { if fields.worldedit_gui_replace_search_search or fields.worldedit_gui_replace_replace_search
worldedit_gui_replace_search_search = true, or fields.worldedit_gui_replace_submit or fields.worldedit_gui_replace_submit_inverse then
worldedit_gui_replace_replace_search = true, gui_nodename1[name] = tostring(fields.worldedit_gui_replace_search)
worldedit_gui_replace_search = gui_nodename1, gui_nodename2[name] = tostring(fields.worldedit_gui_replace_replace)
worldedit_gui_replace_replace = gui_nodename2,
}
local ret = handle_changes(name, "worldedit_gui_replace", fields, cg)
if fields.worldedit_gui_replace_submit or fields.worldedit_gui_replace_submit_inverse then
copy_changes(name, fields, cg)
worldedit.show_page(name, "worldedit_gui_replace") worldedit.show_page(name, "worldedit_gui_replace")
local submit = "replace" local submit = nil
if fields.worldedit_gui_replace_submit_inverse then if fields.worldedit_gui_replace_submit then
submit = "replace"
elseif fields.worldedit_gui_replace_submit_inverse then
submit = "replaceinverse" submit = "replaceinverse"
end end
if submit then
local n1 = worldedit.normalize_nodename(gui_nodename1[name]) local n1 = worldedit.normalize_nodename(gui_nodename1[name])
local n2 = worldedit.normalize_nodename(gui_nodename2[name]) local n2 = worldedit.normalize_nodename(gui_nodename2[name])
if n1 and n2 then if n1 and n2 then
execute_worldedit_command(submit, name, n1 .. " " .. n2) minetest.chatcommands["/"..submit].func(name, string.format("%s %s", n1, n2))
end
end end
return true return true
end end
return ret return false
end) end)
worldedit.register_gui_function("worldedit_gui_sphere_dome", { worldedit.register_gui_function("worldedit_gui_sphere_dome", {
@ -329,11 +249,9 @@ worldedit.register_gui_function("worldedit_gui_sphere_dome", {
local nodename = worldedit.normalize_nodename(node) local nodename = worldedit.normalize_nodename(node)
return "size[6.5,5]" .. worldedit.get_formspec_header("worldedit_gui_sphere_dome") .. return "size[6.5,5]" .. worldedit.get_formspec_header("worldedit_gui_sphere_dome") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_sphere_dome_node;Name;%s]", minetest.formspec_escape(node)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_sphere_dome_node;Name;%s]", minetest.formspec_escape(node)) ..
"field_close_on_enter[worldedit_gui_sphere_dome_node;false]" ..
"button[4,1.18;1.5,0.8;worldedit_gui_sphere_dome_search;Search]" .. "button[4,1.18;1.5,0.8;worldedit_gui_sphere_dome_search;Search]" ..
formspec_node("5.5,1.1", nodename) .. formspec_node("5.5,1.1", nodename) ..
string.format("field[0.5,2.5;4,0.8;worldedit_gui_sphere_dome_radius;Radius;%s]", minetest.formspec_escape(radius)) .. string.format("field[0.5,2.5;4,0.8;worldedit_gui_sphere_dome_radius;Radius;%s]", minetest.formspec_escape(radius)) ..
"field_close_on_enter[worldedit_gui_sphere_dome_radius;false]" ..
"button_exit[0,3.5;3,0.8;worldedit_gui_sphere_dome_submit_hollow;Hollow Sphere]" .. "button_exit[0,3.5;3,0.8;worldedit_gui_sphere_dome_submit_hollow;Hollow Sphere]" ..
"button_exit[3.5,3.5;3,0.8;worldedit_gui_sphere_dome_submit_solid;Solid Sphere]" .. "button_exit[3.5,3.5;3,0.8;worldedit_gui_sphere_dome_submit_solid;Solid Sphere]" ..
"button_exit[0,4.5;3,0.8;worldedit_gui_sphere_dome_submit_hollow_dome;Hollow Dome]" .. "button_exit[0,4.5;3,0.8;worldedit_gui_sphere_dome_submit_hollow_dome;Hollow Dome]" ..
@ -342,87 +260,81 @@ worldedit.register_gui_function("worldedit_gui_sphere_dome", {
}) })
worldedit.register_gui_handler("worldedit_gui_sphere_dome", function(name, fields) worldedit.register_gui_handler("worldedit_gui_sphere_dome", function(name, fields)
local cg = { if fields.worldedit_gui_sphere_dome_search
worldedit_gui_sphere_dome_search = true, or fields.worldedit_gui_sphere_dome_submit_hollow or fields.worldedit_gui_sphere_dome_submit_solid
worldedit_gui_sphere_dome_node = gui_nodename1,
worldedit_gui_sphere_dome_radius = gui_distance2,
}
local ret = handle_changes(name, "worldedit_gui_sphere_dome", fields, cg)
if fields.worldedit_gui_sphere_dome_submit_hollow or fields.worldedit_gui_sphere_dome_submit_solid
or fields.worldedit_gui_sphere_dome_submit_hollow_dome or fields.worldedit_gui_sphere_dome_submit_solid_dome then or fields.worldedit_gui_sphere_dome_submit_hollow_dome or fields.worldedit_gui_sphere_dome_submit_solid_dome then
copy_changes(name, fields, cg) gui_nodename1[name] = tostring(fields.worldedit_gui_sphere_dome_node)
gui_distance2[name] = tostring(fields.worldedit_gui_sphere_dome_radius)
worldedit.show_page(name, "worldedit_gui_sphere_dome") worldedit.show_page(name, "worldedit_gui_sphere_dome")
local submit = "hollowsphere" local submit = nil
if fields.worldedit_gui_sphere_dome_submit_solid then if fields.worldedit_gui_sphere_dome_submit_hollow then
submit = "hollowsphere"
elseif fields.worldedit_gui_sphere_dome_submit_solid then
submit = "sphere" submit = "sphere"
elseif fields.worldedit_gui_sphere_dome_submit_hollow_dome then elseif fields.worldedit_gui_sphere_dome_submit_hollow_dome then
submit = "hollowdome" submit = "hollowdome"
elseif fields.worldedit_gui_sphere_dome_submit_solid_dome then elseif fields.worldedit_gui_sphere_dome_submit_solid_dome then
submit = "dome" submit = "dome"
end end
if submit then
local n = worldedit.normalize_nodename(gui_nodename1[name]) local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then if n then
execute_worldedit_command(submit, name, minetest.chatcommands["/"..submit].func(name, string.format("%s %s", gui_distance2[name], n))
gui_distance2[name] .. " " .. n) end
end end
return true return true
end end
return ret return false
end) end)
worldedit.register_gui_function("worldedit_gui_cylinder", { worldedit.register_gui_function("worldedit_gui_cylinder", {
name = "Cylinder", name = "Cylinder",
privs = combine_we_privs({"hollowcylinder", "cylinder"}), privs = combine_we_privs({"hollowcylinder", "cylinder"}),
get_formspec = function(name) get_formspec = function(name)
local node, axis, length = gui_nodename1[name], gui_axis1[name], gui_distance1[name] local node, axis, length, radius = gui_nodename1[name], gui_axis1[name], gui_distance1[name], gui_distance2[name]
local radius1, radius2 = gui_distance2[name], gui_distance3[name]
local nodename = worldedit.normalize_nodename(node) local nodename = worldedit.normalize_nodename(node)
return "size[6.5,6]" .. worldedit.get_formspec_header("worldedit_gui_cylinder") .. return "size[6.5,5]" .. worldedit.get_formspec_header("worldedit_gui_cylinder") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_cylinder_node;Name;%s]", minetest.formspec_escape(node)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_cylinder_node;Name;%s]", minetest.formspec_escape(node)) ..
"field_close_on_enter[worldedit_gui_cylinder_node;false]" ..
"button[4,1.18;1.5,0.8;worldedit_gui_cylinder_search;Search]" .. "button[4,1.18;1.5,0.8;worldedit_gui_cylinder_search;Search]" ..
formspec_node("5.5,1.1", nodename) .. formspec_node("5.5,1.1", nodename) ..
string.format("field[0.5,2.5;4,0.8;worldedit_gui_cylinder_length;Length;%s]", minetest.formspec_escape(length)) .. string.format("field[0.5,2.5;4,0.8;worldedit_gui_cylinder_length;Length;%s]", minetest.formspec_escape(length)) ..
string.format("dropdown[4,2.18;2.5;worldedit_gui_cylinder_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) .. string.format("dropdown[4,2.18;2.5;worldedit_gui_cylinder_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) ..
string.format("field[0.5,3.5;2,0.8;worldedit_gui_cylinder_radius1;Base Radius;%s]", minetest.formspec_escape(radius1)) .. string.format("field[0.5,3.5;4,0.8;worldedit_gui_cylinder_radius;Radius;%s]", minetest.formspec_escape(radius)) ..
string.format("field[2.5,3.5;2,0.8;worldedit_gui_cylinder_radius2;Top Radius;%s]", minetest.formspec_escape(radius2)) .. "button_exit[0,4.5;3,0.8;worldedit_gui_cylinder_submit_hollow;Hollow Cylinder]" ..
"field_close_on_enter[worldedit_gui_cylinder_length;false]" .. "button_exit[3.5,4.5;3,0.8;worldedit_gui_cylinder_submit_solid;Solid Cylinder]"
"field_close_on_enter[worldedit_gui_cylinder_radius1;false]" ..
"field_close_on_enter[worldedit_gui_cylinder_radius2;false]" ..
"label[0.25,4;Equal base and top radius creates a cylinder,\n"..
"zero top radius creates a cone.\nConsult documentation for more information.]"..
"button_exit[0,5.5;3,0.8;worldedit_gui_cylinder_submit_hollow;Hollow Cylinder]" ..
"button_exit[3.5,5.5;3,0.8;worldedit_gui_cylinder_submit_solid;Solid Cylinder]"
end, end,
}) })
worldedit.register_gui_handler("worldedit_gui_cylinder", function(name, fields) worldedit.register_gui_handler("worldedit_gui_cylinder", function(name, fields)
local cg = { if fields.worldedit_gui_cylinder_search
worldedit_gui_cylinder_search = true, or fields.worldedit_gui_cylinder_submit_hollow or fields.worldedit_gui_cylinder_submit_solid then
worldedit_gui_cylinder_node = gui_nodename1, gui_nodename1[name] = tostring(fields.worldedit_gui_cylinder_node)
worldedit_gui_cylinder_axis = gui_axis1, gui_axis1[name] = axis_indices[fields.worldedit_gui_cylinder_axis]
worldedit_gui_cylinder_length = gui_distance1, gui_distance1[name] = tostring(fields.worldedit_gui_cylinder_length)
worldedit_gui_cylinder_radius1 = gui_distance2, gui_distance2[name] = tostring(fields.worldedit_gui_cylinder_radius)
worldedit_gui_cylinder_radius2 = gui_distance3,
}
local ret = handle_changes(name, "worldedit_gui_cylinder", fields, cg)
if fields.worldedit_gui_cylinder_submit_hollow or fields.worldedit_gui_cylinder_submit_solid then
copy_changes(name, fields, cg)
worldedit.show_page(name, "worldedit_gui_cylinder") worldedit.show_page(name, "worldedit_gui_cylinder")
local submit = "hollowcylinder" local submit = nil
if fields.worldedit_gui_cylinder_submit_solid then if fields.worldedit_gui_cylinder_submit_hollow then
submit = "hollowcylinder"
elseif fields.worldedit_gui_cylinder_submit_solid then
submit = "cylinder" submit = "cylinder"
end end
if submit then
local n = worldedit.normalize_nodename(gui_nodename1[name]) local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then if n then
local args = string.format("%s %s %s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_distance2[name], gui_distance3[name], n) minetest.chatcommands["/"..submit].func(name, string.format("%s %s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_distance2[name], n))
execute_worldedit_command(submit, name, args) end
end end
return true return true
end end
return ret if fields.worldedit_gui_cylinder_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_cylinder_axis]
worldedit.show_page(name, "worldedit_gui_cylinder")
return true
end
return false
end) end)
worldedit.register_gui_function("worldedit_gui_pyramid", { worldedit.register_gui_function("worldedit_gui_pyramid", {
@ -433,42 +345,42 @@ worldedit.register_gui_function("worldedit_gui_pyramid", {
local nodename = worldedit.normalize_nodename(node) local nodename = worldedit.normalize_nodename(node)
return "size[6.5,4]" .. worldedit.get_formspec_header("worldedit_gui_pyramid") .. return "size[6.5,4]" .. worldedit.get_formspec_header("worldedit_gui_pyramid") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_pyramid_node;Name;%s]", minetest.formspec_escape(node)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_pyramid_node;Name;%s]", minetest.formspec_escape(node)) ..
"field_close_on_enter[worldedit_gui_pyramid_node;false]" ..
"button[4,1.18;1.5,0.8;worldedit_gui_pyramid_search;Search]" .. "button[4,1.18;1.5,0.8;worldedit_gui_pyramid_search;Search]" ..
formspec_node("5.5,1.1", nodename) .. formspec_node("5.5,1.1", nodename) ..
string.format("field[0.5,2.5;4,0.8;worldedit_gui_pyramid_length;Length;%s]", minetest.formspec_escape(length)) .. string.format("field[0.5,2.5;4,0.8;worldedit_gui_pyramid_length;Length;%s]", minetest.formspec_escape(length)) ..
string.format("dropdown[4,2.18;2.5;worldedit_gui_pyramid_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) .. string.format("dropdown[4,2.18;2.5;worldedit_gui_pyramid_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) ..
"field_close_on_enter[worldedit_gui_pyramid_length;false]" ..
"button_exit[0,3.5;3,0.8;worldedit_gui_pyramid_submit_hollow;Hollow Pyramid]" .. "button_exit[0,3.5;3,0.8;worldedit_gui_pyramid_submit_hollow;Hollow Pyramid]" ..
"button_exit[3.5,3.5;3,0.8;worldedit_gui_pyramid_submit_solid;Solid Pyramid]" "button_exit[3.5,3.5;3,0.8;worldedit_gui_pyramid_submit_solid;Solid Pyramid]"
end, end,
}) })
worldedit.register_gui_handler("worldedit_gui_pyramid", function(name, fields) worldedit.register_gui_handler("worldedit_gui_pyramid", function(name, fields)
local cg = { if fields.worldedit_gui_pyramid_search or fields.worldedit_gui_pyramid_submit_solid or fields.worldedit_gui_pyramid_submit_hollow or fields.worldedit_gui_pyramid_axis then
worldedit_gui_pyramid_search = true, gui_nodename1[name] = tostring(fields.worldedit_gui_pyramid_node)
worldedit_gui_pyramid_node = gui_nodename1, gui_axis1[name] = axis_indices[fields.worldedit_gui_pyramid_axis]
worldedit_gui_pyramid_axis = gui_axis1, gui_distance1[name] = tostring(fields.worldedit_gui_pyramid_length)
worldedit_gui_pyramid_length = gui_distance1,
}
local ret = handle_changes(name, "worldedit_gui_pyramid", fields, cg)
if fields.worldedit_gui_pyramid_submit_solid or fields.worldedit_gui_pyramid_submit_hollow then
copy_changes(name, fields, cg)
worldedit.show_page(name, "worldedit_gui_pyramid") worldedit.show_page(name, "worldedit_gui_pyramid")
local submit = "pyramid" local submit = nil
if fields.worldedit_gui_pyramid_submit_hollow then if fields.worldedit_gui_pyramid_submit_solid then
submit = "pyramid"
elseif fields.worldedit_gui_pyramid_submit_hollow then
submit = "hollowpyramid" submit = "hollowpyramid"
end end
if submit then
local n = worldedit.normalize_nodename(gui_nodename1[name]) local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then if n then
execute_worldedit_command(submit, name, minetest.chatcommands["/"..submit].func(name, string.format("%s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], n))
string.format("%s %s %s", axis_values[gui_axis1[name]], end
gui_distance1[name], n))
end end
return true return true
end end
return ret if fields.worldedit_gui_pyramid_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_pyramid_axis]
worldedit.show_page(name, "worldedit_gui_pyramid")
return true
end
return false
end) end)
worldedit.register_gui_function("worldedit_gui_spiral", { worldedit.register_gui_function("worldedit_gui_spiral", {
@ -479,41 +391,31 @@ worldedit.register_gui_function("worldedit_gui_spiral", {
local nodename = worldedit.normalize_nodename(node) local nodename = worldedit.normalize_nodename(node)
return "size[6.5,6]" .. worldedit.get_formspec_header("worldedit_gui_spiral") .. return "size[6.5,6]" .. worldedit.get_formspec_header("worldedit_gui_spiral") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_spiral_node;Name;%s]", minetest.formspec_escape(node)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_spiral_node;Name;%s]", minetest.formspec_escape(node)) ..
"field_close_on_enter[worldedit_gui_spiral_node;false]" ..
"button[4,1.18;1.5,0.8;worldedit_gui_spiral_search;Search]" .. "button[4,1.18;1.5,0.8;worldedit_gui_spiral_search;Search]" ..
formspec_node("5.5,1.1", nodename) .. formspec_node("5.5,1.1", nodename) ..
string.format("field[0.5,2.5;4,0.8;worldedit_gui_spiral_length;Side Length;%s]", minetest.formspec_escape(length)) .. string.format("field[0.5,2.5;4,0.8;worldedit_gui_spiral_length;Side Length;%s]", minetest.formspec_escape(length)) ..
string.format("field[0.5,3.5;4,0.8;worldedit_gui_spiral_height;Height;%s]", minetest.formspec_escape(height)) .. string.format("field[0.5,3.5;4,0.8;worldedit_gui_spiral_height;Height;%s]", minetest.formspec_escape(height)) ..
string.format("field[0.5,4.5;4,0.8;worldedit_gui_spiral_space;Wall Spacing;%s]", minetest.formspec_escape(space)) .. string.format("field[0.5,4.5;4,0.8;worldedit_gui_spiral_space;Wall Spacing;%s]", minetest.formspec_escape(space)) ..
"field_close_on_enter[worldedit_gui_spiral_length;false]" ..
"field_close_on_enter[worldedit_gui_spiral_height;false]" ..
"field_close_on_enter[worldedit_gui_spiral_space;false]" ..
"button_exit[0,5.5;3,0.8;worldedit_gui_spiral_submit;Spiral]" "button_exit[0,5.5;3,0.8;worldedit_gui_spiral_submit;Spiral]"
end, end,
}) })
worldedit.register_gui_handler("worldedit_gui_spiral", function(name, fields) worldedit.register_gui_handler("worldedit_gui_spiral", function(name, fields)
local cg = { if fields.worldedit_gui_spiral_search or fields.worldedit_gui_spiral_submit then
worldedit_gui_spiral_search = true, gui_nodename1[name] = fields.worldedit_gui_spiral_node
worldedit_gui_spiral_node = gui_nodename1, gui_distance1[name] = tostring(fields.worldedit_gui_spiral_length)
worldedit_gui_spiral_length = gui_distance1, gui_distance2[name] = tostring(fields.worldedit_gui_spiral_height)
worldedit_gui_spiral_height = gui_distance2, gui_distance3[name] = tostring(fields.worldedit_gui_spiral_space)
worldedit_gui_spiral_space = gui_distance3,
}
local ret = handle_changes(name, "worldedit_gui_spiral", fields, cg)
if fields.worldedit_gui_spiral_submit then
copy_changes(name, fields, cg)
worldedit.show_page(name, "worldedit_gui_spiral") worldedit.show_page(name, "worldedit_gui_spiral")
if fields.worldedit_gui_spiral_submit then
local n = worldedit.normalize_nodename(gui_nodename1[name]) local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then if n then
execute_worldedit_command("spiral", name, minetest.chatcommands["/spiral"].func(name, string.format("%s %s %s %s", gui_distance1[name], gui_distance2[name], gui_distance3[name], n))
string.format("%s %s %s %s", gui_distance1[name], end
gui_distance2[name], gui_distance3[name], n))
end end
return true return true
end end
return ret return false
end) end)
worldedit.register_gui_function("worldedit_gui_copy_move", { worldedit.register_gui_function("worldedit_gui_copy_move", {
@ -525,31 +427,29 @@ worldedit.register_gui_function("worldedit_gui_copy_move", {
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_copy_move") .. return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_copy_move") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_copy_move_amount;Amount;%s]", minetest.formspec_escape(amount)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_copy_move_amount;Amount;%s]", minetest.formspec_escape(amount)) ..
string.format("dropdown[4,1.18;2.5;worldedit_gui_copy_move_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) .. string.format("dropdown[4,1.18;2.5;worldedit_gui_copy_move_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) ..
"field_close_on_enter[worldedit_gui_copy_move_amount;false]" ..
"button_exit[0,2.5;3,0.8;worldedit_gui_copy_move_copy;Copy Region]" .. "button_exit[0,2.5;3,0.8;worldedit_gui_copy_move_copy;Copy Region]" ..
"button_exit[3.5,2.5;3,0.8;worldedit_gui_copy_move_move;Move Region]" "button_exit[3.5,2.5;3,0.8;worldedit_gui_copy_move_move;Move Region]"
end, end,
}) })
worldedit.register_gui_handler("worldedit_gui_copy_move", function(name, fields) worldedit.register_gui_handler("worldedit_gui_copy_move", function(name, fields)
local cg = {
worldedit_gui_copy_move_amount = gui_distance1,
worldedit_gui_copy_move_axis = gui_axis1,
}
local ret = handle_changes(name, "worldedit_gui_spiral", fields, cg)
if fields.worldedit_gui_copy_move_copy or fields.worldedit_gui_copy_move_move then if fields.worldedit_gui_copy_move_copy or fields.worldedit_gui_copy_move_move then
copy_changes(name, fields, cg) gui_axis1[name] = axis_indices[fields.worldedit_gui_copy_move_axis] or 4
gui_distance1[name] = tostring(fields.worldedit_gui_copy_move_amount)
worldedit.show_page(name, "worldedit_gui_copy_move") worldedit.show_page(name, "worldedit_gui_copy_move")
if fields.worldedit_gui_copy_move_copy then
local submit = "copy" minetest.chatcommands["/copy"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], gui_distance1[name]))
if fields.worldedit_gui_copy_move_move then else --fields.worldedit_gui_copy_move_move
submit = "move" minetest.chatcommands["/move"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], gui_distance1[name]))
end end
execute_worldedit_command(submit, name,
axis_values[gui_axis1[name]] .. " " .. gui_distance1[name])
return true return true
end end
return ret if fields.worldedit_gui_copy_move_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_copy_move_axis] or 4
worldedit.show_page(name, "worldedit_gui_copy_move")
return true
end
return false
end) end)
worldedit.register_gui_function("worldedit_gui_stack", { worldedit.register_gui_function("worldedit_gui_stack", {
@ -560,26 +460,24 @@ worldedit.register_gui_function("worldedit_gui_stack", {
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_stack") .. return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_stack") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_stack_count;Count;%s]", minetest.formspec_escape(count)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_stack_count;Count;%s]", minetest.formspec_escape(count)) ..
string.format("dropdown[4,1.18;2.5;worldedit_gui_stack_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) .. string.format("dropdown[4,1.18;2.5;worldedit_gui_stack_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) ..
"field_close_on_enter[worldedit_gui_stack_count;false]" ..
"button_exit[0,2.5;3,0.8;worldedit_gui_stack_submit;Stack]" "button_exit[0,2.5;3,0.8;worldedit_gui_stack_submit;Stack]"
end, end,
}) })
worldedit.register_gui_handler("worldedit_gui_stack", function(name, fields) worldedit.register_gui_handler("worldedit_gui_stack", function(name, fields)
local cg = {
worldedit_gui_stack_axis = gui_axis1,
worldedit_gui_stack_count = gui_count1,
}
local ret = handle_changes(name, "worldedit_gui_stack", fields, cg)
if fields.worldedit_gui_stack_submit then if fields.worldedit_gui_stack_submit then
copy_changes(name, fields, cg) gui_axis1[name] = axis_indices[fields.worldedit_gui_stack_axis]
gui_count1[name] = tostring(fields.worldedit_gui_stack_count)
worldedit.show_page(name, "worldedit_gui_stack") worldedit.show_page(name, "worldedit_gui_stack")
minetest.chatcommands["/stack"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], gui_count1[name]))
execute_worldedit_command("stack", name,
axis_values[gui_axis1[name]] .. " " .. gui_count1[name])
return true return true
end end
return ret if fields.worldedit_gui_stack_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_stack_axis]
worldedit.show_page(name, "worldedit_gui_stack")
return true
end
return false
end) end)
worldedit.register_gui_function("worldedit_gui_stretch", { worldedit.register_gui_function("worldedit_gui_stretch", {
@ -591,29 +489,20 @@ worldedit.register_gui_function("worldedit_gui_stretch", {
string.format("field[0.5,1.5;4,0.8;worldedit_gui_stretch_x;Stretch X;%s]", minetest.formspec_escape(stretchx)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_stretch_x;Stretch X;%s]", minetest.formspec_escape(stretchx)) ..
string.format("field[0.5,2.5;4,0.8;worldedit_gui_stretch_y;Stretch Y;%s]", minetest.formspec_escape(stretchy)) .. string.format("field[0.5,2.5;4,0.8;worldedit_gui_stretch_y;Stretch Y;%s]", minetest.formspec_escape(stretchy)) ..
string.format("field[0.5,3.5;4,0.8;worldedit_gui_stretch_z;Stretch Z;%s]", minetest.formspec_escape(stretchz)) .. string.format("field[0.5,3.5;4,0.8;worldedit_gui_stretch_z;Stretch Z;%s]", minetest.formspec_escape(stretchz)) ..
"field_close_on_enter[worldedit_gui_stretch_x;false]" ..
"field_close_on_enter[worldedit_gui_stretch_y;false]" ..
"field_close_on_enter[worldedit_gui_stretch_z;false]" ..
"button_exit[0,4.5;3,0.8;worldedit_gui_stretch_submit;Stretch]" "button_exit[0,4.5;3,0.8;worldedit_gui_stretch_submit;Stretch]"
end, end,
}) })
worldedit.register_gui_handler("worldedit_gui_stretch", function(name, fields) worldedit.register_gui_handler("worldedit_gui_stretch", function(name, fields)
local cg = {
worldedit_gui_stretch_x = gui_count1,
worldedit_gui_stretch_y = gui_count2,
worldedit_gui_stretch_z = gui_count3,
}
local ret = handle_changes(name, "worldedit_gui_stretch", fields, cg)
if fields.worldedit_gui_stretch_submit then if fields.worldedit_gui_stretch_submit then
copy_changes(name, fields, cg) gui_count1[name] = tostring(fields.worldedit_gui_stretch_x)
gui_count2[name] = tostring(fields.worldedit_gui_stretch_y)
gui_count3[name] = tostring(fields.worldedit_gui_stretch_z)
worldedit.show_page(name, "worldedit_gui_stretch") worldedit.show_page(name, "worldedit_gui_stretch")
minetest.chatcommands["/stretch"].func(name, string.format("%s %s %s", gui_count1[name], gui_count2[name], gui_count3[name]))
execute_worldedit_command("stretch", name, string.format("%s %s %s",
gui_count1[name], gui_count2[name], gui_count3[name]))
return true return true
end end
return ret return false
end) end)
worldedit.register_gui_function("worldedit_gui_transpose", { worldedit.register_gui_function("worldedit_gui_transpose", {
@ -629,19 +518,23 @@ worldedit.register_gui_function("worldedit_gui_transpose", {
}) })
worldedit.register_gui_handler("worldedit_gui_transpose", function(name, fields) worldedit.register_gui_handler("worldedit_gui_transpose", function(name, fields)
local cg = {
worldedit_gui_transpose_axis1 = gui_axis1,
worldedit_gui_transpose_axis2 = gui_axis2,
}
local ret = handle_changes(name, "worldedit_gui_transpose", fields, cg)
if fields.worldedit_gui_transpose_submit then if fields.worldedit_gui_transpose_submit then
copy_changes(name, fields, cg) gui_axis1[name] = axis_indices[fields.worldedit_gui_transpose_axis1]
worldedit.show_page(name, "worldedit_gui_transpose")
execute_worldedit_command("transpose", name, minetest.chatcommands["/transpose"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], axis_values[gui_axis2[name]]))
axis_values[gui_axis1[name]] .. " " .. axis_values[gui_axis2[name]])
return true return true
end end
return ret if fields.worldedit_gui_transpose_axis1 then
gui_axis1[name] = axis_indices[fields.worldedit_gui_transpose_axis1]
worldedit.show_page(name, "worldedit_gui_transpose")
return true
end
if fields.worldedit_gui_transpose_axis2 then
gui_axis2[name] = axis_indices[fields.worldedit_gui_transpose_axis2]
worldedit.show_page(name, "worldedit_gui_transpose")
return true
end
return false
end) end)
worldedit.register_gui_function("worldedit_gui_flip", { worldedit.register_gui_function("worldedit_gui_flip", {
@ -656,18 +549,18 @@ worldedit.register_gui_function("worldedit_gui_flip", {
}) })
worldedit.register_gui_handler("worldedit_gui_flip", function(name, fields) worldedit.register_gui_handler("worldedit_gui_flip", function(name, fields)
local cg = {
worldedit_gui_flip_axis = gui_axis1
}
local ret = handle_changes(name, "worldedit_gui_flip", fields, cg)
if fields.worldedit_gui_flip_submit then if fields.worldedit_gui_flip_submit then
copy_changes(name, fields, cg) gui_axis1[name] = axis_indices[fields.worldedit_gui_flip_axis]
worldedit.show_page(name, "worldedit_gui_flip") worldedit.show_page(name, "worldedit_gui_flip")
minetest.chatcommands["/flip"].func(name, axis_values[gui_axis1[name]])
execute_worldedit_command("flip", name, axis_values[gui_axis1[name]])
return true return true
end end
return ret if fields.worldedit_gui_flip_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_flip_axis]
worldedit.show_page(name, "worldedit_gui_flip")
return true
end
return false
end) end)
worldedit.register_gui_function("worldedit_gui_rotate", { worldedit.register_gui_function("worldedit_gui_rotate", {
@ -683,20 +576,24 @@ worldedit.register_gui_function("worldedit_gui_rotate", {
}) })
worldedit.register_gui_handler("worldedit_gui_rotate", function(name, fields) worldedit.register_gui_handler("worldedit_gui_rotate", function(name, fields)
local cg = {
worldedit_gui_rotate_axis = gui_axis1,
worldedit_gui_rotate_angle = gui_angle,
}
local ret = handle_changes(name, "worldedit_gui_rotate", fields, cg)
if fields.worldedit_gui_rotate_submit then if fields.worldedit_gui_rotate_submit then
copy_changes(name, fields, cg) gui_axis1[name] = axis_indices[fields.worldedit_gui_rotate_axis]
gui_angle[name] = angle_indices[fields.worldedit_gui_rotate_angle]
worldedit.show_page(name, "worldedit_gui_rotate") worldedit.show_page(name, "worldedit_gui_rotate")
minetest.chatcommands["/rotate"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], angle_values[gui_angle[name]]))
execute_worldedit_command("rotate", name,
axis_values[gui_axis1[name]] .. " " .. angle_values[gui_angle[name]])
return true return true
end end
return ret if fields.worldedit_gui_rotate_axis then
gui_axis1[name] = axis_indices[fields.worldedit_gui_rotate_axis]
worldedit.show_page(name, "worldedit_gui_rotate")
return true
end
if fields.worldedit_gui_rotate_angle then
gui_angle[name] = angle_indices[fields.worldedit_gui_rotate_angle]
worldedit.show_page(name, "worldedit_gui_rotate")
return true
end
return false
end) end)
worldedit.register_gui_function("worldedit_gui_orient", { worldedit.register_gui_function("worldedit_gui_orient", {
@ -711,26 +608,25 @@ worldedit.register_gui_function("worldedit_gui_orient", {
}) })
worldedit.register_gui_handler("worldedit_gui_orient", function(name, fields) worldedit.register_gui_handler("worldedit_gui_orient", function(name, fields)
local cg = {
worldedit_gui_orient_angle = gui_angle,
}
local ret = handle_changes(name, "worldedit_gui_orient", fields, cg)
if fields.worldedit_gui_orient_submit then if fields.worldedit_gui_orient_submit then
copy_changes(name, fields, cg) gui_angle[name] = angle_indices[fields.worldedit_gui_orient_angle]
worldedit.show_page(name, "worldedit_gui_orient") worldedit.show_page(name, "worldedit_gui_orient")
minetest.chatcommands["/orient"].func(name, tostring(angle_values[gui_angle[name]]))
execute_worldedit_command("orient", name,
tostring(angle_values[gui_angle[name]]))
return true return true
end end
return ret if fields.worldedit_gui_orient_angle then
gui_angle[name] = angle_indices[fields.worldedit_gui_orient_angle]
worldedit.show_page(name, "worldedit_gui_orient")
return true
end
return false
end) end)
worldedit.register_gui_function("worldedit_gui_fixlight", { worldedit.register_gui_function("worldedit_gui_fixlight", {
name = "Fix Lighting", name = "Fix Lighting",
privs = we_privs("fixlight"), privs = we_privs("fixlight"),
on_select = function(name) on_select = function(name)
execute_worldedit_command("fixlight", name, "") minetest.chatcommands["/fixlight"].func(name, "")
end, end,
}) })
@ -738,7 +634,7 @@ worldedit.register_gui_function("worldedit_gui_hide", {
name = "Hide Region", name = "Hide Region",
privs = we_privs("hide"), privs = we_privs("hide"),
on_select = function(name) on_select = function(name)
execute_worldedit_command("hide", name, "") minetest.chatcommands["/hide"].func(name, "")
end, end,
}) })
@ -750,7 +646,6 @@ worldedit.register_gui_function("worldedit_gui_suppress", {
local nodename = worldedit.normalize_nodename(node) local nodename = worldedit.normalize_nodename(node)
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_suppress") .. return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_suppress") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_suppress_node;Name;%s]", minetest.formspec_escape(node)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_suppress_node;Name;%s]", minetest.formspec_escape(node)) ..
"field_close_on_enter[worldedit_gui_suppress_node;false]" ..
"button[4,1.18;1.5,0.8;worldedit_gui_suppress_search;Search]" .. "button[4,1.18;1.5,0.8;worldedit_gui_suppress_search;Search]" ..
formspec_node("5.5,1.1", nodename) .. formspec_node("5.5,1.1", nodename) ..
"button_exit[0,2.5;3,0.8;worldedit_gui_suppress_submit;Suppress Nodes]" "button_exit[0,2.5;3,0.8;worldedit_gui_suppress_submit;Suppress Nodes]"
@ -758,22 +653,18 @@ worldedit.register_gui_function("worldedit_gui_suppress", {
}) })
worldedit.register_gui_handler("worldedit_gui_suppress", function(name, fields) worldedit.register_gui_handler("worldedit_gui_suppress", function(name, fields)
local cg = { if fields.worldedit_gui_suppress_search or fields.worldedit_gui_suppress_submit then
worldedit_gui_suppress_search = true, gui_nodename1[name] = tostring(fields.worldedit_gui_suppress_node)
worldedit_gui_suppress_node = gui_nodename1,
}
local ret = handle_changes(name, "worldedit_gui_suppress", fields, cg)
if fields.worldedit_gui_suppress_submit then
copy_changes(name, fields, cg)
worldedit.show_page(name, "worldedit_gui_suppress") worldedit.show_page(name, "worldedit_gui_suppress")
if fields.worldedit_gui_suppress_submit then
local n = worldedit.normalize_nodename(gui_nodename1[name]) local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then if n then
execute_worldedit_command("suppress", name, n) minetest.chatcommands["/suppress"].func(name, n)
end
end end
return true return true
end end
return ret return false
end) end)
worldedit.register_gui_function("worldedit_gui_highlight", { worldedit.register_gui_function("worldedit_gui_highlight", {
@ -784,7 +675,6 @@ worldedit.register_gui_function("worldedit_gui_highlight", {
local nodename = worldedit.normalize_nodename(node) local nodename = worldedit.normalize_nodename(node)
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_highlight") .. return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_highlight") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_highlight_node;Name;%s]", minetest.formspec_escape(node)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_highlight_node;Name;%s]", minetest.formspec_escape(node)) ..
"field_close_on_enter[worldedit_gui_highlight_node;false]" ..
"button[4,1.18;1.5,0.8;worldedit_gui_highlight_search;Search]" .. "button[4,1.18;1.5,0.8;worldedit_gui_highlight_search;Search]" ..
formspec_node("5.5,1.1", nodename) .. formspec_node("5.5,1.1", nodename) ..
"button_exit[0,2.5;3,0.8;worldedit_gui_highlight_submit;Highlight Nodes]" "button_exit[0,2.5;3,0.8;worldedit_gui_highlight_submit;Highlight Nodes]"
@ -792,29 +682,25 @@ worldedit.register_gui_function("worldedit_gui_highlight", {
}) })
worldedit.register_gui_handler("worldedit_gui_highlight", function(name, fields) worldedit.register_gui_handler("worldedit_gui_highlight", function(name, fields)
local cg = { if fields.worldedit_gui_highlight_search or fields.worldedit_gui_highlight_submit then
worldedit_gui_highlight_search = true, gui_nodename1[name] = tostring(fields.worldedit_gui_highlight_node)
worldedit_gui_highlight_node = gui_nodename1,
}
local ret = handle_changes(name, "worldedit_gui_highlight", fields, cg)
if fields.worldedit_gui_highlight_submit then
copy_changes(name, fields, cg)
worldedit.show_page(name, "worldedit_gui_highlight") worldedit.show_page(name, "worldedit_gui_highlight")
if fields.worldedit_gui_highlight_submit then
local n = worldedit.normalize_nodename(gui_nodename1[name]) local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then if n then
execute_worldedit_command("highlight", name, n) minetest.chatcommands["/highlight"].func(name, n)
end
end end
return true return true
end end
return ret return false
end) end)
worldedit.register_gui_function("worldedit_gui_restore", { worldedit.register_gui_function("worldedit_gui_restore", {
name = "Restore Region", name = "Restore Region",
privs = we_privs("restore"), privs = we_privs("restore"),
on_select = function(name) on_select = function(name)
execute_worldedit_command("restore", name, "") minetest.chatcommands["/restore"].func(name, "")
end, end,
}) })
@ -825,7 +711,6 @@ worldedit.register_gui_function("worldedit_gui_save_load", {
local filename = gui_filename[name] local filename = gui_filename[name]
return "size[6,4]" .. worldedit.get_formspec_header("worldedit_gui_save_load") .. return "size[6,4]" .. worldedit.get_formspec_header("worldedit_gui_save_load") ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_save_filename;Filename;%s]", minetest.formspec_escape(filename)) .. string.format("field[0.5,1.5;4,0.8;worldedit_gui_save_filename;Filename;%s]", minetest.formspec_escape(filename)) ..
"field_close_on_enter[worldedit_gui_save_filename;false]" ..
"button_exit[0,2.5;3,0.8;worldedit_gui_save_load_submit_save;Save]" .. "button_exit[0,2.5;3,0.8;worldedit_gui_save_load_submit_save;Save]" ..
"button_exit[3,2.5;3,0.8;worldedit_gui_save_load_submit_allocate;Allocate]" .. "button_exit[3,2.5;3,0.8;worldedit_gui_save_load_submit_allocate;Allocate]" ..
"button_exit[0,3.5;3,0.8;worldedit_gui_save_load_submit_load;Load]" "button_exit[0,3.5;3,0.8;worldedit_gui_save_load_submit_load;Load]"
@ -836,101 +721,48 @@ worldedit.register_gui_handler("worldedit_gui_save_load", function(name, fields)
if fields.worldedit_gui_save_load_submit_save or fields.worldedit_gui_save_load_submit_allocate or fields.worldedit_gui_save_load_submit_load then if fields.worldedit_gui_save_load_submit_save or fields.worldedit_gui_save_load_submit_allocate or fields.worldedit_gui_save_load_submit_load then
gui_filename[name] = tostring(fields.worldedit_gui_save_filename) gui_filename[name] = tostring(fields.worldedit_gui_save_filename)
worldedit.show_page(name, "worldedit_gui_save_load") worldedit.show_page(name, "worldedit_gui_save_load")
if fields.worldedit_gui_save_load_submit_save then if fields.worldedit_gui_save_load_submit_save then
execute_worldedit_command("save", name, gui_filename[name]) minetest.chatcommands["/save"].func(name, gui_filename[name])
elseif fields.worldedit_gui_save_load_submit_allocate then elseif fields.worldedit_gui_save_load_submit_allocate then
execute_worldedit_command("allocate", name, gui_filename[name]) minetest.chatcommands["/allocate"].func(name, gui_filename[name])
else --fields.worldedit_gui_save_load_submit_load else --fields.worldedit_gui_save_load_submit_load
execute_worldedit_command("load", name, gui_filename[name]) minetest.chatcommands["/load"].func(name, gui_filename[name])
end end
return true return true
end end
return false return false
end) end)
worldedit.register_gui_function("worldedit_gui_cube", { worldedit.register_gui_function("worldedit_gui_lua", {
name = "Cube", name = "Run Lua", privs = minetest.chatcommands["/clearobjects"].privs,
privs = combine_we_privs({"hollowcube", "cube"}), privs = we_privs("lua"),
get_formspec = function(name) get_formspec = function(name)
local width, height, length = gui_distance1[name], gui_distance2[name], gui_distance3[name] local code = gui_code[name]
local node = gui_nodename1[name] return "size[8,6.5]" .. worldedit.get_formspec_header("worldedit_gui_lua") ..
local nodename = worldedit.normalize_nodename(node) string.format("textarea[0.5,1;7.5,5.5;worldedit_gui_lua_code;Lua Code;%s]", minetest.formspec_escape(code)) ..
return "size[6.5,4]" .. worldedit.get_formspec_header("worldedit_gui_cube") .. "button_exit[0,6;3,0.8;worldedit_gui_lua_run;Run Lua]" ..
string.format("field[0.5,1.5;4,0.8;worldedit_gui_cube_node;Name;%s]", minetest.formspec_escape(node)) .. "button_exit[5,6;3,0.8;worldedit_gui_lua_transform;Lua Transform]"
"field_close_on_enter[worldedit_gui_cube_node;false]" ..
"button[4,1.18;1.5,0.8;worldedit_gui_cube_search;Search]" ..
formspec_node("5.5,1.1", nodename) ..
string.format("field[0.5,2.5;1,0.8;worldedit_gui_cube_width;Width;%s]", minetest.formspec_escape(width)) ..
string.format("field[1.5,2.5;1,0.8;worldedit_gui_cube_height;Height;%s]", minetest.formspec_escape(height)) ..
string.format("field[2.5,2.5;1,0.8;worldedit_gui_cube_length;Length;%s]", minetest.formspec_escape(length)) ..
"field_close_on_enter[worldedit_gui_cube_width;false]" ..
"field_close_on_enter[worldedit_gui_cube_height;false]" ..
"field_close_on_enter[worldedit_gui_cube_length;false]" ..
"button_exit[0,3.5;3,0.8;worldedit_gui_cube_submit_hollow;Hollow Cuboid]" ..
"button_exit[3.5,3.5;3,0.8;worldedit_gui_cube_submit_solid;Solid Cuboid]"
end, end,
}) })
worldedit.register_gui_handler("worldedit_gui_cube", function(name, fields) worldedit.register_gui_handler("worldedit_gui_lua", function(name, fields)
local cg = { if fields.worldedit_gui_lua_run or fields.worldedit_gui_lua_transform then
worldedit_gui_cube_search = true, gui_code[name] = fields.worldedit_gui_lua_code
worldedit_gui_cube_node = gui_nodename1, worldedit.show_page(name, "worldedit_gui_lua")
worldedit_gui_cube_width = gui_distance1, if fields.worldedit_gui_lua_run then
worldedit_gui_cube_height = gui_distance2, minetest.chatcommands["/lua"].func(name, gui_code[name])
worldedit_gui_cube_length = gui_distance3, else --fields.worldedit_gui_lua_transform
} minetest.chatcommands["/luatransform"].func(name, gui_code[name])
local ret = handle_changes(name, "worldedit_gui_cube", fields, cg)
if fields.worldedit_gui_cube_submit_hollow or fields.worldedit_gui_cube_submit_solid then
copy_changes(name, fields, cg)
worldedit.show_page(name, "worldedit_gui_cube")
local submit = "hollowcube"
if fields.worldedit_gui_cube_submit_solid then
submit = "cube"
end
local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
local args = string.format("%s %s %s %s", gui_distance1[name], gui_distance2[name], gui_distance3[name], n)
execute_worldedit_command(submit, name, args)
end end
return true return true
end end
return ret return false
end) end)
worldedit.register_gui_function("worldedit_gui_clearobjects", { worldedit.register_gui_function("worldedit_gui_clearobjects", {
name = "Clear Objects", name = "Clear Objects",
privs = we_privs("clearobjects"), privs = we_privs("clearobjects"),
on_select = function(name) on_select = function(name)
execute_worldedit_command("clearobjects", name, "") minetest.chatcommands["/clearobjects"].func(name, "")
end, end,
}) })
worldedit.register_gui_function("worldedit_gui_param2", {
name = "Set Param2",
privs = we_privs("param2"),
get_formspec = function(name)
local value = gui_param2[name] or "0"
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_param2") ..
"textarea[0.5,1;5,2;;;Some values may break the node!]"..
string.format("field[0.5,2.5;2,0.8;worldedit_gui_param2_value;New Param2;%s]", minetest.formspec_escape(value)) ..
"field_close_on_enter[worldedit_gui_param2_value;false]" ..
"button_exit[3.5,2.5;3,0.8;worldedit_gui_param2_submit;Set Param2]"
end,
})
worldedit.register_gui_handler("worldedit_gui_param2", function(name, fields)
local cg = {
worldedit_gui_param2_value = gui_param2,
}
local ret = handle_changes(name, "worldedit_gui_param2", fields, cg)
if fields.worldedit_gui_param2_submit then
copy_changes(name, fields, cg)
worldedit.show_page(name, "worldedit_gui_param2")
execute_worldedit_command("param2", name, gui_param2[name])
return true
end
return ret
end)

142
worldedit_gui/init.lua Normal file → Executable file
View File

@ -70,7 +70,7 @@ local get_formspec = function(name, identifier)
end end
--implement worldedit.show_page(name, page) in different ways depending on the available APIs --implement worldedit.show_page(name, page) in different ways depending on the available APIs
if minetest.global_exists("unified_inventory") then -- unified inventory installed if rawget(_G, "unified_inventory") then --unified inventory installed
local old_func = worldedit.register_gui_function local old_func = worldedit.register_gui_function
worldedit.register_gui_function = function(identifier, options) worldedit.register_gui_function = function(identifier, options)
old_func(identifier, options) old_func(identifier, options)
@ -80,9 +80,8 @@ if minetest.global_exists("unified_inventory") then -- unified inventory install
unified_inventory.register_button("worldedit_gui", { unified_inventory.register_button("worldedit_gui", {
type = "image", type = "image",
image = "inventory_plus_worldedit_gui.png", image = "inventory_plus_worldedit_gui.png",
condition = function(player) tooltip = "Worldedit GUI",
return minetest.check_player_privs(player:get_player_name(), {worldedit=true}) show_with = "worldedit", --Modiff MFF (Crabman 30/06/2015)
end,
}) })
minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
@ -106,7 +105,7 @@ if minetest.global_exists("unified_inventory") then -- unified inventory install
player:set_inventory_formspec(get_formspec(name, page)) player:set_inventory_formspec(get_formspec(name, page))
end end
end end
elseif minetest.global_exists("inventory_plus") then -- inventory++ installed elseif rawget(_G, "inventory_plus") then --inventory++ installed
minetest.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)
local can_worldedit = minetest.check_player_privs(player:get_player_name(), {worldedit=true}) local can_worldedit = minetest.check_player_privs(player:get_player_name(), {worldedit=true})
if can_worldedit then if can_worldedit then
@ -137,56 +136,7 @@ elseif minetest.global_exists("inventory_plus") then -- inventory++ installed
inventory_plus.set_inventory_formspec(player, get_formspec(name, page)) inventory_plus.set_inventory_formspec(player, get_formspec(name, page))
end end
end end
elseif minetest.global_exists("smart_inventory") then -- smart_inventory installed elseif rawget(_G, "sfinv") then --sfinv installed (part of minetest_game since 0.4.15)
-- redefinition: Update the code element on inventory page to show the we-page
function worldedit.show_page(name, page)
local state = smart_inventory.get_page_state("worldedit_gui", name)
if state then
state:get("code"):set_we_formspec(page)
state.location.rootState:show() -- update inventory page
end
end
-- smart_inventory page callback. Contains just a "custom code" element
local function smart_worldedit_gui_callback(state)
local codebox = state:element("code", { name = "code", code = "" })
function codebox:set_we_formspec(we_page)
local new_formspec = get_formspec(state.location.rootState.location.player, we_page)
new_formspec = new_formspec:gsub('button_exit','button') --no inventory closing
self.data.code = "container[1,1]".. new_formspec .. "container_end[]"
end
codebox:set_we_formspec("worldedit_gui")
-- process input (the back button)
state:onInput(function(state, fields, player)
if fields.worldedit_gui then --main page
state:get("code"):set_we_formspec("worldedit_gui")
elseif fields.worldedit_gui_exit then --return to original page
state:get("code"):set_we_formspec("worldedit_gui")
state.location.parentState:get("crafting_button"):submit() -- switch to the crafting tab
end
end)
end
-- all handler should return false to force inventory UI update
local orig_register_gui_handler = worldedit.register_gui_handler
worldedit.register_gui_handler = function(identifier, handler)
local wrapper = function(...)
handler(...)
return false
end
orig_register_gui_handler(identifier, wrapper)
end
-- register the inventory button
smart_inventory.register_page({
name = "worldedit_gui",
tooltip = "Edit your World!",
icon = "inventory_plus_worldedit_gui.png",
smartfs_callback = smart_worldedit_gui_callback,
sequence = 99
})
elseif minetest.global_exists("sfinv") then -- sfinv installed
assert(sfinv.enabled) assert(sfinv.enabled)
local orig_get = sfinv.pages["sfinv:crafting"].get local orig_get = sfinv.pages["sfinv:crafting"].get
sfinv.override_page("sfinv:crafting", { sfinv.override_page("sfinv:crafting", {
@ -215,14 +165,82 @@ elseif minetest.global_exists("sfinv") then -- sfinv installed
player:set_inventory_formspec(get_formspec(name, page)) player:set_inventory_formspec(get_formspec(name, page))
end end
end end
else --fallback button
-- FIXME: this is a huge clusterfuck and the back button is broken
local player_formspecs = {}
local update_main_formspec = function(name)
local formspec = player_formspecs[name]
if not formspec then
return
end
local player = minetest.get_player_by_name(name)
if not player then --this is in case the player signs off while the media is loading
return
end
if (minetest.check_player_privs(name, {creative=true}) or
minetest.setting_getbool("creative_mode")) and
creative then --creative is active, add button to modified formspec
local creative_formspec = player:get_inventory_formspec()
local tab_id = tonumber(creative_formspec:match("tabheader%[.-;(%d+)%;"))
if tab_id == 1 then
formspec = creative_formspec ..
"image_button[0,1;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
elseif not tab_id then
formspec = creative_formspec ..
"image_button[6,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
else else
error( return
"worldedit_gui requires a supported gui management mod to be installed.\n".. end
"To use the it you need to either:\n".. else
"* use minetest_game or another sfinv-compatible subgame\n".. formspec = formspec .. "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
"* install Unified Inventory, Inventory++ or Smart Inventory\n".. end
"If you don't want to use worldedit_gui, disable it by editing world.mt or from the main menu." player:set_inventory_formspec(formspec)
) end
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
minetest.after(1, function()
if minetest.get_player_by_name(name) then --ensure the player is still signed in
player_formspecs[name] = player:get_inventory_formspec()
minetest.after(0.01, function()
update_main_formspec(name)
end)
end
end)
end)
minetest.register_on_leaveplayer(function(player)
player_formspecs[player:get_player_name()] = nil
end)
local gui_player_formspecs = {}
minetest.register_on_player_receive_fields(function(player, formname, fields)
local name = player:get_player_name()
if fields.worldedit_gui then --main page
gui_player_formspecs[name] = player:get_inventory_formspec()
worldedit.show_page(name, "worldedit_gui")
return true
elseif fields.worldedit_gui_exit then --return to original page
if gui_player_formspecs[name] then
player:set_inventory_formspec(gui_player_formspecs[name])
end
return true
else --deal with creative_inventory setting the formspec on every single message
minetest.after(0.01,function()
update_main_formspec(name)
end)
return false --continue processing in creative inventory
end
end)
worldedit.show_page = function(name, page)
local player = minetest.get_player_by_name(name)
if player then
player:set_inventory_formspec(get_formspec(name, page))
end
end
end end
worldedit.register_gui_function("worldedit_gui", { worldedit.register_gui_function("worldedit_gui", {

View File

@ -1,4 +0,0 @@
name = worldedit_gui
description = WorldEdit GUI
depends = worldedit, worldedit_commands
optional_depends = unified_inventory, inventory_plus, sfinv, creative, smart_inventory

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 25 KiB

1
worldedit_infinity/depends.txt Executable file
View File

@ -0,0 +1 @@
worldedit?

103
worldedit_infinity/init.lua Executable file
View File

@ -0,0 +1,103 @@
worldedit = rawget(_G, "worldedit") or {}
local minetest = minetest --local copy of global
local get_pointed = function(pos, nearest, distance)
if distance > 100 then
return false
end
--check for collision with node
local nodename = minetest.get_node(pos).name
if nodename ~= "air"
and nodename ~= "default:water_source"
and nodename ~= "default:water_flowing" then
if nodename ~= "ignore" then
return nearest
end
return false
end
end
local use = function(itemstack, user, pointed_thing)
if pointed_thing.type == "nothing" then --pointing at nothing
local placepos = worldedit.raytrace(user:getpos(), user:get_look_dir(), get_pointed)
if placepos then --extended reach
pointed_thing.type = "node"
pointed_thing.under = nil --wip
pointed_thing.above = nil --wip
end
end
return minetest.item_place_node(itemstack, user, pointed_thing)
end
--
worldedit.raytrace = function(pos, dir, callback)
local base = {x=math.floor(pos.x), y=math.floor(pos.y), z=math.floor(pos.z)}
local stepx, stepy, stepz = 0, 0, 0
local componentx, componenty, componentz = 0, 0, 0
local intersectx, intersecty, intersectz = 0, 0, 0
if dir.x == 0 then
intersectx = math.huge
elseif dir.x > 0 then
stepx = 1
componentx = 1 / dir.x
intersectx = ((base.x - pos.x) + 1) * componentx
else
stepx = -1
componentx = 1 / -dir.x
intersectx = (pos.x - base.x) * componentx
end
if dir.y == 0 then
intersecty = math.huge
elseif dir.y > 0 then
stepy = 1
componenty = 1 / dir.y
intersecty = ((base.y - pos.y) + 1) * componenty
else
stepy = -1
componenty = 1 / -dir.y
intersecty = (pos.y - base.y) * componenty
end
if dir.z == 0 then
intersectz = math.huge
elseif dir.z > 0 then
stepz = 1
componentz = 1 / dir.z
intersectz = ((base.z - pos.z) + 1) * componentz
else
stepz = -1
componentz = 1 / -dir.z
intersectz = (pos.z - base.z) * componentz
end
local distance = 0
local nearest = {x=base.x, y=base.y, z=base.z}
while true do
local values = {callback(base, nearest, distance)}
if #values > 0 then
return unpack(values)
end
nearest.x, nearest.y, nearest.z = base.x, base.y, base.z
if intersectx < intersecty then
if intersectx < intersectz then
base.x = base.x + stepx
distance = intersectx
intersectx = intersectx + componentx
else
base.z = base.z + stepz
distance = intersectz
intersectz = intersectz + componentz
end
elseif intersecty < intersectz then
base.y = base.y + stepy
distance = intersecty
intersecty = intersecty + componenty
else
base.z = base.z + stepz
distance = intersectz
intersectz = intersectz + componentz
end
end
end

1
worldedit_limited/depends.txt Executable file
View File

@ -0,0 +1 @@
worldedit

120
worldedit_limited/init.lua Executable file
View File

@ -0,0 +1,120 @@
do return end
do
local MAX_VOLUME = 30 * 30 * 30
local we = worldedit
local volume = we.volume
local safewrap = function(func)
return function(pos1, pos2, ...)
if validbox(pos1, pos2) then
return func(pos1, pos2, ...)
end
return 0, pos1, pos2
end
end
local validbox = function(pos1, pos2)
tpos1, tpos2 = we.sort_pos(pos1, pos2)
if volume(tpos1, tpos2) > MAX_VOLUME then
return false
end
--check for ownership of area if ownership mod is installed
if owner_defs then
local inside = false
for _, def in pairs(owner_defs) do
--sort positions
local tdef = {x1=def.x1, x2 = def.x2, y1=def.y1, y2=def.y2, z1=def.z1, z2=def.z2}
if tdef.x1 > tdef.x2 then
tdef.x1, tdef.x2 = tdef.x2, tdef.x1
end
if tdef.y1 > tdef.y2 then
tdef.y1, tdef.y2 = tdef.y2, tdef.y1
end
if tdef.z1 > tdef.z2 then
tdef.z1, tdef.z2 = tdef.z2, tdef.z1
end
--check ownership
if tpos1.x >= tdef.x1 and tpos1.x <= tdef.x2
and tpos2.x >= tdef.x1 and tpos2.x <= tdef.x2
and tpos1.y >= tdef.y1 and tpos1.y <= tdef.y2
and tpos2.y >= tdef.y1 and tpos2.y <= tdef.y2
and tpos1.z >= tdef.z1 and tpos1.z <= tdef.z2
and tpos2.z >= tdef.z1 and tpos2.z <= tdef.z2
and name == def.owner then --wip: name isn't available here
inside = true
break
end
end
if not inside then
return false
end
end
return true
end
worldedit = {
sort_pos = we.sort_pos,
set = safewrap(we.set),
replace = safewrap(we.replace),
replaceinverse = safewrap(we.replaceinverse),
copy = function(pos1, pos2, axis, amount)
tpos1, tpos2 = we.sort_pos(pos1, pos2)
tpos1[axis] = tpos1[axis] + amount
tpos2[axis] = tpos2[axis] + amount
if validbox(pos1, pos2) and validbox(tpos1, tpos2) then
we.copy(pos1, pos2, axis, amount)
else
return 0
end
end,
move = function(pos1, pos2, axis, amount)
tpos1, tpos2 = we.sort_pos(pos1, pos2)
tpos1[axis] = tpos1[axis] + amount
tpos2[axis] = tpos2[axis] + amount
if validbox(pos1, pos2) and validbox(tpos1, tpos2) then
we.move(pos1, pos2, axis, amount)
else
return 0
end
end,
stack = function(pos1, pos2, axis, count)
tpos1, tpos2 = we.sort_pos(pos1, pos2)
local length = (tpos2[axis] - tpos1[axis] + 1) * count
if count < 0 then
tpos1[axis] = tpos1[axis] + length
else
tpos2[axis] = tpos2[axis] + length
end
if validbox(tpos1, tpos2) then
we.stack(pos1, pos2, axis, amount)
else
return 0
end
end,
--wip: add transpose, rotate safely
flip = safewrap(we.flip),
orient = safewrap(we.orient),
fixlight = safewrap(we.fixlight),
--wip: add primitives here
volume = we.volume,
hide = safewrap(we.hide),
suppress = safewrap(we.suppress),
highlight = safewrap(we.highlight),
restore = safewrap(we.restore),
serialize = safewrap(we.serialize),
allocate = we.allocate,
deserialize = function(originpos, value)
local tpos1, tpos2 = we.allocate(originpos, value)
if validbox(tpos1, tpos2) then
we.deserialize(originpos, value)
else
return 0
end
end,
}
end

View File

@ -0,0 +1 @@
worldedit_commands

91
worldedit_shortcommands/init.lua Normal file → Executable file
View File

@ -1,52 +1,51 @@
--provides shorter names for the commands in `worldedit_commands` --provides shorter names for the commands in `worldedit_commands`
worldedit.alias_command = function(alias, original) --returns true if command could not be aliased, false otherwise
if not worldedit.registered_commands[original] then worldedit.alias_chatcommand = function(alias, original_command)
minetest.log("error", "worldedit_shortcommands: original " .. original .. " does not exist") if not minetest.chatcommands[original_command] then
return minetest.log("error", "worldedit_shortcommands: original command " .. original_command .. " does not exist")
return true
end end
if minetest.chatcommands["/" .. alias] then if minetest.chatcommands[alias] then
minetest.log("error", "worldedit_shortcommands: alias " .. alias .. " already exists") minetest.log("error", "worldedit_shortcommands: alias " .. alias .. " already exists")
return return true
end
minetest.register_chatcommand(alias, minetest.chatcommands[original_command])
return false
end end
minetest.register_chatcommand("/" .. alias, minetest.chatcommands["/" .. original]) worldedit.alias_chatcommand("/i", "/inspect")
worldedit.registered_commands[alias] = worldedit.registered_commands[original] worldedit.alias_chatcommand("/rst", "/reset")
end worldedit.alias_chatcommand("/mk", "/mark")
worldedit.alias_chatcommand("/umk", "/unmark")
worldedit.alias_command("i", "inspect") worldedit.alias_chatcommand("/1", "/pos1")
worldedit.alias_command("rst", "reset") worldedit.alias_chatcommand("/2", "/pos2")
worldedit.alias_command("mk", "mark") worldedit.alias_chatcommand("/fp", "/fixedpos")
worldedit.alias_command("umk", "unmark") worldedit.alias_chatcommand("/v", "/volume")
worldedit.alias_command("1", "pos1") worldedit.alias_chatcommand("/s", "/set")
worldedit.alias_command("2", "pos2") worldedit.alias_chatcommand("/r", "/replace")
worldedit.alias_command("fp", "fixedpos") worldedit.alias_chatcommand("/ri", "/replaceinverse")
worldedit.alias_command("v", "volume") worldedit.alias_chatcommand("/hspr", "/hollowsphere")
worldedit.alias_command("s", "set") worldedit.alias_chatcommand("/spr", "/sphere")
worldedit.alias_command("r", "replace") worldedit.alias_chatcommand("/hdo", "/hollowdome")
worldedit.alias_command("ri", "replaceinverse") worldedit.alias_chatcommand("/do", "/dome")
worldedit.alias_command("hcube", "hollowcube") worldedit.alias_chatcommand("/hcyl", "/hollowcylinder")
worldedit.alias_command("hspr", "hollowsphere") worldedit.alias_chatcommand("/cyl", "/cylinder")
worldedit.alias_command("spr", "sphere") worldedit.alias_chatcommand("/hpyr", "/hollowpyramid")
worldedit.alias_command("hdo", "hollowdome") worldedit.alias_chatcommand("/pyr", "/pyramid")
worldedit.alias_command("do", "dome") worldedit.alias_chatcommand("/spl", "/spiral")
worldedit.alias_command("hcyl", "hollowcylinder") worldedit.alias_chatcommand("/m", "/move")
worldedit.alias_command("cyl", "cylinder") worldedit.alias_chatcommand("/c", "/copy")
worldedit.alias_command("hpyr", "hollowpyramid") worldedit.alias_chatcommand("/stk", "/stack")
worldedit.alias_command("pyr", "pyramid") worldedit.alias_chatcommand("/sch", "/stretch")
worldedit.alias_command("spl", "spiral") worldedit.alias_chatcommand("/tps", "/transpose")
worldedit.alias_command("m", "move") worldedit.alias_chatcommand("/fl", "/flip")
worldedit.alias_command("c", "copy") worldedit.alias_chatcommand("/rot", "/rotate")
worldedit.alias_command("stk", "stack") worldedit.alias_chatcommand("/ort", "/orient")
worldedit.alias_command("sch", "stretch") worldedit.alias_chatcommand("/hi", "/hide")
worldedit.alias_command("tps", "transpose") worldedit.alias_chatcommand("/sup", "/suppress")
worldedit.alias_command("fl", "flip") worldedit.alias_chatcommand("/hlt", "/highlight")
worldedit.alias_command("rot", "rotate") worldedit.alias_chatcommand("/rsr", "/restore")
worldedit.alias_command("ort", "orient") worldedit.alias_chatcommand("/l", "/lua")
worldedit.alias_command("hi", "hide") worldedit.alias_chatcommand("/lt", "/luatransform")
worldedit.alias_command("sup", "suppress") worldedit.alias_chatcommand("/clro", "/clearobjects")
worldedit.alias_command("hlt", "highlight")
worldedit.alias_command("rsr", "restore")
worldedit.alias_command("l", "lua")
worldedit.alias_command("lt", "luatransform")
worldedit.alias_command("clro", "clearobjects")

View File

@ -1,3 +0,0 @@
name = worldedit_shortcommands
description = WorldEdit command short aliases
depends = worldedit_commands