274 lines
7.5 KiB
Lua
274 lines
7.5 KiB
Lua
--[[
|
|
|
|
TechAge
|
|
=======
|
|
|
|
Copyright (C) 2020-2025 Joachim Stolberg
|
|
|
|
AGPL v3
|
|
See LICENSE.txt for more information
|
|
|
|
Block fly/move library II
|
|
|
|
]]--
|
|
|
|
-- for lazy programmers
|
|
local M = minetest.get_meta
|
|
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
|
local S2P = minetest.string_to_pos
|
|
local S = techage.S
|
|
local fly1 = techage.flylib
|
|
|
|
local flylib2 = {}
|
|
local MAX_SPEED = 8
|
|
|
|
local DelayedJobs = {}
|
|
|
|
local function call_after(func, ...)
|
|
DelayedJobs[#DelayedJobs + 1] = {func = func, args = {...}}
|
|
end
|
|
|
|
local function run_delayed_jobs()
|
|
for idx = 1, #DelayedJobs do
|
|
local job = DelayedJobs[idx]
|
|
job.func(unpack(job.args))
|
|
end
|
|
DelayedJobs = {}
|
|
end
|
|
|
|
local function slots(slot)
|
|
return function(slot, i)
|
|
if slot == 0 then
|
|
i = i + 1
|
|
if i <= 16 then
|
|
return i, i -- variabel, result
|
|
end
|
|
else
|
|
i = i + 1
|
|
if i == 1 then
|
|
return slot,slot
|
|
end
|
|
end
|
|
end, tonumber(slot), 0 -- param, start value
|
|
end
|
|
|
|
local function destinations(nvm, dest_pos, slot)
|
|
if slot == 0 then
|
|
local tbl = {dest_pos}
|
|
-- Calculate dest-pos for all nodes
|
|
local base_pos = nvm.lNodes[1].base_pos
|
|
for idx = 2,16 do
|
|
if nvm.lNodes[idx] then
|
|
tbl[idx] = vector.add(vector.subtract(nvm.lNodes[idx].base_pos, base_pos), dest_pos)
|
|
end
|
|
end
|
|
return tbl
|
|
end
|
|
return {[slot] = dest_pos}
|
|
end
|
|
|
|
local function distance(v)
|
|
return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
|
|
end
|
|
|
|
|
|
local function remove_node(pos, node)
|
|
if minecart.is_cart(node.name) then
|
|
return minecart.remove_cart(pos)
|
|
else
|
|
local metadata = M(pos):to_table()
|
|
minetest.remove_node(pos)
|
|
return metadata
|
|
end
|
|
end
|
|
|
|
local function place_node(dest_pos, node, metadata)
|
|
local name = techage.get_node_lvm(dest_pos).name
|
|
local ndef = minetest.registered_nodes[name]
|
|
|
|
if minecart.is_cart(node.name) then
|
|
minecart.place_and_start_cart(dest_pos, {name = node.name, param2 = node.param2}, metadata)
|
|
return true
|
|
elseif ndef.buildable_to then
|
|
minetest.set_node(dest_pos, {name = node.name, param2 = node.param2})
|
|
local meta = M(dest_pos)
|
|
meta:from_table(metadata or {})
|
|
return true
|
|
end
|
|
end
|
|
|
|
local function get_node_from_inventory(inv, idx)
|
|
local stack = inv:get_stack("main", idx)
|
|
local param2 = stack:get_meta():get_int("param2")
|
|
inv:set_stack("main", idx, nil)
|
|
return {name = stack:get_name(), param2 = param2}
|
|
end
|
|
|
|
-- Move node from 'curr_pos' to 'dest_pos'
|
|
-- * pos is the controller block position
|
|
-- * max_speed is the maximum speed
|
|
-- * height is move block height as value between 0 and 1 and used to calculate the offset
|
|
-- for the attached object (player).
|
|
-- * yoffset is an additional offset for non-player objects
|
|
-- * idx is the node related movecontroller inventory slot (optional)
|
|
local function move_node(pos, curr_pos, dest_pos, max_speed, height, yoffset, idx)
|
|
local dir = fly1.determine_dir(curr_pos, dest_pos)
|
|
local obj, is_cart = fly1.node_to_entity(pos, curr_pos, dest_pos, idx)
|
|
|
|
if obj then
|
|
if is_cart then
|
|
fly1.attach_objects(curr_pos, 0, obj, yoffset, {x = 0, y = -0.4, z = 0})
|
|
else
|
|
local offs = {x=0, y=height or 1, z=0}
|
|
fly1.attach_objects(curr_pos, offs, obj, yoffset)
|
|
if dir.y == 0 then
|
|
if (dir.x ~= 0 and dir.z == 0) or (dir.x == 0 and dir.z ~= 0) then
|
|
fly1.attach_objects(curr_pos, dir, obj, yoffset)
|
|
end
|
|
end
|
|
end
|
|
local self = obj:get_luaentity()
|
|
self.path_idx = 2
|
|
self.lmove = {vector.subtract(dest_pos, curr_pos)}
|
|
self.max_speed = max_speed
|
|
self.yoffs = yoffset
|
|
self.idx = idx
|
|
fly1.move_entity(obj, dest_pos, dir)
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- Place node to start position, if necessary
|
|
local function correct_node(pos, idx, node, dest_pos)
|
|
local cnode = techage.get_node_lvm(node.curr_pos)
|
|
local inv = M(pos):get_inventory()
|
|
if inv:get_stack("main", idx):get_count() == 1 then
|
|
local ndef = minetest.registered_nodes[cnode.name]
|
|
if ndef.buildable_to then
|
|
local inode = get_node_from_inventory(inv, idx)
|
|
return place_node(node.curr_pos, inode)
|
|
else
|
|
local inode = get_node_from_inventory(inv, idx)
|
|
minetest.swap_node(node.curr_pos, inode)
|
|
call_after(minetest.set_node, node.curr_pos, {name = cnode.name, param2 = cnode.param2})
|
|
return true
|
|
end
|
|
end
|
|
if cnode.name == node.name then
|
|
return true
|
|
end
|
|
local dest_name = techage.get_node_lvm(dest_pos).name
|
|
if dest_name == node.name then
|
|
local metadata = remove_node(dest_pos, node)
|
|
return place_node(node.curr_pos, node, metadata)
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function is_simple_node(node)
|
|
if not minecart.is_rail(pos, node.name) then
|
|
local ndef = minetest.registered_nodes[node.name]
|
|
return node.name ~= "air" and techage.can_dig_node(node.name, ndef) or minecart.is_cart(node.name)
|
|
end
|
|
end
|
|
|
|
function flylib2.get_pos(payload)
|
|
if payload then
|
|
local x,y,z = unpack(string.split(payload, ",", false, 2))
|
|
if z then
|
|
x = tonumber(x) or 0
|
|
y = tonumber(y) or 0
|
|
z = tonumber(z) or 0
|
|
return {x = x, y = y, z = z}
|
|
end
|
|
end
|
|
end
|
|
|
|
function flylib2.get_node_base_positions(lNodes)
|
|
local tbl = {}
|
|
for idx, item in ipairs(lNodes) do
|
|
tbl[idx] = item.base_pos
|
|
end
|
|
return tbl
|
|
end
|
|
|
|
function flylib2.get_nodes(pos_list)
|
|
local tbl = {}
|
|
for idx,pos in ipairs(pos_list) do
|
|
local node = techage.get_node_lvm(pos)
|
|
if is_simple_node(node) then
|
|
tbl[idx] = {base_pos = pos, curr_pos = pos, name = node.name, param2 = node.param2}
|
|
else
|
|
tbl[idx] = {base_pos = pos, curr_pos = pos, name = "techage:invalid_node", param2 = 0}
|
|
end
|
|
end
|
|
return tbl
|
|
end
|
|
|
|
function flylib2.valid_distance(nvm, destpos, slot, max_dist)
|
|
slot = slot > 0 and slot or 1
|
|
local basepos = nvm.lNodes[slot] and nvm.lNodes[slot].base_pos
|
|
if basepos and destpos then
|
|
local v = vector.subtract(destpos, basepos)
|
|
return distance(v) <= max_dist
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- pos = movecontroller position
|
|
-- slot = inventory slot index (1..16) or 0 for all slots
|
|
function flylib2.reset_nodes(pos, nvm, slot)
|
|
local meta = M(pos)
|
|
if nvm.running then return false end
|
|
local max_speed = meta:contains("max_speed") and meta:get_float("max_speed") or MAX_SPEED
|
|
local height = techage.in_range(meta:contains("height") and meta:get_float("height") or 1, 0, 1) -- platform height
|
|
local yoffs = meta:get_float("offset") -- for non-player objects
|
|
|
|
for idx in slots(slot) do
|
|
nvm.lNodes = nvm.lNodes or {}
|
|
local node = nvm.lNodes[idx]
|
|
if node and node.curr_pos and node.curr_pos ~= node.base_pos then
|
|
if correct_node(pos, idx, node, node.base_pos) then
|
|
move_node(pos, node.curr_pos, node.base_pos, max_speed, height, yoffs, idx)
|
|
node.curr_pos = node.base_pos
|
|
end
|
|
end
|
|
end
|
|
run_delayed_jobs()
|
|
return true
|
|
end
|
|
|
|
-- pos = movecontroller position
|
|
-- slot = inventory slot index (1..16) or 0 for all slots
|
|
function flylib2.move_nodes(pos, nvm, dest_pos, slot)
|
|
local meta = M(pos)
|
|
if nvm.running then return false end
|
|
local max_speed = meta:contains("max_speed") and meta:get_float("max_speed") or MAX_SPEED
|
|
local height = techage.in_range(meta:contains("height") and meta:get_float("height") or 1, 0, 1) -- platform height
|
|
local yoffs = meta:get_float("offset") -- for non-player objects
|
|
local dests = destinations(nvm, dest_pos, slot)
|
|
|
|
for idx in slots(slot) do
|
|
local node = nvm.lNodes[idx]
|
|
local dest_pos = dests[idx]
|
|
if node and node.curr_pos and node.curr_pos ~= dest_pos then
|
|
if correct_node(pos, idx, node, dest_pos) then
|
|
move_node(pos, node.curr_pos, dest_pos, max_speed, height, yoffs, idx)
|
|
node.curr_pos = dest_pos
|
|
end
|
|
end
|
|
end
|
|
run_delayed_jobs()
|
|
return true
|
|
end
|
|
|
|
minetest.register_craftitem("techage:invalid_node", {
|
|
description = S("Invalid node"),
|
|
inventory_image = "techage_invalid_node.png",
|
|
groups = {not_in_creative_inventory = 1},
|
|
})
|
|
|
|
techage.flylib2 = flylib2
|