180 lines
5.5 KiB
Go

package ground_spawn
import (
"fmt"
"eq2emu/internal/common"
"eq2emu/internal/database"
"zombiezen.com/go/sqlite"
)
// MasterList manages all ground spawns using the generic base
type MasterList struct {
*common.MasterList[int32, *GroundSpawn]
}
// NewMasterList creates a new ground spawn master list
func NewMasterList() *MasterList {
return &MasterList{
MasterList: common.NewMasterList[int32, *GroundSpawn](),
}
}
// AddGroundSpawn adds a ground spawn to the master list
func (ml *MasterList) AddGroundSpawn(gs *GroundSpawn) bool {
return ml.MasterList.Add(gs)
}
// GetGroundSpawn retrieves a ground spawn by ID
func (ml *MasterList) GetGroundSpawn(groundSpawnID int32) *GroundSpawn {
return ml.MasterList.Get(groundSpawnID)
}
// RemoveGroundSpawn removes a ground spawn by ID
func (ml *MasterList) RemoveGroundSpawn(groundSpawnID int32) bool {
return ml.MasterList.Remove(groundSpawnID)
}
// GetByZone returns all ground spawns in a specific zone
func (ml *MasterList) GetByZone(zoneID int32) []*GroundSpawn {
return ml.MasterList.Filter(func(gs *GroundSpawn) bool {
return gs.ZoneID == zoneID
})
}
// GetBySkill returns all ground spawns that require a specific skill
func (ml *MasterList) GetBySkill(skill string) []*GroundSpawn {
return ml.MasterList.Filter(func(gs *GroundSpawn) bool {
return gs.CollectionSkill == skill
})
}
// GetAvailableSpawns returns all harvestable ground spawns
func (ml *MasterList) GetAvailableSpawns() []*GroundSpawn {
return ml.MasterList.Filter(func(gs *GroundSpawn) bool {
return gs.IsAvailable()
})
}
// GetDepletedSpawns returns all depleted ground spawns
func (ml *MasterList) GetDepletedSpawns() []*GroundSpawn {
return ml.MasterList.Filter(func(gs *GroundSpawn) bool {
return gs.IsDepleted()
})
}
// LoadFromDatabase loads all ground spawns from database
func (ml *MasterList) LoadFromDatabase(db *database.Database) error {
if db.GetType() == database.SQLite {
return ml.loadFromSQLite(db)
}
return ml.loadFromMySQL(db)
}
func (ml *MasterList) loadFromSQLite(db *database.Database) error {
return db.ExecTransient(`
SELECT id, groundspawn_id, name, collection_skill, number_harvests,
attempts_per_harvest, randomize_heading, respawn_time,
x, y, z, heading, zone_id, grid_id
FROM ground_spawns
ORDER BY groundspawn_id
`, func(stmt *sqlite.Stmt) error {
gs := &GroundSpawn{
ID: stmt.ColumnInt32(0),
GroundSpawnID: stmt.ColumnInt32(1),
Name: stmt.ColumnText(2),
CollectionSkill: stmt.ColumnText(3),
NumberHarvests: int8(stmt.ColumnInt32(4)),
AttemptsPerHarvest: int8(stmt.ColumnInt32(5)),
RandomizeHeading: stmt.ColumnBool(6),
RespawnTime: stmt.ColumnInt32(7),
X: float32(stmt.ColumnFloat(8)),
Y: float32(stmt.ColumnFloat(9)),
Z: float32(stmt.ColumnFloat(10)),
Heading: float32(stmt.ColumnFloat(11)),
ZoneID: stmt.ColumnInt32(12),
GridID: stmt.ColumnInt32(13),
db: db,
isNew: false,
IsAlive: true,
CurrentHarvests: int8(stmt.ColumnInt32(4)), // Initialize to max harvests
HarvestEntries: make([]*HarvestEntry, 0),
HarvestItems: make([]*HarvestEntryItem, 0),
}
// Load harvest data for this ground spawn
if err := gs.loadHarvestData(); err != nil {
return fmt.Errorf("failed to load harvest data for ground spawn %d: %w", gs.GroundSpawnID, err)
}
ml.MasterList.Add(gs)
return nil
})
}
func (ml *MasterList) loadFromMySQL(db *database.Database) error {
rows, err := db.Query(`
SELECT id, groundspawn_id, name, collection_skill, number_harvests,
attempts_per_harvest, randomize_heading, respawn_time,
x, y, z, heading, zone_id, grid_id
FROM ground_spawns
ORDER BY groundspawn_id
`)
if err != nil {
return fmt.Errorf("failed to query ground spawns: %w", err)
}
defer rows.Close()
for rows.Next() {
gs := &GroundSpawn{
db: db,
isNew: false,
IsAlive: true,
HarvestEntries: make([]*HarvestEntry, 0),
HarvestItems: make([]*HarvestEntryItem, 0),
}
err := rows.Scan(&gs.ID, &gs.GroundSpawnID, &gs.Name, &gs.CollectionSkill,
&gs.NumberHarvests, &gs.AttemptsPerHarvest, &gs.RandomizeHeading,
&gs.RespawnTime, &gs.X, &gs.Y, &gs.Z, &gs.Heading, &gs.ZoneID, &gs.GridID)
if err != nil {
return fmt.Errorf("failed to scan ground spawn: %w", err)
}
// Initialize current harvests to max
gs.CurrentHarvests = gs.NumberHarvests
// Load harvest data for this ground spawn
if err := gs.loadHarvestData(); err != nil {
return fmt.Errorf("failed to load harvest data for ground spawn %d: %w", gs.GroundSpawnID, err)
}
ml.MasterList.Add(gs)
}
return rows.Err()
}
// GetStatistics returns statistics about the ground spawn system
func (ml *MasterList) GetStatistics() *Statistics {
availableSpawns := len(ml.GetAvailableSpawns())
// Count by zone
zoneMap := make(map[int32]int)
skillMap := make(map[string]int64)
ml.MasterList.ForEach(func(id int32, gs *GroundSpawn) {
zoneMap[gs.ZoneID]++
skillMap[gs.CollectionSkill]++
})
return &Statistics{
TotalHarvests: 0, // Would need to be tracked separately
SuccessfulHarvests: 0, // Would need to be tracked separately
RareItemsHarvested: 0, // Would need to be tracked separately
SkillUpsGenerated: 0, // Would need to be tracked separately
HarvestsBySkill: skillMap,
ActiveGroundSpawns: availableSpawns,
GroundSpawnsByZone: zoneMap,
}
}