295 lines
6.6 KiB
Go
295 lines
6.6 KiB
Go
/*
|
|
Package towns is the active record implementation for towns in the game.
|
|
|
|
# Basic Usage
|
|
|
|
To retrieve a town by ID:
|
|
|
|
town, err := towns.Find(db, 1)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Printf("Found town: %s at (%d,%d)\n", town.Name, town.X, town.Y)
|
|
|
|
To get all towns:
|
|
|
|
allTowns, err := towns.All(db)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
for _, town := range allTowns {
|
|
fmt.Printf("Town: %s\n", town.Name)
|
|
}
|
|
|
|
To find a town by name:
|
|
|
|
midworld, err := towns.ByName(db, "Midworld")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
To filter towns by affordability:
|
|
|
|
cheapInns, err := towns.ByMaxInnCost(db, 25)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
affordableTP, err := towns.ByMaxTPCost(db, 50)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
To find nearby towns:
|
|
|
|
nearbyTowns, err := towns.ByDistance(db, playerX, playerY, 100)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
# Creating Towns with Builder Pattern
|
|
|
|
The package provides a fluent builder interface for creating new towns:
|
|
|
|
town, err := towns.NewBuilder(db).
|
|
WithName("New Settlement").
|
|
WithCoordinates(150, -75).
|
|
WithInnCost(35).
|
|
WithMapCost(125).
|
|
WithTPCost(40).
|
|
WithShopItems([]string{"1", "5", "10", "20"}).
|
|
Create()
|
|
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Printf("Created town with ID: %d\n", town.ID)
|
|
|
|
# Updating Towns
|
|
|
|
Towns can be modified and saved back to the database:
|
|
|
|
town, _ := towns.Find(db, 1)
|
|
town.Name = "Enhanced Midworld"
|
|
town.InnCost = 8
|
|
town.MapCost = 10
|
|
|
|
err := town.Save()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
# Deleting Towns
|
|
|
|
Towns can be removed from the database:
|
|
|
|
town, _ := towns.Find(db, 1)
|
|
err := town.Delete()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
# Database Schema
|
|
|
|
The towns table has the following structure:
|
|
|
|
CREATE TABLE towns (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
x INTEGER NOT NULL DEFAULT 0,
|
|
y INTEGER NOT NULL DEFAULT 0,
|
|
inn_cost INTEGER NOT NULL DEFAULT 0,
|
|
map_cost INTEGER NOT NULL DEFAULT 0,
|
|
tp_cost INTEGER NOT NULL DEFAULT 0,
|
|
shop_list TEXT NOT NULL DEFAULT ''
|
|
)
|
|
|
|
Where:
|
|
- id: Unique identifier
|
|
- name: Display name of the town
|
|
- x, y: World coordinates for the town location
|
|
- inn_cost: Cost in gold to rest at the inn
|
|
- map_cost: Cost in gold to buy the world map from this town
|
|
- tp_cost: Cost in gold to teleport to this town from elsewhere
|
|
- shop_list: Comma-separated list of item IDs sold in the town shop
|
|
|
|
# Town Economics
|
|
|
|
## Inn System
|
|
|
|
Towns provide rest services through inns:
|
|
|
|
if town.CanAffordInn(playerGold) {
|
|
fmt.Printf("You can rest at %s for %d gold\n", town.Name, town.InnCost)
|
|
}
|
|
|
|
Inn costs typically increase with town size and distance from starting area.
|
|
|
|
## Map Purchase
|
|
|
|
Maps can be bought from certain towns:
|
|
|
|
if town.MapCost > 0 && town.CanAffordMap(playerGold) {
|
|
fmt.Printf("Map available for %d gold at %s\n", town.MapCost, town.Name)
|
|
}
|
|
|
|
Not all towns sell maps (cost = 0 means unavailable).
|
|
|
|
## Teleportation Network
|
|
|
|
Towns with teleport access allow fast travel:
|
|
|
|
if town.TPCost > 0 && town.CanAffordTeleport(playerGold) {
|
|
fmt.Printf("Can teleport to %s for %d gold\n", town.Name, town.TPCost)
|
|
}
|
|
|
|
Starting town (Midworld) typically has no teleport cost.
|
|
|
|
# Shop System
|
|
|
|
## Item Management
|
|
|
|
Towns have shops that sell specific items:
|
|
|
|
shopItems := town.GetShopItems()
|
|
fmt.Printf("%s sells %d different items\n", town.Name, len(shopItems))
|
|
|
|
if town.HasShopItem("5") {
|
|
fmt.Println("This town sells item ID 5")
|
|
}
|
|
|
|
## Shop Inventory
|
|
|
|
Shop inventories are stored as comma-separated item ID lists:
|
|
|
|
// Current inventory
|
|
items := town.GetShopItems() // Returns []string{"1", "2", "3"}
|
|
|
|
// Update inventory
|
|
newItems := []string{"10", "15", "20", "25"}
|
|
town.SetShopItems(newItems)
|
|
town.Save() // Persist changes
|
|
|
|
# Geographic Queries
|
|
|
|
## Coordinate System
|
|
|
|
Towns exist on a 2D coordinate plane:
|
|
|
|
fmt.Printf("%s is located at (%d, %d)\n", town.Name, town.X, town.Y)
|
|
|
|
// Calculate distance between towns
|
|
distance := town1.DistanceFrom(town2.X, town2.Y)
|
|
|
|
## Proximity Search
|
|
|
|
Find towns within a certain distance:
|
|
|
|
// Get all towns within 50 units of player position
|
|
nearbyTowns, err := towns.ByDistance(db, playerX, playerY, 50)
|
|
|
|
// Results are ordered by distance (closest first)
|
|
if len(nearbyTowns) > 0 {
|
|
closest := nearbyTowns[0]
|
|
fmt.Printf("Closest town: %s\n", closest.Name)
|
|
}
|
|
|
|
## Starting Location
|
|
|
|
The starting town is identifiable:
|
|
|
|
if town.IsStartingTown() {
|
|
fmt.Println("This is where new players begin")
|
|
}
|
|
|
|
Starting towns are typically at coordinates (0, 0).
|
|
|
|
# Cost-Based Queries
|
|
|
|
## Budget-Friendly Services
|
|
|
|
Find towns within budget constraints:
|
|
|
|
// Towns with affordable inns
|
|
cheapInns, err := towns.ByMaxInnCost(db, playerGold)
|
|
|
|
// Towns with affordable teleportation
|
|
affordableTP, err := towns.ByMaxTPCost(db, playerGold)
|
|
|
|
Results are ordered by cost (cheapest first).
|
|
|
|
## Service Availability
|
|
|
|
Check what services a town offers:
|
|
|
|
fmt.Printf("Services at %s:\n", town.Name)
|
|
if town.InnCost > 0 {
|
|
fmt.Printf("- Inn: %d gold\n", town.InnCost)
|
|
}
|
|
if town.MapCost > 0 {
|
|
fmt.Printf("- Map: %d gold\n", town.MapCost)
|
|
}
|
|
if town.TPCost > 0 {
|
|
fmt.Printf("- Teleport destination: %d gold\n", town.TPCost)
|
|
}
|
|
|
|
# Game Progression
|
|
|
|
## Town Hierarchy
|
|
|
|
Towns typically follow a progression pattern:
|
|
|
|
1. **Starting Towns** (Midworld): Free teleport, basic services, low costs
|
|
2. **Early Game Towns** (Roma, Bris): Moderate costs, expanded shops
|
|
3. **Mid Game Towns** (Kalle, Narcissa): Higher costs, specialized items
|
|
4. **Late Game Towns** (Hambry, Gilead, Endworld): Premium services, rare items
|
|
|
|
## Economic Scaling
|
|
|
|
Service costs often scale with game progression:
|
|
|
|
// Example progression analysis
|
|
towns, _ := towns.All(db)
|
|
for _, town := range towns {
|
|
if town.TPCost > 100 {
|
|
fmt.Printf("%s is a late-game destination\n", town.Name)
|
|
}
|
|
}
|
|
|
|
# World Map Integration
|
|
|
|
## Navigation
|
|
|
|
Towns serve as waypoints for world navigation:
|
|
|
|
// Find path between towns
|
|
startTown, _ := towns.ByName(db, "Midworld")
|
|
endTown, _ := towns.ByName(db, "Endworld")
|
|
|
|
distance := startTown.DistanceFrom(endTown.X, endTown.Y)
|
|
fmt.Printf("Distance from %s to %s: %.0f units\n",
|
|
startTown.Name, endTown.Name, distance)
|
|
|
|
## Strategic Planning
|
|
|
|
Use town data for strategic decisions:
|
|
|
|
// Find cheapest inn route
|
|
route := []string{"Midworld", "Roma", "Bris"}
|
|
totalInnCost := 0
|
|
|
|
for _, townName := range route {
|
|
town, _ := towns.ByName(db, townName)
|
|
totalInnCost += town.InnCost
|
|
}
|
|
|
|
fmt.Printf("Route inn cost: %d gold\n", totalInnCost)
|
|
|
|
# Error Handling
|
|
|
|
All functions return appropriate errors for common failure cases:
|
|
- Town not found (Find/ByName returns error for non-existent towns)
|
|
- Database connection issues
|
|
- Invalid operations (e.g., saving/deleting towns without IDs)
|
|
*/
|
|
package towns |