-- assorted_mecha/mecha_controller.lua -- Remote control item for managing all nearby mechs local FORMNAME = "assorted_mecha:controller" if not assorted_mecha_controller_refs then assorted_mecha_controller_refs = {} end -- Find all main mech entities within radius (no owner filter) local function find_mechs_nearby(player, radius) local pos = player:get_pos() local mechs = {} for _, obj in ipairs(minetest.get_objects_inside_radius(pos, radius or 512)) do if not obj:is_player() then local ent = obj:get_luaentity() if ent and ent.name and string.match(ent.name, "^assorted_mecha:mech%d+$") then table.insert(mechs, {ent = ent, pos = obj:get_pos()}) end end end return mechs end local function build_overview(pname, mechs, selected_idx) local fs = "formspec_version[4]" .. "size[10,9.5]" .. "bgcolor[#1a1a2e;true]" .. "box[0,0;10,0.8;#0f3460]" .. "label[0.3,0.5;MECHA REMOTE | " .. pname .. "]" if #mechs == 0 then fs = fs .. "box[0.5,1.5;9,1.2;#16213e]" .. "label[0.8,2.0;No mechs found within 512 nodes.]" .. "label[0.8,2.55;Move closer to your mech and reopen.]" else local rows = "Type,AI,Owner,Position" for _, entry in ipairs(mechs) do local ent = entry.ent local p = entry.pos local typ = string.gsub(ent.name, "assorted_mecha:", "") local ai = ent._ai_enabled and "ON" or "off" local own = (ent._ai_owner and ent._ai_owner ~= "") and ent._ai_owner or "-" local pstr = string.format("%d/%d/%d", math.floor(p.x), math.floor(p.y), math.floor(p.z)) rows = rows .. "," .. typ .. "," .. ai .. "," .. own .. "," .. pstr end -- selected_idx is 1-based index into mechs table (nil = none) local table_selected = selected_idx and (selected_idx + 1) or 1 fs = fs .. "tablecolumns[text,align=left,width=4;text,align=center,width=2;text,align=center,width=3;text,align=left,width=4]" .. "table[0.3,0.9;9.4,6.8;mech_table;" .. rows .. ";" .. table_selected .. "]" .. "box[0,7.8;10,0.05;#16213e]" .. "button[0.3,7.9;2.9,0.75;open_panel;Configure]" .. "button[3.3,7.9;2.9,0.75;toggle_ai;Toggle AI]" .. "button[6.3,7.9;3.4,0.75;claim_selected;Claim]" .. "box[0,8.75;10,0.05;#16213e]" .. "button[0.3,8.85;4.0,0.55;claim_all;Claim all nearby]" end fs = fs .. "button_exit[7.5,8.85;2.2,0.55;close;Close]" return fs end local function open_overview(player, selected_idx) local pname = player:get_player_name() local ref = assorted_mecha_controller_refs[pname] or {} local mechs = find_mechs_nearby(player) -- preserve selection across refreshes ref.mechs = mechs ref.selected = selected_idx or ref.selected assorted_mecha_controller_refs[pname] = ref minetest.show_formspec(pname, FORMNAME, build_overview(pname, mechs, ref.selected)) end -- Register the remote control item minetest.register_craftitem("assorted_mecha:mecha_controller", { description = "Mecha Remote Control\nLeft-click: open mech manager\nRight-click on mech: configure directly", inventory_image = "mecha_controller.png", stack_max = 1, on_use = function(itemstack, user, pointed_thing) if user and user:is_player() then open_overview(user) end return itemstack end, on_place = function(itemstack, placer, pointed_thing) if pointed_thing.type == "object" then local obj = pointed_thing.ref if obj and not obj:is_player() then local ent = obj:get_luaentity() if ent and ent.name and string.match(ent.name, "^assorted_mecha:mech%d+$") then assorted_mecha_open_panel(ent, placer) return itemstack end end end return itemstack end, }) -- Formspec receive minetest.register_on_player_receive_fields(function(player, formname, fields) if formname ~= FORMNAME then return false end local pname = player:get_player_name() local ref = assorted_mecha_controller_refs[pname] if not ref then return true end local mechs = ref.mechs or {} if fields.quit or fields.close then assorted_mecha_controller_refs[pname] = nil return true end -- Update selected index whenever table is interacted with -- Luanti table field format on click: "CHG:N" where N is row (1=header, 2=first data row) if fields.mech_table then local row = tonumber(string.match(fields.mech_table, "%a+:(%d+)")) if row then local idx = row - 1 -- subtract header row if idx >= 1 and idx <= #mechs then ref.selected = idx end end end local function get_selected_ent() local idx = ref.selected if idx and mechs[idx] and mechs[idx].ent then -- verify entity still alive if mechs[idx].ent.object and mechs[idx].ent.object:get_pos() then return mechs[idx].ent end end return nil end if fields.open_panel then local ent = get_selected_ent() if ent then assorted_mecha_controller_refs[pname] = nil assorted_mecha_open_panel(ent, player) else minetest.chat_send_player(pname, "[Remote] Select a mech first.") open_overview(player, ref.selected) end return true end if fields.toggle_ai then local ent = get_selected_ent() if ent then ent._ai_enabled = not ent._ai_enabled if ent._ai_enabled then assorted_mecha_ai_start(ent) minetest.chat_send_player(pname, "[Remote] AI enabled.") else ent._ai_running = false minetest.chat_send_player(pname, "[Remote] AI disabled.") end else minetest.chat_send_player(pname, "[Remote] Select a mech first.") end open_overview(player, ref.selected) return true end if fields.claim_selected then local ent = get_selected_ent() if ent then ent._ai_owner = pname minetest.chat_send_player(pname, "[Remote] Mech claimed.") else minetest.chat_send_player(pname, "[Remote] Select a mech first.") end open_overview(player, ref.selected) return true end if fields.claim_all then local count = 0 for _, entry in ipairs(mechs) do if entry.ent and entry.ent.object and entry.ent.object:get_pos() then entry.ent._ai_owner = pname count = count + 1 end end minetest.chat_send_player(pname, "[Remote] Claimed " .. count .. " mech(s).") open_overview(player, ref.selected) return true end -- Re-render on any table click to keep selection visible open_overview(player, ref.selected) return true end)