package commands import ( "fmt" "strconv" "strings" "eq2emu/internal/entity" "eq2emu/internal/spawn" ) // NewCommandContext creates a new command context func NewCommandContext(commandType CommandType, commandName string, arguments []string) *CommandContext { return &CommandContext{ CommandType: commandType, CommandName: commandName, Arguments: arguments, RawArguments: strings.Join(arguments, " "), Messages: make([]CommandMessage, 0), Results: make(map[string]any), } } // WithClient adds a client to the context func (ctx *CommandContext) WithClient(client ClientInterface) *CommandContext { ctx.Client = client if client != nil { ctx.Player = client.GetPlayer() ctx.Zone = client.GetZone() ctx.AdminLevel = client.GetAdminLevel() } return ctx } // WithPlayer adds a player to the context func (ctx *CommandContext) WithPlayer(player *entity.Entity) *CommandContext { ctx.Player = player return ctx } // WithTarget adds a target to the context func (ctx *CommandContext) WithTarget(target *spawn.Spawn) *CommandContext { ctx.Target = target return ctx } // WithZone adds a zone to the context func (ctx *CommandContext) WithZone(zone ZoneInterface) *CommandContext { ctx.Zone = zone return ctx } // WithAdminLevel sets the admin level for the context func (ctx *CommandContext) WithAdminLevel(level int) *CommandContext { ctx.AdminLevel = level return ctx } // GetArgument retrieves an argument by index func (ctx *CommandContext) GetArgument(index int) (string, bool) { if index < 0 || index >= len(ctx.Arguments) { return "", false } return ctx.Arguments[index], true } // GetArgumentInt retrieves an integer argument by index func (ctx *CommandContext) GetArgumentInt(index int, defaultValue int) int { if arg, exists := ctx.GetArgument(index); exists { if value, err := strconv.Atoi(arg); err == nil { return value } } return defaultValue } // GetArgumentFloat retrieves a float argument by index func (ctx *CommandContext) GetArgumentFloat(index int, defaultValue float64) float64 { if arg, exists := ctx.GetArgument(index); exists { if value, err := strconv.ParseFloat(arg, 64); err == nil { return value } } return defaultValue } // GetArgumentBool retrieves a boolean argument by index func (ctx *CommandContext) GetArgumentBool(index int, defaultValue bool) bool { if arg, exists := ctx.GetArgument(index); exists { switch strings.ToLower(arg) { case "true", "yes", "on", "1": return true case "false", "no", "off", "0": return false } } return defaultValue } // GetRemainingArguments gets all arguments from index onwards as a string func (ctx *CommandContext) GetRemainingArguments(fromIndex int) string { if fromIndex < 0 || fromIndex >= len(ctx.Arguments) { return "" } return strings.Join(ctx.Arguments[fromIndex:], " ") } // HasArgument checks if an argument exists at the given index func (ctx *CommandContext) HasArgument(index int) bool { _, exists := ctx.GetArgument(index) return exists } // ArgumentCount returns the number of arguments func (ctx *CommandContext) ArgumentCount() int { return len(ctx.Arguments) } // AddMessage adds a message to be sent to the client func (ctx *CommandContext) AddMessage(channel, color int, message string) { ctx.mutex.Lock() defer ctx.mutex.Unlock() ctx.Messages = append(ctx.Messages, CommandMessage{ Channel: channel, Color: color, Message: message, }) } // AddDefaultMessage adds a message with default channel and color func (ctx *CommandContext) AddDefaultMessage(message string) { ctx.AddMessage(ChannelDefault, ColorWhite, message) } // AddErrorMessage adds an error message func (ctx *CommandContext) AddErrorMessage(message string) { ctx.AddMessage(ChannelError, ColorRed, message) } // AddStatusMessage adds a status message func (ctx *CommandContext) AddStatusMessage(message string) { ctx.AddMessage(ChannelStatus, ColorYellow, message) } // SendMessages sends all queued messages to the client func (ctx *CommandContext) SendMessages() { if ctx.Client == nil { return } ctx.mutex.RLock() messages := make([]CommandMessage, len(ctx.Messages)) copy(messages, ctx.Messages) ctx.mutex.RUnlock() for _, msg := range messages { ctx.Client.SendMessage(msg.Channel, msg.Color, msg.Message) } } // ClearMessages clears all queued messages func (ctx *CommandContext) ClearMessages() { ctx.mutex.Lock() defer ctx.mutex.Unlock() ctx.Messages = ctx.Messages[:0] } // SetResult sets a result value func (ctx *CommandContext) SetResult(name string, value any) { ctx.mutex.Lock() defer ctx.mutex.Unlock() if ctx.Results == nil { ctx.Results = make(map[string]any) } ctx.Results[name] = value } // GetResult retrieves a result value func (ctx *CommandContext) GetResult(name string) (any, bool) { ctx.mutex.RLock() defer ctx.mutex.RUnlock() value, exists := ctx.Results[name] return value, exists } // GetResultString retrieves a string result func (ctx *CommandContext) GetResultString(name string, defaultValue string) string { if value, exists := ctx.GetResult(name); exists { if str, ok := value.(string); ok { return str } } return defaultValue } // GetResultInt retrieves an integer result func (ctx *CommandContext) GetResultInt(name string, defaultValue int) int { if value, exists := ctx.GetResult(name); exists { switch v := value.(type) { case int: return v case int32: return int(v) case int64: return int(v) case float32: return int(v) case float64: return int(v) } } return defaultValue } // ValidateArgumentCount validates that the command has the required number of arguments func (ctx *CommandContext) ValidateArgumentCount(min, max int) error { argCount := len(ctx.Arguments) if argCount < min { return fmt.Errorf("command '%s' requires at least %d arguments, got %d", ctx.CommandName, min, argCount) } if max >= 0 && argCount > max { return fmt.Errorf("command '%s' accepts at most %d arguments, got %d", ctx.CommandName, max, argCount) } return nil } // RequirePlayer ensures a player is present in the context func (ctx *CommandContext) RequirePlayer() error { if ctx.Player == nil { return fmt.Errorf("command '%s' requires a player", ctx.CommandName) } return nil } // RequireTarget ensures a target is present in the context func (ctx *CommandContext) RequireTarget() error { if ctx.Target == nil { return fmt.Errorf("command '%s' requires a target", ctx.CommandName) } return nil } // RequireZone ensures a zone is present in the context func (ctx *CommandContext) RequireZone() error { if ctx.Zone == nil { return fmt.Errorf("command '%s' requires a zone", ctx.CommandName) } return nil } // RequireAdminLevel ensures the user has the required admin level func (ctx *CommandContext) RequireAdminLevel(requiredLevel int) error { if ctx.AdminLevel < requiredLevel { return fmt.Errorf("command '%s' requires admin level %d, you have %d", ctx.CommandName, requiredLevel, ctx.AdminLevel) } return nil } // GetPlayerName safely gets the player's name func (ctx *CommandContext) GetPlayerName() string { if ctx.Player != nil { return ctx.Player.GetName() } if ctx.Client != nil { return ctx.Client.GetName() } return "Unknown" } // GetTargetName safely gets the target's name func (ctx *CommandContext) GetTargetName() string { if ctx.Target != nil { return ctx.Target.GetName() } return "No Target" } // GetZoneName safely gets the zone's name func (ctx *CommandContext) GetZoneName() string { if ctx.Zone != nil { return ctx.Zone.GetName() } return "No Zone" }