package actions import ( "dk/internal/models/control" "dk/internal/models/fights" "dk/internal/models/monsters" "dk/internal/models/towns" "dk/internal/models/users" "fmt" "math" "math/rand" ) type Direction int const ( North Direction = iota East South West ) func (d Direction) String() string { switch d { case North: return "North" case East: return "East" case South: return "South" case West: return "West" default: return "Unknown" } } func Move(user *users.User, dir Direction) (string, int, int, error) { control := control.Get() newX, newY := user.GetPosition() switch dir { case North: newY++ case East: newX++ case South: newY-- case West: newX-- } if !control.IsWithinWorldBounds(newX, newY) { return user.Currently, user.X, user.Y, fmt.Errorf("You've hit the edge of the world.") } if towns.ExistsAt(newX, newY) { return "In Town", newX, newY, nil } // 33% chance to start a fight when moving to non-town location if rand.Float32() < 0.33 { monster, err := getRandomMonsterByDistance(newX, newY) if err != nil { return "Exploring", newX, newY, nil // Fall back to exploring if monster selection fails } // Create new fight record fight := fights.New(user.ID, monster.ID) fight.MonsterHP = monster.MaxHP fight.MonsterMaxHP = monster.MaxHP fight.MonsterImmune = monster.Immune err = fight.Insert() if err != nil { return "Exploring", newX, newY, nil // Fall back if fight creation fails } user.FightID = fight.ID return "Fighting", newX, newY, nil } return "Exploring", newX, newY, nil } // getRandomMonsterByDistance selects a random monster based on distance from origin func getRandomMonsterByDistance(x, y int) (*monsters.Monster, error) { // Calculate Manhattan distance from origin distance := int(math.Abs(float64(x)) + math.Abs(float64(y))) // Determine level range based on distance (every 5 units increases level) minLevel := 1 + (distance / 5) maxLevel := minLevel + 2 // 3-level range // Cap the levels at reasonable bounds if minLevel > 50 { minLevel = 50 } if maxLevel > 50 { maxLevel = 50 } if minLevel < 1 { minLevel = 1 } // Get monsters in the level range availableMonsters, err := monsters.ByLevelRange(minLevel, maxLevel) if err != nil || len(availableMonsters) == 0 { // Fallback to level 1 monsters if no monsters found availableMonsters, err = monsters.ByLevel(1) if err != nil || len(availableMonsters) == 0 { return nil, fmt.Errorf("no monsters available") } } // Pick a random monster from available ones randomIndex := rand.Intn(len(availableMonsters)) return availableMonsters[randomIndex], nil }