303 lines
6.6 KiB
Go
303 lines
6.6 KiB
Go
package fights
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
nigiri "git.sharkk.net/Sharkk/Nigiri"
|
|
)
|
|
|
|
// Fight represents a fight, past or present
|
|
type Fight struct {
|
|
ID int `json:"id"`
|
|
UserID int `json:"user_id" db:"index"`
|
|
MonsterID int `json:"monster_id" db:"index"`
|
|
MonsterHP int `json:"monster_hp"`
|
|
MonsterMaxHP int `json:"monster_max_hp"`
|
|
MonsterSleep int `json:"monster_sleep"`
|
|
MonsterImmune int `json:"monster_immune"`
|
|
UberDamage int `json:"uber_damage"`
|
|
UberDefense int `json:"uber_defense"`
|
|
FirstStrike bool `json:"first_strike"`
|
|
Turn int `json:"turn"`
|
|
RanAway bool `json:"ran_away"`
|
|
Victory bool `json:"victory"`
|
|
Won bool `json:"won"`
|
|
RewardGold int `json:"reward_gold"`
|
|
RewardExp int `json:"reward_exp"`
|
|
Actions []ActionEntry `json:"actions"`
|
|
Created int64 `json:"created"`
|
|
Updated int64 `json:"updated"`
|
|
}
|
|
|
|
// Global store
|
|
var store *nigiri.BaseStore[Fight]
|
|
var db *nigiri.Collection
|
|
|
|
// Init sets up the Nigiri store and indices
|
|
func Init(collection *nigiri.Collection) {
|
|
db = collection
|
|
store = nigiri.NewBaseStore[Fight]()
|
|
|
|
// Register custom indices
|
|
store.RegisterIndex("byUserID", nigiri.BuildIntGroupIndex(func(f *Fight) int {
|
|
return f.UserID
|
|
}))
|
|
|
|
store.RegisterIndex("byMonsterID", nigiri.BuildIntGroupIndex(func(f *Fight) int {
|
|
return f.MonsterID
|
|
}))
|
|
|
|
store.RegisterIndex("activeFights", nigiri.BuildFilteredIntGroupIndex(
|
|
func(f *Fight) bool {
|
|
return !f.RanAway && !f.Victory
|
|
},
|
|
func(f *Fight) int {
|
|
return f.UserID
|
|
},
|
|
))
|
|
|
|
store.RegisterIndex("allByCreated", nigiri.BuildSortedListIndex(func(a, b *Fight) bool {
|
|
if a.Created != b.Created {
|
|
return a.Created > b.Created // DESC
|
|
}
|
|
return a.ID > b.ID // DESC
|
|
}))
|
|
|
|
store.RegisterIndex("allByUpdated", nigiri.BuildSortedListIndex(func(a, b *Fight) bool {
|
|
if a.Updated != b.Updated {
|
|
return a.Updated > b.Updated // DESC
|
|
}
|
|
return a.ID > b.ID // DESC
|
|
}))
|
|
|
|
store.RebuildIndices()
|
|
}
|
|
|
|
// GetStore returns the fights store
|
|
func GetStore() *nigiri.BaseStore[Fight] {
|
|
if store == nil {
|
|
panic("fights store not initialized - call Initialize first")
|
|
}
|
|
return store
|
|
}
|
|
|
|
// New creates a new Fight with sensible defaults
|
|
func New(userID, monsterID int) *Fight {
|
|
now := time.Now().Unix()
|
|
return &Fight{
|
|
UserID: userID,
|
|
MonsterID: monsterID,
|
|
MonsterHP: 0,
|
|
MonsterMaxHP: 0,
|
|
MonsterSleep: 0,
|
|
MonsterImmune: 0,
|
|
UberDamage: 0,
|
|
UberDefense: 0,
|
|
FirstStrike: false,
|
|
Turn: 0,
|
|
RanAway: false,
|
|
Victory: false,
|
|
Won: false,
|
|
RewardGold: 0,
|
|
RewardExp: 0,
|
|
Actions: make([]ActionEntry, 0),
|
|
Created: now,
|
|
Updated: now,
|
|
}
|
|
}
|
|
|
|
// Validate checks if fight has valid values
|
|
func (f *Fight) Validate() error {
|
|
if f.UserID <= 0 {
|
|
return fmt.Errorf("fight UserID must be positive")
|
|
}
|
|
if f.MonsterID <= 0 {
|
|
return fmt.Errorf("fight MonsterID must be positive")
|
|
}
|
|
if f.Turn < 0 {
|
|
return fmt.Errorf("fight Turn cannot be negative")
|
|
}
|
|
if f.MonsterHP < 0 {
|
|
return fmt.Errorf("fight MonsterHP cannot be negative")
|
|
}
|
|
if f.Created <= 0 {
|
|
return fmt.Errorf("fight Created timestamp must be positive")
|
|
}
|
|
if f.Updated <= 0 {
|
|
return fmt.Errorf("fight Updated timestamp must be positive")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CRUD operations
|
|
func (f *Fight) Save() error {
|
|
f.Updated = time.Now().Unix()
|
|
if f.ID == 0 {
|
|
id, err := store.Create(f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.ID = id
|
|
return nil
|
|
}
|
|
return store.Update(f.ID, f)
|
|
}
|
|
|
|
func (f *Fight) Delete() error {
|
|
store.Remove(f.ID)
|
|
return nil
|
|
}
|
|
|
|
// Insert with ID assignment
|
|
func (f *Fight) Insert() error {
|
|
f.Updated = time.Now().Unix()
|
|
id, err := store.Create(f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.ID = id
|
|
return nil
|
|
}
|
|
|
|
// Query functions
|
|
func Find(id int) (*Fight, error) {
|
|
fight, exists := store.Find(id)
|
|
if !exists {
|
|
return nil, fmt.Errorf("fight with ID %d not found", id)
|
|
}
|
|
return fight, nil
|
|
}
|
|
|
|
func All() ([]*Fight, error) {
|
|
return store.AllSorted("allByCreated"), nil
|
|
}
|
|
|
|
func ByUserID(userID int) ([]*Fight, error) {
|
|
return store.GroupByIndex("byUserID", userID), nil
|
|
}
|
|
|
|
func ByMonsterID(monsterID int) ([]*Fight, error) {
|
|
return store.GroupByIndex("byMonsterID", monsterID), nil
|
|
}
|
|
|
|
func ActiveByUserID(userID int) ([]*Fight, error) {
|
|
return store.GroupByIndex("activeFights", userID), nil
|
|
}
|
|
|
|
func Active() ([]*Fight, error) {
|
|
result := store.FilterByIndex("allByCreated", func(f *Fight) bool {
|
|
return !f.RanAway && !f.Victory
|
|
})
|
|
return result, nil
|
|
}
|
|
|
|
func Recent(within time.Duration) ([]*Fight, error) {
|
|
cutoff := time.Now().Add(-within).Unix()
|
|
|
|
result := store.FilterByIndex("allByCreated", func(f *Fight) bool {
|
|
return f.Created >= cutoff
|
|
})
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// Helper methods
|
|
func (f *Fight) CreatedTime() time.Time {
|
|
return time.Unix(f.Created, 0)
|
|
}
|
|
|
|
func (f *Fight) UpdatedTime() time.Time {
|
|
return time.Unix(f.Updated, 0)
|
|
}
|
|
|
|
func (f *Fight) IsActive() bool {
|
|
return !f.RanAway && !f.Victory
|
|
}
|
|
|
|
func (f *Fight) IsComplete() bool {
|
|
return f.RanAway || f.Victory
|
|
}
|
|
|
|
func (f *Fight) GetStatus() string {
|
|
if f.Won {
|
|
return "Won"
|
|
}
|
|
if f.Victory && !f.Won {
|
|
return "Lost"
|
|
}
|
|
if f.RanAway {
|
|
return "Ran Away"
|
|
}
|
|
return "Active"
|
|
}
|
|
|
|
func (f *Fight) GetMonsterHealthPercent() float64 {
|
|
if f.MonsterMaxHP <= 0 {
|
|
return 0.0
|
|
}
|
|
return float64(f.MonsterHP) / float64(f.MonsterMaxHP) * 100.0
|
|
}
|
|
|
|
func (f *Fight) IsMonsterSleeping() bool {
|
|
return f.MonsterSleep > 0
|
|
}
|
|
|
|
func (f *Fight) IsMonsterImmune() bool {
|
|
return f.MonsterImmune > 0
|
|
}
|
|
|
|
func (f *Fight) HasUberBonus() bool {
|
|
return f.UberDamage > 0 || f.UberDefense > 0
|
|
}
|
|
|
|
func (f *Fight) GetDuration() time.Duration {
|
|
return time.Unix(f.Updated, 0).Sub(time.Unix(f.Created, 0))
|
|
}
|
|
|
|
func (f *Fight) EndFight(victory bool) {
|
|
f.Victory = victory
|
|
f.RanAway = !victory
|
|
f.Updated = time.Now().Unix()
|
|
}
|
|
|
|
func (f *Fight) WinFight(goldReward, expReward int) {
|
|
f.Victory = true
|
|
f.Won = true
|
|
f.RanAway = false
|
|
f.RewardGold = goldReward
|
|
f.RewardExp = expReward
|
|
f.Updated = time.Now().Unix()
|
|
}
|
|
|
|
func (f *Fight) LoseFight() {
|
|
f.Victory = true
|
|
f.Won = false
|
|
f.RanAway = false
|
|
f.Updated = time.Now().Unix()
|
|
}
|
|
|
|
func (f *Fight) RunAway() {
|
|
f.RanAway = true
|
|
f.Victory = false
|
|
f.Updated = time.Now().Unix()
|
|
}
|
|
|
|
func (f *Fight) IncrementTurn() {
|
|
f.Turn++
|
|
f.Updated = time.Now().Unix()
|
|
}
|
|
|
|
func (f *Fight) SetMonsterHP(hp int) {
|
|
f.MonsterHP = hp
|
|
f.Updated = time.Now().Unix()
|
|
}
|
|
|
|
func (f *Fight) DamageMonster(damage int) {
|
|
f.MonsterHP -= damage
|
|
if f.MonsterHP < 0 {
|
|
f.MonsterHP = 0
|
|
}
|
|
f.Updated = time.Now().Unix()
|
|
}
|