99 Commits
master ... 1.2

Author SHA1 Message Date
59dc2d34d9 WorldEdit 1.2 2017-09-12 14:56:01 +02:00
283f47f10d Fix invalid node error message of //set and //mix 2017-09-12 14:56:01 +02:00
35ad2f031d Remove work in progress/unused components
infinity: unused and not public, superseded by minetest.raycast()
limited: wip and disabled entirely anyway
2017-09-12 14:13:18 +02:00
d572d769ce Optimize gui icon texture 2017-09-12 13:30:11 +02:00
5bbb6b3909 Support //cube in the GUI 2017-09-12 13:25:33 +02:00
d6a9b517b5 Remove Lua functions from WorldEdit GUI 2017-09-12 13:04:25 +02:00
3c61759bae Allow to bulk-set param2 of regions (#144) 2017-09-05 14:40:46 +02:00
2f4eb19a3a Fix one-node high cylinders
closes #146
2017-09-03 20:12:38 +02:00
2cb3fcde66 Improve node name normalization 2017-08-31 20:38:38 +02:00
e680d8087e Fix small logic error in worldedit.cylinder 2017-08-31 20:10:08 +02:00
870873ad15 Add //hollowcube and //cube
closes #143
2017-08-31 19:08:10 +02:00
e356f4521c WorldEdit GUI support for new //cylinder functionality 2017-08-31 16:46:57 +02:00
bf8e2a8233 Dual-based cylinder 2017-08-31 16:25:22 +02:00
6ceb56c3dc Disable worldedit_gui fallback code (closes #141) 2017-08-20 12:21:41 +02:00
1fabe60d77 Make sfinv gui code compatible with sfinv as included in MTG 0.4.15
fixes #142
2017-08-20 12:13:56 +02:00
acc9188828 Add logging for //lua and //luatransform 2017-05-21 17:31:59 +02:00
3240167b13 Forgot this one during refactor (a31f955fb1) 2017-05-13 11:53:52 +02:00
66b1fa032d Remove formspec tester from worldedit_gui 2017-05-13 11:52:45 +02:00
a31f955fb1 Refactor priv determination in worldedit_gui 2017-05-13 11:51:36 +02:00
38e9b42caf Do not allow any worldedit_gui commands without privs 2017-05-13 11:51:36 +02:00
ea465f8fe4 Remove useless privilege checks
This is covered by the privs = {...},
2017-05-13 11:51:36 +02:00
0ce45a5900 Fix worldedit_gui_lua privs
This fixes the worldedit_gui_lua feature so that it requires certain privs to run.
2017-05-13 11:17:45 +02:00
56f77a2f27 Remove unused toolcaps of the wand tool 2017-03-11 12:50:27 +01:00
610cd9981d Use sfinv if possible, fixes #124 with recent minetest versions 2017-03-11 12:47:30 +01:00
c0f3bb6958 GUI: Use nicer picture to indicate unknown nodes (-> no search results) 2017-03-11 12:24:47 +01:00
e49f717000 GUI: Always pass normalized node names to commands 2017-03-11 12:03:06 +01:00
6e7b9a60be Update wand definition (#129)
Fixes "maxwear" deprecated warning
2017-02-11 00:04:04 +01:00
bcac45a476 Fix "safe region" functionality errornously rejecting pos1-only commands (e.g. //sphere) 2017-01-31 19:52:54 +01:00
426f3b949f Show light level a node receives with //inspect, fixes #128 2017-01-26 22:27:30 +01:00
78e4ba828e Show light level with //inspect 2017-01-03 17:11:38 +01:00
92fe95fab7 Make //fixlight work again
The dig-air-nodes method seems to have stopped working a few Minetest version ago
2017-01-02 19:03:21 +01:00
3a7fb5bf1e Add //help command (fixes #123) 2017-01-01 22:27:22 +01:00
aa0e46d4e2 Handle failure of minetest.add_entity()
(see minetest/minetest#4923)
2016-12-21 14:26:24 +01:00
83288c969e Don't mark or load areas into memory when they are over a certain size, fixes #97 2016-12-10 22:02:40 +01:00
f9311b2b15 Move wand.lua to worldedit_commands 2016-12-10 21:54:51 +01:00
15f0cea72e Disable wand tool on entities 2016-12-10 21:39:44 +01:00
6e2e2385e9 Merge remote-tracking branch 'tmp/hollowpyramid' 2016-10-13 17:51:01 +02:00
152707a322 Fix 'Run Lua' button in WorldEdit GUI (fixes #121) 2016-10-12 20:10:36 +02:00
d040d324e8 Fix //spiral requiring a selected region despite using only pos1 2016-10-12 19:45:38 +02:00
df6b54d2f5 Fix blowing up TNT near worldedit markers 2016-10-12 19:38:41 +02:00
5abe1cee3d Fix exiting mtschemprob entry dialog with ESC 2016-08-05 16:41:37 +02:00
5afea424ba Move cuboidapi.lua to correct place 2016-07-04 19:57:48 +02:00
2aed498849 Add completion chat message to //expand and //contract 2016-07-04 19:53:12 +02:00
0b68b2aec6 Fix //shift with absolute axis (x/y/z) 2016-07-04 19:51:28 +02:00
f72abdb766 Merge remote-tracking branch 'tmp/cuboidmanip' 2016-07-04 19:49:23 +02:00
e18525d8c9 Modify outset and inset to accept both hv and vh combinations. Update documentation accordingly. 2016-07-03 21:44:02 -05:00
78890dde07 Clean some documentation issues 2016-07-03 21:44:02 -05:00
b922097e7a Update command documentation 2016-07-03 21:44:02 -05:00
48f3f59fc3 Implement some safety mechanisms 2016-07-03 21:44:02 -05:00
f8f1f3b9cc Reimplement /expand and /contract to conform to WE standards 2016-07-03 21:44:02 -05:00
d5e004be67 Fix /outset and /inset to conform to WE standards 2016-07-03 21:44:02 -05:00
fad021d0ba Remove some debug code 2016-07-03 21:44:02 -05:00
f1fe0c6bba Fix /shift command to conform to worldedit command standard 2016-07-03 21:44:02 -05:00
40cee99700 Fix a crash that happened when trying to shift the cuboid using relative direction while looking straight up or down 2016-07-03 21:44:02 -05:00
04d20de4cd Fix several typos 2016-07-03 21:44:02 -05:00
859c6bd12a Implement /expand and /contract 2016-07-03 21:44:02 -05:00
240380ff16 Implement /inset and /shift 2016-07-03 21:44:02 -05:00
8d213d32a0 refactor code for mantainability and reusability. Start of chat command refactor. Implement /outset. 2016-07-03 21:44:02 -05:00
ae29a9f064 Reimplement inset/outset to make use of new marker functions 2016-07-03 21:44:02 -05:00
6cf8b92434 Reimplement /shift to make use of new marker functions 2016-07-03 21:44:02 -05:00
0b97a7c740 Finish /expand implementation. Add /contract command. 2016-07-03 21:44:02 -05:00
900b2f25aa Fix faulty regex in expand. Add relative direction decoding. 2016-07-03 21:44:02 -05:00
f49663902c Major progress and simplification in /expand function 2016-07-03 21:44:02 -05:00
d475682d8f Add common functions to move and update the markers 2016-07-03 21:44:02 -05:00
9e087ff995 Add outset, inset, shift and initial draft of expand 2016-07-03 21:44:02 -05:00
b23e92921c fix gui bug with back button (#116)
Fixed the Worldedit GUI problem where BACK button wouldn't return back to inventory plus menu.
2016-06-20 18:05:47 +02:00
61ab240cea Add //drain 2016-03-23 22:09:55 +01:00
b06e004d80 Merge pull request #108 from kilbith/master
Fix unability to browse inv. items on creative inventory
2016-02-22 15:54:25 +01:00
6dddc93ed7 Fix unability to browse inv. items on creative inventory 2016-02-22 12:29:06 +01:00
b99a51f468 Merge pull request #107 from kilbith/master
Re-adapt WorldEdit's GUI button position to new creative inventory
2016-02-22 10:33:04 +01:00
91c5053e67 Re-adapt WorldEdit's GUI button position to creative inventory 2016-02-22 10:29:53 +01:00
f2f714c19e Fix crash when loading version 3 schematics 2016-01-23 16:57:27 -05:00
47712844a3 Added a WorldEdit wand item that can be used to select areas with worldedit. 2016-01-22 22:24:17 +01:00
2bd4d6fa8f Merge pull request #101 from kilbith/master
Adapt WorldEdit GUI button to the new creative inventory
2016-01-17 15:29:06 +01:00
e9e8de385c Adapt WorldEdit GUI button to the new creative inventory 2016-01-17 15:26:47 +01:00
2e2fcfdfa2 Fixed Issue #83 : upsidedown pyramid not working well 2016-01-05 14:11:04 +01:00
e0a2661700 Fix //stack2 not working (closes #94) 2016-01-05 13:57:48 +01:00
Rui
7a19c5303b Fix undeclared global variable 2016-01-05 13:33:12 +01:00
35b68c481b Fixed dropdown menus in the WorldEdit GUI (closes #78, thanks to @sponce) 2016-01-05 13:14:47 +01:00
51158eca9f Fix Save/Load from WorldEdit GUI, fixes #90 2016-01-05 12:42:01 +01:00
3aa315f134 Fix Orient in WorldEdit GUI 2016-01-05 12:24:06 +01:00
3d30588a68 Clarify //deleteblocks 2015-12-18 17:33:29 -05:00
48f9c6c23f Fix #93 (thanks @za267!) 2015-11-01 16:50:36 -05:00
5f9efb1205 Added hollow pyramids 2015-09-13 22:08:04 +02:00
fc037e9c82 Fix formspec typo causing crashing upon using /orient in the GUI. 2015-06-18 18:14:45 -04:00
90d6b3d237 Allow more characters in file names 2015-06-01 17:08:43 -04:00
4bd5d56909 Localize mkdir helper 2015-06-01 16:53:03 -04:00
163dffccb3 Fix leaking {safe,check}_region 2015-05-16 19:52:48 -04:00
6b2fe397e6 Use minetest.mkdir when available 2015-05-16 19:46:33 -04:00
5c115e282c Fix existence check trying to open files for writing 2015-05-16 19:27:27 -04:00
ab47385f7b Fix crash (worldedit/manipulations.lua:526: attempt to call global 'set_node' (a nil value)) 2015-05-04 18:48:27 +02:00
78915d4a54 Merge pull request #76 from est31/fixserialisation
Load first node too with LuaJIT
2015-03-11 22:08:32 -04:00
09de34aabf Load first node too with LuaJIT
Before, the first node would have had the version number prepended (e.g. "5:"), and therefore wouldn't be loaded.
2015-03-12 02:40:19 +01:00
c1bd4986b0 Merge pull request #74 from est31/deleteblocks
Add //deleteblocks command
2015-02-21 00:42:05 -05:00
bea38a116a Add //deleteblocks command 2015-02-20 22:30:34 +01:00
4336e7ca14 Merge pull request #73 from est31/hidebutton
Make inventory++ hide button for players without worldedit priv
2015-02-16 19:58:59 -05:00
eca54f0851 Make inventory++ hide button for players without worldedit priv 2015-02-16 23:05:18 +01:00
b0fbcf197f Merge pull request #72 from HybridDog/globalfix
fix an error message
2015-02-14 23:25:20 -05:00
b468e24a20 fix an error message 2015-02-14 23:10:21 +01:00
24 changed files with 1469 additions and 575 deletions

View File

@ -17,11 +17,15 @@ 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` |
| `//do` | `//dome` | | `//do` | `//dome` |
| `//hcyl` | `//hollowcylinder` | | `//hcyl` | `//hollowcylinder` |
| `//cyl` | `//cylinder` |
| `//hpyr` | `//hollowpyramid` |
| `//pyr` | `//pyramid` |
### `//about` ### `//about`
@ -98,6 +102,12 @@ Display the volume of the current WorldEdit region.
//volume //volume
### `//deleteblocks`
Delete the MapBlocks (16x16x16 units) that contain the selected region. This means that mapgen will be invoked for that area. As only whole MapBlocks get removed, the deleted area is usually larger than the selected one. Also, mapgen can trigger mechanisms like mud reflow or cavegen, which affects nodes (up to 112 nodes away) outside the MapBlock, so dont use this near buildings. Note that active entities are not part of a MapBlock and do not get deleted.
//deleteblocks
### `//set <node>` ### `//set <node>`
Set the current WorldEdit region to `<node>`. Set the current WorldEdit region to `<node>`.
@ -107,6 +117,10 @@ Set the current WorldEdit region to `<node>`.
//set Blue Lightstone //set Blue Lightstone
//set dirt with grass //set dirt with grass
### `//param2 <param2>`
Set the param2 value of all nodes in the current WorldEdit region to `<param2>`.
### `//mix <node1> ...` ### `//mix <node1> ...`
Fill the current WorldEdit region with a random mix of `<node1>`, `...`. Fill the current WorldEdit region with a random mix of `<node1>`, `...`.
@ -134,6 +148,19 @@ 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>`.
@ -166,24 +193,45 @@ 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> <radius> <node>` ### `//hollowcylinder x/y/z/? <length> <radius1> [radius2] <node>`
Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length `<length>` and radius `<radius>`, composed of `<node>`. Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length `<length>`, 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
### `//cylinder x/y/z/? <length> <radius> <node>` //hollowcylinder y 10 10 0 walls:cobble
//hollowcylinder x 6 0 5 Dirt
//hollowcylinder z 20 10 20 default:desert_stone
Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length `<length>` and radius `<radius>`, composed of `<node>`. ### `//cylinder x/y/z/? <length> <radius1> [radius2] <node>`
Add cylinder at WorldEdit position 1 along the x/y/z/? 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
//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>`.
//hollowpyramid x 8 Diamond Block
//hollowpyramid y -5 glass
//hollowpyramid z 2 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 x/y/z/? 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>`.
@ -285,6 +333,12 @@ Fixes the lighting in the current WorldEdit region.
//fixlight //fixlight
### `//drain`
Removes any fluid node within the current WorldEdit region.
//drain
### `//hide` ### `//hide`
Hide all nodes in the current WorldEdit region non-destructively. Hide all nodes in the current WorldEdit region non-destructively.
@ -374,3 +428,38 @@ This mode can be left with `//mtschemprob finish`. `//mtschemprob get` will disp
Clears all objects within the WorldEdit region. Clears all objects within the WorldEdit region.
//clearobjects //clearobjects
### `//shift x/y/z/?/up/down/left/right/front/back [+|-]<amount>`
Shifts the selection area by `[+|-]<amount>` without touching its contents. The shifting axis can be absolute (`x/y/z`) or
relative (`up/down/left/right/front/back`).
//shift left 5
### `//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. If specified, the selection can be expanded in the
opposite direction over the same axis by `[reverse-amount]`.
//expand right 7 5
### `//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. If specified, the selection can be contracted in the
opposite direction over the same axis by `[reverse-amount]`.
//expand right 7 5
### `//outset [hv] <amount>`
Expands the selection in all directions by `<amount>`. If specified, the selection can be expanded horizontally in the x and z axes `[h]`
or vertically in the y axis `[v]`.
//outset v 5
### `//inset [hv] <amount>`
Contracts the selection in all directions by `<amount>`. If specified, the selection can be contracted horizontally in the x and z axes `[h]`
or vertically in the y axis `[v]`.
//outset v 5

View File

@ -1,5 +1,5 @@
WorldEdit v1.1 for Minetest 0.4.8+ WorldEdit v1.2
================================== ==============
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.
@ -33,7 +33,7 @@ WorldEdit works primarily through the WorldEdit GUI and chat commands. Depending
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`. 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`.
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. Certain functions/commands such as WorldEdit `//lua` and `//luatransform` chat commands 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.
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.
@ -51,21 +51,19 @@ This mod supports Minetest versions 0.4.8 and newer. Older versions of WorldEdit
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 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. WorldEdit GUI requires one of [sfinv](https://github.com/minetest/minetest_game/tree/master/mods/sfinv) (included in minetest_game since 0.4.15), [Unified Inventory](https://forum.minetest.net/viewtopic.php?id=3933) or [Inventory++](https://forum.minetest.net/viewtopic.php?id=6204).
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. 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. WorldEdit exposes all significant functionality in a simple Lua interface.
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: 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.
dofile(minetest.get_modpath("worldedit").."/primitives.lua")
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. 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). This API is documented in the [WorldEdit API Reference](WorldEdit%20API.md).
Axes Axes
---- ----
@ -141,9 +139,13 @@ WorldEdit would not be possible without the contributions of many developers and
cheapie cheapie
cornernote cornernote
cyisfor cyisfor
danierukun
electricface electricface
est31
kaeza kaeza
khonkhortisan khonkhortisan
pickardjoe
Sebastien Ponce
sfan5 sfan5
ShadowNinja ShadowNinja
spillz spillz
@ -156,5 +158,4 @@ Copyright 2013 sfan5, Anthony Zhang (Uberi/Temperest), and Brett O'Donnell (corn
This mod is licensed under the [GNU Affero General Public License](http://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.

View File

@ -27,6 +27,12 @@ Sets a region defined by positions `pos1` and `pos2` to `node_name`. To clear a
Returns the number of nodes set. 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.
### count = worldedit.replace(pos1, pos2, searchnode, replacenode) ### count = worldedit.replace(pos1, pos2, searchnode, replacenode)
Replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`. Replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`.
@ -45,6 +51,12 @@ Copies the region defined by positions `pos1` and `pos2` along the `axis` axis (
Returns the number of nodes copied. Returns the number of nodes copied.
### count = worldedit.copy2(pos1, pos2, off)
Copies the region defined by positions `pos1` and `pos2` by the offset vector `off`.
Returns the number of nodes copied.
### count = worldedit.move(pos1, pos2, axis, amount) ### count = worldedit.move(pos1, pos2, axis, amount)
Moves the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes. Moves the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes.
@ -109,6 +121,12 @@ 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`.
@ -121,15 +139,15 @@ 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, radius, node_name, hollow) ### count = worldedit.cylinder(pos, axis, length, radius1, radius2, node_name, hollow)
Adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `node_name`. 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`.
Returns the number of nodes added. Returns the number of nodes added.
### count = worldedit.pyramid(pos, axis, height, node_name) ### count = worldedit.pyramid(pos, axis, height, node_name, hollow)
Adds a pyramid centered at `pos` along the `axis` axis ("x" or "y" or "z") with height `height`. Adds a pyramid centered at `pos` along the `axis` axis ("x" or "y" or "z") with height `height`, composed of `node_name`.
Returns the number of nodes added. Returns the number of nodes added.

258
worldedit/cuboid.lua Normal file
View File

@ -0,0 +1,258 @@
-- Expands or contracts the cuboid in all axes by amount (positive or negative)
worldedit.cuboid_volumetric_expand = function(name, amount)
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
return false, "Undefined cuboid"
end
local delta1 = vector.new()
local delta2 = vector.new()
local delta_dir1
local delta_dir2
delta1 = vector.add(delta1, amount)
delta2 = vector.add(delta2, amount)
delta_dir1, delta_dir2 = worldedit.get_expansion_directions(pos1, pos2)
delta1 = vector.multiply(delta1, delta_dir1)
delta2 = vector.multiply(delta2, delta_dir2)
worldedit.pos1[name] = vector.add(pos1, delta1)
worldedit.pos2[name] = vector.add(pos2, delta2)
return true
end
-- Expands or contracts the cuboid in a single axis by amount (positive or negative)
worldedit.cuboid_linear_expand = function(name, axis, direction, amount)
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
return false, "undefined cuboid"
end
if direction ~= 1 and direction ~= -1 then
return false, "invalid marker"
end
local marker = worldedit.marker_get_closest_to_axis(name, axis, direction)
local deltavect = vector.new()
if axis == 'x' then
deltavect.x = amount * direction
elseif axis == 'y' then
deltavect.y = amount * direction
elseif axis == 'z' then
deltavect.z = amount * direction
else
return false, "invalid axis"
end
worldedit.marker_move(name, marker, deltavect)
return true
end
-- Shifts the cuboid by '+-amount' in axis 'axis'
worldedit.cuboid_shift = function(name, axis, amount)
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
return false, "undefined cuboid"
end
if axis == 'x' then
worldedit.pos1[name].x = pos1.x + amount
worldedit.pos2[name].x = pos2.x + amount
elseif axis == 'y' then
worldedit.pos1[name].y = pos1.y + amount
worldedit.pos2[name].y = pos2.y + amount
elseif axis == 'z' then
worldedit.pos1[name].z = pos1.z + amount
worldedit.pos2[name].z = pos2.z + amount
else
return false, "invalid axis"
end
return true
end
-- Moves the location of a single marker by adding deltavector
worldedit.marker_move = function(name, marker, deltavector)
if marker ~= 1 and marker ~= 2 then
return false
end
if marker == 1 then
local pos = worldedit.pos1[name]
worldedit.pos1[name] = vector.add(deltavector, pos)
else
local pos = worldedit.pos2[name]
worldedit.pos2[name] = vector.add(deltavector, pos)
end
return true
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
worldedit.get_expansion_directions = function(mark1, mark2)
if mark1 == nil or mark2 == nil then
return
end
local dir1 = vector.new()
local dir2 = vector.new()
if mark1.x < mark2.x then
dir1.x = -1
dir2.x = 1
else
dir1.x = 1
dir2.x = -1
end
if mark1.y < mark2.y then
dir1.y = -1
dir2.y = 1
else
dir1.y = 1
dir2.y = -1
end
if mark1.z < mark2.z then
dir1.z = -1
dir2.z = 1
else
dir1.z = 1
dir2.z = -1
end
return dir1, dir2
end
-- Return the marker that is closest to the player
worldedit.marker_get_closest_to_player = function(name)
local playerpos = minetest.get_player_by_name(name):getpos()
local dist1 = vector.distance(playerpos, worldedit.pos1[name])
local dist2 = vector.distance(playerpos, worldedit.pos2[name])
if dist1 < dist2 then
return 1
else
return 2
end
end
-- Returns the closest marker to the specified axis and direction
worldedit.marker_get_closest_to_axis = function(name, axis, direction)
local pos1 = vector.new()
local pos2 = vector.new()
if direction ~= 1 and direction ~= -1 then
return nil
end
if axis == 'x' then
pos1.x = worldedit.pos1[name].x * direction
pos2.x = worldedit.pos2[name].x * direction
if pos1.x > pos2.x then
return 1
else
return 2
end
elseif axis == 'y' then
pos1.y = worldedit.pos1[name].y * direction
pos2.y = worldedit.pos2[name].y * direction
if pos1.y > pos2.y then
return 1
else
return 2
end
elseif axis == 'z' then
pos1.z = worldedit.pos1[name].z * direction
pos2.z = worldedit.pos2[name].z * direction
if pos1.z > pos2.z then
return 1
else
return 2
end
else
return nil
end
end
-- Translates up, down, left, right, front, back to their corresponding axes and
-- directions according to faced direction
worldedit.translate_direction = function(name, direction)
local axis, dir = worldedit.player_axis(name)
local resaxis, resdir
if direction == "up" then
return 'y', 1
end
if direction == "down" then
return 'y', -1
end
if direction == "front" then
if axis == "y" then
resaxis = nil
resdir = nil
else
resaxis = axis
resdir = dir
end
end
if direction == "back" then
if axis == "y" then
resaxis = nil
resdir = nil
else
resaxis = axis
resdir = -dir
end
end
if direction == "left" then
if axis == 'x' then
resaxis = 'z'
resdir = dir
elseif axis == 'z' then
resaxis = 'x'
resdir = -dir
end
end
if direction == "right" then
if axis == 'x' then
resaxis = 'z'
resdir = -dir
elseif axis == 'z' then
resaxis = 'x'
resdir = dir
end
end
return resaxis, resdir
end

View File

@ -1,6 +1,6 @@
--- Worldedit. --- Worldedit.
-- @module worldedit -- @module worldedit
-- @release 1.1 -- @release 1.2
-- @copyright 2013 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
@ -8,9 +8,12 @@
-- @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=2}
worldedit.version = ver
worldedit.version_string = string.format("%d.%d", ver.major, ver.minor)
if not minetest.get_voxel_manip then if not minetest.get_voxel_manip then
local err_msg = "This version of WorldEdit requires Minetest 0.4.8 or later! You have an old version." local err_msg = "This version of WorldEdit requires Minetest 0.4.8 or later! You have an old version."
@ -23,7 +26,7 @@ end
local path = minetest.get_modpath(minetest.get_current_modname()) local path = minetest.get_modpath(minetest.get_current_modname())
local function load_module(path) local function load_module(path)
local file = io.open(path) local file = io.open(path, "r")
if not file then return end if not file then return end
file:close() file:close()
return dofile(path) return dofile(path)
@ -36,6 +39,7 @@ load_module(path .. "/visualization.lua")
load_module(path .. "/serialization.lua") load_module(path .. "/serialization.lua")
load_module(path .. "/code.lua") load_module(path .. "/code.lua")
load_module(path .. "/compatibility.lua") load_module(path .. "/compatibility.lua")
load_module(path .. "/cuboid.lua")
if minetest.setting_getbool("log_mods") then if minetest.setting_getbool("log_mods") then

View File

@ -38,6 +38,29 @@ 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`.
@ -90,7 +113,7 @@ function worldedit.stack2(pos1, pos2, direction, amount, finished)
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, volume) worldedit.copy2(pos1, pos2, translated)
minetest.after(0, next_one) minetest.after(0, next_one)
else else
if finished then if finished then
@ -164,6 +187,38 @@ function worldedit.copy(pos1, pos2, axis, amount)
return worldedit.volume(pos1, pos2) return worldedit.volume(pos1, pos2)
end end
--- Copies a region by offset vector `off`.
-- @param pos1
-- @param pos2
-- @param off
-- @return The number of nodes copied.
function worldedit.copy2(pos1, pos2, off)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
worldedit.keep_loaded(pos1, pos2)
local get_node, get_meta, set_node = minetest.get_node,
minetest.get_meta, minetest.set_node
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 newpos = vector.add(pos, off) -- Calculate new position
set_node(newpos, node) -- Copy node to new position
get_meta(newpos):from_table(meta) -- Set metadata of new node
pos.z = pos.z - 1
end
pos.y = pos.y - 1
end
pos.x = pos.x - 1
end
return worldedit.volume(pos1, pos2)
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.
@ -503,8 +558,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, get_meta, swap_node = minetest.get_node, local set_node, get_node, get_meta, swap_node = minetest.set_node,
minetest.get_meta, minetest.swap_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
@ -543,14 +598,11 @@ end
function worldedit.fixlight(pos1, pos2) function worldedit.fixlight(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
worldedit.keep_loaded(pos1, pos2) local vmanip = minetest.get_voxel_manip(pos1, pos2)
vmanip:write_to_map()
vmanip:update_map() -- this updates the lighting
local nodes = minetest.find_nodes_in_area(pos1, pos2, "air") return worldedit.volume(pos1, pos2)
local dig_node = minetest.dig_node
for _, pos in ipairs(nodes) do
dig_node(pos)
end
return #nodes
end end

View File

@ -4,6 +4,47 @@
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.
@ -92,35 +133,48 @@ 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 radius Cylinder radius. -- @param radius1 Cylinder base 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, radius, node_name, hollow) function worldedit.cylinder(pos, axis, length, radius1, radius2, 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, radius, length) local manip, area = mh.init_axis_radius_length(current_pos, axis, math.max(radius1, radius2), length)
local data = mh.get_empty_data(area) local data = mh.get_empty_data(area)
-- Add cylinder -- Add desired shape (anything inbetween cylinder & cone)
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
@ -128,13 +182,11 @@ function worldedit.cylinder(pos, axis, length, radius, node_name, hollow)
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 -- Position is in cylinder, add node here
-- Add column along axis local vi = new_index3 + (offset[axis] + i) * stride[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
@ -150,14 +202,15 @@ end
-- @param axis Axis ("x", "y", or "z") -- @param axis Axis ("x", "y", or "z")
-- @param height Pyramid height. -- @param height Pyramid height.
-- @param node_name Name of node to make pyramid of. -- @param node_name Name of node to make pyramid of.
-- @param hollow Whether the pyramid should be hollow.
-- @return The number of nodes added. -- @return The number of nodes added.
function worldedit.pyramid(pos, axis, height, node_name) 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
local manip, area = mh.init_axis_radius(pos, axis, local manip, area = mh.init_axis_radius(pos, axis,
height >= 0 and height or -height) height >= 0 and height or -height)
local data = mh.get_empty_data() local data = mh.get_empty_data(area)
-- Handle inverted pyramids -- Handle inverted pyramids
local start_axis, end_axis, step local start_axis, end_axis, step
@ -177,7 +230,7 @@ function worldedit.pyramid(pos, axis, height, node_name)
y = pos.y - area.MinEdge.y, y = pos.y - area.MinEdge.y,
z = pos.z - area.MinEdge.z, z = pos.z - area.MinEdge.z,
} }
local size = height * step local size = math.abs(height * step)
local count = 0 local count = 0
-- For each level of the pyramid -- For each level of the pyramid
for index1 = 0, height, step do for index1 = 0, height, step do
@ -187,10 +240,12 @@ function worldedit.pyramid(pos, axis, height, node_name)
local new_index2 = new_index1 + (index2 + offset[other1]) * stride[other1] local new_index2 = new_index1 + (index2 + offset[other1]) * stride[other1]
for index3 = -size, size do for index3 = -size, size do
local i = new_index2 + (index3 + offset[other2]) * stride[other2] local i = new_index2 + (index3 + offset[other2]) * stride[other2]
if (not hollow or size - math.abs(index2) < 2 or size - math.abs(index3) < 2) then
data[i] = node_id data[i] = node_id
count = count + 1
end
end end
end end
count = count + (size * 2 + 1) ^ 2
size = size - 1 size = size - 1
end end

View File

@ -144,9 +144,9 @@ local function load_schematic(value)
"([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do "([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do
param1, param2 = tonumber(param1), tonumber(param2) param1, param2 = tonumber(param1), tonumber(param2)
table.insert(nodes, { table.insert(nodes, {
x = originx + tonumber(x), x = tonumber(x),
y = originy + tonumber(y), y = tonumber(y),
z = originz + tonumber(z), z = tonumber(z),
name = name, name = name,
param1 = param1 ~= 0 and param1 or nil, param1 = param1 ~= 0 and param1 or nil,
param2 = param2 ~= 0 and param2 or nil, param2 = param2 ~= 0 and param2 or nil,
@ -159,20 +159,20 @@ local function load_schematic(value)
else else
-- XXX: This is a filthy hack that works surprisingly well - in LuaJIT, `minetest.deserialize` will fail due to the register limit -- XXX: This is a filthy hack that works surprisingly well - in LuaJIT, `minetest.deserialize` will fail due to the register limit
nodes = {} nodes = {}
value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1) -- remove the starting and ending values to leave only the node data content = content:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1) -- remove the starting and ending values to leave only the node data
local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end) local escaped = content:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
local startpos, startpos1, endpos = 1, 1 local startpos, startpos1, endpos = 1, 1
while true do -- go through each individual node entry (except the last) while true do -- go through each individual node entry (except the last)
startpos, endpos = escaped:find("},%s*{", startpos) startpos, endpos = escaped:find("},%s*{", startpos)
if not startpos then if not startpos then
break break
end end
local current = value:sub(startpos1, startpos) local current = content:sub(startpos1, startpos)
local entry = minetest.deserialize("return " .. current) local entry = minetest.deserialize("return " .. current)
table.insert(nodes, entry) table.insert(nodes, entry)
startpos, startpos1 = endpos, endpos startpos, startpos1 = endpos, endpos
end end
local entry = minetest.deserialize("return " .. value:sub(startpos1)) -- process the last entry local entry = minetest.deserialize("return " .. content:sub(startpos1)) -- process the last entry
table.insert(nodes, entry) table.insert(nodes, entry)
end end
else else

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

1
worldedit_commands/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*~

View File

@ -0,0 +1,240 @@
minetest.register_chatcommand("/outset", {
params = "[h|v] <amount>",
description = "outset the selection",
privs = {worldedit=true},
func = function(name, param)
local find, _, dir, amount = param:find("(%a*)%s*([+-]?%d+)")
if find == nil then
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
local hv_test = dir:find("[^hv]+")
if hv_test ~= nil then
return false, "Invalid direction."
end
if dir == "" or dir == "hv" or dir == "vh" then
assert(worldedit.cuboid_volumetric_expand(name, amount))
elseif dir == "h" then
assert(worldedit.cuboid_linear_expand(name, 'x', 1, amount))
assert(worldedit.cuboid_linear_expand(name, 'x', -1, amount))
assert(worldedit.cuboid_linear_expand(name, 'z', 1, amount))
assert(worldedit.cuboid_linear_expand(name, 'z', -1, amount))
elseif dir == "v" then
assert(worldedit.cuboid_linear_expand(name, 'y', 1, amount))
assert(worldedit.cuboid_linear_expand(name, 'y', -1, amount))
else
return false, "Invalid number of arguments"
end
worldedit.marker_update(name)
return true, "Region outset by " .. amount .. " blocks"
end,
}
)
minetest.register_chatcommand("/inset", {
params = "[h|v] <amount>",
description = "inset the selection",
privs = {worldedit=true},
func = function(name, param)
local find, _, dir, amount = param:find("(%a*)%s*([+-]?%d+)")
if find == nil then
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
local hv_test = dir:find("[^hv]+")
if hv_test ~= nil then
return false, "Invalid direction."
end
if dir == "" or dir == "vh" or dir == "hv" then
assert(worldedit.cuboid_volumetric_expand(name, -amount))
elseif dir == "h" then
assert(worldedit.cuboid_linear_expand(name, 'x', 1, -amount))
assert(worldedit.cuboid_linear_expand(name, 'x', -1, -amount))
assert(worldedit.cuboid_linear_expand(name, 'z', 1, -amount))
assert(worldedit.cuboid_linear_expand(name, 'z', -1, -amount))
elseif dir == "v" then
assert(worldedit.cuboid_linear_expand(name, 'y', 1, -amount))
assert(worldedit.cuboid_linear_expand(name, 'y', -1, -amount))
else
return false, "Invalid number of arguments"
end
worldedit.marker_update(name)
return true, "Region inset by " .. amount .. " blocks"
end,
}
)
minetest.register_chatcommand("/shift", {
params = "[x|y|z|?|up|down|left|right|front|back] [+|-]<amount>",
description = "Moves the selection region. Does not move contents.",
privs = {worldedit=true},
func = function(name, param)
local pos1 = worldedit.pos1[name]
local pos2 = worldedit.pos2[name]
local find, _, direction, amount = param:find("([%?%l]+)%s*([+-]?%d+)")
if find == nil then
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
local axis, dir
if direction == "x" or direction == "y" or direction == "z" then
axis, dir = direction, 1
elseif direction == "?" then
axis, dir = worldedit.player_axis(name)
else
axis, dir = worldedit.translate_direction(name, direction)
end
if axis == nil or dir == nil then
return false, "Invalid if looking straight up or down"
end
assert(worldedit.cuboid_shift(name, axis, amount * dir))
worldedit.marker_update(name)
return true, "Region shifted by " .. amount .. " nodes"
end,
}
)
minetest.register_chatcommand("/expand", {
params = "[+|-]<x|y|z|?|up|down|left|right|front|back> <amount> [reverse-amount]",
description = "expand the selection in one or two directions at once",
privs = {worldedit=true},
func = function(name, param)
local find, _, sign, direction, amount,
rev_amount = param:find("([+-]?)([%?%l]+)%s*(%d+)%s*(%d*)")
if find == nil then
worldedit.player_notify(name, "invalid use: " .. param)
return
end
if worldedit.pos1[name] == nil or worldedit.pos2[name] == nil then
worldedit.player_notify(name,
"Undefined region. Region must be defined beforehand.")
return
end
local absolute = direction:find("[xyz?]")
local dir, axis
if rev_amount == "" then
rev_amount = 0
end
if absolute == nil then
axis, dir = worldedit.translate_direction(name, direction)
if axis == nil or dir == nil then
return false, "Invalid if looking straight up or down"
end
else
if direction == "?" then
axis, dir = worldedit.player_axis(name)
else
axis = direction
dir = 1
end
end
if sign == "-" then
dir = -dir
end
worldedit.cuboid_linear_expand(name, axis, dir, amount)
worldedit.cuboid_linear_expand(name, axis, -dir, rev_amount)
worldedit.marker_update(name)
return true, "Region expanded by " .. (amount + rev_amount) .. " nodes"
end,
}
)
minetest.register_chatcommand("/contract", {
params = "[+|-]<x|y|z|?|up|down|left|right|front|back> <amount> [reverse-amount]",
description = "contract the selection in one or two directions at once",
privs = {worldedit=true},
func = function(name, param)
local find, _, sign, direction, amount,
rev_amount = param:find("([+-]?)([%?%l]+)%s*(%d+)%s*(%d*)")
if find == nil then
worldedit.player_notify(name, "invalid use: " .. param)
return
end
if worldedit.pos1[name] == nil or worldedit.pos2[name] == nil then
worldedit.player_notify(name,
"Undefined region. Region must be defined beforehand.")
return
end
local absolute = direction:find("[xyz?]")
local dir, axis
if rev_amount == "" then
rev_amount = 0
end
if absolute == nil then
axis, dir = worldedit.translate_direction(name, direction)
if axis == nil or dir == nil then
return false, "Invalid if looking straight up or down"
end
else
if direction == "?" then
axis, dir = worldedit.player_axis(name)
else
axis = direction
dir = 1
end
end
if sign == "-" then
dir = -dir
end
worldedit.cuboid_linear_expand(name, axis, dir, -amount)
worldedit.cuboid_linear_expand(name, axis, -dir, -rev_amount)
worldedit.marker_update(name)
return true, "Region contracted by " .. (amount + rev_amount) .. " nodes"
end,
}
)

View File

@ -10,10 +10,12 @@ if minetest.place_schematic then
worldedit.prob_list = {} worldedit.prob_list = {}
end end
dofile(minetest.get_modpath("worldedit_commands") .. "/cuboid.lua")
dofile(minetest.get_modpath("worldedit_commands") .. "/mark.lua") dofile(minetest.get_modpath("worldedit_commands") .. "/mark.lua")
dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua"); safe_region = safe_region or function(callback) return callback end dofile(minetest.get_modpath("worldedit_commands") .. "/wand.lua")
local safe_region, check_region, reset_pending = dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua")
local get_position = function(name) --position 1 retrieval function for when not using `safe_region` local function get_position(name) --position 1 retrieval function for when not using `safe_region`
local pos1 = worldedit.pos1[name] local pos1 = worldedit.pos1[name]
if pos1 == nil then if pos1 == nil then
worldedit.player_notify(name, "no position 1 selected") worldedit.player_notify(name, "no position 1 selected")
@ -21,7 +23,8 @@ local get_position = function(name) --position 1 retrieval function for when not
return pos1 return pos1
end end
local get_node = function(name, nodename) -- normalize_nodename wrapper for convenience purposes
local function get_node(name, nodename)
local node = worldedit.normalize_nodename(nodename) local node = worldedit.normalize_nodename(nodename)
if not node then if not node then
worldedit.player_notify(name, "invalid node name: " .. nodename) worldedit.player_notify(name, "invalid node name: " .. nodename)
@ -30,34 +33,54 @@ local get_node = function(name, nodename)
return node return node
end end
worldedit.player_notify = function(name, message) function worldedit.player_notify(name, message)
minetest.chat_send_player(name, "WorldEdit -!- " .. message, false) minetest.chat_send_player(name, "WorldEdit -!- " .. message, false)
end end
--determines whether `nodename` is a valid node name, returning a boolean local function string_endswith(full, part)
return full:find(part, 1, true) == #full - #part + 1
end
-- normalizes node "description" `nodename`, returning a string (or nil)
worldedit.normalize_nodename = function(nodename) worldedit.normalize_nodename = function(nodename)
nodename = nodename:gsub("^%s*(.-)%s*$", "%1") nodename = nodename:gsub("^%s*(.-)%s*$", "%1") -- strip spaces
if nodename == "" then return nil end if nodename == "" then return nil end
local fullname = ItemStack({name=nodename}):get_name() --resolve aliases of node names to full names
if minetest.registered_nodes[fullname] or fullname == "air" then --directly found node name or alias of nodename local fullname = ItemStack({name=nodename}):get_name() -- resolve aliases
if minetest.registered_nodes[fullname] or fullname == "air" then -- full name
return fullname return fullname
end end
for key, value in pairs(minetest.registered_nodes) do for key, value in pairs(minetest.registered_nodes) do
if key:find(":" .. nodename, 1, true) then --found in mod if string_endswith(key, ":" .. nodename) then -- matches name (w/o mod part)
return key return key
end end
end end
nodename = nodename:lower() --lowercase both for case insensitive comparison nodename = nodename:lower() -- lowercase both for case insensitive comparison
for key, value in pairs(minetest.registered_nodes) do for key, value in pairs(minetest.registered_nodes) do
if value.description:lower() == nodename then --found in description local desc = value.description:lower()
if desc == nodename then -- matches description
return key
end
if string_endswith(desc, " block") and desc == nodename.." block" then
-- fuzzy description match (e.g. "Steel" == "Steel Block")
return key return key
end end
end end
local match = nil
for key, value in pairs(minetest.registered_nodes) do
if value.description:lower():find(nodename, 1, true) ~= nil then
if match ~= nil then
return nil return nil
end
match = key -- substring description match (only if no ambiguities)
end
end
return match
end end
--determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1) -- Determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1)
worldedit.player_axis = function(name) function worldedit.player_axis(name)
local dir = minetest.get_player_by_name(name):get_look_dir() local dir = minetest.get_player_by_name(name):get_look_dir()
local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z) local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z)
if x > y then if x > y then
@ -70,6 +93,19 @@ worldedit.player_axis = function(name)
return "z", dir.z > 0 and 1 or -1 return "z", dir.z > 0 and 1 or -1
end end
local function mkdir(path)
if minetest.mkdir then
minetest.mkdir(path)
else
os.execute('mkdir "' .. path .. '"')
end
end
local function check_filename(name)
return name:find("^[%w%s%^&'@{}%[%],%$=!%-#%(%)%%%.%+~_]+$") ~= nil
end
minetest.register_chatcommand("/about", { minetest.register_chatcommand("/about", {
params = "", params = "",
description = "Get information about the mod", description = "Get information about the mod",
@ -78,6 +114,56 @@ minetest.register_chatcommand("/about", {
end, end,
}) })
-- mostly copied from builtin/chatcommands.lua with minor modifications
minetest.register_chatcommand("/help", {
privs = {},
params = "[all/<cmd>]",
description = "Get help for WorldEdit commands",
func = function(name, param)
local function is_we_command(cmd)
return cmd:sub(0, 1) == "/"
end
local function format_help_line(cmd, def)
local msg = minetest.colorize("#00ffff", "/"..cmd)
if def.params and def.params ~= "" then
msg = msg .. " " .. def.params
end
if def.description and def.description ~= "" then
msg = msg .. ": " .. def.description
end
return msg
end
if not minetest.check_player_privs(name, "worldedit") then
return false, "You are not allowed to use any WorldEdit commands."
end
if param == "" then
local msg = ""
local cmds = {}
for cmd, def in pairs(minetest.chatcommands) do
if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then
cmds[#cmds + 1] = cmd:sub(2) -- strip the /
end
end
table.sort(cmds)
return true, "Available commands: " .. table.concat(cmds, " ") .. "\n"
.. "Use '//help <cmd>' to get more information,"
.. " or '//help all' to list everything."
elseif param == "all" then
local cmds = {}
for cmd, def in pairs(minetest.chatcommands) do
if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then
cmds[#cmds + 1] = format_help_line(cmd, def)
end
end
table.sort(cmds)
return true, "Available commands:\n"..table.concat(cmds, "\n")
else
return minetest.chatcommands["help"].func(name, "/" .. param)
end
end,
})
minetest.register_chatcommand("/inspect", { minetest.register_chatcommand("/inspect", {
params = "on/off/1/0/true/false/yes/no/enable/disable/<blank>", params = "on/off/1/0/true/false/yes/no/enable/disable/<blank>",
description = "Enable or disable node inspection", description = "Enable or disable node inspection",
@ -97,16 +183,28 @@ minetest.register_chatcommand("/inspect", {
end, end,
}) })
local function get_node_rlight(pos)
local vecs = { -- neighboring nodes
{x= 1, y= 0, z= 0},
{x=-1, y= 0, z= 0},
{x= 0, y= 1, z= 0},
{x= 0, y=-1, z= 0},
{x= 0, y= 0, z= 1},
{x= 0, y= 0, z=-1},
}
local ret = 0
for _, v in ipairs(vecs) do
ret = math.max(ret, minetest.get_node_light(vector.add(pos, v)))
end
return ret
end
minetest.register_on_punchnode(function(pos, node, puncher) minetest.register_on_punchnode(function(pos, node, puncher)
local name = puncher:get_player_name() local name = puncher:get_player_name()
if worldedit.inspect[name] then if worldedit.inspect[name] then
if minetest.check_player_privs(name, {worldedit=true}) then
local axis, sign = worldedit.player_axis(name) local axis, sign = worldedit.player_axis(name)
message = string.format("inspector: %s at %s (param1=%d, param2=%d) punched by %s facing the %s axis", message = string.format("inspector: %s at %s (param1=%d, param2=%d, received light=%d) punched facing the %s axis",
node.name, minetest.pos_to_string(pos), node.param1, node.param2, name, axis .. (sign > 0 and "+" or "-")) node.name, minetest.pos_to_string(pos), node.param1, node.param2, get_node_rlight(pos), axis .. (sign > 0 and "+" or "-"))
else
message = "inspector: worldedit privileges required"
end
worldedit.player_notify(name, message) worldedit.player_notify(name, message)
end end
end) end)
@ -121,6 +219,8 @@ minetest.register_chatcommand("/reset", {
worldedit.mark_pos1(name) worldedit.mark_pos1(name)
worldedit.mark_pos2(name) worldedit.mark_pos2(name)
worldedit.set_pos[name] = nil worldedit.set_pos[name] = nil
--make sure the user does not try to confirm an operation after resetting pos:
reset_pending(name)
worldedit.player_notify(name, "region reset") worldedit.player_notify(name, "region reset")
end, end,
}) })
@ -277,22 +377,53 @@ minetest.register_chatcommand("/volume", {
end, end,
}) })
minetest.register_chatcommand("/deleteblocks", {
params = "",
description = "remove all MapBlocks (16x16x16) containing the selected area from the map",
privs = {worldedit=true},
func = safe_region(function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
local success = minetest.delete_area(pos1, pos2)
if success then
worldedit.player_notify(name, "Area deleted.")
else
worldedit.player_notify(name, "There was an error during deletion of the area.")
end
end),
})
minetest.register_chatcommand("/set", { minetest.register_chatcommand("/set", {
params = "<node>", params = "<node>",
description = "Set the current WorldEdit region to <node>", description = "Set the current WorldEdit region to <node>",
privs = {worldedit=true}, privs = {worldedit=true},
func = safe_region(function(name, param) func = safe_region(function(name, param)
local node = get_node(name, param) local node = get_node(name, param)
if not node then if not node then return end
worldedit.player_notify(name, "Could not identify node \"" .. param .. "\"")
return
end
local count = worldedit.set(worldedit.pos1[name], worldedit.pos2[name], node) local count = worldedit.set(worldedit.pos1[name], worldedit.pos2[name], node)
worldedit.player_notify(name, count .. " nodes set") worldedit.player_notify(name, count .. " nodes set")
end, check_region), end, check_region),
}) })
minetest.register_chatcommand("/param2", {
params = "<param2>",
description = "Set param2 of all nodes in the current WorldEdit region to <param2>",
privs = {worldedit=true},
func = safe_region(function(name, param)
local param2 = tonumber(param)
if not param2 then
worldedit.player_notify(name, "Invalid or missing param2 argument")
return
elseif param2 < 0 or param2 > 255 then
worldedit.player_notify(name, "Param2 is out of range (must be between 0 and 255 inclusive)!")
return
end
local count = worldedit.set_param2(worldedit.pos1[name], worldedit.pos2[name], param2)
worldedit.player_notify(name, count .. " nodes altered")
end, check_region),
})
minetest.register_chatcommand("/mix", { minetest.register_chatcommand("/mix", {
params = "<node1> ...", params = "<node1> ...",
description = "Fill the current WorldEdit region with a random mix of <node1>, ...", description = "Fill the current WorldEdit region with a random mix of <node1>, ...",
@ -301,10 +432,7 @@ minetest.register_chatcommand("/mix", {
local nodes = {} local nodes = {}
for nodename in param:gmatch("[^%s]+") do for nodename in param:gmatch("[^%s]+") do
local node = get_node(name, nodename) local node = get_node(name, nodename)
if not node then if not node then return end
worldedit.player_notify(name, "Could not identify node \"" .. name .. "\"")
return
end
nodes[#nodes + 1] = node nodes[#nodes + 1] = node
end end
@ -361,6 +489,45 @@ minetest.register_chatcommand("/replaceinverse", {
end, check_replace), end, check_replace),
}) })
local check_cube = function(name, param)
if worldedit.pos1[name] == nil then
worldedit.player_notify(name, "no position 1 selected")
return nil
end
local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
if found == nil then
worldedit.player_notify(name, "invalid usage: " .. param)
return nil
end
local node = get_node(name, nodename)
if not node then return nil end
return tonumber(w) * tonumber(h) * tonumber(l)
end
minetest.register_chatcommand("/hollowcube", {
params = "<width> <height> <length> <node>",
description = "Add a hollow cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.",
privs = {worldedit=true},
func = safe_region(function(name, param)
local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
local node = get_node(name, nodename)
local count = worldedit.cube(worldedit.pos1[name], tonumber(w), tonumber(h), tonumber(l), node, true)
worldedit.player_notify(name, count .. " nodes added")
end, check_cube),
})
minetest.register_chatcommand("/cube", {
params = "<width> <height> <length> <node>",
description = "Add a cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.",
privs = {worldedit=true},
func = safe_region(function(name, param)
local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
local node = get_node(name, nodename)
local count = worldedit.cube(worldedit.pos1[name], tonumber(w), tonumber(h), tonumber(l), node)
worldedit.player_notify(name, count .. " nodes added")
end, check_cube),
})
local check_sphere = function(name, param) local check_sphere = function(name, param)
if worldedit.pos1[name] == nil then if worldedit.pos1[name] == nil then
worldedit.player_notify(name, "no position 1 selected") worldedit.player_notify(name, "no position 1 selected")
@ -444,50 +611,102 @@ local check_cylinder = function(name, param)
worldedit.player_notify(name, "no position 1 selected") worldedit.player_notify(name, "no position 1 selected")
return nil return nil
end end
local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$") -- two radii
local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
if found == nil then
-- single radius
found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
radius2 = radius1
end
if found == nil then if found == nil then
worldedit.player_notify(name, "invalid usage: " .. param) worldedit.player_notify(name, "invalid usage: " .. param)
return nil return nil
end end
local node = get_node(name, nodename) local node = get_node(name, nodename)
if not node then return nil end if not node then return nil end
return math.ceil(math.pi * (tonumber(radius) ^ 2) * tonumber(length)) local radius = math.max(tonumber(radius1), tonumber(radius2))
return math.ceil(math.pi * (radius ^ 2) * tonumber(length))
end end
minetest.register_chatcommand("/hollowcylinder", { minetest.register_chatcommand("/hollowcylinder", {
params = "x/y/z/? <length> <radius> <node>", params = "x/y/z/? <length> <radius1> [radius2] <node>",
description = "Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>", description = "Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>",
privs = {worldedit=true}, privs = {worldedit=true},
func = safe_region(function(name, param) func = safe_region(function(name, param)
local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$") -- two radii
local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
if found == nil then
-- single radius
found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
radius2 = radius1
end
length = tonumber(length) length = tonumber(length)
if axis == "?" then if axis == "?" then
axis, sign = worldedit.player_axis(name) axis, sign = worldedit.player_axis(name)
length = length * sign length = length * sign
end end
local node = get_node(name, nodename) local node = get_node(name, nodename)
local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius), node, true) local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius1), tonumber(radius2), node, true)
worldedit.player_notify(name, count .. " nodes added") worldedit.player_notify(name, count .. " nodes added")
end, check_cylinder), end, check_cylinder),
}) })
minetest.register_chatcommand("/cylinder", { minetest.register_chatcommand("/cylinder", {
params = "x/y/z/? <length> <radius> <node>", params = "x/y/z/? <length> <radius1> [radius2] <node>",
description = "Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>", description = "Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>",
privs = {worldedit=true}, privs = {worldedit=true},
func = safe_region(function(name, param) func = safe_region(function(name, param)
local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$") -- two radii
local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
if found == nil then
-- single radius
found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
radius2 = radius1
end
length = tonumber(length) length = tonumber(length)
if axis == "?" then if axis == "?" then
axis, sign = worldedit.player_axis(name) axis, sign = worldedit.player_axis(name)
length = length * sign length = length * sign
end end
local node = get_node(name, nodename) local node = get_node(name, nodename)
local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius), node) local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius1), tonumber(radius2), node)
worldedit.player_notify(name, count .. " nodes added") worldedit.player_notify(name, count .. " nodes added")
end, check_cylinder), end, check_cylinder),
}) })
local check_pyramid = function(name, param)
if worldedit.pos1[name] == nil then
worldedit.player_notify(name, "no position 1 selected")
return nil
end
local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
if found == nil then
worldedit.player_notify(name, "invalid usage: " .. param)
return nil
end
local node = get_node(name, nodename)
if not node then return nil end
height = tonumber(height)
return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
end
minetest.register_chatcommand("/hollowpyramid", {
params = "x/y/z/? <height> <node>",
description = "Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
privs = {worldedit=true},
func = safe_region(function(name, param)
local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
height = tonumber(height)
if axis == "?" then
axis, sign = worldedit.player_axis(name)
height = height * sign
end
local node = get_node(name, nodename)
local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node, true)
worldedit.player_notify(name, count .. " nodes added")
end, check_pyramid),
})
minetest.register_chatcommand("/pyramid", { minetest.register_chatcommand("/pyramid", {
params = "x/y/z/? <height> <node>", params = "x/y/z/? <height> <node>",
description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>", description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
@ -502,22 +721,7 @@ minetest.register_chatcommand("/pyramid", {
local node = get_node(name, nodename) local node = get_node(name, nodename)
local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node) local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)
worldedit.player_notify(name, count .. " nodes added") worldedit.player_notify(name, count .. " nodes added")
end, end, check_pyramid),
function(name, param)
if worldedit.pos1[name] == nil then
worldedit.player_notify(name, "no position 1 selected")
return nil
end
local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
if found == nil then
worldedit.player_notify(name, "invalid usage: " .. param)
return nil
end
local node = get_node(name, nodename)
if not node then return nil end
height = tonumber(height)
return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
end),
}) })
minetest.register_chatcommand("/spiral", { minetest.register_chatcommand("/spiral", {
@ -542,7 +746,7 @@ minetest.register_chatcommand("/spiral", {
end end
local node = get_node(name, nodename) local node = get_node(name, nodename)
if not node then return nil end if not node then return nil end
return check_region(name, param) return 1 -- TODO: return an useful value
end), end),
}) })
@ -810,6 +1014,30 @@ minetest.register_chatcommand("/fixlight", {
end), end),
}) })
minetest.register_chatcommand("/drain", {
params = "",
description = "Remove any fluid node within the current WorldEdit region",
privs = {worldedit=true},
func = safe_region(function(name, param)
-- TODO: make an API function for this
local count = 0
local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
for x = pos1.x, pos2.x do
for y = pos1.y, pos2.y do
for z = pos1.z, pos2.z do
local n = minetest.get_node({x=x, y=y, z=z}).name
local d = minetest.registered_nodes[n]
if d ~= nil and (d["drawtype"] == "liquid" or d["drawtype"] == "flowingliquid") then
minetest.remove_node({x=x, y=y, z=z})
count = count + 1
end
end
end
end
worldedit.player_notify(name, count .. " nodes updated")
end),
})
minetest.register_chatcommand("/hide", { minetest.register_chatcommand("/hide", {
params = "", params = "",
description = "Hide all nodes in the current WorldEdit region non-destructively", description = "Hide all nodes in the current WorldEdit region non-destructively",
@ -861,20 +1089,21 @@ minetest.register_chatcommand("/save", {
worldedit.player_notify(name, "invalid usage: " .. param) worldedit.player_notify(name, "invalid usage: " .. param)
return return
end end
if not string.find(param, "^[%w \t.,+-_=!@#$%%^&*()%[%]{};'\"]+$") then if not check_filename(param) then
worldedit.player_notify(name, "invalid file name: " .. param) worldedit.player_notify(name, "Disallowed file name: " .. param)
return return
end end
local result, count = worldedit.serialize(worldedit.pos1[name],
local result, count = worldedit.serialize(worldedit.pos1[name], worldedit.pos2[name]) worldedit.pos2[name])
local path = minetest.get_worldpath() .. "/schems" local path = minetest.get_worldpath() .. "/schems"
-- Create directory if it does not already exist
mkdir(path)
local filename = path .. "/" .. param .. ".we" local filename = path .. "/" .. param .. ".we"
filename = filename:gsub("\"", "\\\""):gsub("\\", "\\\\") --escape any nasty characters
os.execute("mkdir \"" .. path .. "\"") --create directory if it does not already exist
local file, err = io.open(filename, "wb") local file, err = io.open(filename, "wb")
if err ~= nil then if err ~= nil then
worldedit.player_notify(name, "could not save file to \"" .. filename .. "\"") worldedit.player_notify(name, "Could not save file to \"" .. filename .. "\"")
return return
end end
file:write(result) file:write(result)
@ -897,8 +1126,8 @@ minetest.register_chatcommand("/allocate", {
worldedit.player_notify(name, "invalid usage: " .. param) worldedit.player_notify(name, "invalid usage: " .. param)
return return
end end
if not string.find(param, "^[%w \t.,+-_=!@#$%%^&*()%[%]{};'\"]+$") then if not check_filename(param) then
worldedit.player_notify(name, "invalid file name: " .. param) worldedit.player_notify(name, "Disallowed file name: " .. param)
return return
end end
@ -986,16 +1215,13 @@ minetest.register_chatcommand("/lua", {
description = "Executes <code> as a Lua chunk in the global namespace", description = "Executes <code> as a Lua chunk in the global namespace",
privs = {worldedit=true, server=true}, privs = {worldedit=true, server=true},
func = function(name, param) func = function(name, param)
local admin = minetest.setting_get("name")
if not admin or not name == admin then
worldedit.player_notify(name, "this command can only be run by the server administrator")
return
end
local err = worldedit.lua(param) local err = worldedit.lua(param)
if err then if err then
worldedit.player_notify(name, "code error: " .. err) worldedit.player_notify(name, "code error: " .. err)
minetest.log("action", name.." tried to execute "..param)
else else
worldedit.player_notify(name, "code successfully executed", false) worldedit.player_notify(name, "code successfully executed", false)
minetest.log("action", name.." executed "..param)
end end
end, end,
}) })
@ -1005,41 +1231,44 @@ minetest.register_chatcommand("/luatransform", {
description = "Executes <code> as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region", description = "Executes <code> as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region",
privs = {worldedit=true, server=true}, privs = {worldedit=true, server=true},
func = safe_region(function(name, param) func = safe_region(function(name, param)
local admin = minetest.setting_get("name")
if not admin or not name == admin then
worldedit.player_notify(name, "this command can only be run by the server administrator")
return
end
local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param) local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param)
if err then if err then
worldedit.player_notify(name, "code error: " .. err, false) worldedit.player_notify(name, "code error: " .. err, false)
minetest.log("action", name.." tried to execute luatransform "..param)
else else
worldedit.player_notify(name, "code successfully executed", false) worldedit.player_notify(name, "code successfully executed", false)
minetest.log("action", name.." executed luatransform "..param)
end end
end), end),
}) })
minetest.register_chatcommand("/mtschemcreate", { minetest.register_chatcommand("/mtschemcreate", {
params = "<file>", params = "<file>",
description = "Save the current WorldEdit region using the Minetest Schematic format to \"(world folder)/schems/<filename>.mts\"", description = "Save the current WorldEdit region using the Minetest "..
"Schematic format to \"(world folder)/schems/<filename>.mts\"",
privs = {worldedit=true}, privs = {worldedit=true},
func = safe_region(function(name, param) func = safe_region(function(name, param)
if param == nil then if param == nil then
worldedit.player_notify(name, "No filename specified") worldedit.player_notify(name, "No filename specified")
return return
end end
if not check_filename(param) then
worldedit.player_notify(name, "Disallowed file name: " .. param)
return
end
local path = minetest.get_worldpath() .. "/schems" local path = minetest.get_worldpath() .. "/schems"
local filename = path .. "/" .. param .. ".mts" -- Create directory if it does not already exist
filename = filename:gsub("\"", "\\\""):gsub("\\", "\\\\") --escape any nasty characters mkdir(path)
os.execute("mkdir \"" .. path .. "\"") --create directory if it does not already exist
local ret = minetest.create_schematic(worldedit.pos1[name], worldedit.pos2[name], worldedit.prob_list[name], filename) local filename = path .. "/" .. param .. ".mts"
local ret = minetest.create_schematic(worldedit.pos1[name],
worldedit.pos2[name], worldedit.prob_list[name],
filename)
if ret == nil then if ret == nil then
worldedit.player_notify(name, "failed to create Minetest schematic", false) worldedit.player_notify(name, "Failed to create Minetest schematic", false)
else else
worldedit.player_notify(name, "saved Minetest schematic to " .. param, false) worldedit.player_notify(name, "Saved Minetest schematic to " .. param, false)
end end
worldedit.prob_list[name] = {} worldedit.prob_list[name] = {}
end), end),
@ -1050,10 +1279,14 @@ minetest.register_chatcommand("/mtschemplace", {
description = "Load nodes from \"(world folder)/schems/<file>.mts\" with position 1 of the current WorldEdit region as the origin", description = "Load nodes from \"(world folder)/schems/<file>.mts\" with position 1 of the current WorldEdit region as the origin",
privs = {worldedit=true}, privs = {worldedit=true},
func = function(name, param) func = function(name, param)
if param == nil then if param == "" then
worldedit.player_notify(name, "no filename specified") worldedit.player_notify(name, "no filename specified")
return return
end end
if not check_filename(param) then
worldedit.player_notify(name, "Disallowed file name: " .. param)
return
end
local pos = get_position(name) local pos = get_position(name)
if pos == nil then return end if pos == nil then return end
@ -1087,8 +1320,8 @@ minetest.register_chatcommand("/mtschemprob", {
return return
end end
for k,v in pairs(problist) do for k,v in pairs(problist) do
local prob = math.floor(((v["prob"] / 256) * 100) * 100 + 0.5) / 100 local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100
text = text .. minetest.pos_to_string(v["pos"]) .. ": " .. prob .. "% | " text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | "
end end
worldedit.player_notify(name, "currently set node probabilities:") worldedit.player_notify(name, "currently set node probabilities:")
worldedit.player_notify(name, text) worldedit.player_notify(name, text)
@ -1098,16 +1331,14 @@ minetest.register_chatcommand("/mtschemprob", {
end, end,
}) })
minetest.register_on_player_receive_fields( minetest.register_on_player_receive_fields(function(player, formname, fields)
function(player, formname, fields) if formname == "prob_val_enter" and not (fields.text == "" or fields.text == nil) then
if (formname == "prob_val_enter") and (fields.text ~= "") then
local name = player:get_player_name() local name = player:get_player_name()
local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)} local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}
local index = table.getn(worldedit.prob_list[name]) + 1 local index = table.getn(worldedit.prob_list[name]) + 1
worldedit.prob_list[name][index] = prob_entry worldedit.prob_list[name][index] = prob_entry
end end
end end)
)
minetest.register_chatcommand("/clearobjects", { minetest.register_chatcommand("/clearobjects", {
params = "", params = "",

View File

@ -58,8 +58,19 @@ worldedit.mark_region = function(name)
end end
worldedit.marker_region[name] = nil worldedit.marker_region[name] = nil
end end
if pos1 ~= nil and pos2 ~= nil then if pos1 ~= nil and pos2 ~= nil then
local pos1, pos2 = worldedit.sort_pos(pos1, pos2) local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local vec = vector.subtract(pos2, pos1)
local maxside = math.max(vec.x, math.max(vec.y, vec.z))
local limit = tonumber(minetest.setting_get("active_object_send_range_blocks")) * 16
if maxside > limit * 1.5 then
-- 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
return
end
local thickness = 0.2 local thickness = 0.2
local sizex, sizey, sizez = (1 + pos2.x - pos1.x) / 2, (1 + pos2.y - pos1.y) / 2, (1 + pos2.z - pos1.z) / 2 local sizex, sizey, sizez = (1 + pos2.x - pos1.x) / 2, (1 + pos2.y - pos1.y) / 2, (1 + pos2.z - pos1.z) / 2
@ -72,6 +83,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 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({x=pos1.x + sizex - 0.5, y=pos1.y + sizey - 0.5, z=z}, "worldedit:region_cube")
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},
collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness}, collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness},
@ -79,10 +91,12 @@ worldedit.mark_region = function(name)
marker:get_luaentity().player_name = name marker:get_luaentity().player_name = name
table.insert(markers, marker) table.insert(markers, marker)
end end
end
--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 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({x=x, y=pos1.y + sizey - 0.5, z=pos1.z + sizez - 0.5}, "worldedit:region_cube")
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},
@ -91,6 +105,7 @@ worldedit.mark_region = function(name)
marker:get_luaentity().player_name = name marker:get_luaentity().player_name = name
table.insert(markers, marker) table.insert(markers, marker)
end end
end
worldedit.marker_region[name] = markers worldedit.marker_region[name] = markers
end end
@ -153,7 +168,11 @@ minetest.register_entity(":worldedit:region_cube", {
end end
end, end,
on_punch = function(self, hitter) on_punch = function(self, hitter)
for _, entity in ipairs(worldedit.marker_region[self.player_name]) do local markers = worldedit.marker_region[self.player_name]
if not markers then
return
end
for _, entity in ipairs(markers) do
entity:remove() entity:remove()
end end
worldedit.marker_region[self.player_name] = nil worldedit.marker_region[self.player_name] = nil

View File

@ -1,7 +1,7 @@
local safe_region_callback = {} local safe_region_callback = {}
local safe_region_param = {} local safe_region_param = {}
check_region = function(name, param) local function check_region(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] --obtain positions local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] --obtain positions
if pos1 == nil or pos2 == nil then if pos1 == nil or pos2 == nil then
worldedit.player_notify(name, "no region selected") worldedit.player_notify(name, "no region selected")
@ -12,7 +12,7 @@ end
--`callback` is a callback to run when the user confirms --`callback` is a callback to run when the user confirms
--`nodes_needed` is a function accepting `param`, `pos1`, and `pos2` to calculate the number of nodes needed --`nodes_needed` is a function accepting `param`, `pos1`, and `pos2` to calculate the number of nodes needed
safe_region = function(callback, nodes_needed) local function safe_region(callback, nodes_needed)
--default node volume calculation --default node volume calculation
nodes_needed = nodes_needed or check_region nodes_needed = nodes_needed or check_region
@ -30,6 +30,10 @@ safe_region = function(callback, nodes_needed)
end end
end end
local function reset_pending(name)
safe_region_callback[name], safe_region_param[name] = nil, nil
end
minetest.register_chatcommand("/y", { minetest.register_chatcommand("/y", {
params = "", params = "",
description = "Confirm a pending operation", description = "Confirm a pending operation",
@ -40,15 +44,8 @@ minetest.register_chatcommand("/y", {
return return
end end
--obtain positions
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
worldedit.player_notify(name, "no region selected")
return
end
safe_region_callback[name], safe_region_param[name] = nil, nil --reset pending operation safe_region_callback[name], safe_region_param[name] = nil, nil --reset pending operation
callback(name, param, pos1, pos2) callback(name, param)
end, end,
}) })
@ -63,3 +60,6 @@ minetest.register_chatcommand("/n", {
safe_region_callback[name], safe_region_param[name] = nil, nil safe_region_callback[name], safe_region_param[name] = nil, nil
end, end,
}) })
return safe_region, check_region, reset_pending

View File

@ -0,0 +1,24 @@
minetest.register_tool(":worldedit:wand", {
description = "WorldEdit Wand tool, Left-click to set 1st position, right-click to set 2nd",
inventory_image = "worldedit_wand.png",
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
on_use = function(itemstack, placer, pointed_thing)
if placer ~= nil and pointed_thing ~= nil and pointed_thing.type == "node" then
local name = placer:get_player_name()
worldedit.pos1[name] = pointed_thing.under
worldedit.mark_pos1(name)
end
return itemstack -- nothing consumed, nothing changed
end,
on_place = function(itemstack, placer, pointed_thing) -- Left Click
if placer ~= nil and pointed_thing ~= nil and pointed_thing.type == "node" then
local name = placer:get_player_name()
worldedit.pos2[name] = pointed_thing.under
worldedit.mark_pos2(name)
end
return itemstack -- nothing consumed, nothing changed
end,
})

View File

@ -2,4 +2,5 @@ worldedit
worldedit_commands worldedit_commands
unified_inventory? unified_inventory?
inventory_plus? inventory_plus?
sfinv?
creative? creative?

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 (arbitrary strings may also appear as values) local gui_nodename1 = {} --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_nodename2 = {} --mapping of player names to node names
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,9 +10,7 @@ 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 (arbitrary strings may also appear as values) local gui_filename = {} --mapping of player names to file names
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})
@ -27,8 +25,6 @@ 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_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", "?"}
@ -40,13 +36,13 @@ local angle_values = {90, 180, 270}
setmetatable(angle_indices, {__index = function () return 1 end}) setmetatable(angle_indices, {__index = function () return 1 end})
setmetatable(angle_values, {__index = function () return 90 end}) setmetatable(angle_values, {__index = function () return 90 end})
--given multiple sets of privileges, produces a single set of privs that would have the same effect as requiring all of them at the same time -- given multiple sets of privileges, produces a single set of privs that would have the same effect as requiring all of them at the same time
local combine_privs = function(...) local combine_privs = function(...)
local result = {} local result = {}
for i, privs in ipairs({...}) do for i, privs in ipairs({...}) do
for name, value in pairs(privs) do for name, value in pairs(privs) do
if result[name] ~= nil and result[name] ~= value then --the priv must be both true and false, which can never happen if result[name] ~= nil and result[name] ~= value then --the priv must be both true and false, which can never happen
return {__fake_priv_that_nobody_has__=true} --priviledge table that can never be satisfied return {__fake_priv_that_nobody_has__=true} --privilege table that can never be satisfied
end end
result[name] = value result[name] = value
end end
@ -54,22 +50,44 @@ local combine_privs = function(...)
return result return result
end end
-- display node (or unknown_node image otherwise) at specified pos in formspec
local formspec_node = function(pos, nodename)
return nodename and string.format("item_image[%s;1,1;%s]", pos, nodename)
or string.format("image[%s;1,1;worldedit_gui_unknown.png]", pos)
end
-- two further priv helpers
local function we_privs(command)
return minetest.chatcommands["/" .. command].privs
end
local function combine_we_privs(list)
local args = {}
for _, t in ipairs(list) do
table.insert(args, we_privs(t))
end
return combine_privs(unpack(args))
end
worldedit.register_gui_function("worldedit_gui_about", { worldedit.register_gui_function("worldedit_gui_about", {
name = "About", privs = minetest.chatcommands["/about"].privs, name = "About",
privs = {interact=true},
on_select = function(name) on_select = function(name)
minetest.chatcommands["/about"].func(name, "") minetest.chatcommands["/about"].func(name, "")
end, end,
}) })
worldedit.register_gui_function("worldedit_gui_inspect", { worldedit.register_gui_function("worldedit_gui_inspect", {
name = "Toggle Inspect", privs = minetest.chatcommands["/inspect"].privs, name = "Toggle Inspect",
privs = we_privs("inspect"),
on_select = function(name) on_select = function(name)
minetest.chatcommands["/inspect"].func(name, worldedit.inspect[name] and "disable" or "enable") minetest.chatcommands["/inspect"].func(name, worldedit.inspect[name] and "disable" or "enable")
end, end,
}) })
worldedit.register_gui_function("worldedit_gui_region", { worldedit.register_gui_function("worldedit_gui_region", {
name = "Get/Set Region", privs = combine_privs(minetest.chatcommands["/p"].privs, minetest.chatcommands["/pos1"].privs, minetest.chatcommands["/pos2"].privs, minetest.chatcommands["/reset"].privs, minetest.chatcommands["/mark"].privs, minetest.chatcommands["/unmark"].privs, minetest.chatcommands["/volume"].privs, minetest.chatcommands["/fixedpos"].privs), name = "Get/Set Region",
privs = combine_we_privs({"p", "pos1", "pos2", "reset", "mark", "unmark", "volume", "fixedpos"}),
get_formspec = function(name) get_formspec = function(name)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name] local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
return "size[9,7]" .. worldedit.get_formspec_header("worldedit_gui_region") .. return "size[9,7]" .. worldedit.get_formspec_header("worldedit_gui_region") ..
@ -148,15 +166,15 @@ worldedit.register_gui_handler("worldedit_gui_region", function(name, fields)
end) end)
worldedit.register_gui_function("worldedit_gui_set", { worldedit.register_gui_function("worldedit_gui_set", {
name = "Set Nodes", privs = minetest.chatcommands["/set"].privs, name = "Set Nodes",
privs = we_privs("set"),
get_formspec = function(name) get_formspec = function(name)
local node = gui_nodename1[name] local node = gui_nodename1[name]
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)) ..
"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]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) formspec_node("5.5,1.1", nodename) ..
or "image[5.5,1.1;1,1;unknown_node.png]") ..
"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]"
end, end,
}) })
@ -166,7 +184,10 @@ worldedit.register_gui_handler("worldedit_gui_set", function(name, fields)
gui_nodename1[name] = tostring(fields.worldedit_gui_set_node) gui_nodename1[name] = tostring(fields.worldedit_gui_set_node)
worldedit.show_page(name, "worldedit_gui_set") worldedit.show_page(name, "worldedit_gui_set")
if fields.worldedit_gui_set_submit then if fields.worldedit_gui_set_submit then
minetest.chatcommands["/set"].func(name, gui_nodename1[name]) local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/set"].func(name, n)
end
end end
return true return true
end end
@ -174,19 +195,18 @@ worldedit.register_gui_handler("worldedit_gui_set", function(name, fields)
end) end)
worldedit.register_gui_function("worldedit_gui_replace", { worldedit.register_gui_function("worldedit_gui_replace", {
name = "Replace Nodes", privs = combine_privs(minetest.chatcommands["/replace"].privs, minetest.chatcommands["/replaceinverse"].privs), name = "Replace Nodes",
privs = combine_we_privs({"replace", "replaceinverse"}),
get_formspec = function(name) get_formspec = function(name)
local search, replace = gui_nodename1[name], gui_nodename2[name] local search, replace = gui_nodename1[name], gui_nodename2[name]
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)) ..
"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]" ..
(search_nodename and string.format("item_image[5.5,1.1;1,1;%s]", search_nodename) formspec_node("5.5,1.1", search_nodename) ..
or "image[5.5,1.1;1,1;unknown_node.png]") ..
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)) ..
"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]" ..
(replace_nodename and string.format("item_image[5.5,2.1;1,1;%s]", replace_nodename) formspec_node("5.5,2.1", replace_nodename) ..
or "image[5.5,2.1;1,1;unknown_node.png]") ..
"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]" ..
"button_exit[3.5,3.5;3,0.8;worldedit_gui_replace_submit_inverse;Replace Inverse]" "button_exit[3.5,3.5;3,0.8;worldedit_gui_replace_submit_inverse;Replace Inverse]"
end, end,
@ -198,10 +218,19 @@ worldedit.register_gui_handler("worldedit_gui_replace", function(name, fields)
gui_nodename1[name] = tostring(fields.worldedit_gui_replace_search) gui_nodename1[name] = tostring(fields.worldedit_gui_replace_search)
gui_nodename2[name] = tostring(fields.worldedit_gui_replace_replace) gui_nodename2[name] = tostring(fields.worldedit_gui_replace_replace)
worldedit.show_page(name, "worldedit_gui_replace") worldedit.show_page(name, "worldedit_gui_replace")
local submit = nil
if fields.worldedit_gui_replace_submit then if fields.worldedit_gui_replace_submit then
minetest.chatcommands["/replace"].func(name, string.format("%s %s", gui_nodename1[name], gui_nodename2[name])) submit = "replace"
elseif fields.worldedit_gui_replace_submit_inverse then elseif fields.worldedit_gui_replace_submit_inverse then
minetest.chatcommands["/replaceinverse"].func(name, string.format("%s %s", gui_nodename1[name], gui_nodename2[name])) submit = "replaceinverse"
end
if submit then
local n1 = worldedit.normalize_nodename(gui_nodename1[name])
local n2 = worldedit.normalize_nodename(gui_nodename2[name])
if n1 and n2 then
minetest.chatcommands["/"..submit].func(name, string.format("%s %s", n1, n2))
end
end end
return true return true
end end
@ -209,15 +238,15 @@ worldedit.register_gui_handler("worldedit_gui_replace", function(name, fields)
end) end)
worldedit.register_gui_function("worldedit_gui_sphere_dome", { worldedit.register_gui_function("worldedit_gui_sphere_dome", {
name = "Sphere/Dome", privs = combine_privs(minetest.chatcommands["/hollowsphere"].privs, minetest.chatcommands["/sphere"].privs, minetest.chatcommands["/hollowdome"].privs, minetest.chatcommands["/dome"].privs), name = "Sphere/Dome",
privs = combine_we_privs({"hollowsphere", "sphere", "hollowdome", "dome"}),
get_formspec = function(name) get_formspec = function(name)
local node, radius = gui_nodename1[name], gui_distance2[name] local node, radius = gui_nodename1[name], gui_distance2[name]
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)) ..
"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]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) formspec_node("5.5,1.1", nodename) ..
or "image[5.5,1.1;1,1;unknown_node.png]") ..
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)) ..
"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]" ..
@ -233,14 +262,22 @@ worldedit.register_gui_handler("worldedit_gui_sphere_dome", function(name, field
gui_nodename1[name] = tostring(fields.worldedit_gui_sphere_dome_node) gui_nodename1[name] = tostring(fields.worldedit_gui_sphere_dome_node)
gui_distance2[name] = tostring(fields.worldedit_gui_sphere_dome_radius) 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 = nil
if fields.worldedit_gui_sphere_dome_submit_hollow then if fields.worldedit_gui_sphere_dome_submit_hollow then
minetest.chatcommands["/hollowsphere"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name])) submit = "hollowsphere"
elseif fields.worldedit_gui_sphere_dome_submit_solid then elseif fields.worldedit_gui_sphere_dome_submit_solid then
minetest.chatcommands["/sphere"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name])) submit = "sphere"
elseif fields.worldedit_gui_sphere_dome_submit_hollow_dome then elseif fields.worldedit_gui_sphere_dome_submit_hollow_dome then
minetest.chatcommands["/hollowdome"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name])) submit = "hollowdome"
elseif fields.worldedit_gui_sphere_dome_submit_solid_dome then elseif fields.worldedit_gui_sphere_dome_submit_solid_dome then
minetest.chatcommands["/dome"].func(name, string.format("%s %s", gui_distance2[name], gui_nodename1[name])) submit = "dome"
end
if submit then
local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/"..submit].func(name, string.format("%s %s", gui_distance2[name], n))
end
end end
return true return true
end end
@ -248,20 +285,24 @@ worldedit.register_gui_handler("worldedit_gui_sphere_dome", function(name, field
end) end)
worldedit.register_gui_function("worldedit_gui_cylinder", { worldedit.register_gui_function("worldedit_gui_cylinder", {
name = "Cylinder", privs = combine_privs(minetest.chatcommands["/hollowcylinder"].privs, minetest.chatcommands["/cylinder"].privs), name = "Cylinder",
privs = combine_we_privs({"hollowcylinder", "cylinder"}),
get_formspec = function(name) get_formspec = function(name)
local node, axis, length, radius = gui_nodename1[name], gui_axis1[name], gui_distance1[name], gui_distance2[name] local node, axis, length = gui_nodename1[name], gui_axis1[name], gui_distance1[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,5]" .. worldedit.get_formspec_header("worldedit_gui_cylinder") .. return "size[6.5,6]" .. 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)) ..
"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]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) formspec_node("5.5,1.1", nodename) ..
or "image[5.5,1.1;1,1;unknown_node.png]") ..
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;4,0.8;worldedit_gui_cylinder_radius;Radius;%s]", minetest.formspec_escape(radius)) .. string.format("field[0.5,3.5;2,0.8;worldedit_gui_cylinder_radius1;Base Radius;%s]", minetest.formspec_escape(radius1)) ..
"button_exit[0,4.5;3,0.8;worldedit_gui_cylinder_submit_hollow;Hollow Cylinder]" .. string.format("field[2.5,3.5;2,0.8;worldedit_gui_cylinder_radius2;Top Radius;%s]", minetest.formspec_escape(radius2)) ..
"button_exit[3.5,4.5;3,0.8;worldedit_gui_cylinder_submit_solid;Solid Cylinder]" "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,
}) })
@ -271,58 +312,89 @@ worldedit.register_gui_handler("worldedit_gui_cylinder", function(name, fields)
gui_nodename1[name] = tostring(fields.worldedit_gui_cylinder_node) gui_nodename1[name] = tostring(fields.worldedit_gui_cylinder_node)
gui_axis1[name] = axis_indices[fields.worldedit_gui_cylinder_axis] gui_axis1[name] = axis_indices[fields.worldedit_gui_cylinder_axis]
gui_distance1[name] = tostring(fields.worldedit_gui_cylinder_length) gui_distance1[name] = tostring(fields.worldedit_gui_cylinder_length)
gui_distance2[name] = tostring(fields.worldedit_gui_cylinder_radius) gui_distance2[name] = tostring(fields.worldedit_gui_cylinder_radius1)
gui_distance3[name] = tostring(fields.worldedit_gui_cylinder_radius2)
worldedit.show_page(name, "worldedit_gui_cylinder") worldedit.show_page(name, "worldedit_gui_cylinder")
local submit = nil
if fields.worldedit_gui_cylinder_submit_hollow then if fields.worldedit_gui_cylinder_submit_hollow then
minetest.chatcommands["/hollowcylinder"].func(name, string.format("%s %s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_distance2[name], gui_nodename1[name])) submit = "hollowcylinder"
elseif fields.worldedit_gui_cylinder_submit_solid then elseif fields.worldedit_gui_cylinder_submit_solid then
minetest.chatcommands["/cylinder"].func(name, string.format("%s %s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_distance2[name], gui_nodename1[name])) submit = "cylinder"
end end
if submit then
local n = worldedit.normalize_nodename(gui_nodename1[name])
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, args)
end
end
return true
end
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 return true
end end
return false return false
end) end)
worldedit.register_gui_function("worldedit_gui_pyramid", { worldedit.register_gui_function("worldedit_gui_pyramid", {
name = "Pyramid", privs = minetest.chatcommands["/pyramid"].privs, name = "Pyramid",
privs = we_privs("pyramid"),
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 = gui_nodename1[name], gui_axis1[name], gui_distance1[name]
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)) ..
"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]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) formspec_node("5.5,1.1", nodename) ..
or "image[5.5,1.1;1,1;unknown_node.png]") ..
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) ..
"button_exit[0,3.5;3,0.8;worldedit_gui_pyramid_submit;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]"
end, end,
}) })
worldedit.register_gui_handler("worldedit_gui_pyramid", function(name, fields) worldedit.register_gui_handler("worldedit_gui_pyramid", function(name, fields)
if fields.worldedit_gui_pyramid_search or fields.worldedit_gui_pyramid_submit then 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
gui_nodename1[name] = tostring(fields.worldedit_gui_pyramid_node) gui_nodename1[name] = tostring(fields.worldedit_gui_pyramid_node)
gui_axis1[name] = axis_indices[fields.worldedit_gui_pyramid_axis] gui_axis1[name] = axis_indices[fields.worldedit_gui_pyramid_axis]
gui_distance1[name] = tostring(fields.worldedit_gui_pyramid_length) gui_distance1[name] = tostring(fields.worldedit_gui_pyramid_length)
worldedit.show_page(name, "worldedit_gui_pyramid") worldedit.show_page(name, "worldedit_gui_pyramid")
if fields.worldedit_gui_pyramid_submit then
minetest.chatcommands["/pyramid"].func(name, string.format("%s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], gui_nodename1[name])) local submit = nil
if fields.worldedit_gui_pyramid_submit_solid then
submit = "pyramid"
elseif fields.worldedit_gui_pyramid_submit_hollow then
submit = "hollowpyramid"
end end
if submit then
local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/"..submit].func(name, string.format("%s %s %s", axis_values[gui_axis1[name]], gui_distance1[name], n))
end
end
return true
end
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 return true
end end
return false return false
end) end)
worldedit.register_gui_function("worldedit_gui_spiral", { worldedit.register_gui_function("worldedit_gui_spiral", {
name = "Spiral", privs = minetest.chatcommands["/spiral"].privs, name = "Spiral",
privs = we_privs("spiral"),
get_formspec = function(name) get_formspec = function(name)
local node, length, height, space = gui_nodename1[name], gui_distance1[name], gui_distance2[name], gui_distance3[name] local node, length, height, space = gui_nodename1[name], gui_distance1[name], 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_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)) ..
"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]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) formspec_node("5.5,1.1", nodename) ..
or "image[5.5,1.1;1,1;unknown_node.png]") ..
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)) ..
@ -338,7 +410,10 @@ worldedit.register_gui_handler("worldedit_gui_spiral", function(name, fields)
gui_distance3[name] = tostring(fields.worldedit_gui_spiral_space) gui_distance3[name] = tostring(fields.worldedit_gui_spiral_space)
worldedit.show_page(name, "worldedit_gui_spiral") worldedit.show_page(name, "worldedit_gui_spiral")
if fields.worldedit_gui_spiral_submit then if fields.worldedit_gui_spiral_submit then
minetest.chatcommands["/spiral"].func(name, string.format("%s %s %s %s", gui_distance1[name], gui_distance2[name], gui_distance3[name], gui_nodename1[name])) local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/spiral"].func(name, string.format("%s %s %s %s", gui_distance1[name], gui_distance2[name], gui_distance3[name], n))
end
end end
return true return true
end end
@ -346,7 +421,8 @@ worldedit.register_gui_handler("worldedit_gui_spiral", function(name, fields)
end) end)
worldedit.register_gui_function("worldedit_gui_copy_move", { worldedit.register_gui_function("worldedit_gui_copy_move", {
name = "Copy/Move", privs = combine_privs(minetest.chatcommands["/copy"].privs, minetest.chatcommands["/move"].privs), name = "Copy/Move",
privs = combine_we_privs({"copy", "move"}),
get_formspec = function(name) get_formspec = function(name)
local axis = gui_axis1[name] or 4 local axis = gui_axis1[name] or 4
local amount = gui_distance1[name] or "10" local amount = gui_distance1[name] or "10"
@ -370,11 +446,17 @@ worldedit.register_gui_handler("worldedit_gui_copy_move", function(name, fields)
end end
return true return true
end end
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 return false
end) end)
worldedit.register_gui_function("worldedit_gui_stack", { worldedit.register_gui_function("worldedit_gui_stack", {
name = "Stack", privs = minetest.chatcommands["/stack"].privs, name = "Stack",
privs = we_privs("stack"),
get_formspec = function(name) get_formspec = function(name)
local axis, count = gui_axis1[name], gui_count1[name] local axis, count = gui_axis1[name], gui_count1[name]
return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_stack") .. return "size[6.5,3]" .. worldedit.get_formspec_header("worldedit_gui_stack") ..
@ -392,11 +474,17 @@ worldedit.register_gui_handler("worldedit_gui_stack", function(name, fields)
minetest.chatcommands["/stack"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], gui_count1[name])) minetest.chatcommands["/stack"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], gui_count1[name]))
return true return true
end end
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 return false
end) end)
worldedit.register_gui_function("worldedit_gui_stretch", { worldedit.register_gui_function("worldedit_gui_stretch", {
name = "Stretch", privs = minetest.chatcommands["/stretch"].privs, name = "Stretch",
privs = we_privs("stretch"),
get_formspec = function(name) get_formspec = function(name)
local stretchx, stretchy, stretchz = gui_count1[name], gui_count2[name], gui_count3[name] local stretchx, stretchy, stretchz = gui_count1[name], gui_count2[name], gui_count3[name]
return "size[5,5]" .. worldedit.get_formspec_header("worldedit_gui_stretch") .. return "size[5,5]" .. worldedit.get_formspec_header("worldedit_gui_stretch") ..
@ -420,7 +508,8 @@ worldedit.register_gui_handler("worldedit_gui_stretch", function(name, fields)
end) end)
worldedit.register_gui_function("worldedit_gui_transpose", { worldedit.register_gui_function("worldedit_gui_transpose", {
name = "Transpose", privs = minetest.chatcommands["/transpose"].privs, name = "Transpose",
privs = we_privs("transpose"),
get_formspec = function(name) get_formspec = function(name)
local axis1, axis2 = gui_axis1[name], gui_axis2[name] local axis1, axis2 = gui_axis1[name], gui_axis2[name]
return "size[5.5,3]" .. worldedit.get_formspec_header("worldedit_gui_transpose") .. return "size[5.5,3]" .. worldedit.get_formspec_header("worldedit_gui_transpose") ..
@ -433,18 +522,28 @@ 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)
if fields.worldedit_gui_transpose_submit then if fields.worldedit_gui_transpose_submit then
gui_axis1[name] = axis_indices[fields.worldedit_gui_transpose_axis1] gui_axis1[name] = axis_indices[fields.worldedit_gui_transpose_axis1]
gui_axis2[name] = axis_indices[fields.worldedit_gui_transpose_axis2]
worldedit.show_page(name, "worldedit_gui_transpose") worldedit.show_page(name, "worldedit_gui_transpose")
minetest.chatcommands["/transpose"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], axis_values[gui_axis2[name]])) minetest.chatcommands["/transpose"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], axis_values[gui_axis2[name]]))
return true return true
end end
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 return false
end) end)
worldedit.register_gui_function("worldedit_gui_flip", { worldedit.register_gui_function("worldedit_gui_flip", {
name = "Flip", privs = minetest.chatcommands["/flip"].privs, name = "Flip",
privs = we_privs("flip"),
get_formspec = function(name) get_formspec = function(name)
local axis = gui_axis2[name] local axis = gui_axis1[name]
return "size[5,3]" .. worldedit.get_formspec_header("worldedit_gui_flip") .. return "size[5,3]" .. worldedit.get_formspec_header("worldedit_gui_flip") ..
string.format("dropdown[0,1;2.5;worldedit_gui_flip_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) .. string.format("dropdown[0,1;2.5;worldedit_gui_flip_axis;X axis,Y axis,Z axis,Look direction;%d]", axis) ..
"button_exit[0,2.5;3,0.8;worldedit_gui_flip_submit;Flip]" "button_exit[0,2.5;3,0.8;worldedit_gui_flip_submit;Flip]"
@ -453,16 +552,22 @@ 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)
if fields.worldedit_gui_flip_submit then if fields.worldedit_gui_flip_submit then
gui_axis2[name] = axis_indices[fields.worldedit_gui_flip_axis] gui_axis1[name] = axis_indices[fields.worldedit_gui_flip_axis]
worldedit.show_page(name, "worldedit_gui_flip")
minetest.chatcommands["/flip"].func(name, axis_values[gui_axis1[name]])
return true
end
if fields.worldedit_gui_flip_axis then
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_axis2[name]])
return true return true
end end
return false return false
end) end)
worldedit.register_gui_function("worldedit_gui_rotate", { worldedit.register_gui_function("worldedit_gui_rotate", {
name = "Rotate", privs = minetest.chatcommands["/rotate"].privs, name = "Rotate",
privs = we_privs("rotate"),
get_formspec = function(name) get_formspec = function(name)
local axis, angle = gui_axis1[name], gui_angle[name] local axis, angle = gui_axis1[name], gui_angle[name]
return "size[5.5,3]" .. worldedit.get_formspec_header("worldedit_gui_rotate") .. return "size[5.5,3]" .. worldedit.get_formspec_header("worldedit_gui_rotate") ..
@ -480,15 +585,26 @@ worldedit.register_gui_handler("worldedit_gui_rotate", function(name, fields)
minetest.chatcommands["/rotate"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], angle_values[gui_angle[name]])) minetest.chatcommands["/rotate"].func(name, string.format("%s %s", axis_values[gui_axis1[name]], angle_values[gui_angle[name]]))
return true return true
end end
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 return false
end) end)
worldedit.register_gui_function("worldedit_gui_orient", { worldedit.register_gui_function("worldedit_gui_orient", {
name = "Orient", privs = minetest.chatcommands["/orient"].privs, name = "Orient",
privs = we_privs("orient"),
get_formspec = function(name) get_formspec = function(name)
local angle = gui_angle[name] local angle = gui_angle[name]
return "size[5,3]" .. worldedit.get_formspec_header("worldedit_gui_orient") .. return "size[5,3]" .. worldedit.get_formspec_header("worldedit_gui_orient") ..
string.format("dropdown[0,1;2.5;worldedit_gui_rotate_angle;90 degrees,180 degrees,270 degrees;%s]", angle) .. string.format("dropdown[0,1;2.5;worldedit_gui_orient_angle;90 degrees,180 degrees,270 degrees;%s]", angle) ..
"button_exit[0,2.5;3,0.8;worldedit_gui_orient_submit;Orient]" "button_exit[0,2.5;3,0.8;worldedit_gui_orient_submit;Orient]"
end, end,
}) })
@ -497,36 +613,43 @@ worldedit.register_gui_handler("worldedit_gui_orient", function(name, fields)
if fields.worldedit_gui_orient_submit then if fields.worldedit_gui_orient_submit then
gui_angle[name] = angle_indices[fields.worldedit_gui_orient_angle] 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, angle_values[gui_angle[name]]) minetest.chatcommands["/orient"].func(name, tostring(angle_values[gui_angle[name]]))
return true
end
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 return true
end end
return false return false
end) end)
worldedit.register_gui_function("worldedit_gui_fixlight", { worldedit.register_gui_function("worldedit_gui_fixlight", {
name = "Fix Lighting", privs = minetest.chatcommands["/fixlight"].privs, name = "Fix Lighting",
privs = we_privs("fixlight"),
on_select = function(name) on_select = function(name)
minetest.chatcommands["/fixlight"].func(name, "") minetest.chatcommands["/fixlight"].func(name, "")
end, end,
}) })
worldedit.register_gui_function("worldedit_gui_hide", { worldedit.register_gui_function("worldedit_gui_hide", {
name = "Hide Region", privs = minetest.chatcommands["/hide"].privs, name = "Hide Region",
privs = we_privs("hide"),
on_select = function(name) on_select = function(name)
minetest.chatcommands["/hide"].func(name, "") minetest.chatcommands["/hide"].func(name, "")
end, end,
}) })
worldedit.register_gui_function("worldedit_gui_suppress", { worldedit.register_gui_function("worldedit_gui_suppress", {
name = "Suppress Nodes", privs = minetest.chatcommands["/suppress"].privs, name = "Suppress Nodes",
privs = we_privs("suppress"),
get_formspec = function(name) get_formspec = function(name)
local node = gui_nodename1[name] local node = gui_nodename1[name]
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)) ..
"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]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) formspec_node("5.5,1.1", nodename) ..
or "image[5.5,1.1;1,1;unknown_node.png]") ..
"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]"
end, end,
}) })
@ -536,7 +659,10 @@ worldedit.register_gui_handler("worldedit_gui_suppress", function(name, fields)
gui_nodename1[name] = tostring(fields.worldedit_gui_suppress_node) gui_nodename1[name] = tostring(fields.worldedit_gui_suppress_node)
worldedit.show_page(name, "worldedit_gui_suppress") worldedit.show_page(name, "worldedit_gui_suppress")
if fields.worldedit_gui_suppress_submit then if fields.worldedit_gui_suppress_submit then
minetest.chatcommands["/suppress"].func(name, gui_nodename1[name]) local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/suppress"].func(name, n)
end
end end
return true return true
end end
@ -544,15 +670,15 @@ worldedit.register_gui_handler("worldedit_gui_suppress", function(name, fields)
end) end)
worldedit.register_gui_function("worldedit_gui_highlight", { worldedit.register_gui_function("worldedit_gui_highlight", {
name = "Highlight Nodes", privs = minetest.chatcommands["/highlight"].privs, name = "Highlight Nodes",
privs = we_privs("highlight"),
get_formspec = function(name) get_formspec = function(name)
local node = gui_nodename1[name] local node = gui_nodename1[name]
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)) ..
"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]" ..
(nodename and string.format("item_image[5.5,1.1;1,1;%s]", nodename) formspec_node("5.5,1.1", nodename) ..
or "image[5.5,1.1;1,1;unknown_node.png]") ..
"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]"
end, end,
}) })
@ -562,7 +688,10 @@ worldedit.register_gui_handler("worldedit_gui_highlight", function(name, fields)
gui_nodename1[name] = tostring(fields.worldedit_gui_highlight_node) gui_nodename1[name] = tostring(fields.worldedit_gui_highlight_node)
worldedit.show_page(name, "worldedit_gui_highlight") worldedit.show_page(name, "worldedit_gui_highlight")
if fields.worldedit_gui_highlight_submit then if fields.worldedit_gui_highlight_submit then
minetest.chatcommands["/highlight"].func(name, gui_nodename1[name]) local n = worldedit.normalize_nodename(gui_nodename1[name])
if n then
minetest.chatcommands["/highlight"].func(name, n)
end
end end
return true return true
end end
@ -570,14 +699,16 @@ worldedit.register_gui_handler("worldedit_gui_highlight", function(name, fields)
end) end)
worldedit.register_gui_function("worldedit_gui_restore", { worldedit.register_gui_function("worldedit_gui_restore", {
name = "Restore Region", privs = minetest.chatcommands["/restore"].privs, name = "Restore Region",
privs = we_privs("restore"),
on_select = function(name) on_select = function(name)
minetest.chatcommands["/restore"].func(name, "") minetest.chatcommands["/restore"].func(name, "")
end, end,
}) })
worldedit.register_gui_function("worldedit_gui_save_load", { worldedit.register_gui_function("worldedit_gui_save_load", {
name = "Save/Load", privs = combine_privs(minetest.chatcommands["/save"].privs, minetest.chatcommands["/allocate"].privs, minetest.chatcommands["/load"].privs), name = "Save/Load",
privs = combine_we_privs({"save", "allocate", "load"}),
get_formspec = function(name) get_formspec = function(name)
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") ..
@ -588,9 +719,9 @@ worldedit.register_gui_function("worldedit_gui_save_load", {
end, end,
}) })
worldedit.register_gui_handler("worldedit_gui_save", function(name, fields) worldedit.register_gui_handler("worldedit_gui_save_load", function(name, fields)
if fields.worldedit_gui_save_load_submit_save or worldedit_gui_save_load_submit_allocate or 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_axis) 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
minetest.chatcommands["/save"].func(name, gui_filename[name]) minetest.chatcommands["/save"].func(name, gui_filename[name])
@ -604,25 +735,46 @@ worldedit.register_gui_handler("worldedit_gui_save", function(name, fields)
return false return false
end) end)
worldedit.register_gui_function("worldedit_gui_lua", { worldedit.register_gui_function("worldedit_gui_cube", {
name = "Run Lua", name = "Cuboid", -- technically the command is misnamed, I know...
privs = combine_we_privs({"hollowcube", "cube"}),
get_formspec = function(name) get_formspec = function(name)
local code = gui_code[name] local width, height, length = gui_distance1[name], gui_distance2[name], gui_distance3[name]
return "size[8,6.5]" .. worldedit.get_formspec_header("worldedit_gui_lua") .. local node = gui_nodename1[name]
string.format("textarea[0.5,1;7.5,5.5;worldedit_gui_lua_code;Lua Code;%s]", minetest.formspec_escape(code)) .. local nodename = worldedit.normalize_nodename(node)
"button_exit[0,6;3,0.8;worldedit_gui_lua_run;Run Lua]" .. return "size[6.5,4]" .. worldedit.get_formspec_header("worldedit_gui_cube") ..
"button_exit[5,6;3,0.8;worldedit_gui_lua_transform;Lua Transform]" string.format("field[0.5,1.5;4,0.8;worldedit_gui_cube_node;Name;%s]", minetest.formspec_escape(node)) ..
"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)) ..
"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_lua", function(name, fields) worldedit.register_gui_handler("worldedit_gui_cube", function(name, fields)
if fields.worldedit_gui_lua_run or fields.worldedit_gui_lua_transform then if fields.worldedit_gui_cube_search
gui_code[name] = fields.worldedit_gui_lua_value or fields.worldedit_gui_cube_submit_hollow or fields.worldedit_gui_cube_submit_solid then
worldedit.show_page(name, "worldedit_gui_lua") gui_nodename1[name] = tostring(fields.worldedit_gui_cube_node)
if fields.worldedit_gui_lua_run then gui_distance1[name] = tostring(fields.worldedit_gui_cube_width)
minetest.chatcommands["/lua"].func(name, gui_code[name]) gui_distance2[name] = tostring(fields.worldedit_gui_cube_height)
else --fields.worldedit_gui_lua_transform gui_distance3[name] = tostring(fields.worldedit_gui_cube_length)
minetest.chatcommands["/luatransform"].func(name, gui_code[name]) worldedit.show_page(name, "worldedit_gui_cube")
local submit = nil
if fields.worldedit_gui_cube_submit_hollow then
submit = "hollowcube"
elseif fields.worldedit_gui_cube_submit_solid then
submit = "cube"
end
if submit then
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)
minetest.chatcommands["/"..submit].func(name, args)
end
end end
return true return true
end end
@ -630,28 +782,9 @@ worldedit.register_gui_handler("worldedit_gui_lua", function(name, fields)
end) end)
worldedit.register_gui_function("worldedit_gui_clearobjects", { worldedit.register_gui_function("worldedit_gui_clearobjects", {
name = "Clear Objects", privs = minetest.chatcommands["/clearobjects"].privs, name = "Clear Objects",
privs = we_privs("clearobjects"),
on_select = function(name) on_select = function(name)
minetest.chatcommands["/clearobjects"].func(name, "") minetest.chatcommands["/clearobjects"].func(name, "")
end, end,
}) })
worldedit.register_gui_function("worldedit_gui_formspec_tester", {
name = "Formspec Tester",
get_formspec = function(name)
local value = gui_formspec[name]
return "size[8,6.5]" .. worldedit.get_formspec_header("worldedit_gui_formspec_tester") ..
string.format("textarea[0.5,1;7.5,5.5;worldedit_gui_formspec_tester_value;Formspec Code;%s]", minetest.formspec_escape(value)) ..
"button_exit[0,6;3,0.8;worldedit_gui_formspec_tester_show;Show Formspec]"
end,
})
worldedit.register_gui_handler("worldedit_gui_formspec_tester", function(name, fields)
if fields.worldedit_gui_formspec_tester_show then
gui_formspec[name] = fields.worldedit_gui_formspec_tester_value or ""
worldedit.show_page(name, "worldedit_gui_formspec_tester")
minetest.show_formspec(name, "worldedit:formspec_tester", gui_formspec[name])
return true
end
return false
end)

View File

@ -14,7 +14,7 @@ Use `nil` for the `options` parameter to unregister the function associated with
Use `nil` for the `get_formspec` field to denote that the function does not have its own screen. Use `nil` for the `get_formspec` field to denote that the function does not have its own screen.
Use `nil` for the `privs` field to denote that no special privileges are required to use the function. The `privs` field may not be `nil`.
If the identifier is already registered to another function, it will be replaced by the new one. If the identifier is already registered to another function, it will be replaced by the new one.
@ -24,6 +24,9 @@ The `on_select` function must not call `worldedit.show_page`
worldedit.pages = {} --mapping of identifiers to options worldedit.pages = {} --mapping of identifiers to options
local identifiers = {} --ordered list of identifiers local identifiers = {} --ordered list of identifiers
worldedit.register_gui_function = function(identifier, options) worldedit.register_gui_function = function(identifier, options)
if options.privs == nil or next(options.privs) == nil then
error("privs unset")
end
worldedit.pages[identifier] = options worldedit.pages[identifier] = options
table.insert(identifiers, identifier) table.insert(identifiers, identifier)
end end
@ -46,7 +49,7 @@ worldedit.register_gui_handler = function(identifier, handler)
--ensure the player has permission to perform the action --ensure the player has permission to perform the action
local entry = worldedit.pages[identifier] local entry = worldedit.pages[identifier]
if entry and minetest.check_player_privs(name, entry.privs or {}) then if entry and minetest.check_player_privs(name, entry.privs) then
return handler(name, fields) return handler(name, fields)
end end
return false return false
@ -67,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 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)
@ -100,9 +103,12 @@ if unified_inventory then --unified inventory installed
player:set_inventory_formspec(get_formspec(name, page)) player:set_inventory_formspec(get_formspec(name, page))
end end
end end
elseif 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})
if can_worldedit then
inventory_plus.register_button(player, "worldedit_gui", "WorldEdit") inventory_plus.register_button(player, "worldedit_gui", "WorldEdit")
end
end) end)
--show the form when the button is pressed and hide it when done --show the form when the button is pressed and hide it when done
@ -115,7 +121,7 @@ elseif inventory_plus then --inventory++ installed
return true return true
elseif fields.worldedit_gui_exit then --return to original page elseif fields.worldedit_gui_exit then --return to original page
if gui_player_formspecs[name] then if gui_player_formspecs[name] then
inventory_plus.set_inventory_formspec(player, gui_player_formspecs[name]) inventory_plus.set_inventory_formspec(player, inventory_plus.get_formspec(player, "main"))
end end
return true return true
end end
@ -128,60 +134,35 @@ elseif 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
else --fallback button elseif rawget(_G, "sfinv") then --sfinv installed (part of minetest_game since 0.4.15)
local player_formspecs = {} assert(sfinv.enabled)
local orig_get = sfinv.pages["sfinv:crafting"].get
sfinv.override_page("sfinv:crafting", {
get = function(self, player, context)
local can_worldedit = minetest.check_player_privs(player, {worldedit=true})
local fs = orig_get(self, player, context)
return fs .. (can_worldedit and "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]" or "")
end
})
local update_main_formspec = function(name) --compatibility with pre-0.4.16 sfinv
local formspec = player_formspecs[name] local set_page = sfinv.set_page or function(player, name)
if not formspec then --assumptions: src pg has no leave callback, dst pg has no enter callback
return local ctx = {page=name}
end sfinv.contexts[player:get_player_name()] = ctx
local player = minetest.get_player_by_name(name) sfinv.set_player_inventory_formspec(player, ctx)
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_inventory then --creative_inventory is active, add button to modified formspec
formspec = player:get_inventory_formspec() .. "image_button[6,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
else
formspec = formspec .. "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]"
end
player:set_inventory_formspec(formspec)
end end
minetest.register_on_joinplayer(function(player) --show the form when the button is pressed and hide it when done
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) minetest.register_on_player_receive_fields(function(player, formname, fields)
local name = player:get_player_name()
if fields.worldedit_gui then --main page if fields.worldedit_gui then --main page
gui_player_formspecs[name] = player:get_inventory_formspec() worldedit.show_page(player:get_player_name(), "worldedit_gui")
worldedit.show_page(name, "worldedit_gui")
return true return true
elseif fields.worldedit_gui_exit then --return to original page elseif fields.worldedit_gui_exit then --return to original page
if gui_player_formspecs[name] then set_page(player, "sfinv:crafting")
player:set_inventory_formspec(gui_player_formspecs[name])
end
return true 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
return false
end) end)
worldedit.show_page = function(name, page) worldedit.show_page = function(name, page)
@ -190,10 +171,19 @@ else --fallback button
player:set_inventory_formspec(get_formspec(name, page)) player:set_inventory_formspec(get_formspec(name, page))
end end
end end
else
error(
"worldedit_gui requires a supported \"gui management\" mod to be installed\n"..
"To use the GUI you need to either\n"..
"* Use minetest_game (at least 0.4.15) or a subgame with compatible sfinv\n"..
"* Install Unified Inventory or Inventory++\n"..
"If you do not want to use worldedit_gui, disable it by editing world.mt or from the Main Menu"
)
end end
worldedit.register_gui_function("worldedit_gui", { worldedit.register_gui_function("worldedit_gui", {
name = "WorldEdit GUI", name = "WorldEdit GUI",
privs = {interact=true},
get_formspec = function(name) get_formspec = function(name)
--create a form with all the buttons arranged in a grid --create a form with all the buttons arranged in a grid
local buttons, x, y, index = {}, 0, 1, 0 local buttons, x, y, index = {}, 0, 1, 0
@ -226,7 +216,7 @@ worldedit.register_gui_handler("worldedit_gui", function(name, fields)
for identifier, entry in pairs(worldedit.pages) do --check for WorldEdit GUI main formspec button selection for identifier, entry in pairs(worldedit.pages) do --check for WorldEdit GUI main formspec button selection
if fields[identifier] and identifier ~= "worldedit_gui" then if fields[identifier] and identifier ~= "worldedit_gui" then
--ensure player has permission to perform action --ensure player has permission to perform action
local has_privs, missing_privs = minetest.check_player_privs(name, entry.privs or {}) local has_privs, missing_privs = minetest.check_player_privs(name, entry.privs)
if not has_privs then if not has_privs then
worldedit.player_notify(name, "you are not allowed to use this function (missing privileges: " .. table.concat(missing_privs, ", ") .. ")") worldedit.player_notify(name, "you are not allowed to use this function (missing privileges: " .. table.concat(missing_privs, ", ") .. ")")
return false return false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

View File

@ -1,103 +0,0 @@
worldedit = 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

View File

@ -1 +0,0 @@
worldedit

View File

@ -1,120 +0,0 @@
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

@ -25,12 +25,14 @@ worldedit.alias_chatcommand("/v", "/volume")
worldedit.alias_chatcommand("/s", "/set") worldedit.alias_chatcommand("/s", "/set")
worldedit.alias_chatcommand("/r", "/replace") worldedit.alias_chatcommand("/r", "/replace")
worldedit.alias_chatcommand("/ri", "/replaceinverse") worldedit.alias_chatcommand("/ri", "/replaceinverse")
worldedit.alias_chatcommand("/hcube", "/hollowcube")
worldedit.alias_chatcommand("/hspr", "/hollowsphere") worldedit.alias_chatcommand("/hspr", "/hollowsphere")
worldedit.alias_chatcommand("/spr", "/sphere") worldedit.alias_chatcommand("/spr", "/sphere")
worldedit.alias_chatcommand("/hdo", "/hollowdome") worldedit.alias_chatcommand("/hdo", "/hollowdome")
worldedit.alias_chatcommand("/do", "/dome") worldedit.alias_chatcommand("/do", "/dome")
worldedit.alias_chatcommand("/hcyl", "/hollowcylinder") worldedit.alias_chatcommand("/hcyl", "/hollowcylinder")
worldedit.alias_chatcommand("/cyl", "/cylinder") worldedit.alias_chatcommand("/cyl", "/cylinder")
worldedit.alias_chatcommand("/hpyr", "/hollowpyramid")
worldedit.alias_chatcommand("/pyr", "/pyramid") worldedit.alias_chatcommand("/pyr", "/pyramid")
worldedit.alias_chatcommand("/spl", "/spiral") worldedit.alias_chatcommand("/spl", "/spiral")
worldedit.alias_chatcommand("/m", "/move") worldedit.alias_chatcommand("/m", "/move")