03a281d009
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
160 lines
3.8 KiB
Go
160 lines
3.8 KiB
Go
package empyrionitems
|
|
|
|
// Loot group parser for Empyrion's `LootGroups.ecf`.
|
|
// Format:
|
|
//
|
|
// { +LootGroup Name: EscapePodEasy
|
|
// Count: all
|
|
// Item_0: EmergencyRations, param1: 1
|
|
// Item_1: WaterBottle, param1: 5
|
|
// ...
|
|
// }
|
|
//
|
|
// param1 = count (or "low-high" range), param2 = optional probability.
|
|
// Item names are canonical Item.Name tokens.
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
type LootEntry struct {
|
|
Item string `json:"item"`
|
|
Count string `json:"count,omitempty"`
|
|
Probability string `json:"probability,omitempty"`
|
|
}
|
|
|
|
type LootGroup struct {
|
|
Name string `json:"name"`
|
|
Count string `json:"count,omitempty"`
|
|
Items []LootEntry `json:"items"`
|
|
}
|
|
|
|
type LootIndex struct {
|
|
Groups []LootGroup
|
|
byName map[string]*LootGroup
|
|
byItem map[string][]string // ingredientLower → group names
|
|
}
|
|
|
|
func LoadLootFromFile(path string) (*LootIndex, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
return LoadLootFromReader(f)
|
|
}
|
|
|
|
func LoadLootFromReader(r io.Reader) (*LootIndex, error) {
|
|
idx := &LootIndex{byName: map[string]*LootGroup{}, byItem: map[string][]string{}}
|
|
sc := bufio.NewScanner(r)
|
|
sc.Buffer(make([]byte, 1024*1024), 1024*1024)
|
|
|
|
var current *LootGroup
|
|
var depth int
|
|
|
|
for sc.Scan() {
|
|
line := stripComment(sc.Text())
|
|
trim := strings.TrimSpace(line)
|
|
if trim == "" {
|
|
continue
|
|
}
|
|
|
|
if current == nil {
|
|
if strings.HasPrefix(trim, "{ +LootGroup ") || strings.HasPrefix(trim, "{+LootGroup ") || strings.HasPrefix(trim, "{ LootGroup ") {
|
|
body := strings.TrimPrefix(trim, "{ +LootGroup ")
|
|
body = strings.TrimPrefix(body, "{+LootGroup ")
|
|
body = strings.TrimPrefix(body, "{ LootGroup ")
|
|
current = &LootGroup{}
|
|
for _, kv := range splitTopLevel(body, ',') {
|
|
parts := strings.SplitN(strings.TrimSpace(kv), ":", 2)
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
k := strings.TrimSpace(parts[0])
|
|
v := stripQuotes(strings.TrimSpace(parts[1]))
|
|
if strings.EqualFold(k, "name") {
|
|
current.Name = v
|
|
}
|
|
}
|
|
depth = 1
|
|
}
|
|
continue
|
|
}
|
|
|
|
opens := strings.Count(trim, "{")
|
|
closes := strings.Count(trim, "}")
|
|
depth += opens - closes
|
|
if depth <= 0 {
|
|
if current.Name != "" {
|
|
cp := *current
|
|
idx.Groups = append(idx.Groups, cp)
|
|
gp := &idx.Groups[len(idx.Groups)-1]
|
|
idx.byName[strings.ToLower(cp.Name)] = gp
|
|
for _, it := range cp.Items {
|
|
low := strings.ToLower(it.Item)
|
|
idx.byItem[low] = append(idx.byItem[low], cp.Name)
|
|
}
|
|
}
|
|
current = nil
|
|
depth = 0
|
|
continue
|
|
}
|
|
|
|
parts := strings.SplitN(trim, ":", 2)
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
key := strings.TrimSpace(parts[0])
|
|
val := stripComment(strings.TrimSpace(parts[1]))
|
|
|
|
if strings.HasPrefix(strings.ToLower(key), "item_") {
|
|
// Item_N: Name, param1: count, param2: probability
|
|
le := LootEntry{}
|
|
pieces := splitTopLevel(val, ',')
|
|
for i, p := range pieces {
|
|
p = strings.TrimSpace(p)
|
|
if i == 0 {
|
|
le.Item = stripQuotes(p)
|
|
continue
|
|
}
|
|
kv := strings.SplitN(p, ":", 2)
|
|
if len(kv) != 2 {
|
|
continue
|
|
}
|
|
kk := strings.ToLower(strings.TrimSpace(kv[0]))
|
|
vv := strings.TrimSpace(kv[1])
|
|
switch kk {
|
|
case "param1", "param":
|
|
le.Count = vv
|
|
case "param2", "xdata":
|
|
le.Probability = vv
|
|
}
|
|
}
|
|
if le.Item != "" {
|
|
current.Items = append(current.Items, le)
|
|
}
|
|
} else if strings.EqualFold(key, "Count") {
|
|
current.Count = strings.TrimSpace(val)
|
|
}
|
|
}
|
|
if err := sc.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return idx, nil
|
|
}
|
|
|
|
func (idx *LootIndex) ByName(name string) *LootGroup {
|
|
if g, ok := idx.byName[strings.ToLower(name)]; ok {
|
|
c := *g
|
|
return &c
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (idx *LootIndex) DroppedBy(itemName string) []string {
|
|
return append([]string{}, idx.byItem[strings.ToLower(itemName)]...)
|
|
}
|