eq2go/internal/heroic_ops/heroic_op_starter.go
2025-08-08 14:31:44 -05:00

313 lines
7.2 KiB
Go

package heroic_ops
import (
"fmt"
"eq2emu/internal/database"
)
// NewHeroicOPStarter creates a new heroic opportunity starter
func NewHeroicOPStarter(db *database.Database) *HeroicOPStarter {
return &HeroicOPStarter{
db: db,
isNew: true,
Abilities: [6]int16{},
SaveNeeded: false,
}
}
// LoadHeroicOPStarter loads a heroic opportunity starter by ID
func LoadHeroicOPStarter(db *database.Database, id int32) (*HeroicOPStarter, error) {
starter := &HeroicOPStarter{
db: db,
isNew: false,
}
query := "SELECT id, start_class, starter_icon, ability1, ability2, ability3, ability4, ability5, ability6, name, description FROM heroic_op_starters WHERE id = ?"
err := db.QueryRow(query, id).Scan(
&starter.ID,
&starter.StartClass,
&starter.StarterIcon,
&starter.Abilities[0],
&starter.Abilities[1],
&starter.Abilities[2],
&starter.Abilities[3],
&starter.Abilities[4],
&starter.Abilities[5],
&starter.Name,
&starter.Description,
)
if err != nil {
return nil, fmt.Errorf("failed to load heroic op starter: %w", err)
}
starter.SaveNeeded = false
return starter, nil
}
// GetID returns the starter ID
func (hos *HeroicOPStarter) GetID() int32 {
hos.mu.RLock()
defer hos.mu.RUnlock()
return hos.ID
}
// Save persists the starter to the database
func (hos *HeroicOPStarter) Save() error {
hos.mu.Lock()
defer hos.mu.Unlock()
if !hos.SaveNeeded {
return nil
}
if hos.isNew {
// Insert new record
query := `INSERT INTO heroic_op_starters (id, start_class, starter_icon, ability1, ability2, ability3, ability4, ability5, ability6, name, description)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
_, err := hos.db.Exec(query,
hos.ID,
hos.StartClass,
hos.StarterIcon,
hos.Abilities[0],
hos.Abilities[1],
hos.Abilities[2],
hos.Abilities[3],
hos.Abilities[4],
hos.Abilities[5],
hos.Name,
hos.Description,
)
if err != nil {
return fmt.Errorf("failed to insert heroic op starter: %w", err)
}
hos.isNew = false
} else {
// Update existing record
query := `UPDATE heroic_op_starters SET start_class = ?, starter_icon = ?, ability1 = ?, ability2 = ?, ability3 = ?, ability4 = ?, ability5 = ?, ability6 = ?, name = ?, description = ? WHERE id = ?`
_, err := hos.db.Exec(query,
hos.StartClass,
hos.StarterIcon,
hos.Abilities[0],
hos.Abilities[1],
hos.Abilities[2],
hos.Abilities[3],
hos.Abilities[4],
hos.Abilities[5],
hos.Name,
hos.Description,
hos.ID,
)
if err != nil {
return fmt.Errorf("failed to update heroic op starter: %w", err)
}
}
hos.SaveNeeded = false
return nil
}
// Delete removes the starter from the database
func (hos *HeroicOPStarter) Delete() error {
hos.mu.Lock()
defer hos.mu.Unlock()
if hos.isNew {
return nil // Nothing to delete
}
query := "DELETE FROM heroic_op_starters WHERE id = ?"
_, err := hos.db.Exec(query, hos.ID)
if err != nil {
return fmt.Errorf("failed to delete heroic op starter: %w", err)
}
return nil
}
// Reload refreshes the starter data from the database
func (hos *HeroicOPStarter) Reload() error {
hos.mu.Lock()
defer hos.mu.Unlock()
if hos.isNew {
return fmt.Errorf("cannot reload unsaved heroic op starter")
}
query := "SELECT start_class, starter_icon, ability1, ability2, ability3, ability4, ability5, ability6, name, description FROM heroic_op_starters WHERE id = ?"
err := hos.db.QueryRow(query, hos.ID).Scan(
&hos.StartClass,
&hos.StarterIcon,
&hos.Abilities[0],
&hos.Abilities[1],
&hos.Abilities[2],
&hos.Abilities[3],
&hos.Abilities[4],
&hos.Abilities[5],
&hos.Name,
&hos.Description,
)
if err != nil {
return fmt.Errorf("failed to reload heroic op starter: %w", err)
}
hos.SaveNeeded = false
return nil
}
// Copy creates a deep copy of the starter
func (hos *HeroicOPStarter) Copy() *HeroicOPStarter {
hos.mu.RLock()
defer hos.mu.RUnlock()
newStarter := &HeroicOPStarter{
ID: hos.ID,
StartClass: hos.StartClass,
StarterIcon: hos.StarterIcon,
Abilities: hos.Abilities, // Arrays are copied by value
Name: hos.Name,
Description: hos.Description,
db: hos.db,
isNew: true, // Copy is always new unless explicitly saved
SaveNeeded: false,
}
return newStarter
}
// GetAbility returns the ability icon at the specified position
func (hos *HeroicOPStarter) GetAbility(position int) int16 {
hos.mu.RLock()
defer hos.mu.RUnlock()
if position < 0 || position >= MaxAbilities {
return AbilityIconNone
}
return hos.Abilities[position]
}
// SetAbility sets the ability icon at the specified position
func (hos *HeroicOPStarter) SetAbility(position int, abilityIcon int16) bool {
hos.mu.Lock()
defer hos.mu.Unlock()
if position < 0 || position >= MaxAbilities {
return false
}
hos.Abilities[position] = abilityIcon
hos.SaveNeeded = true
return true
}
// IsComplete checks if the starter chain is complete (has completion marker)
func (hos *HeroicOPStarter) IsComplete(position int) bool {
hos.mu.RLock()
defer hos.mu.RUnlock()
if position < 0 || position >= MaxAbilities {
return false
}
return hos.Abilities[position] == AbilityIconAny
}
// CanInitiate checks if the specified class can initiate this starter
func (hos *HeroicOPStarter) CanInitiate(playerClass int8) bool {
hos.mu.RLock()
defer hos.mu.RUnlock()
return hos.StartClass == ClassAny || hos.StartClass == playerClass
}
// MatchesAbility checks if the given ability matches the current position
func (hos *HeroicOPStarter) MatchesAbility(position int, abilityIcon int16) bool {
hos.mu.RLock()
defer hos.mu.RUnlock()
if position < 0 || position >= MaxAbilities {
return false
}
requiredAbility := hos.Abilities[position]
// Wildcard matches any ability
if requiredAbility == AbilityIconAny {
return true
}
// Exact match required
return requiredAbility == abilityIcon
}
// Validate checks if the starter is properly configured
func (hos *HeroicOPStarter) Validate() error {
hos.mu.RLock()
defer hos.mu.RUnlock()
if hos.ID <= 0 {
return fmt.Errorf("invalid starter ID: %d", hos.ID)
}
if hos.StarterIcon <= 0 {
return fmt.Errorf("invalid starter icon: %d", hos.StarterIcon)
}
// Check for at least one non-zero ability
hasAbility := false
for _, ability := range hos.Abilities {
if ability != AbilityIconNone {
hasAbility = true
break
}
}
if !hasAbility {
return fmt.Errorf("starter must have at least one ability")
}
return nil
}
// SetID sets the starter ID
func (hos *HeroicOPStarter) SetID(id int32) {
hos.mu.Lock()
defer hos.mu.Unlock()
hos.ID = id
hos.SaveNeeded = true
}
// SetStartClass sets the start class
func (hos *HeroicOPStarter) SetStartClass(startClass int8) {
hos.mu.Lock()
defer hos.mu.Unlock()
hos.StartClass = startClass
hos.SaveNeeded = true
}
// SetStarterIcon sets the starter icon
func (hos *HeroicOPStarter) SetStarterIcon(icon int16) {
hos.mu.Lock()
defer hos.mu.Unlock()
hos.StarterIcon = icon
hos.SaveNeeded = true
}
// SetName sets the starter name
func (hos *HeroicOPStarter) SetName(name string) {
hos.mu.Lock()
defer hos.mu.Unlock()
hos.Name = name
hos.SaveNeeded = true
}
// SetDescription sets the starter description
func (hos *HeroicOPStarter) SetDescription(description string) {
hos.mu.Lock()
defer hos.mu.Unlock()
hos.Description = description
hos.SaveNeeded = true
}