-- assorted_mecha/mecha_control_panel.lua -- Per-mech configuration UI, opened via the remote control item -- State is stored directly in self_ent._ai_* fields (no persistent meta on entities) local FORMNAME = "assorted_mecha:control_panel" if not assorted_mecha_panel_refs then assorted_mecha_panel_refs = {} end local function build_formspec(ent, clicker_name) local ai_on = ent._ai_enabled == true local atk_mob = ent._ai_attack_mobs ~= false local atk_ply = ent._ai_attack_players == true local prot = ent._ai_protect_owner ~= false local radius = tonumber(ent._ai_scan_radius) or 20 local owner = ent._ai_owner or "" local mode = ent._ai_mode or "guard" local enemies = ent._ai_enemy_players or {} local enemy_set = {} for _, n in ipairs(enemies) do enemy_set[n] = true end local player_list = {} for _, p in ipairs(minetest.get_connected_players()) do local pn = p:get_player_name() if pn ~= clicker_name then table.insert(player_list, pn) end end local fs = "formspec_version[4]" .. "size[9,10.5]" .. "bgcolor[#1a1a2e;true]" .. "box[0,0;9,0.8;#0f3460]" .. "label[0.3,0.5;MECHA CONTROL PANEL]" -- AI on/off status fs = fs .. "box[0.2,1.0;8.6,1.3;#16213e]" if ai_on then fs = fs .. "box[3.4,1.05;5.2,1.2;#0d4f1a]" .. "label[3.7,1.7;ACTIVE - boarding disabled]" .. "button[0.5,1.1;2.7,0.9;toggle_ai;Deactivate]" else fs = fs .. "box[3.4,1.05;5.2,1.2;#4f1a0d]" .. "label[3.7,1.7;MANUAL - mech is boardable]" .. "button[0.5,1.1;2.7,0.9;toggle_ai;Activate AI]" end -- Owner fs = fs .. "box[0.2,2.45;8.6,0.7;#16213e]" .. "label[0.5,2.85;Owner: " .. (owner ~= "" and owner or "(none)") .. "]" .. "button[6.5,2.5;2.1,0.55;claim_owner;Claim]" -- Owner protection fs = fs .. "box[0.2,3.3;8.6,0.7;#16213e]" .. "checkbox[0.5,3.7;cb_protect;Protect owner (no friendly fire);" .. (prot and "true" or "false") .. "]" -- AI mode fs = fs .. "box[0.2,4.1;8.6,0.75;#16213e]" .. "label[0.5,4.4;AI Mode:]" .. "button[2.5,4.15;2.9,0.55;mode_guard;" .. (mode == "guard" and "[GUARD]" or "Guard") .. "]" .. "button[5.5,4.15;2.9,0.55;mode_freerun;" .. (mode == "freerun" and "[FREERUN]" or "Freerun") .. "]" -- Attack targets fs = fs .. "box[0.2,5.0;8.6,1.65;#16213e]" .. "label[0.5,5.35;Attack targets:]" .. "checkbox[0.5,5.75;cb_mobs;Hostile mobs and entities;" .. (atk_mob and "true" or "false") .. "]" .. "checkbox[0.5,6.25;cb_players;Other players (PvP);" .. (atk_ply and "true" or "false") .. "]" -- Scan radius local bar_val = math.floor((radius / 50) * 1000) fs = fs .. "box[0.2,6.85;8.6,1.05;#16213e]" .. "label[0.5,7.2;Scan radius: " .. radius .. " nodes]" .. "scrollbar[0.5,7.4;8.0,0.45;horizontal;radius_bar;" .. bar_val .. "]" -- Enemy player list (only shown when PvP is enabled) if atk_ply then fs = fs .. "box[0.2,8.05;8.6,1.6;#16213e]" .. "label[0.5,8.4;Mark as enemy:]" if #player_list > 0 then local col, row = 0, 0 for _, pname in ipairs(player_list) do local x = 0.5 + col * 4.3 local y = 8.75 + row * 0.55 fs = fs .. "checkbox[" .. x .. "," .. y .. ";enemy_" .. pname .. ";" .. pname .. ";" .. (enemy_set[pname] and "true" or "false") .. "]" col = col + 1 if col >= 2 then col = 0; row = row + 1 end end else fs = fs .. "label[0.5,8.8;(No other players online)]" end end -- Save / Cancel fs = fs .. "box[0,9.9;9,0.6;#0f3460]" .. "button_exit[0.5,9.95;3.5,0.85;save;Save]" .. "button_exit[5.0,9.95;3.5,0.85;cancel;Cancel]" return fs end function assorted_mecha_open_panel(ent, clicker) if not ent or not clicker then return end local cname = clicker:get_player_name() assorted_mecha_panel_refs[cname] = ent minetest.show_formspec(cname, FORMNAME, build_formspec(ent, cname)) end minetest.register_on_player_receive_fields(function(player, formname, fields) if formname ~= FORMNAME then return false end local pname = player:get_player_name() local ent = assorted_mecha_panel_refs[pname] if not ent or not ent.object or not ent.object:get_pos() then assorted_mecha_panel_refs[pname] = nil return true end if fields.toggle_ai then ent._ai_enabled = not ent._ai_enabled if ent._ai_enabled then assorted_mecha_ai_start(ent) else ent._ai_running = false end minetest.show_formspec(pname, FORMNAME, build_formspec(ent, pname)) return true end if fields.claim_owner then ent._ai_owner = pname minetest.show_formspec(pname, FORMNAME, build_formspec(ent, pname)) return true end if fields.cancel then assorted_mecha_panel_refs[pname] = nil return true end if fields.mode_guard then ent._ai_mode = "guard" ent._ai_waypoint = nil minetest.show_formspec(pname, FORMNAME, build_formspec(ent, pname)) return true end if fields.mode_freerun then ent._ai_mode = "freerun" minetest.show_formspec(pname, FORMNAME, build_formspec(ent, pname)) return true end if fields.save then if fields.cb_mobs ~= nil then ent._ai_attack_mobs = fields.cb_mobs == "true" end if fields.cb_players ~= nil then ent._ai_attack_players = fields.cb_players == "true" end if fields.cb_protect ~= nil then ent._ai_protect_owner = fields.cb_protect == "true" end if fields.radius_bar then local bar_val = tonumber(string.match(fields.radius_bar, "CHG:(%d+)") or string.match(fields.radius_bar, "(%d+)") or "400") local r = math.max(5, math.min(50, math.floor((bar_val / 1000) * 50))) ent._ai_scan_radius = r end local enemies = {} for k, v in pairs(fields) do if string.sub(k, 1, 6) == "enemy_" and v == "true" then table.insert(enemies, string.sub(k, 7)) end end ent._ai_enemy_players = enemies if ent._ai_enabled and not ent._ai_running then assorted_mecha_ai_start(ent) end assorted_mecha_panel_refs[pname] = nil minetest.chat_send_player(pname, "[Mecha] Settings saved.") return true end return true end)