package common import ( "encoding/json" "fmt" "sort" ) // EidolonMaterialIDs includes the separate rank-item catalog, which is not // part of InventoryItemConfig's general Material/Usable/Mission table. func EidolonMaterialIDs() ([]uint32, error) { raw, err := ReadResourceFile("resources/EidolonConfig.json", "EidolonConfig.json") if err != nil { return nil, err } var resource struct { Avatars map[uint32]struct { Ranks []struct { Cost []struct{ ItemID, ItemNum uint32 } `json:"cost"` } `json:"ranks"` } `json:"avatars"` ExMaterials map[uint32]struct { ItemID uint32 `json:"item_id"` Count uint32 `json:"count"` } `json:"ex_materials"` } if err := json.Unmarshal(raw, &resource); err != nil { return nil, err } if len(resource.Avatars) == 0 { return nil, fmt.Errorf("Eidolon item catalog is empty") } ids := map[uint32]bool{} for avatarID, avatar := range resource.Avatars { if avatarID == 0 || len(avatar.Ranks) == 0 { return nil, fmt.Errorf("invalid Eidolon avatar %d", avatarID) } for _, rank := range avatar.Ranks { if len(rank.Cost) == 0 { return nil, fmt.Errorf("missing Eidolon cost for %d", avatarID) } for _, cost := range rank.Cost { if cost.ItemID == 0 || cost.ItemNum == 0 { return nil, fmt.Errorf("invalid Eidolon cost for %d", avatarID) } ids[cost.ItemID] = true } } } for avatarID, grant := range resource.ExMaterials { if AvatarExRankMap[avatarID] == 0 || grant.ItemID == 0 || grant.Count == 0 { return nil, fmt.Errorf("invalid EX item for %d", avatarID) } ids[grant.ItemID] = true } out := make([]uint32, 0, len(ids)) for id := range ids { out = append(out, id) } sort.Slice(out, func(i, j int) bool { return out[i] < out[j] }) return out, nil }