diff --git a/basis/command.lua b/basis/command.lua index f09fe94..28afa00 100644 --- a/basis/command.lua +++ b/basis/command.lua @@ -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 []"), + 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 []" + end +}) diff --git a/basis/counting.lua b/basis/counting.lua index 265b048..33db84d 100644 --- a/basis/counting.lua +++ b/basis/counting.lua @@ -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 diff --git a/basis/fly_lib.lua b/basis/fly_lib.lua new file mode 100644 index 0000000..9eeb126 --- /dev/null +++ b/basis/fly_lib.lua @@ -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 diff --git a/basis/lib.lua b/basis/lib.lua index 7905a85..ba274b7 100644 --- a/basis/lib.lua +++ b/basis/lib.lua @@ -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 diff --git a/basis/mark_lib.lua b/basis/mark_lib.lua new file mode 100644 index 0000000..8cc52ce --- /dev/null +++ b/basis/mark_lib.lua @@ -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 \ No newline at end of file diff --git a/doc/manual_DE.lua b/doc/manual_DE.lua index 6038fb7..72a878e 100644 --- a/doc/manual_DE.lua +++ b/doc/manual_DE.lua @@ -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".. diff --git a/doc/manual_EN.lua b/doc/manual_EN.lua index 73d4bd3..ee4219f 100644 --- a/doc/manual_EN.lua +++ b/doc/manual_EN.lua @@ -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".. diff --git a/init.lua b/init.lua index f191e6d..4b407d1 100644 --- a/init.lua +++ b/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") diff --git a/locale/techage.de.tr b/locale/techage.de.tr index 16fe76b..9c007f1 100644 --- a/locale/techage.de.tr +++ b/locale/techage.de.tr @@ -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 diff --git a/locale/template.txt b/locale/template.txt index 7c52ea0..78d3ce3 100644 --- a/locale/template.txt +++ b/locale/template.txt @@ -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= diff --git a/logic/movecontroller.lua b/logic/movecontroller.lua deleted file mode 100644 index 72c1dbb..0000000 --- a/logic/movecontroller.lua +++ /dev/null @@ -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) diff --git a/manuals/manual_ta4_DE.md b/manuals/manual_ta4_DE.md index 40b8628..d9f8edc 100644 --- a/manuals/manual_ta4_DE.md +++ b/manuals/manual_ta4_DE.md @@ -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: diff --git a/manuals/manual_ta4_EN.md b/manuals/manual_ta4_EN.md index 4f0cf6c..c7623d9 100644 --- a/manuals/manual_ta4_EN.md +++ b/manuals/manual_ta4_EN.md @@ -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 diff --git a/logic/doorblock.lua b/move_controller/doorblock.lua similarity index 100% rename from logic/doorblock.lua rename to move_controller/doorblock.lua diff --git a/logic/doorcontroller.lua b/move_controller/doorcontroller.lua similarity index 100% rename from logic/doorcontroller.lua rename to move_controller/doorcontroller.lua diff --git a/logic/doorcontroller2.lua b/move_controller/doorcontroller2.lua similarity index 100% rename from logic/doorcontroller2.lua rename to move_controller/doorcontroller2.lua diff --git a/move_controller/flycontroller.lua b/move_controller/flycontroller.lua new file mode 100644 index 0000000..c902ed9 --- /dev/null +++ b/move_controller/flycontroller.lua @@ -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"}, +-- }, +--}) diff --git a/logic/gateblock.lua b/move_controller/gateblock.lua similarity index 100% rename from logic/gateblock.lua rename to move_controller/gateblock.lua diff --git a/move_controller/movecontroller.lua b/move_controller/movecontroller.lua new file mode 100644 index 0000000..0d88762 --- /dev/null +++ b/move_controller/movecontroller.lua @@ -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"}, + }, +}) diff --git a/move_controller/turncontroller.lua b/move_controller/turncontroller.lua new file mode 100644 index 0000000..28a9010 --- /dev/null +++ b/move_controller/turncontroller.lua @@ -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"}, +-- }, +--}) diff --git a/textures/techage_appl_turn.png b/textures/techage_appl_turn.png new file mode 100644 index 0000000..4a2998d Binary files /dev/null and b/textures/techage_appl_turn.png differ