This repository has been archived on 2026-10-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Ananta-ps/internal/catalogs/talent_tree_repository.go
T

154 lines
5.1 KiB
Go

package catalogs
import (
"encoding/json"
"os"
"path/filepath"
"sync"
)
var _ = json.RawMessage{}
var _ = sync.Mutex{}
var _ = os.ReadFile
var _ = filepath.Join
//
// Client talent tables (天赋树) imported from the 4229938 config dump into
// paths.clientConfigs:
// · TalentTreeGamePlayConfig.json — the gameplay ids the talent panel is split into (1..5).
// · TalentTreeConfig.json — one row per tree: how the client keys it (gameplay / job
// class / spirit) — see TalentTree.
// · TalentTreeTalentConfig.json — one row per talent node: owning tree, point cost, max layer.
// Optional, like the economy tables: a missing file only disables talent point accounting instead
// of refusing to start. The server needs these numbers because the client's AskActiveGameplayTalentLayer
// only carries (gameplayId, talentId, addLayer) — no cost — so a server that wants to debit points
// has to know what the node costs itself.
//
var (
TalentTreeRepository_cacheLock sync.RWMutex
TalentTreeRepository_cached = false
TalentTreeRepository_entries []any
)
// LoadTalentTreeRepository loads catalog records from client config directory
func LoadTalentTreeRepository(configDir string) ([]any, error) {
TalentTreeRepository_cacheLock.RLock()
if TalentTreeRepository_cached {
defer TalentTreeRepository_cacheLock.RUnlock()
return TalentTreeRepository_entries, nil
}
TalentTreeRepository_cacheLock.RUnlock()
TalentTreeRepository_cacheLock.Lock()
defer TalentTreeRepository_cacheLock.Unlock()
if TalentTreeRepository_cached { return TalentTreeRepository_entries, nil }
filePath := filepath.Join(configDir, "TalentTreeRepository.json")
data, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
var raw struct {
Records []json.RawMessage `json:"records"`
}
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
var result []any
for _, item := range raw.Records {
var m map[string]any
if err := json.Unmarshal(item, &m); err == nil {
result = append(result, m)
}
}
TalentTreeRepository_entries = result
TalentTreeRepository_cached = true
return result, nil
}
// Forces the (optional) tables to load so the startup log reports what was imported.
func TalentTreeRepository_Warmup() {
// Catalog helper TalentTreeRepository_Warmup
}
func TalentTreeRepository_TryTalent(id uint32, node TalentNode) bool {
// Catalog helper TalentTreeRepository_TryTalent
return false
}
//
// Nodes of every tree matching , de-duplicated by node id and
// ascending. A node can sit in several trees (33 of them do), so the union has to be distinct.
//
func TalentTreeRepository_NodesOfTrees(predicate any) []TalentNode {
// Catalog helper TalentTreeRepository_NodesOfTrees
return nil
}
//
// Every node belonging to one talent tree (TalentTreeid row), ascending by id.
// Needed by the career-talent seeding (TalentTreeSettings.SeedCareerTalents): nodes have
// prerequisites, so activating one node in isolation can still read as locked on the client.
// Publishing a whole tree sidesteps that dependency order entirely.
//
func TalentTreeRepository_NodesOfTree(treeId uint32) []TalentNode {
// Catalog helper TalentTreeRepository_NodesOfTree
return nil
}
func TalentTreeRepository_ReadRecords(path string, warnings []string) []any {
// Catalog helper TalentTreeRepository_ReadRecords
return nil
}
// Dump records may carry fully qualified keys ("LT.ConfigGen.IFoo.Id"), so match on the leaf name.
func TalentTreeRepository_TryProperty(row any, name string, value any) bool {
// Catalog helper TalentTreeRepository_TryProperty
return false
}
func TalentTreeRepository_TryUInt32(row any, name string, result uint32) bool {
// Catalog helper TalentTreeRepository_TryUInt32
return false
}
// One talent node. is the first TalentTreeid row (a node can
// belong to several trees in the dump; the panel key we echo back only needs one).
type TalentNode struct {
Id uint32 `json:"id"`
TreeId uint32 `json:"treeId"`
CostPoint uint32 `json:"costPoint"`
LayerNum uint32 `json:"layerNum"`
}
//
// One talent tree, i.e. one TalentTreeConfig row. The three ids are mutually exclusive
// in stock 4229938 and decide which client container the tree's nodes have to be published in
// (TalentTreeMgr.OnStartCurrent):
// <list type="bullet">
// <item>GameplayId != 0 — keyed by gameplay id, read from GameplayTalentInfos.</item>
// <item>JobClassId != 0 — keyed by job class, read from that career's
// SpiritJob.TalentInfo (so a tree only shows up for characters that own the career).</item>
// <item>both 0 — dumped into SpiritInfo.TalentInfo, further narrowed by
// SpiritIds (Kingpin lists the two protagonists; some rows pin a single spirit).</item>
// </list>
//
type TalentTree struct {
Id uint32 `json:"id"`
Name string `json:"name"`
GameplayId uint32 `json:"gameplayId"`
JobClassId uint32 `json:"jobClassId"`
SpiritIds []uint32 `json:"spiritIds"`
}
type TalentTreeRepository_Data struct {
GameplayIds []uint32 `json:"gameplayIds"`
Trees []TalentTree `json:"trees"`
Nodes map[uint32]TalentNode `json:"nodes"`
ByTree map[uint32][]TalentNode `json:"byTree"`
}