724 lines
21 KiB
Go
724 lines
21 KiB
Go
// Package ground_spawn provides harvestable resource node management for EQ2.
|
|
//
|
|
// Basic Usage:
|
|
//
|
|
// gs := ground_spawn.New(db)
|
|
// gs.CollectionSkill = "Mining"
|
|
// gs.NumberHarvests = 5
|
|
// gs.Save()
|
|
//
|
|
// loaded, _ := ground_spawn.Load(db, 1001)
|
|
// result, _ := loaded.ProcessHarvest(context)
|
|
//
|
|
// Master List:
|
|
//
|
|
// masterList := ground_spawn.NewMasterList()
|
|
// masterList.Add(gs)
|
|
package ground_spawn
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"eq2emu/internal/database"
|
|
"zombiezen.com/go/sqlite"
|
|
"zombiezen.com/go/sqlite/sqlitex"
|
|
)
|
|
|
|
// GroundSpawn represents a harvestable resource node with embedded database operations
|
|
type GroundSpawn struct {
|
|
// Database fields
|
|
ID int32 `json:"id" db:"id"` // Auto-generated ID
|
|
GroundSpawnID int32 `json:"groundspawn_id" db:"groundspawn_id"` // Entry ID for this type of ground spawn
|
|
Name string `json:"name" db:"name"` // Display name
|
|
CollectionSkill string `json:"collection_skill" db:"collection_skill"` // Required skill (Mining, Gathering, etc.)
|
|
NumberHarvests int8 `json:"number_harvests" db:"number_harvests"` // Harvests before depletion
|
|
AttemptsPerHarvest int8 `json:"attempts_per_harvest" db:"attempts_per_harvest"` // Attempts per harvest session
|
|
RandomizeHeading bool `json:"randomize_heading" db:"randomize_heading"` // Randomize spawn heading
|
|
RespawnTime int32 `json:"respawn_time" db:"respawn_time"` // Respawn time in seconds
|
|
|
|
// Position data
|
|
X float32 `json:"x" db:"x"` // World X coordinate
|
|
Y float32 `json:"y" db:"y"` // World Y coordinate
|
|
Z float32 `json:"z" db:"z"` // World Z coordinate
|
|
Heading float32 `json:"heading" db:"heading"` // Spawn heading/rotation
|
|
ZoneID int32 `json:"zone_id" db:"zone_id"` // Zone identifier
|
|
GridID int32 `json:"grid_id" db:"grid_id"` // Grid identifier
|
|
|
|
// State data
|
|
IsAlive bool `json:"is_alive"` // Whether spawn is active
|
|
CurrentHarvests int8 `json:"current_harvests"` // Current harvest count
|
|
LastHarvested time.Time `json:"last_harvested"` // When last harvested
|
|
NextRespawn time.Time `json:"next_respawn"` // When it will respawn
|
|
|
|
// Associated data (loaded separately)
|
|
HarvestEntries []*HarvestEntry `json:"harvest_entries,omitempty"`
|
|
HarvestItems []*HarvestEntryItem `json:"harvest_items,omitempty"`
|
|
|
|
// Database connection and internal state
|
|
db *database.Database `json:"-"`
|
|
isNew bool `json:"-"`
|
|
harvestMux sync.Mutex `json:"-"`
|
|
}
|
|
|
|
// New creates a new ground spawn with database connection
|
|
func New(db *database.Database) *GroundSpawn {
|
|
return &GroundSpawn{
|
|
HarvestEntries: make([]*HarvestEntry, 0),
|
|
HarvestItems: make([]*HarvestEntryItem, 0),
|
|
db: db,
|
|
isNew: true,
|
|
IsAlive: true,
|
|
CurrentHarvests: 0,
|
|
NumberHarvests: 5, // Default
|
|
AttemptsPerHarvest: 1, // Default
|
|
RandomizeHeading: true,
|
|
}
|
|
}
|
|
|
|
// Load loads a ground spawn by ID from database
|
|
func Load(db *database.Database, groundSpawnID int32) (*GroundSpawn, error) {
|
|
gs := &GroundSpawn{
|
|
db: db,
|
|
isNew: false,
|
|
}
|
|
|
|
if db.GetType() == database.SQLite {
|
|
err := 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 WHERE groundspawn_id = ?
|
|
`, func(stmt *sqlite.Stmt) error {
|
|
gs.ID = stmt.ColumnInt32(0)
|
|
gs.GroundSpawnID = stmt.ColumnInt32(1)
|
|
gs.Name = stmt.ColumnText(2)
|
|
gs.CollectionSkill = stmt.ColumnText(3)
|
|
gs.NumberHarvests = int8(stmt.ColumnInt32(4))
|
|
gs.AttemptsPerHarvest = int8(stmt.ColumnInt32(5))
|
|
gs.RandomizeHeading = stmt.ColumnBool(6)
|
|
gs.RespawnTime = stmt.ColumnInt32(7)
|
|
gs.X = float32(stmt.ColumnFloat(8))
|
|
gs.Y = float32(stmt.ColumnFloat(9))
|
|
gs.Z = float32(stmt.ColumnFloat(10))
|
|
gs.Heading = float32(stmt.ColumnFloat(11))
|
|
gs.ZoneID = stmt.ColumnInt32(12)
|
|
gs.GridID = stmt.ColumnInt32(13)
|
|
return nil
|
|
}, groundSpawnID)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ground spawn not found: %d", groundSpawnID)
|
|
}
|
|
} else {
|
|
// MySQL implementation
|
|
row := db.QueryRow(`
|
|
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 WHERE groundspawn_id = ?
|
|
`, groundSpawnID)
|
|
|
|
err := row.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 nil, fmt.Errorf("ground spawn not found: %d", groundSpawnID)
|
|
}
|
|
}
|
|
|
|
// Initialize state
|
|
gs.IsAlive = true
|
|
gs.CurrentHarvests = gs.NumberHarvests
|
|
|
|
// Load harvest entries and items
|
|
if err := gs.loadHarvestData(); err != nil {
|
|
return nil, fmt.Errorf("failed to load harvest data: %w", err)
|
|
}
|
|
|
|
return gs, nil
|
|
}
|
|
|
|
// Save saves the ground spawn to database
|
|
func (gs *GroundSpawn) Save() error {
|
|
if gs.db == nil {
|
|
return fmt.Errorf("no database connection")
|
|
}
|
|
|
|
if gs.isNew {
|
|
return gs.insert()
|
|
}
|
|
return gs.update()
|
|
}
|
|
|
|
// Delete removes the ground spawn from database
|
|
func (gs *GroundSpawn) Delete() error {
|
|
if gs.db == nil {
|
|
return fmt.Errorf("no database connection")
|
|
}
|
|
if gs.isNew {
|
|
return fmt.Errorf("cannot delete unsaved ground spawn")
|
|
}
|
|
|
|
if gs.db.GetType() == database.SQLite {
|
|
return gs.db.Execute("DELETE FROM ground_spawns WHERE groundspawn_id = ?",
|
|
&sqlitex.ExecOptions{Args: []any{gs.GroundSpawnID}})
|
|
}
|
|
_, err := gs.db.Exec("DELETE FROM ground_spawns WHERE groundspawn_id = ?", gs.GroundSpawnID)
|
|
return err
|
|
}
|
|
|
|
// GetID returns the ground spawn ID (implements common.Identifiable)
|
|
func (gs *GroundSpawn) GetID() int32 {
|
|
return gs.GroundSpawnID
|
|
}
|
|
|
|
// IsGroundSpawn returns true (compatibility method)
|
|
func (gs *GroundSpawn) IsGroundSpawn() bool {
|
|
return true
|
|
}
|
|
|
|
// IsDepleted returns true if the ground spawn has no harvests remaining
|
|
func (gs *GroundSpawn) IsDepleted() bool {
|
|
return gs.CurrentHarvests <= 0
|
|
}
|
|
|
|
// IsAvailable returns true if the ground spawn can be harvested
|
|
func (gs *GroundSpawn) IsAvailable() bool {
|
|
return gs.CurrentHarvests > 0 && gs.IsAlive
|
|
}
|
|
|
|
// GetHarvestMessageName returns the appropriate harvest verb based on skill
|
|
func (gs *GroundSpawn) GetHarvestMessageName(presentTense bool, failure bool) string {
|
|
skill := strings.ToLower(gs.CollectionSkill)
|
|
|
|
switch skill {
|
|
case "gathering", "collecting":
|
|
if presentTense {
|
|
return "gather"
|
|
}
|
|
return "gathered"
|
|
|
|
case "mining":
|
|
if presentTense {
|
|
return "mine"
|
|
}
|
|
return "mined"
|
|
|
|
case "fishing":
|
|
if presentTense {
|
|
return "fish"
|
|
}
|
|
return "fished"
|
|
|
|
case "trapping":
|
|
if failure {
|
|
return "trap"
|
|
}
|
|
if presentTense {
|
|
return "acquire"
|
|
}
|
|
return "acquired"
|
|
|
|
case "foresting":
|
|
if presentTense {
|
|
return "forest"
|
|
}
|
|
return "forested"
|
|
|
|
default:
|
|
if presentTense {
|
|
return "collect"
|
|
}
|
|
return "collected"
|
|
}
|
|
}
|
|
|
|
// GetHarvestSpellType returns the spell type for harvesting
|
|
func (gs *GroundSpawn) GetHarvestSpellType() string {
|
|
skill := strings.ToLower(gs.CollectionSkill)
|
|
|
|
switch skill {
|
|
case "gathering", "collecting":
|
|
return SpellTypeGather
|
|
case "mining":
|
|
return SpellTypeMine
|
|
case "trapping":
|
|
return SpellTypeTrap
|
|
case "foresting":
|
|
return SpellTypeChop
|
|
case "fishing":
|
|
return SpellTypeFish
|
|
default:
|
|
return SpellTypeGather
|
|
}
|
|
}
|
|
|
|
// GetHarvestSpellName returns the spell name for harvesting
|
|
func (gs *GroundSpawn) GetHarvestSpellName() string {
|
|
skill := gs.CollectionSkill
|
|
|
|
if skill == SkillCollecting {
|
|
return SkillGathering
|
|
}
|
|
|
|
return skill
|
|
}
|
|
|
|
// ProcessHarvest handles the complex harvesting logic (preserves C++ algorithm)
|
|
func (gs *GroundSpawn) ProcessHarvest(player Player, skill Skill, totalSkill int16) (*HarvestResult, error) {
|
|
gs.harvestMux.Lock()
|
|
defer gs.harvestMux.Unlock()
|
|
|
|
// Check if ground spawn is depleted
|
|
if gs.CurrentHarvests <= 0 {
|
|
return &HarvestResult{
|
|
Success: false,
|
|
MessageText: "This spawn has nothing more to harvest!",
|
|
}, nil
|
|
}
|
|
|
|
// Validate harvest data
|
|
if len(gs.HarvestEntries) == 0 {
|
|
return &HarvestResult{
|
|
Success: false,
|
|
MessageText: fmt.Sprintf("Error: No groundspawn entries assigned to groundspawn id: %d", gs.GroundSpawnID),
|
|
}, nil
|
|
}
|
|
|
|
if len(gs.HarvestItems) == 0 {
|
|
return &HarvestResult{
|
|
Success: false,
|
|
MessageText: fmt.Sprintf("Error: No groundspawn items assigned to groundspawn id: %d", gs.GroundSpawnID),
|
|
}, nil
|
|
}
|
|
|
|
// Check for collection skill
|
|
isCollection := gs.CollectionSkill == "Collecting"
|
|
|
|
result := &HarvestResult{
|
|
Success: true,
|
|
ItemsAwarded: make([]*HarvestedItem, 0),
|
|
}
|
|
|
|
// Process each harvest attempt (preserving C++ logic)
|
|
for attempt := int8(0); attempt < gs.AttemptsPerHarvest; attempt++ {
|
|
attemptResult := gs.processHarvestAttempt(player, skill, totalSkill, isCollection)
|
|
if attemptResult != nil {
|
|
result.ItemsAwarded = append(result.ItemsAwarded, attemptResult.ItemsAwarded...)
|
|
if attemptResult.SkillGained {
|
|
result.SkillGained = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Decrement harvest count and update state
|
|
gs.CurrentHarvests--
|
|
gs.LastHarvested = time.Now()
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// processHarvestAttempt handles a single harvest attempt (preserves C++ algorithm)
|
|
func (gs *GroundSpawn) processHarvestAttempt(player Player, skill Skill, totalSkill int16, isCollection bool) *HarvestResult {
|
|
// Filter available harvest tables based on player skill and level
|
|
availableTables := gs.filterHarvestTables(player, totalSkill)
|
|
if len(availableTables) == 0 {
|
|
return &HarvestResult{
|
|
Success: false,
|
|
MessageText: "You lack the skills to harvest this node!",
|
|
}
|
|
}
|
|
|
|
// Select harvest table based on skill roll (matching C++ algorithm)
|
|
selectedTable := gs.selectHarvestTable(availableTables, totalSkill)
|
|
if selectedTable == nil {
|
|
return &HarvestResult{
|
|
Success: false,
|
|
MessageText: "Failed to determine harvest table",
|
|
}
|
|
}
|
|
|
|
// Determine harvest type based on table percentages
|
|
harvestType := gs.determineHarvestType(selectedTable, isCollection)
|
|
if harvestType == HarvestTypeNone {
|
|
return &HarvestResult{
|
|
Success: false,
|
|
MessageText: fmt.Sprintf("You failed to %s anything from %s.",
|
|
gs.GetHarvestMessageName(true, true), gs.Name),
|
|
}
|
|
}
|
|
|
|
// Award items based on harvest type
|
|
items := gs.awardHarvestItems(harvestType, player)
|
|
|
|
// Handle skill progression (simplified for now)
|
|
skillGained := false // TODO: Implement skill progression
|
|
|
|
return &HarvestResult{
|
|
Success: len(items) > 0,
|
|
HarvestType: harvestType,
|
|
ItemsAwarded: items,
|
|
SkillGained: skillGained,
|
|
}
|
|
}
|
|
|
|
// filterHarvestTables filters tables based on player capabilities (preserves C++ logic)
|
|
func (gs *GroundSpawn) filterHarvestTables(player Player, totalSkill int16) []*HarvestEntry {
|
|
var filtered []*HarvestEntry
|
|
|
|
for _, entry := range gs.HarvestEntries {
|
|
// Check skill requirement
|
|
if entry.MinSkillLevel > totalSkill {
|
|
continue
|
|
}
|
|
|
|
// Check level requirement for bonus tables
|
|
if entry.BonusTable && player.GetLevel() < entry.MinAdventureLevel {
|
|
continue
|
|
}
|
|
|
|
filtered = append(filtered, entry)
|
|
}
|
|
|
|
return filtered
|
|
}
|
|
|
|
// selectHarvestTable selects a harvest table based on skill level (preserves C++ algorithm)
|
|
func (gs *GroundSpawn) selectHarvestTable(tables []*HarvestEntry, totalSkill int16) *HarvestEntry {
|
|
if len(tables) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Find lowest skill requirement (matching C++ logic)
|
|
lowestSkill := int16(32767)
|
|
for _, table := range tables {
|
|
if table.MinSkillLevel < lowestSkill {
|
|
lowestSkill = table.MinSkillLevel
|
|
}
|
|
}
|
|
|
|
// Roll for table selection (matching C++ MakeRandomInt)
|
|
tableChoice := int16(rand.Intn(int(totalSkill-lowestSkill+1))) + lowestSkill
|
|
|
|
// Find best matching table (matching C++ logic)
|
|
var bestTable *HarvestEntry
|
|
bestScore := int16(0)
|
|
|
|
for _, table := range tables {
|
|
if tableChoice >= table.MinSkillLevel && table.MinSkillLevel > bestScore {
|
|
bestTable = table
|
|
bestScore = table.MinSkillLevel
|
|
}
|
|
}
|
|
|
|
// If multiple tables match, pick randomly (matching C++ logic)
|
|
var matches []*HarvestEntry
|
|
for _, table := range tables {
|
|
if table.MinSkillLevel == bestScore {
|
|
matches = append(matches, table)
|
|
}
|
|
}
|
|
|
|
if len(matches) > 1 {
|
|
return matches[rand.Intn(len(matches))]
|
|
}
|
|
|
|
return bestTable
|
|
}
|
|
|
|
// determineHarvestType determines what type of harvest occurs (preserves C++ algorithm)
|
|
func (gs *GroundSpawn) determineHarvestType(table *HarvestEntry, isCollection bool) int8 {
|
|
chance := rand.Float32() * 100.0
|
|
|
|
// Collection items always get 1 item (matching C++ logic)
|
|
if isCollection {
|
|
return HarvestType1Item
|
|
}
|
|
|
|
// Check harvest types in order of rarity (matching C++ order)
|
|
if chance <= table.Harvest10 {
|
|
return HarvestType10AndRare
|
|
}
|
|
if chance <= table.HarvestRare {
|
|
return HarvestTypeRare
|
|
}
|
|
if chance <= table.HarvestImbue {
|
|
return HarvestTypeImbue
|
|
}
|
|
if chance <= table.Harvest5 {
|
|
return HarvestType5Items
|
|
}
|
|
if chance <= table.Harvest3 {
|
|
return HarvestType3Items
|
|
}
|
|
if chance <= table.Harvest1 {
|
|
return HarvestType1Item
|
|
}
|
|
|
|
return HarvestTypeNone
|
|
}
|
|
|
|
// awardHarvestItems awards items based on harvest type (preserves C++ algorithm)
|
|
func (gs *GroundSpawn) awardHarvestItems(harvestType int8, player Player) []*HarvestedItem {
|
|
var items []*HarvestedItem
|
|
|
|
// Filter items based on harvest type and player location (matching C++ logic)
|
|
normalItems := gs.filterItems(gs.HarvestItems, ItemRarityNormal, player.GetLocation())
|
|
rareItems := gs.filterItems(gs.HarvestItems, ItemRarityRare, player.GetLocation())
|
|
imbueItems := gs.filterItems(gs.HarvestItems, ItemRarityImbue, player.GetLocation())
|
|
|
|
switch harvestType {
|
|
case HarvestType1Item:
|
|
items = gs.selectRandomItems(normalItems, 1)
|
|
case HarvestType3Items:
|
|
items = gs.selectRandomItems(normalItems, 3)
|
|
case HarvestType5Items:
|
|
items = gs.selectRandomItems(normalItems, 5)
|
|
case HarvestTypeImbue:
|
|
items = gs.selectRandomItems(imbueItems, 1)
|
|
case HarvestTypeRare:
|
|
items = gs.selectRandomItems(rareItems, 1)
|
|
case HarvestType10AndRare:
|
|
normal := gs.selectRandomItems(normalItems, 10)
|
|
rare := gs.selectRandomItems(rareItems, 1)
|
|
items = append(normal, rare...)
|
|
}
|
|
|
|
return items
|
|
}
|
|
|
|
// filterItems filters items by rarity and grid restriction (preserves C++ logic)
|
|
func (gs *GroundSpawn) filterItems(items []*HarvestEntryItem, rarity int8, playerGrid int32) []*HarvestEntryItem {
|
|
var filtered []*HarvestEntryItem
|
|
|
|
for _, item := range items {
|
|
if item.IsRare != rarity {
|
|
continue
|
|
}
|
|
|
|
// Check grid restriction (matching C++ logic)
|
|
if item.GridID != 0 && item.GridID != playerGrid {
|
|
continue
|
|
}
|
|
|
|
filtered = append(filtered, item)
|
|
}
|
|
|
|
return filtered
|
|
}
|
|
|
|
// selectRandomItems randomly selects items from available list (preserves C++ logic)
|
|
func (gs *GroundSpawn) selectRandomItems(items []*HarvestEntryItem, quantity int16) []*HarvestedItem {
|
|
if len(items) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var result []*HarvestedItem
|
|
|
|
for i := int16(0); i < quantity; i++ {
|
|
selectedItem := items[rand.Intn(len(items))]
|
|
|
|
harvestedItem := &HarvestedItem{
|
|
ItemID: selectedItem.ItemID,
|
|
Quantity: selectedItem.Quantity,
|
|
IsRare: selectedItem.IsRare == ItemRarityRare,
|
|
Name: fmt.Sprintf("Item_%d", selectedItem.ItemID), // Placeholder for item name lookup
|
|
}
|
|
|
|
result = append(result, harvestedItem)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// Respawn resets the ground spawn to harvestable state
|
|
func (gs *GroundSpawn) Respawn() {
|
|
gs.harvestMux.Lock()
|
|
defer gs.harvestMux.Unlock()
|
|
|
|
// Reset harvest count to default
|
|
gs.CurrentHarvests = gs.NumberHarvests
|
|
|
|
// Randomize heading if configured
|
|
if gs.RandomizeHeading {
|
|
gs.Heading = rand.Float32() * 360.0
|
|
}
|
|
|
|
// Mark as alive and update times
|
|
gs.IsAlive = true
|
|
gs.NextRespawn = time.Time{} // Clear next respawn time
|
|
}
|
|
|
|
// Private database helper methods
|
|
|
|
func (gs *GroundSpawn) insert() error {
|
|
if gs.db.GetType() == database.SQLite {
|
|
return gs.db.Execute(`
|
|
INSERT INTO ground_spawns (
|
|
groundspawn_id, name, collection_skill, number_harvests,
|
|
attempts_per_harvest, randomize_heading, respawn_time,
|
|
x, y, z, heading, zone_id, grid_id
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`, &sqlitex.ExecOptions{
|
|
Args: []any{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},
|
|
})
|
|
}
|
|
|
|
// MySQL
|
|
_, err := gs.db.Exec(`
|
|
INSERT INTO ground_spawns (
|
|
groundspawn_id, name, collection_skill, number_harvests,
|
|
attempts_per_harvest, randomize_heading, respawn_time,
|
|
x, y, z, heading, zone_id, grid_id
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`, 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 {
|
|
gs.isNew = false
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (gs *GroundSpawn) update() error {
|
|
if gs.db.GetType() == database.SQLite {
|
|
return gs.db.Execute(`
|
|
UPDATE ground_spawns SET
|
|
name = ?, collection_skill = ?, number_harvests = ?,
|
|
attempts_per_harvest = ?, randomize_heading = ?, respawn_time = ?,
|
|
x = ?, y = ?, z = ?, heading = ?, zone_id = ?, grid_id = ?
|
|
WHERE groundspawn_id = ?
|
|
`, &sqlitex.ExecOptions{
|
|
Args: []any{gs.Name, gs.CollectionSkill, gs.NumberHarvests,
|
|
gs.AttemptsPerHarvest, gs.RandomizeHeading, gs.RespawnTime,
|
|
gs.X, gs.Y, gs.Z, gs.Heading, gs.ZoneID, gs.GridID, gs.GroundSpawnID},
|
|
})
|
|
}
|
|
|
|
// MySQL
|
|
_, err := gs.db.Exec(`
|
|
UPDATE ground_spawns SET
|
|
name = ?, collection_skill = ?, number_harvests = ?,
|
|
attempts_per_harvest = ?, randomize_heading = ?, respawn_time = ?,
|
|
x = ?, y = ?, z = ?, heading = ?, zone_id = ?, grid_id = ?
|
|
WHERE groundspawn_id = ?
|
|
`, gs.Name, gs.CollectionSkill, gs.NumberHarvests,
|
|
gs.AttemptsPerHarvest, gs.RandomizeHeading, gs.RespawnTime,
|
|
gs.X, gs.Y, gs.Z, gs.Heading, gs.ZoneID, gs.GridID, gs.GroundSpawnID)
|
|
|
|
return err
|
|
}
|
|
|
|
func (gs *GroundSpawn) loadHarvestData() error {
|
|
// Load harvest entries
|
|
if err := gs.loadHarvestEntries(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Load harvest items
|
|
if err := gs.loadHarvestItems(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (gs *GroundSpawn) loadHarvestEntries() error {
|
|
gs.HarvestEntries = make([]*HarvestEntry, 0)
|
|
|
|
if gs.db.GetType() == database.SQLite {
|
|
return gs.db.ExecTransient(`
|
|
SELECT groundspawn_id, min_skill_level, min_adventure_level, bonus_table,
|
|
harvest1, harvest3, harvest5, harvest_imbue, harvest_rare, harvest10, harvest_coin
|
|
FROM groundspawn_entries WHERE groundspawn_id = ?
|
|
`, func(stmt *sqlite.Stmt) error {
|
|
entry := &HarvestEntry{
|
|
GroundSpawnID: stmt.ColumnInt32(0),
|
|
MinSkillLevel: int16(stmt.ColumnInt32(1)),
|
|
MinAdventureLevel: int16(stmt.ColumnInt32(2)),
|
|
BonusTable: stmt.ColumnBool(3),
|
|
Harvest1: float32(stmt.ColumnFloat(4)),
|
|
Harvest3: float32(stmt.ColumnFloat(5)),
|
|
Harvest5: float32(stmt.ColumnFloat(6)),
|
|
HarvestImbue: float32(stmt.ColumnFloat(7)),
|
|
HarvestRare: float32(stmt.ColumnFloat(8)),
|
|
Harvest10: float32(stmt.ColumnFloat(9)),
|
|
HarvestCoin: float32(stmt.ColumnFloat(10)),
|
|
}
|
|
gs.HarvestEntries = append(gs.HarvestEntries, entry)
|
|
return nil
|
|
}, gs.GroundSpawnID)
|
|
} else {
|
|
// MySQL implementation
|
|
rows, err := gs.db.Query(`
|
|
SELECT groundspawn_id, min_skill_level, min_adventure_level, bonus_table,
|
|
harvest1, harvest3, harvest5, harvest_imbue, harvest_rare, harvest10, harvest_coin
|
|
FROM groundspawn_entries WHERE groundspawn_id = ?
|
|
`, gs.GroundSpawnID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
entry := &HarvestEntry{}
|
|
err := rows.Scan(&entry.GroundSpawnID, &entry.MinSkillLevel, &entry.MinAdventureLevel,
|
|
&entry.BonusTable, &entry.Harvest1, &entry.Harvest3, &entry.Harvest5,
|
|
&entry.HarvestImbue, &entry.HarvestRare, &entry.Harvest10, &entry.HarvestCoin)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
gs.HarvestEntries = append(gs.HarvestEntries, entry)
|
|
}
|
|
return rows.Err()
|
|
}
|
|
}
|
|
|
|
func (gs *GroundSpawn) loadHarvestItems() error {
|
|
gs.HarvestItems = make([]*HarvestEntryItem, 0)
|
|
|
|
if gs.db.GetType() == database.SQLite {
|
|
return gs.db.ExecTransient(`
|
|
SELECT groundspawn_id, item_id, is_rare, grid_id, quantity
|
|
FROM groundspawn_items WHERE groundspawn_id = ?
|
|
`, func(stmt *sqlite.Stmt) error {
|
|
item := &HarvestEntryItem{
|
|
GroundSpawnID: stmt.ColumnInt32(0),
|
|
ItemID: stmt.ColumnInt32(1),
|
|
IsRare: int8(stmt.ColumnInt32(2)),
|
|
GridID: stmt.ColumnInt32(3),
|
|
Quantity: int16(stmt.ColumnInt32(4)),
|
|
}
|
|
gs.HarvestItems = append(gs.HarvestItems, item)
|
|
return nil
|
|
}, gs.GroundSpawnID)
|
|
} else {
|
|
// MySQL implementation
|
|
rows, err := gs.db.Query(`
|
|
SELECT groundspawn_id, item_id, is_rare, grid_id, quantity
|
|
FROM groundspawn_items WHERE groundspawn_id = ?
|
|
`, gs.GroundSpawnID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
item := &HarvestEntryItem{}
|
|
err := rows.Scan(&item.GroundSpawnID, &item.ItemID, &item.IsRare, &item.GridID, &item.Quantity)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
gs.HarvestItems = append(gs.HarvestItems, item)
|
|
}
|
|
return rows.Err()
|
|
}
|
|
}
|