119 lines
4.0 KiB
Go
119 lines
4.0 KiB
Go
package common
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
)
|
|
|
|
type CurrencyWarsBonfireOption struct {
|
|
EventID, OptionID, DialogID uint32
|
|
JsonPath, OptionShowType string
|
|
EffectParamList []struct{ Value float64 }
|
|
}
|
|
|
|
type CurrencyWarsBonfireRecipe struct {
|
|
ID, Option uint32
|
|
Status, Engine string
|
|
Effects []struct {
|
|
Trigger string
|
|
Actions []struct {
|
|
Action, Name string
|
|
Value json.RawMessage
|
|
}
|
|
}
|
|
}
|
|
|
|
type CurrencyWarsBonfire struct {
|
|
Events map[uint32]uint32
|
|
Options []CurrencyWarsBonfireOption
|
|
Configs map[string]CurrencyWarsBonfireRecipe
|
|
Heal map[uint32]uint32
|
|
}
|
|
|
|
// This is a labelled FFGO implementation reference, not a Client table capture.
|
|
// Its event IDs must additionally occur on the active Client run's node.
|
|
func LoadCurrencyWarsBonfire(c *CurrencyWarsCatalog) (*CurrencyWarsBonfire, error) {
|
|
raw, err := ReadResourceFile("resources/CurrencyWarsBonfire.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var r struct {
|
|
Format uint32 `json:"format_version"`
|
|
Client string `json:"target_client_version"`
|
|
Assembly string `json:"target_assembly_sha256"`
|
|
Binary string `json:"source_binary_sha256"`
|
|
Tables struct {
|
|
Events []struct{ ID, PetPalID, DialogID uint32 } `json:"events"`
|
|
Options []CurrencyWarsBonfireOption `json:"options"`
|
|
Heal []struct{ ChapterID, HealNum uint32 } `json:"heal"`
|
|
} `json:"tables"`
|
|
Configs map[string]CurrencyWarsBonfireRecipe `json:"configs"`
|
|
}
|
|
if err = json.Unmarshal(raw, &r); err != nil {
|
|
return nil, err
|
|
}
|
|
if c == nil || r.Format != 1 || r.Client != c.ClientVersion || r.Assembly != c.Provenance.GameAssemblySHA256 {
|
|
return nil, fmt.Errorf("CW Bonfire reference compatibility differs")
|
|
}
|
|
if h, e := hex.DecodeString(r.Binary); e != nil || len(h) != 32 {
|
|
return nil, fmt.Errorf("CW Bonfire source identity missing")
|
|
}
|
|
out := &CurrencyWarsBonfire{Events: map[uint32]uint32{}, Options: r.Tables.Options, Configs: r.Configs, Heal: map[uint32]uint32{}}
|
|
for _, v := range r.Tables.Events {
|
|
_, duplicate := out.Events[v.ID]
|
|
if v.ID == 0 || duplicate {
|
|
return nil, fmt.Errorf("CW Bonfire event identity invalid")
|
|
}
|
|
out.Events[v.ID] = v.DialogID
|
|
}
|
|
for _, v := range r.Tables.Heal {
|
|
if v.ChapterID == 0 || out.Heal[v.ChapterID] != 0 {
|
|
return nil, fmt.Errorf("CW Bonfire heal identity invalid")
|
|
}
|
|
out.Heal[v.ChapterID] = v.HealNum
|
|
}
|
|
seen := map[[2]uint32]bool{}
|
|
for _, v := range out.Options {
|
|
key := [2]uint32{v.EventID, v.OptionID}
|
|
_, eventExists := out.Events[v.EventID]
|
|
if v.EventID == 0 || v.OptionID == 0 || !eventExists || seen[key] {
|
|
return nil, fmt.Errorf("CW Bonfire option identity invalid")
|
|
}
|
|
seen[key] = true
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (b *CurrencyWarsBonfire) OptionRules(option CurrencyWarsBonfireOption) (map[string]uint32, error) {
|
|
path := strings.TrimPrefix(option.JsonPath, "Config/Level/GridFight/")
|
|
recipe, ok := b.Configs[path]
|
|
if !ok || recipe.ID != option.EventID || recipe.Option != option.OptionID || recipe.Status != "Implemented" || recipe.Engine != "TeaBreakOption" {
|
|
return nil, fmt.Errorf("CW Bonfire option %d/%d has no supported recipe", option.EventID, option.OptionID)
|
|
}
|
|
rules := map[string]uint32{}
|
|
for _, effect := range recipe.Effects {
|
|
if effect.Trigger != "Immediately" {
|
|
return nil, fmt.Errorf("CW Bonfire trigger %s not implemented", effect.Trigger)
|
|
}
|
|
for _, action := range effect.Actions {
|
|
var parameter string
|
|
_ = json.Unmarshal(action.Value, ¶meter)
|
|
if action.Action != "SetRule" || action.Name != "PetPalStarDown" || parameter != "#1" || len(option.EffectParamList) < 1 {
|
|
return nil, fmt.Errorf("CW Bonfire action %s/%s not implemented", action.Action, action.Name)
|
|
}
|
|
value := option.EffectParamList[0].Value
|
|
if math.IsNaN(value) || math.IsInf(value, 0) || value < 0 || value > 100 || value != math.Trunc(value) {
|
|
return nil, fmt.Errorf("CW Bonfire probability invalid")
|
|
}
|
|
rules[action.Name] = uint32(value)
|
|
}
|
|
}
|
|
if len(rules) == 0 {
|
|
return nil, fmt.Errorf("CW Bonfire option has no implemented effects")
|
|
}
|
|
return rules, nil
|
|
}
|