-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinit.lua
More file actions
228 lines (180 loc) · 6.39 KB
/
init.lua
File metadata and controls
228 lines (180 loc) · 6.39 KB
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
local m = {}
screalms = m
local storage = minetest.get_mod_storage()
local realm_store = storage:get("realms") and minetest.deserialize(storage:get("realms")) or {}
local areastore = AreaStore()
-- Save modified realm_store back to storage.
local function save()
storage:set_string("realms", minetest.serialize(realm_store))
end
local realms = {}
m.realms = realms
-- Realm sizes must be aligned
m.ALIGN = 16
local function coord_ok(value)
return (value % m.ALIGN) == 0
end
-- There must be sizable "buffer" space between realms.
m.SPACING = 400
local function global_box(corner, size)
return b.box.new_add(corner, size)
end
local function allocate_position(realm)
minetest.log("action", "Allocating position for new realm: " .. realm.id)
-- Loop through all possible positions until a space is found that does not collide.
for x=b.WORLDA.min.x, b.WORLDA.max.x, realm.size.x + m.SPACING do
for z=b.WORLDA.min.z, b.WORLDA.max.z, realm.size.z + m.SPACING do
local corner = vector.new(x, realm.y - realm.size.y / 2, z)
local box = global_box(corner, realm.size)
-- Ensure within world and not collides with another realm.
if b.box.inside_box(box, b.WORLDA.box) and #b.t.keys(areastore:get_areas_in_area(box.a, box.b, true)) == 0 then
-- Use this position.
return corner
end
end
end
-- No position found.
return nil
end
-- Get the corner and size of an allocated or previous realm region.
local function get_position(realm)
-- Table describing the stored realm.
local store = realm_store[realm.id] or {
corner = allocate_position(realm),
size = realm.size,
}
-- No corner means not allocated.
if not store.corner then
return nil
end
-- Changing realm sizes could lead to spatial collisions.
-- Warn if we're using a different stored size.
if not vector.equals(store.size, realm.size) then
minetest.log("warning", ("Realm %s stored size %s differs from registered size %s; will continue using stored size"):format(realm.id, minetest.pos_to_string(store.size), minetest.pos_to_string(realm.size)))
end
-- Save the realm data.
realm_store[realm.id] = store
save()
-- Return the (possibly previously) allocated position and the realm size (stored size if already generated, otherwise registered size)
return store.corner, store.size
end
-- Register a new realm.
function m.register(id, def)
assert(not realms[id], "realm already exists")
-- Create realm from defaults and supplied values.
local r = b.t.combine({
-- Human-readable identifier of the realm.
description = "?",
-- Realm size.
size = vector.new(480, 480, 480),
-- Realm Y location.
y = 0,
-- Realm limits nodes.
limit_top = "aurum_base:limit",
limit_bottom = "aurum_base:foundation",
-- Apply client-side appearances.
apply_player = function(player) m.apply_underground(player) end,
}, def, {
id = id,
-- Default biome setup.
biome_default = b.t.combine({
node_stone = "aurum_base:stone",
node_water = "aurum_base:water_source",
node_river_water = "aurum_base:river_water_source",
depth_filler = 0,
node_riverbed = "aurum_base:sand",
depth_riverbed = 2,
node_cave_liquid = {"aurum_base:water_source", "aurum_base:lava_source"},
node_dungeon = "aurum_base:stone_brick",
node_dungeon_stair = "aurum_base:stone_brick_sh_stairs",
}, def.biome_default or {}),
})
-- Ensure valid positioning.
assert(coord_ok(r.size.x))
assert(coord_ok(r.size.y))
assert(coord_ok(r.size.z))
-- Find a global position and get the actual size of the realm
r.global_corner, r.size = get_position(r)
assert(r.global_corner, "out of room registering " .. r.id .. ", cannot add a realm of this size")
-- Relative 0,0,0 point.
r.center = vector.divide(r.size, 2)
-- Global center.
r.global_center = vector.add(r.global_corner, r.center)
-- Local bounding box.
r.local_box = b.box.new(vector.multiply(r.center, -1), r.center)
-- Global bounding box.
r.global_box = global_box(r.global_corner, r.size)
-- Add realm global box to areastore.
areastore:insert_area(r.global_box.a, r.global_box.b, id)
minetest.log("action", ("Registered realm (%s) centered at %s, size %s"):format(id, minetest.pos_to_string(r.global_center), minetest.pos_to_string(r.size)))
realms[id] = r
return r
end
-- Remove a realm by ID, freeing the id and stored position.
-- Note that if any of this realm has already been generated, *weird things* can happen.
function m.unregister(id)
realms[id] = nil
realm_store[id] = nil
end
-- Get a realm definition.
function m.get(id)
return realms[id]
end
-- Get position within realm.
function m.rpos(realm_id, global_pos)
return vector.subtract(global_pos, realms[realm_id].global_center)
end
-- Get global position from realm.
function m.gpos(realm_id, realm_pos)
return vector.add(realms[realm_id].global_center, realm_pos)
end
-- Checks which realm a point is in.
-- Returns realm id or nil
function m.pos_to_realm(global_pos)
local a = areastore:get_areas_for_pos(global_pos, false, true)
for k,v in pairs(a) do
return v.data
end
end
-- Checks which realm a box is colliding with. Does not support multiple collisions.
-- Returns realm id or nil.
function m.box_to_realm(global_box)
local a = areastore:get_areas_in_area(global_box.a, global_box.b, true, false, true)
for k,v in pairs(a) do
return v.data
end
end
-- Generate the realm border.
minetest.register_on_generated(function(minp, maxp, seed)
-- Check if any part of the block is in a realm.
local realm = m.box_to_realm(b.box.new(minp, maxp))
-- If not within a realm, then we don't need to generate the border.
if not realm then
return
end
realm = m.get(realm)
-- Read all data.
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax}
local data = vm:get_data()
local center = vector.divide(vector.add(emin, emax), 2)
local c_border = minetest.get_content_id((maxp.y > 0) and realm.limit_top or realm.limit_bottom)
for _,axis in ipairs{"x", "y", "z"} do
local sign = math.sign(center[axis] - realm.global_center[axis])
local corner = (sign < 0) and "a" or "b"
local border = realm.global_box[corner][axis]
if emin[axis] <= border and emax[axis] >= border then
local emin = table.copy(emin)
local emax = table.copy(emax)
emin[axis] = border
emax[axis] = border
for i in area:iterp(emin, emax) do
data[i] = c_border
end
end
end
-- And write back.
vm:set_data(data)
vm:write_to_map()
end)
b.dofile("check_underground.lua")