From 7e00678df69df968ccf235d46190702bdc59e9ad Mon Sep 17 00:00:00 2001 From: cheapie Date: Sat, 2 May 2020 23:58:34 -0500 Subject: [PATCH] Add digilines game controller This node allows players to have their keypresses forwarded to a LuaC, instead of moving the player around. This is intended to be used for making LuaC-based games. --- README | 10 + controller.lua | 238 ++++++++++++++++++++++++ init.lua | 1 + textures/digistuff_controller_sides.png | Bin 0 -> 1942 bytes textures/digistuff_controller_top.png | Bin 0 -> 2056 bytes 5 files changed, 249 insertions(+) create mode 100644 controller.lua create mode 100644 textures/digistuff_controller_sides.png create mode 100644 textures/digistuff_controller_top.png diff --git a/README b/README index d04d661..37baa7f 100644 --- a/README +++ b/README @@ -80,3 +80,13 @@ To write a card, send a command in the following format: After sending the write command, swipe the card to be written and the reader will send back the following message: {event = "write"} Both blank and previously written cards can be written to. If the card was not blank, it will be overwritten. + +How to use the game controller: +After setting a channel, right-click the controller to start/stop using it. +While using a controller, it will send a table with the control inputs, pitch, yaw, look vector, and name of the player using the controller each time one of these values changes, up to 5 times per second. +When a player leaves a controller, the string "player_left" is sent. +In addition to right-clicking the controller in use to stop using it, the following will also cause a player to stop using the controller: +* The controller is moved or removed +* The player leaves the game +* The player is teleported away from the controller +* The controller receives the string "release" on its digilines channel diff --git a/controller.lua b/controller.lua new file mode 100644 index 0000000..4d2fdf7 --- /dev/null +++ b/controller.lua @@ -0,0 +1,238 @@ +local digiline_rules = { + {x = 1,y = 0,z = 0}, + {x = -1,y = 0,z = 0}, + {x = 0,y = 0,z = 1}, + {x = 0,y = 0,z = -1}, + {x = 0,y = -1,z = 0}, + {x = 1,y = -1,z = 0}, + {x = -1,y = -1,z = 0}, + {x = 0,y = -1,z = 1}, + {x = 0,y = -1,z = -1}, +} + +local players_on_controller = {} + +local last_seen_inputs = {} + +local function process_inputs(pos) + local meta = minetest.get_meta(pos) + local hash = minetest.hash_node_position(pos) + if minetest.get_node(pos).name ~= "digistuff:controller_programmed" then + local player = minetest.get_player_by_name(players_on_controller[hash]) + if player then + player:set_physics_override({speed = 1,jump = 1,}) + player:set_pos(vector.add(pos,vector.new(0.25,0,0.25))) + minetest.chat_send_player(players_on_controller[hash],"You are now free to move.") + end + last_seen_inputs[players_on_controller[hash]] = nil + players_on_controller[hash] = nil + return + end + local name = players_on_controller[hash] + local player = minetest.get_player_by_name(name) + if not player then + digiline:receptor_send(pos,digiline_rules,meta:get_string("channel"),"player_left") + minetest.get_meta(pos):set_string("infotext","Digilines Game Controller Ready\n(right-click to use)") + players_on_controller[hash] = nil + return + end + local distance = vector.distance(pos,player:get_pos()) + if distance > 1 then + digiline:receptor_send(pos,digiline_rules,meta:get_string("channel"),"player_left") + minetest.get_meta(pos):set_string("infotext","Digilines Game Controller Ready\n(right-click to use)") + player:set_physics_override({speed = 1,jump = 1,}) + players_on_controller[hash] = nil + return + end + local inputs = player:get_player_control() + inputs.pitch = player:get_look_vertical() + inputs.yaw = player:get_look_horizontal() + local send_needed = false + if not last_seen_inputs[name] then + send_needed = true + else + for k,v in pairs(inputs) do + if v ~= last_seen_inputs[name][k] then + send_needed = true + break + end + end + end + last_seen_inputs[name] = inputs + if send_needed then + local channel = meta:get_string("channel") + local inputs = table.copy(inputs) + inputs.look_vector = player:get_look_dir() + inputs.name = name + digiline:receptor_send(pos,digiline_rules,channel,inputs) + end +end + +local function release_player(pos) + local hash = minetest.hash_node_position(pos) + local player = minetest.get_player_by_name(players_on_controller[hash]) + if player then + player:set_physics_override({speed = 1,jump = 1,}) + player:set_pos(vector.add(pos,vector.new(0.25,0,0.25))) + minetest.chat_send_player(players_on_controller[hash],"You are now free to move.") + end + local meta = minetest.get_meta(pos) + meta:set_string("infotext","Digilines Game Controller Ready\n(right-click to use)") + last_seen_inputs[players_on_controller[hash]] = nil + players_on_controller[hash] = nil + digiline:receptor_send(pos,digiline_rules,meta:get_string("channel"),"player_left") +end + +local function trap_player(pos,player) + local hash = minetest.hash_node_position(pos) + local oldname = players_on_controller[hash] + local newname = player:get_player_name() + if oldname and minetest.get_player_by_name(oldname) then + minetest.chat_send_player(player:get_player_name(),"Controller is already occupied by "..oldname) + return + else + players_on_controller[hash] = newname + player:set_pos(vector.add(pos,vector.new(0,-0.4,0))) + player:set_physics_override({speed = 0,jump = 0,}) + minetest.chat_send_player(newname,"You are now using a digilines game controller. Right-click the controller again to be released.") + local meta = minetest.get_meta(pos) + meta:set_string("infotext","Digilines Game Controller\nIn use by: "..newname) + process_inputs(pos) + end +end + +local function toggle_trap_player(pos,player) + if players_on_controller[minetest.hash_node_position(pos)] then + release_player(pos) + else + trap_player(pos,player) + end +end + +minetest.register_node("digistuff:controller", { + description = "Digilines Game Controller", + tiles = { + "digistuff_controller_top.png", + "digistuff_controller_sides.png", + }, + paramtype = "light", + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.5,-0.5,-0.5,0.5,-0.45,0.5}, + } + }, + _digistuff_channelcopier_fieldname = "channel", + _digistuff_channelcopier_onset = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("formspec","") + meta:set_string("infotext","Digilines Game Controller Ready\n(right-click to use)") + minetest.swap_node(pos,{name = "digistuff:controller_programmed",}) + end, + groups = {cracky = 1,}, + is_ground_content = false, + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("formspec","field[channel;Channel;${channel}") + end, + 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) + meta:set_string("formspec","") + meta:set_string("infotext","Digilines Game Controller Ready\n(right-click to use)") + minetest.swap_node(pos,{name = "digistuff:controller_programmed",}) + end + end, + digiline = { + receptor = {}, + wire = { + rules = digiline_rules, + }, + }, +}) + +minetest.register_node("digistuff:controller_programmed", { + description = "Digilines Game Controller (programmed state - you hacker you!)", + drop = "digistuff:controller", + tiles = { + "digistuff_controller_top.png", + "digistuff_controller_sides.png", + }, + paramtype = "light", + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.5,-0.5,-0.5,0.5,-0.45,0.5}, + } + }, + _digistuff_channelcopier_fieldname = "channel", + groups = {cracky = 1,not_in_creative_inventory = 1,}, + is_ground_content = false, + on_rightclick = function(pos,_,clicker) + if clicker and clicker:get_player_name() then + toggle_trap_player(pos,clicker) + end + end, + _digistuff_channelcopier_fieldname = "channel", + digiline = { + receptor = {}, + wire = { + rules = digiline_rules, + }, + effector = { + action = function(pos,node,channel,msg) + local setchannel = minetest.get_meta(pos):get_string("channel") + if channel ~= setchannel then return end + if msg == "release" then + local hash = minetest.hash_node_position(pos) + if players_on_controller[hash] then + release_player(pos) + end + end + end, + }, + }, +}) + +local acc_dtime = 0 + +minetest.register_globalstep(function(dtime) + acc_dtime = acc_dtime + dtime + if acc_dtime < 0.2 then return end + acc_dtime = 0 + for hash in pairs(players_on_controller) do + local pos = minetest.get_position_from_hash(hash) + process_inputs(pos) + end +end) + +minetest.register_lbm({ + name = "digistuff:reset_controllers", + label = "Reset game controllers to idle", + nodenames = {"digistuff:controller_programmed"}, + run_at_every_load = true, + action = function(pos) + if not players_on_controller[minetest.hash_node_position(pos)] then + local meta = minetest.get_meta(pos) + digiline:receptor_send(pos,digiline_rules,meta:get_string("channel"),"player_left") + meta:set_string("infotext","Digilines Game Controller Ready\n(right-click to use)") + end + end, +}) + +minetest.register_craft({ + output = "digistuff:controller", + recipe = { + {"","digistuff:button","",}, + {"digistuff:button","group:wool","digistuff:button",}, + {"","digistuff:button","",}, + }, +}) diff --git a/init.lua b/init.lua index 765ed74..8c772fd 100644 --- a/init.lua +++ b/init.lua @@ -15,6 +15,7 @@ local components = { "timer", "cardreader", "channelcopier", + "controller", } if minetest.get_modpath("mesecons_luacontroller") then table.insert(components,"ioexpander") end diff --git a/textures/digistuff_controller_sides.png b/textures/digistuff_controller_sides.png new file mode 100644 index 0000000000000000000000000000000000000000..a502054d76932fba6ab830583af58d8d9f6bc64b GIT binary patch literal 1942 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