package commands import ( "fmt" "strings" ) // RegisterAdminCommands registers all admin-level commands func RegisterAdminCommands(cm *CommandManager) error { commands := []*Command{ // Player management commands { Name: "kick", Type: CommandTypeAdmin, Description: "Kicks a player from the server", Usage: "/kick [reason]", RequiredLevel: AdminLevelGM, Handler: HandleKick, }, { Name: "ban", Type: CommandTypeAdmin, Description: "Bans a player from the server", Usage: "/ban [duration] [reason]", RequiredLevel: AdminLevelAdmin, Handler: HandleBan, }, { Name: "unban", Type: CommandTypeAdmin, Description: "Unbans a player", Usage: "/unban ", RequiredLevel: AdminLevelAdmin, Handler: HandleUnban, }, { Name: "summon", Type: CommandTypeAdmin, Description: "Summons a player to your location", Usage: "/summon ", RequiredLevel: AdminLevelGM, Handler: HandleSummon, }, { Name: "goto", Type: CommandTypeAdmin, Description: "Teleports you to a player or location", Usage: "/goto ", RequiredLevel: AdminLevelGM, Handler: HandleGoto, }, { Name: "zone", Type: CommandTypeAdmin, Description: "Zone management commands", Usage: "/zone ", RequiredLevel: AdminLevelGM, Handler: HandleZonePlayer, }, // Communication commands { Name: "broadcast", Type: CommandTypeAdmin, Description: "Broadcasts a message to all players", Usage: "/broadcast ", RequiredLevel: AdminLevelGM, Handler: HandleBroadcast, }, { Name: "announce", Type: CommandTypeAdmin, Description: "Announces a message to all players", Usage: "/announce ", RequiredLevel: AdminLevelGM, Handler: HandleAnnounce, }, // Spawn management commands { Name: "spawn", Type: CommandTypeAdmin, Description: "Spawn management commands", Usage: "/spawn [options]", RequiredLevel: AdminLevelGM, Handler: HandleSpawn, }, { Name: "npc", Type: CommandTypeAdmin, Description: "NPC management commands", Usage: "/npc [options]", RequiredLevel: AdminLevelGM, Handler: HandleNPC, }, // Item commands { Name: "item", Type: CommandTypeAdmin, Description: "Item management commands", Usage: "/item [options]", RequiredLevel: AdminLevelGM, Handler: HandleItem, }, { Name: "giveitem", Type: CommandTypeAdmin, Description: "Gives an item to a player", Usage: "/giveitem [quantity]", RequiredLevel: AdminLevelGM, Handler: HandleGiveItem, }, // Character modification commands { Name: "modify", Type: CommandTypeAdmin, Description: "Modification commands", Usage: "/modify [options]", RequiredLevel: AdminLevelAdmin, Handler: HandleModify, }, { Name: "setlevel", Type: CommandTypeAdmin, Description: "Sets a player's level", Usage: "/setlevel ", RequiredLevel: AdminLevelGM, Handler: HandleSetLevel, }, { Name: "setclass", Type: CommandTypeAdmin, Description: "Sets a player's class", Usage: "/setclass ", RequiredLevel: AdminLevelAdmin, Handler: HandleSetClass, }, // Server management commands { Name: "reload", Type: CommandTypeAdmin, Description: "Reloads server data", Usage: "/reload ", RequiredLevel: AdminLevelAdmin, Handler: HandleReload, }, { Name: "shutdown", Type: CommandTypeAdmin, Description: "Shuts down the server", Usage: "/shutdown [time_minutes] [reason]", RequiredLevel: AdminLevelAdmin, Handler: HandleShutdown, }, { Name: "cancelshutdown", Type: CommandTypeAdmin, Description: "Cancels a scheduled shutdown", Usage: "/cancelshutdown", RequiredLevel: AdminLevelAdmin, Handler: HandleCancelShutdown, }, // GM utility commands { Name: "invisible", Type: CommandTypeAdmin, Description: "Toggles GM invisibility", Usage: "/invisible", RequiredLevel: AdminLevelGM, Handler: HandleInvisible, }, { Name: "invulnerable", Type: CommandTypeAdmin, Description: "Toggles invulnerability", Usage: "/invulnerable", RequiredLevel: AdminLevelGM, Handler: HandleInvulnerable, }, { Name: "speed", Type: CommandTypeAdmin, Description: "Sets movement speed", Usage: "/speed ", RequiredLevel: AdminLevelGM, Handler: HandleSpeed, }, { Name: "flymode", Type: CommandTypeAdmin, Description: "Toggles fly mode", Usage: "/flymode", RequiredLevel: AdminLevelGM, Handler: HandleFlyMode, }, // Information commands { Name: "info", Type: CommandTypeAdmin, Description: "Shows detailed information about target", Usage: "/info [target]", RequiredLevel: AdminLevelGM, Handler: HandleInfo, }, { Name: "version", Type: CommandTypeAdmin, Description: "Shows server version information", Usage: "/version", RequiredLevel: AdminLevelGM, Handler: HandleVersion, }, // Testing commands { Name: "test", Type: CommandTypeAdmin, Description: "Testing command for development", Usage: "/test [parameters]", RequiredLevel: AdminLevelAdmin, Handler: HandleTest, }, } for _, cmd := range commands { if err := cm.Register(cmd); err != nil { return fmt.Errorf("failed to register admin command %s: %w", cmd.Name, err) } } // Register subcommands if err := registerModifySubcommands(cm); err != nil { return fmt.Errorf("failed to register modify subcommands: %w", err) } if err := registerSpawnSubcommands(cm); err != nil { return fmt.Errorf("failed to register spawn subcommands: %w", err) } return nil } // registerModifySubcommands registers subcommands for the /modify command func registerModifySubcommands(cm *CommandManager) error { subcommands := []*Command{ { Name: "character", Type: CommandTypeAdmin, Description: "Modifies character attributes", Usage: "/modify character ", RequiredLevel: AdminLevelAdmin, Handler: HandleModifyCharacter, }, { Name: "spawn", Type: CommandTypeAdmin, Description: "Modifies spawn attributes", Usage: "/modify spawn ", RequiredLevel: AdminLevelAdmin, Handler: HandleModifySpawn, }, { Name: "zone", Type: CommandTypeAdmin, Description: "Modifies zone attributes", Usage: "/modify zone ", RequiredLevel: AdminLevelAdmin, Handler: HandleModifyZone, }, } for _, subcmd := range subcommands { if err := cm.RegisterSubCommand("modify", subcmd); err != nil { return err } } return nil } // registerSpawnSubcommands registers subcommands for the /spawn command func registerSpawnSubcommands(cm *CommandManager) error { subcommands := []*Command{ { Name: "create", Type: CommandTypeAdmin, Description: "Creates a new spawn", Usage: "/spawn create [options]", RequiredLevel: AdminLevelGM, Handler: HandleSpawnCreate, }, { Name: "remove", Type: CommandTypeAdmin, Description: "Removes a spawn", Usage: "/spawn remove ", RequiredLevel: AdminLevelGM, Handler: HandleSpawnRemove, }, { Name: "list", Type: CommandTypeAdmin, Description: "Lists spawns in area", Usage: "/spawn list [radius]", RequiredLevel: AdminLevelGM, Handler: HandleSpawnList, }, } for _, subcmd := range subcommands { if err := cm.RegisterSubCommand("spawn", subcmd); err != nil { return err } } return nil } // HandleKick handles the /kick command func HandleKick(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(1, -1); err != nil { ctx.AddErrorMessage("Usage: /kick [reason]") return nil } playerName := ctx.Arguments[0] reason := "Kicked by administrator" if ctx.ArgumentCount() > 1 { reason = ctx.GetRemainingArguments(1) } // TODO: Implement actual kick functionality ctx.AddStatusMessage(fmt.Sprintf("Kicked player '%s' with reason: %s", playerName, reason)) // TODO: Find and disconnect player // if player := world.FindPlayerByName(playerName); player != nil { // player.Disconnect(reason) // broadcast to admins about kick // } else { // ctx.AddErrorMessage(fmt.Sprintf("Player '%s' not found", playerName)) // } return nil } // HandleBan handles the /ban command func HandleBan(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(1, -1); err != nil { ctx.AddErrorMessage("Usage: /ban [duration] [reason]") return nil } playerName := ctx.Arguments[0] duration := "permanent" reason := "Banned by administrator" if ctx.ArgumentCount() > 1 { duration = ctx.Arguments[1] } if ctx.ArgumentCount() > 2 { reason = ctx.GetRemainingArguments(2) } // TODO: Implement actual ban functionality ctx.AddStatusMessage(fmt.Sprintf("Banned player '%s' for %s with reason: %s", playerName, duration, reason)) return nil } // HandleUnban handles the /unban command func HandleUnban(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(1, 1); err != nil { ctx.AddErrorMessage("Usage: /unban ") return nil } playerName := ctx.Arguments[0] // TODO: Implement actual unban functionality ctx.AddStatusMessage(fmt.Sprintf("Unbanned player '%s'", playerName)) return nil } // HandleSummon handles the /summon command func HandleSummon(ctx *CommandContext) error { if err := ctx.RequirePlayer(); err != nil { return err } if err := ctx.ValidateArgumentCount(1, 1); err != nil { ctx.AddErrorMessage("Usage: /summon ") return nil } targetName := ctx.Arguments[0] // TODO: Implement actual summon functionality ctx.AddStatusMessage(fmt.Sprintf("Summoned player '%s' to your location", targetName)) // TODO: Find target player and teleport to admin location // if target := world.FindPlayerByName(targetName); target != nil { // adminPos := ctx.Player.GetPosition() // target.Teleport(adminPos.X, adminPos.Y, adminPos.Z, ctx.Zone.GetID()) // } else { // ctx.AddErrorMessage(fmt.Sprintf("Player '%s' not found", targetName)) // } return nil } // HandleGoto handles the /goto command func HandleGoto(ctx *CommandContext) error { if err := ctx.RequirePlayer(); err != nil { return err } if err := ctx.ValidateArgumentCount(1, 3); err != nil { ctx.AddErrorMessage("Usage: /goto or /goto ") return nil } if ctx.ArgumentCount() == 1 { // Go to player targetName := ctx.Arguments[0] // TODO: Implement teleport to player ctx.AddStatusMessage(fmt.Sprintf("Teleported to player '%s'", targetName)) } else if ctx.ArgumentCount() == 3 { // Go to coordinates x := ctx.GetArgumentFloat(0, 0) y := ctx.GetArgumentFloat(1, 0) z := ctx.GetArgumentFloat(2, 0) // TODO: Implement teleport to coordinates ctx.AddStatusMessage(fmt.Sprintf("Teleported to coordinates (%.2f, %.2f, %.2f)", x, y, z)) } else { ctx.AddErrorMessage("Usage: /goto or /goto ") } return nil } // HandleZonePlayer handles the /zone command func HandleZonePlayer(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(2, 2); err != nil { ctx.AddErrorMessage("Usage: /zone ") return nil } playerName := ctx.Arguments[0] zoneName := ctx.Arguments[1] // TODO: Implement zone transfer ctx.AddStatusMessage(fmt.Sprintf("Zoned player '%s' to '%s'", playerName, zoneName)) return nil } // HandleBroadcast handles the /broadcast command func HandleBroadcast(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(1, -1); err != nil { ctx.AddErrorMessage("Usage: /broadcast ") return nil } message := ctx.RawArguments adminName := ctx.GetPlayerName() // TODO: Implement server-wide broadcast ctx.AddMessage(ChannelBroadcast, ColorYellow, fmt.Sprintf("[BROADCAST] %s: %s", adminName, message)) return nil } // HandleAnnounce handles the /announce command func HandleAnnounce(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(1, -1); err != nil { ctx.AddErrorMessage("Usage: /announce ") return nil } message := ctx.RawArguments // TODO: Implement server announcement ctx.AddMessage(ChannelBroadcast, ColorRed, fmt.Sprintf("[ANNOUNCEMENT] %s", message)) return nil } // HandleSpawn handles the main /spawn command func HandleSpawn(ctx *CommandContext) error { if ctx.ArgumentCount() == 0 { ctx.AddErrorMessage("Usage: /spawn [options]") ctx.AddErrorMessage("Use /spawn for specific help") return nil } // Subcommands are handled by the command manager return fmt.Errorf("spawn subcommand '%s' not found", ctx.Arguments[0]) } // HandleSpawnCreate handles the /spawn create subcommand func HandleSpawnCreate(ctx *CommandContext) error { if err := ctx.RequirePlayer(); err != nil { return err } if err := ctx.ValidateArgumentCount(1, -1); err != nil { ctx.AddErrorMessage("Usage: /spawn create [name] [level]") return nil } spawnType := ctx.Arguments[0] spawnName := "New Spawn" spawnLevel := 1 if ctx.ArgumentCount() > 1 { spawnName = ctx.Arguments[1] } if ctx.ArgumentCount() > 2 { spawnLevel = ctx.GetArgumentInt(2, 1) } // TODO: Implement spawn creation ctx.AddStatusMessage(fmt.Sprintf("Created %s spawn '%s' (level %d) at your location", spawnType, spawnName, spawnLevel)) return nil } // HandleSpawnRemove handles the /spawn remove subcommand func HandleSpawnRemove(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(1, 1); err != nil { ctx.AddErrorMessage("Usage: /spawn remove ") return nil } spawnID := ctx.GetArgumentInt(0, 0) if spawnID <= 0 { ctx.AddErrorMessage("Invalid spawn ID") return nil } // TODO: Implement spawn removal ctx.AddStatusMessage(fmt.Sprintf("Removed spawn with ID %d", spawnID)) return nil } // HandleSpawnList handles the /spawn list subcommand func HandleSpawnList(ctx *CommandContext) error { if err := ctx.RequirePlayer(); err != nil { return err } radius := 50.0 // Default radius if ctx.ArgumentCount() > 0 { radius = ctx.GetArgumentFloat(0, 50.0) } // TODO: Implement spawn listing ctx.AddStatusMessage(fmt.Sprintf("Spawns within %.1f units:", radius)) ctx.AddStatusMessage(" ID: 1, Name: Test NPC, Level: 10, Distance: 5.2") ctx.AddStatusMessage("Total: 1 spawn found") return nil } // HandleNPC handles the /npc command func HandleNPC(ctx *CommandContext) error { if ctx.ArgumentCount() == 0 { ctx.AddErrorMessage("Usage: /npc [options]") return nil } subCommand := strings.ToLower(ctx.Arguments[0]) switch subCommand { case "create": // TODO: Implement NPC creation ctx.AddStatusMessage("NPC creation (not yet implemented)") case "remove": // TODO: Implement NPC removal ctx.AddStatusMessage("NPC removal (not yet implemented)") case "edit": // TODO: Implement NPC editing ctx.AddStatusMessage("NPC editing (not yet implemented)") default: ctx.AddErrorMessage("Usage: /npc [options]") } return nil } // HandleItem handles the /item command func HandleItem(ctx *CommandContext) error { if ctx.ArgumentCount() == 0 { ctx.AddErrorMessage("Usage: /item [options]") return nil } subCommand := strings.ToLower(ctx.Arguments[0]) switch subCommand { case "give": if ctx.ArgumentCount() < 3 { ctx.AddErrorMessage("Usage: /item give [quantity]") return nil } return handleItemGive(ctx) case "remove": // TODO: Implement item removal ctx.AddStatusMessage("Item removal (not yet implemented)") case "create": // TODO: Implement item creation ctx.AddStatusMessage("Item creation (not yet implemented)") default: ctx.AddErrorMessage("Usage: /item [options]") } return nil } // handleItemGive handles giving items to players func handleItemGive(ctx *CommandContext) error { playerName := ctx.Arguments[1] itemID := ctx.GetArgumentInt(2, 0) quantity := ctx.GetArgumentInt(3, 1) if itemID <= 0 { ctx.AddErrorMessage("Invalid item ID") return nil } // TODO: Implement item giving ctx.AddStatusMessage(fmt.Sprintf("Gave %d x item %d to player '%s'", quantity, itemID, playerName)) return nil } // HandleGiveItem handles the /giveitem command func HandleGiveItem(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(2, 3); err != nil { ctx.AddErrorMessage("Usage: /giveitem [quantity]") return nil } playerName := ctx.Arguments[0] itemID := ctx.GetArgumentInt(1, 0) quantity := ctx.GetArgumentInt(2, 1) if itemID <= 0 { ctx.AddErrorMessage("Invalid item ID") return nil } // TODO: Implement item giving ctx.AddStatusMessage(fmt.Sprintf("Gave %d x item %d to player '%s'", quantity, itemID, playerName)) return nil } // HandleModify handles the main /modify command func HandleModify(ctx *CommandContext) error { if ctx.ArgumentCount() == 0 { ctx.AddErrorMessage("Usage: /modify [options]") return nil } // Subcommands are handled by the command manager return fmt.Errorf("modify subcommand '%s' not found", ctx.Arguments[0]) } // HandleModifyCharacter handles the /modify character subcommand func HandleModifyCharacter(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(3, 3); err != nil { ctx.AddErrorMessage("Usage: /modify character ") return nil } playerName := ctx.Arguments[0] attribute := ctx.Arguments[1] value := ctx.Arguments[2] // TODO: Implement character modification ctx.AddStatusMessage(fmt.Sprintf("Modified %s's %s to %s", playerName, attribute, value)) return nil } // HandleModifySpawn handles the /modify spawn subcommand func HandleModifySpawn(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(3, 3); err != nil { ctx.AddErrorMessage("Usage: /modify spawn ") return nil } spawnID := ctx.GetArgumentInt(0, 0) attribute := ctx.Arguments[1] value := ctx.Arguments[2] if spawnID <= 0 { ctx.AddErrorMessage("Invalid spawn ID") return nil } // TODO: Implement spawn modification ctx.AddStatusMessage(fmt.Sprintf("Modified spawn %d's %s to %s", spawnID, attribute, value)) return nil } // HandleModifyZone handles the /modify zone subcommand func HandleModifyZone(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(3, 3); err != nil { ctx.AddErrorMessage("Usage: /modify zone ") return nil } zoneID := ctx.GetArgumentInt(0, 0) attribute := ctx.Arguments[1] value := ctx.Arguments[2] if zoneID <= 0 { ctx.AddErrorMessage("Invalid zone ID") return nil } // TODO: Implement zone modification ctx.AddStatusMessage(fmt.Sprintf("Modified zone %d's %s to %s", zoneID, attribute, value)) return nil } // HandleSetLevel handles the /setlevel command func HandleSetLevel(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(2, 2); err != nil { ctx.AddErrorMessage("Usage: /setlevel ") return nil } playerName := ctx.Arguments[0] level := ctx.GetArgumentInt(1, 1) if level < 1 || level > 100 { ctx.AddErrorMessage("Level must be between 1 and 100") return nil } // TODO: Implement level setting ctx.AddStatusMessage(fmt.Sprintf("Set %s's level to %d", playerName, level)) return nil } // HandleSetClass handles the /setclass command func HandleSetClass(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(2, 2); err != nil { ctx.AddErrorMessage("Usage: /setclass ") return nil } playerName := ctx.Arguments[0] classID := ctx.GetArgumentInt(1, 0) if classID < 0 { ctx.AddErrorMessage("Invalid class ID") return nil } // TODO: Implement class setting ctx.AddStatusMessage(fmt.Sprintf("Set %s's class to %d", playerName, classID)) return nil } // HandleReload handles the /reload command func HandleReload(ctx *CommandContext) error { if err := ctx.ValidateArgumentCount(1, 1); err != nil { ctx.AddErrorMessage("Usage: /reload ") return nil } reloadType := strings.ToLower(ctx.Arguments[0]) switch reloadType { case "spells": ctx.AddStatusMessage("Reloaded spells") case "quests": ctx.AddStatusMessage("Reloaded quests") case "zones": ctx.AddStatusMessage("Reloaded zones") case "items": ctx.AddStatusMessage("Reloaded items") case "all": ctx.AddStatusMessage("Reloaded all server data") default: ctx.AddErrorMessage("Usage: /reload ") } return nil } // HandleShutdown handles the /shutdown command func HandleShutdown(ctx *CommandContext) error { minutes := 5 // Default 5 minutes reason := "Server maintenance" if ctx.ArgumentCount() > 0 { minutes = ctx.GetArgumentInt(0, 5) } if ctx.ArgumentCount() > 1 { reason = ctx.GetRemainingArguments(1) } // TODO: Implement server shutdown ctx.AddStatusMessage(fmt.Sprintf("Server shutdown scheduled in %d minutes. Reason: %s", minutes, reason)) return nil } // HandleCancelShutdown handles the /cancelshutdown command func HandleCancelShutdown(ctx *CommandContext) error { // TODO: Implement shutdown cancellation ctx.AddStatusMessage("Server shutdown cancelled") return nil } // HandleInvisible handles the /invisible command func HandleInvisible(ctx *CommandContext) error { if err := ctx.RequirePlayer(); err != nil { return err } // TODO: Toggle GM invisibility ctx.AddStatusMessage("GM invisibility toggled") return nil } // HandleInvulnerable handles the /invulnerable command func HandleInvulnerable(ctx *CommandContext) error { if err := ctx.RequirePlayer(); err != nil { return err } // TODO: Toggle invulnerability ctx.AddStatusMessage("Invulnerability toggled") return nil } // HandleSpeed handles the /speed command func HandleSpeed(ctx *CommandContext) error { if err := ctx.RequirePlayer(); err != nil { return err } if err := ctx.ValidateArgumentCount(1, 1); err != nil { ctx.AddErrorMessage("Usage: /speed ") return nil } multiplier := ctx.GetArgumentFloat(0, 1.0) if multiplier < 0.1 || multiplier > 10.0 { ctx.AddErrorMessage("Speed multiplier must be between 0.1 and 10.0") return nil } // TODO: Set movement speed ctx.AddStatusMessage(fmt.Sprintf("Movement speed set to %.1fx", multiplier)) return nil } // HandleFlyMode handles the /flymode command func HandleFlyMode(ctx *CommandContext) error { if err := ctx.RequirePlayer(); err != nil { return err } // TODO: Toggle fly mode ctx.AddStatusMessage("Fly mode toggled") return nil } // HandleInfo handles the /info command func HandleInfo(ctx *CommandContext) error { // TODO: Show detailed target information targetName := ctx.GetTargetName() if targetName == "No Target" { ctx.AddErrorMessage("You must target something to get info") return nil } ctx.AddStatusMessage(fmt.Sprintf("Information for %s:", targetName)) ctx.AddStatusMessage(" ID: 12345") ctx.AddStatusMessage(" Level: 25") ctx.AddStatusMessage(" HP: 1250/1250") ctx.AddStatusMessage(" Position: 100.5, 200.3, 150.7") return nil } // HandleVersion handles the /version command func HandleVersion(ctx *CommandContext) error { // TODO: Get actual version information ctx.AddStatusMessage("EQ2Go Server Version 1.0.0") ctx.AddStatusMessage("Build: Development") ctx.AddStatusMessage("Go Version: go1.24.5") return nil } // HandleTest handles the /test command func HandleTest(ctx *CommandContext) error { ctx.AddStatusMessage("Test command executed successfully") if ctx.ArgumentCount() > 0 { ctx.AddStatusMessage(fmt.Sprintf("Arguments: %s", ctx.RawArguments)) } // TODO: Add actual test functionality for development return nil }