diff --git a/build/scripts/commands/general.js b/build/scripts/commands/general.js index 37f21622..b2146157 100644 --- a/build/scripts/commands/general.js +++ b/build/scripts/commands/general.js @@ -1,1187 +1,1251 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains most in-game chat commands that can be run by untrusted players. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.commands = void 0; -var api = require("/api"); -var config_1 = require("/config"); -var commands_1 = require("/frameworks/commands"); -var menus_1 = require("/frameworks/menus"); -var funcs_1 = require("/funcs"); -var globals_1 = require("/globals"); -var maps_1 = require("/maps"); -var players_1 = require("/players"); -var ranks_1 = require("/ranks"); -var utils_1 = require("/utils"); -var votes_1 = require("/votes"); -exports.commands = (0, commands_1.commandList)(__assign(__assign({ about: { - args: [], - description: 'Prints information about the plugin.', - perm: commands_1.Perm.none, - handler: function (_a) { - var _b, _c; - var output = _a.output; - output("[accent][cyan]fish-commands[] is the monolithic plugin used for the Fish servers' features.\n[accent]==========\n[accent]Source code available at: [cyan]https://github.com/Fish-Community/fish-commands/\n[accent]Current plugin version: [cyan]".concat((_c = (_b = globals_1.fishPlugin.version) === null || _b === void 0 ? void 0 : _b.slice(0, 8)) !== null && _c !== void 0 ? _c : "[scarlet]null[]", "[]")); - } - }, unpause: (0, commands_1.command)({ - args: [], - description: 'Unpauses the game.', - perm: commands_1.Perm.trusted, - requirements: [commands_1.Req.mode('pvp')], - init: function () { - var data = { unpaused: false }; - Events.on(EventType.PlayEvent, function () { - if (data.unpaused) { - data.unpaused = false; - Vars.state.rules.pvpAutoPause = true; - } - }); - return data; - }, - handler: function (_a) { - var data = _a.data, outputSuccess = _a.outputSuccess; - Vars.state.rules.pvpAutoPause = false; - data.unpaused = true; - Core.app.post(function () { return Vars.state.set(GameState.State.playing); }); - outputSuccess("Unpaused."); - }, - }), tp: { - args: ['player:player'], - description: 'Teleport to another player.', - perm: commands_1.Perm.play, - requirements: [commands_1.Req.modeNot("pvp")], - handler: function (_a) { - var _b, _c, _d; - var args = _a.args, sender = _a.sender; - if (!((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) - (0, commands_1.fail)("Can only teleport while in a core unit."); - if (sender.team() !== args.player.team()) - (0, commands_1.fail)("Cannot teleport to players on another team."); - if ((_d = (_c = sender.unit()).hasPayload) === null || _d === void 0 ? void 0 : _d.call(_c)) - (0, commands_1.fail)("Cannot teleport to players while holding a payload."); - (0, utils_1.teleportPlayer)(sender.player, args.player.player); - }, - }, clean: { - args: [], - description: 'Removes all boulders from the map.', - perm: commands_1.Perm.play, - requirements: [commands_1.Req.cooldownGlobal(100000)], - handler: function (_a) { - var sender = _a.sender, outputSuccess = _a.outputSuccess; - Timer.schedule(function () { return Call.sound(sender.con, Sounds.rockBreak, 1, 1, 0); }, 0, 0.05, 10); - Vars.world.tiles.eachTile(function (t) { - if (t.breakable() && t.block() instanceof Prop) { - t.removeNet(); - } - }); - outputSuccess("Cleared the map of boulders."); - } - }, die: { - args: [], - description: 'Kills your unit.', - perm: commands_1.Perm.mod.exceptModes({ - sandbox: commands_1.Perm.play - }, "You do not have permission to die."), - handler: function (_a) { - var _b; - var sender = _a.sender; - (_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.kill(); - }, - }, discord: { - args: [], - description: 'Takes you to our discord.', - perm: commands_1.Perm.none, - handler: function (_a) { - var sender = _a.sender; - Call.openURI(sender.con, config_1.text.discordURL); - }, - }, tilelog: { - args: ['persist:boolean?'], - description: 'Checks the history of a tile.', - perm: commands_1.Perm.none, - handler: function (_a) { - var args = _a.args, output = _a.output, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps; - if (currentTapMode == "off") { - if (args.persist) { - handleTaps("on"); - outputSuccess("Tilelog mode enabled. Click tiles to check their recent history. Run /tilelog again to disable."); - } - else { - handleTaps("once"); - output("Click on a tile to check its recent history..."); - } - } - else { - handleTaps("off"); - outputSuccess("Tilelog disabled."); - } - }, - tapped: function (_a) { - var _b; - var tile = _a.tile, x = _a.x, y = _a.y, output = _a.output, sender = _a.sender, admins = _a.admins; - var historyData = (_b = globals_1.tileHistory["".concat(x, ",").concat(y)]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("There is no recorded history for the selected tile (".concat(tile.x, ", ").concat(tile.y, ").")); - var history = funcs_1.StringIO.read(historyData, function (str) { return str.readArray(function (d) { return ({ - action: d.readString(2), - uuid: d.readString(3), - time: d.readNumber(16), - type: d.readString(2), - }); }, 1); }); - output("[yellow]Tile history for tile (".concat(tile.x, ", ").concat(tile.y, "):\n") + history.map(function (e) { - var _a, _b; - return globals_1.uuidPattern.test(e.uuid) - ? (sender.hasPerm("viewUUIDs") - ? "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)) - : "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time))) - : "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - }).join('\n')); - } - }, aoelog: (0, commands_1.command)(function () { - var allowedActions = [ - "built", "broke", "rotated", "killed", "configured", "pay-dropped", "picked up", "controlled" - ]; - var cachedPointMap = Object.create(null); - return { - args: ['persist:boolean?', 'amount:number?', 'action:string?'], - description: 'Checks the history of all tiles in the selected region. Can be filtered by action.', - perm: commands_1.Perm.none, - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps; - if (currentTapMode === "off" || args.action || args.amount) { - if (args.action && !allowedActions.includes(args.action)) - (0, commands_1.fail)("Invalid action. Allowed actions: ".concat(allowedActions.join(", "))); - if (args.amount && args.amount > 100) - (0, commands_1.fail)("Limit cannot be greater than 100."); - cachedPointMap[sender.uuid] = undefined; - handleTaps("on"); - outputSuccess("Aoelog mode enabled. To see the recent history of all tiles in a rectangular region, tap opposite corners of the rectangle. Run /aoelog with no arguments to disable."); - } - else { - handleTaps("off"); - outputSuccess("Aoelog disabled."); - } - }, - tapped: function (_a) { - var x = _a.x, y = _a.y, output = _a.output, outputFail = _a.outputFail, sender = _a.sender, admins = _a.admins, handleTaps = _a.handleTaps, args = _a.args; - function handleArea(p1, p2) { - var minX = Math.min(p1[0], p2[0]); - var maxX = Math.max(p1[0], p2[0]); - var minY = Math.min(p1[1], p2[1]); - var maxY = Math.max(p1[1], p2[1]); - var limitTiles = 0; - var amount = args.amount != null ? Math.floor(Math.abs(args.amount)) : 10; - outer: for (var i = minX; i <= maxX; i++) { - for (var j = minY; j <= maxY; j++) { - var tileData = globals_1.tileHistory["".concat(i, ",").concat(j)]; - if (!tileData) - continue; - var history = funcs_1.StringIO.read(globals_1.tileHistory["".concat(i, ",").concat(j)], function (str) { return str.readArray(function (d) { - var _a, _b, _c; - return ({ - action: (_a = d.readString(2)) !== null && _a !== void 0 ? _a : "??", - uuid: (_b = d.readString(3)) !== null && _b !== void 0 ? _b : "??", - time: d.readNumber(16), - type: (_c = d.readString(2)) !== null && _c !== void 0 ? _c : "??", - }); - }, 1); }); - if (args.action) - history = history.filter(function (e) { return e.action === args.action; }); - if (history.length == 0) - continue; - output("[yellow]Tile history for tile (".concat(i, ", ").concat(j, "):\n") + history.map(function (e) { - var _a, _b; - if (globals_1.uuidPattern.test(e.uuid)) { - if (sender.hasPerm("viewUUIDs")) - return "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - else - return "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - } - else - return "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); - }).join('\n')); - limitTiles++; - if (limitTiles === amount) - break outer; - } - } - if (limitTiles == 0) { - if (args.action) - outputFail("There is no recorded history for the selected region matching the provided filters."); - else - outputFail("There is no recorded history for the selected region."); - } - if (limitTiles == amount) - output("Displaying first ".concat(limitTiles, " entries. To show other entries, increase the limit or select a smaller area.")); - } - var p1 = cachedPointMap[sender.uuid]; - if (!p1) { - cachedPointMap[sender.uuid] = [x, y]; - output("1st point set at (".concat(x, ",").concat(y, ")")); - } - else { - var p2 = [x, y]; - output("2nd point set at (".concat(x, ", ").concat(y, ")")); - var width = Math.abs(p1[0] - p2[0]); - var height = Math.abs(p1[1] - p2[1]); - if (width > 50 || height > 50) - (0, commands_1.fail)("Selection too large: width/height cannot be more than 50."); - handleArea(p1, p2); - cachedPointMap[sender.uuid] = undefined; - if (!args.persist) - handleTaps("off"); - } - }, - }; - }), afk: { - args: [], - description: 'Toggles your afk status.', - perm: commands_1.Perm.none, - handler: function (_a) { - var sender = _a.sender, outputSuccess = _a.outputSuccess; - sender.manualAfk = !sender.manualAfk; - sender.updateName(); - if (sender.manualAfk) - outputSuccess("You are now marked as AFK."); - else - outputSuccess("You are no longer marked as AFK."); - }, - }, vanish: { - args: ['target:player?'], - description: "Toggles visibility of your rank and flags.", - perm: commands_1.Perm.vanish, - handler: function (_a) { - var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess; - if (sender.stelled()) - (0, commands_1.fail)("Marked players may not hide flags."); - if (sender.muted) - (0, commands_1.fail)("Muted players may not hide flags."); - if (sender != target && target.hasPerm("blockTrolling")) - (0, commands_1.fail)("Target is insufficentlly trollable."); - if (sender != target && !sender.ranksAtLeast("mod")) - (0, commands_1.fail)("You do not have permission to vanish other players."); - target.showRankPrefix = !target.showRankPrefix; - outputSuccess("".concat(target == sender ? "Your" : "".concat(target.name, "'s"), " rank prefix is now ").concat(target.showRankPrefix ? "visible" : "hidden", ".")); - }, - }, tileid: { - args: [], - description: 'Checks id of a tile.', - perm: commands_1.Perm.none, - handler: function (_a) { - var output = _a.output, handleTaps = _a.handleTaps; - handleTaps("once"); - output("Click a tile to see its id..."); - }, - tapped: function (_a) { - var output = _a.output, f = _a.f, tile = _a.tile; - output(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["ID is ", ""], ["ID is ", ""])), tile.block().id)); - } - } }, Object.fromEntries(config_1.FishServer.all.map(function (server) { return [ - server.name, - { - args: [], - description: "Switches to the ".concat(server.name, " server."), - perm: server.requiredPerm ? commands_1.Perm.getByName(server.requiredPerm) : commands_1.Perm.none, - isHidden: true, - handler: function (_a) { - var sender = _a.sender; - players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); - Call.connect(sender.con, server.ip, server.port); - }, - }, -]; }))), { switch: { - args: ["server:string", "target:player?"], - description: "Switches to another server.", - perm: commands_1.Perm.play, - handler: function (_a) { - var _b, _c; - var args = _a.args, sender = _a.sender, f = _a.f; - if (args.target != null && args.target != sender && !sender.canModerate(args.target, true, "admin", true)) - (0, commands_1.fail)(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["You do not have permission to switch player ", "."], ["You do not have permission to switch player ", "."])), args.target)); - var target = (_b = args.target) !== null && _b !== void 0 ? _b : sender; - if (globals_1.ipPortPattern.test(args.server) && sender.hasPerm("admin")) { - //direct connect - Call.connect.apply(Call, __spreadArray([target.con], __read(args.server.split(":")), false)); - } - else { - var unknownServerMessage = "Unknown server ".concat(args.server, ". Valid options: ").concat(config_1.FishServer.all.filter(function (s) { return !s.requiredPerm || sender.hasPerm(s.requiredPerm); }).map(function (s) { return s.name; }).join(", ")); - var server = (_c = config_1.FishServer.byName(args.server)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(unknownServerMessage); - //Pretend the server doesn't exist - if (server.requiredPerm && !sender.hasPerm(server.requiredPerm)) - (0, commands_1.fail)(unknownServerMessage); - if (target == sender) - players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); - Call.connect(target.con, server.ip, server.port); - } - } - }, s: { - args: ['message:string'], - description: "Sends a message to staff only.", - perm: commands_1.Perm.chat, - handler: function (_a) { - var sender = _a.sender, args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, lastUsedSender = _a.lastUsedSender; - if (!sender.hasPerm("mod")) { - if (Date.now() - lastUsedSender < 4000) - (0, commands_1.fail)("This command was used recently and is on cooldown. [orange]Misuse of this command may result in a mute."); - } - api.sendStaffMessage(args.message, sender.name, function (sent) { - if (!sender.hasPerm("mod")) { - if (sent) { - outputSuccess("Message sent to [orange]all online staff."); - } - else { - var wasReceived = players_1.FishPlayer.messageStaff(sender.prefixedName, args.message); - if (wasReceived) - outputSuccess("Message sent to staff."); - else - outputFail("No staff were online to receive your message."); - } - } - }); - }, - }, - /** - * This command is mostly for mobile (or players without foos). - * - * Since the player's unit follows the camera and we are moving the - * camera, we need to keep setting the players real position to the - * spot the command was made. This is pretty buggy but otherwise the - * player will be up the target player's butt - */ - watch: (0, commands_1.command)({ - args: ['player:player?'], - description: "Watch/unwatch a player.", - perm: commands_1.Perm.none, - data: new Set, - handler: function (_a) { - var args = _a.args, data = _a.data, sender = _a.sender, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; - if (data.has(sender.uuid)) { - outputSuccess("No longer watching a player."); - data.delete(sender.uuid); - } - else if (args.player) { - data.add(sender.uuid); - var senderUnit_1 = sender.unit(); - var stayX_1 = senderUnit_1 === null || senderUnit_1 === void 0 ? void 0 : senderUnit_1.x; - var stayY_1 = senderUnit_1 === null || senderUnit_1 === void 0 ? void 0 : senderUnit_1.y; - var target_1 = args.player.player; - (function watch() { - var _a, _b; - if (data.has(sender.uuid) && target_1.unit()) { - // Self.X+(172.5-Self.X)/10 - Call.setCameraPosition(sender.con, target_1.unit().x, target_1.unit().y); - if (senderUnit_1) - (_b = (_a = sender.unit()) === null || _a === void 0 ? void 0 : _a.set) === null || _b === void 0 ? void 0 : _b.call(_a, stayX_1, stayY_1); - Timer.schedule(function () { return watch(); }, 0.1, 0.1, 0); - } - else { - Call.setCameraPosition(sender.con, stayX_1, stayY_1); - } - })(); - } - else { - outputFail("No player to unwatch."); - } - }, - }), spectate: (0, commands_1.command)(function () { - //TODO revise code - /** Mapping between player and original team */ - var spectators = new Map(); - function spectate(target) { - spectators.set(target, target.team()); - target.forceRespawn(); - target.setTeam(Team.derelict); - target.forceRespawn(); - } - function resume(target) { - if (spectators.get(target) == null) - return; // this state is possible for a person who left not in spectate - target.setTeam(spectators.get(target)); - spectators.delete(target); - target.forceRespawn(); - } - Events.on(EventType.GameOverEvent, function () { return spectators.clear(); }); - Events.on(EventType.PlayerLeave, function (_a) { - var player = _a.player; - return resume(players_1.FishPlayer.get(player)); - }); - return { - args: ["target:player?"], - description: "Toggles spectator mode in PVP games.", - perm: commands_1.Perm.play, - requirements: [commands_1.Req.gameRunning], - handler: function (_a) { - var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess, f = _a.f; - if (!config_1.Gamemode.pvp() && !sender.hasPerm("mod")) - (0, commands_1.fail)("You do not have permission to spectate on a non-pvp server."); - if (target !== sender && target.hasPerm("blockTrolling")) - (0, commands_1.fail)("Target player is insufficiently trollable."); - if (target !== sender && !sender.ranksAtLeast("admin")) - (0, commands_1.fail)("You do not have permission to force other players to spectate."); - if (spectators.has(target)) { - resume(target); - outputSuccess(target == sender - ? f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Rejoining game as team ", "."], ["Rejoining game as team ", "."])), target.team()) : f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Forced ", " out of spectator mode."], ["Forced ", " out of spectator mode."])), target)); - } - else { - spectate(target); - outputSuccess(target == sender - ? f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Now spectating. Run /spectate again to resume gameplay."], ["Now spectating. Run /spectate again to resume gameplay."]))) : f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Forced ", " into spectator mode."], ["Forced ", " into spectator mode."])), target)); - } - } - }; - }), help: { - args: ['name:string?'], - description: 'Displays a list of all commands.', - perm: commands_1.Perm.none, - handler: function (_a) { - var _b; - var args = _a.args, output = _a.output, sender = _a.sender, allCommands = _a.allCommands; - var formatCommand = function (name, color) { - return new funcs_1.StringBuilder() - .add("".concat(color, "/").concat(name)) - .chunk("[white]".concat(allCommands[name].args.map(commands_1.formatArg).join(' '))) - .chunk("[lightgray]- ".concat(allCommands[name].description)).str; - }; - var formatList = function (commandList, color) { return commandList.map(function (c) { return formatCommand(c, color); }).join('\n'); }; - if (args.name && isNaN(parseInt(args.name)) && !['mod', 'admin', 'member'].includes(args.name)) { - //name is not a number or a category, therefore it is probably a command name - if (args.name in allCommands && (!allCommands[args.name].isHidden || allCommands[args.name].perm.check(sender))) { - output("Help for command ".concat(args.name, ":\n\t").concat(allCommands[args.name].description, "\n\tUsage: [sky]/").concat(args.name, " [white]").concat(allCommands[args.name].args.map(commands_1.formatArg).join(' '), "\n\tPermission required: ").concat(allCommands[args.name].perm.name)); - } - else - (0, commands_1.fail)("Command \"".concat(args.name, "\" does not exist.")); - } - else { - var commands_2 = { - player: [], - mod: [], - admin: [], - member: [], - }; - //TODO change this to category, not perm - Object.entries(allCommands).forEach(function (_a) { - var _b = __read(_a, 2), name = _b[0], data = _b[1]; - return (data.perm === commands_1.Perm.admin ? commands_2.admin : data.perm === commands_1.Perm.mod ? commands_2.mod : data.perm === commands_1.Perm.member ? commands_2.member : commands_2.player).push(name); - }); - var chunkedPlayerCommands = (0, funcs_1.to2DArray)(commands_2.player, 15); - switch (args.name) { - case 'admin': - output("".concat(commands_1.Perm.admin.color, "-- Admin commands --\n") + formatList(commands_2.admin, commands_1.Perm.admin.color)); - break; - case 'mod': - output("".concat(commands_1.Perm.mod.color, "-- Mod commands --\n") + formatList(commands_2.mod, commands_1.Perm.mod.color)); - break; - case 'member': - output("".concat(commands_1.Perm.member.color, "-- Member commands --\n") + formatList(commands_2.member, commands_1.Perm.member.color)); - break; - default: { - var pageNumber = args.name != undefined ? parseInt(args.name) : 1; - var page = (_b = chunkedPlayerCommands[pageNumber - 1]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("\"".concat(args.name, "\" is an invalid page number.")); - output("[sky]-- Commands page [lightgrey]".concat(pageNumber, "/").concat(chunkedPlayerCommands.length, "[sky] --\n") + formatList(page, '[sky]')); - } - } - } - }, - }, msg: { - args: ['player:player', 'message:string'], - description: 'Send a message to only one player.', - perm: commands_1.Perm.chat, - handler: function (_a) { - var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; - globals_1.recentWhispers[args.player.uuid] = sender.uuid; - args.player.sendMessage("".concat(sender.prefixedName, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); - output(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), args.player, args.message)); - }, - }, r: { - args: ['message:string'], - description: 'Reply to the most recent message.', - perm: commands_1.Perm.chat, - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; - var recipient = players_1.FishPlayer.getById((_b = globals_1.recentWhispers[sender.uuid]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("It doesn't look like someone has messaged you recently. Try whispering to them with [white]\"/msg \"")); - if (!(recipient === null || recipient === void 0 ? void 0 : recipient.connected())) - (0, commands_1.fail)("The person who last messaged you doesn't seem to exist anymore. Try whispering to someone with [white]\"/msg \""); - globals_1.recentWhispers[globals_1.recentWhispers[sender.uuid]] = sender.uuid; - recipient.sendMessage("".concat(sender.name, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); - output(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), recipient, args.message)); - }, - }, trail: { - args: ['type:string?', 'color:string?'], - description: 'Use command to see options and toggle trail on/off.', - perm: commands_1.Perm.none, - handler: function (_a) { - var args = _a.args, sender = _a.sender, output = _a.output, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; - //overload 1: type not specified - if (!args.type) { - if (sender.trail != null) { - sender.trail = null; - outputSuccess("Trail turned off."); - } - else { - output("Available types:[yellow]\n1 - fluxVapor (flowing smoke, long lasting)\n2 - overclocked (diamonds)\n3 - overdriven (squares)\n4 - shieldBreak (smol)\n5 - upgradeCoreBloom (square, long lasting, only orange)\n6 - electrified (tiny spiratic diamonds, but only green)\n7 - unitDust (same as above but round, and can change colors)\n[white]Usage: [orange]/trail [lightgrey] [color/#hex/r,g,b]"); - } - return; - } - //overload 2: type specified - var trailTypes = { - "1": 'fluxVapor', - "2": 'overclocked', - "3": 'overdriven', - "4": 'shieldBreak', - "5": 'upgradeCoreBloom', - "6": 'electrified', - "7": 'unitDust', - }; - var selectedType = trailTypes[args.type]; - if (!selectedType) { - if (Object.values(trailTypes).includes(args.type)) - (0, commands_1.fail)("Please use the numeric id to refer to a trail type."); - else - (0, commands_1.fail)("\"".concat(args.type, "\" is not an available type.")); - } - var color = args.color ? (0, utils_1.getColor)(args.color) : Color.white; - if (color instanceof Color) { - sender.trail = { - type: selectedType, - color: color, - }; - } - else { - outputFail("[scarlet]Sorry, \"".concat(args.color, "\" is not a valid color.\n[yellow]Color can be in the following formats:\n[pink]pink [white]| [gray]#696969 [white]| 255,0,0.")); - } - }, - }, ohno: (0, commands_1.command)({ - args: [], - description: 'Spawns an ohno.', - perm: commands_1.Perm.play, - init: function () { - var Ohnos = { - enabled: true, - ohnos: new Array(), - makeOhno: function (team, x, y) { - var ohno = UnitTypes.atrax.create(team); - ohno.set(x, y); - ohno.type = UnitTypes.alpha; - ohno.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); - ohno.resetController(); //does this work? - ohno.add(); - this.ohnos.push(ohno); - return ohno; - }, - updateLength: function () { - this.ohnos = this.ohnos.filter(function (o) { return o && o.isAdded() && !o.dead; }); - }, - killAll: function () { - this.ohnos.forEach(function (ohno) { var _a; return (_a = ohno === null || ohno === void 0 ? void 0 : ohno.kill) === null || _a === void 0 ? void 0 : _a.call(ohno); }); - this.ohnos = []; - }, - amount: function () { - return this.ohnos.length; - }, - }; - Events.on(EventType.GameOverEvent, function (e) { - Ohnos.killAll(); - }); - return Ohnos; - }, - requirements: [ - commands_1.Req.gameRunning, commands_1.Req.modeNot("pvp"), - commands_1.Req.unitExists("You cannot spawn ohnos while dead.") - ], - handler: function (_a) { - var sender = _a.sender, Ohnos = _a.data; - if (!Ohnos.enabled) - (0, commands_1.fail)("Ohnos have been temporarily disabled."); - Ohnos.updateLength(); - if (Ohnos.ohnos.length >= (Groups.player.size() + 1) || - sender.team().data().countType(UnitTypes.alpha) >= Units.getCap(sender.team())) - (0, commands_1.fail)("Sorry, the max number of ohno units has been reached."); - if ((0, utils_1.nearbyEnemyTile)((sender.unit()), 6) != null) - (0, commands_1.fail)("Too close to an enemy building!"); - if (!UnitTypes.alpha.supportsEnv(Vars.state.rules.env)) - (0, commands_1.fail)("Ohnos cannot survive in this map."); - Ohnos.makeOhno(sender.team(), sender.player.x, sender.player.y); - }, - }), ranks: { - args: [], - description: 'Displays information about all ranks.', - perm: commands_1.Perm.none, - handler: function (_a) { - var output = _a.output; - output("List of ranks:\n" + - Object.values(ranks_1.Rank.ranks) - .map(function (rank) { return "".concat(rank.prefix, " ").concat(rank.color).concat((0, funcs_1.capitalizeText)(rank.name), "[]: ").concat(rank.color).concat(rank.description, "[]\n"); }) - .join("") + - "List of flags:\n" + - Object.values(ranks_1.RoleFlag.flags) - .map(function (flag) { return "".concat(flag.prefix, " ").concat(flag.color).concat((0, funcs_1.capitalizeText)(flag.name), "[]: ").concat(flag.color).concat(flag.description, "[]\n"); }) - .join("")); - }, - }, rules: { - args: ['player:player?'], - description: 'Displays the server rules.', - perm: commands_1.Perm.none, - handler: function (_a) { - var _b; - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - var target = (_b = args.player) !== null && _b !== void 0 ? _b : sender; - if (target !== sender) { - if (!sender.hasPerm("warn")) - (0, commands_1.fail)("You do not have permission to show rules to other players."); - if (target.hasPerm("blockTrolling")) - (0, commands_1.fail)(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); - } - menus_1.Menu.menu("Rules for [#0000ff]>|||> FISH [white]servers", config_1.rules.join("\n\n"), ["[green]I agree to abide by these rules[]", "No"], target).then(function (option) { - if (option == "No") { - target.kick("You must agree to the rules to play on this server. Rejoin to agree to the rules.", 1); - outputSuccess('Player rejected the rules and was kicked.'); - } - else { - outputSuccess('Player acknowledged the rules.'); - } - }); - if (target !== sender) - outputSuccess(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Reminded ", " of the rules."], ["Reminded ", " of the rules."])), target)); - }, - }, void: { - args: ["player:player?"], - description: 'Warns other players about power voids.', - perm: commands_1.Perm.play, - requirements: function (_a) { - var args = _a.args; - return [ - commands_1.Req.mode("attack"), - args.player ? commands_1.Req.cooldown(20000) : commands_1.Req.cooldownGlobal(10000) - ]; - }, - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - if (args.player) { - if (!sender.hasPerm("trusted")) - (0, commands_1.fail)("You do not have permission to show popups to other players, please run /void with no arguments to send a chat message to everyone."); - if (args.player !== sender && args.player.hasPerm("blockTrolling")) - (0, commands_1.fail)("Target player is insufficiently trollable."); - menus_1.Menu.menu("\uf83f [scarlet]WARNING[] \uf83f", "[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to.\nIf you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!", ["I understand"], args.player, { onCancel: 'null' }).then(function () { return outputSuccess(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", " acknowledged the warning."], ["Player ", " acknowledged the warning."])), args.player)); }); - (0, utils_1.logAction)("showed void warning", sender, args.player); - outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Warned ", " about power voids with a popup message."], ["Warned ", " about power voids with a popup message."])), args.player)); - } - else { - Call.sendMessage("[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to. If you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!"); - } - }, - }, team: { - args: ['team:team', 'reason:string?'], - description: 'Changes your team.', - perm: commands_1.Perm.changeTeam, - handler: function (_a) { - var _b; - var sender = _a.sender, _c = _a.args, team = _c.team, reason = _c.reason, outputSuccess = _a.outputSuccess, f = _a.f; - if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) - (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); - if (config_1.Gamemode.sandbox() && team === Vars.state.rules.waveTeam && !sender.hasPerm("admin")) - (0, commands_1.fail)("You do not have permission to change to the wave team on sandbox."); - if (!config_1.Gamemode.sandbox() && !sender.hasPerm("mod") && !reason) - (0, commands_1.fail)("Please specify a reason for changing teams."); - if (!sender.hasPerm("changeTeamExternal")) { - if (team.data().cores.size <= 0) - (0, commands_1.fail)("You do not have permission to change to a team with no cores."); - if (!sender.player.dead() && !((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) - sender.forceRespawn(); - } - if (!sender.hasPerm("mod")) - sender.changedTeam = true; - sender.setTeam(team); - outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Changed your team to ", "."], ["Changed your team to ", "."])), team)); - if (reason && !config_1.Gamemode.sandbox()) - (0, utils_1.logAction)("changed team to ".concat(team.name, " on ").concat((0, funcs_1.escapeTextDiscord)(Vars.state.map.plainName()), " with reason ").concat((0, funcs_1.escapeTextDiscord)(reason)), sender); - }, - }, teamp: { - args: ['team:team', 'target:player'], - description: 'Changes the team of a player.', - perm: commands_1.Perm.changeTeam, - handler: function (_a) { - var _b; - var sender = _a.sender, _c = _a.args, team = _c.team, target = _c.target, outputSuccess = _a.outputSuccess, f = _a.f; - if (!sender.canModerate(target, true, "mod", true)) - (0, commands_1.fail)(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["You do not have permission to change the team of ", ""], ["You do not have permission to change the team of ", ""])), target)); - if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) - (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); - if (!sender.hasPerm("changeTeamExternal")) { - if (team.data().cores.size <= 0) - (0, commands_1.fail)("You do not have permission to change to a team with no cores."); - if (!target.player.dead() && !((_b = target.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) - target.forceRespawn(); - } - target.setTeam(team); - outputSuccess(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["Changed team of player ", " to ", "."], ["Changed team of player ", " to ", "."])), target, team)); - }, - }, rank: { - args: ['player:player'], - description: 'Displays the rank of a player.', - perm: commands_1.Perm.none, - handler: function (_a) { - var args = _a.args, output = _a.output, f = _a.f; - output(f(templateObject_16 || (templateObject_16 = __makeTemplateObject(["Player ", "'s rank is ", "."], ["Player ", "'s rank is ", "."])), args.player, args.player.rank)); - }, - }, forcevnw: { - args: ["force:boolean?"], - description: 'Force skip to the next wave.', - perm: commands_1.Perm.admin, - handler: function (_a) { - var allCommands = _a.allCommands, sender = _a.sender, _b = _a.args.force, force = _b === void 0 ? true : _b; - if (allCommands.vnw.data.manager.session == null) { - if (force == false) - (0, commands_1.fail)("Cannot clear votes for VNW because no vote is currently ongoing."); - (0, utils_1.skipWaves)(1, true); - } - else { - if (force) - Call.sendMessage("VNW: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green], skipping wave.")); - else - Call.sendMessage("VNW: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); - allCommands.vnw.data.manager.forceVote(force); - } - }, - }, vnw: (0, commands_1.command)({ - args: [], - description: "Vote to start the next wave.", - perm: commands_1.Perm.play, - init: function () { return ({ - manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5)) - .on("success", function (t) { return (0, utils_1.skipWaves)(t.session.data, true); }) - .on("vote passed", function () { return Call.sendMessage('VNW: [green]Vote passed, skipping to next wave.'); }) - .on("vote failed", function () { return Call.sendMessage('VNW: [red]Vote failed.'); }) - .on("player vote change", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has voted on skipping [accent]").concat(t.session.data, "[white] wave(s). [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - .on("player vote removed", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has left. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - }); }, - requirements: [commands_1.Req.cooldown(3000), commands_1.Req.mode("survival"), commands_1.Req.gameRunning], - handler: function (_a) { - return __awaiter(this, arguments, void 0, function (_b) { - var option; - var sender = _b.sender, manager = _b.data.manager; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - if (!!manager.session) return [3 /*break*/, 2]; - return [4 /*yield*/, menus_1.Menu.menu("Start a Next Wave Vote", "Select the amount of waves you would like to skip.", [1, 5, 10], sender, { - includeCancel: true, - optionStringifier: function (n) { return "".concat(n, " waves"); } - })]; - case 1: - option = _c.sent(); - if (manager.session) { - //Someone else started a vote - if (manager.session.data != option) - (0, commands_1.fail)("Someone else started a vote with a different number of waves to skip."); - else - manager.vote(sender, sender.voteWeight(), option); - } - else { - manager.start(sender, sender.voteWeight(), option); - } - return [3 /*break*/, 3]; - case 2: - manager.vote(sender, sender.voteWeight(), null); - _c.label = 3; - case 3: return [2 /*return*/]; - } - }); - }); - } - }), forcertv: { - args: ["force:boolean?"], - description: 'Force skip to the next map.', - perm: commands_1.Perm.admin, - handler: function (_a) { - var _b = _a.args.force, force = _b === void 0 ? true : _b, sender = _a.sender, allCommands = _a.allCommands; - if (allCommands.rtv.data.manager.session == null) { - if (force == false) - (0, commands_1.fail)("Cannot clear votes for RTV because no vote is currently ongoing."); - allCommands.rtv.data.manager.forceVote(true); - } - else { - if (force) - Call.sendMessage("RTV: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green].")); - else - Call.sendMessage("RTV: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); - allCommands.rtv.data.manager.forceVote(force); - } - } - }, rtv: (0, commands_1.command)({ - args: [], - description: 'Rock the vote to change map.', - perm: commands_1.Perm.play, - init: function () { return ({ - manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), config_1.Gamemode.hexed() ? ["fractionOfVoters", 1] : undefined) //Require unanimity in Hexed, as it is often 1 v everyone - .on("success", function () { return (0, utils_1.neutralGameover)(); }) - .on("vote passed", function () { return Call.sendMessage("RTV: [green]Vote has passed, changing map."); }) - .on("vote failed", function () { return Call.sendMessage("RTV: [red]Vote failed."); }) - .on("player vote change", function (t, player, oldVote, newVote) { return Call.sendMessage("RTV: ".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to change the map. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - .on("player vote removed", function (t, player) { return Call.sendMessage("RTV: ".concat(player.name, "[white] has left the game. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) - }); }, - requirements: [commands_1.Req.cooldown(3000), commands_1.Req.gameRunning], - handler: function (_a) { - var sender = _a.sender, manager = _a.data.manager; - manager.vote(sender, 1, 0); //No weighting for RTV except for removing AFK players - } - }), - // votekick: { - // args: ["target:player"], - // description: "Starts a vote to kick a player.", - // perm: Perm.play, - // handler({args, sender}){ - // if(votekickmanager.currentSession) fail(`There is already a votekick in progress.`); - // votekickmanager.start({ - // initiator: sender, - // target: args.player - // }); - // } - // }, - // vote: { - // args: ["vote:boolean"], - // description: "Use /votekick instead.", - // perm: Perm.play, - // handler({sender, args}){ - // votekickmanager.handleVote(sender, args ? 1 : -1); - // } - // }, - forcenextmap: { - args: ["map:map"], - description: 'Override the next map in queue.', - perm: commands_1.Perm.admin.exceptModes({ - testsrv: commands_1.Perm.play - }), - handler: function (_a) { - var allCommands = _a.allCommands, args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; - Vars.maps.setNextMapOverride(args.map); - if (allCommands.nextmap.data.voteEndTime() > -1) { - //Cancel /nextmap vote if it's ongoing - allCommands.nextmap.data.resetVotes(); - Call.sendMessage("[red]Admin ".concat(sender.name, "[red] has cancelled the vote. The next map will be [yellow]").concat(args.map.name(), ".")); - } - else { - outputSuccess(f(templateObject_17 || (templateObject_17 = __makeTemplateObject(["Forced the next map to be \"", "\" by ", ""], ["Forced the next map to be \"", "\" by ", ""])), args.map.name(), args.map.author())); - } - }, - }, maps: { - args: [], - description: 'Lists the available maps.', - perm: commands_1.Perm.none, - handler: function (_a) { - var output = _a.output; - output("[yellow]Use [white]/nextmap [lightgray] [yellow]to vote on a map.\n\n[blue]Available maps:\n_________________________\n".concat(Vars.maps.customMaps().toArray().map(function (map) { - return "[yellow]".concat(map.name()); - }).join("\n"))); - } - }, nextmap: (0, commands_1.command)(function () { - var votes = new Map(); - var lastVoteCount = 0; - var lastVoteTime = 0; - var voteEndTime = -1; - var voteDuration = funcs_1.Duration.minutes(1.5); - var task = null; - function resetVotes() { - votes.clear(); - voteEndTime = -1; - task === null || task === void 0 ? void 0 : task.cancel(); - } - function getMapData() { - return __spreadArray([], __read(votes.values()), false).reduce(function (acc, map) { return (acc.increment(map), acc); }, new ObjectIntMap()).entries().toArray(); - } - function showVotes() { - Call.sendMessage("[green]Current votes:\n------------------------------\n".concat(getMapData().map(function (_a) { - var map = _a.key, votes = _a.value; - return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); - }).toString("\n"))); - } - function startVote() { - voteEndTime = Date.now() + voteDuration; - task = Timer.schedule(endVote, voteDuration / 1000); - } - function endVote() { - if (voteEndTime == -1) - return; //aborted somehow - if (votes.size == 0) - return; //no votes? - if (votes.size + 2 <= lastVoteCount && (Date.now() - lastVoteTime) < funcs_1.Duration.minutes(10)) { - //If the number of votes is 2 less than the previous number of votes for a vote in the past 10 minutes, abor - Call.sendMessage("[cyan]Next Map Vote: [scarlet]Vote aborted because a previous vote had significantly higher turnout"); - resetVotes(); - return; - } - else { - lastVoteTime = Date.now(); - lastVoteCount = votes.size; - } - var mapData = getMapData(); - var highestVoteCount = mapData.max(floatf(function (e) { return e.value; })).value; - var highestVotedMaps = mapData.select(function (e) { return e.value == highestVoteCount; }); - var winner; - if (highestVotedMaps.size > 1) { - winner = highestVotedMaps.random().key; - Call.sendMessage("[green]There was a tie between the following maps:\n".concat(highestVotedMaps.map(function (_a) { - var map = _a.key, votes = _a.value; - return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); - }).toString("\n"), "\n[green]Picking random winner: [yellow]").concat(winner.name())); - } - else { - winner = highestVotedMaps.get(0).key; - Call.sendMessage("[green]Map voting complete! The next map will be [yellow]".concat(winner.name(), " [green]with [yellow]").concat(highestVoteCount, "[green] votes.")); - } - Vars.maps.setNextMapOverride(winner); - resetVotes(); - } - Events.on(EventType.GameOverEvent, resetVotes); - Events.on(EventType.ServerLoadEvent, resetVotes); - return { - args: ['map:map'], - description: 'Allows you to vote for the next map. Use /maps to see all available maps.', - perm: commands_1.Perm.play, - data: { votes: votes, voteEndTime: function () { return voteEndTime; }, resetVotes: resetVotes, endVote: endVote }, - requirements: [commands_1.Req.cooldown(10000)], - handler: function (_a) { - var map = _a.args.map, sender = _a.sender; - if (config_1.Gamemode.testsrv()) - (0, commands_1.fail)("Please use /forcenextmap instead."); - if (votes.get(sender)) - (0, commands_1.fail)("You have already voted."); - votes.set(sender, map); - if (voteEndTime == -1) { - if ((Date.now() - lastVoteTime) < funcs_1.Duration.minutes(1)) - (0, commands_1.fail)("Please wait 1 minute before starting a new map vote."); - startVote(); - Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] started a map vote, and voted for [yellow]").concat(map.name(), "[cyan]. Use [white]/nextmap ").concat(map.plainName(), "[] to add your vote, or run [white]/maps[] to see other available maps.")); - } - else { - Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] voted for [yellow]").concat(map.name(), "[cyan]. Time left: [scarlet]").concat((0, utils_1.formatTimeRelative)(voteEndTime, true))); - showVotes(); - } - } - }; - }), surrender: (0, commands_1.command)(function () { - var prefix = "[orange]Surrender[white]: "; - var managers = Team.all.map(function (team) { - return new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), ["fractionOfVoters", config_1.Gamemode.hexed() ? 1 : 3 / 4], function (p) { return p.team() == team; }) - .on("success", function () { return team.cores().copy().each(function (c) { return c.kill(); }); }) - .on("vote passed", function () { return Call.sendMessage(prefix + "Team ".concat(team.coloredName(), " has voted to forfeit this match.")); }) - .on("vote failed", function (t) { return t.messageEligibleVoters(prefix + "Team ".concat(team.coloredName(), " has chosen not to forfeit this match.")); }) - .on("player vote change", function (t, player, oldVote, newVote) { return t.messageEligibleVoters(prefix + "".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to forfeit this match. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }) - .on("player vote removed", function (t, player) { return t.messageEligibleVoters(prefix + "Player ".concat(player.name, "[white] has left the game. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }); - }); - globals_1.FishEvents.on("playerTeamChange", function (_, fishP, previous) { - managers[previous.id].unvote(fishP); - }); - return { - args: ["force:boolean?"], - description: "Vote to surrender to the enemy team.", - perm: commands_1.Perm.play, - requirements: [commands_1.Req.mode("pvp"), commands_1.Req.teamAlive], - data: { managers: managers }, - handler: function (_a) { - var sender = _a.sender, force = _a.args.force; - var manager = managers[sender.team().id]; - if (sender.hasPerm("admin") && force != undefined) - manager.forceVote(force); - if (sender.ranksAtLeast("mod")) - commands_1.Req.cooldown(5000); - else - commands_1.Req.cooldown(20000); - manager.vote(sender, 1, 0); - }, - }; - }), stats: { - args: ["target:player"], - perm: commands_1.Perm.none, - description: "Views a player's stats.", - handler: function (_a) { - var target = _a.args.target, output = _a.output, f = _a.f; - output(f(templateObject_18 || (templateObject_18 = __makeTemplateObject(["[accent]Statistics for player ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""], ["[accent]\\\nStatistics for player ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""])), target, target.stats.blocksBroken, target.stats.blocksPlaced, target.stats.chatMessagesSent, target.stats.gamesFinished, (0, utils_1.formatTime)(target.stats.timeInGame), target.stats.gamesWon / target.stats.gamesFinished)); - } - }, showworld: { - args: ["x:number?", "y:number?", "size:number?"], - perm: commands_1.Perm.none, - description: "Views the world as a 2D scrollable menu.", - requirements: [commands_1.Req.cooldown(4000)], - handler: function (_a) { - var sender = _a.sender, _b = _a.args, _c = _b.size, size = _c === void 0 ? 7 : _c, x = _b.x, y = _b.y; - if (size > 20) - (0, commands_1.fail)("Size ".concat(size, " is too high!")); - if (Vars.state.rules.fog) - (0, commands_1.fail)("This command is disabled when fog is enabled."); - var options = (0, funcs_1.to2DArray)(Reflect.get(Vars.world.tiles, "array").map(function (tile) { return ({ - text: tile.block().emoji(), - data: null, - }); }), Vars.world.width()).reverse(); - var height = Vars.world.height(); - menus_1.Menu.scroll(sender, "The World", "Use the arrow keys to navigate around the world. Click a blank square to exit.", options, { - columns: size, - rows: size, - x: x ? x - Math.trunc(size / 2) : 0, - y: height - (y ? y + 1 + Math.trunc(size / 2) : size), - getCenterText: function (x, y) { return "".concat(x, ",").concat(height - y - size); } - }); - } - }, mapinfo: { - args: ["map:map?"], - perm: commands_1.Perm.none, - description: "Displays information about a map.", - handler: function (_a) { - var output = _a.output, map = _a.args.map, f = _a.f, sender = _a.sender; - if (map) { - output(maps_1.FMap.getCreate(map).displayStats(f)); - } - else { - menus_1.Menu.textPages(sender, Vars.maps.customMaps().map(function (m) { - return ["Map information", function () { return maps_1.FMap.getCreate(m).displayStats(f); }]; - }).toArray(), { - startPage: Vars.maps.customMaps().toArray().indexOf(Vars.state.map), - }); - } - } - }, gamemode: { - args: ["mode:string"], - perm: new commands_1.Perm("changeGamemode", "manager").exceptModes({ - testsrv: commands_1.Perm.play, - }), - description: "Sets the gamemode.", - requirements: [commands_1.Req.cooldownGlobal(10000)], - handler: function (_a) { - var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, lastUsedSuccessfully = _a.lastUsedSuccessfully; - if (!sender.hasPerm('trusted')) - commands_1.Req.cooldownGlobal(30000)({ lastUsedSuccessfully: lastUsedSuccessfully }); - //Unpause - Vars.state.set(GameState.State.playing); - switch (args.mode) { - case "attack": - Vars.state.rules.attackMode = true; - Vars.state.rules.pvp = false; - Vars.state.rules.infiniteResources = false; - break; - case "survival": - Vars.state.rules.attackMode = false; - Vars.state.rules.waves = true; - Vars.state.rules.pvp = false; - Vars.state.rules.infiniteResources = false; - break; - case "pvp": - Vars.state.rules.attackMode = true; - Vars.state.rules.pvp = true; - Vars.state.rules.waves = false; - Vars.state.rules.infiniteResources = false; - break; - case "sandbox": - Vars.state.rules.attackMode = true; - Vars.state.rules.pvp = false; - Vars.state.rules.waves = false; - Vars.state.rules.infiniteResources = true; - break; - default: (0, commands_1.fail)("Invalid mode, valid modes are: attack, survival, pvp"); - } - var reloader = new WorldReloader(); - Reflect.set(reloader, "wasServer", true); - Reflect.set(reloader, "players", Groups.player.copy()); - Call.worldDataBegin(); - reloader.end(); - Call.sendMessage("[orange]Player ".concat(sender.cleanedName, " changed the gamemode to ").concat(args.mode)); - outputSuccess("Changed mode to ".concat(args.mode)); - } - }, mixunit: { - args: ["type:unittype", "base:unittype"], - description: "Spawns a unit that is made of two unit types mixed together.", - perm: commands_1.Perm.admin.exceptModes({ - sandbox: commands_1.Perm.play - }), - requirements: function (_a) { - var sender = _a.sender; - return [!sender.hasPerm("admin") && commands_1.Req.cooldown(1500), commands_1.Req.unitExists()].filter(Boolean); - }, - handler: function (_a) { - var args = _a.args, sender = _a.sender, f = _a.f, outputSuccess = _a.outputSuccess; - var _b = sender.unit(), team = _b.team, x = _b.x, y = _b.y; - var unit = args.base.create(team); - unit.type = args.type; - unit.maxHealth = args.type.health; //because half-dead units aren't fun - unit.set(x, y); - unit.add(); - outputSuccess(f(templateObject_19 || (templateObject_19 = __makeTemplateObject(["Spawned a ", " that is partly a ", "."], ["Spawned a ", " that is partly a ", "."])), args.type, args.base)); - } - } })); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18, templateObject_19; +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains most in-game chat commands that can be run by untrusted players. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.commands = void 0; +var api = require("/api"); +var config_1 = require("/config"); +var commands_1 = require("/frameworks/commands"); +var menus_1 = require("/frameworks/menus"); +var funcs_1 = require("/funcs"); +var globals_1 = require("/globals"); +var maps_1 = require("/maps"); +var players_1 = require("/players"); +var ranks_1 = require("/ranks"); +var utils_1 = require("/utils"); +var votes_1 = require("/votes"); +exports.commands = (0, commands_1.commandList)(__assign(__assign({ about: { + args: [], + description: 'Prints information about the plugin.', + perm: commands_1.Perm.none, + handler: function (_a) { + var _b, _c; + var output = _a.output; + output("[accent][cyan]fish-commands[] is the monolithic plugin used for the Fish servers' features.\n[accent]==========\n[accent]Source code available at: [cyan]https://github.com/Fish-Community/fish-commands/\n[accent]Current plugin version: [cyan]".concat((_c = (_b = globals_1.fishPlugin.version) === null || _b === void 0 ? void 0 : _b.slice(0, 8)) !== null && _c !== void 0 ? _c : "[scarlet]null[]", "[]")); + } + }, unpause: (0, commands_1.command)({ + args: [], + description: 'Unpauses the game.', + perm: commands_1.Perm.trusted, + requirements: [commands_1.Req.mode('pvp')], + init: function () { + var data = { unpaused: false }; + Events.on(EventType.PlayEvent, function () { + if (data.unpaused) { + data.unpaused = false; + Vars.state.rules.pvpAutoPause = true; + } + }); + return data; + }, + handler: function (_a) { + var data = _a.data, outputSuccess = _a.outputSuccess; + Vars.state.rules.pvpAutoPause = false; + data.unpaused = true; + Core.app.post(function () { return Vars.state.set(GameState.State.playing); }); + outputSuccess("Unpaused."); + }, + }), tp: { + args: ['player:player'], + description: 'Teleport to another player.', + perm: commands_1.Perm.play, + requirements: [commands_1.Req.modeNot("pvp")], + handler: function (_a) { + var _b, _c, _d; + var args = _a.args, sender = _a.sender; + if (!((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) + (0, commands_1.fail)("Can only teleport while in a core unit."); + if (sender.team() !== args.player.team()) + (0, commands_1.fail)("Cannot teleport to players on another team."); + if ((_d = (_c = sender.unit()).hasPayload) === null || _d === void 0 ? void 0 : _d.call(_c)) + (0, commands_1.fail)("Cannot teleport to players while holding a payload."); + (0, utils_1.teleportPlayer)(sender.player, args.player.player); + }, + }, clean: { + args: [], + description: 'Removes all boulders from the map.', + perm: commands_1.Perm.play, + requirements: [commands_1.Req.cooldownGlobal(100000)], + handler: function (_a) { + var sender = _a.sender, outputSuccess = _a.outputSuccess; + Timer.schedule(function () { return Call.sound(sender.con, Sounds.rockBreak, 1, 1, 0); }, 0, 0.05, 10); + Vars.world.tiles.eachTile(function (t) { + if (t.breakable() && t.block() instanceof Prop) { + t.removeNet(); + } + }); + outputSuccess("Cleared the map of boulders."); + } + }, die: { + args: [], + description: 'Kills your unit.', + perm: commands_1.Perm.mod.exceptModes({ + sandbox: commands_1.Perm.play + }, "You do not have permission to die."), + handler: function (_a) { + var _b; + var sender = _a.sender; + (_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.kill(); + }, + }, discord: { + args: [], + description: 'Takes you to our discord.', + perm: commands_1.Perm.none, + handler: function (_a) { + var sender = _a.sender; + Call.openURI(sender.con, config_1.text.discordURL); + }, + }, tilelog: { + args: ['persist:boolean?'], + description: 'Checks the history of a tile.', + perm: commands_1.Perm.none, + handler: function (_a) { + var args = _a.args, output = _a.output, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps; + if (currentTapMode == "off") { + if (args.persist) { + handleTaps("on"); + outputSuccess("Tilelog mode enabled. Click tiles to check their recent history. Run /tilelog again to disable."); + } + else { + handleTaps("once"); + output("Click on a tile to check its recent history..."); + } + } + else { + handleTaps("off"); + outputSuccess("Tilelog disabled."); + } + }, + tapped: function (_a) { + var _b; + var tile = _a.tile, x = _a.x, y = _a.y, output = _a.output, sender = _a.sender, admins = _a.admins; + var historyData = (_b = globals_1.tileHistory["".concat(x, ",").concat(y)]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("There is no recorded history for the selected tile (".concat(tile.x, ", ").concat(tile.y, ").")); + var history = funcs_1.StringIO.read(historyData, function (str) { return str.readArray(function (d) { return ({ + action: d.readString(2), + uuid: d.readString(3), + time: d.readNumber(16), + type: d.readString(2), + }); }, 1); }); + output("[yellow]Tile history for tile (".concat(tile.x, ", ").concat(tile.y, "):\n") + history.map(function (e) { + var _a, _b; + return globals_1.uuidPattern.test(e.uuid) + ? (sender.hasPerm("viewUUIDs") + ? "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)) + : "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time))) + : "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + }).join('\n')); + } + }, aoelog: (0, commands_1.command)(function () { + var allowedActions = [ + "built", "broke", "rotated", "killed", "configured", "pay-dropped", "picked up", "controlled" + ]; + var cachedPointMap = Object.create(null); + return { + args: ['persist:boolean?', 'amount:number?', 'action:string?'], + description: 'Checks the history of all tiles in the selected region. Can be filtered by action.', + perm: commands_1.Perm.none, + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, currentTapMode = _a.currentTapMode, handleTaps = _a.handleTaps; + if (currentTapMode === "off" || args.action || args.amount) { + if (args.action && !allowedActions.includes(args.action)) + (0, commands_1.fail)("Invalid action. Allowed actions: ".concat(allowedActions.join(", "))); + if (args.amount && args.amount > 100) + (0, commands_1.fail)("Limit cannot be greater than 100."); + cachedPointMap[sender.uuid] = undefined; + handleTaps("on"); + outputSuccess("Aoelog mode enabled. To see the recent history of all tiles in a rectangular region, tap opposite corners of the rectangle. Run /aoelog with no arguments to disable."); + } + else { + handleTaps("off"); + outputSuccess("Aoelog disabled."); + } + }, + tapped: function (_a) { + var x = _a.x, y = _a.y, output = _a.output, outputFail = _a.outputFail, sender = _a.sender, admins = _a.admins, handleTaps = _a.handleTaps, args = _a.args; + function handleArea(p1, p2) { + var minX = Math.min(p1[0], p2[0]); + var maxX = Math.max(p1[0], p2[0]); + var minY = Math.min(p1[1], p2[1]); + var maxY = Math.max(p1[1], p2[1]); + var limitTiles = 0; + var amount = args.amount != null ? Math.floor(Math.abs(args.amount)) : 10; + outer: for (var i = minX; i <= maxX; i++) { + for (var j = minY; j <= maxY; j++) { + var tileData = globals_1.tileHistory["".concat(i, ",").concat(j)]; + if (!tileData) + continue; + var history = funcs_1.StringIO.read(globals_1.tileHistory["".concat(i, ",").concat(j)], function (str) { return str.readArray(function (d) { + var _a, _b, _c; + return ({ + action: (_a = d.readString(2)) !== null && _a !== void 0 ? _a : "??", + uuid: (_b = d.readString(3)) !== null && _b !== void 0 ? _b : "??", + time: d.readNumber(16), + type: (_c = d.readString(2)) !== null && _c !== void 0 ? _c : "??", + }); + }, 1); }); + if (args.action) + history = history.filter(function (e) { return e.action === args.action; }); + if (history.length == 0) + continue; + output("[yellow]Tile history for tile (".concat(i, ", ").concat(j, "):\n") + history.map(function (e) { + var _a, _b; + if (globals_1.uuidPattern.test(e.uuid)) { + if (sender.hasPerm("viewUUIDs")) + return "[yellow]".concat((_a = admins.getInfoOptional(e.uuid)) === null || _a === void 0 ? void 0 : _a.plainLastName(), "[lightgray](").concat(e.uuid, ")[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + else + return "[yellow]".concat((_b = admins.getInfoOptional(e.uuid)) === null || _b === void 0 ? void 0 : _b.plainLastName(), " ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + } + else + return "[yellow]".concat(e.uuid, "[yellow] ").concat(e.action, " a [cyan]").concat(e.type, "[] ").concat((0, utils_1.formatTimeRelative)(e.time)); + }).join('\n')); + limitTiles++; + if (limitTiles === amount) + break outer; + } + } + if (limitTiles == 0) { + if (args.action) + outputFail("There is no recorded history for the selected region matching the provided filters."); + else + outputFail("There is no recorded history for the selected region."); + } + if (limitTiles == amount) + output("Displaying first ".concat(limitTiles, " entries. To show other entries, increase the limit or select a smaller area.")); + } + var p1 = cachedPointMap[sender.uuid]; + if (!p1) { + cachedPointMap[sender.uuid] = [x, y]; + output("1st point set at (".concat(x, ",").concat(y, ")")); + } + else { + var p2 = [x, y]; + output("2nd point set at (".concat(x, ", ").concat(y, ")")); + var width = Math.abs(p1[0] - p2[0]); + var height = Math.abs(p1[1] - p2[1]); + if (width > 50 || height > 50) + (0, commands_1.fail)("Selection too large: width/height cannot be more than 50."); + handleArea(p1, p2); + cachedPointMap[sender.uuid] = undefined; + if (!args.persist) + handleTaps("off"); + } + }, + }; + }), afk: { + args: [], + description: 'Toggles your afk status.', + perm: commands_1.Perm.none, + handler: function (_a) { + var sender = _a.sender, outputSuccess = _a.outputSuccess; + sender.manualAfk = !sender.manualAfk; + sender.updateName(); + if (sender.manualAfk) + outputSuccess("You are now marked as AFK."); + else + outputSuccess("You are no longer marked as AFK."); + }, + }, vanish: { + args: ['target:player?'], + description: "Toggles visibility of your rank and flags.", + perm: commands_1.Perm.vanish, + handler: function (_a) { + var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess; + if (sender.stelled()) + (0, commands_1.fail)("Marked players may not hide flags."); + if (sender.muted) + (0, commands_1.fail)("Muted players may not hide flags."); + if (sender != target && target.hasPerm("blockTrolling")) + (0, commands_1.fail)("Target is insufficentlly trollable."); + if (sender != target && !sender.ranksAtLeast("mod")) + (0, commands_1.fail)("You do not have permission to vanish other players."); + target.showRankPrefix = !target.showRankPrefix; + outputSuccess("".concat(target == sender ? "Your" : "".concat(target.name, "'s"), " rank prefix is now ").concat(target.showRankPrefix ? "visible" : "hidden", ".")); + }, + }, tileid: { + args: [], + description: 'Checks id of a tile.', + perm: commands_1.Perm.none, + handler: function (_a) { + var output = _a.output, handleTaps = _a.handleTaps; + handleTaps("once"); + output("Click a tile to see its id..."); + }, + tapped: function (_a) { + var output = _a.output, f = _a.f, tile = _a.tile; + output(f(templateObject_1 || (templateObject_1 = __makeTemplateObject(["ID is ", ""], ["ID is ", ""])), tile.block().id)); + } + } }, Object.fromEntries(config_1.FishServer.all.map(function (server) { return [ + server.name, + { + args: [], + description: "Switches to the ".concat(server.name, " server."), + perm: server.requiredPerm ? commands_1.Perm.getByName(server.requiredPerm) : commands_1.Perm.none, + isHidden: true, + handler: function (_a) { + var sender = _a.sender; + players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); + Call.connect(sender.con, server.ip, server.port); + }, + }, +]; }))), { switch: { + args: ["server:string", "target:player?"], + description: "Switches to another server.", + perm: commands_1.Perm.play, + handler: function (_a) { + var _b, _c; + var args = _a.args, sender = _a.sender, f = _a.f; + if (args.target != null && args.target != sender && !sender.canModerate(args.target, true, "admin", true)) + (0, commands_1.fail)(f(templateObject_2 || (templateObject_2 = __makeTemplateObject(["You do not have permission to switch player ", "."], ["You do not have permission to switch player ", "."])), args.target)); + var target = (_b = args.target) !== null && _b !== void 0 ? _b : sender; + if (globals_1.ipPortPattern.test(args.server) && sender.hasPerm("admin")) { + //direct connect + Call.connect.apply(Call, __spreadArray([target.con], __read(args.server.split(":")), false)); + } + else { + var unknownServerMessage = "Unknown server ".concat(args.server, ". Valid options: ").concat(config_1.FishServer.all.filter(function (s) { return !s.requiredPerm || sender.hasPerm(s.requiredPerm); }).map(function (s) { return s.name; }).join(", ")); + var server = (_c = config_1.FishServer.byName(args.server)) !== null && _c !== void 0 ? _c : (0, commands_1.fail)(unknownServerMessage); + //Pretend the server doesn't exist + if (server.requiredPerm && !sender.hasPerm(server.requiredPerm)) + (0, commands_1.fail)(unknownServerMessage); + if (target == sender) + players_1.FishPlayer.messageAllWithPerm(server.requiredPerm, "".concat(sender.name, "[magenta] has gone to the ").concat(server.name, " server. Use [cyan]/").concat(server.name, " [magenta]to join them!")); + Call.connect(target.con, server.ip, server.port); + } + } + }, s: { + args: ['message:string'], + description: "Sends a message to staff only.", + perm: commands_1.Perm.chat, + handler: function (_a) { + var sender = _a.sender, args = _a.args, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail, lastUsedSender = _a.lastUsedSender; + if (!sender.hasPerm("mod")) { + if (Date.now() - lastUsedSender < 4000) + (0, commands_1.fail)("This command was used recently and is on cooldown. [orange]Misuse of this command may result in a mute."); + } + api.sendStaffMessage(args.message, sender.name, function (sent) { + if (!sender.hasPerm("mod")) { + if (sent) { + outputSuccess("Message sent to [orange]all online staff."); + } + else { + var wasReceived = players_1.FishPlayer.messageStaff(sender.prefixedName, args.message); + if (wasReceived) + outputSuccess("Message sent to staff."); + else + outputFail("No staff were online to receive your message."); + } + } + }); + }, + }, + /** + * This command is mostly for mobile (or players without foos). + * + * Since the player's unit follows the camera and we are moving the + * camera, we need to keep setting the players real position to the + * spot the command was made. This is pretty buggy but otherwise the + * player will be up the target player's butt + */ + watch: (0, commands_1.command)({ + args: ['player:player?'], + description: "Watch/unwatch a player.", + perm: commands_1.Perm.none, + data: new Set, + handler: function (_a) { + var args = _a.args, data = _a.data, sender = _a.sender, outputSuccess = _a.outputSuccess, outputFail = _a.outputFail; + if (data.has(sender.uuid)) { + outputSuccess("No longer watching a player."); + data.delete(sender.uuid); + } + else if (args.player) { + data.add(sender.uuid); + var senderUnit_1 = sender.unit(); + var stayX_1 = senderUnit_1 === null || senderUnit_1 === void 0 ? void 0 : senderUnit_1.x; + var stayY_1 = senderUnit_1 === null || senderUnit_1 === void 0 ? void 0 : senderUnit_1.y; + var target_1 = args.player.player; + (function watch() { + var _a, _b; + if (data.has(sender.uuid) && target_1.unit()) { + // Self.X+(172.5-Self.X)/10 + Call.setCameraPosition(sender.con, target_1.unit().x, target_1.unit().y); + if (senderUnit_1) + (_b = (_a = sender.unit()) === null || _a === void 0 ? void 0 : _a.set) === null || _b === void 0 ? void 0 : _b.call(_a, stayX_1, stayY_1); + Timer.schedule(function () { return watch(); }, 0.1, 0.1, 0); + } + else { + Call.setCameraPosition(sender.con, stayX_1, stayY_1); + } + })(); + } + else { + outputFail("No player to unwatch."); + } + }, + }), spectate: (0, commands_1.command)(function () { + //TODO revise code + /** Mapping between player and original team */ + var spectators = new Map(); + function spectate(target) { + spectators.set(target, target.team()); + target.forceRespawn(); + target.setTeam(Team.derelict); + target.forceRespawn(); + } + function resume(target) { + if (spectators.get(target) == null) + return; // this state is possible for a person who left not in spectate + target.setTeam(spectators.get(target)); + spectators.delete(target); + target.forceRespawn(); + } + Events.on(EventType.GameOverEvent, function () { return spectators.clear(); }); + Events.on(EventType.PlayerLeave, function (_a) { + var player = _a.player; + return resume(players_1.FishPlayer.get(player)); + }); + return { + args: ["target:player?"], + description: "Toggles spectator mode in PVP games.", + perm: commands_1.Perm.play, + requirements: [commands_1.Req.gameRunning], + handler: function (_a) { + var sender = _a.sender, _b = _a.args.target, target = _b === void 0 ? sender : _b, outputSuccess = _a.outputSuccess, f = _a.f; + if (!config_1.Gamemode.pvp() && !sender.hasPerm("mod")) + (0, commands_1.fail)("You do not have permission to spectate on a non-pvp server."); + if (target !== sender && target.hasPerm("blockTrolling")) + (0, commands_1.fail)("Target player is insufficiently trollable."); + if (target !== sender && !sender.ranksAtLeast("admin")) + (0, commands_1.fail)("You do not have permission to force other players to spectate."); + if (spectators.has(target)) { + resume(target); + outputSuccess(target == sender + ? f(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Rejoining game as team ", "."], ["Rejoining game as team ", "."])), target.team()) : f(templateObject_4 || (templateObject_4 = __makeTemplateObject(["Forced ", " out of spectator mode."], ["Forced ", " out of spectator mode."])), target)); + } + else { + spectate(target); + outputSuccess(target == sender + ? f(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Now spectating. Run /spectate again to resume gameplay."], ["Now spectating. Run /spectate again to resume gameplay."]))) : f(templateObject_6 || (templateObject_6 = __makeTemplateObject(["Forced ", " into spectator mode."], ["Forced ", " into spectator mode."])), target)); + } + } + }; + }), help: { + args: ['name:string?'], + description: 'Displays a list of all commands.', + perm: commands_1.Perm.none, + handler: function (_a) { + var _b; + var args = _a.args, output = _a.output, sender = _a.sender, allCommands = _a.allCommands; + var formatCommand = function (name, color) { + return new funcs_1.StringBuilder() + .add("".concat(color, "/").concat(name)) + .chunk("[white]".concat(allCommands[name].args.map(commands_1.formatArg).join(' '))) + .chunk("[lightgray]- ".concat(allCommands[name].description)).str; + }; + var formatList = function (commandList, color) { return commandList.map(function (c) { return formatCommand(c, color); }).join('\n'); }; + if (args.name && isNaN(parseInt(args.name)) && !['mod', 'admin', 'member'].includes(args.name)) { + //name is not a number or a category, therefore it is probably a command name + if (args.name in allCommands && (!allCommands[args.name].isHidden || allCommands[args.name].perm.check(sender))) { + output("Help for command ".concat(args.name, ":\n\t").concat(allCommands[args.name].description, "\n\tUsage: [sky]/").concat(args.name, " [white]").concat(allCommands[args.name].args.map(commands_1.formatArg).join(' '), "\n\tPermission required: ").concat(allCommands[args.name].perm.name)); + } + else + (0, commands_1.fail)("Command \"".concat(args.name, "\" does not exist.")); + } + else { + var commands_2 = { + player: [], + mod: [], + admin: [], + member: [], + }; + //TODO change this to category, not perm + Object.entries(allCommands).forEach(function (_a) { + var _b = __read(_a, 2), name = _b[0], data = _b[1]; + return (data.perm === commands_1.Perm.admin ? commands_2.admin : data.perm === commands_1.Perm.mod ? commands_2.mod : data.perm === commands_1.Perm.member ? commands_2.member : commands_2.player).push(name); + }); + var chunkedPlayerCommands = (0, funcs_1.to2DArray)(commands_2.player, 15); + switch (args.name) { + case 'admin': + output("".concat(commands_1.Perm.admin.color, "-- Admin commands --\n") + formatList(commands_2.admin, commands_1.Perm.admin.color)); + break; + case 'mod': + output("".concat(commands_1.Perm.mod.color, "-- Mod commands --\n") + formatList(commands_2.mod, commands_1.Perm.mod.color)); + break; + case 'member': + output("".concat(commands_1.Perm.member.color, "-- Member commands --\n") + formatList(commands_2.member, commands_1.Perm.member.color)); + break; + default: { + var pageNumber = args.name != undefined ? parseInt(args.name) : 1; + var page = (_b = chunkedPlayerCommands[pageNumber - 1]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("\"".concat(args.name, "\" is an invalid page number.")); + output("[sky]-- Commands page [lightgrey]".concat(pageNumber, "/").concat(chunkedPlayerCommands.length, "[sky] --\n") + formatList(page, '[sky]')); + } + } + } + }, + }, msg: { + args: ['player:player', 'message:string'], + description: 'Send a message to only one player.', + perm: commands_1.Perm.chat, + handler: function (_a) { + var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; + globals_1.recentWhispers[args.player.uuid] = sender.uuid; + args.player.sendMessage("".concat(sender.prefixedName, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); + output(f(templateObject_7 || (templateObject_7 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), args.player, args.message)); + }, + }, r: { + args: ['message:string'], + description: 'Reply to the most recent message.', + perm: commands_1.Perm.chat, + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, output = _a.output, f = _a.f; + var recipient = players_1.FishPlayer.getById((_b = globals_1.recentWhispers[sender.uuid]) !== null && _b !== void 0 ? _b : (0, commands_1.fail)("It doesn't look like someone has messaged you recently. Try whispering to them with [white]\"/msg \"")); + if (!(recipient === null || recipient === void 0 ? void 0 : recipient.connected())) + (0, commands_1.fail)("The person who last messaged you doesn't seem to exist anymore. Try whispering to someone with [white]\"/msg \""); + globals_1.recentWhispers[globals_1.recentWhispers[sender.uuid]] = sender.uuid; + recipient.sendMessage("".concat(sender.name, "[lightgray] whispered:[#BBBBBB] ").concat(args.message)); + output(f(templateObject_8 || (templateObject_8 = __makeTemplateObject(["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""], ["[lightgray]Whispered to ", "[lightgray]:[#BBBBBB] ", ""])), recipient, args.message)); + }, + }, trail: { + args: ['type:string?', 'color:string?'], + description: 'Use command to see options and toggle trail on/off.', + perm: commands_1.Perm.none, + handler: function (_a) { + var args = _a.args, sender = _a.sender, output = _a.output, outputFail = _a.outputFail, outputSuccess = _a.outputSuccess; + //overload 1: type not specified + if (!args.type) { + if (sender.trail != null) { + sender.trail = null; + outputSuccess("Trail turned off."); + } + else { + output("Available types:[yellow]\n1 - fluxVapor (flowing smoke, long lasting)\n2 - overclocked (diamonds)\n3 - overdriven (squares)\n4 - shieldBreak (smol)\n5 - upgradeCoreBloom (square, long lasting, only orange)\n6 - electrified (tiny spiratic diamonds, but only green)\n7 - unitDust (same as above but round, and can change colors)\n[white]Usage: [orange]/trail [lightgrey] [color/#hex/r,g,b]"); + } + return; + } + //overload 2: type specified + var trailTypes = { + "1": 'fluxVapor', + "2": 'overclocked', + "3": 'overdriven', + "4": 'shieldBreak', + "5": 'upgradeCoreBloom', + "6": 'electrified', + "7": 'unitDust', + }; + var selectedType = trailTypes[args.type]; + if (!selectedType) { + if (Object.values(trailTypes).includes(args.type)) + (0, commands_1.fail)("Please use the numeric id to refer to a trail type."); + else + (0, commands_1.fail)("\"".concat(args.type, "\" is not an available type.")); + } + var color = args.color ? (0, utils_1.getColor)(args.color) : Color.white; + if (color instanceof Color) { + sender.trail = { + type: selectedType, + color: color, + }; + } + else { + outputFail("[scarlet]Sorry, \"".concat(args.color, "\" is not a valid color.\n[yellow]Color can be in the following formats:\n[pink]pink [white]| [gray]#696969 [white]| 255,0,0.")); + } + }, + }, ohno: (0, commands_1.command)({ + args: [], + description: 'Spawns an ohno.', + perm: commands_1.Perm.play, + init: function () { + var Ohnos = { + enabled: true, + ohnos: new Array(), + makeOhno: function (team, x, y) { + var ohno = UnitTypes.atrax.create(team); + ohno.set(x, y); + ohno.type = UnitTypes.alpha; + ohno.apply(StatusEffects.disarmed, Number.MAX_SAFE_INTEGER); + ohno.resetController(); //does this work? + ohno.add(); + this.ohnos.push(ohno); + return ohno; + }, + updateLength: function () { + this.ohnos = this.ohnos.filter(function (o) { return o && o.isAdded() && !o.dead; }); + }, + killAll: function () { + this.ohnos.forEach(function (ohno) { var _a; return (_a = ohno === null || ohno === void 0 ? void 0 : ohno.kill) === null || _a === void 0 ? void 0 : _a.call(ohno); }); + this.ohnos = []; + }, + amount: function () { + return this.ohnos.length; + }, + }; + Events.on(EventType.GameOverEvent, function (e) { + Ohnos.killAll(); + }); + return Ohnos; + }, + requirements: [ + commands_1.Req.gameRunning, commands_1.Req.modeNot("pvp"), + commands_1.Req.unitExists("You cannot spawn ohnos while dead.") + ], + handler: function (_a) { + var sender = _a.sender, Ohnos = _a.data; + if (!Ohnos.enabled) + (0, commands_1.fail)("Ohnos have been temporarily disabled."); + Ohnos.updateLength(); + if (Ohnos.ohnos.length >= (Groups.player.size() + 1) || + sender.team().data().countType(UnitTypes.alpha) >= Units.getCap(sender.team())) + (0, commands_1.fail)("Sorry, the max number of ohno units has been reached."); + if ((0, utils_1.nearbyEnemyTile)((sender.unit()), 6) != null) + (0, commands_1.fail)("Too close to an enemy building!"); + if (!UnitTypes.alpha.supportsEnv(Vars.state.rules.env)) + (0, commands_1.fail)("Ohnos cannot survive in this map."); + Ohnos.makeOhno(sender.team(), sender.player.x, sender.player.y); + }, + }), ranks: { + args: [], + description: 'Displays information about all ranks.', + perm: commands_1.Perm.none, + handler: function (_a) { + var output = _a.output; + output("List of ranks:\n" + + Object.values(ranks_1.Rank.ranks) + .map(function (rank) { return "".concat(rank.prefix, " ").concat(rank.color).concat((0, funcs_1.capitalizeText)(rank.name), "[]: ").concat(rank.color).concat(rank.description, "[]\n"); }) + .join("") + + "List of flags:\n" + + Object.values(ranks_1.RoleFlag.flags) + .map(function (flag) { return "".concat(flag.prefix, " ").concat(flag.color).concat((0, funcs_1.capitalizeText)(flag.name), "[]: ").concat(flag.color).concat(flag.description, "[]\n"); }) + .join("")); + }, + }, rules: { + args: ['player:player?'], + description: 'Displays the server rules.', + perm: commands_1.Perm.none, + handler: function (_a) { + var _b; + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + var target = (_b = args.player) !== null && _b !== void 0 ? _b : sender; + if (target !== sender) { + if (!sender.hasPerm("warn")) + (0, commands_1.fail)("You do not have permission to show rules to other players."); + if (target.hasPerm("blockTrolling")) + (0, commands_1.fail)(f(templateObject_9 || (templateObject_9 = __makeTemplateObject(["Player ", " is insufficiently trollable."], ["Player ", " is insufficiently trollable."])), args.player)); + } + menus_1.Menu.menu("Rules for [#0000ff]>|||> FISH [white]servers", config_1.rules.join("\n\n"), ["[green]I agree to abide by these rules[]", "No"], target).then(function (option) { + if (option == "No") { + target.kick("You must agree to the rules to play on this server. Rejoin to agree to the rules.", 1); + outputSuccess('Player rejected the rules and was kicked.'); + } + else { + outputSuccess('Player acknowledged the rules.'); + } + }); + if (target !== sender) + outputSuccess(f(templateObject_10 || (templateObject_10 = __makeTemplateObject(["Reminded ", " of the rules."], ["Reminded ", " of the rules."])), target)); + }, + }, void: { + args: ["player:player?"], + description: 'Warns other players about power voids.', + perm: commands_1.Perm.play, + requirements: function (_a) { + var args = _a.args; + return [ + commands_1.Req.mode("attack"), + args.player ? commands_1.Req.cooldown(20000) : commands_1.Req.cooldownGlobal(10000) + ]; + }, + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + if (args.player) { + if (!sender.hasPerm("trusted")) + (0, commands_1.fail)("You do not have permission to show popups to other players, please run /void with no arguments to send a chat message to everyone."); + if (args.player !== sender && args.player.hasPerm("blockTrolling")) + (0, commands_1.fail)("Target player is insufficiently trollable."); + menus_1.Menu.menu("\uf83f [scarlet]WARNING[] \uf83f", "[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to.\nIf you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!", ["I understand"], args.player, { onCancel: 'null' }).then(function () { return outputSuccess(f(templateObject_11 || (templateObject_11 = __makeTemplateObject(["Player ", " acknowledged the warning."], ["Player ", " acknowledged the warning."])), args.player)); }); + (0, utils_1.logAction)("showed void warning", sender, args.player); + outputSuccess(f(templateObject_12 || (templateObject_12 = __makeTemplateObject(["Warned ", " about power voids with a popup message."], ["Warned ", " about power voids with a popup message."])), args.player)); + } + else { + Call.sendMessage("[white]Don't break the Power Void (\uF83F), it's a trap!\nPower voids disable anything they are connected to. If you break it, [scarlet]you will get attacked[] by enemy units.\nPlease stop attacking and [lime]build defenses[] first!"); + } + }, + }, team: { + args: ['team:team', 'reason:string?'], + description: 'Changes your team.', + perm: commands_1.Perm.changeTeam, + handler: function (_a) { + var _b; + var sender = _a.sender, _c = _a.args, team = _c.team, reason = _c.reason, outputSuccess = _a.outputSuccess, f = _a.f; + if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) + (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); + if (config_1.Gamemode.sandbox() && team === Vars.state.rules.waveTeam && !sender.hasPerm("admin")) + (0, commands_1.fail)("You do not have permission to change to the wave team on sandbox."); + if (!config_1.Gamemode.sandbox() && !sender.hasPerm("mod") && !reason) + (0, commands_1.fail)("Please specify a reason for changing teams."); + if (!sender.hasPerm("changeTeamExternal")) { + if (team.data().cores.size <= 0) + (0, commands_1.fail)("You do not have permission to change to a team with no cores."); + if (!sender.player.dead() && !((_b = sender.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) + sender.forceRespawn(); + } + if (!sender.hasPerm("mod")) + sender.changedTeam = true; + sender.setTeam(team); + outputSuccess(f(templateObject_13 || (templateObject_13 = __makeTemplateObject(["Changed your team to ", "."], ["Changed your team to ", "."])), team)); + if (reason && !config_1.Gamemode.sandbox()) + (0, utils_1.logAction)("changed team to ".concat(team.name, " on ").concat((0, funcs_1.escapeTextDiscord)(Vars.state.map.plainName()), " with reason ").concat((0, funcs_1.escapeTextDiscord)(reason)), sender); + }, + }, teamp: { + args: ['team:team', 'target:player'], + description: 'Changes the team of a player.', + perm: commands_1.Perm.changeTeam, + handler: function (_a) { + var _b; + var sender = _a.sender, _c = _a.args, team = _c.team, target = _c.target, outputSuccess = _a.outputSuccess, f = _a.f; + if (!sender.canModerate(target, true, "mod", true)) + (0, commands_1.fail)(f(templateObject_14 || (templateObject_14 = __makeTemplateObject(["You do not have permission to change the team of ", ""], ["You do not have permission to change the team of ", ""])), target)); + if (config_1.Gamemode.sandbox() && globals_1.fishState.peacefulMode && !sender.hasPerm("admin")) + (0, commands_1.fail)("You do not have permission to change teams because peaceful mode is on."); + if (!sender.hasPerm("changeTeamExternal")) { + if (team.data().cores.size <= 0) + (0, commands_1.fail)("You do not have permission to change to a team with no cores."); + if (!target.player.dead() && !((_b = target.unit()) === null || _b === void 0 ? void 0 : _b.spawnedByCore)) + target.forceRespawn(); + } + target.setTeam(team); + outputSuccess(f(templateObject_15 || (templateObject_15 = __makeTemplateObject(["Changed team of player ", " to ", "."], ["Changed team of player ", " to ", "."])), target, team)); + }, + }, rank: { + args: ['player:player'], + description: 'Displays the rank of a player.', + perm: commands_1.Perm.none, + handler: function (_a) { + var args = _a.args, output = _a.output, f = _a.f; + output(f(templateObject_16 || (templateObject_16 = __makeTemplateObject(["Player ", "'s rank is ", "."], ["Player ", "'s rank is ", "."])), args.player, args.player.rank)); + }, + }, forcevnw: { + args: ["force:boolean?"], + description: 'Force skip to the next wave.', + perm: commands_1.Perm.admin, + handler: function (_a) { + var allCommands = _a.allCommands, sender = _a.sender, _b = _a.args.force, force = _b === void 0 ? true : _b; + if (allCommands.vnw.data.manager.session == null) { + if (force == false) + (0, commands_1.fail)("Cannot clear votes for VNW because no vote is currently ongoing."); + (0, utils_1.skipWaves)(1, true); + } + else { + if (force) + Call.sendMessage("VNW: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green], skipping wave.")); + else + Call.sendMessage("VNW: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); + allCommands.vnw.data.manager.forceVote(force); + } + }, + }, vnw: (0, commands_1.command)({ + args: [], + description: "Vote to start the next wave.", + perm: commands_1.Perm.play, + init: function () { return ({ + manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5)) + .on("success", function (t) { return (0, utils_1.skipWaves)(t.session.data, true); }) + .on("vote passed", function () { return Call.sendMessage('VNW: [green]Vote passed, skipping to next wave.'); }) + .on("vote failed", function () { return Call.sendMessage('VNW: [red]Vote failed.'); }) + .on("player vote change", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has voted on skipping [accent]").concat(t.session.data, "[white] wave(s). [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + .on("player vote removed", function (t, player) { return Call.sendMessage("VNW: ".concat(player.name, " [white] has left. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + }); }, + requirements: [commands_1.Req.cooldown(3000), commands_1.Req.mode("survival"), commands_1.Req.gameRunning], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var option; + var sender = _b.sender, manager = _b.data.manager; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + if (!!manager.session) return [3 /*break*/, 2]; + return [4 /*yield*/, menus_1.Menu.menu("Start a Next Wave Vote", "Select the amount of waves you would like to skip.", [1, 5, 10], sender, { + includeCancel: true, + optionStringifier: function (n) { return "".concat(n, " waves"); } + })]; + case 1: + option = _c.sent(); + if (manager.session) { + //Someone else started a vote + if (manager.session.data != option) + (0, commands_1.fail)("Someone else started a vote with a different number of waves to skip."); + else + manager.vote(sender, sender.voteWeight(), option); + } + else { + manager.start(sender, sender.voteWeight(), option); + } + return [3 /*break*/, 3]; + case 2: + manager.vote(sender, sender.voteWeight(), null); + _c.label = 3; + case 3: return [2 /*return*/]; + } + }); + }); + } + }), forcertv: { + args: ["force:boolean?"], + description: 'Force skip to the next map.', + perm: commands_1.Perm.admin, + handler: function (_a) { + var _b = _a.args.force, force = _b === void 0 ? true : _b, sender = _a.sender, allCommands = _a.allCommands; + if (allCommands.rtv.data.manager.session == null) { + if (force == false) + (0, commands_1.fail)("Cannot clear votes for RTV because no vote is currently ongoing."); + allCommands.rtv.data.manager.forceVote(true); + } + else { + if (force) + Call.sendMessage("RTV: [green]Vote was forced by admin [yellow]".concat(sender.name, "[green].")); + else + Call.sendMessage("RTV: [red]Votes cleared by admin [yellow]".concat(sender.name, "[red].")); + allCommands.rtv.data.manager.forceVote(force); + } + } + }, rtv: (0, commands_1.command)({ + args: [], + description: 'Rock the vote to change map.', + perm: commands_1.Perm.play, + init: function () { return ({ + manager: new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), config_1.Gamemode.hexed() ? ["fractionOfVoters", 1] : undefined) //Require unanimity in Hexed, as it is often 1 v everyone + .on("success", function () { return (0, utils_1.neutralGameover)(); }) + .on("vote passed", function () { return Call.sendMessage("RTV: [green]Vote has passed, changing map."); }) + .on("vote failed", function () { return Call.sendMessage("RTV: [red]Vote failed."); }) + .on("player vote change", function (t, player, oldVote, newVote) { return Call.sendMessage("RTV: ".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to change the map. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + .on("player vote removed", function (t, player) { return Call.sendMessage("RTV: ".concat(player.name, "[white] has left the game. [green]").concat(t.currentVotes(), "[white] votes, [green]").concat(t.requiredVotes(), "[white] required.")); }) + }); }, + requirements: [commands_1.Req.cooldown(3000), commands_1.Req.gameRunning], + handler: function (_a) { + var sender = _a.sender, manager = _a.data.manager; + manager.vote(sender, 1, 0); //No weighting for RTV except for removing AFK players + } + }), + // votekick: { + // args: ["target:player"], + // description: "Starts a vote to kick a player.", + // perm: Perm.play, + // handler({args, sender}){ + // if(votekickmanager.currentSession) fail(`There is already a votekick in progress.`); + // votekickmanager.start({ + // initiator: sender, + // target: args.player + // }); + // } + // }, + // vote: { + // args: ["vote:boolean"], + // description: "Use /votekick instead.", + // perm: Perm.play, + // handler({sender, args}){ + // votekickmanager.handleVote(sender, args ? 1 : -1); + // } + // }, + forcenextmap: { + args: ["map:map"], + description: 'Override the next map in queue.', + perm: commands_1.Perm.admin.exceptModes({ + testsrv: commands_1.Perm.play + }), + handler: function (_a) { + var allCommands = _a.allCommands, args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, f = _a.f; + Vars.maps.setNextMapOverride(args.map); + if (allCommands.nextmap.data.voteEndTime() > -1) { + //Cancel /nextmap vote if it's ongoing + allCommands.nextmap.data.resetVotes(); + Call.sendMessage("[red]Admin ".concat(sender.name, "[red] has cancelled the vote. The next map will be [yellow]").concat(args.map.name(), ".")); + } + else { + outputSuccess(f(templateObject_17 || (templateObject_17 = __makeTemplateObject(["Forced the next map to be \"", "\" by ", ""], ["Forced the next map to be \"", "\" by ", ""])), args.map.name(), args.map.author())); + } + }, + }, maps: { + args: [], + description: 'Lists the available maps.', + perm: commands_1.Perm.none, + handler: function (_a) { + var output = _a.output; + output("[yellow]Use [white]/nextmap [lightgray] [yellow]to vote on a map.\n\n[blue]Available maps:\n_________________________\n".concat(Vars.maps.customMaps().toArray().map(function (map) { + return "[yellow]".concat(map.name()); + }).join("\n"))); + } + }, nextmap: (0, commands_1.command)(function () { + var votes = new Map(); + var lastVoteCount = 0; + var lastVoteTime = 0; + var voteEndTime = -1; + var voteDuration = funcs_1.Duration.minutes(1.5); + var task = null; + function resetVotes() { + votes.clear(); + voteEndTime = -1; + task === null || task === void 0 ? void 0 : task.cancel(); + } + function getMapData() { + return __spreadArray([], __read(votes.values()), false).reduce(function (acc, map) { return (acc.increment(map), acc); }, new ObjectIntMap()).entries().toArray(); + } + function showVotes() { + Call.sendMessage("[green]Current votes:\n------------------------------\n".concat(getMapData().map(function (_a) { + var map = _a.key, votes = _a.value; + return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); + }).toString("\n"))); + } + function startVote() { + voteEndTime = Date.now() + voteDuration; + task = Timer.schedule(endVote, voteDuration / 1000); + } + function endVote() { + if (voteEndTime == -1) + return; //aborted somehow + if (votes.size == 0) + return; //no votes? + if (votes.size + 2 <= lastVoteCount && (Date.now() - lastVoteTime) < funcs_1.Duration.minutes(10)) { + //If the number of votes is 2 less than the previous number of votes for a vote in the past 10 minutes, abor + Call.sendMessage("[cyan]Next Map Vote: [scarlet]Vote aborted because a previous vote had significantly higher turnout"); + resetVotes(); + return; + } + else { + lastVoteTime = Date.now(); + lastVoteCount = votes.size; + } + var mapData = getMapData(); + var highestVoteCount = mapData.max(floatf(function (e) { return e.value; })).value; + var highestVotedMaps = mapData.select(function (e) { return e.value == highestVoteCount; }); + var winner; + if (highestVotedMaps.size > 1) { + winner = highestVotedMaps.random().key; + Call.sendMessage("[green]There was a tie between the following maps:\n".concat(highestVotedMaps.map(function (_a) { + var map = _a.key, votes = _a.value; + return "[cyan]".concat(map.name(), "[yellow]: ").concat(votes); + }).toString("\n"), "\n[green]Picking random winner: [yellow]").concat(winner.name())); + } + else { + winner = highestVotedMaps.get(0).key; + Call.sendMessage("[green]Map voting complete! The next map will be [yellow]".concat(winner.name(), " [green]with [yellow]").concat(highestVoteCount, "[green] votes.")); + } + Vars.maps.setNextMapOverride(winner); + resetVotes(); + } + Events.on(EventType.GameOverEvent, resetVotes); + Events.on(EventType.ServerLoadEvent, resetVotes); + return { + args: ['map:map'], + description: 'Allows you to vote for the next map. Use /maps to see all available maps.', + perm: commands_1.Perm.play, + data: { votes: votes, voteEndTime: function () { return voteEndTime; }, resetVotes: resetVotes, endVote: endVote }, + requirements: [commands_1.Req.cooldown(10000)], + handler: function (_a) { + var map = _a.args.map, sender = _a.sender; + if (config_1.Gamemode.testsrv()) + (0, commands_1.fail)("Please use /forcenextmap instead."); + if (votes.get(sender)) + (0, commands_1.fail)("You have already voted."); + votes.set(sender, map); + if (voteEndTime == -1) { + if ((Date.now() - lastVoteTime) < funcs_1.Duration.minutes(1)) + (0, commands_1.fail)("Please wait 1 minute before starting a new map vote."); + startVote(); + Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] started a map vote, and voted for [yellow]").concat(map.name(), "[cyan]. Use [white]/nextmap ").concat(map.plainName(), "[] to add your vote, or run [white]/maps[] to see other available maps.")); + } + else { + Call.sendMessage("[cyan]Next Map Vote: ".concat(sender.name, "[cyan] voted for [yellow]").concat(map.name(), "[cyan]. Time left: [scarlet]").concat((0, utils_1.formatTimeRelative)(voteEndTime, true))); + showVotes(); + } + } + }; + }), surrender: (0, commands_1.command)(function () { + var prefix = "[orange]Surrender[white]: "; + var managers = Team.all.map(function (team) { + return new votes_1.VoteManager(funcs_1.Duration.minutes(1.5), ["fractionOfVoters", config_1.Gamemode.hexed() ? 1 : 3 / 4], function (p) { return p.team() == team; }) + .on("success", function () { return team.cores().copy().each(function (c) { return c.kill(); }); }) + .on("vote passed", function () { return Call.sendMessage(prefix + "Team ".concat(team.coloredName(), " has voted to forfeit this match.")); }) + .on("vote failed", function (t) { return t.messageEligibleVoters(prefix + "Team ".concat(team.coloredName(), " has chosen not to forfeit this match.")); }) + .on("player vote change", function (t, player, oldVote, newVote) { return t.messageEligibleVoters(prefix + "".concat(player.name, "[white] ").concat(oldVote == newVote ? "still " : "", "wants to forfeit this match. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }) + .on("player vote removed", function (t, player) { return t.messageEligibleVoters(prefix + "Player ".concat(player.name, "[white] has left the game. [orange]").concat(t.currentVotes(), "[white] votes, [orange]").concat(t.requiredVotes(), "[white] required.")); }); + }); + globals_1.FishEvents.on("playerTeamChange", function (_, fishP, previous) { + managers[previous.id].unvote(fishP); + }); + return { + args: ["force:boolean?"], + description: "Vote to surrender to the enemy team.", + perm: commands_1.Perm.play, + requirements: [commands_1.Req.mode("pvp"), commands_1.Req.teamAlive], + data: { managers: managers }, + handler: function (_a) { + var sender = _a.sender, force = _a.args.force; + var manager = managers[sender.team().id]; + if (sender.hasPerm("admin") && force != undefined) + manager.forceVote(force); + if (sender.ranksAtLeast("mod")) + commands_1.Req.cooldown(5000); + else + commands_1.Req.cooldown(20000); + manager.vote(sender, 1, 0); + }, + }; + }), stats: { + args: ["target:player"], + perm: commands_1.Perm.none, + description: "Views a player's stats.", + handler: function (_a) { + var target = _a.args.target, output = _a.output, f = _a.f; + output(f(templateObject_18 || (templateObject_18 = __makeTemplateObject(["[accent]Statistics for player ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""], ["[accent]\\\nStatistics for player ", ":\n(note: we started recording statistics on 22 Jan 2024)\n[white]--------------[]\nBlocks broken: ", "\nBlocks placed: ", "\nChat messages sent: ", "\nGames finished: ", "\nTime in-game: ", "\nWin rate: ", ""])), target, target.stats.blocksBroken, target.stats.blocksPlaced, target.stats.chatMessagesSent, target.stats.gamesFinished, (0, utils_1.formatTime)(target.stats.timeInGame), target.stats.gamesWon / target.stats.gamesFinished)); + } + }, showworld: { + args: ["x:number?", "y:number?", "size:number?"], + perm: commands_1.Perm.none, + description: "Views the world as a 2D scrollable menu.", + requirements: [commands_1.Req.cooldown(4000)], + handler: function (_a) { + var sender = _a.sender, _b = _a.args, _c = _b.size, size = _c === void 0 ? 7 : _c, x = _b.x, y = _b.y; + if (size > 20) + (0, commands_1.fail)("Size ".concat(size, " is too high!")); + if (Vars.state.rules.fog) + (0, commands_1.fail)("This command is disabled when fog is enabled."); + var options = (0, funcs_1.to2DArray)(Reflect.get(Vars.world.tiles, "array").map(function (tile) { return ({ + text: tile.block().emoji(), + data: null, + }); }), Vars.world.width()).reverse(); + var height = Vars.world.height(); + menus_1.Menu.scroll(sender, "The World", "Use the arrow keys to navigate around the world. Click a blank square to exit.", options, { + columns: size, + rows: size, + x: x ? x - Math.trunc(size / 2) : 0, + y: height - (y ? y + 1 + Math.trunc(size / 2) : size), + getCenterText: function (x, y) { return "".concat(x, ",").concat(height - y - size); } + }); + } + }, mapinfo: { + args: ["map:map?"], + perm: commands_1.Perm.none, + description: "Displays information about a map.", + handler: function (_a) { + var output = _a.output, map = _a.args.map, f = _a.f, sender = _a.sender; + if (map) { + output(maps_1.FMap.getCreate(map).displayStats(f)); + } + else { + menus_1.Menu.textPages(sender, Vars.maps.customMaps().map(function (m) { + return ["Map information", function () { return maps_1.FMap.getCreate(m).displayStats(f); }]; + }).toArray(), { + startPage: Vars.maps.customMaps().toArray().indexOf(Vars.state.map), + }); + } + } + }, gamemode: { + args: ["mode:string"], + perm: new commands_1.Perm("changeGamemode", "manager").exceptModes({ + testsrv: commands_1.Perm.play, + }), + description: "Sets the gamemode.", + requirements: [commands_1.Req.cooldownGlobal(10000)], + handler: function (_a) { + var args = _a.args, sender = _a.sender, outputSuccess = _a.outputSuccess, lastUsedSuccessfully = _a.lastUsedSuccessfully; + if (!sender.hasPerm('trusted')) + commands_1.Req.cooldownGlobal(30000)({ lastUsedSuccessfully: lastUsedSuccessfully }); + //Unpause + Vars.state.set(GameState.State.playing); + switch (args.mode) { + case "attack": + Vars.state.rules.attackMode = true; + Vars.state.rules.pvp = false; + Vars.state.rules.infiniteResources = false; + break; + case "survival": + Vars.state.rules.attackMode = false; + Vars.state.rules.waves = true; + Vars.state.rules.pvp = false; + Vars.state.rules.infiniteResources = false; + break; + case "pvp": + Vars.state.rules.attackMode = true; + Vars.state.rules.pvp = true; + Vars.state.rules.waves = false; + Vars.state.rules.infiniteResources = false; + break; + case "sandbox": + Vars.state.rules.attackMode = true; + Vars.state.rules.pvp = false; + Vars.state.rules.waves = false; + Vars.state.rules.infiniteResources = true; + break; + default: (0, commands_1.fail)("Invalid mode, valid modes are: attack, survival, pvp"); + } + var reloader = new WorldReloader(); + Reflect.set(reloader, "wasServer", true); + Reflect.set(reloader, "players", Groups.player.copy()); + Call.worldDataBegin(); + reloader.end(); + Call.sendMessage("[orange]Player ".concat(sender.cleanedName, " changed the gamemode to ").concat(args.mode)); + outputSuccess("Changed mode to ".concat(args.mode)); + } + }, mixunit: { + args: ["type:unittype", "base:unittype"], + description: "Spawns a unit that is made of two unit types mixed together.", + perm: commands_1.Perm.admin.exceptModes({ + sandbox: commands_1.Perm.play + }), + requirements: function (_a) { + var sender = _a.sender; + return [!sender.hasPerm("admin") && commands_1.Req.cooldown(1500), commands_1.Req.unitExists()].filter(Boolean); + }, + handler: function (_a) { + var args = _a.args, sender = _a.sender, f = _a.f, outputSuccess = _a.outputSuccess; + var _b = sender.unit(), team = _b.team, x = _b.x, y = _b.y; + var unit = args.base.create(team); + unit.type = args.type; + unit.maxHealth = args.type.health; //because half-dead units aren't fun + unit.set(x, y); + unit.add(); + outputSuccess(f(templateObject_19 || (templateObject_19 = __makeTemplateObject(["Spawned a ", " that is partly a ", "."], ["Spawned a ", " that is partly a ", "."])), args.type, args.base)); + } + }, report: { + args: [], + description: 'Report a player to staff with a selected reason.', + perm: commands_1.Perm.play, + requirements: [commands_1.Req.cooldown(4000)], + handler: function (_a) { + return __awaiter(this, arguments, void 0, function (_b) { + var onlinePlayers, target, baseReasons, reasons, reason, issuerName, targetName, serverName, message; + var _c, _d, _e; + var sender = _b.sender, outputSuccess = _b.outputSuccess, outputFail = _b.outputFail, f = _b.f; + return __generator(this, function (_f) { + switch (_f.label) { + case 0: + onlinePlayers = (0, funcs_1.setToArray)(Groups.player); + if (onlinePlayers.length === 0) { + outputFail('No players online to report.'); + return [2 /*return*/]; + } + return [4 /*yield*/, menus_1.Menu.menu('Report Player', 'Select a player to report.', onlinePlayers, sender, { + includeCancel: true, + optionStringifier: function (player) { return player.name; } + }).catch(function () { + outputFail('Report cancelled.'); + return; + })]; + case 1: + target = _f.sent(); + if (!target) + return [2 /*return*/]; + baseReasons = [ + 'Griefing', + 'Harassment', + 'Cheating / Exploiting', + 'Spam', + 'Trolling', + 'Other', + ]; + reasons = target.admin ? __spreadArray(__spreadArray([], __read(baseReasons), false), ['Admin Abuse'], false) : baseReasons; + return [4 /*yield*/, menus_1.Menu.menu('Report Reason', "Select a reason for reporting [accent]".concat(target.name, "[]"), reasons, sender, { includeCancel: true }).catch(function () { + outputFail('Report cancelled.'); + return; + })]; + case 2: + reason = _f.sent(); + if (!reason) + return [2 /*return*/]; + issuerName = (_e = (_d = (_c = sender.player) === null || _c === void 0 ? void 0 : _c.name) !== null && _d !== void 0 ? _d : sender.name) !== null && _e !== void 0 ? _e : 'Unknown'; + targetName = target.name; + serverName = config_1.Gamemode.name(); + message = "[Report] Server: ".concat(serverName, "\n") + + "Issuer: ".concat(Strings.stripColors(issuerName), "\n") + + "Target: ".concat(Strings.stripColors(targetName)).concat(target.admin ? ' (Admin)' : '', "\n") + + "Reason: ".concat(reason); + api.sendStaffMessage(message, issuerName, function (sent) { + if (sent) + outputSuccess(f(templateObject_20 || (templateObject_20 = __makeTemplateObject(["Report sent to staff: ", " for \"", "\"."], ["Report sent to staff: ", " for \"", "\"."])), targetName, reason)); + else + outputFail('Failed to send report to staff. Please try again later.'); + }); + return [2 /*return*/]; + } + }); + }); + }, + } })); +var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18, templateObject_19, templateObject_20; diff --git a/build/scripts/config.js b/build/scripts/config.js index ff9e9d5a..77623ce3 100644 --- a/build/scripts/config.js +++ b/build/scripts/config.js @@ -1,350 +1,351 @@ -"use strict"; -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains configurable constants. -*/ -var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { - if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } - return cooked; -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.rules = exports.tips = exports.FColor = exports.text = exports.prefixes = exports.Gamemode = exports.FishServer = exports.mapRepoURLs = exports.Mode = exports.backendIP = exports.stopAntiEvadeTime = exports.heuristics = exports.adminNames = exports.multiCharSubstitutions = exports.substitutions = exports.bannedWords = void 0; -var globals_1 = require("/globals"); -var ranks_1 = require("/ranks"); -var funcs_1 = require("/funcs"); -function processBannedWordList(words) { - return words.map(function (word) { - return (typeof word == "string" || word instanceof RegExp) ? - [word, []] - : [word[0], word.slice(1)]; - }); -} -exports.bannedWords = { - // README: Information on how to update this list - // All words must be in *lowercase*. - // Words need to be separated by commas, even if they are on a new line. - // If a word can be contained in another word that should be allowed (the scunthorpe problem), - // surround the entire thing in square brackets, then list out the words after - // like this: ["badw", "goodbadw"] - /** Normal: banned always. */ - normal: processBannedWordList([ - "fanum tax", "gyatt", "rizz", "skibidi", //With love, DarthScion - //>:( -dart - "uwu", //lol - "nig" + "ger", "nig" + "ga", "niger", "ni8" + "8er", "nig" + "gre", //our apologies to citizens of the Republic of Niger - "негр", "ниггер", - "re" + "tard", - 'kill yourself', 'kill urself', /\bkys\b/, - "kill blacks", "heil hitler", "heil nazis", "heil the nazis", "sieg heil", "hail hitler", "hail nazis", "hail the nazis", "sieg hail", //nazi-related words - ["co" + "ck", "cockroach", "poppycock", "cocktail"], - "iamasussyimposter", - ["cu" + "nt", "scunthorpe"], - ["penis", "peniston"], - "hawk tuah", - ["rape", "grape", "therap", "drape", "scrape", "trapez", "earrape", "atrape"], - /\bf(a)g\b/, "fa" + "gg" + "ot", - /\bc(u)m\b/, ["semen", "sement", "horsemen", "housemen", "defensemen", "those", "menders"], - ["porn", "maporn"], - "futa" + "nari", "futa", - "ur gay", "your gay", "youre gay", "you're gay", - "gooning", "gooner", "dildo", "loli", /\banal\b/, "cunny" - ]), - /** Strict: banned in names and for players with a chat strictness level of 'strict'. */ - strict: processBannedWordList([ - "fu" + "ck", "bi" + "tch", ["sh" + "it", "harshit"], /\ba(s)s\b/, "as" + "shole", ["dick", "medick", "dickens"], - ]), - /** Names: banned only in names. */ - names: processBannedWordList([ - "sex", /\bgoldberg\b/, "hitler", "stalin", "putin", "lenin", /^something$/, "[something]", "[[something]", "卐", - globals_1.uuidPattern, globals_1.ipPattern, globals_1.ipPortPattern - ]), - /** autoWhack: new players saying one of these words will be automatically stopped and muted. */ - autoWhack: [ - "nig" + "ger", "nig" + "ga", "ni8" + "8er", "hit" + "ler", "fa" + "gg" + "ot", "nazis", - ], -}; -//for some reason the external mindustry server does not read the files correctly, so we can only use ASCII -exports.substitutions = Object.fromEntries(Object.entries({ - "a": "\u0430\u1E9A\u1EA1\u1E01\u00E4\u03B1@\u0101\u0103\u0105\u03AC", - "b": "\u1E03\u1E07\u1E03\u0253\u0185", - "c": "\u0441\u217D\u00E7\u03C2\u010B", - "d": "\u217E\u1E0B\u1E11\u010F\u1E13\u1E0D\u1E0F\u0257\u20AB\u0256\u056A", - "e": "\u0435\u1E1B\u0113\u1E17\u0229\u0451\u011B\u0205\u03F5\u03B5\u025B3", - "f": "\u1E1F\u0493\u0192", - "g": "\u0581\u0123\u01F5\u0260\u011F\u011D\u01E5\u1E21", - "h": "\u1E23\u021F\u1E25\u1E2B\u0570\u056B\u1E29\u0266\u1E27\u1E23\u0266\u1E96\u0127", - "i": "\u0456\u012F\u03B9\u1EC9\u1F31\u1F77\u012B1\u00A1\u0457\u0390\u03CA", - "j": "\u0458\u029D\u0575\u025F\u0135\u0237\u01F0", - "k": "\u049F\u1E31\u0137\u0138\u043A\u0199\u049D", - "l": "\u217C\u1E3D\u1E3B\u013E\u0140\u013C\u1E39\u0142\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u03B9", - "m": "\u217F\u1E43\u0271\u1E41\u1E3F", - "n": "\u00F1\u0144\u0146\u0148\u0149\u01F9\u03AE\u03B7\u0578\u057C\u0580\u1E45\u1E47\u03A0", - "o": "\u00F2\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1F40\u1F41\u1F42\u1F43\u1F44\u1F45\u1F78\u1F79\u03C3\u0E50\u00F6\u014D\u014F\u0151\u01A1\u01D2\u03BF\u03CC0", - "p": "\u03C1\u0440\u048F\u1E55\u1E57\u1FE4\u1FE5\u2374", - "q": "\u051B\u0563\u0566\u0563\u0566", - "r": "\u0155\u0157\u0159\u0211\u0213\u027C\u027D\u0433\u0453\u0491\u04F7\u1E59\u1E5B\u1E5D", - "s": "\u015B\u015D\u015F\u0161\u0219\u0282\u0455\u1E61\u1E63\u1E65\u1E67\u1E69\u03C2", - "t": "\u0163\u0165\u01AB\u021B\u0288\u1E6B\u1E6D\u1E6F\u1E71\u1E97\u0236\u2020\u04AD", - "u": "\u00B5\u03BC\u00F9\u00FA\u00FB\u00FC\u0169\u016B\u016D\u016F\u0171\u0173\u01B0\u01D4\u0215\u0217\u0265\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u03BC\u03C5\u03CB\u03CD", - "v": "\u03BD\u0475\u0477\u1E7D\u1E7F\u2174\u2228\u03C5\u03CB\u03CD", - "w": "\u0175\u051D\u1E81\u1E83\u1E85\u1E87\u1E89\u1E98\u03C9\u03CE", - "x": "\u0445\u04B3\u1E8B\u1E8D\u03C7", - "y": "\u00FD\u00FF\u0177\u01B4\u0233\u03B3\u0443\u045E\u04EF\u04F1\u04F3\u1E8F\u1E99\u1EF3\u1EF5\u1EF7\u1EF9\u04AF\u04B1", - "z": "\u017A\u017C\u017E\u01B6\u0225\u0290\u1E91\u1E93\u1E95", - "A": "\u1E00\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAC\u1F08\u1F09\u1F88\u1F89\u1FB8\u1FB9\u1FBA\u1FBC\u212B\u0100\u0102\u0104\u0386\u0391", - "B": "\u0181\u0392\u0412\u1E02\u1E04\u1E06", - "C": "\u00C7\u0106\u0108\u010A\u010C\u0187\u0421\u04AA\u1E08\u216D\u03F9", - "D": "\u00D0\u010E\u0110\u0189\u018A\u1E0A\u1E0C\u1E0E\u216E", - "E": "\u00C8\u00C9\u00CA\u00CB\u0112\u0114\u0116\u0118\u011A\u0204\u0206\u0228\u0395\u0400\u0415\u04D6\u1E18\u0510\u2107\u0190\u1F19\u1FC8\u0404\u0388\u03AD\u03B5\u03B7", - "F": "\u03DC\u1E1E\u0492\u0191\u0492\u0493", - "G": "\u011C\u011E\u0120\u0122\u0193\u01E6\u01F4\u1E20", - "H": "\u0124\u021E\u0397\u041D\u04A2\u04A4\u04C7\u04C9\u1E22\u1E24\u1E26\u1E28\u1E2A\u1FCC\uA726\u0389", - "I": "\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u217C\u1E3D\u1E3B\u026B\u013E\u0140\u013C\u1E39\u038A", - "J": "\u0134\u0408\u037F", - "K": "\u0136\u0198\u01E8\u039A\u040C\u041A\u051E\u1E30\u1E32\u1E34\u20AD\u212A\u03BA", - "L": "\u0139\u013B\u013D\u013F\u0141\u053C\u1E36\u1E38\u1E3A\u1E3C\u216C", - "M": "\u039C\u041C\u04CD\u1E3E\u1E40\u1E42\u216F", - "N": "\u00D1\u0143\u0145\u0147\u01F8\u039D\u1E44\u1E46\u1E48\u1E4A\u019D", - "O": "\u03B8\u236C\u00D2\u00D3\u00D4\u00D5\u00D6\u014C\u014E\u0150\u019F\u01A0\u01D1\u020E\u022E\u0230\u0398\u039F\u041E\u04E6\u0555\u1ECC\u1ECE\u1ED4\u1FF9\u038C", - "P": "\u01A4\u03A1\u0420\u048E\u1E54\u1E56\u1FEC", - "Q": "\u051A", - "R": "\u0154\u0156\u0158\u0210\u0212\u1E58\u1E5A\u1E5C\u1E5E\u211E\u024C\u2C64", - "S": "\u015A\u015C\u015E\u0160\u0218\u0405\u054F\u1E60\u1E62\u1E68\u1E64\u1E66", - "T": "\u0162\u0164\u0166\u01AE\u021A\u03A4\u0422\u04AC\u1E6A\u1E6C\u1E6E\u1E70\u038A\u1FDB\uA68C\u0372\u0373\u03C4", - "U": "\u016A\u016C\u016E\u0170\u0172\u01AF\u01D3\u1EE8\u1EEA\u1EEC\u1EEE\u0544", - "V": "\u0474\u0476\u1E7C\u1E7E\u22C1\u2164", - "W": "\u051C\u1E80\u1E82\u1E84\u1E86\u1E88\u019C", - "X": "\u03A7\u0425\u04B2\u1E8A\u1E8C\u2169", - "Y": "\u01B3\u0232\u03A5\u03AB\u03D3\u0423\u04AE\u04B0\u1E8E\u1EF2\u1EF4\u038E", - "Z": "\u0179\u017B\u017D\u0224\u0396\u1E90\u1E92\u1E94", - "": "\u200B\u200C\u200D", -}).map(function (_a) { - var _b = __read(_a, 2), char = _b[0], alts = _b[1]; - return alts.split("").map(function (alt) { return [alt, char]; }); -}).flat(1)); -exports.multiCharSubstitutions = [ - [/\|-\|/g, "H"] -]; -//#endregion -//#region misc -/** Used for anti-impersonation. Make sure to replace numbers with letters, for example, balam314 -> balamei4. */ -exports.adminNames = ["fish", "balamei4", "clashgone", "darthscion", "firefridge", "aricia", "rawsewage", "skeledragon", "edh8e", "everydayhuman8e", "benjamonsrl", "cudspent"]; -exports.heuristics = { - /** Will trip if more than this many blocks are broken within 25 seconds of joining. */ - blocksBrokenAfterJoin: 40, -}; -exports.stopAntiEvadeTime = funcs_1.Duration.minutes(30); -exports.backendIP = '45.79.202.111:5082'; -exports.Mode = { - localDebug: new Fi("config/.debug").exists(), - noBackend: new Fi("config/.debug").exists() && !exports.backendIP.startsWith("127.0.0.1:"), - isChristmas: new Date().getMonth() == 11, - isAprilFools: new Date().getMonth() == 3 && new Date().getDate() == 1, -}; -//#endregion -//#region servers -/** Stores the repository url for the maps for each gamemode. */ -exports.mapRepoURLs = { - attack: "https://api.github.com/repos/Fish-Community/fish-maps/contents/attack", - survival: "https://api.github.com/repos/Fish-Community/fish-maps/contents/survival", - pvp: "https://api.github.com/repos/Fish-Community/fish-maps/contents/pvp", - hexed: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hexed", - sandbox: "https://api.github.com/repos/Fish-Community/fish-maps/contents/sandbox", - hardcore: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hardcore", - testsrv: "https://api.github.com/repos/Fish-Community/fish-maps/contents/testsrv", - minigame: "https://api.github.com/repos/Fish-Community/fish-maps/contents/minigame", -}; -/** Stores the names and addresses of each active server. */ -var FishServer = /** @class */ (function () { - function FishServer(name, ip, port, aliases, - /** If set, this permission is required to switch to or get information about this server. */ - requiredPerm) { - this.name = name; - this.ip = ip; - this.port = port; - this.aliases = aliases; - this.requiredPerm = requiredPerm; - FishServer.all.push(this); - } - FishServer.byName = function (input) { - var _a; - input = input.toLowerCase(); - return (_a = FishServer.all.find(function (s) { return s.aliases.concat(s.name).includes(input); })) !== null && _a !== void 0 ? _a : null; - }; - FishServer.all = []; - FishServer.attack = new FishServer("attack", "162.248.100.98", "6567", ["attac", "atack", "atak", "atck", "atk", "a"]); - FishServer.survival = new FishServer("survival", "162.248.101.95", "6567", ["surviv", "surv", "sur", "su", "s", "sl"]); - FishServer.pvp = new FishServer("pvp", "162.248.102.101", "6567", ["pv", "p", "v", "playerversusplayer"]); - FishServer.sandbox = new FishServer("sandbox", "162.248.101.53", "6567", ["sand", "box", "sa", "sb"]); - FishServer.hexed = new FishServer("hexed", "162.248.100.133", "6567", ["h", "hx", "hxd", "hpvp", "hxpvp", "hexpvp"]); - FishServer.minigame = new FishServer("minigame", "162.248.101.116", "6567", ["m", "mg", "mini", "minig", "mgame", "mng", "minigame", "mpvp"]); - return FishServer; -}()); -exports.FishServer = FishServer; -; -/** Stores functions that return whether the specified gamemode is the current gamemode. */ -exports.Gamemode = { - attack: function () { return exports.Gamemode.name() == "attack"; }, - survival: function () { return exports.Gamemode.name() == "survival"; }, - pvp: function () { return exports.Gamemode.name() == "pvp" || exports.Gamemode.name() == "hexed" || exports.Gamemode.name() == "minigame"; }, - sandbox: function () { return exports.Gamemode.name() == "sandbox"; }, - hexed: function () { return exports.Gamemode.name() == "hexed"; }, - hardcore: function () { return exports.Gamemode.name() == "hardcore"; }, - testsrv: function () { return exports.Gamemode.name() == "testsrv"; }, - minigame: function () { return exports.Gamemode.name() == "minigame"; }, - name: function () { return Core.settings.get("mode", Vars.state.rules.mode().name()); }, -}; -//#endregion -//#region text content -exports.prefixes = { - marked: '[yellow]\u26A0[scarlet]Marked Griefer[]\u26A0[]', - flagged: '[yellow]\u26A0[orange]Flagged[]\u26A0[]', - muted: '[white](muted)', -}; -exports.text = { - discordURL: "https://discord.gg/VpzcYSQ33Y", - membershipURL: "https://patreon.com/FishServers", - welcomeMessage: function () { return (0, funcs_1.random)([ - "[gold]Welcome![]" - ]); }, - chatFilterReplacement: { - message: function () { return "I really hope everyone is having a fun time :) <3"; }, - highlight: function () { return "[#f456f]"; }, - // `[#22AA22]Merry [#EC4444]Christmas!`, - // `[gold]Happy Holidays! [white]•*•☃*•`, - // `[gold]Happy Hanukkah!`, - // `[#EC4444]May your days be merry and bright!`, - // `[gold]Merry Fishmas! >|||> [white]☃`, - // `[gold]Deck the halls with lots of fun!`, - // `[gold]>|||> Fish wishes you [#22AA22]a merry Christmas!`, - // ]), - // chatFilterReplacement: { - // message: () => random([ - // `Have a holly jolly Christmas :) <3`, - // `I really hope everyone is jolly for the season! :D`, - // `All I want for Christmaaaaaaas is everyone having a fun time! :)`, - // `Remember to be nice in chat: Santa is watching! <3`, - // `All I want for Christmas is Fish! >|||>`, - // ]), - // highlight: () => random([ - // `[#22AA22]`, `[#EC4444]`, `[#FFFFFF]` - // ]), - }, - dataFetchFailed: "[scarlet]\u26A0 Data fetch failed!\n[white]Please disconnect and rejoin the server if you encounter further issues, such as missing rank or statistics.", -}; -//TODO use this -exports.FColor = (function (data) { - return Object.fromEntries(Object.entries(data).map(function (_a) { - var _b = __read(_a, 2), k = _b[0], c = _b[1]; - return [k, function (str) { - var varChunks = []; - for (var _i = 1; _i < arguments.length; _i++) { - varChunks[_i - 1] = arguments[_i]; - } - return str != null ? - "".concat(c).concat(Array.isArray(str) ? String.raw.apply(String, __spreadArray([{ raw: str }], __read(varChunks), false)) : str, "[]") - : c; - } - ]; - })); -})({ - discord: "[#7289DA]", - /** Used for tips and welcome messages. */ - tip: "[gold]", - member: "[pink]", -}); -/** Tips that are shown to players randomly. */ -exports.tips = { - ads: [ - "".concat(exports.FColor.member(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can access the ").concat(exports.FColor.member(templateObject_2 || (templateObject_2 = __makeTemplateObject(["/pet"], ["/pet"]))), " command, which spawns a merui that follows you around. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), - "".concat(exports.FColor.member(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_4 || (templateObject_4 = __makeTemplateObject(["/highlight"], ["/highlight"]))), " command, which turns your chat messages to a color of your choice. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), - "".concat(exports.FColor.member(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_6 || (templateObject_6 = __makeTemplateObject(["/rainbow"], ["/rainbow"]))), " command, which makes your name flash different colors. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), - "Want to support the server and get some perks? Get a ".concat(exports.FColor.member(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " at[sky] ").concat(exports.text.membershipURL, " []"), - "Join our ".concat(exports.FColor.discord(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Discord server"], ["Discord server"]))), "[]! ").concat(exports.FColor.discord(exports.text.discordURL), " or type ").concat(exports.FColor.discord(templateObject_9 || (templateObject_9 = __makeTemplateObject(["/discord"], ["/discord"])))), - ], - normal: [ - //commands - "You can spawn an [scarlet]Ohno[] with the [scarlet]/ohno[] command. Ohnos are harmless creatures that were created by fusing an alpha and an atrax.", - "Ohnos cannot be spawned near enemy buildings, because they are peaceful and do not want to be used for attacks.", - "You can use [white]/tp[] to teleport directly to any other player! (But only when you're in a core unit)", - "Hate boulders? You can remove them with [white]/clean[].", - "You can check our rules at any time by running [white]/rules[].", - // `You can kill your unit by running [white]/die[].`, - "We have a tilelog system to help catch griefers. Run [white]/tilelog[], then click a tile to see what's happened there.", - "Run [white]/tilelog 1[] to check the tile history of multiple tiles.", - "Tilelog stores when a building is placed, broken, rotated, configured, and picked up/dropped by a payload unit. Access it with [white]/tilelog[]", - "Tilelog doesn't just log tile actions, it also logs unit deaths! Access it with [white]/tilelog[]", - "Did someone kill a T5 with commands? Run [white]/aoelog 0 15 killed[] to check tilelogs for unit deaths in a large area.", - "Aoelog can show the history of tiles in an area. Select the opposite corners of a rectangle to view the history of its tiles.", - "Aoelog is the plural version of tilelog, access it via [white]/aoelog[]", - "You can mark yourself as AFK(away from keyboard) with [white]/afk[].", - "Run /survival, /attack, /pvp, /sandbox, /hexed or /minigame to quickly change to another server.", - "Need to get rid of an active griefer? Use [#6FFC7C]/s[] to send a message to all staff members across all servers.", - "Use [white]/help to get more information about a specific command.", - "If you want to send a message to just one player, you can use the [white]/msg[] command.", - "Use [white]/r[] to reply to a message sent by another player.", - "[white]/trail[] can be used to give your unit a trail of particle effects.", - "Run [white]/ranks[] to see all the ranks on our server.", - "Is someone impersonating a staff member? Run [white]/rank[] to see their real rank.", - "Don't like the map? Vote to change it with [white]/rtv[].", - "If you want to end the current map, DO NOT BREAK DEFENCES! Vote to change the map with [white]/rtv[].", - //misc - "Anyone attempting to impersonate a ranked player, or the server, will have [scarlet]SUSSY IMPOSTOR[] prepended to their name. Beware!", - "Griefers will often be found with the text ".concat(exports.prefixes.marked, " prepended to their name."), - "Players marked as ".concat(exports.prefixes.flagged, " have been flagged as suspicious by our detection systems, but they may not be griefers."), - "Need to appeal a moderation action? Join the discord at ".concat(exports.FColor.discord(exports.text.discordURL), " or type /discord"), - "Want to send the phrase [white]\"/command\"[] in chat? Type [white]\"./command\"[] and the [white].[] will be removed.", - "All commands with a player as an argument support using a menu to specify the player. Just run the command leaving the argument blank (using two spaces if necessary), and a menu will show up.", - "Players with a ".concat(ranks_1.Rank.trusted.prefix, " in front of their name aren't staff members, but they do have extra powers."), - "Staff members will have the following prefixes in front of their name: ".concat(ranks_1.Rank.manager.prefix, ", ").concat(ranks_1.Rank.admin.prefix, ", ").concat(ranks_1.Rank.mod.prefix), - "Wave cooldown too long? Skip the wait with [white]/vnw[]", - "You can tell new players not to break power voids with [white]/void[]", - "You can add [pink]color[] to things with color tags! Try typing \"[[".concat(["pink", "green", "cyan", "acid", "royal", "coral"][Math.floor(Math.random() * 6)], "]Hello\" in chat, and see what happens!") - ], - christmas: [ - "Remember to be nice in-game, Santa is watching!", - "Santa's checking his list, so be nice!", - "Have a merry christmas and a happy new year!", - "Fish becomes a bit more jolly around Christmastime!", - "Many server maps have been changed for the season.", - ], - staff: [], -}; -exports.rules = [ - "# 1: [red]No griefing. This refers to intentionally hurting your own team in any way.", - "# 2: [orange]False votekicking isn't allowed. Avoid votekicking if there's an active staff member in the server.", - "# 3: [yellow]Gore, pornography, suggestive content and jokes, and flashing images aren't allowed here. Being horny and a creep in chat will result in a ban.", - "# 4: [green]Do not harass other people. We have zero tolerance for any bigotry. Please respect everyone.", - "# 5: [#00D8D8]Spamming is prohibited. Be reasonable with messaging staff in-game. Misuse may result in a mute.", - "# 6: [blue]Impersonating people or ranks is prohibited.", - "# 7: [purple]Talking about controversial or sensitive topics is not allowed in-game. Hate symbols, such as swastikas, are not permitted.", - "# 8: [pink]No uncomfortable trolling or intentionally causing chaos. This includes any actions or messages that create an unpleasant atmosphere.", - "Failure to follow these rules will result in consequences: likely a ".concat(exports.prefixes.marked, " tag for any game disruption, mute for broken chat rules, and bans for repeated offenses or bypasses.") -].map(function (r) { return "[white]".concat(r); }); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9; -//#endregion +"use strict"; +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains configurable constants. +*/ +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.rules = exports.tips = exports.FColor = exports.text = exports.prefixes = exports.Gamemode = exports.FishServer = exports.mapRepoURLs = exports.Mode = exports.backendIP = exports.stopAntiEvadeTime = exports.heuristics = exports.adminNames = exports.multiCharSubstitutions = exports.substitutions = exports.bannedWords = void 0; +var globals_1 = require("/globals"); +var ranks_1 = require("/ranks"); +var funcs_1 = require("/funcs"); +function processBannedWordList(words) { + return words.map(function (word) { + return (typeof word == "string" || word instanceof RegExp) ? + [word, []] + : [word[0], word.slice(1)]; + }); +} +exports.bannedWords = { + // README: Information on how to update this list + // All words must be in *lowercase*. + // Words need to be separated by commas, even if they are on a new line. + // If a word can be contained in another word that should be allowed (the scunthorpe problem), + // surround the entire thing in square brackets, then list out the words after + // like this: ["badw", "goodbadw"] + /** Normal: banned always. */ + normal: processBannedWordList([ + "fanum tax", "gyatt", "rizz", "skibidi", //With love, DarthScion + //>:( -dart + "uwu", //lol + // why?? why the uwu??? -starkatt + "nig" + "ger", "nig" + "ga", "niger", "ni8" + "8er", "nig" + "gre", //our apologies to citizens of the Republic of Niger + "негр", "ниггер", + "re" + "tard", + 'kill yourself', 'kill urself', /\bkys\b/, + "kill blacks", "heil hitler", "heil nazis", "heil the nazis", "sieg heil", "hail hitler", "hail nazis", "hail the nazis", "sieg hail", //nazi-related words + ["co" + "ck", "cockroach", "poppycock", "cocktail"], + "iamasussyimposter", + ["cu" + "nt", "scunthorpe"], + ["penis", "peniston"], + "hawk tuah", + ["rape", "grape", "therap", "drape", "scrape", "trapez", "earrape", "atrape"], + /\bf(a)g\b/, "fa" + "gg" + "ot", + /\bc(u)m\b/, ["semen", "sement", "horsemen", "housemen", "defensemen", "those", "menders"], + ["porn", "maporn"], + "futa" + "nari", "futa", + "ur gay", "your gay", "youre gay", "you're gay", + "gooning", "gooner", "dildo", "loli", /\banal\b/, "cunny" + ]), + /** Strict: banned in names and for players with a chat strictness level of 'strict'. */ + strict: processBannedWordList([ + "fu" + "ck", "bi" + "tch", ["sh" + "it", "harshit"], /\ba(s)s\b/, "as" + "shole", ["dick", "medick", "dickens"], + ]), + /** Names: banned only in names. */ + names: processBannedWordList([ + "sex", /\bgoldberg\b/, "hitler", "stalin", "putin", "lenin", /^something$/, "[something]", "[[something]", "卐", + globals_1.uuidPattern, globals_1.ipPattern, globals_1.ipPortPattern + ]), + /** autoWhack: new players saying one of these words will be automatically stopped and muted. */ + autoWhack: [ + "nig" + "ger", "nig" + "ga", "ni8" + "8er", "hit" + "ler", "fa" + "gg" + "ot", "nazis", + ], +}; +//for some reason the external mindustry server does not read the files correctly, so we can only use ASCII +exports.substitutions = Object.fromEntries(Object.entries({ + "a": "\u0430\u1E9A\u1EA1\u1E01\u00E4\u03B1@\u0101\u0103\u0105\u03AC", + "b": "\u1E03\u1E07\u1E03\u0253\u0185", + "c": "\u0441\u217D\u00E7\u03C2\u010B", + "d": "\u217E\u1E0B\u1E11\u010F\u1E13\u1E0D\u1E0F\u0257\u20AB\u0256\u056A", + "e": "\u0435\u1E1B\u0113\u1E17\u0229\u0451\u011B\u0205\u03F5\u03B5\u025B3", + "f": "\u1E1F\u0493\u0192", + "g": "\u0581\u0123\u01F5\u0260\u011F\u011D\u01E5\u1E21", + "h": "\u1E23\u021F\u1E25\u1E2B\u0570\u056B\u1E29\u0266\u1E27\u1E23\u0266\u1E96\u0127", + "i": "\u0456\u012F\u03B9\u1EC9\u1F31\u1F77\u012B1\u00A1\u0457\u0390\u03CA", + "j": "\u0458\u029D\u0575\u025F\u0135\u0237\u01F0", + "k": "\u049F\u1E31\u0137\u0138\u043A\u0199\u049D", + "l": "\u217C\u1E3D\u1E3B\u013E\u0140\u013C\u1E39\u0142\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u03B9", + "m": "\u217F\u1E43\u0271\u1E41\u1E3F", + "n": "\u00F1\u0144\u0146\u0148\u0149\u01F9\u03AE\u03B7\u0578\u057C\u0580\u1E45\u1E47\u03A0", + "o": "\u00F2\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1F40\u1F41\u1F42\u1F43\u1F44\u1F45\u1F78\u1F79\u03C3\u0E50\u00F6\u014D\u014F\u0151\u01A1\u01D2\u03BF\u03CC0", + "p": "\u03C1\u0440\u048F\u1E55\u1E57\u1FE4\u1FE5\u2374", + "q": "\u051B\u0563\u0566\u0563\u0566", + "r": "\u0155\u0157\u0159\u0211\u0213\u027C\u027D\u0433\u0453\u0491\u04F7\u1E59\u1E5B\u1E5D", + "s": "\u015B\u015D\u015F\u0161\u0219\u0282\u0455\u1E61\u1E63\u1E65\u1E67\u1E69\u03C2", + "t": "\u0163\u0165\u01AB\u021B\u0288\u1E6B\u1E6D\u1E6F\u1E71\u1E97\u0236\u2020\u04AD", + "u": "\u00B5\u03BC\u00F9\u00FA\u00FB\u00FC\u0169\u016B\u016D\u016F\u0171\u0173\u01B0\u01D4\u0215\u0217\u0265\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u03BC\u03C5\u03CB\u03CD", + "v": "\u03BD\u0475\u0477\u1E7D\u1E7F\u2174\u2228\u03C5\u03CB\u03CD", + "w": "\u0175\u051D\u1E81\u1E83\u1E85\u1E87\u1E89\u1E98\u03C9\u03CE", + "x": "\u0445\u04B3\u1E8B\u1E8D\u03C7", + "y": "\u00FD\u00FF\u0177\u01B4\u0233\u03B3\u0443\u045E\u04EF\u04F1\u04F3\u1E8F\u1E99\u1EF3\u1EF5\u1EF7\u1EF9\u04AF\u04B1", + "z": "\u017A\u017C\u017E\u01B6\u0225\u0290\u1E91\u1E93\u1E95", + "A": "\u1E00\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAC\u1F08\u1F09\u1F88\u1F89\u1FB8\u1FB9\u1FBA\u1FBC\u212B\u0100\u0102\u0104\u0386\u0391", + "B": "\u0181\u0392\u0412\u1E02\u1E04\u1E06", + "C": "\u00C7\u0106\u0108\u010A\u010C\u0187\u0421\u04AA\u1E08\u216D\u03F9", + "D": "\u00D0\u010E\u0110\u0189\u018A\u1E0A\u1E0C\u1E0E\u216E", + "E": "\u00C8\u00C9\u00CA\u00CB\u0112\u0114\u0116\u0118\u011A\u0204\u0206\u0228\u0395\u0400\u0415\u04D6\u1E18\u0510\u2107\u0190\u1F19\u1FC8\u0404\u0388\u03AD\u03B5\u03B7", + "F": "\u03DC\u1E1E\u0492\u0191\u0492\u0493", + "G": "\u011C\u011E\u0120\u0122\u0193\u01E6\u01F4\u1E20", + "H": "\u0124\u021E\u0397\u041D\u04A2\u04A4\u04C7\u04C9\u1E22\u1E24\u1E26\u1E28\u1E2A\u1FCC\uA726\u0389", + "I": "\u038A\u00CC\u00CD\u00CE\u00CF\u0128\u012A\u012C\u012E\u0130\u0196\u0208\u020A\u0399\u03AA\u0406\u0407\u04C0\u04CF\u1E2C\u1EC8\u1F38\u1F39\u1FD8\u1FD9\u1FDA\u01D0\u217C\u1E3D\u1E3B\u026B\u013E\u0140\u013C\u1E39\u038A", + "J": "\u0134\u0408\u037F", + "K": "\u0136\u0198\u01E8\u039A\u040C\u041A\u051E\u1E30\u1E32\u1E34\u20AD\u212A\u03BA", + "L": "\u0139\u013B\u013D\u013F\u0141\u053C\u1E36\u1E38\u1E3A\u1E3C\u216C", + "M": "\u039C\u041C\u04CD\u1E3E\u1E40\u1E42\u216F", + "N": "\u00D1\u0143\u0145\u0147\u01F8\u039D\u1E44\u1E46\u1E48\u1E4A\u019D", + "O": "\u03B8\u236C\u00D2\u00D3\u00D4\u00D5\u00D6\u014C\u014E\u0150\u019F\u01A0\u01D1\u020E\u022E\u0230\u0398\u039F\u041E\u04E6\u0555\u1ECC\u1ECE\u1ED4\u1FF9\u038C", + "P": "\u01A4\u03A1\u0420\u048E\u1E54\u1E56\u1FEC", + "Q": "\u051A", + "R": "\u0154\u0156\u0158\u0210\u0212\u1E58\u1E5A\u1E5C\u1E5E\u211E\u024C\u2C64", + "S": "\u015A\u015C\u015E\u0160\u0218\u0405\u054F\u1E60\u1E62\u1E68\u1E64\u1E66", + "T": "\u0162\u0164\u0166\u01AE\u021A\u03A4\u0422\u04AC\u1E6A\u1E6C\u1E6E\u1E70\u038A\u1FDB\uA68C\u0372\u0373\u03C4", + "U": "\u016A\u016C\u016E\u0170\u0172\u01AF\u01D3\u1EE8\u1EEA\u1EEC\u1EEE\u0544", + "V": "\u0474\u0476\u1E7C\u1E7E\u22C1\u2164", + "W": "\u051C\u1E80\u1E82\u1E84\u1E86\u1E88\u019C", + "X": "\u03A7\u0425\u04B2\u1E8A\u1E8C\u2169", + "Y": "\u01B3\u0232\u03A5\u03AB\u03D3\u0423\u04AE\u04B0\u1E8E\u1EF2\u1EF4\u038E", + "Z": "\u0179\u017B\u017D\u0224\u0396\u1E90\u1E92\u1E94", + "": "\u200B\u200C\u200D", +}).map(function (_a) { + var _b = __read(_a, 2), char = _b[0], alts = _b[1]; + return alts.split("").map(function (alt) { return [alt, char]; }); +}).flat(1)); +exports.multiCharSubstitutions = [ + [/\|-\|/g, "H"] +]; +//#endregion +//#region misc +/** Used for anti-impersonation. Make sure to replace numbers with letters, for example, balam314 -> balamei4. */ +exports.adminNames = ["fish", "balamei4", "clashgone", "darthscion", "firefridge", "aricia", "rawsewage", "skeledragon", "edh8e", "everydayhuman8e", "benjamonsrl", "cudspent"]; +exports.heuristics = { + /** Will trip if more than this many blocks are broken within 25 seconds of joining. */ + blocksBrokenAfterJoin: 40, +}; +exports.stopAntiEvadeTime = funcs_1.Duration.minutes(30); +exports.backendIP = '45.79.202.111:5082'; +exports.Mode = { + localDebug: new Fi("config/.debug").exists(), + noBackend: new Fi("config/.debug").exists() && !exports.backendIP.startsWith("127.0.0.1:"), + isChristmas: new Date().getMonth() == 11, + isAprilFools: new Date().getMonth() == 3 && new Date().getDate() == 1, +}; +//#endregion +//#region servers +/** Stores the repository url for the maps for each gamemode. */ +exports.mapRepoURLs = { + attack: "https://api.github.com/repos/Fish-Community/fish-maps/contents/attack", + survival: "https://api.github.com/repos/Fish-Community/fish-maps/contents/survival", + pvp: "https://api.github.com/repos/Fish-Community/fish-maps/contents/pvp", + hexed: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hexed", + sandbox: "https://api.github.com/repos/Fish-Community/fish-maps/contents/sandbox", + hardcore: "https://api.github.com/repos/Fish-Community/fish-maps/contents/hardcore", + testsrv: "https://api.github.com/repos/Fish-Community/fish-maps/contents/testsrv", + minigame: "https://api.github.com/repos/Fish-Community/fish-maps/contents/minigame", +}; +/** Stores the names and addresses of each active server. */ +var FishServer = /** @class */ (function () { + function FishServer(name, ip, port, aliases, + /** If set, this permission is required to switch to or get information about this server. */ + requiredPerm) { + this.name = name; + this.ip = ip; + this.port = port; + this.aliases = aliases; + this.requiredPerm = requiredPerm; + FishServer.all.push(this); + } + FishServer.byName = function (input) { + var _a; + input = input.toLowerCase(); + return (_a = FishServer.all.find(function (s) { return s.aliases.concat(s.name).includes(input); })) !== null && _a !== void 0 ? _a : null; + }; + FishServer.all = []; + FishServer.attack = new FishServer("attack", "162.248.100.98", "6567", ["attac", "atack", "atak", "atck", "atk", "a"]); + FishServer.survival = new FishServer("survival", "162.248.101.95", "6567", ["surviv", "surv", "sur", "su", "s", "sl"]); + FishServer.pvp = new FishServer("pvp", "162.248.102.101", "6567", ["pv", "p", "v", "playerversusplayer"]); + FishServer.sandbox = new FishServer("sandbox", "162.248.101.53", "6567", ["sand", "box", "sa", "sb"]); + FishServer.hexed = new FishServer("hexed", "162.248.100.133", "6567", ["h", "hx", "hxd", "hpvp", "hxpvp", "hexpvp"]); + FishServer.minigame = new FishServer("minigame", "162.248.101.116", "6567", ["m", "mg", "mini", "minig", "mgame", "mng", "minigame", "mpvp"]); + return FishServer; +}()); +exports.FishServer = FishServer; +; +/** Stores functions that return whether the specified gamemode is the current gamemode. */ +exports.Gamemode = { + attack: function () { return exports.Gamemode.name() == "attack"; }, + survival: function () { return exports.Gamemode.name() == "survival"; }, + pvp: function () { return exports.Gamemode.name() == "pvp" || exports.Gamemode.name() == "hexed" || exports.Gamemode.name() == "minigame"; }, + sandbox: function () { return exports.Gamemode.name() == "sandbox"; }, + hexed: function () { return exports.Gamemode.name() == "hexed"; }, + hardcore: function () { return exports.Gamemode.name() == "hardcore"; }, + testsrv: function () { return exports.Gamemode.name() == "testsrv"; }, + minigame: function () { return exports.Gamemode.name() == "minigame"; }, + name: function () { return Core.settings.get("mode", Vars.state.rules.mode().name()); }, +}; +//#endregion +//#region text content +exports.prefixes = { + marked: '[yellow]\u26A0[scarlet]Marked Griefer[]\u26A0[]', + flagged: '[yellow]\u26A0[orange]Flagged[]\u26A0[]', + muted: '[white](muted)', +}; +exports.text = { + discordURL: "https://discord.gg/VpzcYSQ33Y", + membershipURL: "https://patreon.com/FishServers", + welcomeMessage: function () { return (0, funcs_1.random)([ + "[gold]Welcome![]" + ]); }, + chatFilterReplacement: { + message: function () { return "I really hope everyone is having a fun time :) <3"; }, + highlight: function () { return "[#f456f]"; }, + // `[#22AA22]Merry [#EC4444]Christmas!`, + // `[gold]Happy Holidays! [white]•*•☃*•`, + // `[gold]Happy Hanukkah!`, + // `[#EC4444]May your days be merry and bright!`, + // `[gold]Merry Fishmas! >|||> [white]☃`, + // `[gold]Deck the halls with lots of fun!`, + // `[gold]>|||> Fish wishes you [#22AA22]a merry Christmas!`, + // ]), + // chatFilterReplacement: { + // message: () => random([ + // `Have a holly jolly Christmas :) <3`, + // `I really hope everyone is jolly for the season! :D`, + // `All I want for Christmaaaaaaas is everyone having a fun time! :)`, + // `Remember to be nice in chat: Santa is watching! <3`, + // `All I want for Christmas is Fish! >|||>`, + // ]), + // highlight: () => random([ + // `[#22AA22]`, `[#EC4444]`, `[#FFFFFF]` + // ]), + }, + dataFetchFailed: "[scarlet]\u26A0 Data fetch failed!\n[white]Please disconnect and rejoin the server if you encounter further issues, such as missing rank or statistics.", +}; +//TODO use this +exports.FColor = (function (data) { + return Object.fromEntries(Object.entries(data).map(function (_a) { + var _b = __read(_a, 2), k = _b[0], c = _b[1]; + return [k, function (str) { + var varChunks = []; + for (var _i = 1; _i < arguments.length; _i++) { + varChunks[_i - 1] = arguments[_i]; + } + return str != null ? + "".concat(c).concat(Array.isArray(str) ? String.raw.apply(String, __spreadArray([{ raw: str }], __read(varChunks), false)) : str, "[]") + : c; + } + ]; + })); +})({ + discord: "[#7289DA]", + /** Used for tips and welcome messages. */ + tip: "[gold]", + member: "[pink]", +}); +/** Tips that are shown to players randomly. */ +exports.tips = { + ads: [ + "".concat(exports.FColor.member(templateObject_1 || (templateObject_1 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can access the ").concat(exports.FColor.member(templateObject_2 || (templateObject_2 = __makeTemplateObject(["/pet"], ["/pet"]))), " command, which spawns a merui that follows you around. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), + "".concat(exports.FColor.member(templateObject_3 || (templateObject_3 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_4 || (templateObject_4 = __makeTemplateObject(["/highlight"], ["/highlight"]))), " command, which turns your chat messages to a color of your choice. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), + "".concat(exports.FColor.member(templateObject_5 || (templateObject_5 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " subscribers can use the ").concat(exports.FColor.member(templateObject_6 || (templateObject_6 = __makeTemplateObject(["/rainbow"], ["/rainbow"]))), " command, which makes your name flash different colors. Get a Fish Membership at[sky] ").concat(exports.text.membershipURL, " []"), + "Want to support the server and get some perks? Get a ".concat(exports.FColor.member(templateObject_7 || (templateObject_7 = __makeTemplateObject(["Fish Membership"], ["Fish Membership"]))), " at[sky] ").concat(exports.text.membershipURL, " []"), + "Join our ".concat(exports.FColor.discord(templateObject_8 || (templateObject_8 = __makeTemplateObject(["Discord server"], ["Discord server"]))), "[]! ").concat(exports.FColor.discord(exports.text.discordURL), " or type ").concat(exports.FColor.discord(templateObject_9 || (templateObject_9 = __makeTemplateObject(["/discord"], ["/discord"])))), + ], + normal: [ + //commands + "You can spawn an [scarlet]Ohno[] with the [scarlet]/ohno[] command. Ohnos are harmless creatures that were created by fusing an alpha and an atrax.", + "Ohnos cannot be spawned near enemy buildings, because they are peaceful and do not want to be used for attacks.", + "You can use [white]/tp[] to teleport directly to any other player! (But only when you're in a core unit)", + "Hate boulders? You can remove them with [white]/clean[].", + "You can check our rules at any time by running [white]/rules[].", + // `You can kill your unit by running [white]/die[].`, + "We have a tilelog system to help catch griefers. Run [white]/tilelog[], then click a tile to see what's happened there.", + "Run [white]/tilelog 1[] to check the tile history of multiple tiles.", + "Tilelog stores when a building is placed, broken, rotated, configured, and picked up/dropped by a payload unit. Access it with [white]/tilelog[]", + "Tilelog doesn't just log tile actions, it also logs unit deaths! Access it with [white]/tilelog[]", + "Did someone kill a T5 with commands? Run [white]/aoelog 0 15 killed[] to check tilelogs for unit deaths in a large area.", + "Aoelog can show the history of tiles in an area. Select the opposite corners of a rectangle to view the history of its tiles.", + "Aoelog is the plural version of tilelog, access it via [white]/aoelog[]", + "You can mark yourself as AFK(away from keyboard) with [white]/afk[].", + "Run /survival, /attack, /pvp, /sandbox, /hexed or /minigame to quickly change to another server.", + "Need to get rid of an active griefer? Use [#6FFC7C]/s[] to send a message to all staff members across all servers.", + "Use [white]/help to get more information about a specific command.", + "If you want to send a message to just one player, you can use the [white]/msg[] command.", + "Use [white]/r[] to reply to a message sent by another player.", + "[white]/trail[] can be used to give your unit a trail of particle effects.", + "Run [white]/ranks[] to see all the ranks on our server.", + "Is someone impersonating a staff member? Run [white]/rank[] to see their real rank.", + "Don't like the map? Vote to change it with [white]/rtv[].", + "If you want to end the current map, DO NOT BREAK DEFENCES! Vote to change the map with [white]/rtv[].", + //misc + "Anyone attempting to impersonate a ranked player, or the server, will have [scarlet]SUSSY IMPOSTOR[] prepended to their name. Beware!", + "Griefers will often be found with the text ".concat(exports.prefixes.marked, " prepended to their name."), + "Players marked as ".concat(exports.prefixes.flagged, " have been flagged as suspicious by our detection systems, but they may not be griefers."), + "Need to appeal a moderation action? Join the discord at ".concat(exports.FColor.discord(exports.text.discordURL), " or type /discord"), + "Want to send the phrase [white]\"/command\"[] in chat? Type [white]\"./command\"[] and the [white].[] will be removed.", + "All commands with a player as an argument support using a menu to specify the player. Just run the command leaving the argument blank (using two spaces if necessary), and a menu will show up.", + "Players with a ".concat(ranks_1.Rank.trusted.prefix, " in front of their name aren't staff members, but they do have extra powers."), + "Staff members will have the following prefixes in front of their name: ".concat(ranks_1.Rank.manager.prefix, ", ").concat(ranks_1.Rank.admin.prefix, ", ").concat(ranks_1.Rank.mod.prefix), + "Wave cooldown too long? Skip the wait with [white]/vnw[]", + "You can tell new players not to break power voids with [white]/void[]", + "You can add [pink]color[] to things with color tags! Try typing \"[[".concat(["pink", "green", "cyan", "acid", "royal", "coral"][Math.floor(Math.random() * 6)], "]Hello\" in chat, and see what happens!") + ], + christmas: [ + "Remember to be nice in-game, Santa is watching!", + "Santa's checking his list, so be nice!", + "Have a merry christmas and a happy new year!", + "Fish becomes a bit more jolly around Christmastime!", + "Many server maps have been changed for the season.", + ], + staff: [], +}; +exports.rules = [ + "# 1: [red]No griefing. This refers to intentionally hurting your own team in any way.", + "# 2: [orange]False votekicking isn't allowed. Avoid votekicking if there's an active staff member in the server.", + "# 3: [yellow]Gore, pornography, suggestive content and jokes, and flashing images aren't allowed here. Being horny and a creep in chat will result in a ban.", + "# 4: [green]Do not harass other people. We have zero tolerance for any bigotry. Please respect everyone.", + "# 5: [#00D8D8]Spamming is prohibited. Be reasonable with messaging staff in-game. Misuse may result in a mute.", + "# 6: [blue]Impersonating people or ranks is prohibited.", + "# 7: [purple]Talking about controversial or sensitive topics is not allowed in-game. Hate symbols, such as swastikas, are not permitted.", + "# 8: [pink]No uncomfortable trolling or intentionally causing chaos. This includes any actions or messages that create an unpleasant atmosphere.", + "Failure to follow these rules will result in consequences: likely a ".concat(exports.prefixes.marked, " tag for any game disruption, mute for broken chat rules, and bans for repeated offenses or bypasses.") +].map(function (r) { return "[white]".concat(r); }); +var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9; +//#endregion diff --git a/build/scripts/frameworks/menus.js b/build/scripts/frameworks/menus.js index f8106970..0221340e 100644 --- a/build/scripts/frameworks/menus.js +++ b/build/scripts/frameworks/menus.js @@ -1,351 +1,351 @@ -"use strict"; -/* eslint-disable @typescript-eslint/array-type */ -/* -Copyright © BalaM314, 2026. All Rights Reserved. -This file contains the menu framework. -For usage information, see docs/framework-usage-guide.md -For maintenance information, see docs/frameworks.md -*/ -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -var __rest = (this && this.__rest) || function (s, e) { - var t = {}; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) - t[p] = s[p]; - if (s != null && typeof Object.getOwnPropertySymbols === "function") - for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { - if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) - t[p[i]] = s[p[i]]; - } - return t; -}; -var __values = (this && this.__values) || function(o) { - var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; - if (m) return m.call(o); - if (o && typeof o.length === "number") return { - next: function () { - if (o && i >= o.length) o = void 0; - return { value: o && o[i++], done: !o }; - } - }; - throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); -}; -var __read = (this && this.__read) || function (o, n) { - var m = typeof Symbol === "function" && o[Symbol.iterator]; - if (!m) return o; - var i = m.call(o), r, ar = [], e; - try { - while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); - } - catch (error) { e = { error: error }; } - finally { - try { - if (r && !r.done && (m = i["return"])) m.call(i); - } - finally { if (e) throw e.error; } - } - return ar; -}; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.listeners = exports.Menu = void 0; -exports.registerListeners = registerListeners; -var commands_1 = require("/frameworks/commands"); -var funcs_1 = require("/funcs"); -var players_1 = require("/players"); -var promise_1 = require("/promise"); -var utils_1 = require("/utils"); -/** Used to change the behavior of adding another menu when being run in a menu callback. */ -var isInMenuCallback = false; -/** Stores a mapping from name to the numeric id of a listener that has been registered. */ -var registeredListeners = {}; -exports.listeners = registeredListeners; -/** Stores all listeners in use by fish-commands. */ -var listeners = { - generic: function (player, option) { - var fishSender = players_1.FishPlayer.get(player); - var prevCallback = fishSender.activeMenus.shift(); - if (!prevCallback) - return; //No menu to process, do nothing - isInMenuCallback = true; - prevCallback.callback(option); - isInMenuCallback = false; - }, - none: function (player, option) { - //do nothing - } -}; -/** Registers all listeners, should be called on server load. */ -function registerListeners() { - var e_1, _a; - var _b; - try { - for (var _c = __values(Object.entries(listeners)), _d = _c.next(); !_d.done; _d = _c.next()) { - var _e = __read(_d.value, 2), key = _e[0], listener = _e[1]; - (_b = registeredListeners[key]) !== null && _b !== void 0 ? _b : (registeredListeners[key] = Menus.registerMenu(listener)); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (_d && !_d.done && (_a = _c.return)) _a.call(_c); - } - finally { if (e_1) throw e_1.error; } - } -} -exports.Menu = { - /** Displays a menu to a player, returning a Promise. */ - raw: function (title, description, arrangedOptions, target, _a) { - var _b = _a === void 0 ? {} : _a, _c = _b.optionStringifier, optionStringifier = _c === void 0 ? String : _c, _d = _b.onCancel, onCancel = _d === void 0 ? "ignore" : _d, _e = _b.cancelOptionId, cancelOptionId = _e === void 0 ? -1 : _e; - var _f = promise_1.Promise.withResolvers(), promise = _f.promise, reject = _f.reject, resolve = _f.resolve; - //The target fishPlayer has a property called activeMenu, which stores information about the last menu triggered. - //If menu() is being called from a menu calback, add it to the front of the queue so it is processed before any other menus. - //Otherwise, two multi-step menus queued together would alternate, which would confuse the player. - target.activeMenus[isInMenuCallback ? "unshift" : "push"]({ callback: function (option) { - //Additional permission validation could be done here, but the only way that callback() can be called is if the above statement executed, - //and on sensitive menus such as the stop menu, the only way to reach that is if menu() was called by the /stop command, - //which already checks permissions. - //Additionally, the callback is cleared by the generic menu listener after it is executed. - try { - var options = arrangedOptions.flat(); - //We do need to validate option though, as it can be any number. - if (option === -1 || option === cancelOptionId || !(option in options)) { - //Consider any invalid option to be a cancellation - if (onCancel == "null") - resolve(null); - else if (onCancel == "reject") - reject("cancel"); - else - return; - } - else { - resolve(options[option]); - } - } - catch (err) { - if (err instanceof commands_1.CommandError) { - //If the error is a command error, then just outputFail - (0, utils_1.outputFail)(err.data, target); - } - else { - target.sendMessage("[scarlet]\u274C An error occurred while executing the command!"); - if (target.hasPerm("seeErrorMessages")) - target.sendMessage((0, funcs_1.parseError)(err)); - Log.err("Unhandled error in menu callback: ".concat(target.cleanedName, " submitted menu \"").concat(title, "\" \"").concat(description, "\"")); - Log.err(err); - } - } - } }); - var i = 0; - var stringifiedOptions = arrangedOptions.map(function (r) { return r.map(function (item) { - if (i === cancelOptionId) - return item; - i++; - return optionStringifier(item); - }); }); - Call.menu(target.con, registeredListeners.generic, title, description, stringifiedOptions); - return promise; - }, - /** Displays a menu to a player, returning a Promise. Arranges options into a 2D array, and can add a Cancel option. */ - menu: function (title, description, options, target, _a) { - var _b = _a === void 0 ? {} : _a, _c = _b.includeCancel, includeCancel = _c === void 0 ? false : _c, _d = _b.optionStringifier, optionStringifier = _d === void 0 ? String : _d, _e = _b.columns, columns = _e === void 0 ? 3 : _e, _f = _b.onCancel, onCancel = _f === void 0 ? "ignore" : _f, _g = _b.cancelOptionId, cancelOptionId = _g === void 0 ? -1 : _g; - //Set up the 2D array of options, and maybe add cancel - //Call.menu() with [[]] will cause a client crash, make sure to pass [] instead - var arrangedOptions = (options.length == 0 && !includeCancel) ? [] : (0, funcs_1.to2DArray)(options, columns); - if (includeCancel) { - arrangedOptions.push(["[red]Cancel[]"]); - //This is safe because cancelOptionId is set, - //so the handler will never get called with "Cancel". - cancelOptionId = options.length; - } - return exports.Menu.raw(title, description, arrangedOptions, target, { - cancelOptionId: cancelOptionId, - onCancel: onCancel, - optionStringifier: optionStringifier - }); - }, - /** Rejects with a CommandError if the user chooses to cancel. */ - confirm: function (target, description, _a) { - var _b = _a === void 0 ? {} : _a, _c = _b.cancelOutput, cancelOutput = _c === void 0 ? "Cancelled." : _c, _d = _b.title, title = _d === void 0 ? "Confirm" : _d, _e = _b.confirmText, confirmText = _e === void 0 ? "[green]Confirm" : _e, _f = _b.cancelText, cancelText = _f === void 0 ? "[red]Cancel" : _f; - return exports.Menu.menu(title, description, [confirmText, cancelText], target, { onCancel: "reject", cancelOptionId: 1 }).catch(function (e) { - if (e === "cancel") - (0, commands_1.fail)(cancelOutput); - throw e; //some random error, rethrow it - }); - }, - /** Same as confirm(), but with inverted colors, for potentially dangerous actions. */ - confirmDangerous: function (target, description, _a) { - if (_a === void 0) { _a = {}; } - var _b = _a.confirmText, confirmText = _b === void 0 ? "[red]Confirm" : _b, _c = _a.cancelText, cancelText = _c === void 0 ? "[green]Cancel" : _c, rest = __rest(_a, ["confirmText", "cancelText"]); - return exports.Menu.confirm(target, description, __assign({ cancelText: cancelText, confirmText: confirmText }, rest)); - }, - buttons: function (target, title, description, options, cfg) { - if (cfg === void 0) { cfg = {}; } - return exports.Menu.raw(title, description, options, target, __assign(__assign({}, cfg), { optionStringifier: function (o) { return o.text; } })).then(function (o) { return o === null || o === void 0 ? void 0 : o.data; }); - }, - pages: function (target, title, description, options, cfg) { - var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; - function showPage(index) { - var opts = __spreadArray(__spreadArray([], __read(options[index].map(function (r) { return r.map(function (d) { return ({ text: d.text, data: [d.data] }); }); })), false), [ - [ - { data: "left", text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, - { data: "numbers", text: "[accent]".concat(index + 1, "/").concat(options.length) }, - { data: "right", text: "[".concat(index == options.length - 1 ? "gray" : "accent", "]-->") } - ] - ], false); - exports.Menu.buttons(target, title, description, opts, cfg).then(function (response) { - if (response instanceof Array) - resolve(response[0]); - else if (response === "right") - showPage(Math.min(index + 1, options.length - 1)); - else if (response === "left") - showPage(Math.max(index - 1, 0)); - else { - //Treat numbers as cancel - if (cfg.onCancel == "null") - resolve(null); - else if (cfg.onCancel == "reject") - reject("cancel"); - //otherwise, just let the promise hang - } - }); - } - showPage(0); - return promise; - }, - textPages: function (target, pages, cfg) { - if (cfg === void 0) { cfg = {}; } - var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; - var pageSkipSize = Math.max(Math.floor(pages.length / 8), 5); - function showPage(index) { - var opts = [ - [ - { data: ["left", pageSkipSize], text: "[".concat(index == 0 ? "gray" : "accent", "]<<<") }, - { data: ["left", 1], text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, - { data: ["right", 1], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]-->") }, - { data: ["right", pageSkipSize], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]>>>") }, - ], - [ - { data: ["numbers"], text: "[accent]Page ".concat(index + 1, "/").concat(pages.length) }, - { data: ["cancel"], text: "[red]Close" }, - ] - ]; - exports.Menu.buttons(target, pages[index][0], pages[index][1](), opts, cfg).then(function (response) { - if ((response === null || response === void 0 ? void 0 : response[0]) === "right") - showPage(Math.min(index + response[1], pages.length - 1)); - else if ((response === null || response === void 0 ? void 0 : response[0]) === "left") - showPage(Math.max(index - response[1], 0)); - else { - //Treat numbers as cancel - if (cfg.onCancel == "null") - resolve(null); - else if (cfg.onCancel == "reject") - reject("cancel"); - //otherwise, just let the promise hang - } - }); - } - var index = (function () { - if (cfg.startPage == undefined) - return 0; - if (typeof cfg.startPage === 'number') { - if (cfg.startPage < 0) - return 0; - return cfg.startPage; - } - var index = pages.findIndex(function (_a) { - var _b = __read(_a, 1), title = _b[0]; - return title === cfg.startPage; - }); - if (index === -1) - return 0; - return index; - })(); - showPage(index); - return promise; - }, - scroll: function (target, title, description, options, cfg) { - var _a, _b; - if (cfg === void 0) { cfg = {}; } - var _c = promise_1.Promise.withResolvers(), promise = _c.promise, reject = _c.reject, resolve = _c.resolve; - var _d = cfg.rows, rows = _d === void 0 ? 5 : _d, _e = cfg.columns, cols = _e === void 0 ? 5 : _e; - var height = options.length; - var width = options[0].length; - function showPage(x, y) { - var _a, _b; - var opts = __spreadArray(__spreadArray([], __read(options.slice(y, y + rows).map(function (r) { return r.slice(x, x + cols).map(function (d) { return ({ text: d.text, data: [d.data] }); }); })), false), [ - [ - { data: "blank", text: "" }, - { data: "up", text: "[".concat(y == 0 ? "gray" : "accent", "]^\n|") }, - { data: "blank", text: "" }, - ], [ - { data: "left", text: "[".concat(x == 0 ? "gray" : "accent", "]<--") }, - { data: "blank", text: (_b = (_a = cfg.getCenterText) === null || _a === void 0 ? void 0 : _a.call(cfg, x, y)) !== null && _b !== void 0 ? _b : '' }, - { data: "right", text: "[".concat(x == width - cols ? "gray" : "accent", "]-->") } - ], [ - { data: "blank", text: "" }, - { data: "down", text: "[".concat(y == height - rows ? "gray" : "accent", "]|\nV") }, - { data: "blank", text: "" }, - ] - ], false); - exports.Menu.buttons(target, title, description, opts, cfg).then(function (response) { - if (response instanceof Array) - resolve(response[0]); - else if (response === "right") - showPage(Math.min(x + 1, width - cols), y); - else if (response === "left") - showPage(Math.max(x - 1, 0), y); - else if (response === "up") - showPage(x, Math.max(y - 1, 0)); - else if (response === "down") - showPage(x, Math.min(y + 1, height - rows)); - else { - //Treat numbers as cancel - if (cfg.onCancel == "null") - resolve(null); - else if (cfg.onCancel == "reject") - reject("cancel"); - //otherwise, just let the promise hang - } - }); - } - showPage(Math.min((_a = cfg.x) !== null && _a !== void 0 ? _a : 0, width - cols), Math.min((_b = cfg.y) !== null && _b !== void 0 ? _b : 0, height - rows)); - return promise; - }, - pagedListButtons: function (target, title, description, options, _a) { - var _b; - var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, cfg = __rest(_a, ["rowsPerPage", "columns"]); - //Generate pages - var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options, columns), rowsPerPage); - if (pages.length <= 1) - return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); - return exports.Menu.pages(target, title, description, pages, cfg); - }, - pagedList: function (target, title, description, options, _a) { - var _b; - if (_a === void 0) { _a = {}; } - var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, _e = _a.optionStringifier, optionStringifier = _e === void 0 ? String : _e, cfg = __rest(_a, ["rowsPerPage", "columns", "optionStringifier"]); - //Generate pages - var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options.map(function (o) { return ({ data: o, get text() { return optionStringifier(o); } }); }), columns), rowsPerPage); - if (pages.length <= 1) - return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); - return exports.Menu.pages(target, title, description, pages, cfg); - } -}; +"use strict"; +/* eslint-disable @typescript-eslint/array-type */ +/* +Copyright © BalaM314, 2026. All Rights Reserved. +This file contains the menu framework. +For usage information, see docs/framework-usage-guide.md +For maintenance information, see docs/frameworks.md +*/ +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); +}; +var __read = (this && this.__read) || function (o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; +}; +var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.listeners = exports.Menu = void 0; +exports.registerListeners = registerListeners; +var commands_1 = require("/frameworks/commands"); +var funcs_1 = require("/funcs"); +var players_1 = require("/players"); +var promise_1 = require("/promise"); +var utils_1 = require("/utils"); +/** Used to change the behavior of adding another menu when being run in a menu callback. */ +var isInMenuCallback = false; +/** Stores a mapping from name to the numeric id of a listener that has been registered. */ +var registeredListeners = {}; +exports.listeners = registeredListeners; +/** Stores all listeners in use by fish-commands. */ +var listeners = { + generic: function (player, option) { + var fishSender = players_1.FishPlayer.get(player); + var prevCallback = fishSender.activeMenus.shift(); + if (!prevCallback) + return; //No menu to process, do nothing + isInMenuCallback = true; + prevCallback.callback(option); + isInMenuCallback = false; + }, + none: function (player, option) { + //do nothing + } +}; +/** Registers all listeners, should be called on server load. */ +function registerListeners() { + var e_1, _a; + var _b; + try { + for (var _c = __values(Object.entries(listeners)), _d = _c.next(); !_d.done; _d = _c.next()) { + var _e = __read(_d.value, 2), key = _e[0], listener = _e[1]; + (_b = registeredListeners[key]) !== null && _b !== void 0 ? _b : (registeredListeners[key] = Menus.registerMenu(listener)); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_1) throw e_1.error; } + } +} +exports.Menu = { + /** Displays a menu to a player, returning a Promise. */ + raw: function (title, description, arrangedOptions, target, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b.optionStringifier, optionStringifier = _c === void 0 ? String : _c, _d = _b.onCancel, onCancel = _d === void 0 ? "ignore" : _d, _e = _b.cancelOptionId, cancelOptionId = _e === void 0 ? -1 : _e; + var _f = promise_1.Promise.withResolvers(), promise = _f.promise, reject = _f.reject, resolve = _f.resolve; + //The target fishPlayer has a property called activeMenu, which stores information about the last menu triggered. + //If menu() is being called from a menu calback, add it to the front of the queue so it is processed before any other menus. + //Otherwise, two multi-step menus queued together would alternate, which would confuse the player. + target.activeMenus[isInMenuCallback ? "unshift" : "push"]({ callback: function (option) { + //Additional permission validation could be done here, but the only way that callback() can be called is if the above statement executed, + //and on sensitive menus such as the stop menu, the only way to reach that is if menu() was called by the /stop command, + //which already checks permissions. + //Additionally, the callback is cleared by the generic menu listener after it is executed. + try { + var options = arrangedOptions.flat(); + //We do need to validate option though, as it can be any number. + if (option === -1 || option === cancelOptionId || !(option in options)) { + //Consider any invalid option to be a cancellation + if (onCancel == "null") + resolve(null); + else if (onCancel == "reject") + reject("cancel"); + else + return; + } + else { + resolve(options[option]); + } + } + catch (err) { + if (err instanceof commands_1.CommandError) { + //If the error is a command error, then just outputFail + (0, utils_1.outputFail)(err.data, target); + } + else { + target.sendMessage("[scarlet]\u274C An error occurred while executing the command!"); + if (target.hasPerm("seeErrorMessages")) + target.sendMessage((0, funcs_1.parseError)(err)); + Log.err("Unhandled error in menu callback: ".concat(target.cleanedName, " submitted menu \"").concat(title, "\" \"").concat(description, "\"")); + Log.err(err); + } + } + } }); + var i = 0; + var stringifiedOptions = arrangedOptions.map(function (r) { return r.map(function (item) { + if (i === cancelOptionId) + return item; + i++; + return optionStringifier(item); + }); }); + Call.menu(target.con, registeredListeners.generic, title, description, stringifiedOptions); + return promise; + }, + /** Displays a menu to a player, returning a Promise. Arranges options into a 2D array, and can add a Cancel option. */ + menu: function (title, description, options, target, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b.includeCancel, includeCancel = _c === void 0 ? false : _c, _d = _b.optionStringifier, optionStringifier = _d === void 0 ? String : _d, _e = _b.columns, columns = _e === void 0 ? 3 : _e, _f = _b.onCancel, onCancel = _f === void 0 ? "ignore" : _f, _g = _b.cancelOptionId, cancelOptionId = _g === void 0 ? -1 : _g; + //Set up the 2D array of options, and maybe add cancel + //Call.menu() with [[]] will cause a client crash, make sure to pass [] instead + var arrangedOptions = (options.length == 0 && !includeCancel) ? [] : (0, funcs_1.to2DArray)(options, columns); + if (includeCancel) { + arrangedOptions.push(["[red]Cancel[]"]); + //This is safe because cancelOptionId is set, + //so the handler will never get called with "Cancel". + cancelOptionId = options.length; + } + return exports.Menu.raw(title, description, arrangedOptions, target, { + cancelOptionId: cancelOptionId, + onCancel: onCancel, + optionStringifier: optionStringifier + }); + }, + /** Rejects with a CommandError if the user chooses to cancel. */ + confirm: function (target, description, _a) { + var _b = _a === void 0 ? {} : _a, _c = _b.cancelOutput, cancelOutput = _c === void 0 ? "Cancelled." : _c, _d = _b.title, title = _d === void 0 ? "Confirm" : _d, _e = _b.confirmText, confirmText = _e === void 0 ? "[green]Confirm" : _e, _f = _b.cancelText, cancelText = _f === void 0 ? "[red]Cancel" : _f; + return exports.Menu.menu(title, description, [confirmText, cancelText], target, { onCancel: "reject", cancelOptionId: 1 }).catch(function (e) { + if (e === "cancel") + (0, commands_1.fail)(cancelOutput); + throw e; //some random error, rethrow it + }); + }, + /** Same as confirm(), but with inverted colors, for potentially dangerous actions. */ + confirmDangerous: function (target, description, _a) { + if (_a === void 0) { _a = {}; } + var _b = _a.confirmText, confirmText = _b === void 0 ? "[red]Confirm" : _b, _c = _a.cancelText, cancelText = _c === void 0 ? "[green]Cancel" : _c, rest = __rest(_a, ["confirmText", "cancelText"]); + return exports.Menu.confirm(target, description, __assign({ cancelText: cancelText, confirmText: confirmText }, rest)); + }, + buttons: function (target, title, description, options, cfg) { + if (cfg === void 0) { cfg = {}; } + return exports.Menu.raw(title, description, options, target, __assign(__assign({}, cfg), { optionStringifier: function (o) { return o.text; } })).then(function (o) { return o === null || o === void 0 ? void 0 : o.data; }); + }, + pages: function (target, title, description, options, cfg) { + var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; + function showPage(index) { + var opts = __spreadArray(__spreadArray([], __read(options[index].map(function (r) { return r.map(function (d) { return ({ text: d.text, data: [d.data] }); }); })), false), [ + [ + { data: "left", text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, + { data: "numbers", text: "[accent]".concat(index + 1, "/").concat(options.length) }, + { data: "right", text: "[".concat(index == options.length - 1 ? "gray" : "accent", "]-->") } + ] + ], false); + exports.Menu.buttons(target, title, description, opts, cfg).then(function (response) { + if (response instanceof Array) + resolve(response[0]); + else if (response === "right") + showPage(Math.min(index + 1, options.length - 1)); + else if (response === "left") + showPage(Math.max(index - 1, 0)); + else { + //Treat numbers as cancel + if (cfg.onCancel == "null") + resolve(null); + else if (cfg.onCancel == "reject") + reject("cancel"); + //otherwise, just let the promise hang + } + }); + } + showPage(0); + return promise; + }, + textPages: function (target, pages, cfg) { + if (cfg === void 0) { cfg = {}; } + var _a = promise_1.Promise.withResolvers(), promise = _a.promise, reject = _a.reject, resolve = _a.resolve; + var pageSkipSize = Math.max(Math.floor(pages.length / 8), 5); + function showPage(index) { + var opts = [ + [ + { data: ["left", pageSkipSize], text: "[".concat(index == 0 ? "gray" : "accent", "]<<<") }, + { data: ["left", 1], text: "[".concat(index == 0 ? "gray" : "accent", "]<--") }, + { data: ["right", 1], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]-->") }, + { data: ["right", pageSkipSize], text: "[".concat(index == pages.length - 1 ? "gray" : "accent", "]>>>") }, + ], + [ + { data: ["numbers"], text: "[accent]Page ".concat(index + 1, "/").concat(pages.length) }, + { data: ["cancel"], text: "[red]Close" }, + ] + ]; + exports.Menu.buttons(target, pages[index][0], pages[index][1](), opts, cfg).then(function (response) { + if ((response === null || response === void 0 ? void 0 : response[0]) === "right") + showPage(Math.min(index + response[1], pages.length - 1)); + else if ((response === null || response === void 0 ? void 0 : response[0]) === "left") + showPage(Math.max(index - response[1], 0)); + else { + //Treat numbers as cancel + if (cfg.onCancel == "null") + resolve(null); + else if (cfg.onCancel == "reject") + reject("cancel"); + //otherwise, just let the promise hang + } + }); + } + var index = (function () { + if (cfg.startPage == undefined) + return 0; + if (typeof cfg.startPage === 'number') { + if (cfg.startPage < 0) + return 0; + return cfg.startPage; + } + var index = pages.findIndex(function (_a) { + var _b = __read(_a, 1), title = _b[0]; + return title === cfg.startPage; + }); + if (index === -1) + return 0; + return index; + })(); + showPage(index); + return promise; + }, + scroll: function (target, title, description, options, cfg) { + var _a, _b; + if (cfg === void 0) { cfg = {}; } + var _c = promise_1.Promise.withResolvers(), promise = _c.promise, reject = _c.reject, resolve = _c.resolve; + var _d = cfg.rows, rows = _d === void 0 ? 5 : _d, _e = cfg.columns, cols = _e === void 0 ? 5 : _e; + var height = options.length; + var width = options[0].length; + function showPage(x, y) { + var _a, _b; + var opts = __spreadArray(__spreadArray([], __read(options.slice(y, y + rows).map(function (r) { return r.slice(x, x + cols).map(function (d) { return ({ text: d.text, data: [d.data] }); }); })), false), [ + [ + { data: "blank", text: "" }, + { data: "up", text: "[".concat(y == 0 ? "gray" : "accent", "]^\n|") }, + { data: "blank", text: "" }, + ], [ + { data: "left", text: "[".concat(x == 0 ? "gray" : "accent", "]<--") }, + { data: "blank", text: (_b = (_a = cfg.getCenterText) === null || _a === void 0 ? void 0 : _a.call(cfg, x, y)) !== null && _b !== void 0 ? _b : '' }, + { data: "right", text: "[".concat(x == width - cols ? "gray" : "accent", "]-->") } + ], [ + { data: "blank", text: "" }, + { data: "down", text: "[".concat(y == height - rows ? "gray" : "accent", "]|\nV") }, + { data: "blank", text: "" }, + ] + ], false); + exports.Menu.buttons(target, title, description, opts, cfg).then(function (response) { + if (response instanceof Array) + resolve(response[0]); + else if (response === "right") + showPage(Math.min(x + 1, width - cols), y); + else if (response === "left") + showPage(Math.max(x - 1, 0), y); + else if (response === "up") + showPage(x, Math.max(y - 1, 0)); + else if (response === "down") + showPage(x, Math.min(y + 1, height - rows)); + else { + //Treat numbers as cancel + if (cfg.onCancel == "null") + resolve(null); + else if (cfg.onCancel == "reject") + reject("cancel"); + //otherwise, just let the promise hang + } + }); + } + showPage(Math.min((_a = cfg.x) !== null && _a !== void 0 ? _a : 0, width - cols), Math.min((_b = cfg.y) !== null && _b !== void 0 ? _b : 0, height - rows)); + return promise; + }, + pagedListButtons: function (target, title, description, options, _a) { + var _b; + var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, cfg = __rest(_a, ["rowsPerPage", "columns"]); + //Generate pages + var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options, columns), rowsPerPage); + if (pages.length <= 1) + return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); + return exports.Menu.pages(target, title, description, pages, cfg); + }, + pagedList: function (target, title, description, options, _a) { + var _b; + if (_a === void 0) { _a = {}; } + var _c = _a.rowsPerPage, rowsPerPage = _c === void 0 ? 10 : _c, _d = _a.columns, columns = _d === void 0 ? 3 : _d, _e = _a.optionStringifier, optionStringifier = _e === void 0 ? String : _e, cfg = __rest(_a, ["rowsPerPage", "columns", "optionStringifier"]); + //Generate pages + var pages = (0, funcs_1.to2DArray)((0, funcs_1.to2DArray)(options.map(function (o) { return ({ data: o, get text() { return optionStringifier(o); } }); }), columns), rowsPerPage); + if (pages.length <= 1) + return exports.Menu.buttons(target, title, description, (_b = pages[0]) !== null && _b !== void 0 ? _b : [], cfg); + return exports.Menu.pages(target, title, description, pages, cfg); + } +}; diff --git a/src/commands/general.ts b/src/commands/general.ts index 178133c1..91bdf18a 100644 --- a/src/commands/general.ts +++ b/src/commands/general.ts @@ -8,7 +8,7 @@ import { FishServer, Gamemode, rules, text } from "/config"; import { command, commandList, fail, formatArg, Perm, Req } from "/frameworks/commands"; import type { FishCommandData } from "/frameworks/commands/types"; import { Menu } from "/frameworks/menus"; -import { capitalizeText, Duration, escapeTextDiscord, StringBuilder, StringIO, to2DArray } from "/funcs"; +import { capitalizeText, Duration, escapeTextDiscord, StringBuilder, StringIO, to2DArray, setToArray } from "/funcs"; import { FishEvents, fishPlugin, fishState, ipPortPattern, recentWhispers, tileHistory, uuidPattern } from "/globals"; import { FMap } from "/maps"; import { FishPlayer } from "/players"; @@ -1133,5 +1133,73 @@ Win rate: ${target.stats.gamesWon / target.stats.gamesFinished}` unit.add(); outputSuccess(f`Spawned a ${args.type} that is partly a ${args.base}.`); } + }, + + report: { + args: [], + description: 'Report a player to staff with a selected reason.', + perm: Perm.play, + requirements: [Req.cooldown(4000)], + async handler({sender, outputSuccess, outputFail, f}) { + const onlinePlayers = setToArray(Groups.player); + if(onlinePlayers.length === 0){ + outputFail('No players online to report.'); + return; + } + const target = await Menu.menu( + 'Report Player', + 'Select a player to report.', + onlinePlayers, + sender, + { + includeCancel: true, + optionStringifier: player => player.name + } + ).catch(() => { + outputFail('Report cancelled.'); + return; + }); + if(!target) return; + if(target === sender.player){ + outputFail('You cannot report yourself.'); + return; + } + + const baseReasons = [ + 'Griefing', + 'Harassment', + 'Cheating / Exploiting', + 'Spam', + 'Trolling', + 'Other', + ]; + const reasons = target.admin ? [...baseReasons, 'Admin Abuse'] : baseReasons; + const reason = await Menu.menu( + 'Report Reason', + `Select a reason for reporting [accent]${target.name}[]`, + reasons, + sender, + { includeCancel: true } + ).catch(() => { + outputFail('Report cancelled.'); + return; + }); + if(!reason) return; + + const issuerName = sender.player?.name ?? 'Unknown'; + const targetName = target.name; + const serverName = Gamemode.name(); + const message = + `[Report] Server: ${serverName}\n` + + `Issuer: ${Strings.stripColors(issuerName)}\n` + + `Target: ${Strings.stripColors(targetName)}${target.admin ? ' (Admin)' : ''}\n` + + `Reason: ${reason}`; + + api.sendStaffMessage(message, issuerName, (sent) => { + if(sent) outputSuccess(f`Report sent to staff: ${targetName} for "${reason}".`); + else outputFail('Failed to send report to staff. Please try again later.'); + }); + }, } + }); diff --git a/src/config.ts b/src/config.ts index 5f2ed12a..83e0e63d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -38,6 +38,7 @@ export const bannedWords: { "fanum tax", "gyatt", "rizz", "skibidi", //With love, DarthScion //>:( -dart "uwu", //lol + // why?? why the uwu??? -starkatt "nig"+"ger", "nig"+"ga", "niger", "ni8"+"8er", "nig"+"gre", //our apologies to citizens of the Republic of Niger "негр", "ниггер",