Files
GaCy-GO/internal/common/technique_rules.go
T

108 lines
3.3 KiB
Go

package common
import (
"encoding/json"
"fmt"
"sync"
)
type TechniqueFood struct {
RecoverMP uint32 `json:"recover_mp"`
MaxUse uint32 `json:"max_use"`
PileLimit uint32 `json:"pile_limit"`
}
type MazeSkillRule struct {
ID uint32 `json:"MazeSkillId"`
MPCost uint32 `json:"MPCost"`
Trigger string `json:"SkillTriggerKey"`
}
type TechniqueRules struct {
ClientVersion string `json:"client_version"`
BaseMaxMP uint32 `json:"base_max_mp"`
InitialMP uint32 `json:"initial_mp"`
MaxMPByAvatar map[uint32]uint32 `json:"max_mp_by_avatar"`
NonAttackTechniques []uint32 `json:"non_attack_techniques"`
Avatars map[uint32][]MazeSkillRule `json:"avatars"`
Foods map[uint32]TechniqueFood `json:"foods"`
InitialFoodCount uint32 `json:"initial_food_count"`
AutoFoodDefault bool `json:"auto_food_default"`
}
var techniqueOnce sync.Once
var techniqueRules TechniqueRules
var techniqueErr error
func LoadTechniqueRules() (*TechniqueRules, error) {
techniqueOnce.Do(func() {
raw, err := ReadResourceFile("resources/TechniqueRules.json")
if err != nil {
techniqueErr = err
return
}
if err = json.Unmarshal(raw, &techniqueRules); err != nil {
techniqueErr = err
return
}
cfg, err := LoadRuntimeConfig()
if err != nil {
techniqueErr = err
return
}
if techniqueRules.ClientVersion != cfg.ClientVersion || techniqueRules.BaseMaxMP == 0 || techniqueRules.InitialMP > techniqueRules.BaseMaxMP {
techniqueErr = fmt.Errorf("invalid or mismatched TechniqueRules client resource")
return
}
for id, f := range techniqueRules.Foods {
if id == 0 || f.RecoverMP == 0 || f.MaxUse == 0 || techniqueRules.InitialFoodCount > f.PileLimit {
techniqueErr = fmt.Errorf("invalid technique food %d", id)
return
}
}
})
return &techniqueRules, techniqueErr
}
func (r *TechniqueRules) MaxMP(avatars map[uint32]uint32) uint32 {
maximum := r.BaseMaxMP
// PearlSR buildLineup selects the largest team cap; it does not add both caps.
for _, id := range avatars {
if value := r.MaxMPByAvatar[id]; value > maximum {
maximum = value
}
}
return maximum
}
// Called only by an explicit inventory/cast operation, never by save readers.
func (f *FreesrData) EnsureTechniqueFoods() error {
if f.TechniqueFoodStock != nil && f.AutoTechniqueFood != nil {
return nil
}
r, err := LoadTechniqueRules()
if err != nil {
return err
}
if f.TechniqueFoodStock == nil {
f.TechniqueFoodStock = map[uint32]uint32{}
for id := range r.Foods {
f.TechniqueFoodStock[id] = r.InitialFoodCount
}
}
if f.AutoTechniqueFood == nil {
value := r.AutoFoodDefault
f.AutoTechniqueFood = &value
}
return f.SavePersistentChecked()
}
// Refresh just the setting, and advance its merge baseline so a subsequent
// session position save cannot undo an external API toggle.
func (f *FreesrData) RefreshTechniqueSettings(p *Persistent) {
f.AutoTechniqueFood = p.AutoTechniqueFood
if snapshot, err := jsonObject(f.persistentSnapshot()); err == nil {
if f.persistentBaseline == nil {
f.persistentBaseline = map[string]any{}
}
f.persistentBaseline["auto_technique_food"] = snapshot["auto_technique_food"]
}
}