// Name: Haxball OP Mode Script // Description: Opens a prebuilt Haxball Headless Host "OP mode" script and copies it to your clipboard. // Author: htzzsdcs9m-lang // GitHub: htzzsdcs9m-lang import "@johnlindquist/kit" const haxballOpModeScript = `/* Haxball OP Mode Script - Admin commands: !op [id|name], !deop [id|name], !listop, !help - OP players get a ball speed boost on kick (boostFactor). - Optional "magnet" gently pulls the ball toward OP players (disabled by default). Usage: - Paste this code into https://www.haxball.com/headless (Console tab). - The first player to join becomes admin. Notes: - This script uses Headless Host APIs: HBInit, onPlayerBallKick, getDiscProperties, setDiscProperties, setPlayerDiscProperties, etc. - If some APIs are unavailable in your environment, comment those sections out or update to the latest headless host. */ var room = HBInit({ roomName: "OP Mode Room", maxPlayers: 16, public: true, noPlayer: false }); .(roomsetDefaultStadium"Classic"); room.setScoreLimit(3); room.setTimeLimit(3); var boostFactor = 1.8; // Multiplier applied to ball speed when an OP kicks var enableMagnet = false; // Mild attraction of the ball toward OP players var magnetRadius = 50; // Distance within which magnet can affect the ball var magnetForce = 0.25; // Velocity added toward OP player per tick when within radius var ops = {}; // { [playerId]: true } var muted = {}; // simple chat mute map if needed later function getAdmins() { return room.getPlayerList().filter(function (p) { return p.admin; }); } function makeAdminIfNone(player) { if (getAdmins().length === 0) { room.setPlayerAdmin(player.id, true); room.sendChat("[System] " + player.name + " is now admin (first join)."); } } function findPlayer(arg) { var list = room.getPlayerList(); if (!arg) return null; // by id var id = parseInt(arg, 10); if (!isNaN(id)) { return list.find(function (p) { return p.id === id; }) || null; } // by name (substring, case-insensitive) var lower = ("" + arg).toLowerCase(); return list.find(function (p) { return p.name.toLowerCase().indexOf(lower) !== -1; }) || null; } function isAdmin(player) { return !!(player && player.admin); } function isOp(playerId) { return !!ops[playerId]; } function setOp(playerId, value) { if (value) ops[playerId] = true; else delete ops[playerId]; } function listOps() { var list = room.getPlayerList().filter(function (p) { return isOp(p.id); }); if (list.length === 0) { room.sendChat("[OP] No OP players."); return; } var names = list.map(function (p) { return p.name + " (#" + p.id + ")"; }).join(", "); room.sendChat("[OP] " + names); } function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); } function distance(ax, ay, bx, by) { var dx = ax - bx, dy = ay - by; return Math.sqrt(dx*dx + dy*dy); } // EVENTS room.onPlayerJoin = function (player) { makeAdminIfNone(player); room.sendChat("Welcome " + player.name + " (#" + player.id + "). Type !help for commands."); }; room.onPlayerLeave = function (player) { setOp(player.id, false); delete muted[player.id]; }; room.onTeamGoal = function (team) { // no-op }; room.onGameStart = function (byPlayer) { // no-op }; room.onGameStop = function (byPlayer) { // no-op }; room.onPlayerBallKick = function (player) { // If the player is OP, boost the ball speed at the moment of kick if (!isOp(player.id)) return; // get current ball velocity and multiply if (typeof room.getDiscProperties === "function" && typeof room.setDiscProperties === "function") { var ball = room.getDiscProperties(0); // 0 = ball if (ball && typeof ball.xspeed === "number" && typeof ball.yspeed === "number") { var boostedX = ball.xspeed * boostFactor; var boostedY = ball.yspeed * boostFactor; // avoid NaN and clamp to a reasonable range var maxSpeed = 20; boostedX = clamp(boostedX, -maxSpeed, maxSpeed); boostedY = clamp(boostedY, -maxSpeed, maxSpeed); room.setDiscProperties(0, { xspeed: boostedX, yspeed: boostedY }); room.sendChat("[OP] " + player.name + " unleashed a boosted kick!"); } } }; room.onGameTick = function () { if (!enableMagnet) return; if (typeof room.getDiscProperties !== "function" || typeof room.setDiscProperties !== "function") return; // gather OP players var opPlayers = room.getPlayerList().filter(function (p) { return isOp(p.id); }); if (opPlayers.length === 0) return; var ball = room.getDiscProperties(0); if (!ball) return; // find closest OP to ball var closest = null; var closestDist = Infinity; for (var i = 0; i < opPlayers.length; i++) { var p = opPlayers[i]; // get player disc position if available var pd = (typeof room.getPlayerDiscProperties === "function") ? room.getPlayerDiscProperties(p.id) : null; if (!pd || typeof pd.x !== "number" || typeof pd.y !== "number") continue; var d = distance(pd.x, pd.y, ball.x, ball.y); if (d < closestDist) { closestDist = d; closest = pd; } } // apply mild pull toward closest OP if within radius if (closest && closestDist <= magnetRadius) { var vx = ball.xspeed || 0; var vy = ball.yspeed || 0; var dx = closest.x - ball.x; var dy = closest.y - ball.y; var len = Math.sqrt(dx*dx + dy*dy) || 1; // normalized vector toward player dx /= len; dy /= len; var nvx = vx + dx * magnetForce; var nvy = vy + dy * magnetForce; var maxSpeed = 20; nvx = clamp(nvx, -maxSpeed, maxSpeed); nvy = clamp(nvy, -maxSpeed, maxSpeed); room.setDiscProperties(0, { xspeed: nvx, yspeed: nvy }); } }; // CHAT COMMANDS room.onPlayerChat = function (player, message) { if (muted[player.id]) return false; if (message.charAt(0) !== "!") return true; var parts = message.trim().split(/\\s+/); var cmd = (parts[0] || "").toLowerCase(); var arg = parts.slice(1).join(" "); if (cmd === "!help") { room.sendChat("Commands: !op [id|name], !deop [id|name], !listop, !help"); room.sendChat("OP effects: Boosted kicks" + (enableMagnet ? " + Magnet" : "")); return false; } if (cmd === "!listop") { listOps(); return false; } if (cmd === "!op" || cmd === "!deop") { if (!isAdmin(player)) { room.sendChat("[OP] Only admins can use this command."); return false; } var target = findPlayer(arg) || player; if (!target) { room.sendChat("[OP] Player not found."); return false; } if (cmd === "!op") { setOp(target.id, true); room.sendChat("[OP] " + target.name + " (#" + target.id + ") is now OP."); } else { setOp(target.id, false); room.sendChat("[OP] " + target.name + " (#" + target.id + ") is no longer OP."); } return false; } // Unknown command room.sendChat("[OP] Unknown command. Type !help"); return false; }; `; await copy(haxballOpModeScript) await editor({ value: haxballOpModeScript, hint: "This is a ready-to-paste Haxball Headless Host 'OP mode' script. It has been copied to your clipboard.", language: "javascript", shortcuts: [ { name: "Copy", key: `${cmd}+c`, onPress: async () => { await copy(haxballOpModeScript) await toast("Copied script to clipboard") }, bar: "right", }, { name: "Save as file", key: `${cmd}+s`, onPress: async input => { const outPath = await path({ hint: "Choose a path/filename (e.g., op-mode.js). If file exists, it will be overwritten.", }) await writeFile(outPath, haxballOpModeScript, "utf8") await revealFile(outPath) }, bar: "right", }, ], }) await notify("Haxball OP Mode script copied to clipboard")// Name: Haxball Helper // Description: Open Haxball quickly and view fair-play tips and resources. // Author: htzzsdcs9m-lang // GitHub: htzzsdcs9m-lang import "@johnlindquist/kit" const CHOICES = [ { name: "Play Haxball", description: "Open haxball.com to start or join a room", value: "open", }, { name: "Headless Host Docs", description: "Official Haxball headless host API documentation", value: "docs", }, { name: "Controls Reference", description: "Show a quick reference for default controls", value: "controls", }, { name: "Fair Play Tips", description: "Good sportsmanship and play-nice reminders", value: "tips", }, { name: "Join Room by Link", description: "Paste a room link and open it in your browser", value: "join", }, ] as const const selection = await arg( { placeholder: "Haxball Helper", enter: "Select", preview: (input: string, { focused }) => { if (!focused) return "" if (focused.value === "controls") { return md(` # Haxball Controls (Default) - Arrow Keys: Move - X (or Z): Kick (hold to charge) - Enter: Chat - Shift: Team Chat - P: Pause (room host authority) `) } if (focused.value === "tips") { return md(` # Fair Play Tips - Be respectful in chat; avoid toxicity. - Don't exploit bugs or use unauthorized tools. - Play as a team and rotate positions. - Host fairly: balance teams and avoid kicking without reason. - Report issues to room admins calmly. `) } if (focused.value === "docs") { return md(` # Headless Host Docs - Learn to host your own rooms and configure settings. - URL: https://www.haxball.com/headless `) } if (focused.value === "open") { return md(` # Play Haxball - Opens the official site: https://www.haxball.com `) } if (focused.value === "join") { return md(` # Join Room by Link - Paste a full room URL (e.g. haxball.com/play?...). `) } return "" }, }, CHOICES ) switch (selection) { case "open": { await hide() await browse("https://www.haxball.com") break } case "docs": { await hide() await browse("https://www.haxball.com/headless") break } case "controls": { await div( md(` # Haxball Controls (Default) - Arrow Keys: Move - X (or Z): Kick (hold to charge) - Enter: Chat - Shift: Team Chat - P: Pause (room host authority) `) ) break } case "tips": { await div( md(` # Fair Play Tips - Be respectful in chat; avoid toxicity. - Don't exploit bugs or use unauthorized tools. - Play as a team and rotate positions. - Host fairly: balance teams and avoid kicking without reason. - Report issues to room admins calmly. Enjoy the game! `) ) break } case "join": { const link = await arg({ placeholder: "Paste a Haxball room link (e.g., https://www.haxball.com/play?...)", strict: false, enter: "Open Link", }) if (typeof link === "string" && link.trim()) { await hide() await browse(link.trim()) } break } default: { // no-op } } await toast("Done")