You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
2.8 KiB
Go
112 lines
2.8 KiB
Go
package game
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"math/rand"
|
|
"time"
|
|
|
|
"github.com/denisovdennis/autohero/internal/model"
|
|
"github.com/denisovdennis/autohero/internal/tuning"
|
|
)
|
|
|
|
// TryAutoEquipInMemory equips the item if the slot is empty or the new item improves combat
|
|
// rating by at least the runtime-configured threshold. Mutates hero.Gear. Does not touch the database.
|
|
func TryAutoEquipInMemory(hero *model.Hero, item *model.GearItem, now time.Time) bool {
|
|
hero.EnsureGearMap()
|
|
current := hero.Gear[item.Slot]
|
|
if current == nil {
|
|
hero.Gear[item.Slot] = item
|
|
return true
|
|
}
|
|
oldRating := hero.CombatRatingAt(now)
|
|
hero.Gear[item.Slot] = item
|
|
if hero.CombatRatingAt(now) >= oldRating*tuning.Get().AutoEquipThreshold {
|
|
return true
|
|
}
|
|
hero.Gear[item.Slot] = current
|
|
return false
|
|
}
|
|
|
|
// TryEquipOrStashOffline runs TryAutoEquipInMemory; if not equipped, appends to inventory
|
|
// or invokes onDiscard with an adventure-log message when the backpack is full.
|
|
func TryEquipOrStashOffline(hero *model.Hero, item *model.GearItem, now time.Time, onDiscard func(string)) {
|
|
hero.EnsureInventorySlice()
|
|
if TryAutoEquipInMemory(hero, item, now) {
|
|
return
|
|
}
|
|
if len(hero.Inventory) >= model.MaxInventorySlots {
|
|
if onDiscard != nil {
|
|
onDiscard(fmt.Sprintf("Inventory full — dropped %s (%s)", item.Name, item.Rarity))
|
|
}
|
|
return
|
|
}
|
|
hero.Inventory = append(hero.Inventory, item)
|
|
}
|
|
|
|
// AutoSellRandomInventoryShare sells a random share of inventory items.
|
|
// At least minShare (0..1) of current inventory is sold; returns sold count and gold gained.
|
|
func AutoSellRandomInventoryShare(hero *model.Hero, minShare float64, rng *rand.Rand) (int, int64) {
|
|
if hero == nil {
|
|
return 0, 0
|
|
}
|
|
hero.EnsureInventorySlice()
|
|
n := len(hero.Inventory)
|
|
if n == 0 {
|
|
return 0, 0
|
|
}
|
|
if minShare < 0 {
|
|
minShare = 0
|
|
}
|
|
if minShare > 1 {
|
|
minShare = 1
|
|
}
|
|
|
|
minSell := int(math.Ceil(float64(n) * minShare))
|
|
if minSell < 1 {
|
|
minSell = 1
|
|
}
|
|
if minSell > n {
|
|
minSell = n
|
|
}
|
|
|
|
var soldCount int
|
|
if n == minSell {
|
|
soldCount = n
|
|
} else if rng == nil {
|
|
soldCount = minSell + rand.Intn(n-minSell+1)
|
|
} else {
|
|
soldCount = minSell + rng.Intn(n-minSell+1)
|
|
}
|
|
|
|
perm := make([]int, n)
|
|
for i := 0; i < n; i++ {
|
|
perm[i] = i
|
|
}
|
|
if rng == nil {
|
|
rand.Shuffle(n, func(i, j int) { perm[i], perm[j] = perm[j], perm[i] })
|
|
} else {
|
|
rng.Shuffle(n, func(i, j int) { perm[i], perm[j] = perm[j], perm[i] })
|
|
}
|
|
|
|
sold := make(map[int]struct{}, soldCount)
|
|
for i := 0; i < soldCount; i++ {
|
|
sold[perm[i]] = struct{}{}
|
|
}
|
|
|
|
kept := make([]*model.GearItem, 0, n-soldCount)
|
|
var goldGained int64
|
|
for i, item := range hero.Inventory {
|
|
if _, ok := sold[i]; ok {
|
|
if item != nil {
|
|
goldGained += model.AutoSellPrice(item.Rarity)
|
|
}
|
|
continue
|
|
}
|
|
kept = append(kept, item)
|
|
}
|
|
hero.Inventory = kept
|
|
hero.Gold += goldGained
|
|
return soldCount, goldGained
|
|
}
|