package towns import ( "fmt" "math" "slices" "sort" "dk/internal/database" "dk/internal/helpers" ) // Town represents a town in the game type Town struct { ID int Name string X int Y int InnCost int MapCost int TPCost int ShopList string } // New creates a new Town with sensible defaults func New() *Town { return &Town{ Name: "", X: 0, Y: 0, InnCost: 50, MapCost: 100, TPCost: 25, ShopList: "", } } // Validate checks if town has valid values func (t *Town) Validate() error { if t.Name == "" { return fmt.Errorf("town name cannot be empty") } if t.InnCost < 0 { return fmt.Errorf("town InnCost cannot be negative") } if t.MapCost < 0 { return fmt.Errorf("town MapCost cannot be negative") } if t.TPCost < 0 { return fmt.Errorf("town TPCost cannot be negative") } return nil } // CRUD operations func (t *Town) Delete() error { return database.Exec("DELETE FROM towns WHERE id = %d", t.ID) } func (t *Town) Insert() error { id, err := database.Insert("towns", t, "ID") if err != nil { return err } t.ID = int(id) return nil } // Query functions func Find(id int) (*Town, error) { var town Town err := database.Get(&town, "SELECT * FROM towns WHERE id = %d", id) if err != nil { return nil, fmt.Errorf("town with ID %d not found", id) } return &town, nil } func All() ([]*Town, error) { var towns []*Town err := database.Select(&towns, "SELECT * FROM towns ORDER BY id ASC") return towns, err } func ByName(name string) (*Town, error) { var town Town err := database.Get(&town, "SELECT * FROM towns WHERE name = %s COLLATE NOCASE", name) if err != nil { return nil, fmt.Errorf("town with name '%s' not found", name) } return &town, nil } func ByMaxInnCost(maxCost int) ([]*Town, error) { var towns []*Town err := database.Select(&towns, "SELECT * FROM towns WHERE inn_cost <= %d ORDER BY id ASC", maxCost) return towns, err } func ByMaxTPCost(maxCost int) ([]*Town, error) { var towns []*Town err := database.Select(&towns, "SELECT * FROM towns WHERE tp_cost <= %d ORDER BY id ASC", maxCost) return towns, err } func ByCoords(x, y int) (*Town, error) { var town Town err := database.Get(&town, "SELECT * FROM towns WHERE x = %d AND y = %d", x, y) if err != nil { return nil, nil // Return nil if not found (like original) } return &town, nil } func ExistsAt(x, y int) bool { var count int err := database.Get(&count, "SELECT COUNT(*) FROM towns WHERE x = %d AND y = %d", x, y) return err == nil && count > 0 } func ByDistance(fromX, fromY, maxDistance int) ([]*Town, error) { var towns []*Town err := database.Select(&towns, "SELECT * FROM towns") if err != nil { return nil, err } maxDistance2 := float64(maxDistance * maxDistance) var result []*Town for _, town := range towns { if town.DistanceFromSquared(fromX, fromY) <= maxDistance2 { result = append(result, town) } } // Sort by distance, then by ID sort.Slice(result, func(i, j int) bool { distI := result[i].DistanceFromSquared(fromX, fromY) distJ := result[j].DistanceFromSquared(fromX, fromY) if distI == distJ { return result[i].ID < result[j].ID } return distI < distJ }) return result, nil } // Helper methods func (t *Town) GetShopItems() []int { return helpers.StringToInts(t.ShopList) } func (t *Town) SetShopItems(items []int) { t.ShopList = helpers.IntsToString(items) } func (t *Town) HasShopItem(itemID int) bool { return slices.Contains(t.GetShopItems(), itemID) } func (t *Town) DistanceFromSquared(x, y int) float64 { dx := float64(t.X - x) dy := float64(t.Y - y) return dx*dx + dy*dy // Return squared distance for performance } func (t *Town) DistanceFrom(x, y int) float64 { return math.Sqrt(t.DistanceFromSquared(x, y)) } func (t *Town) IsStartingTown() bool { return t.X == 0 && t.Y == 0 } func (t *Town) CanAffordInn(gold int) bool { return gold >= t.InnCost } func (t *Town) CanAffordMap(gold int) bool { return gold >= t.MapCost } func (t *Town) CanAffordTeleport(gold int) bool { return gold >= t.TPCost } func (t *Town) HasShop() bool { return len(t.GetShopItems()) > 0 } func (t *Town) GetPosition() (int, int) { return t.X, t.Y } func (t *Town) SetPosition(x, y int) { t.X = x t.Y = y }