From 15b7e221b799ef107dec44ecdb294026dfd3d059 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Wed, 24 Jun 2026 22:55:35 -0400 Subject: refactor: pulled techs out to yaml files, unified numerous combat functions, broke up game object --- internal/game/core_startup.go | 80 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) (limited to 'internal/game/core_startup.go') diff --git a/internal/game/core_startup.go b/internal/game/core_startup.go index 323e519..2e81eac 100644 --- a/internal/game/core_startup.go +++ b/internal/game/core_startup.go @@ -27,6 +27,7 @@ func (g *Game) ValidateStartup() []StartupIssue { issues = append(issues, validateIDs(g.DataDir, "drops", "drop table")...) issues = append(issues, validateIDs(g.DataDir, "courses", "course")...) issues = append(issues, validateIDs(g.DataDir, "modules", "module")...) + issues = append(issues, validateIDs(g.DataDir, "techs", "tech")...) issues = append(issues, validateIDs(g.DataDir, "help", "help topic")...) issues = append(issues, validateRooms(g)...) @@ -37,6 +38,7 @@ func (g *Game) ValidateStartup() []StartupIssue { issues = append(issues, validateCraftBlocks(g)...) issues = append(issues, validateDropTables(g)...) issues = append(issues, validateCourses(g)...) + issues = append(issues, validateTechs(g)...) issues = append(issues, validateRoomWiring(g)...) return issues @@ -747,6 +749,84 @@ func validateNodeAction(prefix string, na *action.NodeAction, itemIDs map[string return issues } +var knownTechCategories = map[string]bool{ + "accuracy": true, + "strength": true, + "defense": true, + "ranged": true, + "science": true, + "protection": true, + "utility": true, + "combo": true, +} + +// Reserved tech IDs whose semantics are coupled to code logic — +// damageAfterTechProtection maps attack types to protect_melee/ranged/science +// and killPlayer checks for retribution. These IDs must exist in YAML. +var reservedTechIDs = map[string]bool{ + "protect_melee": true, + "protect_ranged": true, + "protect_science": true, + "retribution": true, +} + +func validateTechs(g *Game) []StartupIssue { + var issues []StartupIssue + seen := make(map[string]bool) + for _, def := range AllTechs { + if def.ID == "" { + issues = append(issues, StartupIssue{ + Level: "ERROR", + Type: "reference", + Message: "Tech has empty id", + }) + continue + } + if seen[def.ID] { + issues = append(issues, StartupIssue{ + Level: "ERROR", + Type: "duplicate", + Message: fmt.Sprintf("Duplicate tech %q", def.ID), + }) + } + seen[def.ID] = true + + if def.Name == "" { + issues = append(issues, StartupIssue{ + Level: "WARN", + Type: "reference", + Message: fmt.Sprintf("Tech %q: has no name", def.ID), + }) + } + if def.Category != "" && !knownTechCategories[def.Category] { + issues = append(issues, StartupIssue{ + Level: "WARN", + Type: "reference", + Message: fmt.Sprintf("Tech %q: unknown category %q", def.ID, def.Category), + }) + } + if def.DrainRate < 0 { + issues = append(issues, StartupIssue{ + Level: "ERROR", + Type: "reference", + Message: fmt.Sprintf("Tech %q: negative drain_rate %.2f", def.ID, def.DrainRate), + }) + } + } + + for id := range reservedTechIDs { + if _, ok := techByID[id]; !ok { + issues = append(issues, StartupIssue{ + Level: "ERROR", + Type: "reference", + Message: fmt.Sprintf("Reserved tech %q does not exist — required by code (damage protection / retribution)", id), + }) + } + } + + return issues +} + func dropTableIDSet(dataDir string) map[string]bool { dir := filepath.Join(dataDir, "drops") ids := make(map[string]bool) -- cgit v1.2.3