313 lines
7.2 KiB
Go
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
|
|
} |