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.
51 lines
1.6 KiB
Go
51 lines
1.6 KiB
Go
package storage
|
|
|
|
import (
|
|
"math/rand/v2"
|
|
"slices"
|
|
|
|
"github.com/denisovdennis/autohero/internal/model"
|
|
)
|
|
|
|
// FilterCapOfferableQuests drops quest templates whose id is in taken, then shuffles the rest
|
|
// deterministically from seed and returns at most limit entries. If limit <= 0, returns all offerable quests (still filtered).
|
|
func FilterCapOfferableQuests(all []model.Quest, taken map[int64]struct{}, limit int, seed int64) []model.Quest {
|
|
var offer []model.Quest
|
|
for _, q := range all {
|
|
if _, skip := taken[q.ID]; skip {
|
|
continue
|
|
}
|
|
offer = append(offer, q)
|
|
}
|
|
if len(offer) == 0 {
|
|
return offer
|
|
}
|
|
if limit <= 0 || len(offer) <= limit {
|
|
return offer
|
|
}
|
|
shuffled := slices.Clone(offer)
|
|
rng := rand.New(rand.NewPCG(uint64(seed), uint64(seed>>32)^0x9e3779b97f4a7c15))
|
|
for i := len(shuffled) - 1; i > 0; i-- {
|
|
j := rng.IntN(i + 1)
|
|
shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
|
|
}
|
|
return shuffled[:limit]
|
|
}
|
|
|
|
// questOfferDrySpellSalt mixes into the RNG seed so dry-spell draws are independent of quest shuffle draws.
|
|
const questOfferDrySpellSalt int64 = 0x8BADF00D
|
|
|
|
// QuestOfferDrySpellThisPeriod reports whether this hero/NPC/time bucket is a "dry spell" (no offers shown).
|
|
// Deterministic: same inputs always yield the same result. dryChance in [0,1]; 0 disables, 1 always dry.
|
|
func QuestOfferDrySpellThisPeriod(npcID, heroID, timeBucket int64, dryChance float64) bool {
|
|
if dryChance <= 0 {
|
|
return false
|
|
}
|
|
if dryChance >= 1 {
|
|
return true
|
|
}
|
|
seed := npcID ^ heroID ^ timeBucket ^ questOfferDrySpellSalt
|
|
rng := rand.New(rand.NewPCG(uint64(seed), uint64(seed>>32)^0x9e3779b97f4a7c15))
|
|
return rng.Float64() < dryChance
|
|
}
|