Add fly and turn controllers

master
Joachim Stolberg 2021-11-15 19:26:59 +01:00
parent deab00aa07
commit 5597836d06
21 changed files with 1447 additions and 821 deletions

View File

@ -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
})

View File

@ -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
View 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

View File

@ -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
View 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

View File

@ -1610,11 +1610,11 @@ 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"..
" - Über das Gabelschlüssel-Menü kann im Controller auch ein \"handover\" programmiert werden. Durch Eingabe einer Blocknummer werden die Blöcke dann an den nächsten Move Controller übergeben. So lassen sich auch zusammenhängende Bewegungen über mehrere Move Controller realisieren.\n"..
" - Über das Gabelschlüssel-Menü kann im Controller auch ein \"handover\" programmiert werden. Durch Eingabe einer Blocknummer werden die Blöcke dann an den nächsten Move Controller übergeben. So lassen sich auch zusammenhängende Bewegungen über mehrere Move Controller realisieren.\n"..
"\n"..
"Der Move Controller unterstützt folgende techage Kommandos:\n"..
"\n"..

View File

@ -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"..

View File

@ -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")

View File

@ -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

View File

@ -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=

View File

@ -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)

View File

@ -531,11 +531,11 @@ 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
- Über das Gabelschlüssel-Menü kann im Controller auch ein "handover" programmiert werden. Durch Eingabe einer Blocknummer werden die Blöcke dann an den nächsten Move Controller übergeben. So lassen sich auch zusammenhängende Bewegungen über mehrere Move Controller realisieren.
- Über das Gabelschlüssel-Menü kann im Controller auch ein "handover" programmiert werden. Durch Eingabe einer Blocknummer werden die Blöcke dann an den nächsten Move Controller übergeben. So lassen sich auch zusammenhängende Bewegungen über mehrere Move Controller realisieren.
Der Move Controller unterstützt folgende techage Kommandos:

View File

@ -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

View 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"},
-- },
--})

View 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"},
},
})

View 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"},
-- },
--})

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B