8a94ffd58f
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
171 lines
4.1 KiB
Go
171 lines
4.1 KiB
Go
package empyrionitems
|
|
|
|
// Template (recipe) parser for Empyrion's `Templates.ecf`.
|
|
// Format snippet:
|
|
//
|
|
// { +Template Name: PlasticMaterial
|
|
// BaseItem: true
|
|
// CraftTime: 2
|
|
// Target: "SurvC,SmallC,HoverC,BaseC,LargeC,AdvC"
|
|
// { Child Inputs
|
|
// CrushedStone: 1
|
|
// }
|
|
// }
|
|
//
|
|
// We extract Name + outer props (CraftTime, Target, BaseItem,
|
|
// OutputCount, etc.) and the Inputs map.
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
type Template struct {
|
|
Name string `json:"name"`
|
|
BaseItem bool `json:"baseItem,omitempty"`
|
|
CraftTime string `json:"craftTime,omitempty"`
|
|
Target string `json:"target,omitempty"`
|
|
OutputCount string `json:"outputCount,omitempty"`
|
|
Ref string `json:"ref,omitempty"`
|
|
Inputs map[string]string `json:"inputs"` // ingredientName → count token
|
|
Extras map[string]string `json:"extras,omitempty"`
|
|
}
|
|
|
|
type TemplateIndex struct {
|
|
Templates []Template
|
|
byName map[string]*Template
|
|
}
|
|
|
|
func LoadTemplatesFromFile(path string) (*TemplateIndex, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
return LoadTemplatesFromReader(f)
|
|
}
|
|
|
|
func LoadTemplatesFromReader(r io.Reader) (*TemplateIndex, error) {
|
|
idx := &TemplateIndex{byName: map[string]*Template{}}
|
|
sc := bufio.NewScanner(r)
|
|
sc.Buffer(make([]byte, 1024*1024), 1024*1024)
|
|
|
|
var current *Template
|
|
var inInputs bool
|
|
var depth int
|
|
|
|
for sc.Scan() {
|
|
line := stripComment(sc.Text())
|
|
trim := strings.TrimSpace(line)
|
|
if trim == "" {
|
|
continue
|
|
}
|
|
|
|
if current == nil {
|
|
if strings.HasPrefix(trim, "{ +Template ") || strings.HasPrefix(trim, "{+Template ") || strings.HasPrefix(trim, "{ Template ") {
|
|
body := strings.TrimPrefix(trim, "{ +Template ")
|
|
body = strings.TrimPrefix(body, "{+Template ")
|
|
body = strings.TrimPrefix(body, "{ Template ")
|
|
body = strings.TrimSpace(body)
|
|
current = &Template{Inputs: map[string]string{}, Extras: map[string]string{}}
|
|
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]))
|
|
switch strings.ToLower(k) {
|
|
case "name":
|
|
current.Name = v
|
|
case "ref":
|
|
current.Ref = v
|
|
}
|
|
}
|
|
depth = 1
|
|
}
|
|
continue
|
|
}
|
|
|
|
opens := strings.Count(trim, "{")
|
|
closes := strings.Count(trim, "}")
|
|
depth += opens - closes
|
|
if depth <= 0 {
|
|
if current.Name != "" {
|
|
cp := *current
|
|
idx.Templates = append(idx.Templates, cp)
|
|
idx.byName[strings.ToLower(cp.Name)] = &idx.Templates[len(idx.Templates)-1]
|
|
}
|
|
current = nil
|
|
inInputs = false
|
|
depth = 0
|
|
continue
|
|
}
|
|
|
|
// Track child blocks like `{ Child Inputs`.
|
|
if strings.HasPrefix(trim, "{ Child Inputs") || strings.HasPrefix(trim, "{Child Inputs") {
|
|
inInputs = true
|
|
continue
|
|
}
|
|
if inInputs && strings.HasPrefix(trim, "}") {
|
|
inInputs = false
|
|
continue
|
|
}
|
|
|
|
// Property line.
|
|
parts := strings.SplitN(trim, ":", 2)
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
key := strings.TrimSpace(parts[0])
|
|
val := stripQuotes(strings.TrimSpace(parts[1]))
|
|
val = strings.TrimSpace(stripComment(val))
|
|
|
|
if inInputs {
|
|
current.Inputs[key] = val
|
|
continue
|
|
}
|
|
switch strings.ToLower(key) {
|
|
case "baseitem":
|
|
current.BaseItem = strings.EqualFold(val, "true")
|
|
case "crafttime":
|
|
current.CraftTime = val
|
|
case "target":
|
|
current.Target = val
|
|
case "outputcount":
|
|
current.OutputCount = val
|
|
default:
|
|
current.Extras[key] = val
|
|
}
|
|
}
|
|
if err := sc.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return idx, nil
|
|
}
|
|
|
|
func (idx *TemplateIndex) ByName(name string) *Template {
|
|
if t, ok := idx.byName[strings.ToLower(name)]; ok {
|
|
c := *t
|
|
return &c
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UsedIn returns the list of templates whose inputs include the named item.
|
|
func (idx *TemplateIndex) UsedIn(itemName string) []Template {
|
|
want := strings.ToLower(itemName)
|
|
out := []Template{}
|
|
for _, t := range idx.Templates {
|
|
for k := range t.Inputs {
|
|
if strings.ToLower(k) == want {
|
|
out = append(out, t)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|