Add fly and turn controllers
This commit is contained in:
parent
deab00aa07
commit
5597836d06
@ -544,3 +544,25 @@ function techage.get_inv_state(inv, listname)
|
||||
end
|
||||
return state
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("ta_send", {
|
||||
description = minetest.formspec_escape(
|
||||
"Send a techage command to the block with the number given: /ta_send <number> <topic> [<data>]"),
|
||||
func = function(name, param)
|
||||
local num, cmnd, payload = param:match('^([0-9]+)%s+(%w+)%s*(.*)$')
|
||||
|
||||
if num and cmnd then
|
||||
if techage.not_protected(num, name) then
|
||||
local resp = techage.send_single("0", num, cmnd, payload)
|
||||
if type(resp) == "string" then
|
||||
return true, resp
|
||||
else
|
||||
return true, dump(resp)
|
||||
end
|
||||
else
|
||||
return false, "Destination block is protected"
|
||||
end
|
||||
end
|
||||
return false, "Syntax: /ta_send <number> <topic> [<data>]"
|
||||
end
|
||||
})
|
||||
|
@ -34,6 +34,10 @@ function techage.counting_hit()
|
||||
end
|
||||
end
|
||||
|
||||
function techage.counting_add(player_name, points)
|
||||
PlayerPoints[player_name] = (PlayerPoints[player_name] or 0) + points
|
||||
end
|
||||
|
||||
local function output()
|
||||
for name, val in pairs(PlayerPoints) do
|
||||
if val > MAX_POINTS then
|
||||
|
642
basis/fly_lib.lua
Normal file
642
basis/fly_lib.lua
Normal file
@ -0,0 +1,642 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2020-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Block fly/move library
|
||||
|
||||
]]--
|
||||
|
||||
-- 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 flylib = {}
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- to_path function for the fly/move path
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local function strsplit(text)
|
||||
text = text:gsub("\r\n", "\n")
|
||||
text = text:gsub("\r", "\n")
|
||||
return string.split(text, "\n", true)
|
||||
end
|
||||
|
||||
local function trim(s)
|
||||
return (s:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
function flylib.distance(v)
|
||||
return math.abs(v.x) + math.abs(v.y) + math.abs(v.z)
|
||||
end
|
||||
|
||||
function flylib.to_vector(s)
|
||||
local x,y,z = unpack(string.split(s, ","))
|
||||
if x and y and z then
|
||||
return {
|
||||
x=tonumber(x) or 0,
|
||||
y=tonumber(y) or 0,
|
||||
z=tonumber(z) or 0,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function flylib.to_path(s, max_dist)
|
||||
local tPath
|
||||
|
||||
for _, line in ipairs(strsplit(s)) do
|
||||
line = trim(line)
|
||||
line = string.split(line, "--", true, 1)[1] or ""
|
||||
if line ~= "" then
|
||||
local v = flylib.to_vector(line)
|
||||
if v and (not max_dist or flylib.distance(v) <= max_dist) then
|
||||
tPath = tPath or {}
|
||||
tPath[#tPath + 1] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
return tPath
|
||||
end
|
||||
|
||||
local function next_path_pos(pos, lpath, idx)
|
||||
local offs = lpath[idx]
|
||||
if offs then
|
||||
return vector.add(pos, offs)
|
||||
end
|
||||
end
|
||||
|
||||
local function reverse_path(lpath)
|
||||
local lres = {}
|
||||
for i = #lpath, 1, -1 do
|
||||
lres[#lres + 1] = vector.multiply(lpath[i], -1)
|
||||
end
|
||||
return lres
|
||||
end
|
||||
|
||||
local function dest_offset(lpath)
|
||||
local offs = {x=0, y=0, z=0}
|
||||
for i = 1,#lpath do
|
||||
offs = vector.add(offs, lpath[i])
|
||||
end
|
||||
return offs
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Entity / Move / Attach / Detach
|
||||
-------------------------------------------------------------------------------
|
||||
local MIN_SPEED = 0.4
|
||||
local MAX_SPEED = 8
|
||||
local CORNER_SPEED = 4
|
||||
local SimpleNodes = techage.logic.SimpleNodes
|
||||
|
||||
local function calc_speed(v)
|
||||
return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
|
||||
end
|
||||
|
||||
-- Only the ID ist stored, not the object
|
||||
local function get_object_id(object)
|
||||
for id, entity in pairs(minetest.luaentities) do
|
||||
if entity.object == object then
|
||||
return id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- determine exact position of attached entities
|
||||
local function obj_pos(obj)
|
||||
local _, _, pos = obj:get_attach()
|
||||
if pos then
|
||||
pos = vector.divide(pos, 29)
|
||||
return vector.add(obj:get_pos(), pos)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check access conflicts with other mods
|
||||
local function lock_player(player)
|
||||
local meta = player:get_meta()
|
||||
if meta:get_int("player_physics_locked") == 0 then
|
||||
meta:set_int("player_physics_locked", 1)
|
||||
meta:set_string("player_physics_locked_by", "ta_flylib")
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function unlock_player(player)
|
||||
local meta = player:get_meta()
|
||||
if meta:get_int("player_physics_locked") == 1 then
|
||||
if meta:get_string("player_physics_locked_by") == "ta_flylib" then
|
||||
meta:set_int("player_physics_locked", 0)
|
||||
meta:set_string("player_physics_locked_by", "")
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function detach_player(player)
|
||||
local pos = obj_pos(player)
|
||||
if pos then
|
||||
player:set_detach()
|
||||
player:set_properties({visual_size = {x=1, y=1}})
|
||||
player:set_pos(pos)
|
||||
end
|
||||
-- TODO: move to save position
|
||||
end
|
||||
|
||||
-- Attach player/mob to given parent object (block)
|
||||
local function attach_single_object(parent, obj, dir)
|
||||
local self = parent:get_luaentity()
|
||||
local rot = obj:get_rotation()
|
||||
local res = obj:get_attach()
|
||||
if not res then
|
||||
local offs = table.copy(dir)
|
||||
dir = vector.multiply(dir, 29)
|
||||
obj:set_attach(parent, "", dir, rot, true)
|
||||
obj:set_properties({visual_size = {x=2.9, y=2.9}})
|
||||
if obj:is_player() then
|
||||
if lock_player(obj) then
|
||||
table.insert(self.players, {name = obj:get_player_name(), offs = offs})
|
||||
end
|
||||
else
|
||||
table.insert(self.entities, {objID = get_object_id(obj), offs = offs})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Attach all objects around to the parent object
|
||||
-- offs is the search/attach position offset
|
||||
local function attach_objects(pos, offs, parent)
|
||||
local pos1 = vector.add(pos, offs)
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(pos1, 0.9)) do
|
||||
local dir = vector.subtract(obj:get_pos(), pos)
|
||||
local entity = obj:get_luaentity()
|
||||
if entity then
|
||||
if entity.name == "__builtin:item" then -- dropped items
|
||||
--obj:set_attach(objref, "", {x=0, y=0, z=0}, {x=0, y=0, z=0}, true) -- hier kracht es
|
||||
elseif entity.name ~= "techage:move_item" then
|
||||
attach_single_object(parent, obj, dir)
|
||||
end
|
||||
elseif obj:is_player() then
|
||||
attach_single_object(parent, obj, dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Detach all attached objects from the parent object
|
||||
local function detach_objects(pos, self)
|
||||
for _, item in ipairs(self.entities or {}) do
|
||||
local entity = minetest.luaentities[item.objID]
|
||||
if entity then
|
||||
local obj = entity.object
|
||||
obj:set_detach()
|
||||
obj:set_properties({visual_size = {x=1, y=1}})
|
||||
local pos1 = vector.add(pos, item.offs)
|
||||
obj:set_pos(pos1)
|
||||
end
|
||||
end
|
||||
for _, item in ipairs(self.players or {}) do
|
||||
local obj = minetest.get_player_by_name(item.name)
|
||||
if obj then
|
||||
obj:set_detach()
|
||||
obj:set_properties({visual_size = {x=1, y=1}})
|
||||
local pos1 = vector.add(pos, item.offs)
|
||||
obj:set_pos(pos1)
|
||||
unlock_player(obj)
|
||||
end
|
||||
end
|
||||
self.entities = {}
|
||||
self.players = {}
|
||||
end
|
||||
|
||||
local function entity_to_node(pos, obj)
|
||||
local self = obj:get_luaentity()
|
||||
if self then
|
||||
local name = self.item_name or "air"
|
||||
local param2 = self.param2 or 0
|
||||
local metadata = self.metadata or {}
|
||||
detach_objects(pos, self)
|
||||
obj:remove()
|
||||
|
||||
local node = minetest.get_node(pos)
|
||||
local ndef1 = minetest.registered_nodes[name]
|
||||
local ndef2 = minetest.registered_nodes[node.name]
|
||||
if ndef1 and ndef2 then
|
||||
if ndef2.buildable_to then
|
||||
local meta = M(pos)
|
||||
minetest.set_node(pos, {name=name, param2=param2})
|
||||
meta:from_table(metadata)
|
||||
meta:set_string("ta_move_block", "")
|
||||
return
|
||||
end
|
||||
local meta = M(pos)
|
||||
if not meta:contains("ta_move_block") then
|
||||
meta:set_string("ta_move_block", minetest.serialize({name=name, param2=param2}))
|
||||
return
|
||||
end
|
||||
minetest.add_item(pos, ItemStack(name))
|
||||
elseif ndef1 then
|
||||
minetest.add_item(pos, ItemStack(name))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function node_to_entity(start_pos)
|
||||
local meta = M(start_pos)
|
||||
local node, metadata
|
||||
|
||||
if meta:contains("ta_move_block") then
|
||||
-- Move-block stored as metadata
|
||||
node = minetest.deserialize(meta:get_string("ta_move_block"))
|
||||
metadata = {}
|
||||
meta:set_string("ta_move_block", "")
|
||||
else
|
||||
-- Block with other metadata
|
||||
node = minetest.get_node(start_pos)
|
||||
metadata = meta:to_table()
|
||||
minetest.remove_node(start_pos)
|
||||
end
|
||||
local obj = minetest.add_entity(start_pos, "techage:move_item")
|
||||
if obj then
|
||||
local self = obj:get_luaentity()
|
||||
local rot = techage.facedir_to_rotation(node.param2)
|
||||
obj:set_rotation(rot)
|
||||
obj:set_properties({wield_item=node.name})
|
||||
obj:set_armor_groups({immortal=1})
|
||||
|
||||
-- To be able to revert to node
|
||||
self.item_name = node.name
|
||||
self.param2 = node.param2
|
||||
self.metadata = metadata or {}
|
||||
|
||||
-- Prepare for attachments
|
||||
self.players = {}
|
||||
self.entities = {}
|
||||
-- Prepare for path walk
|
||||
self.idx = 1
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
-- move block direction
|
||||
local function determine_dir(pos1, pos2)
|
||||
local vdist = vector.subtract(pos2, pos1)
|
||||
local ndist = vector.length(vdist)
|
||||
return vector.divide(vdist, ndist)
|
||||
end
|
||||
|
||||
local function move_entity(obj, dest_pos, dir, is_corner)
|
||||
local self = obj:get_luaentity()
|
||||
self.dest_pos = dest_pos
|
||||
self.dir = dir
|
||||
if is_corner then
|
||||
local vel = vector.multiply(dir, math.min(CORNER_SPEED, self.max_speed))
|
||||
obj:set_velocity(vel)
|
||||
end
|
||||
local acc = vector.multiply(dir, self.max_speed / 2)
|
||||
obj:set_acceleration(acc)
|
||||
end
|
||||
|
||||
local function moveon_entity(obj, self, pos1)
|
||||
local pos2 = next_path_pos(pos1, self.lpath, self.idx)
|
||||
if pos2 then
|
||||
self.idx = self.idx + 1
|
||||
local dir = determine_dir(pos1, pos2)
|
||||
move_entity(obj, pos2, dir, true)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Handover the entity to the next movecontroller
|
||||
local function handover_to(obj, self, pos1)
|
||||
local info = techage.get_node_info(self.handover)
|
||||
if info and info.name == "techage:ta4_movecontroller" then
|
||||
local meta = M(info.pos)
|
||||
if self.move2to1 then
|
||||
self.handover = meta:contains("handoverB") and meta:get_string("handoverB")
|
||||
else
|
||||
self.handover = meta:contains("handoverA") and meta:get_string("handoverA")
|
||||
end
|
||||
local offs = flylib.to_vector(meta:get_string("path"))
|
||||
if pos1 and offs then
|
||||
self.dest_pos = vector.add(pos1, offs)
|
||||
local dir = determine_dir(pos1, info.pos)
|
||||
move_entity(obj, info.pos, dir)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("techage:move_item", {
|
||||
initial_properties = {
|
||||
pointable = true,
|
||||
makes_footstep_sound = true,
|
||||
static_save = true,
|
||||
collide_with_objects = false,
|
||||
physical = false,
|
||||
visual = "wielditem",
|
||||
wield_item = "default:dirt",
|
||||
visual_size = {x=0.67, y=0.67, z=0.67},
|
||||
selectionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
},
|
||||
|
||||
get_staticdata = function(self)
|
||||
return minetest.serialize({
|
||||
item_name = self.item_name,
|
||||
param2 = self.param2,
|
||||
metadata = self.metadata,
|
||||
move2to1 = self.move2to1,
|
||||
handover = self.handover,
|
||||
idx = self.idx,
|
||||
lpath = self.lpath,
|
||||
start_pos = self.start_pos,
|
||||
max_speed = self.max_speed,
|
||||
dest_pos = self.dest_pos,
|
||||
dir = self.dir,
|
||||
respawn = true,
|
||||
})
|
||||
end,
|
||||
|
||||
on_activate = function(self, staticdata)
|
||||
if staticdata then
|
||||
local tbl = minetest.deserialize(staticdata) or {}
|
||||
self.item_name = tbl.item_name or "air"
|
||||
self.param2 = tbl.param2 or 0
|
||||
self.metadata = tbl.metadata or {}
|
||||
self.move2to1 = tbl.move2to1 or false
|
||||
self.handover = tbl.handover
|
||||
self.idx = tbl.idx or 1
|
||||
self.lpath = tbl.lpath or {}
|
||||
self.max_speed = tbl.max_speed or MAX_SPEED
|
||||
self.dest_pos = tbl.dest_pos or self.object:get_pos()
|
||||
self.start_pos = tbl.start_pos or self.object:get_pos()
|
||||
self.dir = tbl.dir or {x=0, y=0, z=0}
|
||||
self.object:set_properties({wield_item = self.item})
|
||||
print("tbl.respawn", tbl.respawn)
|
||||
if tbl.respawn then
|
||||
entity_to_node(self.start_pos, self.object)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
on_step = function(self, dtime, moveresult)
|
||||
local stop_obj = function(obj, self)
|
||||
obj:move_to(self.dest_pos, true)
|
||||
obj:set_acceleration({x=0, y=0, z=0})
|
||||
obj:set_velocity({x=0, y=0, z=0})
|
||||
self.dest_pos = nil
|
||||
self.old_dist = nil
|
||||
self.ttl = 2
|
||||
end
|
||||
|
||||
if self.dest_pos then
|
||||
local obj = self.object
|
||||
local pos = obj:get_pos()
|
||||
local dist = vector.distance(pos, self.dest_pos)
|
||||
local speed = calc_speed(obj:get_velocity())
|
||||
self.old_dist = self.old_dist or dist
|
||||
|
||||
-- Landing
|
||||
if self.lpath and self.lpath[self.idx] then
|
||||
if dist < 1 or dist > self.old_dist then
|
||||
local dest_pos = self.dest_pos
|
||||
stop_obj(obj, self)
|
||||
if not moveon_entity(obj, self, dest_pos) then
|
||||
minetest.after(0.5, entity_to_node, dest_pos, obj)
|
||||
end
|
||||
return
|
||||
end
|
||||
elseif self.handover and dist < 0.2 or dist > self.old_dist then
|
||||
if not handover_to(obj, self, self.dest_pos) then
|
||||
local dest_pos = self.dest_pos
|
||||
stop_obj(obj, self)
|
||||
minetest.after(0.5, entity_to_node, dest_pos, obj)
|
||||
return
|
||||
end
|
||||
else
|
||||
if dist < 0.05 or dist > self.old_dist then
|
||||
local dest_pos = self.dest_pos
|
||||
stop_obj(obj, self)
|
||||
minetest.after(0.5, entity_to_node, dest_pos, obj)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
self.old_dist = dist
|
||||
|
||||
-- Braking or limit max speed
|
||||
if self.handover then
|
||||
if speed > (dist * 4) or speed > self.max_speed then
|
||||
speed = math.min(speed, math.max(dist * 4, MIN_SPEED))
|
||||
local vel = vector.multiply(self.dir,speed)
|
||||
obj:set_velocity(vel)
|
||||
obj:set_acceleration({x=0, y=0, z=0})
|
||||
end
|
||||
else
|
||||
if speed > (dist * 2) or speed > self.max_speed then
|
||||
speed = math.min(speed, math.max(dist * 2, MIN_SPEED))
|
||||
local vel = vector.multiply(self.dir,speed)
|
||||
obj:set_velocity(vel)
|
||||
obj:set_acceleration({x=0, y=0, z=0})
|
||||
end
|
||||
end
|
||||
|
||||
elseif self.ttl then
|
||||
self.ttl = self.ttl - dtime
|
||||
if self.ttl < 0 then
|
||||
local obj = self.object
|
||||
local pos = obj:get_pos()
|
||||
entity_to_node(pos, obj)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
local function is_valid_dest(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef and ndef.buildable_to then
|
||||
return true
|
||||
end
|
||||
if not M(pos):contains("ta_move_block") then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function is_simple_node(pos)
|
||||
-- special handling
|
||||
local name = minetest.get_node(pos).name
|
||||
if SimpleNodes[name] ~= nil then
|
||||
return SimpleNodes[name]
|
||||
end
|
||||
|
||||
local ndef = minetest.registered_nodes[name]
|
||||
if not ndef or name == "air" or name == "ignore" then return false end
|
||||
-- don't remove nodes with some intelligence or undiggable nodes
|
||||
if ndef.drop == "" then return false end
|
||||
if ndef.diggable == false then return false end
|
||||
if ndef.after_dig_node then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function table_add(tbl, offs)
|
||||
if not tbl or not offs then return end
|
||||
|
||||
local tbl2 = {}
|
||||
for _, v in ipairs(tbl) do
|
||||
tbl2[#tbl2 + 1] = vector.add(v, offs)
|
||||
end
|
||||
return tbl2
|
||||
end
|
||||
|
||||
local function move_node(pos, start_pos, lpath, max_speed, height, move2to1, handover)
|
||||
local pos2 = next_path_pos(start_pos, lpath, 1)
|
||||
print("move_node", P2S(pos), P2S(start_pos), lpath, max_speed, height, move2to1, P2S(pos2))
|
||||
if pos2 then
|
||||
local dir = determine_dir(start_pos, pos2)
|
||||
local obj = node_to_entity(start_pos)
|
||||
|
||||
if obj then
|
||||
local offs = {x=0, y=height or 1, z=0}
|
||||
attach_objects(start_pos, offs, obj)
|
||||
if dir.y == 0 then
|
||||
if (dir.x ~= 0 and dir.z == 0) or (dir.x == 0 and dir.z ~= 0) then
|
||||
attach_objects(start_pos, dir, obj)
|
||||
end
|
||||
end
|
||||
local self = obj:get_luaentity()
|
||||
self.idx = 2
|
||||
self.lpath = lpath
|
||||
self.max_speed = max_speed
|
||||
self.start_pos = start_pos
|
||||
self.move2to1 = move2to1
|
||||
self.handover = handover
|
||||
move_entity(obj, pos2, dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, handover)
|
||||
print("move_nodes", dump(nvm), dump(lpath), max_speed, height, move2to1, handover)
|
||||
local owner = meta:get_string("owner")
|
||||
techage.counting_add(owner, #nvm.lpos1 * #lpath)
|
||||
|
||||
for idx = 1, #nvm.lpos1 do
|
||||
local pos1 = nvm.lpos1[idx]
|
||||
local pos2 = nvm.lpos2[idx]
|
||||
|
||||
if move2to1 then
|
||||
pos1, pos2 = pos2, pos1
|
||||
end
|
||||
|
||||
print("move_nodes", P2S(pos1), P2S(pos2))
|
||||
if not minetest.is_protected(pos1, owner) and not minetest.is_protected(pos2, owner) then
|
||||
if is_simple_node(pos1) and is_valid_dest(pos2) then
|
||||
move_node(pos, pos1, lpath, max_speed, height, move2to1, handover)
|
||||
else
|
||||
if not is_simple_node(pos1) then
|
||||
meta:set_string("status", S("No valid node at the start position"))
|
||||
else
|
||||
meta:set_string("status", S("No valid destination position"))
|
||||
end
|
||||
end
|
||||
else
|
||||
if minetest.is_protected(pos1, owner) then
|
||||
meta:set_string("status", S("Start position is protected"))
|
||||
else
|
||||
meta:set_string("status", S("Destination position is protected"))
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function flylib.move_to_other_pos(pos, move2to1)
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local lpath = flylib.to_path(meta:get_string("path")) or {}
|
||||
local max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED
|
||||
local height = meta:contains("height") and meta:get_float("height") or 1
|
||||
local handover
|
||||
height = techage.in_range(height, 0, 1)
|
||||
max_speed = techage.in_range(max_speed, MIN_SPEED, MAX_SPEED)
|
||||
|
||||
local offs = dest_offset(lpath)
|
||||
if move2to1 then
|
||||
lpath = reverse_path(lpath)
|
||||
end
|
||||
nvm.lpos1 = nvm.lpos1 or {}
|
||||
nvm.lpos2 = table_add(nvm.lpos1, offs)
|
||||
|
||||
if move2to1 then
|
||||
handover = meta:contains("handoverA") and meta:get_string("handoverA")
|
||||
else
|
||||
handover = meta:contains("handoverB") and meta:get_string("handoverB")
|
||||
end
|
||||
return move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, handover)
|
||||
end
|
||||
|
||||
-- rot is one of "l", "r", "2l", "2r"
|
||||
-- cpos is the center pos (optional)
|
||||
function flylib.rotate_nodes(pos, posses1, rot)
|
||||
local meta = M(pos)
|
||||
local owner = meta:get_string("owner")
|
||||
local cpos = meta:contains("center") and flylib.to_vector(meta:get_string("center"))
|
||||
local posses2 = techage.rotate_around_center(posses1, rot, cpos)
|
||||
local param2
|
||||
local nodes2 = {}
|
||||
|
||||
techage.counting_add(owner, #posses1 * 2)
|
||||
|
||||
for i, pos1 in ipairs(posses1) do
|
||||
local node = techage.get_node_lvm(pos1)
|
||||
if rot == "l" then
|
||||
param2 = techage.param2_turn_left(node.param2)
|
||||
elseif rot == "r" then
|
||||
param2 = techage.param2_turn_right(node.param2)
|
||||
else
|
||||
param2 = techage.param2_turn_left(techage.param2_turn_left(node.param2))
|
||||
end
|
||||
if not minetest.is_protected(pos1, owner) and is_simple_node(pos1) then
|
||||
minetest.remove_node(pos1)
|
||||
nodes2[#nodes2 + 1] = {pos = posses2[i], name = node.name, param2 = param2}
|
||||
end
|
||||
end
|
||||
for _,item in ipairs(nodes2) do
|
||||
if not minetest.is_protected(item.pos, owner) and is_valid_dest(item.pos) then
|
||||
minetest.add_node(item.pos, {name = item.name, param2 = item.param2})
|
||||
end
|
||||
end
|
||||
return posses2
|
||||
end
|
||||
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
unlock_player(player)
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
if unlock_player(player) then
|
||||
detach_player(player)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_dieplayer(function(player)
|
||||
if unlock_player(player) then
|
||||
detach_player(player)
|
||||
end
|
||||
end)
|
||||
|
||||
return flylib
|
@ -37,21 +37,98 @@ local PARAM2_TO_ROT = {[0] =
|
||||
3,7,11,15
|
||||
}
|
||||
|
||||
local Rotations = {}
|
||||
local Idx_to_Rot = {}
|
||||
|
||||
for x = 0,3 do
|
||||
for y = 0,3 do
|
||||
for z = 0,3 do
|
||||
Rotations[#Rotations + 1] = {x=x*math.pi/2, y=y*math.pi/2, z=z*math.pi/2}
|
||||
Idx_to_Rot[#Idx_to_Rot + 1] = {x=x*math.pi/2, y=y*math.pi/2, z=z*math.pi/2}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Input data to turn a "facedir" block to the right/left
|
||||
local ROTATION = {
|
||||
{5,14,11,16}, -- x+
|
||||
{7,12,9,18}, -- x-
|
||||
{0,1,2,3}, -- y+
|
||||
{22,21,20,23}, -- y-
|
||||
{6,15,8,17}, -- z+
|
||||
{4,13,10,19}, -- z-
|
||||
}
|
||||
|
||||
local RotationViaYAxis = {}
|
||||
|
||||
for _,row in ipairs(ROTATION) do
|
||||
for i = 1,4 do
|
||||
local val = row[i]
|
||||
local left = row[i == 1 and 4 or i - 1]
|
||||
local right = row[i == 4 and 1 or i + 1]
|
||||
RotationViaYAxis[val] = {left, right}
|
||||
end
|
||||
end
|
||||
|
||||
function techage.facedir_to_rotation(facedir)
|
||||
local idx = PARAM2_TO_ROT[facedir] or 0
|
||||
return Rotations[idx]
|
||||
return Idx_to_Rot[idx]
|
||||
end
|
||||
|
||||
function techage.param2_turn_left(param2)
|
||||
return (RotationViaYAxis[param2] or RotationViaYAxis[0])[1]
|
||||
end
|
||||
|
||||
function techage.param2_turn_right(param2)
|
||||
return (RotationViaYAxis[param2] or RotationViaYAxis[0])[2]
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Rotate nodes around the center
|
||||
-------------------------------------------------------------------------------
|
||||
local function center(nodes)
|
||||
local c = {x=0, y=0, z=0}
|
||||
for _,v in ipairs(nodes) do
|
||||
c = vector.add(c, v)
|
||||
end
|
||||
c = vector.divide(c, #nodes)
|
||||
c = vector.round(c)
|
||||
c.y = 0
|
||||
return c
|
||||
end
|
||||
|
||||
local function rotate_around_axis(v, c, rot)
|
||||
local dx, dz = v.x - c.x, v.z - c.z
|
||||
if rot == "l" then
|
||||
return {
|
||||
x = c.x - dz,
|
||||
y = v.y,
|
||||
z = c.z + dx,
|
||||
}
|
||||
elseif rot == "r" then
|
||||
return {
|
||||
x = c.x + dz,
|
||||
y = v.y,
|
||||
z = c.z - dx,
|
||||
}
|
||||
else -- turn 180 degree
|
||||
return {
|
||||
x = c.x - dx,
|
||||
y = v.y,
|
||||
z = c.z - dz,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Function returns a list ẃith the new node positions
|
||||
-- rot is one of "l", "r", "2l", "2r"
|
||||
-- cpos is the center pos (optional)
|
||||
function techage.rotate_around_center(nodes1, rot, cpos)
|
||||
cpos = cpos or center(nodes1)
|
||||
local nodes2 = {}
|
||||
for _,pos in ipairs(nodes1) do
|
||||
nodes2[#nodes2 + 1] = rotate_around_axis(pos, cpos, rot)
|
||||
end
|
||||
return nodes2
|
||||
end
|
||||
|
||||
-- allowed for digging
|
||||
local RegisteredNodesToBeDug = {}
|
||||
@ -356,6 +433,14 @@ function techage.mydump(o, indent, nested, level)
|
||||
return "{"..table.concat(t, ", ").."}"
|
||||
end
|
||||
|
||||
function techage.vector_dump(posses)
|
||||
local t = {}
|
||||
for _,pos in ipairs(posses) do
|
||||
t[#t + 1] = minetest.pos_to_string(pos)
|
||||
end
|
||||
return table.concat(t, " ")
|
||||
end
|
||||
|
||||
-- title bar help (width is the fornmspec width)
|
||||
function techage.question_mark_help(width, tooltip)
|
||||
local x = width- 0.6
|
||||
|
122
basis/mark_lib.lua
Normal file
122
basis/mark_lib.lua
Normal file
@ -0,0 +1,122 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2020-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Block marker lib for door/move/fly controller
|
||||
|
||||
]]--
|
||||
|
||||
local marker = {}
|
||||
|
||||
local MarkedNodes = {} -- t[player] = {{entity, pos},...}
|
||||
local MaxNumber = {}
|
||||
local CurrentPos -- to mark punched entities
|
||||
|
||||
local function unmark_position(name, pos)
|
||||
pos = vector.round(pos)
|
||||
for idx,item in ipairs(MarkedNodes[name] or {}) do
|
||||
if vector.equals(pos, item.pos) then
|
||||
item.entity:remove()
|
||||
table.remove(MarkedNodes[name], idx)
|
||||
CurrentPos = pos
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function marker.unmark_all(name)
|
||||
for _,item in ipairs(MarkedNodes[name] or {}) do
|
||||
item.entity:remove()
|
||||
end
|
||||
MarkedNodes[name] = nil
|
||||
end
|
||||
|
||||
local function mark_position(name, pos)
|
||||
pos = vector.round(pos)
|
||||
if not CurrentPos or not vector.equals(pos, CurrentPos) then -- entity not punched?
|
||||
if #MarkedNodes[name] < MaxNumber[name] then
|
||||
local entity = minetest.add_entity(pos, "techage:block_marker")
|
||||
if entity ~= nil then
|
||||
entity:get_luaentity().player_name = name
|
||||
table.insert(MarkedNodes[name], {pos = pos, entity = entity})
|
||||
end
|
||||
CurrentPos = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
CurrentPos = nil
|
||||
end
|
||||
|
||||
function marker.get_poslist(name)
|
||||
local idx = 0
|
||||
local lst = {}
|
||||
for _,item in ipairs(MarkedNodes[name] or {}) do
|
||||
table.insert(lst, item.pos)
|
||||
idx = idx + 1
|
||||
if idx >= 16 then break end
|
||||
end
|
||||
return lst
|
||||
end
|
||||
|
||||
minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing)
|
||||
if puncher and puncher:is_player() then
|
||||
local name = puncher:get_player_name()
|
||||
|
||||
if not MarkedNodes[name] then
|
||||
return
|
||||
end
|
||||
|
||||
mark_position(name, pointed_thing.under)
|
||||
end
|
||||
end)
|
||||
|
||||
function marker.start(name, max_num)
|
||||
MaxNumber[name] = max_num or 99
|
||||
MarkedNodes[name] = {}
|
||||
end
|
||||
|
||||
function marker.stop(name)
|
||||
MarkedNodes[name] = nil
|
||||
MaxNumber[name] = nil
|
||||
end
|
||||
|
||||
minetest.register_entity(":techage:block_marker", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
textures = {
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
},
|
||||
--use_texture_alpha = true,
|
||||
physical = false,
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
collisionbox = {-0.55,-0.55,-0.55, 0.55,0.55,0.55},
|
||||
glow = 8,
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
self.ttl = (self.ttl or 2400) - 1
|
||||
if self.ttl <= 0 then
|
||||
local pos = self.object:get_pos()
|
||||
unmark_position(self.player_name, pos)
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
local pos = self.object:get_pos()
|
||||
local name = hitter:get_player_name()
|
||||
if name == self.player_name then
|
||||
unmark_position(name, pos)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
return marker
|
@ -1610,7 +1610,7 @@ techage.manual_DE.aText = {
|
||||
"Anleitung:\n"..
|
||||
"\n"..
|
||||
" - Controller setzen und die Blöcke\\, die bewegt werden sollen\\, über das Menü an-trainieren (Es können bis zu 16 Blöcke an-trainiert werden)\n"..
|
||||
" - die \"Flugstrecke\" muss über eine x\\,y\\,z Angabe (relativ) eingegeben werden (die maximale Distanz beträgt 100 m)\n"..
|
||||
" - die \"Flugstrecke\" muss über eine x\\,y\\,z Angabe (relativ) eingegeben werden (die maximale Distanz beträgt 200 m)\n"..
|
||||
" - mit den Menü-Tasten \"Bewege A-B\" sowie \"Bewege B-A\" kann die Bewegung getestet werden\n"..
|
||||
" - man kann auch durch Wände oder andere Blöcke fliegen\n"..
|
||||
" - auch die Zielposition für die Blöcke kann belegt sein. Die Blöcke werden in diesem Falle \"unsichtbar\" gespeichert. Dies ist für Schiebetüren und ähnliches gedacht\n"..
|
||||
|
@ -1608,7 +1608,7 @@ techage.manual_EN.aText = {
|
||||
"Instructions:\n"..
|
||||
"\n"..
|
||||
" - Set the controller and train the blocks to be moved via the menu (up to 16 blocks can be trained)\n"..
|
||||
" - the \"flight route\" must be entered via an x\\, y\\, z specification (relative) (the maximum distance is 100 m)\n"..
|
||||
" - the \"flight route\" must be entered via an x\\, y\\, z specification (relative) (the maximum distance is 200 m)\n"..
|
||||
" - The movement can be tested with the menu buttons \"Move A-B\" and \"Move B-A\"\n"..
|
||||
" - you can also fly through walls or other blocks\n"..
|
||||
" - The target position for the blocks can also be occupied. In this case\\, the blocks are saved \"invisibly\". This is intended for sliding doors and the like\n"..
|
||||
|
15
init.lua
15
init.lua
@ -275,20 +275,25 @@ dofile(MP.."/logic/logic_block.lua") -- new
|
||||
dofile(MP.."/logic/node_detector.lua")
|
||||
dofile(MP.."/logic/player_detector.lua")
|
||||
dofile(MP.."/logic/cart_detector.lua")
|
||||
dofile(MP.."/logic/gateblock.lua")
|
||||
dofile(MP.."/logic/doorblock.lua")
|
||||
dofile(MP.."/logic/doorcontroller.lua") -- old
|
||||
dofile(MP.."/logic/doorcontroller2.lua") -- new
|
||||
dofile(MP.."/logic/collector.lua")
|
||||
dofile(MP.."/logic/button_2x.lua")
|
||||
dofile(MP.."/logic/button_4x.lua")
|
||||
dofile(MP.."/logic/signallamp_2x.lua")
|
||||
dofile(MP.."/logic/signallamp_4x.lua")
|
||||
dofile(MP.."/logic/movecontroller.lua")
|
||||
if minetest.global_exists("mesecon") then
|
||||
dofile(MP.."/logic/mesecons_converter.lua")
|
||||
end
|
||||
|
||||
-- move_controller
|
||||
dofile(MP.."/move_controller/gateblock.lua")
|
||||
dofile(MP.."/move_controller/doorblock.lua")
|
||||
dofile(MP.."/move_controller/doorcontroller.lua") -- old
|
||||
dofile(MP.."/move_controller/doorcontroller2.lua") -- new
|
||||
dofile(MP.."/move_controller/movecontroller.lua")
|
||||
dofile(MP.."/move_controller/turncontroller.lua")
|
||||
dofile(MP.."/move_controller/flycontroller.lua")
|
||||
|
||||
|
||||
-- Test
|
||||
dofile(MP.."/recipe_checker.lua")
|
||||
dofile(MP.."/.test/sink.lua")
|
||||
|
@ -763,7 +763,7 @@ Error: Distance > 100 m !!=Fehler: Distanz > 100 m !!
|
||||
Handover to A=Übergabe an A
|
||||
Handover to B=Übergabe an B
|
||||
Maximum Speed=Maximalgeschwindigkeit
|
||||
Maximum speed for the moving block.=Maximale Geschwindigkeit für den beweglichen Block.
|
||||
Maximum speed for moving blocks=Maximale Geschwindigkeit für bewegliche Blöcke
|
||||
Move A-B=Bewege A-B
|
||||
Move B-A=Bewege B-A
|
||||
Move block height=Move Block Höhe
|
||||
|
@ -763,7 +763,7 @@ Error: Distance > 100 m !!=
|
||||
Handover to A=
|
||||
Handover to B=
|
||||
Maximum Speed=
|
||||
Maximum speed for the moving block.=
|
||||
Maximum speed for moving blocks=
|
||||
Move A-B=
|
||||
Move B-A=
|
||||
Move block height=
|
||||
|
@ -1,805 +0,0 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2020-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA4 Move Controller
|
||||
|
||||
]]--
|
||||
|
||||
-- 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
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Entity / Move / Attach / Detach
|
||||
-------------------------------------------------------------------------------
|
||||
local MIN_SPEED = 0.4
|
||||
local MAX_SPEED = 8
|
||||
|
||||
local function to_vector(s)
|
||||
local x,y,z = unpack(string.split(s, ","))
|
||||
if x and y and z then
|
||||
return {
|
||||
x=tonumber(x) or 0,
|
||||
y=tonumber(y) or 0,
|
||||
z=tonumber(z) or 0,
|
||||
}
|
||||
end
|
||||
return {x=0, y=0, z=0}
|
||||
end
|
||||
|
||||
-- Only the ID ist stored, not the object
|
||||
local function get_object_id(object)
|
||||
for id, entity in pairs(minetest.luaentities) do
|
||||
if entity.object == object then
|
||||
return id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- determine exact position of attached entities
|
||||
local function obj_pos(obj)
|
||||
local _, _, pos = obj:get_attach()
|
||||
if pos then
|
||||
pos = vector.divide(pos, 29)
|
||||
return vector.add(obj:get_pos(), pos)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check access conflicts with other mods
|
||||
local function lock_player(player)
|
||||
local meta = player:get_meta()
|
||||
if meta:get_int("player_physics_locked") == 0 then
|
||||
meta:set_int("player_physics_locked", 1)
|
||||
meta:set_string("player_physics_locked_by", "ta_movecontroller")
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function unlock_player(player)
|
||||
local meta = player:get_meta()
|
||||
if meta:get_int("player_physics_locked") == 1 then
|
||||
if meta:get_string("player_physics_locked_by") == "ta_movecontroller" then
|
||||
meta:set_int("player_physics_locked", 0)
|
||||
meta:set_string("player_physics_locked_by", "")
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function detach_player(player)
|
||||
local pos = obj_pos(player)
|
||||
if pos then
|
||||
player:set_detach()
|
||||
player:set_properties({visual_size = {x=1, y=1}})
|
||||
player:set_pos(pos)
|
||||
end
|
||||
-- TODO: move to save position
|
||||
end
|
||||
|
||||
|
||||
-- Attach player/mob to given parent object (block)
|
||||
local function attach_single_object(parent, obj, dir)
|
||||
local self = parent:get_luaentity()
|
||||
local rot = obj:get_rotation()
|
||||
local res = obj:get_attach()
|
||||
if not res then
|
||||
local offs = table.copy(dir)
|
||||
dir = vector.multiply(dir, 29)
|
||||
obj:set_attach(parent, "", dir, rot, true)
|
||||
obj:set_properties({visual_size = {x=2.9, y=2.9}})
|
||||
if obj:is_player() then
|
||||
if lock_player(obj) then
|
||||
table.insert(self.players, {name = obj:get_player_name(), offs = offs})
|
||||
end
|
||||
else
|
||||
table.insert(self.entities, {objID = get_object_id(obj), offs = offs})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Attach all objects around to the parent object
|
||||
-- offs is the search/attach position offset
|
||||
local function attach_objects(pos, offs, parent)
|
||||
local pos1 = vector.add(pos, offs)
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(pos1, 0.9)) do
|
||||
local dir = vector.subtract(obj:get_pos(), pos)
|
||||
local entity = obj:get_luaentity()
|
||||
if entity then
|
||||
if entity.name == "__builtin:item" then -- dropped items
|
||||
--obj:set_attach(objref, "", {x=0, y=0, z=0}, {x=0, y=0, z=0}, true) -- hier kracht es
|
||||
elseif entity.name ~= "techage:move_item" then
|
||||
attach_single_object(parent, obj, dir)
|
||||
end
|
||||
elseif obj:is_player() then
|
||||
attach_single_object(parent, obj, dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Detach all attached objects from the parent object
|
||||
local function detach_objects(pos, self)
|
||||
for _, item in ipairs(self.entities or {}) do
|
||||
local entity = minetest.luaentities[item.objID]
|
||||
if entity then
|
||||
local obj = entity.object
|
||||
obj:set_detach()
|
||||
obj:set_properties({visual_size = {x=1, y=1}})
|
||||
local pos1 = vector.add(pos, item.offs)
|
||||
obj:set_pos(pos1)
|
||||
end
|
||||
end
|
||||
for _, item in ipairs(self.players or {}) do
|
||||
local obj = minetest.get_player_by_name(item.name)
|
||||
if obj then
|
||||
obj:set_detach()
|
||||
obj:set_properties({visual_size = {x=1, y=1}})
|
||||
local pos1 = vector.add(pos, item.offs)
|
||||
obj:set_pos(pos1)
|
||||
unlock_player(obj)
|
||||
end
|
||||
end
|
||||
self.entities = {}
|
||||
self.players = {}
|
||||
end
|
||||
|
||||
local function entity_to_node(pos, obj)
|
||||
local self = obj:get_luaentity()
|
||||
if self then
|
||||
local name = self.item or "air"
|
||||
local param2 = self.param2 or 0
|
||||
local metadata = self.metadata or {}
|
||||
local rot = obj:get_rotation()
|
||||
detach_objects(pos, self)
|
||||
obj:remove()
|
||||
|
||||
pos = vector.round(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local ndef1 = minetest.registered_nodes[name]
|
||||
local ndef2 = minetest.registered_nodes[node.name]
|
||||
if ndef1 and ndef2 then
|
||||
if ndef2.buildable_to then
|
||||
local meta = M(pos)
|
||||
minetest.set_node(pos, {name=name, param2=param2})
|
||||
meta:from_table(metadata)
|
||||
meta:set_string("ta_move_block", "")
|
||||
return
|
||||
end
|
||||
local meta = M(pos)
|
||||
if not meta:contains("ta_move_block") then
|
||||
meta:set_string("ta_move_block", minetest.serialize({name=name, param2=param2}))
|
||||
return
|
||||
end
|
||||
minetest.add_item(pos, ItemStack(name))
|
||||
elseif ndef1 then
|
||||
minetest.add_item(pos, ItemStack(name))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function node_to_entity(pos, handover, pos_2to1)
|
||||
local meta = M(pos)
|
||||
local node, metadata
|
||||
|
||||
if meta:contains("ta_move_block") then
|
||||
node = minetest.deserialize(meta:get_string("ta_move_block"))
|
||||
metadata = {}
|
||||
meta:set_string("ta_move_block", "")
|
||||
else
|
||||
node = minetest.get_node(pos)
|
||||
meta:set_string("ta_move_block", "")
|
||||
metadata = meta:to_table()
|
||||
minetest.remove_node(pos)
|
||||
end
|
||||
local obj = minetest.add_entity(pos, "techage:move_item")
|
||||
if obj then
|
||||
local self = obj:get_luaentity()
|
||||
local rot = techage.facedir_to_rotation(node.param2)
|
||||
obj:set_rotation(rot)
|
||||
obj:set_properties({wield_item=node.name})
|
||||
obj:set_armor_groups({immortal=1})
|
||||
self.item = node.name
|
||||
self.param2 = node.param2
|
||||
self.metadata = metadata or {}
|
||||
self.handover = handover
|
||||
self.pos_2to1 = pos_2to1
|
||||
self.start_pos = table.copy(pos)
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
local function capture_entity(pos)
|
||||
local l = minetest.get_objects_in_area(pos, pos)
|
||||
for _, obj in ipairs(l) do
|
||||
local self = obj:get_luaentity()
|
||||
if self and self.name == "techage:move_item" then
|
||||
return obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- move block direction
|
||||
local function determine_dir(pos1, pos2)
|
||||
local vdist = vector.subtract(pos2, pos1)
|
||||
local ndist = vector.length(vdist)
|
||||
return vector.divide(vdist, ndist)
|
||||
end
|
||||
|
||||
local function move_entity(obj, pos2, dir, max_speed)
|
||||
local self = obj:get_luaentity()
|
||||
self.max_speed = max_speed
|
||||
self.dest_pos = table.copy(pos2)
|
||||
self.dir = dir
|
||||
local acc = vector.multiply(dir, max_speed / 2)
|
||||
obj:set_acceleration(acc)
|
||||
end
|
||||
|
||||
-- Handover the entity to the next movecontroller
|
||||
local function handover_to(pos, self)
|
||||
local info = techage.get_node_info(self.handover)
|
||||
if info and info.name == "techage:ta4_movecontroller" then
|
||||
local mem = techage.get_mem(info.pos)
|
||||
if not mem.entities_are_there then
|
||||
mem.entities_are_there = true
|
||||
minetest.after(0.2, techage.send_single, "0", self.handover, "handover", self.pos_2to1)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("techage:move_item", {
|
||||
initial_properties = {
|
||||
pointable = true,
|
||||
makes_footstep_sound = true,
|
||||
static_save = true,
|
||||
collide_with_objects = false,
|
||||
physical = false,
|
||||
visual = "wielditem",
|
||||
wield_item = "default:dirt",
|
||||
visual_size = {x=0.67, y=0.67, z=0.67},
|
||||
selectionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
},
|
||||
|
||||
get_staticdata = function(self)
|
||||
return minetest.serialize({
|
||||
item = self.item,
|
||||
max_speed = self.max_speed,
|
||||
dest_pos = self.dest_pos,
|
||||
start_pos = self.start_pos,
|
||||
dir = self.dir,
|
||||
metadata = self.metadata,
|
||||
respawn = true,
|
||||
})
|
||||
end,
|
||||
|
||||
on_activate = function(self, staticdata)
|
||||
if staticdata then
|
||||
local tbl = minetest.deserialize(staticdata) or {}
|
||||
self.item = tbl.item or "air"
|
||||
self.max_speed = tbl.max_speed or MAX_SPEED
|
||||
self.dest_pos = tbl.dest_pos or self.object:get_pos()
|
||||
self.start_pos = tbl.start_pos or self.object:get_pos()
|
||||
self.dir = tbl.dir or {x=0, y=0, z=0}
|
||||
self.metadata = tbl.metadata or {}
|
||||
self.object:set_properties({wield_item = self.item})
|
||||
if tbl.respawn then
|
||||
entity_to_node(self.start_pos, self.object)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
on_step = function(self, dtime, moveresult)
|
||||
if self.dest_pos then
|
||||
local obj = self.object
|
||||
local pos = obj:get_pos()
|
||||
local dist = vector.distance(pos, self.dest_pos)
|
||||
local speed = vector.length(obj:get_velocity())
|
||||
self.old_dist = self.old_dist or dist
|
||||
|
||||
-- Landing
|
||||
if dist < 0.05 or dist > self.old_dist then
|
||||
obj:move_to(self.dest_pos, true)
|
||||
obj:set_acceleration({x=0, y=0, z=0})
|
||||
obj:set_velocity({x=0, y=0, z=0})
|
||||
self.dest_pos = nil
|
||||
self.old_dist = nil
|
||||
if not self.handover or not handover_to(pos, self) then
|
||||
minetest.after(0.5, entity_to_node, pos, obj)
|
||||
end
|
||||
self.ttl = 2
|
||||
return
|
||||
end
|
||||
|
||||
self.old_dist = dist
|
||||
|
||||
-- Braking or limit max speed
|
||||
if speed > (dist * 2) or speed > self.max_speed then
|
||||
local speed = math.min(speed, math.max(dist * 2, MIN_SPEED))
|
||||
local vel = vector.multiply(self.dir,speed)
|
||||
obj:set_velocity(vel)
|
||||
obj:set_acceleration({x=0, y=0, z=0})
|
||||
end
|
||||
elseif self.ttl then
|
||||
self.ttl = self.ttl - dtime
|
||||
if self.ttl < 0 then
|
||||
local obj = self.object
|
||||
local pos = obj:get_pos()
|
||||
entity_to_node(pos, obj)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Marker / Record
|
||||
-------------------------------------------------------------------------------
|
||||
local MarkedNodes = {} -- t[player] = {{entity, pos},...}
|
||||
local CurrentPos -- to mark punched entities
|
||||
local SimpleNodes = techage.logic.SimpleNodes
|
||||
|
||||
local function is_valid_dest(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef and ndef.buildable_to then
|
||||
return true
|
||||
end
|
||||
if not M(pos):contains("ta_move_block") then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function is_simple_node(pos)
|
||||
-- special handling
|
||||
local name = minetest.get_node(pos).name
|
||||
if SimpleNodes[name] ~= nil then
|
||||
return SimpleNodes[name]
|
||||
end
|
||||
|
||||
local ndef = minetest.registered_nodes[name]
|
||||
if not ndef or name == "air" or name == "ignore" then return false end
|
||||
-- don't remove nodes with some intelligence or undiggable nodes
|
||||
if ndef.drop == "" then return false end
|
||||
if ndef.diggable == false then return false end
|
||||
if ndef.after_dig_node then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function table_add(tbl, offs)
|
||||
if not tbl or not offs then return end
|
||||
|
||||
local tbl2 = {}
|
||||
for _, v in ipairs(tbl) do
|
||||
tbl2[#tbl2 + 1] = vector.add(v, offs)
|
||||
end
|
||||
return tbl2
|
||||
end
|
||||
|
||||
local function unmark_position(name, pos)
|
||||
pos = vector.round(pos)
|
||||
for idx,item in ipairs(MarkedNodes[name] or {}) do
|
||||
if vector.equals(pos, item.pos) then
|
||||
item.entity:remove()
|
||||
table.remove(MarkedNodes[name], idx)
|
||||
CurrentPos = pos
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function unmark_all(name)
|
||||
for _,item in ipairs(MarkedNodes[name] or {}) do
|
||||
item.entity:remove()
|
||||
end
|
||||
MarkedNodes[name] = nil
|
||||
end
|
||||
|
||||
local function mark_position(name, pos)
|
||||
MarkedNodes[name] = MarkedNodes[name] or {}
|
||||
pos = vector.round(pos)
|
||||
if not CurrentPos or not vector.equals(pos, CurrentPos) then -- entity not punched?
|
||||
local entity = minetest.add_entity(pos, "techage:moveblock_marker")
|
||||
if entity ~= nil then
|
||||
entity:get_luaentity().player_name = name
|
||||
table.insert(MarkedNodes[name], {pos = pos, entity = entity})
|
||||
end
|
||||
CurrentPos = nil
|
||||
return true
|
||||
end
|
||||
CurrentPos = nil
|
||||
end
|
||||
|
||||
local function get_poslist(name)
|
||||
local idx = 0
|
||||
local lst = {}
|
||||
for _,item in ipairs(MarkedNodes[name] or {}) do
|
||||
table.insert(lst, item.pos)
|
||||
idx = idx + 1
|
||||
if idx >= 16 then break end
|
||||
end
|
||||
return lst
|
||||
end
|
||||
|
||||
minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing)
|
||||
if puncher and puncher:is_player() then
|
||||
local name = puncher:get_player_name()
|
||||
|
||||
if not MarkedNodes[name] then
|
||||
return
|
||||
end
|
||||
|
||||
mark_position(name, pointed_thing.under)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
minetest.register_entity(":techage:moveblock_marker", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
textures = {
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
},
|
||||
--use_texture_alpha = true,
|
||||
physical = false,
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
collisionbox = {-0.55,-0.55,-0.55, 0.55,0.55,0.55},
|
||||
glow = 8,
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
self.ttl = (self.ttl or 2400) - 1
|
||||
if self.ttl <= 0 then
|
||||
local pos = self.object:get_pos()
|
||||
unmark_position(self.player_name, pos)
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
local pos = self.object:get_pos()
|
||||
local name = hitter:get_player_name()
|
||||
if name == self.player_name then
|
||||
unmark_position(name, pos)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- TA4 Move Controller
|
||||
-------------------------------------------------------------------------------
|
||||
local WRENCH_MENU = {
|
||||
{
|
||||
type = "dropdown",
|
||||
choices = "0.5,1,2,4,6,8",
|
||||
name = "max_speed",
|
||||
label = S("Maximum Speed"),
|
||||
tooltip = S("Maximum speed for the moving block."),
|
||||
default = "8",
|
||||
},
|
||||
{
|
||||
type = "number",
|
||||
name = "handoverB",
|
||||
label = S("Handover to B"),
|
||||
tooltip = S("Number of the next movecontroller."),
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "number",
|
||||
name = "handoverA",
|
||||
label = S("Handover to A"),
|
||||
tooltip = S("Number of the previous movecontroller."),
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "float",
|
||||
name = "height",
|
||||
label = S("Move block height"),
|
||||
tooltip = S("Value in the range of 0.0 to 1.0"),
|
||||
default = "1.0",
|
||||
},
|
||||
}
|
||||
|
||||
local function formspec(nvm, meta)
|
||||
local status = meta:get_string("status")
|
||||
local distance = meta:contains("distance") and meta:get_string("distance") or "0,3,0"
|
||||
return "size[8,5]" ..
|
||||
default.gui_bg ..
|
||||
default.gui_bg_img ..
|
||||
default.gui_slots ..
|
||||
"box[0,-0.1;7.2,0.5;#c6e8ff]" ..
|
||||
"label[0.2,-0.1;" .. minetest.colorize( "#000000", S("TA4 Move Controller")) .. "]" ..
|
||||
techage.wrench_image(7.4, -0.05) ..
|
||||
"button[0.1,0.8;3.8,1;record;" .. S("Record") .. "]" ..
|
||||
"button[4.1,0.8;3.8,1;ready;" .. S("Done") .. "]" ..
|
||||
"field[0.4,2.5;3.8,1;distance;" .. S("Move distance (A to B)") .. ";" .. distance .. "]" ..
|
||||
"button[4.1,2.2;3.8,1;store;" .. S("Store") .. "]" ..
|
||||
"button[0.1,3.3;3.8,1;moveAB;" .. S("Move A-B") .. "]" ..
|
||||
"button[4.1,3.3;3.8,1;moveBA;" .. S("Move B-A") .. "]" ..
|
||||
"label[0.3,4.3;" .. status .. "]"
|
||||
end
|
||||
|
||||
local function move_node(pos, pos1, pos2, max_speed, handover, height, pos_2to1)
|
||||
local dir = determine_dir(pos1, pos2)
|
||||
local obj = node_to_entity(pos1, handover, pos_2to1)
|
||||
local self = obj:get_luaentity()
|
||||
self.players = {}
|
||||
self.entities = {}
|
||||
|
||||
if obj then
|
||||
local offs = {x=0, y=height or 1, z=0}
|
||||
attach_objects(pos1, offs, obj)
|
||||
if dir.y == 0 then
|
||||
if (dir.x ~= 0 and dir.z == 0) or (dir.x == 0 and dir.z ~= 0) then
|
||||
attach_objects(pos1, dir, obj)
|
||||
end
|
||||
end
|
||||
move_entity(obj, pos2, dir, max_speed)
|
||||
end
|
||||
end
|
||||
|
||||
local function move_nodes(pos, lpos1, lpos2, handover, pos_2to1)
|
||||
local meta = M(pos)
|
||||
local owner = meta:get_string("owner")
|
||||
local max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED
|
||||
local height = meta:contains("height") and meta:get_float("height") or 1
|
||||
height = techage.in_range(height, 0, 1)
|
||||
|
||||
if #lpos1 == #lpos2 then
|
||||
for idx = 1, #lpos1 do
|
||||
local pos1 = lpos1[idx]
|
||||
local pos2 = lpos2[idx]
|
||||
if not minetest.is_protected(pos1, owner) and not minetest.is_protected(pos2, owner) then
|
||||
if is_simple_node(pos1) and is_valid_dest(pos2) then
|
||||
move_node(pos, pos1, pos2, max_speed, handover, height, pos_2to1)
|
||||
else
|
||||
if not is_simple_node(pos1) then
|
||||
meta:set_string("status", S("No valid node at the start position"))
|
||||
else
|
||||
meta:set_string("status", S("No valid destination position"))
|
||||
end
|
||||
end
|
||||
else
|
||||
if minetest.is_protected(pos1, owner) then
|
||||
meta:set_string("status", S("Start position is protected"))
|
||||
else
|
||||
meta:set_string("status", S("Destination position is protected"))
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
else
|
||||
meta:set_string("status", S("Position list error"))
|
||||
return false
|
||||
end
|
||||
local info = techage.get_node_info(handover)
|
||||
if info and info.name == "techage:ta4_movecontroller" then
|
||||
local mem = techage.get_mem(info.pos)
|
||||
mem.num_entities = #lpos1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function moveon_nodes(pos, lpos1, lpos2, handover)
|
||||
local meta = M(pos)
|
||||
local owner = meta:get_string("owner")
|
||||
local max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED
|
||||
|
||||
if #lpos1 == #lpos2 then
|
||||
for idx = 1, #lpos1 do
|
||||
local pos1 = lpos1[idx]
|
||||
local pos2 = lpos2[idx]
|
||||
if not minetest.is_protected(pos1, owner) and not minetest.is_protected(pos2, owner) then
|
||||
if is_valid_dest(pos2) then
|
||||
local dir = determine_dir(pos1, pos2)
|
||||
local obj = capture_entity(pos1)
|
||||
if obj then
|
||||
obj:get_luaentity().handover = handover
|
||||
move_entity(obj, pos2, dir, max_speed)
|
||||
end
|
||||
else
|
||||
if not is_simple_node(pos1) then
|
||||
meta:set_string("status", S("No valid node at the start position"))
|
||||
else
|
||||
meta:set_string("status", S("No valid destination position"))
|
||||
end
|
||||
end
|
||||
else
|
||||
if minetest.is_protected(pos1, owner) then
|
||||
meta:set_string("status", S("Start position is protected"))
|
||||
else
|
||||
meta:set_string("status", S("Destination position is protected"))
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
else
|
||||
meta:set_string("status", S("Position list error"))
|
||||
return false
|
||||
end
|
||||
local info = techage.get_node_info(handover)
|
||||
if info and info.name == "techage:ta4_movecontroller" then
|
||||
local mem = techage.get_mem(info.pos)
|
||||
mem.num_entities = #lpos1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function move_to_other_pos(pos, pos_2to1)
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
|
||||
if pos_2to1 then
|
||||
local lpos1 = nvm.lpos1 or {}
|
||||
local lpos2 = nvm.lpos2 or {}
|
||||
local handover = meta:contains("handoverA") and meta:get_string("handoverA")
|
||||
return move_nodes(pos, lpos2, lpos1, handover, pos_2to1)
|
||||
else
|
||||
local lpos1 = nvm.lpos1 or {}
|
||||
local lpos2 = nvm.lpos2 or {}
|
||||
local handover = meta:contains("handoverB") and meta:get_string("handoverB")
|
||||
return move_nodes(pos, lpos1, lpos2, handover, pos_2to1)
|
||||
end
|
||||
end
|
||||
|
||||
local function takeover(pos, pos_2to1)
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
mem.entities_are_there = nil
|
||||
|
||||
if pos_2to1 then
|
||||
local lpos1 = nvm.lpos1 or {}
|
||||
local lpos2 = nvm.lpos2 or {}
|
||||
local handover = meta:contains("handoverA") and meta:get_string("handoverA")
|
||||
return moveon_nodes(pos, lpos2, lpos1, handover)
|
||||
else
|
||||
local lpos1 = nvm.lpos1 or {}
|
||||
local lpos2 = nvm.lpos2 or {}
|
||||
local handover = meta:contains("handoverB") and meta:get_string("handoverB")
|
||||
return moveon_nodes(pos, lpos1, lpos2, handover)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("techage:ta4_movecontroller", {
|
||||
description = S("TA4 Move Controller"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_movecontroller.png",
|
||||
},
|
||||
|
||||
after_place_node = function(pos, placer, itemstack)
|
||||
local meta = M(pos)
|
||||
techage.logic.after_place_node(pos, placer, "techage:ta4_movecontroller", S("TA4 Move Controller"))
|
||||
techage.logic.infotext(meta, S("TA4 Move Controller"))
|
||||
local nvm = techage.get_nvm(pos)
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
|
||||
if fields.record then
|
||||
nvm.lpos1 = nil
|
||||
nvm.lpos2 = nil
|
||||
nvm.pos_2to1 = false
|
||||
meta:set_string("status", S("Recording..."))
|
||||
local name = player:get_player_name()
|
||||
minetest.chat_send_player(name, S("Click on all blocks that shall be moved"))
|
||||
MarkedNodes[name] = {}
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
elseif fields.ready then
|
||||
local name = player:get_player_name()
|
||||
local pos_list = get_poslist(name)
|
||||
local text = #pos_list.." "..S("block positions are stored.")
|
||||
meta:set_string("status", text)
|
||||
meta:set_string("distance", fields.distance)
|
||||
nvm.lpos1 = pos_list
|
||||
nvm.lpos2 = table_add(pos_list, to_vector(fields.distance))
|
||||
nvm.pos_2to1 = false
|
||||
unmark_all(name)
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
elseif fields.store then
|
||||
local dist = to_vector(fields.distance)
|
||||
local l = math.hypot(dist.x, math.hypot(dist.y, dist.z))
|
||||
if l <= 100 then
|
||||
meta:set_string("distance", fields.distance)
|
||||
nvm.lpos2 = table_add(nvm.lpos1, to_vector(fields.distance))
|
||||
nvm.pos_2to1 = false
|
||||
else
|
||||
meta:set_string("status", S("Error: Distance > 100 m !!"))
|
||||
end
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
elseif fields.moveAB then
|
||||
meta:set_string("status", "")
|
||||
nvm.pos_2to1 = false
|
||||
if move_to_other_pos(pos, false) then
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
local name = player:get_player_name()
|
||||
MarkedNodes[name] = nil
|
||||
end
|
||||
elseif fields.moveBA then
|
||||
meta:set_string("status", "")
|
||||
if move_to_other_pos(pos, true) then
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
local name = player:get_player_name()
|
||||
MarkedNodes[name] = nil
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
local name = digger:get_player_name()
|
||||
unmark_all(name)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
end,
|
||||
|
||||
ta4_formspec = WRENCH_MENU,
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
local INFO = [[Commands: 'a2b', 'b2a']]
|
||||
|
||||
techage.register_node({"techage:ta4_movecontroller"}, {
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "info" then
|
||||
return INFO
|
||||
elseif topic == "a2b" then
|
||||
return move_to_other_pos(pos, false)
|
||||
elseif topic == "b2a" then
|
||||
return move_to_other_pos(pos, true)
|
||||
elseif topic == "handover" then
|
||||
return takeover(pos, payload)
|
||||
end
|
||||
return false
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "techage:ta4_movecontroller",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "dye:blue", "default:steel_ingot"},
|
||||
{"default:mese_crystal_fragment", "techage:ta4_wlanchip", "default:mese_crystal_fragment"},
|
||||
{"group:wood", "basic_materials:gear_steel", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
unlock_player(player)
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
if unlock_player(player) then
|
||||
detach_player(player)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_dieplayer(function(player)
|
||||
if unlock_player(player) then
|
||||
detach_player(player)
|
||||
end
|
||||
end)
|
@ -531,7 +531,7 @@ Da die bewegten Blöcke Spieler und Mobs mitnehmen können, die auf dem Block st
|
||||
Anleitung:
|
||||
|
||||
- Controller setzen und die Blöcke, die bewegt werden sollen, über das Menü an-trainieren (Es können bis zu 16 Blöcke an-trainiert werden)
|
||||
- die "Flugstrecke" muss über eine x,y,z Angabe (relativ) eingegeben werden (die maximale Distanz beträgt 100 m)
|
||||
- die "Flugstrecke" muss über eine x,y,z Angabe (relativ) eingegeben werden (die maximale Distanz beträgt 200 m)
|
||||
- mit den Menü-Tasten "Bewege A-B" sowie "Bewege B-A" kann die Bewegung getestet werden
|
||||
- man kann auch durch Wände oder andere Blöcke fliegen
|
||||
- auch die Zielposition für die Blöcke kann belegt sein. Die Blöcke werden in diesem Falle "unsichtbar" gespeichert. Dies ist für Schiebetüren und ähnliches gedacht
|
||||
|
@ -525,7 +525,7 @@ Since the moving blocks can take players and mobs standing on the block with the
|
||||
Instructions:
|
||||
|
||||
- Set the controller and train the blocks to be moved via the menu (up to 16 blocks can be trained)
|
||||
- the "flight route" must be entered via an x, y, z specification (relative) (the maximum distance is 100 m)
|
||||
- the "flight route" must be entered via an x, y, z specification (relative) (the maximum distance is 200 m)
|
||||
- The movement can be tested with the menu buttons "Move A-B" and "Move B-A"
|
||||
- you can also fly through walls or other blocks
|
||||
- The target position for the blocks can also be occupied. In this case, the blocks are saved "invisibly". This is intended for sliding doors and the like
|
||||
|
169
move_controller/flycontroller.lua
Normal file
169
move_controller/flycontroller.lua
Normal file
@ -0,0 +1,169 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2020-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA4 Move Controller
|
||||
|
||||
]]--
|
||||
|
||||
-- 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 MP = minetest.get_modpath("techage")
|
||||
local fly = dofile(MP .. "/basis/fly_lib.lua")
|
||||
local mark = dofile(MP .. "/basis/mark_lib.lua")
|
||||
|
||||
local MAX_DIST = 200
|
||||
local MAX_BLOCKS = 16
|
||||
|
||||
local WRENCH_MENU = {
|
||||
{
|
||||
type = "dropdown",
|
||||
choices = "0.5,1,2,4,6,8",
|
||||
name = "max_speed",
|
||||
label = S("Maximum Speed"),
|
||||
tooltip = S("Maximum speed for moving blocks"),
|
||||
default = "8",
|
||||
},
|
||||
{
|
||||
type = "float",
|
||||
name = "height",
|
||||
label = S("Move block height"),
|
||||
tooltip = S("Value in the range of 0.0 to 1.0"),
|
||||
default = "1.0",
|
||||
},
|
||||
}
|
||||
|
||||
local function formspec(nvm, meta)
|
||||
local status = meta:get_string("status")
|
||||
local path = meta:contains("path") and meta:get_string("path") or "0,3,0"
|
||||
return "size[8,7]" ..
|
||||
"style_type[textarea;font=mono;textcolor=#FFFFFF;border=true]" ..
|
||||
"box[0,-0.1;7.2,0.5;#c6e8ff]" ..
|
||||
"label[0.2,-0.1;" .. minetest.colorize( "#000000", S("TA5 Fly Controller")) .. "]" ..
|
||||
techage.wrench_image(7.4, -0.05) ..
|
||||
"button[0.1,0.7;3.8,1;record;" .. S("Record") .. "]" ..
|
||||
"button[4.1,0.7;3.8,1;done;" .. S("Done") .. "]" ..
|
||||
"textarea[0.4,2.1;3.8,3.8;path;" .. S("Move path (A to B)") .. ";"..path.."]" ..
|
||||
"button[4.1,3.2;3.8,1;store;" .. S("Store") .. "]" ..
|
||||
"button[0.1,5.5;3.8,1;moveAB;" .. S("Move A-B") .. "]" ..
|
||||
"button[4.1,5.5;3.8,1;moveBA;" .. S("Move B-A") .. "]" ..
|
||||
"label[0.3,6.5;" .. status .. "]"
|
||||
end
|
||||
|
||||
|
||||
minetest.register_node("techage:ta5_flycontroller", {
|
||||
description = S("TA5 Fly Controller"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_movecontroller.png",
|
||||
},
|
||||
|
||||
after_place_node = function(pos, placer, itemstack)
|
||||
local meta = M(pos)
|
||||
techage.logic.after_place_node(pos, placer, "techage:ta5_flycontroller", S("TA5 Fly Controller"))
|
||||
techage.logic.infotext(meta, S("TA5 Fly Controller"))
|
||||
local nvm = techage.get_nvm(pos)
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
|
||||
if fields.record then
|
||||
nvm.lpos1 = {}
|
||||
nvm.lpos2 = {}
|
||||
meta:set_string("status", S("Recording..."))
|
||||
local name = player:get_player_name()
|
||||
minetest.chat_send_player(name, S("Click on all blocks that shall be moved"))
|
||||
mark.start(name, MAX_BLOCKS)
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
elseif fields.done then
|
||||
local name = player:get_player_name()
|
||||
local pos_list = mark.get_poslist(name)
|
||||
local text = #pos_list.." "..S("block positions are stored.")
|
||||
meta:set_string("status", text)
|
||||
nvm.lpos1 = pos_list
|
||||
mark.unmark_all(name)
|
||||
mark.stop(name)
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
elseif fields.store then
|
||||
if fly.to_path(fields.path, MAX_DIST) then
|
||||
meta:set_string("path", fields.path)
|
||||
meta:set_string("status", S("Stored"))
|
||||
else
|
||||
meta:set_string("status", S("Error: Invalid path !!"))
|
||||
end
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
local name = player:get_player_name()
|
||||
mark.stop(name)
|
||||
elseif fields.moveAB then
|
||||
meta:set_string("status", "")
|
||||
if fly.move_to_other_pos(pos, false) then
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
local name = player:get_player_name()
|
||||
mark.stop(name)
|
||||
end
|
||||
elseif fields.moveBA then
|
||||
meta:set_string("status", "")
|
||||
if fly.move_to_other_pos(pos, true) then
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
local name = player:get_player_name()
|
||||
mark.stop(name)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
local name = digger:get_player_name()
|
||||
mark.unmark_all(name)
|
||||
mark.stop(name)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
end,
|
||||
|
||||
ta4_formspec = WRENCH_MENU,
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
local INFO = [[Commands: 'a2b', 'b2a']]
|
||||
|
||||
techage.register_node({"techage:ta5_flycontroller"}, {
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "info" then
|
||||
return INFO
|
||||
elseif topic == "a2b" then
|
||||
return fly.move_to_other_pos(pos, false)
|
||||
elseif topic == "b2a" then
|
||||
return fly.move_to_other_pos(pos, true)
|
||||
end
|
||||
return false
|
||||
end,
|
||||
})
|
||||
|
||||
--minetest.register_craft({
|
||||
-- output = "techage:ta5_flycontroller",
|
||||
-- recipe = {
|
||||
-- {"default:steel_ingot", "dye:blue", "default:steel_ingot"},
|
||||
-- {"default:mese_crystal_fragment", "techage:ta4_wlanchip", "default:mese_crystal_fragment"},
|
||||
-- {"group:wood", "basic_materials:gear_steel", "group:wood"},
|
||||
-- },
|
||||
--})
|
208
move_controller/movecontroller.lua
Normal file
208
move_controller/movecontroller.lua
Normal file
@ -0,0 +1,208 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2020-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA4 Move Controller
|
||||
|
||||
]]--
|
||||
|
||||
-- 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 MP = minetest.get_modpath("techage")
|
||||
local fly = dofile(MP .. "/basis/fly_lib.lua")
|
||||
local mark = dofile(MP .. "/basis/mark_lib.lua")
|
||||
|
||||
local MAX_DIST = 100
|
||||
local MAX_BLOCKS = 16
|
||||
|
||||
-- Determine and store the path in the first and in the last block of the chain
|
||||
local function store_path(pos)
|
||||
local lpath = {}
|
||||
local pos2 = table.copy(pos)
|
||||
while pos2 do
|
||||
local meta = M(pos2)
|
||||
lpath[#lpath + 1] = meta:get_string("distance")
|
||||
local number = meta:get_string("handoverB")
|
||||
local info = techage.get_node_info(number)
|
||||
if info and info.name == "techage:ta4_movecontroller" then
|
||||
pos2 = info.pos
|
||||
else
|
||||
local s = table.concat(lpath, "\n")
|
||||
M(pos):set_string("path", s) -- first block
|
||||
M(pos2):set_string("path", s) -- last block
|
||||
techage.get_nvm(pos2).lpos1 = techage.get_nvm(pos).lpos2
|
||||
pos2 = nil
|
||||
end
|
||||
end
|
||||
return #lpath
|
||||
end
|
||||
|
||||
local WRENCH_MENU = {
|
||||
{
|
||||
type = "dropdown",
|
||||
choices = "0.5,1,2,4,6,8",
|
||||
name = "max_speed",
|
||||
label = S("Maximum Speed"),
|
||||
tooltip = S("Maximum speed for moving blocks"),
|
||||
default = "8",
|
||||
},
|
||||
{
|
||||
type = "number",
|
||||
name = "handoverB",
|
||||
label = S("Handover to B"),
|
||||
tooltip = S("Number of the next movecontroller"),
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "number",
|
||||
name = "handoverA",
|
||||
label = S("Handover to A"),
|
||||
tooltip = S("Number of the previous movecontroller"),
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "float",
|
||||
name = "height",
|
||||
label = S("Move block height"),
|
||||
tooltip = S("Value in the range of 0.0 to 1.0"),
|
||||
default = "1.0",
|
||||
},
|
||||
}
|
||||
|
||||
local function formspec(nvm, meta)
|
||||
local status = meta:get_string("status")
|
||||
local distance = meta:contains("distance") and meta:get_string("distance") or "0,3,0"
|
||||
return "size[8,5]" ..
|
||||
default.gui_bg ..
|
||||
default.gui_bg_img ..
|
||||
default.gui_slots ..
|
||||
"box[0,-0.1;7.2,0.5;#c6e8ff]" ..
|
||||
"label[0.2,-0.1;" .. minetest.colorize( "#000000", S("TA4 Move Controller")) .. "]" ..
|
||||
techage.wrench_image(7.4, -0.05) ..
|
||||
"button[0.1,0.8;3.8,1;record;" .. S("Record") .. "]" ..
|
||||
"button[4.1,0.8;3.8,1;done;" .. S("Done") .. "]" ..
|
||||
"field[0.4,2.5;3.8,1;distance;" .. S("Move distance (A to B)") .. ";" .. distance .. "]" ..
|
||||
"button[4.1,2.2;3.8,1;store;" .. S("Store") .. "]" ..
|
||||
"button_exit[0.1,3.3;3.8,1;moveAB;" .. S("Move A-B") .. "]" ..
|
||||
"button_exit[4.1,3.3;3.8,1;moveBA;" .. S("Move B-A") .. "]" ..
|
||||
"label[0.3,4.3;" .. status .. "]"
|
||||
end
|
||||
|
||||
minetest.register_node("techage:ta4_movecontroller", {
|
||||
description = S("TA4 Move Controller"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_movecontroller.png",
|
||||
},
|
||||
|
||||
after_place_node = function(pos, placer, itemstack)
|
||||
local meta = M(pos)
|
||||
techage.logic.after_place_node(pos, placer, "techage:ta4_movecontroller", S("TA4 Move Controller"))
|
||||
techage.logic.infotext(meta, S("TA4 Move Controller"))
|
||||
local nvm = techage.get_nvm(pos)
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
|
||||
if fields.record then
|
||||
nvm.lpos1 = {}
|
||||
nvm.lpos2 = {}
|
||||
meta:set_string("status", S("Recording..."))
|
||||
local name = player:get_player_name()
|
||||
minetest.chat_send_player(name, S("Click on all blocks that shall be moved"))
|
||||
mark.start(name, MAX_BLOCKS)
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
elseif fields.done then
|
||||
local name = player:get_player_name()
|
||||
local pos_list = mark.get_poslist(name)
|
||||
local text = #pos_list.." "..S("block positions are stored.")
|
||||
meta:set_string("status", text)
|
||||
nvm.lpos1 = pos_list
|
||||
mark.unmark_all(name)
|
||||
mark.stop(name)
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
elseif fields.store then
|
||||
if fly.to_vector(fields.distance, MAX_DIST) then
|
||||
meta:set_string("distance", fields.distance)
|
||||
meta:set_string("status", S("Stored"))
|
||||
meta:set_string("path", "")
|
||||
else
|
||||
meta:set_string("status", S("Error: Invalid distance !!"))
|
||||
end
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
local name = player:get_player_name()
|
||||
mark.stop(name)
|
||||
elseif fields.moveAB then
|
||||
meta:set_string("status", "")
|
||||
--store_path(pos)
|
||||
if fly.move_to_other_pos(pos, false) then
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
local name = player:get_player_name()
|
||||
mark.stop(name)
|
||||
end
|
||||
elseif fields.moveBA then
|
||||
meta:set_string("status", "")
|
||||
if fly.move_to_other_pos(pos, true) then
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
local name = player:get_player_name()
|
||||
mark.stop(name)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
local name = digger:get_player_name()
|
||||
mark.unmark_all(name)
|
||||
mark.stop(name)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
end,
|
||||
|
||||
ta4_formspec = WRENCH_MENU,
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
local INFO = [[Commands: 'a2b', 'b2a']]
|
||||
|
||||
techage.register_node({"techage:ta4_movecontroller"}, {
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "info" then
|
||||
return INFO
|
||||
elseif topic == "a2b" then
|
||||
return fly.move_to_other_pos(pos, false)
|
||||
elseif topic == "b2a" then
|
||||
return fly.move_to_other_pos(pos, true)
|
||||
end
|
||||
return false
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "techage:ta4_movecontroller",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "dye:blue", "default:steel_ingot"},
|
||||
{"default:mese_crystal_fragment", "techage:ta4_wlanchip", "default:mese_crystal_fragment"},
|
||||
{"group:wood", "basic_materials:gear_steel", "group:wood"},
|
||||
},
|
||||
})
|
174
move_controller/turncontroller.lua
Normal file
174
move_controller/turncontroller.lua
Normal file
@ -0,0 +1,174 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2020-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA4 Turn Controller
|
||||
|
||||
]]--
|
||||
|
||||
-- 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 MP = minetest.get_modpath("techage")
|
||||
local fly = dofile(MP .. "/basis/fly_lib.lua")
|
||||
local mark = dofile(MP .. "/basis/mark_lib.lua")
|
||||
|
||||
local MAX_BLOCKS = 16
|
||||
local WRENCH_MENU = {
|
||||
{
|
||||
type = "ascii",
|
||||
name = "center",
|
||||
label = S("Center Pos"),
|
||||
tooltip = S("Center block position for the turn, e.g.: 237,6,-125"),
|
||||
default = "",
|
||||
},
|
||||
}
|
||||
|
||||
local function formspec(nvm, meta)
|
||||
local status = meta:get_string("status")
|
||||
local path = meta:contains("path") and meta:get_string("path") or "0,3,0"
|
||||
return "size[8,3]" ..
|
||||
"box[0,-0.1;7.2,0.5;#c6e8ff]" ..
|
||||
"label[0.2,-0.1;" .. minetest.colorize( "#000000", S("TA4 Turn Controller")) .. "]" ..
|
||||
techage.wrench_image(7.4, -0.05) ..
|
||||
"button[0.1,0.7;3.8,1;record;" .. S("Record") .. "]" ..
|
||||
"button[4.1,0.7;3.8,1;done;" .. S("Done") .. "]" ..
|
||||
"button[0.1,1.5;3.8,1;left;" .. S("Turn left") .. "]" ..
|
||||
"button[4.1,1.5;3.8,1;right;" .. S("Turn right") .. "]" ..
|
||||
"label[0.3,2.5;" .. status .. "]"
|
||||
end
|
||||
|
||||
|
||||
minetest.register_node("techage:ta4_turncontroller", {
|
||||
description = S("TA4 Turn Controller"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_turn.png",
|
||||
},
|
||||
|
||||
after_place_node = function(pos, placer, itemstack)
|
||||
local meta = M(pos)
|
||||
techage.logic.after_place_node(pos, placer, "techage:ta4_turncontroller", S("TA4 Turn Controller"))
|
||||
techage.logic.infotext(meta, S("TA4 Turn Controller"))
|
||||
local nvm = techage.get_nvm(pos)
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
|
||||
if fields.record then
|
||||
nvm.lpos1 = {}
|
||||
nvm.lpos2 = {}
|
||||
meta:set_string("status", S("Recording..."))
|
||||
local name = player:get_player_name()
|
||||
minetest.chat_send_player(name, S("Click on all blocks that shall be turned"))
|
||||
mark.start(name, MAX_BLOCKS)
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
elseif fields.done then
|
||||
local name = player:get_player_name()
|
||||
local pos_list = mark.get_poslist(name)
|
||||
local text = #pos_list.." "..S("block positions are stored.")
|
||||
meta:set_string("status", text)
|
||||
nvm.lpos = pos_list
|
||||
mark.unmark_all(name)
|
||||
mark.stop(name)
|
||||
meta:set_string("formspec", formspec(nvm, meta))
|
||||
elseif fields.left then
|
||||
meta:set_string("status", "")
|
||||
local new_posses = fly.rotate_nodes(pos, nvm.lpos, "l")
|
||||
if new_posses then
|
||||
nvm.lpos = new_posses
|
||||
local name = player:get_player_name()
|
||||
mark.stop(name)
|
||||
print("new_posses", #new_posses)
|
||||
end
|
||||
elseif fields.right then
|
||||
meta:set_string("status", "")
|
||||
local new_posses = fly.rotate_nodes(pos, nvm.lpos, "r")
|
||||
if new_posses then
|
||||
nvm.lpos = new_posses
|
||||
local name = player:get_player_name()
|
||||
mark.stop(name)
|
||||
print("new_posses", #new_posses)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
local name = digger:get_player_name()
|
||||
mark.unmark_all(name)
|
||||
mark.stop(name)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
end,
|
||||
|
||||
ta4_formspec = WRENCH_MENU,
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
local INFO = [[Commands: 'left', 'right', 'uturn']]
|
||||
|
||||
techage.register_node({"techage:ta4_turncontroller"}, {
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "info" then
|
||||
return INFO
|
||||
elseif topic == "left" then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local new_posses = fly.rotate_nodes(pos, nvm.lpos, "l")
|
||||
if new_posses then
|
||||
nvm.lpos = new_posses
|
||||
return true
|
||||
end
|
||||
return false
|
||||
elseif topic == "right" then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local new_posses = fly.rotate_nodes(pos, nvm.lpos, "r")
|
||||
if new_posses then
|
||||
nvm.lpos = new_posses
|
||||
return true
|
||||
end
|
||||
return false
|
||||
elseif topic == "uturn" then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local new_posses = fly.rotate_nodes(pos, nvm.lpos, "r")
|
||||
if new_posses then
|
||||
nvm.lpos = new_posses
|
||||
new_posses = fly.rotate_nodes(pos, nvm.lpos, "r")
|
||||
if new_posses then
|
||||
nvm.lpos = new_posses
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
return false
|
||||
end,
|
||||
})
|
||||
|
||||
--minetest.register_craft({
|
||||
-- output = "techage:ta5_flycontroller",
|
||||
-- recipe = {
|
||||
-- {"default:steel_ingot", "dye:blue", "default:steel_ingot"},
|
||||
-- {"default:mese_crystal_fragment", "techage:ta4_wlanchip", "default:mese_crystal_fragment"},
|
||||
-- {"group:wood", "basic_materials:gear_steel", "group:wood"},
|
||||
-- },
|
||||
--})
|
BIN
textures/techage_appl_turn.png
Normal file
BIN
textures/techage_appl_turn.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 708 B |
Loading…
Reference in New Issue
Block a user