From 6c299b2fe545cd006df85b03744b288d9861cd8c Mon Sep 17 00:00:00 2001 From: cheapie Date: Fri, 5 Feb 2021 20:09:30 -0600 Subject: Add digilines movestone --- README | 24 ++++++ init.lua | 1 + movestone.lua | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 294 insertions(+) create mode 100644 movestone.lua diff --git a/README b/README index dc649b8..d9f0b9b 100644 --- a/README +++ b/README @@ -101,3 +101,27 @@ The RAM and EEPROM chips behave identically, except that the RAM chip loses its How to use the 2D graphics processor: Please see gpu.txt for information on this part. + +How to use the digilines pistons: +The following commands are accepted as strings: "extend" (extend the piston), "retract" (retract the piston), and "retract_sticky" (retract the piston, pulling one node like a sticky piston) +You can also send a command as a table. If so, the fields that can be used in the table are as follows: +* action: "extend" or "retract" +* max: The maximum number of nodes to push/pull, cannot be set higher than 16. Set to 0 (or omit) when retracting to perform a non-sticky retraction. +* allsticky: Pull a whole stack of nodes (like movestone), not just one. + +How to use the digilines movestone: +Commands for this node are in the form of a table, with the field "command" set to the desired action, and other fields providing parameters. +The commands are as follows: +* "getstate": Returns a table containing the following elements: "targetpos" (table representing the target position), "pos" (table representing the current position), and "moveaxis" (the current axis being moved along, nil if not moving) +* "absmove": Moves to the absolute position specified by "x" "y" and "z". No axis can move more than 50m as a result of one command. If movements along more than one axis are needed, they are processed in alphabetical order (X,Y,Z). +* "relmove": Same as absmove, but relative to the current position (for example, y=1 moves up 1m, not *to* Y=1) +The available parameters for absmove and relmove are: +* x: Target X position (for absmove) or target change in X position (for relmove) +* y: Same, but for Y +* z: Same, but for Z +* sticky: Whether to pull nodes along behind the movestone +* allsticky: Whether to pull a full stack of nodes like normal movestone (true) or just one like a sticky piston (false) +* maxstack: The maximum number of nodes to push/pull, with the movestone itself counting as 1. Cannot be set higher than 50. +* sound: "mesecons" to have the mesecons movestone sound play or "none" for no sound at all +If any of x/y/z are omitted, then they default to the current position (for absmove) or 0 (for relmove). +If any of maxstack/sticky/allsticky/sound are omitted, they default to the values last used. diff --git a/init.lua b/init.lua index 84b5b37..13b99c1 100644 --- a/init.lua +++ b/init.lua @@ -19,6 +19,7 @@ local components = { "memory", "gpu", "sillystuff", + "movestone", } if minetest.get_modpath("mesecons_luacontroller") then table.insert(components,"ioexpander") end diff --git a/movestone.lua b/movestone.lua new file mode 100644 index 0000000..e597a42 --- /dev/null +++ b/movestone.lua @@ -0,0 +1,269 @@ +if not minetest.get_modpath("mesecons_mvps") then + minetest.log("warning","mesecons_mvps is not installed - digilines movestone will not be available") + return +end + +local function abortmovement(pos) + local meta = minetest.get_meta(pos) + local state = meta:get_string("state") + if state ~= "" then state = minetest.deserialize(state) else state = {} end + state.targetx = pos.x + state.targety = pos.y + state.targetz = pos.z + state.moveaxis = nil + meta:set_string("state",minetest.serialize(state)) +end + +local function checkprotection(pos,player) + if not player then player = "" end + if type(player) ~= "string" then player = player:get_player_name() end + if minetest.is_protected(pos,player) and not minetest.check_player_privs(player,{protection_bypass=true}) then + minetest.record_protection_violation(pos,player) + return false + end + return true +end + +local function move(pos,dir,state) + local newpos = vector.add(pos,dir) + local stack = mesecon.mvps_get_stack(pos,dir,state.maxstack,state.sticky and state.allsticky) + if not stack then + abortmovement(pos) + return false + end + for _,i in pairs(stack) do + if not checkprotection(i.pos,state.player) then + abortmovement(pos) + return false + end + end + local success,stack,oldstack = mesecon.mvps_push(pos,dir,state.maxstack) + if not success then + abortmovement(pos) + return false + end + mesecon.mvps_process_stack(stack) + mesecon.mvps_move_objects(pos,dir,oldstack) + if state.sound == "mesecons" then + minetest.sound_play("movestone",{pos = pos,max_hear_distance = 20,gain = 0.5,},true) + end + if not state.sticky then return true end + local ppos = vector.add(pos,vector.multiply(dir,-1)) + local success,stack,oldstack + if state.allsticky then + success,stack,oldstack = mesecon.mvps_pull_all(ppos,dir,state.maxstack) + else + success,stack,oldstack = mesecon.mvps_pull_single(ppos,dir,state.maxstack) + end + if success then + mesecon.mvps_move_objects(ppos,dir,oldstack,-1) + else + abortmovement(pos) + return false + end + return true +end + +local rules = { + {x = 1, y = 0, z = 0}, + {x =-1, y = 0, z = 0}, + {x = 0, y = 1, z = 0}, + {x = 0, y =-1, z = 0}, + {x = 0, y = 0, z = 1}, + {x = 0, y = 0, z =-1}, +} + +minetest.register_node("digistuff:movestone", { + description = "Digilines Movestone", + groups = {cracky = 3,}, + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("formspec","field[channel;Channel;${channel}") + local initialstate = { + targetx = pos.x, + targety = pos.y, + targetz = pos.z, + sound = "mesecons", + maxstack = 0, + allsticky = false, + } + meta:set_int("active",0) + meta:set_string("state",minetest.serialize(initialstate)) + end, + after_place_node = function(pos,player) + if not player then return end + local meta = minetest.get_meta(pos) + meta:set_string("owner",player:get_player_name()) + end, + tiles = { + "jeija_movestone_side.png", + }, + on_receive_fields = function(pos, formname, fields, sender) + local name = sender:get_player_name() + if minetest.is_protected(pos,name) and not minetest.check_player_privs(name,{protection_bypass=true}) then + minetest.record_protection_violation(pos,name) + return + end + local meta = minetest.get_meta(pos) + if fields.channel then meta:set_string("channel",fields.channel) end + end, + on_timer = function(pos) + local meta = minetest.get_meta(pos) + if meta:get_int("active") < 1 then return end + local state = meta:get_string("state") + local newpos = pos + if state ~= "" then state = minetest.deserialize(state) else return end + if state.moveaxis == "x" then + local dir = vector.new(state.targetx > pos.x and 1 or -1,0,0) + move(pos,dir,state) + newpos = vector.add(pos,dir) + if newpos.x == state.targetx then + if newpos.y ~= state.targety then + state.moveaxis = "y" + elseif newpos.z ~= state.targetz then + state.moveaxis = "z" + else + state.moveaxis = nil + end + end + elseif state.moveaxis == "y" then + local dir = vector.new(0,state.targety > pos.y and 1 or -1,0) + move(pos,dir,state) + newpos = vector.add(pos,dir) + if newpos.y == state.targety then + if newpos.z ~= state.targetz then + state.moveaxis = "z" + else + state.moveaxis = nil + end + end + elseif state.moveaxis == "z" then + local dir = vector.new(0,0,state.targetz > pos.z and 1 or -1) + move(pos,dir,state) + newpos = vector.add(pos,dir) + if newpos.z == state.targetz then + state.moveaxis = nil + end + end + local newmeta = minetest.get_meta(newpos) + newmeta:set_int("active",state.moveaxis and 1 or 0) + newmeta:set_string("state",minetest.serialize(state)) + if state.moveaxis then + local timer = minetest.get_node_timer(newpos) + timer:start(0.33) + end + end, + _digistuff_channelcopier_fieldname = "channel", + digiline = { + wire = { + rules = rules, + }, + receptor = {}, + effector = { + action = function(pos,node,channel,msg) + local meta = minetest.get_meta(pos) + local setchan = meta:get_string("channel") + if channel ~= setchan then return end + if type(msg) ~= "table" or not msg.command then return end + if msg.command == "getstate" then + local ret = {} + local meta = minetest.get_meta(pos) + local state = meta:get_string("state") + if state ~= "" then state = minetest.deserialize(state) else state = {} end + if not state then + minetest.log("error",string.format("Invalid state information for digilines movestone at %d,%d,%d: %s",pos.x,pos.y,pos.z,meta:get_string("state"))) + return + end + ret.pos = pos + ret.targetpos = vector.new(state.targetx,state.targety,state.targetz) + ret.moveaxis = state.moveaxis + digiline:receptor_send(pos,rules,channel,ret) + elseif msg.command == "absmove" then + local ret = {} + local meta = minetest.get_meta(pos) + local state = meta:get_string("state") + if state ~= "" then state = minetest.deserialize(state) else state = {} end + if not state then + minetest.log("error",string.format("Invalid state information for digilines movestone at %d,%d,%d: %s",pos.x,pos.y,pos.z,meta:get_string("state"))) + return + end + if type(msg.sound) == "string" then state.sound = msg.sound end + if type(msg.x) ~= "number" then msg.x = pos.x end + if type(msg.y) ~= "number" then msg.y = pos.y end + if type(msg.z) ~= "number" then msg.z = pos.z end + msg.x = math.max(pos.x-50,math.min(pos.x+50,math.floor(msg.x))) + msg.y = math.max(pos.y-50,math.min(pos.y+50,math.floor(msg.y))) + msg.z = math.max(pos.z-50,math.min(pos.z+50,math.floor(msg.z))) + local firstaxis + if msg.x ~= pos.x then firstaxis = "x" + elseif msg.y ~= pos.y then firstaxis = "y" + elseif msg.z ~= pos.z then firstaxis = "z" end + if firstaxis then + state.targetx = msg.x + state.targety = msg.y + state.targetz = msg.z + state.moveaxis = firstaxis + if msg.sticky then + state.sticky = true + elseif msg.sticky == false then + state.sticky = false + end + if msg.allsticky then + state.allsticky = true + elseif msg.allsticky == false then + state.allsticky = false + end + if type(msg.maxstack) == "number" and msg.maxstack >= 0 and msg.maxstack <= 50 then + state.maxstack = math.floor(msg.maxstack) + end + meta:set_string("state",minetest.serialize(state)) + meta:set_int("active",1) + minetest.get_node_timer(pos):start(0.1) + end + elseif msg.command == "relmove" then + local ret = {} + local meta = minetest.get_meta(pos) + local state = meta:get_string("state") + if state ~= "" then state = minetest.deserialize(state) else state = {} end + if not state then + minetest.log("error",string.format("Invalid state information for digilines movestone at %d,%d,%d: %s",pos.x,pos.y,pos.z,meta:get_string("state"))) + return + end + if type(msg.sound) == "string" then state.sound = msg.sound end + if type(msg.x) ~= "number" then msg.x = 0 end + if type(msg.y) ~= "number" then msg.y = 0 end + if type(msg.z) ~= "number" then msg.z = 0 end + msg.x = pos.x+math.max(0,math.min(50,math.floor(msg.x))) + msg.y = pos.y+math.max(0,math.min(50,math.floor(msg.y))) + msg.z = pos.z+math.max(0,math.min(50,math.floor(msg.z))) + local firstaxis + if msg.x ~= pos.x then firstaxis = "x" + elseif msg.y ~= pos.y then firstaxis = "y" + elseif msg.z ~= pos.z then firstaxis = "z" end + if firstaxis then + state.targetx = msg.x + state.targety = msg.y + state.targetz = msg.z + state.moveaxis = firstaxis + if msg.sticky then + state.sticky = true + elseif msg.sticky == false then + state.sticky = false + end + if msg.allsticky then + state.allsticky = true + elseif msg.allsticky == false then + state.allsticky = false + end + if type(msg.maxstack) == "number" and msg.maxstack >= 0 and msg.maxstack <= 50 then + state.maxstack = math.floor(msg.maxstack) + end + meta:set_string("state",minetest.serialize(state)) + meta:set_int("active",1) + minetest.get_node_timer(pos):start(0.1) + end + end + end + }, + }, +}) -- cgit v1.2.3