-
Notifications
You must be signed in to change notification settings - Fork 6
/
world.lua
268 lines (244 loc) · 7.66 KB
/
world.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
local world = {}
local world_default_node
-- TODO: Add some protection against direct node modifications, preferably with clear warning message
function world.clear()
local nodes = {}
setmetatable(nodes, {
__index = function(self, key) return rawget(self, key) or world_default_node end,
__newindex = function(self, key, node)
local resolved = rawget(core.registered_aliases, node.name)
if resolved then
node = table.copy(node)
node.name = resolved
end
-- TODO: Add option for stricter validation of nodes added to world
--assert(core.registered_nodes[node.name], "Attempt to place invalid node: "..tostring(node.name))
rawset(self, key, node)
end,
})
-- These nodes must not mutate.
world.nodes = nodes
end
world.clear()
-- Helper to execute callbacks
local function call(fn, ...)
if type(fn) == "function" then
return fn(...)
end
end
local function create_node(node, defaults)
node = type(node) == "table" and node or { name = node }
return {
name = node.name and node.name or (defaults and defaults.name),
param1 = node.param1 and node.param1 or (defaults and defaults.param1 or 0),
param2 = node.param2 and node.param2 or (defaults and defaults.param2 or 0),
}
end
-- FIXME: Node metadata should be integrated with world layout to handle set_node and its friends
local worldmeta = {}
function _G.core.get_meta(pos)
local nodeid = minetest.hash_node_position(pos)
if not worldmeta[nodeid] then
worldmeta[nodeid] = NodeMetaRef()
end
return worldmeta[nodeid]
end
-- FIXME: Should also execute other related callbacks
function world.on_dig(pos, node, digger)
node = node or minetest.get_node(pos)
local nodedef = minetest.registered_nodes[node.name]
return nodedef and call(nodedef.on_dig, pos, node, digger) and true or false
end
function world.clear_meta(pos)
if mineunit.destroy_nodetimer then
mineunit:destroy_nodetimer(pos)
end
worldmeta[minetest.hash_node_position(pos)] = nil
end
-- Static pointed_thing
local function get_pointed_thing(pos, pointed_thing_type)
return {
type = pointed_thing_type or "node",
above = {x=pos.x,y=pos.y+1,z=pos.z}, -- Pointing from above to downwards,
under = {x=pos.x,y=pos.y,z=pos.z}, -- crosshair at protected node surface
}
end
function world.set_default_node(node)
world_default_node = node and create_node(node) or nil
end
function world.get_node(pos)
local node = world.nodes[core.hash_node_position(vector.round(pos))]
return node and {name = node.name, param1 = node.param1, param2 = node.param2}
end
-- set_node sets world node without place/dig callbacks
function world.set_node(pos, node)
node = create_node(node)
assert(type(node.name) == "string", "Invalid node name, expected string but got " .. tostring(node.name))
local hash = minetest.hash_node_position(pos)
local nodedef = core.registered_nodes[node.name]
local oldnode = world.nodes[hash]
local oldnodedef = oldnode and core.registered_nodes[oldnode.name]
if oldnodedef then
call(oldnodedef.on_destruct, pos)
end
world.clear_meta(pos)
world.nodes[hash] = node
if oldnodedef then
call(oldnodedef.after_destruct, pos, oldnode)
end
if nodedef then
call(nodedef.on_construct, pos)
end
end
-- swap_node sets world node without any callbacks
function world.swap_node(pos, node)
local hash = minetest.hash_node_position(pos)
node = create_node(node, world.nodes[hash])
assert(type(node.name) == "string", "Invalid node name, expected string but got " .. tostring(node.name))
world.nodes[hash] = node
end
-- Called after constructing node when node was placed using
-- minetest.item_place_node / minetest.place_node.
-- If return true no item is taken from itemstack.
function world.place_node(pos, node, placer, itemstack, pointed_thing)
node = create_node(node)
world.set_node(pos, node)
local nodedef = core.registered_nodes[node.name]
assert(nodedef, "Invalid nodedef for " .. tostring(node.name))
if nodedef.after_place_node then
itemstack = itemstack or ItemStack(node.name .. " 1")
pointed_thing = pointed_thing or get_pointed_thing(pos)
call(nodedef.after_place_node, pos, placer, itemstack, pointed_thing)
end
end
local function has_meta(pos)
local node_id = minetest.hash_node_position(pos)
if worldmeta[node_id] then
-- FIXME: NodeMetaRef / MetaDataRef should have API for this
if count(worldmeta[node_id]._data) > 0 then
return true
end
local inv = get_meta(pos):get_inventory()
local lists = inv:get_lists()
for _, list in ipairs(lists) do
if not inv:is_empty(list) then
return true
end
end
end
end
-- FIXME: Does not handle node groups at all, groups are completely ignored
function world.find_nodes_in_area(p1, p2, nodenames, grouped)
assert.is_table(p1, "Invalid p1, table expected")
assert.is_table(p2, "Invalid p2, table expected")
local sx, sy, sz = math.min(p1.x, p2.x), math.min(p1.y, p2.y), math.min(p1.z, p2.z)
local ex, ey, ez = math.max(p1.x, p2.x), math.max(p1.y, p2.y), math.max(p1.z, p2.z)
assert((sx - ex) * (sy - ey) * (sz - ez) <= 4096000, "find_nodes_in_area area limit exceeded, see documentation")
-- Create lookup table for nodenames
local names = {}
if type(nodenames) == "table" then
for _, name in ipairs(nodenames) do
names[name] = true
end
else
names = { [nodenames] = true }
end
-- Find nodes
if grouped then
local results = {}
for x = sx, ex do
for y = sy, ey do
for z = sz, ez do
local pos = {x=x, y=y, z=z}
local node = minetest.get_node_or_nil(pos)
if node and node.name and names[node.name] then
if not results[node.name] then
results[node.name] = {}
end
table.insert(results[node.name], pos)
end
end
end
end
return results
else
local positions = {}
local counts = {}
for x = sx, ex do
for y = sy, ey do
for z = sz, ez do
local pos = {x=x, y=y, z=z}
local node = minetest.get_node_or_nil(pos)
if node and node.name and names[node.name] then
table.insert(positions, pos)
if not counts[node.name] then
counts[node.name] = 1
end
counts[node.name] = counts[node.name] + 1
end
end
end
end
return positions, counts
end
error("world.find_nodes_in_area unexpected error (yes, bug)")
end
function world.find_nodes_with_meta(p1, p2)
assert.is_table(p1, "Invalid p1, table expected")
assert.is_table(p2, "Invalid p2, table expected")
local sx, sy, sz = math.min(p1.x, p2.x), math.min(p1.y, p2.y), math.min(p1.z, p2.z)
local ex, ey, ez = math.max(p1.x, p2.x), math.max(p1.y, p2.y), math.max(p1.z, p2.z)
local get_meta = minetest.get_meta
local results = {}
for x = sx, ex do
for y = sy, ey do
for z = sz, ez do
local pos = {x=x, y=y, z=z}
if has_meta(pos) then
table.insert(results, pos)
end
end
end
end
return results
end
function world.remove_node(pos)
world.set_node(pos, {name="air"})
end
function world.layout(layout, offset)
world.clear()
world.add_layout(layout, offset)
end
local function get_layout_area(def, offset)
local p1, p2
if def.x and def.y and def.z then
p1, p2 = def, def
else
p1, p2 = def[1], def[2]
end
local sx, sy, sz = math.min(p1.x, p2.x), math.min(p1.y, p2.y), math.min(p1.z, p2.z)
local ex, ey, ez = math.max(p1.x, p2.x), math.max(p1.y, p2.y), math.max(p1.z, p2.z)
p1 = {x=sx, y=sy, z=sz}
p2 = {x=ex, y=ey, z=ez}
if offset then
p1 = vector.add(p1, offset)
p2 = vector.add(p2, offset)
end
return p1, p2
end
function world.add_layout(layout, offset)
for _, node in ipairs(layout) do
local p1, p2 = get_layout_area(node[1], offset)
for x = p1.x, p2.x do
for y = p1.y, p2.y do
for z = p1.z, p2.z do
world.set_node({x=x, y=y, z=z}, {name=node[2], param1=0, param2=0})
end
end
end
end
end
function core.compare_block_status(pos, status)
return true
end
return world