1
0
mirror of https://github.com/Uberi/Minetest-WorldEdit.git synced 2025-06-29 22:50:46 +02:00

104 Commits
0.5 ... 1.0

Author SHA1 Message Date
03327a767d Add version checker since so many people are confused about which version to use. 2013-08-06 16:28:05 -04:00
8bd5db51b2 Final changes and version number bump. 2013-08-01 23:53:38 -04:00
416ffb3709 Fix //pyramid examples. 2013-07-31 22:29:01 -04:00
040282bbe6 New module loader properly halts when load-time error occurs in module. 2013-07-31 22:15:52 -04:00
b0bf52e9b6 Rewrite spirals from scratch and fix upside-down pyramids. Use voxelmanip for markers to ensure area is emerged. 2013-07-31 22:15:08 -04:00
3c51ec8c4a //pyramid works along any axis and backwards. Working //clearobjects and //cylinder command, begin implementing super marker. Remove EnvRef usages and the block queue (the block queue does not work with VoxelManips). More block emergers. 2013-07-31 00:02:37 -04:00
49b683f27f Support negative values to make upside-down domes and pyramids (slight changes to worldedit.pyramid interface for coming changes). Add experimental //clearobjects, make node inspector work per-player. 2013-07-29 12:43:24 -04:00
c1f3cfc1e4 Fix bugs in visualization API and make it ore robust. Fix bugs in //fixedpos, //suppress, and //highlight. 2013-07-28 18:15:46 -04:00
b252df2166 Slight optimizations, and make //pyramid work properly again. 2013-07-28 17:08:12 -04:00
7e1be63344 Add //fixedpos command and document it. 2013-07-28 17:07:47 -04:00
faa4a7b065 More speedups for //hollowsphere, //sphere, //hollowdome, //dome, //cylinder, better module loader, fix message in //volume. 2013-07-27 21:34:42 -04:00
8b9054a1cc Add tutorial for new users, and a note about compatibility. 2013-07-25 16:09:00 -04:00
dfcf02bbc2 Add a few notes on air. 2013-07-22 11:04:03 -04:00
0fddd17f23 Add the node inspector, a tool for finding information about nodes. Configurable via the //inspect command. 2013-07-21 17:16:29 -04:00
e550604747 Add a section about the different types of schematics. 2013-07-21 17:15:04 -04:00
8ebf9d3c2a Super duper VoxelManipulator speedups to nearly every API function, and plus support for unloaded areas. Still in progress. Also, fix //allocate for very large schematics. 2013-07-21 16:54:25 -04:00
ac5e801834 Even bigger speed gains by using LuaVoxelManipulator in a few choice places! Faster //set, //cylinder, etc., but plenty of room for improvements still. 2013-07-12 14:22:30 -04:00
b6bc841c39 Fix volume of spheres, domes, and their hollow variants. 2013-06-24 18:22:01 -04:00
48009b6c62 Support aliases in node name resolution. For example, //set mapgen_dirt. Technique outlined by kharl in IRC. 2013-06-24 17:13:10 -04:00
9db6192eba Avoid using slower and deprecated EnvRef, fix schematic data MapNodes, huge speed boost to worldedit.hollow_cylinder, fix some bugs. 2013-06-23 12:46:59 -04:00
1e5f623cbb Super-speed a few primitives and visualization functions, including hollowsphere, sphere, hollowdome, dome, and suppress. 2013-06-22 23:08:51 -04:00
58970e7fab Clarify the documentation regarding the the positioning of WorldEdit primitives relative to position 1. 2013-06-22 23:05:34 -04:00
7070f81c59 Significant but highly experimental speed-ups for worldedit.set, replace, and scale, plus preparation for more. 2013-06-22 20:59:23 -04:00
a192d51a58 Add initial support for Minetest schematic API
Thanks to kwolekr for the code
2013-06-22 08:25:59 +02:00
d4187866db Add //scale <factor> command (suggested by Jordach), fix transposition description in docs. 2013-06-18 15:05:49 -04:00
74018dab99 Support descriptions and partial node names in chat commands (e.g., //set blue lightstone, //replace piston_normal_off bronze block). 2013-06-12 19:59:13 -04:00
f1cfc47189 Fix //rotate for real this time. 2013-06-02 17:50:14 -04:00
0664cde287 Fix //rotate, improve queue (order now preserved), typos fixed (quene -> queue). 2013-05-30 20:38:56 -04:00
f952ee4740 Block queue doesn't lag anymore on slow operations like digging and placing nodes, better and more efficient //fixlight. 2013-05-30 19:13:21 -04:00
a1dcc43210 Fix //move again 2013-05-20 20:48:12 +02:00
d7ae19b7b8 Fix the //move command (WorldEdit API is unaffected). 2013-05-18 19:00:12 -04:00
52c1379bce Make chat messages consistent. 2013-05-15 17:52:12 -04:00
d52f3d649e Add //unmark chat command, fix position setting not being reset by //reset. 2013-05-15 17:03:26 -04:00
cedecac3f8 Merge remote-tracking branch 'refs/remotes/origin/master' 2013-05-02 18:20:59 -04:00
18bc4e6815 Fix deserialization for real this time. 2013-04-29 19:52:00 -04:00
9e1c2614d3 Update Copyright Year 2013-04-29 14:34:55 +03:00
98c5bc5c5f Fix loading of schematics containing chests that had empty slots. 2013-04-28 19:01:24 -04:00
522edf1cd4 WorldEdit 0.6 2013-04-28 20:27:25 +02:00
5e5c1dc6c3 Add Block Queue 2013-04-28 19:08:09 +02:00
71b6004b92 New commands //hollowdome and //dome, as well as new API functions worldedit.dome and worldedit.hollow_dome. Oh, and spheres generate faster too. 2013-04-27 18:28:20 -04:00
20722389bd Avoid prepending "Server -!- " to WorldEdit messages. 2013-04-27 16:42:51 -04:00
f5145d6ba1 Horrible, vile, disgusting hack. But it works. 2013-04-20 20:28:21 -04:00
0317deb101 Metadata fix for serialization. 2013-03-27 18:07:52 -04:00
65c218c3e2 Dynamic module loading - you can now delete any file in the worldedit mod except init.lua, and the relevant functionality will simply not be included. 2013-03-20 17:31:00 -04:00
d1828affff Oops, forgot to load the compatibility module. 2013-03-20 17:18:21 -04:00
9209d81d20 //metasave and //metaload are now superceded by //save and //load's new functionality. worldedit.deserialize now supports every version of the WorldEdit format that has ever existed, and the new worldedit.valueversion uses file characteristics to determine which type of file format a given file uses. The new WorldEdit file format is the same as the one used by MineTest for serializing data, and is capable of storing arbitrary data, as well as leaving fields for future improvements. In other words, this is the last forward-compatibility breaking change that will be made to the file format. 2013-03-20 17:12:48 -04:00
34c4475d06 Fix styling issues in Chat Commands.md 2013-02-23 09:35:16 +01:00
7eb102181b Fixed bug where unmodified axis used pos1 coords instead of pos2 coords. 2013-02-17 14:11:55 -05:00
4a98773025 Use rounding rather than flooring for more intuitive player position setting. 2013-02-16 21:14:44 -05:00
cfaed1781a Require server priv for Lua commands. 2013-01-14 16:14:54 -05:00
7cf84045d4 Replace //homogenize with //replaceinverse (//homogenize x is equivalent to //replaceinverse air x), add documentation for it. 2013-01-12 18:29:57 -05:00
e2f1c4ef17 Add //homogenize, //lua, and //luatransform commands, as well as their documentation. 2013-01-12 18:20:41 -05:00
c27ab877f1 New command: //orient, that rotates oriented nodes such as furnaces around the Y-axis by a specified angle. 2013-01-12 16:46:40 -05:00
7cb2df24b8 Change all references of //dig to //fixlight, document the changes, fix a small typo. 2013-01-12 16:02:23 -05:00
6fb039fb9d Finally fix anything 2013-01-12 08:24:04 +01:00
0003770ffb Fix my Code again (I've use Python too much) 2013-01-12 08:19:24 +01:00
9f35891996 Fix Code again 2013-01-12 08:17:14 +01:00
6f3127f32e Fix Code 2013-01-12 08:14:20 +01:00
5f1fb56b8b Rename //dig to //fixlight 2013-01-12 08:10:19 +01:00
89e18d6108 Only dig air Nodes 2013-01-12 07:59:41 +01:00
e5331d39ae Many thanks to Smitje for providing a working example of roations.
Changes: fixed rotation and transposition of non-sqaure regions, makers are now moved to the new region boundaries after rotation/transposition, small consistency fixes, finally fix the /hide command.
2012-12-12 17:17:56 -05:00
f087c3ef71 Fix the missing command //suppress <node>. 2012-11-27 19:29:10 -05:00
426dfb0bc3 Quick and dirty fix for metasave not saving ItemStack instances in node metadata inventories. 2012-10-23 23:21:02 -04:00
fdca506505 Fix //load and //allocate for old WorldEdit files. 2012-10-22 23:17:37 -04:00
aa4a70d237 Rename //find to //highlight and worldedit.find to worldedit.highlight, add //allocate and worldedit.allocate as well as related documentation. Improve documentation by fixing a few mistakes and adding return signatures. 2012-10-21 17:49:43 -04:00
5f598db3e6 Implement and document the new //p set1 and //p set2 functionality. 2012-10-18 21:28:58 -04:00
4f0ffe731f Merge remote-tracking branch 'origin/master' 2012-10-18 21:28:45 -04:00
e9cdf616ec Test 2012-10-18 21:28:33 -04:00
5585b7d242 Merge pull request #19 from khonkhortisan/master
Hollow Sphere Returning Count
2012-10-17 17:32:56 -07:00
a76ee00391 worldedit.hollow_sphere returns non-nil count 2012-10-17 16:18:19 -07:00
1c31bd7f0d Separate components into separate mods, add visualization API with hide(), suppress(), find(), and restore() for nondestructive node visualization. Corresponding chat commands are //hide, //suppress, //find, and //restore. Commands and functions documented. 2012-10-13 21:45:50 -04:00
1b13538353 dofile table_save so metasave and metaload work 2012-10-04 13:22:18 +09:30
12dfbd198d Use modpack for easier installation (idea is taken from cornernote's mod format), fix some grammar in API reference, move chat command reference and API reference to separate files. 2012-10-01 16:20:20 -04:00
a82ab9176f Fix worldedit.spiral and the correspondign chat command, //spiral. 2012-09-26 18:36:11 -04:00
382c57d008 Replace worldedit.spiral with worldedit.pyramid, as well as related chat commands. 2012-09-26 18:02:42 -04:00
def676cd2d Add worldedit.hollow_sphere, worldedit.sphere, //hollowsphere, and //sphere, improve cylinder docs slightly. 2012-09-25 18:42:10 -04:00
d89fded397 Fix negative cylinder lengths (thanks @cornernote). 2012-09-25 17:04:24 -04:00
724cd6ccb6 Add cornernote to authors. 2012-09-20 16:25:29 -04:00
f42d2358fd Merge pull request #12 from cornernote/master
added //metasave and //metaload (clean pull)
2012-09-20 13:13:09 -07:00
474130d188 added table_save to load/save metadata 2012-09-20 14:00:20 +09:30
a95164b63b added metasave/metaload to readme 2012-09-20 13:58:00 +09:30
0f9f247cc1 added metasave/metaload to readme 2012-09-20 13:57:01 +09:30
7ce20dbfce added metasave/metaload to chat commands 2012-09-20 13:56:14 +09:30
c8cfd524dd added metasave/metaload to api 2012-09-20 13:55:23 +09:30
6f96131382 Normalization. 2012-09-13 23:13:54 -04:00
8102eb982e Merge pull request #11 from khonkhortisan/master
Allow multiple //moves
2012-09-10 17:06:34 -07:00
93100a20be document the //move change 2012-09-10 10:09:03 -07:00
17c1e3887c allow multiple //moves 2012-09-09 21:44:30 -07:00
a429e104a2 Fix //replace <something> air 2012-09-08 08:34:21 +02:00
ce9ce35a20 Fix //replace air <something> 2012-09-08 08:21:37 +02:00
a25a2a1fb0 Merge pull request #5 from khonkhortisan/master
Understands meta!
2012-08-30 21:38:00 -07:00
340416c789 Fix player axis detection, make spirals code a bit easier to read. 2012-08-30 16:17:15 -04:00
6eb08edd30 Rotate now works with x/y/z/? instead of just y: "//rotate x 90" 2012-08-28 15:57:45 -07:00
91c24d3a60 Fix transpose and rotate 2012-08-26 13:14:47 -07:00
1a9ce8b9c1 Understands meta! Tested in copy, stack (which copies), move, flip.
Transpose (and therefore rotate) do(es) not appear to be functional, but
meta stuff added anyway.
2012-08-25 17:23:39 -07:00
21192ceb57 Remove unused Code 2012-08-19 09:38:16 +02:00
e39493e789 Add //spiral and worldedit.spiral API function 2012-08-19 09:36:11 +02:00
5b66b5ec25 Add the ? axis, which represents the axis the player is facing. 2012-08-18 14:43:12 -04:00
66c7f1fb96 Add //hollowcylinder and //cylinder commands, add worldedit.hollow_cylinder and worldedit.cylinder API functions, document both, use better node validity check. 2012-08-18 00:51:20 -04:00
87f23b8647 Fix //copy, //move, and //stack with negative amounts. 2012-08-16 16:21:00 -04:00
8cd32bcc59 Add the //transpose, //flip, and //rotate commands as well as their documentation and related WorldEdit API functions. Fix chat parameter handling using pattern anchors. 2012-07-22 18:12:29 -04:00
a4a3613170 Remove WorldEdit markers if they are invalid. 2012-07-22 15:55:49 -04:00
9067f73349 Add //mark command and document it. 2012-07-22 15:22:36 -04:00
5ce4517e66 Use minetest.pos_to_string() and check for nil positions when using //p get (possible crash fixed). 2012-07-22 14:53:20 -04:00
20 changed files with 3527 additions and 820 deletions

332
Chat Commands.md Normal file
View File

@ -0,0 +1,332 @@
Chat Commands
-------------
For more information, see the [README](README.md).
### //inspect
Enable or disable node inspection.
//inspect on
//inspect off
//inspect 1
//inspect 0
//inspect true
//inspect false
//inspect yes
//inspect no
//inspect enable
//inspect disable
### //reset
Reset the region so that it is empty.
//reset
### //mark
Show markers at the region positions.
//mark
### //unmark
Hide markers if currently shown.
//unmark
### //pos1
Set WorldEdit region position 1 to the player's location.
//pos1
### //pos2
Set WorldEdit region position 2 to the player's location.
//pos2
### //p set/set1/set2/get
Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region.
//p set
//p set1
//p set2
//p get
### //fixedpos set1 x y z
Set a WorldEdit region position to the position at (<x>, <y>, <z>).
//fixedpos set1 0, 0, 0
//fixedpos set1 -30, 5, 28
//fixedpos set2 1004, -200, 432
### //volume
Display the volume of the current WorldEdit region.
//volume
### //set <node>
Set the current WorldEdit region to <node>.
//set air
//set cactus
//set Bronze Block
//set mesecons:wire_00000000_off
### //replace <search node> <replace node>
Replace all instances of <search node> with <replace node> in the current WorldEdit region.
//replace Cobblestone air
//replace lightstone_blue glass
//replace dirt Bronze Block
//replace mesecons:wire_00000000_off flowers:flower_tulip
### //replaceinverse <search node> <replace node>
Replace all nodes other than <search node> with <replace node> in the current WorldEdit region.
//replaceinverse Cobblestone air
//replaceinverse flowers:flower_waterlily glass
//replaceinverse dirt Bronze Block
//replaceinverse mesecons:wire_00000000_off flowers:flower_tulip
### //hollowsphere <radius> <node>
Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>.
//hollowsphere 5 Diamond Block
//hollowsphere 12 glass
//hollowsphere 17 mesecons:wire_00000000_off
### //sphere <radius> <node>
Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>.
//sphere 5 Diamond Block
//sphere 12 glass
//sphere 17 mesecons:wire_00000000_off
### //hollowdome <radius> <node>
Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>.
//hollowdome 5 Diamond Block
//hollowdome -12 glass
//hollowdome 17 mesecons:wire_00000000_off
### //dome <radius> <node>
Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>.
//dome 5 Diamond Block
//dome -12 glass
//dome 17 mesecons:wire_00000000_off
### //hollowcylinder x/y/z/? <length> <radius> <node>
Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>.
//hollowcylinder x +5 8 Bronze Block
//hollowcylinder y 28 10 glass
//hollowcylinder z -12 3 mesecons:wire_00000000_off
//hollowcylinder ? 2 4 default:stone
### //cylinder x/y/z/? <length> <radius> <node>
Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>.
//cylinder x +5 8 Bronze Block
//cylinder y 28 10 glass
//cylinder z -12 3 mesecons:wire_00000000_off
//cylinder ? 2 4 default:stone
### //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>.
//pyramid x 8 Diamond Block
//pyramid y -5 glass
//pyramid z 2 mesecons:wire_00000000_off
//pyramid ? 12 mesecons:wire_00000000_off
### //spiral <length> <height> <spacer> <node>
Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <spacer>, composed of <node>.
//spiral 20 5 3 Diamond Block
//spiral 5 2 1 glass
//spiral 7 1 5 mesecons:wire_00000000_off
### //copy x/y/z/? <amount>
Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes.
//copy x 15
//copy y -7
//copy z +4
//copy ? 8
### //move x/y/z/? <amount>
Move the current WorldEdit positions and region along the x/y/z/? axis by <amount> nodes.
//move x 15
//move y -7
//move z +4
//move ? -1
### //stack x/y/z/? <count>
Stack the current WorldEdit region along the x/y/z/? axis <count> times.
//stack x 3
//stack y -1
//stack z +5
//stack ? 12
### //scale <factor>
Scale the current WorldEdit positions and region by a factor of positive integer <factor> with position 1 as the origin.
//scale 2
//scale 1
//scale 10
### //transpose x/y/z/? x/y/z/?
Transpose the current WorldEdit positions and region along the x/y/z/? and x/y/z/? axes.
//transpose x y
//transpose x z
//transpose y z
//transpose ? y
### //flip x/y/z/?
Flip the current WorldEdit region along the x/y/z/? axis.
//flip x
//flip y
//flip z
//flip ?
### //rotate x/y/z/? <angle>
Rotate the current WorldEdit positions and region along the x/y/z/? axis by angle <angle> (90 degree increment).
//rotate x 90
//rotate y 180
//rotate z 270
//rotate ? -90
### //orient <angle>
Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)
//orient 90
//orient 180
//orient 270
//orient -90
### //fixlight
Fixes the lighting in the current WorldEdit region.
//fixlight
### //hide
Hide all nodes in the current WorldEdit region non-destructively.
//hide
### //suppress <node>
Suppress all <node> in the current WorldEdit region non-destructively.
//suppress Diamond Block
//suppress glass
//suppress mesecons:wire_00000000_off
### //highlight <node>
Highlight <node> in the current WorldEdit region by hiding everything else non-destructively.
//highlight Diamond Block
//highlight glass
//highlight mesecons:wire_00000000_off
### //restore
Restores nodes hidden with WorldEdit in the current WorldEdit region.
//restore
### //save <file>
Save the current WorldEdit region to "(world folder)/schems/<file>.we".
//save some random filename
//save huge_base
### //allocate <file>
Set the region defined by nodes from "(world folder)/schems/<file>.we" as the current WorldEdit region.
//allocate some random filename
//allocate huge_base
### //load <file>
Load nodes from "(world folder)/schems/<file>.we" with position 1 of the current WorldEdit region as the origin.
//load some random filename
//load huge_base
### //lua <code>
Executes <code> as a Lua chunk in the global namespace.
//lua worldedit.pos1["singleplayer"] = {x=0, y=0, z=0}
//lua worldedit.rotate(worldedit.pos1["singleplayer"], worldedit.pos2["singleplayer"], "y", 90)
### //luatransform <code>
Executes <code> as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region.
//luatransform minetest.add_node(pos, {name="default:stone"})
//luatransform if minetest.get_node(pos).name == "air" then minetest.add_node(pos, {name="default:water_source"})
### //mtschemcreate <file>
Save the current WorldEdit region using the Minetest Schematic format to "(world folder)/schems/<file>.mts".
//mtschemcreate some random filename
//mtschemcreate huge_base
### //mtschemplace <file>
Load nodes from "(world folder)/schems/<file>.mts" with position 1 of the current WorldEdit region as the origin.
//mtschemplace some random filename
//mtschemplace huge_base
### //mtschemprob start/finish/get
After using //mtschemprob start all nodes punched will bring up a text field where a probablity can be entered.
This mode can be left with //mtschemprob finish. //mtschemprob get will display the probabilities saved for the nodes.
//mtschemprob get
### //clearobjects
Clears all objects within the WorldEdit region.
//clearobjects

234
README.md
View File

@ -1,8 +1,10 @@
WorldEdit v0.5 for MineTest 0.4 WorldEdit v1.0 for MineTest 0.4.8+
=============================== ==================================
In-game world editing for [MineTest](http://minetest.net/)! Tons of chat commands 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](http://minetest.net/forum/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.
# New users should see the [tutorial](Tutorial.md).
Usage Usage
----- -----
@ -12,9 +14,53 @@ WorldEdit has a huge potential for abuse by untrusted players. Therefore, users
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.
Chat Commands
-------------
WorldEdit is accessed in-game through an interface. By default, the mod distribution includes a chat interface for this purpose. It is documented in the [Chat Commands Reference](Chat Commands.md).
If visual manipulation of nodes is desired, the [WorldEdit GUI](https://forum.minetest.net/viewtopic.php?id=3112) mod provides a simple interface with buttons and text entry fields for this purpose.
Compatibility
-------------
This mod supports Minetest versions 0.4.8 and newer. Older versions of WorldEdit may work with older versions of Minetest, but are not recommended.
WorldEdit works quite well with other mods, and does not have any known mod conflicts.
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.
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:
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.
This API is documented in the [WorldEdit API Reference](WorldEdit API.md).
Axes
----
The coordinate system is the same as that used by MineTest; Y is upwards, X is perpendicular, and Z is parallel.
When an axis is specified in a WorldEdit command, it is specified as one of the following values: x, y, z, or ?.
The value ? represents the axis the player is currently facing. If the player is facing more than one axis, the axis the player face direction is closest to will be used.
Nodes
-----
Node names are required for many types of commands that identify or modify specific types of nodes. They can be specified in a number of ways.
First, by description - the tooltip that appears when hovering over the item in an inventory. This is case insensitive and includes values such as "Cobblestone" and "bronze block". Note that certain commands (namely, `//replace` and `//replaceinverse`) do not support descriptions that contain spaces in the `<searchnode>` field.
Second, by name - the node name that is defined by code, but without the mod name prefix. This is case sensitive and includes values such as "piston_normal_off" and "cactus". Nodes defined in the `default` mod always take precedence over other nodes when searching for the correct one, and if there are multiple possible nodes (such as "a:celery" and "b:celery"), one is chosen in no particular order.
Finally, by full name - the unambiguous identifier of the node, prefixes and all. This is case sensitive and includes values such as "default:stone" and "mesecons:wire_00000000_off".
The node name "air" can be used anywhere a normal node name can, and acts as a blank node. This is useful for clearing or removing nodes. For example, `//set air` would remove all the nodes in the current WorldEdit region. Similarly, `//sphere 10 air`, when WorldEdit position 1 underground, would dig a large sphere out of the ground.
Regions Regions
------- -------
Most WorldEdit commands operate on regions. Regions are a set of two positions that define a 3D cube. They are local to each player and chat commands affect only the region for the player giving the commands. Most WorldEdit commands operate on regions. Regions are a set of two positions that define a 3D cuboid. They are local to each player and chat commands affect only the region for the player giving the commands.
Each positions together define two opposing corners of the cube. With two opposing corners it is possible to determine both the location and dimensions of the region. Each positions together define two opposing corners of the cube. With two opposing corners it is possible to determine both the location and dimensions of the region.
@ -26,173 +72,39 @@ Entities are used to mark the location of the WorldEdit regions. They appear as
To remove the entities, simply punch them. This does not reset the positions themselves. To remove the entities, simply punch them. This does not reset the positions themselves.
Commands Schematics
-------- ----------
WorldEdit supports two different types of schematics.
### //reset The first is the WorldEdit Schematic format, with the file extension ".we", and in some older versions, ".wem". There have been several previous versions of the WorldEdit Schematic format, but WorldEdit is capable of loading any past versions, and will always support them - there is no need to worry about schematics becoming obselete.
Reset the region so that it is empty. The current version of the WorldEdit Schematic format, internally known as version 4, is essentially an array of node data tables in Lua 5.2 table syntax. Specifically:
//reset return {
{
["y"] = <y-axis coordinate>,
["x"] = <x-axis coordinate>,
["name"] = <node name>,
["z"] = <z-axis coordinate>,
["meta"] = <metadata table>,
["param2"] = <param2 value>,
["param1"] = <y-axis coordinate>,
},
<...>
}
### //pos1 Value ordering and minor aspects of the syntax, such as trailing commas or newlines, are not guaranteed.
Set WorldEdit region position 1 to the player's location. The WorldEdit Schematic format is accessed via the WorldEdit API, or WorldEdit serialization chat commands such as `//serialize` and `//deserialize`.
//pos1 The second is the Minetest Schematic format (MTS). The details of this format may be found in the Minetest documentation and are out of the scope of this document. Access to this format is done via specialized MTS commands such as `//mtschemcreate` and `//mtschemplace`.
### //pos2
Set WorldEdit region position 2 to the player's location.
//pos2
### //p set/get
Set WorldEdit region by punching two nodes, or display the current WorldEdit region.
//p set
//p get
### //volume
Display the volume of the current WorldEdit region.
//volume
### //set <node>
Set the current WorldEdit region to <node>.
//set dirt
//set default:glass
//set mesecons:mesecon
### //replace <search node> <replace node>
Replace all instances of <search node> with <place node> in the current WorldEdit region.
//replace cobble stone
//replace default:steelblock glass
//replace dirt flowers:flower_waterlily
//replace flowers:flower_rose flowers:flower_tulip
### //copy x/y/z <amount>
Copy the current WorldEdit region along the x/y/z axis by <amount> nodes.
//copy x 15
//copy y -7
//copy z +4
### //move x/y/z <amount>
Move the current WorldEdit region along the x/y/z axis by <amount> nodes.
//move x 15
//move y -7
//move z +4
### //stack x/y/z <count>
Stack the current WorldEdit region along the x/y/z axis <count> times.
//stack x 3
//stack y -1
//stack z +5
### //dig
Dig the current WorldEdit region.
//dig
### //save <file>
Save the current WorldEdit region to "(world folder)/schems/<file>.we".
//save some random filename
//save huge_base
### //load <file>
Load nodes from "(world folder)/schems/<file>.we" with position 1 of the current WorldEdit region as the origin.
//load some random filename
//load huge_base
WorldEdit API
-------------
WorldEdit exposes all significant functionality in a simple interface. Adding WorldEdit to the file "depends.txt" in your mod gives you access to all of the `worldedit` functions. These are useful if you're looking for high-performance node manipulation without all the hassle of writing tons of code.
### worldedit.volume(pos1, pos2)
Determines the volume of the region defined by positions `pos1` and `pos2`.
Returns the volume.
### worldedit.set(pos1, pos2, nodename)
Sets a region defined by positions `pos1` and `pos2` to `nodename`. To clear to region, use "air" as the value of `nodename`.
Returns the number of nodes set.
### worldedit.replace(pos1, pos2, searchnode, replacenode)
Replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`.
Returns the number of nodes replaced.
### worldedit.copy(pos1, pos2, axis, amount)
Copies the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes.
Returns the number of nodes copied.
### 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.
Returns the number of nodes moved.
### worldedit.stack(pos1, pos2, axis, count)
duplicates the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") `count` times.
Returns the number of nodes stacked.
### worldedit.dig(pos1, pos2)
Digs a region defined by positions `pos1` and `pos2`.
Returns the number of nodes dug.
### worldedit.serialize(pos1, pos2)
Converts the region defined by positions `pos1` and `pos2` into a single string.
Returns the serialized data and the number of nodes serialized.
### worldedit.deserialize(originpos, value)
Loads the nodes represented by string `value` at position `originpos`.
Returns the number of nodes deserialized.
### worldedit.deserialize_old(originpos, value)
Loads the nodes represented by string `value` at position `originpos`, using the older table-based WorldEdit format.
This function is deprecated, and should not be used unless there is a need to support legacy WorldEdit save files.
Returns the number of nodes deserialized.
License License
------- -------
Copyright 2012 sfan5 and Anthony Zhang (Temperest) Copyright 2013 sfan5, Anthony Zhang (Uberi/Temperest), and Brett O'Donnell (cornernote).
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.

55
Tutorial.md Normal file
View File

@ -0,0 +1,55 @@
WorldEdit Tutorial
==================
This is a step-by-step tutorial outlining the basic usage of WorldEdit. For more information, see the [README](README.md).
Let's start with a few assumptions:
* You have a compatible version of Minetest working.
* See the [README](README.md) for compatibility information.
* You have WorldEdit installed as a mod.
* If using Windows, [MODSTER](https://forum.minetest.net/viewtopic.php?pid=101463) makes installing mods totally painless.
* Simply download the file, extract the archive, and move it to the correct mod folder for Minetest.
* You are familiar with the basics of the game.
* How to walk, jump, and climb.
* How to dig, place, and punch blocks.
* How to type into the chat and read text from it.
Overview
--------
WorldEdit has a "region", which is simply a cuboid area defined by two markers, both of which the player can move around. Every player can have their own region with their own two markers.
WorldEdit chat commands can work inside the region selected, or around the first marker.
Step 1: Selecting a region
--------------------------
In the chat prompt, enter `//p set`. In the chat, you are prompted to punch two nodes to set the positions of the two markers.
Punch a nearby node. Be careful of breakable ones such as torches. A black cube reading "1" will appear around the node. This is the marker for WorldEdit position 1.
Walk away from the node you just punched. Now, punch another node. A black cube reading "2" will appear around the node. This is the marker for WorldEdit position 2.
Step 2: Region commands
-----------------------
In the chat prompt, enter `//set mese`. In the chat, you will see a message showing the number of nodes set after a small delay.
Look at the place between the two markers: it is now filled with MESE blocks!
The `//set <node>` command fills the region with whatever node you want. It is a region-oriented command, which means it works inside the WorldEdit region only.
Now, try a few different variations, such as `//set torch`, `//set cobble`, and `//set water`.
Step 3: Position commands
-------------------------
In the chat prompt, enter `//hollowdome 30 glass`. In the chat, you will see a message showing the number of nodes set after a small delay.
Look around marker 1: it is now surrounded by a hollow glass dome!
The `//hollowdome <radius> <node>` command creates a hollow dome centered around marker 1, made of any node you want. It is a position-oriented command, which means it works around marker 1 and can go outside the WorldEdit region.
Step 4: Other commands
----------------------
There are many more commands than what is shown here. See the [Chat Commands Reference](Chat Commands.md) for a detailed list of them, along with descriptions and examples for every single one.
If you're in-game and forgot how a command works, just use the `/help <command name>` command, without the first forward slash. For example, to see some information about the `//set <node>` command mentioned earlier, simply use `/help /set`.
A very useful command to check out is the `//save <schematic>` command, which can save everything inside the WorldEdit region to a file, stored on the computer hosting the server (the player's computer, in single player mode). You can then later use `//load <schematic>` to load the data in a file into a world, even another world on another computer.

219
WorldEdit API.md Normal file
View File

@ -0,0 +1,219 @@
WorldEdit API
=============
The WorldEdit API is composed of multiple modules, each of which is independent and can be used without the other. Each module is contained within a single file.
If needed, individual modules such as visualization.lua can be removed without affecting the rest of the program. The only file that cannot be removed is init.lua, which is necessary for the mod to run.
For more information, see the [README](README.md).
Manipulations
-------------
Contained in manipulations.lua, this module allows several node operations to be applied over a region.
### count = worldedit.set(pos1, pos2, nodename)
Sets a region defined by positions `pos1` and `pos2` to `nodename`. To clear to region, use "air" as the value of `nodename`.
Returns the number of nodes set.
### count = worldedit.replace(pos1, pos2, searchnode, replacenode)
Replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`.
Returns the number of nodes replaced.
### count = worldedit.replaceinverse(pos1, pos2, searchnode, replacenode)
Replaces all nodes other than `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`.
Returns the number of nodes replaced.
### count = worldedit.copy(pos1, pos2, axis, amount)
Copies the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes.
Returns the number of nodes copied.
### 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.
Returns the number of nodes moved.
### count = worldedit.stack(pos1, pos2, axis, count)
Duplicates the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") `count` times.
Returns the number of nodes stacked.
### count, newpos1, newpos2 = worldedit.scale(pos1, pos2, factor)
Scales the region defined by positions `pos1` and `pos2` by an factor of positive integer `factor` with `pos1` as the origin.
Returns the number of nodes scaled, the new scaled position 1, and the new scaled position 2.
### count, newpos1, newpos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
Transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes ("x" or "y" or "z").
Returns the number of nodes transposed, the new transposed position 1, and the new transposed position 2.
### count = worldedit.flip(pos1, pos2, axis)
Flips a region defined by the positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z").
Returns the number of nodes flipped.
### count, newpos2, newpos2 = worldedit.rotate(pos1, pos2, angle)
Rotates a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise around the y axis (supporting 90 degree increments only).
Returns the number of nodes rotated, the new position 1, and the new position 2.
### count = worldedit.orient(pos1, pos2, angle)
Rotates all oriented nodes in a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise (90 degree increment) around the Y axis.
Returns the number of nodes oriented.
### count = worldedit.fixlight(pos1, pos2)
Fixes the lighting in a region defined by positions `pos1` and `pos2`.
Returns the number of nodes updated.
### count = worldedit.clearobjects(pos1, pos2)
Clears all objects in a region defined by the positions `pos1` and `pos2`.
Returns the number of objects cleared.
Primitives
----------
Contained in primitives.lua, this module allows the creation of several geometric primitives.
### count = worldedit.hollow_sphere(pos, radius, nodename)
Adds a hollow sphere centered at `pos` with radius `radius`, composed of `nodename`.
Returns the number of nodes added.
### count = worldedit.sphere(pos, radius, nodename)
Adds a sphere centered at `pos` with radius `radius`, composed of `nodename`.
Returns the number of nodes added.
### count = worldedit.hollow_dome(pos, radius, nodename)
Adds a hollow dome centered at `pos` with radius `radius`, composed of `nodename`.
Returns the number of nodes added.
### count = worldedit.dome(pos, radius, nodename)
Adds a dome centered at `pos` with radius `radius`, composed of `nodename`.
Returns the number of nodes added.
### count = worldedit.hollow_cylinder(pos, axis, length, radius, nodename)
Adds a hollow cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`.
Returns the number of nodes added.
### count = worldedit.cylinder(pos, axis, length, radius, nodename)
Adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`.
Returns the number of nodes added.
### count = worldedit.pyramid(pos, axis, height, nodename)
Adds a pyramid centered at `pos` along the `axis` axis ("x" or "y" or "z") with height `height`.
Returns the number of nodes added.
### count = worldedit.spiral(pos, length, height, spacer, nodename)
Adds a spiral centered at `pos` with side length `length`, height `height`, space between walls `spacer`, composed of `nodename`.
Returns the number of nodes added.
Visualization
-------------
Contained in visualization.lua, this module allows nodes to be visualized in different ways.
### volume = worldedit.volume(pos1, pos2)
Determines the volume of the region defined by positions `pos1` and `pos2`.
Returns the volume.
### count = worldedit.hide(pos1, pos2)
Hides all nodes in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes.
Returns the number of nodes hidden.
### count = worldedit.suppress(pos1, pos2, nodename)
Suppresses all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes.
Returns the number of nodes suppressed.
### count = worldedit.highlight(pos1, pos2, nodename)
Highlights all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively hiding all other nodes.
Returns the number of nodes found.
### count = worldedit.restore(pos1, pos2)
Restores all nodes hidden with WorldEdit functions in a region defined by positions `pos1` and `pos2`.
Returns the number of nodes restored.
Serialization
-------------
Contained in serialization.lua, this module allows regions of nodes to be serialized and deserialized to formats suitable for use outside MineTest.
### version = worldedit.valueversion(value)
Determines the version of serialized data `value`.
Returns the version as a positive integer or 0 for unknown versions.
### data, count = worldedit.serialize(pos1, pos2)
Converts the region defined by positions `pos1` and `pos2` into a single string.
Returns the serialized data and the number of nodes serialized.
### pos1, pos2, count = worldedit.allocate(originpos, value)
Determines the volume the nodes represented by string `value` would occupy if deserialized at `originpos`.
Returns the two corner positions and the number of nodes.
### count = worldedit.deserialize(originpos, value)
Loads the nodes represented by string `value` at position `originpos`.
Returns the number of nodes deserialized.
Code
----
Contained in code.lua, this module allows arbitrary Lua code to be used with WorldEdit.
### error = worldedit.lua(code)
Executes `code` as a Lua chunk in the global namespace.
Returns an error if the code fails or nil otherwise.
### error = worldedit.luatransform(pos1, pos2, code)
Executes `code` as a Lua chunk in the global namespace with the variable `pos` available, for each node in a region defined by positions `pos1` and `pos2`.
Returns an error if the code fails or nil otherwise.

View File

@ -1,280 +0,0 @@
--modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions
worldedit.sort_pos = function(pos1, pos2)
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
if pos1.x > pos2.x then
pos2.x, pos1.x = pos1.x, pos2.x
end
if pos1.y > pos2.y then
pos2.y, pos1.y = pos1.y, pos2.y
end
if pos1.z > pos2.z then
pos2.z, pos1.z = pos1.z, pos2.z
end
return pos1, pos2
end
--determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume
worldedit.volume = function(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1)
end
--sets a region defined by positions `pos1` and `pos2` to `nodename`, returning the number of nodes filled
worldedit.set = function(pos1, pos2, nodename)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local env = minetest.env
local node = {name=nodename}
local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
env:add_node(pos, 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
--replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced
worldedit.replace = function(pos1, pos2, searchnode, replacenode)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local env = minetest.env
if searchnode:find(":") == nil then
searchnode = "default:" .. searchnode
end
local pos = {x=pos1.x, y=0, z=0}
local node = {name=replacenode}
local count = 0
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
if env:get_node(pos).name == searchnode then
env:add_node(pos, node)
count = count + 1
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return count
end
--copies the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes, returning the number of nodes copied
worldedit.copy = function(pos1, pos2, axis, amount)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local env = minetest.env
if amount < 0 then
local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = env:get_node(pos, node)
local value = pos[axis]
pos[axis] = value - amount
env:add_node(pos, node)
pos[axis] = value
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
else
local pos = {x=pos2.x, y=0, z=0}
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 = minetest.env:get_node(pos, node)
local value = pos[axis]
pos[axis] = value + amount
minetest.env:add_node(pos, node)
pos[axis] = value
pos.z = pos.z - 1
end
pos.y = pos.y - 1
end
pos.x = pos.x - 1
end
end
return worldedit.volume(pos1, pos2)
end
--moves the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes, returning the number of nodes moved
worldedit.move = function(pos1, pos2, axis, amount)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local env = minetest.env
if amount < 0 then
local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = env:get_node(pos, node)
env:remove_node(pos)
local value = pos[axis]
pos[axis] = value - amount
env:add_node(pos, node)
pos[axis] = value
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
else
local pos = {x=pos2.x, y=0, z=0}
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 = minetest.env:get_node(pos, node)
env:remove_node(pos)
local value = pos[axis]
pos[axis] = value + amount
minetest.env:add_node(pos, node)
pos[axis] = value
pos.z = pos.z - 1
end
pos.y = pos.y - 1
end
pos.x = pos.x - 1
end
end
return worldedit.volume(pos1, pos2)
end
--duplicates the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") `count` times, returning the number of nodes stacked
worldedit.stack = function(pos1, pos2, axis, count)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local length = pos2[axis] - pos1[axis] + 1
local amount = 0
local copy = worldedit.copy
if count < 0 then
count = -count
length = -length
end
for i = 1, count do
amount = amount + length
copy(pos1, pos2, axis, amount)
end
return worldedit.volume(pos1, pos2)
end
--digs a region defined by positions `pos1` and `pos2`, returning the number of nodes dug
worldedit.dig = function(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local env = minetest.env
local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
env:dig_node(pos)
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return worldedit.volume(pos1, pos2)
end
--converts the region defined by positions `pos1` and `pos2` into a single string, returning the serialized data and the number of nodes serialized
worldedit.serialize = function(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local pos = {x=pos1.x, y=0, z=0}
local count = 0
local result = {}
local env = minetest.env
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = env:get_node(pos)
if node.name ~= "air" and node.name ~= "ignore" then
count = count + 1
result[count] = pos.x - pos1.x .. " " .. pos.y - pos1.y .. " " .. pos.z - pos1.z .. " " .. node.name .. " " .. node.param1 .. " " .. node.param2
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
result = table.concat(result, "\n")
return result, count
end
--loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
worldedit.deserialize = function(originpos, value)
local pos = {x=0, y=0, z=0}
local node = {name="", param1=0, param2=0}
local count = 0
local env = minetest.env
for x, y, z, name, param1, param2 in value:gmatch("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)%s+([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do
pos.x = originpos.x + tonumber(x)
pos.y = originpos.y + tonumber(y)
pos.z = originpos.z + tonumber(z)
node.name = name
node.param1 = param1
node.param2 = param2
env:add_node(pos, node)
count = count + 1
end
return count
end
--loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
--based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
worldedit.deserialize_old = function(originpos, value)
--obtain the node table
local count = 0
local get_tables = loadstring(value)
if get_tables == nil then --error loading value
return count
end
local tables = get_tables()
--transform the node table into an array of nodes
for i = 1, #tables do
for j, v in pairs(tables[i]) do
if type(v) == "table" then
tables[i][j] = tables[v[1]]
end
end
end
--load the node array
local env = minetest.env
for i, v in ipairs(tables[1]) do
local pos = v[1]
pos.x, pos.y, pos.z = originpos.x + pos.x, originpos.y + pos.y, originpos.z + pos.z
env:add_node(pos, v[2])
count = count + 1
end
return count
end

321
init.lua
View File

@ -1,321 +0,0 @@
minetest.register_privilege("worldedit", "Can use WorldEdit commands")
worldedit = {}
worldedit.set_pos = {}
worldedit.pos1 = {}
worldedit.pos2 = {}
dofile(minetest.get_modpath("worldedit") .. "/functions.lua")
dofile(minetest.get_modpath("worldedit") .. "/mark.lua")
--determines whether `nodename` is a valid node name, returning a boolean
worldedit.node_is_valid = function(temp_pos, nodename)
local originalnode = minetest.env:get_node(temp_pos) --save the original node to restore later
minetest.env:add_node(temp_pos, {name=nodename}) --attempt to add the node
local value = minetest.env:get_node(temp_pos).name --obtain the name of the newly added node
if value == nodename or value == "default:" .. nodename then --successfully added node
minetest.env:add_node(temp_pos, originalnode) --restore the original node
return true --node is valid
end
return false --node is not valid
end
minetest.register_chatcommand("/reset", {
params = "",
description = "Reset the region so that it is empty",
privs = {worldedit=true},
func = function(name, param)
worldedit.pos1[name] = nil
worldedit.pos2[name] = nil
worldedit.mark_pos1(name)
worldedit.mark_pos2(name)
minetest.chat_send_player(name, "WorldEdit region reset")
end,
})
minetest.register_chatcommand("/pos1", {
params = "",
description = "Set WorldEdit region position 1 to the player's location",
privs = {worldedit=true},
func = function(name, param)
local pos = minetest.env:get_player_by_name(name):getpos()
pos.x = math.floor(pos.x)
pos.y = math.floor(pos.y)
pos.z = math.floor(pos.z)
worldedit.pos1[name] = pos
worldedit.mark_pos1(name)
minetest.chat_send_player(name, "WorldEdit position 1 set to (" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")")
end,
})
minetest.register_chatcommand("/pos2", {
params = "",
description = "Set WorldEdit region position 2 to the player's location",
privs = {worldedit=true},
func = function(name, param)
local pos = minetest.env:get_player_by_name(name):getpos()
pos.x = math.floor(pos.x)
pos.y = math.floor(pos.y)
pos.z = math.floor(pos.z)
worldedit.pos2[name] = pos
worldedit.mark_pos2(name)
minetest.chat_send_player(name, "WorldEdit position 2 set to (" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")")
end,
})
minetest.register_chatcommand("/p", {
params = "set/get",
description = "Set WorldEdit region by punching two nodes, or display the current WorldEdit region",
privs = {worldedit=true},
func = function(name, param)
if param == "set" then --set both WorldEdit positions
worldedit.set_pos[name] = 1
minetest.chat_send_player(name, "Select positions by punching two nodes")
elseif param == "get" then --display current WorldEdit positions
local pos = worldedit.pos1[name]
minetest.chat_send_player(name, "WorldEdit position 1: (" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")")
local pos = worldedit.pos2[name]
minetest.chat_send_player(name, "WorldEdit position 2: (" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")")
else
minetest.chat_send_player(name, "Unknown subcommand: " .. param)
end
end,
})
minetest.register_on_punchnode(function(pos, node, puncher)
local name = puncher:get_player_name()
if name ~= "" and worldedit.set_pos[name] ~= nil then --currently setting position
if worldedit.set_pos[name] == 1 then --setting position 1
worldedit.set_pos[name] = 2 --set position 2 on the next invocation
worldedit.pos1[name] = pos
worldedit.mark_pos1(name)
minetest.chat_send_player(name, "WorldEdit region position 1 set to (" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")")
else --setting position 2
worldedit.set_pos[name] = nil --finished setting positions
worldedit.pos2[name] = pos
worldedit.mark_pos2(name)
minetest.chat_send_player(name, "WorldEdit region position 2 set to (" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")")
end
end
end)
minetest.register_chatcommand("/volume", {
params = "",
description = "Display the volume of the current WorldEdit region",
privs = {worldedit=true},
func = function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
minetest.chat_send_player(name, "No WorldEdit region selected")
return
end
local volume = worldedit.volume(pos1, pos2)
minetest.chat_send_player(name, "Current WorldEdit region has a volume of " .. volume .. " nodes (" .. pos2.x - pos1.x .. "*" .. pos2.y - pos1.y .. "*" .. pos2.z - pos1.z .. ")")
end,
})
minetest.register_chatcommand("/set", {
params = "<node>",
description = "Set the current WorldEdit region to <node>",
privs = {worldedit=true},
func = function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
minetest.chat_send_player(name, "No WorldEdit region selected")
return
end
if param == "" or not worldedit.node_is_valid(pos1, param) then
minetest.chat_send_player(name, "Invalid node name: " .. param)
return
end
local count = worldedit.set(pos1, pos2, param)
minetest.chat_send_player(name, count .. " nodes set")
end,
})
minetest.register_chatcommand("/replace", {
params = "<search node> <replace node>",
description = "Replace all instances of <search node> with <place node> in the current WorldEdit region",
privs = {worldedit=true},
func = function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
minetest.chat_send_player(name, "No WorldEdit region selected")
return
end
local found, _, searchnode, replacenode = param:find("([^%s]+)%s+([^%s]+)")
if found == nil then
minetest.chat_send_player(name, "Invalid usage: " .. param)
return
end
if not worldedit.node_is_valid(pos1, searchnode) then
minetest.chat_send_player(name, "Invalid search node name: " .. searchnode)
return
end
if not worldedit.node_is_valid(pos1, replacenode) then
minetest.chat_send_player(name, "Invalid replace node name: " .. replacenode)
return
end
local count = worldedit.replace(pos1, pos2, searchnode, replacenode)
minetest.chat_send_player(name, count .. " nodes replaced")
end,
})
minetest.register_chatcommand("/copy", {
params = "x/y/z <amount>",
description = "Copy the current WorldEdit region along the x/y/z axis by <amount> nodes",
privs = {worldedit=true},
func = function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
minetest.chat_send_player(name, "No WorldEdit region selected")
return
end
local found, _, axis, amount = param:find("([xyz])%s+([+-]?%d+)")
if found == nil then
minetest.chat_send_player(name, "Invalid usage: " .. param)
return
end
local count = worldedit.copy(pos1, pos2, axis, tonumber(amount))
minetest.chat_send_player(name, count .. " nodes copied")
end,
})
minetest.register_chatcommand("/move", {
params = "x/y/z <amount>",
description = "Move the current WorldEdit region along the x/y/z axis by <amount> nodes",
privs = {worldedit=true},
func = function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
minetest.chat_send_player(name, "No WorldEdit region selected")
return
end
local found, _, axis, amount = param:find("([xyz])%s+([+-]?%d+)")
if found == nil then
minetest.chat_send_player(name, "Invalid usage: " .. param)
return
end
local count = worldedit.move(pos1, pos2, axis, tonumber(amount))
minetest.chat_send_player(name, count .. " nodes moved")
end,
})
minetest.register_chatcommand("/stack", {
params = "x/y/z <count>",
description = "Stack the current WorldEdit region along the x/y/z axis <count> times",
privs = {worldedit=true},
func = function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
minetest.chat_send_player(name, "No WorldEdit region selected")
return
end
local found, _, axis, count = param:find("([xyz])%s+([+-]?%d+)")
if found == nil then
minetest.chat_send_player(name, "Invalid usage: " .. param)
return
end
local count = worldedit.stack(pos1, pos2, axis, tonumber(count))
minetest.chat_send_player(name, count .. " nodes stacked")
end,
})
minetest.register_chatcommand("/dig", {
params = "",
description = "Dig the current WorldEdit region",
privs = {worldedit=true},
func = function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
minetest.chat_send_player(name, "No WorldEdit region selected")
return
end
local count = worldedit.dig(pos1, pos2)
minetest.chat_send_player(name, count .. " nodes dug")
end,
})
minetest.register_chatcommand("/save", {
params = "<file>",
description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",
privs = {worldedit=true},
func = function(name, param)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 == nil or pos2 == nil then
minetest.chat_send_player(name, "No WorldEdit region selected")
return
end
if param == "" then
minetest.chat_send_player(name, "Invalid usage: " .. param)
return
end
local result, count = worldedit.serialize(pos1, pos2)
local path = minetest.get_worldpath() .. "/schems"
local filename = path .. "/" .. param .. ".we"
os.execute("mkdir \"" .. path .. "\"") --create directory if it does not already exist
local file, err = io.open(filename, "wb")
if err ~= nil then
minetest.chat_send_player(name, "Could not save file to \"" .. filename .. "\"")
return
end
file:write(result)
file:flush()
file:close()
minetest.chat_send_player(name, count .. " nodes saved")
end,
})
minetest.register_chatcommand("/load", {
params = "<file>",
description = "Load nodes from \"(world folder)/schems/<file>.we\" with position 1 of the current WorldEdit region as the origin",
privs = {worldedit=true},
func = function(name, param)
local pos1 = worldedit.pos1[name]
if pos1 == nil then
minetest.chat_send_player(name, "No WorldEdit region selected")
return
end
if param == "" then
minetest.chat_send_player(name, "Invalid usage: " .. param)
return
end
local filename = minetest.get_worldpath() .. "/schems/" .. param .. ".we"
local file, err = io.open(filename, "rb")
if err ~= nil then
minetest.chat_send_player(name, "Could not open file \"" .. filename .. "\"")
return
end
local value = file:read("*a")
file:close()
local count
if value:find("{") then --old WorldEdit format
count = worldedit.deserialize_old(pos1, value)
else --new WorldEdit format
count = worldedit.deserialize(pos1, value)
end
minetest.chat_send_player(name, count .. " nodes loaded")
end,
})

View File

@ -1,58 +0,0 @@
worldedit.marker1 = {}
worldedit.marker2 = {}
--marks worldedit region position 1
worldedit.mark_pos1 = function(name)
local pos = worldedit.pos1[name]
if worldedit.marker1[name] ~= nil then --marker already exists
worldedit.marker1[name]:remove() --remove marker
worldedit.marker1[name] = nil
end
if pos ~= nil then --add marker
worldedit.marker1[name] = minetest.env:add_entity(pos, "worldedit:pos1")
end
end
--marks worldedit region position 2
worldedit.mark_pos2 = function(name)
local pos = worldedit.pos2[name]
if worldedit.marker2[name] ~= nil then --marker already exists
worldedit.marker2[name]:remove() --remove marker
worldedit.marker2[name] = nil
end
if pos ~= nil then --add marker
worldedit.marker2[name] = minetest.env:add_entity(pos, "worldedit:pos2")
end
end
minetest.register_entity("worldedit:pos1", {
initial_properties = {
visual = "cube",
visual_size = {x=1.1, y=1.1},
textures = {"worldedit_pos1.png", "worldedit_pos1.png",
"worldedit_pos1.png", "worldedit_pos1.png",
"worldedit_pos1.png", "worldedit_pos1.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
},
on_punch = function(self, hitter)
self.object:remove()
local name = hitter:get_player_name()
worldedit.marker1[name] = nil
end,
})
minetest.register_entity("worldedit:pos2", {
initial_properties = {
visual = "cube",
visual_size = {x=1.1, y=1.1},
textures = {"worldedit_pos2.png", "worldedit_pos2.png",
"worldedit_pos2.png", "worldedit_pos2.png",
"worldedit_pos2.png", "worldedit_pos2.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
},
on_punch = function(self, hitter)
self.object:remove()
local name = hitter:get_player_name()
worldedit.marker2[name] = nil
end,
})

0
modpack.txt Normal file
View File

48
worldedit/code.lua Normal file
View File

@ -0,0 +1,48 @@
worldedit = worldedit or {}
local minetest = minetest --local copy of global
--executes `code` as a Lua chunk in the global namespace, returning an error if the code fails or nil otherwise
worldedit.lua = function(code)
local operation, message = loadstring(code)
if operation == nil then --code parsing failed
return message
end
local status, message = pcall(operation)
if status == nil then --operation failed
return message
end
return nil
end
--executes `code` as a Lua chunk in the global namespace with the variable pos available, for each node in a region defined by positions `pos1` and `pos2`, returning an error if the code fails or nil otherwise
worldedit.luatransform = function(pos1, pos2, code)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local factory, message = loadstring("return function(pos) " .. code .. " end")
if factory == nil then --code parsing failed
return message
end
local operation = factory()
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local status, message = pcall(operation, pos)
if status == nil then --operation failed
return message
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return nil
end

View File

@ -0,0 +1,20 @@
worldedit = worldedit or {}
local minetest = minetest --local copy of global
worldedit.allocate_old = worldedit.allocate
worldedit.deserialize_old = worldedit.deserialize
worldedit.metasave = function(pos1, pos2, filename)
local file, err = io.open(filename, "wb")
if err then return 0 end
local data, count = worldedit.serialize(pos1, pos2)
file:write(data)
file:close()
return count
end
worldedit.metaload = function(originpos, filename)
filename = minetest.get_worldpath() .. "/schems/" .. file .. ".wem"
local file, err = io.open(filename, "wb")
if err then return 0 end
local data = file:read("*a")
return worldedit.deserialize(originpos, data)
end

19
worldedit/init.lua Normal file
View File

@ -0,0 +1,19 @@
assert(minetest.get_voxel_manip, string.rep(">", 300) .. "HEY YOU! YES, YOU OVER THERE. THIS VERSION OF WORLDEDIT REQUIRES MINETEST 0.4.8 OR LATER! YOU HAVE AN OLD VERSION." .. string.rep("<", 300))
local path = minetest.get_modpath(minetest.get_current_modname())
local loadmodule = function(path)
local file = io.open(path)
if not file then
return
end
file:close()
return dofile(path)
end
loadmodule(path .. "/manipulations.lua")
loadmodule(path .. "/primitives.lua")
loadmodule(path .. "/visualization.lua")
loadmodule(path .. "/serialization.lua")
loadmodule(path .. "/code.lua")
loadmodule(path .. "/compatibility.lua")

579
worldedit/manipulations.lua Normal file
View File

@ -0,0 +1,579 @@
worldedit = worldedit or {}
local minetest = minetest --local copy of global
--modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions
worldedit.sort_pos = function(pos1, pos2)
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
if pos1.x > pos2.x then
pos2.x, pos1.x = pos1.x, pos2.x
end
if pos1.y > pos2.y then
pos2.y, pos1.y = pos1.y, pos2.y
end
if pos1.z > pos2.z then
pos2.z, pos1.z = pos1.z, pos2.z
end
return pos1, pos2
end
--determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume
worldedit.volume = function(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1)
end
--sets a region defined by positions `pos1` and `pos2` to `nodename`, returning the number of nodes filled
worldedit.set = function(pos1, pos2, nodename)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill emerged area with ignore
local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
for i in area:iterp(pos1, pos2) do
nodes[i] = node_id
end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return worldedit.volume(pos1, pos2)
end
--replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced
worldedit.replace = function(pos1, pos2, searchnode, replacenode)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
local nodes = manip:get_data()
local searchnode_id = minetest.get_content_id(searchnode)
local replacenode_id = minetest.get_content_id(replacenode)
local count = 0
for i in area:iterp(pos1, pos2) do --replace searchnode with replacenode
if nodes[i] == searchnode_id then
nodes[i] = replacenode_id
count = count + 1
end
end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end
--replaces all nodes other than `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced
worldedit.replaceinverse = function(pos1, pos2, searchnode, replacenode)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
local nodes = manip:get_data()
local searchnode_id = minetest.get_content_id(searchnode)
local replacenode_id = minetest.get_content_id(replacenode)
local count = 0
for i in area:iterp(pos1, pos2) do --replace anything that is not searchnode with replacenode
if nodes[i] ~= searchnode_id then
nodes[i] = replacenode_id
count = count + 1
end
end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end
worldedit.copy = function(pos1, pos2, axis, amount)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
if amount == 0 then
return
end
local other1, other2
if axis == "x" then
other1, other2 = "y", "z"
elseif axis == "y" then
other1, other2 = "x", "z"
else --axis == "z"
other1, other2 = "x", "y"
end
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
--prepare slice along axis
local extent = {
[axis] = 1,
[other1]=pos2[other1] - pos1[other1] + 1,
[other2]=pos2[other2] - pos1[other2] + 1,
}
local nodes = {}
local schematic = {size=extent, data=nodes}
local currentpos = {x=pos1.x, y=pos1.y, z=pos1.z}
local stride = {x=1, y=extent.x, z=extent.x * extent.y}
local get_node = minetest.get_node
for index1 = 1, extent[axis] do --go through each slice
--copy slice into schematic
local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed
for index2 = 1, extent[other1] do
local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1]
for index3 = 1, extent[other2] do
local i = newindex2 + (index3 + offset[other2]) * stride[other2]
nodes[i] = get_node(pos)
end
end
--copy schematic to target
currentpos[axis] = currentpos[axis] + amount
place_schematic(currentpos, schematic)
--wip: copy meta
currentpos[axis] = currentpos[axis] + 1
end
return worldedit.volume(pos1, pos2)
end
--copies the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes, returning the number of nodes copied
worldedit.copy = function(pos1, pos2, axis, amount)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
if amount < 0 then
local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = get_node(pos) --obtain current node
local meta = get_meta(pos):to_table() --get meta of current node
local value = pos[axis] --store current position
pos[axis] = value + amount --move along axis
add_node(pos, node) --copy node to new position
get_meta(pos):from_table(meta) --set metadata of new node
pos[axis] = value --restore old position
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
else
local pos = {x=pos2.x, y=0, z=0}
while pos.x >= pos1.x do
pos.y = pos2.y
while pos.y >= pos1.y do
pos.z = pos2.z
while pos.z >= pos1.z do
local node = get_node(pos) --obtain current node
local meta = get_meta(pos):to_table() --get meta of current node
local value = pos[axis] --store current position
pos[axis] = value + amount --move along axis
add_node(pos, node) --copy node to new position
get_meta(pos):from_table(meta) --set metadata of new node
pos[axis] = value --restore old position
pos.z = pos.z - 1
end
pos.y = pos.y - 1
end
pos.x = pos.x - 1
end
end
return worldedit.volume(pos1, pos2)
end
--moves the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes, returning the number of nodes moved
worldedit.move = function(pos1, pos2, axis, amount)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
--wip: move slice by slice using schematic method in the move axis and transfer metadata in separate loop (and if the amount is greater than the length in the axis, copy whole thing at a time and erase original after, using schematic method)
local get_node, get_meta, add_node, remove_node = minetest.get_node, minetest.get_meta, minetest.add_node, minetest.remove_node
if amount < 0 then
local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = get_node(pos) --obtain current node
local meta = get_meta(pos):to_table() --get metadata of current node
remove_node(pos)
local value = pos[axis] --store current position
pos[axis] = value + amount --move along axis
add_node(pos, node) --move node to new position
get_meta(pos):from_table(meta) --set metadata of new node
pos[axis] = value --restore old position
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
else
local pos = {x=pos2.x, y=0, z=0}
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 metadata of current node
remove_node(pos)
local value = pos[axis] --store current position
pos[axis] = value + amount --move along axis
add_node(pos, node) --move node to new position
get_meta(pos):from_table(meta) --set metadata of new node
pos[axis] = value --restore old position
pos.z = pos.z - 1
end
pos.y = pos.y - 1
end
pos.x = pos.x - 1
end
end
return worldedit.volume(pos1, pos2)
end
--duplicates the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") `count` times, returning the number of nodes stacked
worldedit.stack = function(pos1, pos2, axis, count)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local length = pos2[axis] - pos1[axis] + 1
if count < 0 then
count = -count
length = -length
end
local amount = 0
local copy = worldedit.copy
for i = 1, count do
amount = amount + length
copy(pos1, pos2, axis, amount)
end
return worldedit.volume(pos1, pos2) * count
end
--scales the region defined by positions `pos1` and `pos2` by an factor of positive integer `factor` with `pos1` as the origin, returning the number of nodes scaled, the new scaled position 1, and the new scaled position 2
worldedit.scale = function(pos1, pos2, factor)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--prepare schematic of large node
local get_node, get_meta, place_schematic = minetest.get_node, minetest.get_meta, minetest.place_schematic
local placeholder_node = {name="", param1=0, param2=0}
local nodes = {}
for i = 1, factor ^ 3 do
nodes[i] = placeholder_node
end
local schematic = {size={x=factor, y=factor, z=factor}, data=nodes}
local size = factor - 1
--make area stay loaded
local manip = minetest.get_voxel_manip()
local new_pos2 = {x=pos1.x + (pos2.x - pos1.x) * factor + size, y=pos1.y + (pos2.y - pos1.y) * factor + size, z=pos1.z + (pos2.z - pos1.z) * factor + size}
manip:read_from_map(pos1, new_pos2)
local pos = {x=pos2.x, y=0, z=0}
local bigpos = {x=0, y=0, z=0}
while pos.x >= pos1.x do
pos.y = pos2.y
while pos.y >= pos1.y do
pos.z = pos2.z
while pos.z >= pos1.z do
local node = get_node(pos) --obtain current node
local meta = get_meta(pos):to_table() --get meta of current node
local value = pos[axis] --store current position
local posx, posy, posz = pos1.x + (pos.x - pos1.x) * factor, pos1.y + (pos.y - pos1.y) * factor, pos1.z + (pos.z - pos1.z) * factor
--create large node
placeholder_node.name = node.name
placeholder_node.param1, placeholder_node.param2 = node.param1, node.param2
bigpos.x, bigpos.y, bigpos.z = posx, posy, posz
place_schematic(bigpos, schematic)
--fill in large node meta
if next(meta.fields) ~= nil and next(meta.inventory) ~= nil then --node has meta fields
for x = 0, size do
for y = 0, size do
for z = 0, size do
bigpos.x, bigpos.y, bigpos.z = posx + x, posy + y, posz + z
get_meta(bigpos):from_table(meta) --set metadata of new node
end
end
end
end
pos.z = pos.z - 1
end
pos.y = pos.y - 1
end
pos.x = pos.x - 1
end
return worldedit.volume(pos1, pos2) * (factor ^ 3), pos1, new_pos2
end
--transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed, the new transposed position 1, and the new transposed position 2
worldedit.transpose = function(pos1, pos2, axis1, axis2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local compare
local extent1, extent2 = pos2[axis1] - pos1[axis1], pos2[axis2] - pos1[axis2]
if extent1 > extent2 then
compare = function(extent1, extent2)
return extent1 > extent2
end
else
compare = function(extent1, extent2)
return extent1 < extent2
end
end
--calculate the new position 2 after transposition
local new_pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
new_pos2[axis1] = pos1[axis1] + extent2
new_pos2[axis2] = pos1[axis2] + extent1
--make area stay loaded
local manip = minetest.get_voxel_manip()
local upperbound = {x=pos2.x, y=pos2.y, z=pos2.z}
if upperbound[axis1] < new_pos2[axis1] then upperbound[axis1] = new_pos2[axis1] end
if upperbound[axis2] < new_pos2[axis2] then upperbound[axis2] = new_pos2[axis2] end
manip:read_from_map(pos1, upperbound)
local pos = {x=pos1.x, y=0, z=0}
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local extent1, extent2 = pos[axis1] - pos1[axis1], pos[axis2] - pos1[axis2]
if compare(extent1, extent2) then --transpose only if below the diagonal
local node1 = get_node(pos)
local meta1 = get_meta(pos):to_table()
local value1, value2 = pos[axis1], pos[axis2] --save position values
pos[axis1], pos[axis2] = pos1[axis1] + extent2, pos1[axis2] + extent1 --swap axis extents
local node2 = get_node(pos)
local meta2 = get_meta(pos):to_table()
add_node(pos, node1)
get_meta(pos):from_table(meta1)
pos[axis1], pos[axis2] = value1, value2 --restore position values
add_node(pos, node2)
get_meta(pos):from_table(meta2)
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return worldedit.volume(pos1, pos2), pos1, new_pos2
end
--flips a region defined by the positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z"), returning the number of nodes flipped
worldedit.flip = function(pos1, pos2, axis)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
--wip: flip the region slice by slice along the flip axis using schematic method
local pos = {x=pos1.x, y=0, z=0}
local start = pos1[axis] + pos2[axis]
pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node1 = get_node(pos)
local meta1 = get_meta(pos):to_table()
local value = pos[axis]
pos[axis] = start - value
local node2 = get_node(pos)
local meta2 = get_meta(pos):to_table()
add_node(pos, node1)
get_meta(pos):from_table(meta1)
pos[axis] = value
add_node(pos, node2)
get_meta(pos):from_table(meta2)
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return worldedit.volume(pos1, pos2)
end
--rotates a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise around axis `axis` (90 degree increment), returning the number of nodes rotated
worldedit.rotate = function(pos1, pos2, axis, angle)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local axis1, axis2
if axis == "x" then
axis1, axis2 = "z", "y"
elseif axis == "y" then
axis1, axis2 = "x", "z"
else --axis == "z"
axis1, axis2 = "y", "x"
end
angle = angle % 360
local count
if angle == 90 then
worldedit.flip(pos1, pos2, axis1)
count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
elseif angle == 180 then
worldedit.flip(pos1, pos2, axis1)
count = worldedit.flip(pos1, pos2, axis2)
elseif angle == 270 then
worldedit.flip(pos1, pos2, axis2)
count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
end
return count, pos1, pos2
end
--rotates all oriented nodes in a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise (90 degree increment) around the Y axis, returning the number of nodes oriented
worldedit.orient = function(pos1, pos2, angle) --wip: support 6D facedir rotation along arbitrary axis
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local registered_nodes = minetest.registered_nodes
local wallmounted = {
[90]={[0]=0, [1]=1, [2]=5, [3]=4, [4]=2, [5]=3},
[180]={[0]=0, [1]=1, [2]=3, [3]=2, [4]=5, [5]=4},
[270]={[0]=0, [1]=1, [2]=4, [3]=5, [4]=3, [5]=2}
}
local facedir = {
[90]={[0]=1, [1]=2, [2]=3, [3]=0},
[180]={[0]=2, [1]=3, [2]=0, [3]=1},
[270]={[0]=3, [1]=0, [2]=1, [3]=2}
}
angle = angle % 360
if angle == 0 then
return 0
end
local wallmounted_substitution = wallmounted[angle]
local facedir_substitution = facedir[angle]
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local count = 0
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
local pos = {x=pos1.x, y=0, z=0}
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = get_node(pos)
local def = registered_nodes[node.name]
if def then
if def.paramtype2 == "wallmounted" then
node.param2 = wallmounted_substitution[node.param2]
local meta = get_meta(pos):to_table()
add_node(pos, node)
get_meta(pos):from_table(meta)
count = count + 1
elseif def.paramtype2 == "facedir" then
node.param2 = facedir_substitution[node.param2]
local meta = get_meta(pos):to_table()
add_node(pos, node)
get_meta(pos):from_table(meta)
count = count + 1
end
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return count
end
--fixes the lighting in a region defined by positions `pos1` and `pos2`, returning the number of nodes updated
worldedit.fixlight = function(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local nodes = minetest.find_nodes_in_area(pos1, pos2, "air")
local dig_node = minetest.dig_node
for _, pos in ipairs(nodes) do
dig_node(pos)
end
return #nodes
end
--clears all objects in a region defined by the positions `pos1` and `pos2`, returning the number of objects cleared
worldedit.clearobjects = function(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos1x, pos1y, pos1z = pos1.x, pos1.y, pos1.z
local pos2x, pos2y, pos2z = pos2.x + 1, pos2.y + 1, pos2.z + 1
local center = {x=(pos1x + pos2x) / 2, y=(pos1y + pos2y) / 2, z=(pos1z + pos2z) / 2} --center of region
local radius = ((center.x - pos1x + 0.5) + (center.y - pos1y + 0.5) + (center.z - pos1z + 0.5)) ^ 0.5 --bounding sphere radius
local count = 0
for _, obj in pairs(minetest.get_objects_inside_radius(center, radius)) do --all objects in bounding sphere
local entity = obj:get_luaentity()
if not (entity and entity.name:find("^worldedit:")) then --avoid WorldEdit entities
local pos = obj:getpos()
if pos.x >= pos1x and pos.x <= pos2x
and pos.y >= pos1y and pos.y <= pos2y
and pos.z >= pos1z and pos.z <= pos2z then --inside region
obj:remove()
count = count + 1
end
end
end
return count
end

478
worldedit/primitives.lua Normal file
View File

@ -0,0 +1,478 @@
worldedit = worldedit or {}
local minetest = minetest --local copy of global
--adds a hollow sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added
worldedit.hollow_sphere = function(pos, radius, nodename)
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill emerged area with ignore
local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
local zstride, ystride = area.zstride, area.ystride
local count = 0
for z = -radius, radius do
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed
for y = -radius, radius do
local newy = newz + (y + offsety) * ystride
for x = -radius, radius do
local squared = x * x + y * y + z * z
if squared >= min_radius and squared <= max_radius then --position is on surface of sphere
local i = newy + (x + offsetx)
nodes[i] = node_id
count = count + 1
end
end
end
end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end
--adds a sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added
worldedit.sphere = function(pos, radius, nodename)
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill emerged area with ignore
local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local max_radius = radius * (radius + 1)
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
local zstride, ystride = area.zstride, area.ystride
local count = 0
for z = -radius, radius do
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed
for y = -radius, radius do
local newy = newz + (y + offsety) * ystride
for x = -radius, radius do
if x * x + y * y + z * z <= max_radius then --position is inside sphere
local i = newy + (x + offsetx)
nodes[i] = node_id
count = count + 1
end
end
end
end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end
--adds a hollow dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added
worldedit.hollow_dome = function(pos, radius, nodename)
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius}
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill emerged area with ignore
local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
local miny, maxy = 0, radius
if radius < 0 then
radius = -radius
miny, maxy = -radius, 0
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
local zstride, ystride = area.zstride, area.ystride
local count = 0
for z = -radius, radius do
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed
for y = miny, maxy do
local newy = newz + (y + offsety) * ystride
for x = -radius, radius do
local squared = x * x + y * y + z * z
if squared >= min_radius and squared <= max_radius then --position is on surface of sphere
local i = newy + (x + offsetx)
nodes[i] = node_id
count = count + 1
end
end
end
end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end
--adds a dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added
worldedit.dome = function(pos, radius, nodename)
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius}
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill emerged area with ignore
local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
local miny, maxy = 0, radius
if radius < 0 then
radius = -radius
miny, maxy = -radius, 0
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local max_radius = radius * (radius + 1)
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
local zstride, ystride = area.zstride, area.ystride
local count = 0
for z = -radius, radius do
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed
for y = miny, maxy do
local newy = newz + (y + offsety) * ystride
for x = -radius, radius do
if x * x + y * y + z * z <= max_radius then --position is inside sphere
local i = newy + (x + offsetx)
nodes[i] = node_id
count = count + 1
end
end
end
end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end
--adds a hollow cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added
worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename) --wip: rewrite this using voxelmanip
local other1, other2
if axis == "x" then
other1, other2 = "y", "z"
elseif axis == "y" then
other1, other2 = "x", "z"
else --axis == "z"
other1, other2 = "x", "y"
end
--handle negative lengths
local currentpos = {x=pos.x, y=pos.y, z=pos.z}
if length < 0 then
length = -length
currentpos[axis] = currentpos[axis] - length
end
--make area stay loaded
local manip = minetest.get_voxel_manip()
local pos1 = {
[axis]=currentpos[axis],
[other1]=currentpos[other1] - radius,
[other2]=currentpos[other2] - radius
}
local pos2 = {
[axis]=currentpos[axis] + length - 1,
[other1]=currentpos[other1] + radius,
[other2]=currentpos[other2] + radius
}
manip:read_from_map(pos1, pos2)
--create schematic for single node column along the axis
local node = {name=nodename, param1=0, param2=0}
local nodes = {}
for i = 1, length do
nodes[i] = node
end
local schematic = {size={[axis]=length, [other1]=1, [other2]=1}, data=nodes}
--add columns in a circle around axis to form cylinder
local place_schematic = minetest.place_schematic
local count = 0
local offset1, offset2 = 0, radius
local delta = -radius
while offset1 <= offset2 do
--add node at each octant
local first1, first2 = pos[other1] + offset1, pos[other1] - offset1
local second1, second2 = pos[other2] + offset2, pos[other2] - offset2
currentpos[other1], currentpos[other2] = first1, second1
place_schematic(currentpos, schematic) --octant 1
currentpos[other1] = first2
place_schematic(currentpos, schematic) --octant 4
currentpos[other2] = second2
place_schematic(currentpos, schematic) --octant 5
currentpos[other1] = first1
place_schematic(currentpos, schematic) --octant 8
local first1, first2 = pos[other1] + offset2, pos[other1] - offset2
local second1, second2 = pos[other2] + offset1, pos[other2] - offset1
currentpos[other1], currentpos[other2] = first1, second1
place_schematic(currentpos, schematic) --octant 2
currentpos[other1] = first2
place_schematic(currentpos, schematic) --octant 3
currentpos[other2] = second2
place_schematic(currentpos, schematic) --octant 6
currentpos[other1] = first1
place_schematic(currentpos, schematic) --octant 7
count = count + 8 --wip: broken because sometimes currentpos is repeated
--move to next location
delta = delta + (offset1 * 2) + 1
if delta >= 0 then
offset2 = offset2 - 1
delta = delta - (offset2 * 2)
end
offset1 = offset1 + 1
end
count = count * length --apply the length to the number of nodes
return count
end
--adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added
worldedit.cylinder = function(pos, axis, length, radius, nodename)
local other1, other2
if axis == "x" then
other1, other2 = "y", "z"
elseif axis == "y" then
other1, other2 = "x", "z"
else --axis == "z"
other1, other2 = "x", "y"
end
--handle negative lengths
local currentpos = {x=pos.x, y=pos.y, z=pos.z}
if length < 0 then
length = -length
currentpos[axis] = currentpos[axis] - length
end
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local pos1 = {
[axis]=currentpos[axis],
[other1]=currentpos[other1] - radius,
[other2]=currentpos[other2] - radius
}
local pos2 = {
[axis]=currentpos[axis] + length - 1,
[other1]=currentpos[other1] + radius,
[other2]=currentpos[other2] + radius
}
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill emerged area with ignore
local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local max_radius = radius * (radius + 1)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z}
local min_slice, max_slice = offset[axis], offset[axis] + length - 1
local count = 0
for index2 = -radius, radius do
local newindex2 = (index2 + offset[other1]) * stride[other1] + 1 --offset contributed by other axis 1 plus 1 to make it 1-indexed
for index3 = -radius, radius do
local newindex3 = newindex2 + (index3 + offset[other2]) * stride[other2]
if index2 * index2 + index3 * index3 <= max_radius then
for index1 = min_slice, max_slice do --add column along axis
local i = newindex3 + index1 * stride[axis]
nodes[i] = node_id
end
count = count + length
end
end
end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end
--adds a pyramid centered at `pos` with height `height`, composed of `nodename`, returning the number of nodes added
worldedit.pyramid = function(pos, axis, height, nodename)
local other1, other2
if axis == "x" then
other1, other2 = "y", "z"
elseif axis == "y" then
other1, other2 = "x", "z"
else --axis == "z"
other1, other2 = "x", "y"
end
local pos1 = {x=pos.x - height, y=pos.y - height, z=pos.z - height}
local pos2 = {x=pos.x + height, y=pos.y + height, z=pos.z + height}
--handle inverted pyramids
local startaxis, endaxis, step
if height > 0 then
height = height - 1
step = 1
pos1[axis] = pos[axis] --upper half of box
else
height = height + 1
step = -1
pos2[axis] = pos[axis] --lower half of box
end
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill emerged area with ignore
local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--fill selected area with node
local node_id = minetest.get_content_id(nodename)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offset = {x=pos.x - emerged_pos1.x, y=pos.y - emerged_pos1.y, z=pos.z - emerged_pos1.z}
local size = height * step
local count = 0
for index1 = 0, height, step do --go through each level of the pyramid
local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed
for index2 = -size, size do
local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1]
for index3 = -size, size do
local i = newindex2 + (index3 + offset[other2]) * stride[other2]
nodes[i] = node_id
end
end
count = count + (size * 2 + 1) ^ 2
size = size - 1
end
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end
--adds a spiral centered at `pos` with side length `length`, height `height`, space between walls `spacer`, composed of `nodename`, returning the number of nodes added
worldedit.spiral = function(pos, length, height, spacer, nodename)
local extent = math.ceil(length / 2)
local pos1 = {x=pos.x - extent, y=pos.y, z=pos.z - extent}
local pos2 = {x=pos.x + extent, y=pos.y + height, z=pos.z + extent}
--set up voxel manipulator
local manip = minetest.get_voxel_manip()
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
--fill emerged area with ignore
local nodes = {}
local ignore = minetest.get_content_id("ignore")
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
nodes[i] = ignore
end
--
local node_id = minetest.get_content_id(nodename)
local stride = {x=1, y=area.ystride, z=area.zstride}
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
local i = offsetz * stride.z + offsety * stride.y + offsetx + 1
--add first column
local column = i
for y = 1, height do
nodes[column] = node_id
column = column + stride.y
end
--add spiral segments
local axis, other = "x", "z"
local sign = 1
local count = height
for segment = 1, length / spacer - 1 do --go through each segment except the last
for index = 1, segment * spacer do --fill segment
i = i + stride[axis] * sign
local column = i
for y = 1, height do --add column
nodes[column] = node_id
column = column + stride.y
end
count = count + height
end
axis, other = other, axis --swap axes
if segment % 2 == 1 then --change sign every other turn
sign = -sign
end
end
--add shorter final segment
for index = 1, (math.floor(length / spacer) - 2) * spacer do
i = i + stride[axis] * sign
local column = i
for y = 1, height do --add column
nodes[column] = node_id
column = column + stride.y
end
count = count + height
end
print(minetest.serialize(nodes))
--update map nodes
manip:set_data(nodes)
manip:write_to_map()
manip:update_map()
return count
end

273
worldedit/serialization.lua Normal file
View File

@ -0,0 +1,273 @@
worldedit = worldedit or {}
local minetest = minetest --local copy of global
--modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions
worldedit.sort_pos = function(pos1, pos2)
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
if pos1.x > pos2.x then
pos2.x, pos1.x = pos1.x, pos2.x
end
if pos1.y > pos2.y then
pos2.y, pos1.y = pos1.y, pos2.y
end
if pos1.z > pos2.z then
pos2.z, pos1.z = pos1.z, pos2.z
end
return pos1, pos2
end
--determines the version of serialized data `value`, returning the version as a positive integer or 0 for unknown versions
worldedit.valueversion = function(value)
if value:find("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)") and not value:find("%{") then --previous list format
return 3
elseif value:find("^[^\"']+%{%d+%}") then
if value:find("%[\"meta\"%]") then --previous meta flat table format
return 2
end
return 1 --original flat table format
elseif value:find("%{") then --current nested table format
return 4
end
return 0 --unknown format
end
--converts the region defined by positions `pos1` and `pos2` into a single string, returning the serialized data and the number of nodes serialized
worldedit.serialize = function(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local pos = {x=pos1.x, y=0, z=0}
local count = 0
local result = {}
local get_node, get_meta = minetest.get_node, minetest.get_meta
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = get_node(pos)
if node.name ~= "air" and node.name ~= "ignore" then
count = count + 1
local meta = get_meta(pos):to_table()
--convert metadata itemstacks to itemstrings
for name, inventory in pairs(meta.inventory) do
for index, stack in ipairs(inventory) do
inventory[index] = stack.to_string and stack:to_string() or stack
end
end
result[count] = {
x = pos.x - pos1.x,
y = pos.y - pos1.y,
z = pos.z - pos1.z,
name = node.name,
param1 = node.param1,
param2 = node.param2,
meta = meta,
}
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
result = minetest.serialize(result) --convert entries to a string
return result, count
end
--determines the volume the nodes represented by string `value` would occupy if deserialized at `originpos`, returning the two corner positions and the number of nodes
--contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
worldedit.allocate = function(originpos, value)
local huge = math.huge
local pos1x, pos1y, pos1z = huge, huge, huge
local pos2x, pos2y, pos2z = -huge, -huge, -huge
local originx, originy, originz = originpos.x, originpos.y, originpos.z
local count = 0
local version = worldedit.valueversion(value)
if version == 1 or version == 2 then --flat table format
--obtain the node table
local get_tables = loadstring(value)
if get_tables then --error loading value
return originpos, originpos, count
end
local tables = get_tables()
--transform the node table into an array of nodes
for i = 1, #tables do
for j, v in pairs(tables[i]) do
if type(v) == "table" then
tables[i][j] = tables[v[1]]
end
end
end
local nodes = tables[1]
--check the node array
count = #nodes
if version == 1 then --original flat table format
for index = 1, count do
local entry = nodes[index]
local pos = entry[1]
local x, y, z = originx - pos.x, originy - pos.y, originz - pos.z
if x < pos1x then pos1x = x end
if y < pos1y then pos1y = y end
if z < pos1z then pos1z = z end
if x > pos2x then pos2x = x end
if y > pos2y then pos2y = y end
if z > pos2z then pos2z = z end
end
else --previous meta flat table format
for index = 1, count do
local entry = nodes[index]
local x, y, z = originx - entry.x, originy - entry.y, originz - entry.z
if x < pos1x then pos1x = x end
if y < pos1y then pos1y = y end
if z < pos1z then pos1z = z end
if x > pos2x then pos2x = x end
if y > pos2y then pos2y = y end
if z > pos2z then pos2z = z end
end
end
elseif version == 3 then --previous list format
for x, y, z, name, param1, param2 in value:gmatch("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)%s+([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do --match node entries
x, y, z = originx + tonumber(x), originy + tonumber(y), originz + tonumber(z)
if x < pos1x then pos1x = x end
if y < pos1y then pos1y = y end
if z < pos1z then pos1z = z end
if x > pos2x then pos2x = x end
if y > pos2y then pos2y = y end
if z > pos2z then pos2z = z end
count = count + 1
end
elseif version == 4 then --current nested table format
--wip: this is a filthy hack that works surprisingly well
value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1)
local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
local startpos, startpos1, endpos = 1, 1
local nodes = {}
while true do
startpos, endpos = escaped:find("},%s*{", startpos)
if not startpos then
break
end
local current = value:sub(startpos1, startpos)
table.insert(nodes, minetest.deserialize("return " .. current))
startpos, startpos1 = endpos, endpos
end
table.insert(nodes, minetest.deserialize("return " .. value:sub(startpos1)))
--local nodes = minetest.deserialize(value) --wip: this is broken for larger tables in the current version of LuaJIT
count = #nodes
for index = 1, count do
local entry = nodes[index]
x, y, z = originx + entry.x, originy + entry.y, originz + entry.z
if x < pos1x then pos1x = x end
if y < pos1y then pos1y = y end
if z < pos1z then pos1z = z end
if x > pos2x then pos2x = x end
if y > pos2y then pos2y = y end
if z > pos2z then pos2z = z end
end
end
local pos1 = {x=pos1x, y=pos1y, z=pos1z}
local pos2 = {x=pos2x, y=pos2y, z=pos2z}
return pos1, pos2, count
end
--loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
--contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
worldedit.deserialize = function(originpos, value)
--make area stay loaded --wip: not very performant
local pos1, pos2 = worldedit.allocate(originpos, value)
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local originx, originy, originz = originpos.x, originpos.y, originpos.z
local count = 0
local add_node, get_meta = minetest.add_node, minetest.get_meta
local version = worldedit.valueversion(value)
if version == 1 or version == 2 then --original flat table format
--obtain the node table
local get_tables = loadstring(value)
if not get_tables then --error loading value
return count
end
local tables = get_tables()
--transform the node table into an array of nodes
for i = 1, #tables do
for j, v in pairs(tables[i]) do
if type(v) == "table" then
tables[i][j] = tables[v[1]]
end
end
end
local nodes = tables[1]
--load the node array
count = #nodes
if version == 1 then --original flat table format
for index = 1, count do
local entry = nodes[index]
local pos = entry[1]
pos.x, pos.y, pos.z = originx - pos.x, originy - pos.y, originz - pos.z
add_node(pos, entry[2])
end
else --previous meta flat table format
for index = 1, #nodes do
local entry = nodes[index]
entry.x, entry.y, entry.z = originx + entry.x, originy + entry.y, originz + entry.z
add_node(entry, entry) --entry acts both as position and as node
get_meta(entry):from_table(entry.meta)
end
end
elseif version == 3 then --previous list format
local pos = {x=0, y=0, z=0}
local node = {name="", param1=0, param2=0}
for x, y, z, name, param1, param2 in value:gmatch("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)%s+([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do --match node entries
pos.x, pos.y, pos.z = originx + tonumber(x), originy + tonumber(y), originz + tonumber(z)
node.name, node.param1, node.param2 = name, param1, param2
add_node(pos, node)
count = count + 1
end
elseif version == 4 then --current nested table format
--wip: this is a filthy hack that works surprisingly well
value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1)
local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
local startpos, startpos1, endpos = 1, 1
local nodes = {}
while true do
startpos, endpos = escaped:find("},%s*{", startpos)
if not startpos then
break
end
local current = value:sub(startpos1, startpos)
table.insert(nodes, minetest.deserialize("return " .. current))
startpos, startpos1 = endpos, endpos
end
table.insert(nodes, minetest.deserialize("return " .. value:sub(startpos1)))
--local nodes = minetest.deserialize(value) --wip: this is broken for larger tables in the current version of LuaJIT
--load the nodes
count = #nodes
for index = 1, count do
local entry = nodes[index]
entry.x, entry.y, entry.z = originx + entry.x, originy + entry.y, originz + entry.z
add_node(entry, entry) --entry acts both as position and as node
end
--load the metadata
for index = 1, count do
local entry = nodes[index]
get_meta(entry):from_table(entry.meta)
end
end
return count
end

142
worldedit/visualization.lua Normal file
View File

@ -0,0 +1,142 @@
worldedit = worldedit or {}
local minetest = minetest --local copy of global
--modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions
worldedit.sort_pos = function(pos1, pos2)
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
if pos1.x > pos2.x then
pos2.x, pos1.x = pos1.x, pos2.x
end
if pos1.y > pos2.y then
pos2.y, pos1.y = pos1.y, pos2.y
end
if pos1.z > pos2.z then
pos2.z, pos1.z = pos1.z, pos2.z
end
return pos1, pos2
end
--determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume
worldedit.volume = function(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1)
end
minetest.register_node("worldedit:placeholder", {
drawtype = "airlike",
paramtype = "light",
sunlight_propagates = true,
diggable = false,
groups = {not_in_creative_inventory=1},
})
--hides all nodes in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes, returning the number of nodes hidden
worldedit.hide = function(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local pos = {x=pos1.x, y=0, z=0}
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = get_node(pos)
if node.name ~= "worldedit:placeholder" then
local data = get_meta(pos):to_table() --obtain metadata of original node
data.fields.worldedit_placeholder = node.name --add the node's name
node.name = "worldedit:placeholder" --set node name
add_node(pos, node) --add placeholder node
get_meta(pos):from_table(data) --set placeholder metadata to the original node's metadata
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return worldedit.volume(pos1, pos2)
end
--suppresses all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes, returning the number of nodes suppressed
worldedit.suppress = function(pos1, pos2, nodename)
--ignore placeholder supression
if nodename == "worldedit:placeholder" then
return 0
end
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local nodes = minetest.find_nodes_in_area(pos1, pos2, nodename)
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
for _, pos in ipairs(nodes) do
local node = get_node(pos)
local data = get_meta(pos):to_table() --obtain metadata of original node
data.fields.worldedit_placeholder = node.name --add the node's name
node.name = "worldedit:placeholder" --set node name
add_node(pos, node) --add placeholder node
get_meta(pos):from_table(data) --set placeholder metadata to the original node's metadata
end
return #nodes
end
--highlights all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively hiding all other nodes, returning the number of nodes found
worldedit.highlight = function(pos1, pos2, nodename)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local pos = {x=pos1.x, y=0, z=0}
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
local count = 0
while pos.x <= pos2.x do
pos.y = pos1.y
while pos.y <= pos2.y do
pos.z = pos1.z
while pos.z <= pos2.z do
local node = get_node(pos)
if node.name == nodename then --node found
count = count + 1
elseif node.name ~= "worldedit:placeholder" then --hide other nodes
local data = get_meta(pos):to_table() --obtain metadata of original node
data.fields.worldedit_placeholder = node.name --add the node's name
node.name = "worldedit:placeholder" --set node name
add_node(pos, node) --add placeholder node
get_meta(pos):from_table(data) --set placeholder metadata to the original node's metadata
end
pos.z = pos.z + 1
end
pos.y = pos.y + 1
end
pos.x = pos.x + 1
end
return count
end
--restores all nodes hidden with WorldEdit functions in a region defined by positions `pos1` and `pos2`, returning the number of nodes restored
worldedit.restore = function(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local nodes = minetest.find_nodes_in_area(pos1, pos2, "worldedit:placeholder")
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
for _, pos in ipairs(nodes) do
local node = get_node(pos)
local data = get_meta(pos):to_table() --obtain node metadata
node.name = data.fields.worldedit_placeholder --set node name
data.fields.worldedit_placeholder = nil --delete old nodename
add_node(pos, node) --add original node
get_meta(pos):from_table(data) --set original node metadata
end
return #nodes
end

View File

@ -0,0 +1 @@
worldedit

1163
worldedit_commands/init.lua Normal file

File diff suppressed because it is too large Load Diff

125
worldedit_commands/mark.lua Normal file
View File

@ -0,0 +1,125 @@
worldedit.marker1 = {}
worldedit.marker2 = {}
worldedit.marker = {}
--wip: use this as a huge entity to make a full worldedit region box
minetest.register_entity(":worldedit:region_cube", {
initial_properties = {
visual = "upright_sprite",
visual_size = {x=1.1, y=1.1},
textures = {"worldedit_pos1.png"},
visual_size = {x=10, y=10},
physical = false,
},
on_step = function(self, dtime)
if self.active == nil then
self.object:remove()
end
end,
on_punch = function(self, hitter)
--wip: remove the entire region marker
end,
})
--marks worldedit region position 1
worldedit.mark_pos1 = function(name)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos1 ~= nil then
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos1)
end
if worldedit.marker1[name] ~= nil then --marker already exists
worldedit.marker1[name]:remove() --remove marker
worldedit.marker1[name] = nil
end
if pos1 ~= nil then
--add marker
worldedit.marker1[name] = minetest.add_entity(pos1, "worldedit:pos1")
worldedit.marker1[name]:get_luaentity().active = true
if pos2 ~= nil then --region defined
worldedit.mark_region(pos1, pos2)
end
end
end
--marks worldedit region position 2
worldedit.mark_pos2 = function(name)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
if pos2 ~= nil then
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos2, pos2)
end
if worldedit.marker2[name] ~= nil then --marker already exists
worldedit.marker2[name]:remove() --remove marker
worldedit.marker2[name] = nil
end
if pos2 ~= nil then
--add marker
worldedit.marker2[name] = minetest.add_entity(pos2, "worldedit:pos2")
worldedit.marker2[name]:get_luaentity().active = true
if pos1 ~= nil then --region defined
worldedit.mark_region(pos1, pos2)
end
end
end
worldedit.mark_region = function(pos1, pos2)
--make area stay loaded
local manip = minetest.get_voxel_manip()
manip:read_from_map(pos1, pos2)
if worldedit.marker[name] ~= nil then --marker already exists
--wip: remove markers
end
if pos1 ~= nil and pos2 ~= nil then
--wip: place markers
end
end
minetest.register_entity(":worldedit:pos1", {
initial_properties = {
visual = "cube",
visual_size = {x=1.1, y=1.1},
textures = {"worldedit_pos1.png", "worldedit_pos1.png",
"worldedit_pos1.png", "worldedit_pos1.png",
"worldedit_pos1.png", "worldedit_pos1.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
physical = false,
},
on_step = function(self, dtime)
if self.active == nil then
self.object:remove()
end
end,
on_punch = function(self, hitter)
self.object:remove()
local name = hitter:get_player_name()
worldedit.marker1[name] = nil
end,
})
minetest.register_entity(":worldedit:pos2", {
initial_properties = {
visual = "cube",
visual_size = {x=1.1, y=1.1},
textures = {"worldedit_pos2.png", "worldedit_pos2.png",
"worldedit_pos2.png", "worldedit_pos2.png",
"worldedit_pos2.png", "worldedit_pos2.png"},
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
physical = false,
},
on_step = function(self, dtime)
if self.active == nil then
self.object:remove()
end
end,
on_punch = function(self, hitter)
self.object:remove()
local name = hitter:get_player_name()
worldedit.marker2[name] = nil
end,
})

View File

Before

Width:  |  Height:  |  Size: 142 B

After

Width:  |  Height:  |  Size: 142 B

View File

Before

Width:  |  Height:  |  Size: 157 B

After

Width:  |  Height:  |  Size: 157 B