aboutsummaryrefslogtreecommitdiff
path: root/internal/game/core_startup.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/core_startup.go')
-rw-r--r--internal/game/core_startup.go80
1 files changed, 80 insertions, 0 deletions
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)