786 lines
22 KiB
Lua
786 lines
22 KiB
Lua
local adv_chat = minetest.global_exists("adv_chat") and adv_chat
|
|
local speedup = cellestial.conf.speedup
|
|
local mapcache = cellestial.conf.mapcache
|
|
local c_cell = minetest.get_content_id("cellestial:cell")
|
|
local c_border = minetest.get_content_id("cellestial:border")
|
|
local c_air = minetest.CONTENT_AIR
|
|
local area_store = AreaStore()
|
|
local area_store_path = minetest.get_worldpath() .. "/data/cellestial.dat"
|
|
function load_store()
|
|
if modlib.file.exists(area_store_path) then
|
|
area_store:from_file(area_store_path)
|
|
end
|
|
end
|
|
load_store()
|
|
local delta = 0
|
|
function store_store()
|
|
if delta > 0 then
|
|
area_store:to_file(area_store_path)
|
|
delta = 0
|
|
end
|
|
end
|
|
modlib.minetest.register_globalstep(60, function()
|
|
if delta > 10 then
|
|
store_store()
|
|
end
|
|
end)
|
|
modlib.minetest.register_globalstep(600, store_store)
|
|
minetest.register_on_shutdown(store_store)
|
|
|
|
arenas = {}
|
|
function unload_arenas()
|
|
for id, arena in pairs(arenas) do
|
|
local owner_online = false
|
|
for _, owner in pairs(arena.meta.owners) do
|
|
if minetest.get_player_by_name(owner) then
|
|
owner_online = true
|
|
break
|
|
end
|
|
end
|
|
if not owner_online then
|
|
arenas[id] = nil
|
|
end
|
|
end
|
|
end
|
|
modlib.minetest.register_globalstep(60, unload_arenas)
|
|
simulating = {}
|
|
function calculate_offsets(voxelarea)
|
|
--[[
|
|
Lua API: "Y stride and z stride of a flat array"
|
|
+x +1
|
|
-x -1
|
|
+y +ystride
|
|
-y -ystride
|
|
+z +zstride
|
|
-z -zstride
|
|
]]
|
|
local ystride, zstride = voxelarea.ystride, voxelarea.zstride
|
|
local offsets = {}
|
|
for x = -1, 1, 1 do
|
|
for y = -ystride, ystride, ystride do
|
|
for z = -zstride, zstride, zstride do
|
|
local offset = x + y + z
|
|
if offset ~= 0 then
|
|
table.insert(offsets, offset)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return offsets
|
|
end
|
|
|
|
function initialize_cells(self)
|
|
local cells = {}
|
|
self.cells = cells
|
|
for index, data in pairs(self.area) do
|
|
if data == c_cell then
|
|
cells[index] = true
|
|
end
|
|
end
|
|
end
|
|
|
|
function read_from_map(self)
|
|
self.voxelmanip = minetest.get_voxel_manip(self.min, self.max)
|
|
local emin, emax = self.voxelmanip:read_from_map(self.min, self.max)
|
|
self.voxelarea = VoxelArea:new { MinEdge = emin, MaxEdge = emax }
|
|
self.offsets = calculate_offsets(self.voxelarea)
|
|
self.area = self.voxelmanip:get_data()
|
|
end
|
|
|
|
function overlaps(min, max)
|
|
local areas = area_store:get_areas_in_area(min, max, true, true, false)
|
|
if not modlib.table.is_empty(areas) then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
function update(self)
|
|
read_from_map(self)
|
|
if speedup then
|
|
initialize_cells(self)
|
|
calculate_neighbors(self)
|
|
end
|
|
remove_area(self)
|
|
end
|
|
|
|
function create_base(min, max)
|
|
local obj = { min = min, max = max }
|
|
update(obj)
|
|
return setmetatable(obj, { __index = getfenv(1), __call = getfenv(1) })
|
|
end
|
|
|
|
function create_role(self)
|
|
local role = "#" .. self.id
|
|
if adv_chat and not adv_chat.roles[role] then
|
|
adv_chat.register_role(role, { title = self.meta.name, color = cellestial.colors.cell.edge })
|
|
for _, owner in pairs(self.meta.owners) do
|
|
if minetest.get_player_by_name(owner) then
|
|
adv_chat.add_role(owner, role)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function new(min, max, meta)
|
|
local obj = create_base(min, max)
|
|
if not obj then
|
|
return obj
|
|
end
|
|
meta.name = meta.name or cellestial.conf.arena_defaults.name
|
|
obj.meta = meta
|
|
obj.id = assert(store(obj))
|
|
create_role(obj)
|
|
modlib.table.foreach_value(meta.owners, modlib.func.curry(add_owner_to_meta, obj))
|
|
arenas[obj.id] = obj
|
|
obj:reset()
|
|
store_store()
|
|
return obj
|
|
end
|
|
|
|
function deserialize(self, data)
|
|
self.meta = minetest.parse_json(data)
|
|
end
|
|
|
|
function load(id, min, max, data)
|
|
local obj = create_base(min, max)
|
|
obj.id = id
|
|
deserialize(obj, data)
|
|
arenas[id] = obj
|
|
create_role(obj)
|
|
return obj
|
|
end
|
|
|
|
function create_from_area(area)
|
|
return load(area.id, area.min, area.max, area.data)
|
|
end
|
|
|
|
function owner_info(self)
|
|
return table.concat(self.meta.owners, ", ")
|
|
end
|
|
|
|
function info(self)
|
|
local dim = vector.add(get_dim(self), 1)
|
|
return ('Arena #%s "%s" by %s from (%s, %s, %s) to (%s, %s, %s) - %s wide, %s tall and %s long'):format(
|
|
self.id,
|
|
self.meta.name,
|
|
owner_info(self),
|
|
self.min.x,
|
|
self.min.y,
|
|
self.min.z,
|
|
self.max.x,
|
|
self.max.y,
|
|
self.max.z,
|
|
dim.x,
|
|
dim.y,
|
|
dim.z
|
|
)
|
|
end
|
|
|
|
function formspec_table_info(self)
|
|
local dim = vector.add(get_dim(self), 1)
|
|
return table.concat(modlib.table.map({
|
|
cellestial.colors.cell.fill,
|
|
"#" .. self.id,
|
|
cellestial.colors.cell.edge,
|
|
self.meta.name,
|
|
"#FFFFFF",
|
|
owner_info(self),
|
|
table.concat({ self.min.x, self.min.y, self.min.z }, ", ") .. " - " .. table.concat({ self.max.x, self.max.y, self.max.z }, ", ") ..
|
|
" (" .. table.concat({ dim.x, dim.y, dim.z }, ", ") .. ")"
|
|
}, minetest.formspec_escape), ",")
|
|
end
|
|
|
|
function serialize(self)
|
|
return minetest.write_json(self.meta)
|
|
end
|
|
|
|
function store(self)
|
|
delta = delta + 1
|
|
if self.id then
|
|
area_store:remove_area(self.id)
|
|
end
|
|
return area_store:insert_area(self.min, self.max, serialize(self), self.id)
|
|
end
|
|
|
|
function is_owner(self, other_owner)
|
|
return modlib.table.contains(self.meta.owners, other_owner)
|
|
end
|
|
|
|
function get_position(self, name)
|
|
if cellestial.is_cellestial(name) then
|
|
return 1
|
|
end
|
|
return is_owner(self, name)
|
|
end
|
|
|
|
function serialize_ids(ids)
|
|
return table.concat(ids, ",")
|
|
end
|
|
|
|
function store_ids(meta, ids)
|
|
meta:set_string("cellestial_arena_ids", serialize_ids(ids))
|
|
end
|
|
|
|
function deserialize_ids(text)
|
|
return modlib.table.map(modlib.text.split(text, ","), tonumber)
|
|
end
|
|
|
|
function load_ids(meta)
|
|
local ids = meta:get_string("cellestial_arena_ids")
|
|
return deserialize_ids(ids)
|
|
end
|
|
|
|
function owner_action(func)
|
|
return function(self, name)
|
|
local player = minetest.get_player_by_name(name)
|
|
if not player then
|
|
return
|
|
end
|
|
local meta = player:get_meta()
|
|
local ids = load_ids(meta)
|
|
local index = modlib.table.binary_search(ids, self.id)
|
|
local err = func(self, ids, index)
|
|
if err ~= nil then
|
|
return err
|
|
end
|
|
store_ids(meta, ids)
|
|
return true
|
|
end
|
|
end
|
|
|
|
add_owner_to_meta = owner_action(
|
|
function(self, ids, index)
|
|
if index > 0 then
|
|
return false
|
|
end
|
|
table.insert(ids, -index, self.id)
|
|
end
|
|
)
|
|
function add_owner(self, name, index)
|
|
local success = add_owner_to_meta(self, name)
|
|
if success == nil then
|
|
return
|
|
end
|
|
if adv_chat then
|
|
adv_chat.add_role(name, "#" .. self.id)
|
|
end
|
|
if not modlib.table.contains(self.meta.owners) then
|
|
table.insert(self.meta.owners, index or (#self.meta.owners + 1), name)
|
|
end
|
|
arena:store()
|
|
return success
|
|
end
|
|
|
|
remove_owner_from_meta = owner_action(
|
|
function(self, ids, index)
|
|
if index < 1 then
|
|
return false
|
|
end
|
|
table.remove(ids, index)
|
|
end
|
|
)
|
|
function remove_owner(self, name)
|
|
local success = remove_owner_from_meta(self, name)
|
|
if success == nil then
|
|
return
|
|
end
|
|
if adv_chat then
|
|
adv_chat.remove_role(name, "#" .. self.id)
|
|
end
|
|
local owner_index = modlib.table.contains(self.meta.owners, name)
|
|
if owner_index then
|
|
table.remove(self.meta.owners, owner_index)
|
|
end
|
|
arena:store()
|
|
end
|
|
|
|
function set_owners(self, owners)
|
|
local owner_set = modlib.table.set(owners)
|
|
local self_owner_set = modlib.table.set(self.owners)
|
|
local to_be_added = modlib.table.difference(owner_set, self_owner_set)
|
|
local to_be_removed = modlib.table.difference(self_owner_set, owner_set)
|
|
modlib.table.foreach_key(to_be_added, modlib.func.curry(add_owner, self))
|
|
modlib.table.foreach_key(to_be_removed, modlib.func.curry(add_owner, self))
|
|
arena:store()
|
|
end
|
|
|
|
function get_dim(self)
|
|
return vector.subtract(self.max, self.min)
|
|
end
|
|
|
|
function get_area(self)
|
|
if mapcache then
|
|
return self.area
|
|
end
|
|
read_from_map(self)
|
|
self.area = self.voxelmanip:get_data()
|
|
return self.area
|
|
end
|
|
|
|
function get_area_temp(self)
|
|
if mapcache then
|
|
return self.area
|
|
end
|
|
return self.voxelmanip:get_data()
|
|
end
|
|
|
|
function remove_area(self)
|
|
if not mapcache then
|
|
self.area = nil
|
|
end
|
|
end
|
|
|
|
function set_area(self, min, dim)
|
|
local new_min = min or self.min
|
|
local new_max = self.max
|
|
if dim then
|
|
new_max = vector.add(new_min, dim)
|
|
end
|
|
local areas = area_store:get_areas_in_area(new_min, new_max, true, true)
|
|
areas[self.id] = nil
|
|
if modlib.table.is_empty(areas) then
|
|
self.min = new_min
|
|
self.max = new_max
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
function set_borders(self)
|
|
local min, max = self.min, self.max
|
|
for coord = 1, 6 do
|
|
local coords = { min.x, min.y, min.z, max.x, max.y, max.z }
|
|
if coord > 3 then
|
|
coords[coord] = coords[coord - 3]
|
|
else
|
|
coords[coord] = coords[coord + 3]
|
|
end
|
|
for index in self.voxelarea:iter(unpack(coords)) do
|
|
self.area[index] = c_border
|
|
end
|
|
end
|
|
end
|
|
|
|
function resize(self, dim)
|
|
local min = self.min
|
|
local old_max = self.max
|
|
if not set_area(self, nil, dim) then
|
|
return false
|
|
end
|
|
local max = self.max
|
|
local max_max = {}
|
|
for coord, value in pairs(max) do
|
|
max_max[coord] = math.max(value, old_max[coord])
|
|
end
|
|
local voxelmanip = minetest.get_voxel_manip(self.min, max_max)
|
|
local emin, emax = voxelmanip:read_from_map(self.min, max_max)
|
|
local voxelarea = VoxelArea:new{ MinEdge = emin, MaxEdge = emax }
|
|
local area = voxelmanip:get_data()
|
|
-- clear added area
|
|
for coord, v_coord in ipairs{"x", "y", "z"} do
|
|
local coords = { min.x, min.y, min.z, max_max.x, max_max.y, max_max.z }
|
|
local old = old_max[v_coord]
|
|
if old < coords[coord+3] then
|
|
coords[coord] = old
|
|
else
|
|
coords[coord] = max[v_coord]
|
|
coords[coord + 3] = old
|
|
end
|
|
for index in voxelarea:iter(unpack(coords)) do
|
|
area[index] = c_air
|
|
end
|
|
end
|
|
voxelmanip:set_data(area)
|
|
voxelmanip:write_to_map()
|
|
read_from_map(self)
|
|
set_borders(self)
|
|
write_to_map(self)
|
|
update(self)
|
|
store(self)
|
|
return true
|
|
end
|
|
|
|
function move(self, min)
|
|
local old_min, old_max = self.min, self.max
|
|
local voxelmanip = minetest.get_voxel_manip(old_min, old_max)
|
|
local emin, emax = voxelmanip:read_from_map(old_min, old_max)
|
|
local voxelarea = VoxelArea:new{ MinEdge = emin, MaxEdge = emax }
|
|
local previous_data = voxelarea:iterp(old_min, old_max)
|
|
if not set_area(self, min, get_dim(self)) then
|
|
return false
|
|
end
|
|
local area = voxelmanip:get_data()
|
|
local area_copy = modlib.table.copy(area)
|
|
for index in voxelarea:iterp(old_min, old_max) do
|
|
area[index] = c_air
|
|
end
|
|
voxelmanip:set_data(area)
|
|
voxelmanip:write_to_map()
|
|
read_from_map(self)
|
|
for index in self.voxelarea:iterp(self.min, self.max) do
|
|
local previous_data_index = previous_data()
|
|
self.area[index] = area_copy[previous_data_index]
|
|
end
|
|
write_to_map(self)
|
|
remove_area(self)
|
|
store(self)
|
|
return true
|
|
end
|
|
|
|
function get(pos)
|
|
local areas = area_store:get_areas_for_pos(pos, true, true)
|
|
local id = next(areas)
|
|
if not id then
|
|
return
|
|
end
|
|
if next(areas, id) then
|
|
return
|
|
end
|
|
if arenas[id] then
|
|
return arenas[id]
|
|
end
|
|
local area = areas[id]
|
|
area.id = id
|
|
return create_from_area(area)
|
|
end
|
|
|
|
local guaranteed_max = 128
|
|
local cutoff_min_iteration = 4
|
|
local cutoff_factor = 1.25
|
|
|
|
-- uses a monte-carlo like iterative tree level search
|
|
function create_free(meta, origin, dim)
|
|
dim = dim or modlib.table.copy(cellestial.conf.arena_defaults.dimension)
|
|
local visited_ids = {}
|
|
local current_level = { origin or modlib.table.copy(cellestial.conf.arena_defaults.search_origin) }
|
|
local iteration = 1
|
|
local found_min
|
|
local area_found = false
|
|
repeat
|
|
local new_level = {}
|
|
local function process_level()
|
|
for _, min in pairs(current_level) do
|
|
local areas = area_store:get_areas_in_area(min, vector.add(min, dim), true, true, false)
|
|
if modlib.table.is_empty(areas) then
|
|
found_min = min
|
|
area_found = true
|
|
return
|
|
end
|
|
for id, area in pairs(modlib.table.shuffle(areas)) do
|
|
if not visited_ids[id] then
|
|
visited_ids[id] = true
|
|
end
|
|
for _, coord in pairs(modlib.table.shuffle({ "x", "y", "z" })) do
|
|
for _, new_value in pairs(modlib.table.shuffle({ area.min[coord] - dim[coord] - 1, area.max[coord] + 1 })) do
|
|
local new_min = modlib.table.copy(area.min)
|
|
new_min[coord] = new_value
|
|
if iteration <= cutoff_min_iteration or math.random() < 1 / math.pow(cutoff_factor, iteration - cutoff_min_iteration) then
|
|
table.insert(new_level, new_min)
|
|
if #new_level >= guaranteed_max then
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
process_level()
|
|
modlib.table.shuffle(new_level)
|
|
current_level = new_level
|
|
iteration = iteration + 1
|
|
until area_found
|
|
local arena = new(found_min, vector.add(found_min, dim), meta)
|
|
return arena
|
|
end
|
|
|
|
function get_by_id(id)
|
|
if arenas[id] then
|
|
return arenas[id]
|
|
end
|
|
local area = area_store:get_area(id, true, true)
|
|
if not area then
|
|
return
|
|
end
|
|
area.id = id
|
|
return create_from_area(area)
|
|
end
|
|
|
|
function get_by_player(player)
|
|
return get(player:get_pos())
|
|
end
|
|
|
|
function get_by_name(name)
|
|
local player = minetest.get_player_by_name(name)
|
|
if not player then
|
|
return
|
|
end
|
|
return get_by_player(player)
|
|
end
|
|
|
|
function list_ids_by_name(name)
|
|
local player = minetest.get_player_by_name(name)
|
|
if not player then
|
|
return
|
|
end
|
|
local arena_ids = load_ids(player:get_meta())
|
|
return arena_ids
|
|
end
|
|
|
|
function list_by_name(name)
|
|
local ids = list_ids_by_name(name)
|
|
if not ids then
|
|
return ids
|
|
end
|
|
return modlib.table.map(ids, get_by_id)
|
|
end
|
|
|
|
function remove(self)
|
|
arenas[(type(self) == "table" and self.id) or self] = nil
|
|
end
|
|
|
|
function get_cell(self, pos)
|
|
local index = self.voxelarea:indexp(pos)
|
|
if speedup then
|
|
return self.cells[index] == true
|
|
end
|
|
if self.area then
|
|
return self.area[index] == c_cell
|
|
end
|
|
return minetest.get_node(pos).name == "cellestial:cell"
|
|
end
|
|
|
|
if speedup then
|
|
function __set_cell(self, index, cell)
|
|
local cell_or_nil = (cell or nil)
|
|
if self.cells[index] == cell_or_nil then
|
|
return true
|
|
end
|
|
self.cells[index] = cell_or_nil
|
|
local neighbors = self.neighbors
|
|
if cell then
|
|
neighbors[index] = neighbors[index] or 0
|
|
end
|
|
local delta = (cell and 1) or -1
|
|
-- This won't give out of bounds because it only iterates the inside of the voxelarea (content)
|
|
for _, offset in pairs(self.offsets) do
|
|
local newindex = index + offset
|
|
neighbors[newindex] = (neighbors[newindex] or 0) + delta
|
|
end
|
|
end
|
|
end
|
|
-- does everything except setting the node
|
|
function _set_cell(self, pos, cell)
|
|
local index = self.voxelarea:indexp(pos)
|
|
if speedup then
|
|
if __set_cell(self, index, cell) then
|
|
return
|
|
end
|
|
else
|
|
if get_cell(self, pos) == (cell or false) then
|
|
return
|
|
end
|
|
end
|
|
if self.area then
|
|
self.area[index] = (cell and c_cell) or c_air
|
|
end
|
|
return true
|
|
end
|
|
|
|
function set_cell(self, pos, cell)
|
|
if _set_cell(self, pos, cell) then
|
|
minetest.set_node(pos, { name = (cell and "cellestial:cell") or "air" })
|
|
end
|
|
end
|
|
|
|
function calculate_neighbors(self)
|
|
local cells = self.cells
|
|
local offsets = self.offsets
|
|
local neighbors = {}
|
|
self.neighbors = neighbors
|
|
for index, _ in pairs(cells) do
|
|
neighbors[index] = neighbors[index] or 0
|
|
-- This won't give out of bounds because it only iterates the inside of the voxelarea (content)
|
|
for _, offset in pairs(offsets) do
|
|
local new_index = index + offset
|
|
neighbors[new_index] = (neighbors[new_index] or 0) + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
function apply_rules(self, rules)
|
|
local cells, area = self.cells, get_area(self)
|
|
local birth = rules.birth
|
|
local death = rules.death
|
|
local delta_cells = {}
|
|
for index, amount in pairs(self.neighbors) do
|
|
if cells[index] then
|
|
if death[amount] and area[index] == c_cell then
|
|
delta_cells[index] = false
|
|
end
|
|
elseif birth[amount] and area[index] == c_air then
|
|
delta_cells[index] = true
|
|
end
|
|
end
|
|
if birth[0] then
|
|
for index in iter_content(self) do
|
|
if not cells[index] then
|
|
delta_cells[index] = true
|
|
end
|
|
end
|
|
end
|
|
for index, cell in pairs(delta_cells) do
|
|
__set_cell(self, index, cell)
|
|
self.area[index] = (cell and c_cell) or c_air
|
|
end
|
|
end
|
|
|
|
function write_to_map(self)
|
|
local vm = self.voxelmanip
|
|
vm:set_data(self.area)
|
|
vm:write_to_map()
|
|
end
|
|
|
|
if speedup then
|
|
function next_step(self, rules)
|
|
apply_rules(self, rules)
|
|
write_to_map(self)
|
|
remove_area(self)
|
|
end
|
|
else
|
|
function next_step(self, rules)
|
|
local offsets = self.offsets
|
|
local birth = rules.birth
|
|
local death = rules.death
|
|
read_from_map(self)
|
|
local vm = self.voxelmanip
|
|
local data = vm:get_data()
|
|
local new_data = {}
|
|
for index, c_id in ipairs(data) do
|
|
new_data[index] = c_id
|
|
end
|
|
self.area = new_data
|
|
for index in iter_content(self) do
|
|
local c_id = data[index]
|
|
local amount = 0
|
|
-- This won't give out of bounds because it only iterates the inside of the voxelarea (content)
|
|
for _, offset in pairs(offsets) do
|
|
if data[index + offset] == c_cell then
|
|
amount = amount + 1
|
|
end
|
|
end
|
|
if c_id == c_cell then
|
|
if death[amount] then
|
|
c_id = c_air
|
|
end
|
|
elseif c_id == c_air and birth[amount] then
|
|
c_id = c_cell
|
|
end
|
|
new_data[index] = c_id
|
|
end
|
|
write_to_map(self)
|
|
end
|
|
end
|
|
|
|
function iter_content(self)
|
|
return self.voxelarea:iter(self.min.x + 1, self.min.y + 1, self.min.z + 1, self.max.x - 1, self.max.y - 1, self.max.z - 1)
|
|
end
|
|
|
|
function is_content(self, pos)
|
|
return pos.x >= self.min.x + 1 and pos.y >= self.min.y + 1 and pos.z >= self.min.z + 1
|
|
and pos.x <= self.max.x - 1 and pos.y <= self.max.y - 1 and pos.z <= self.max.z - 1
|
|
end
|
|
|
|
function _clear(self)
|
|
for index in iter_content(self) do
|
|
self.area[index] = c_air
|
|
end
|
|
if speedup then
|
|
self.cells = {}
|
|
self.neighbors = {}
|
|
end
|
|
end
|
|
|
|
function clear(self)
|
|
get_area(self)
|
|
_clear(self)
|
|
write_to_map(self)
|
|
remove_area(self)
|
|
end
|
|
|
|
function reset(self)
|
|
local min, max = self.min, self.max
|
|
get_area(self)
|
|
_clear(self)
|
|
set_borders(self)
|
|
local light_data = self.voxelmanip:get_light_data()
|
|
for index in self.voxelarea:iter(min.x, min.y, min.z, max.x, max.y, max.z) do
|
|
light_data[index] = minetest.LIGHT_MAX
|
|
end
|
|
self.voxelmanip:set_light_data(light_data)
|
|
write_to_map(self)
|
|
remove_area(self)
|
|
end
|
|
|
|
function randomize(self, threshold)
|
|
self.area = get_area(self)
|
|
self.cells = {}
|
|
for index in iter_content(self) do
|
|
if math.random() < threshold then
|
|
self.cells[index] = true
|
|
self.area[index] = c_cell
|
|
else
|
|
self.area[index] = c_air
|
|
end
|
|
end
|
|
calculate_neighbors(self)
|
|
write_to_map(self)
|
|
remove_area(self)
|
|
end
|
|
|
|
function next_steps(self, steps, rules)
|
|
for _ = 1, steps do
|
|
next_step(self, rules)
|
|
end
|
|
end
|
|
|
|
function start(self, steps_per_second, rules)
|
|
simulating[self.id] = { arena = self, steps_per_second = steps_per_second, outstanding_steps = 0, rules = rules }
|
|
end
|
|
|
|
function stop(self)
|
|
simulating[self.id] = nil
|
|
end
|
|
|
|
function simulate(self, steps_per_second, rules)
|
|
if simulating[self.id] then
|
|
return stop(self)
|
|
end
|
|
return start(self, steps_per_second, rules)
|
|
end
|
|
|
|
function teleport(self, player)
|
|
local area = get_area(self)
|
|
for index in iter_content(self) do
|
|
local c_id = area[index]
|
|
if c_id == c_air then
|
|
-- move to place with air
|
|
player:set_pos(vector.add(self.voxelarea:position(index), 0.5))
|
|
return true
|
|
end
|
|
end
|
|
player:set_pos(vector.add(self.min, vector.divide(vector.subtract(self.max, self.min), 2))) -- move to center
|
|
return false
|
|
end
|
|
|
|
minetest.register_globalstep(
|
|
function(dtime)
|
|
for _, sim in pairs(simulating) do
|
|
local outstanding_steps = sim.outstanding_steps + dtime * sim.steps_per_second
|
|
local steps = math.floor(outstanding_steps)
|
|
sim.arena:next_steps(steps, sim.rules)
|
|
sim.outstanding_steps = outstanding_steps - steps
|
|
end
|
|
end
|
|
) |