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 0000000..a502054 Binary files /dev/null and b/textures/digistuff_controller_sides.png differ diff --git a/textures/digistuff_controller_top.png b/textures/digistuff_controller_top.png new file mode 100644 index 0000000..1d397ae Binary files /dev/null and b/textures/digistuff_controller_top.png differ