aboutsummaryrefslogtreecommitdiff
path: root/internal/validate
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-19 03:15:59 -0400
committerhistoria <[not public]>2026-09-19 03:15:59 -0400
commit0e60567a7a0addb9421769ee7be7853bfa8788cb (patch)
tree7645112db0e4eae95b8a83c1e4f9aab38bede655 /internal/validate
parentcc33072eced3d5460512c09c42ec4531c807d7af (diff)
downloadthehouseoficarus-0e60567a7a0addb9421769ee7be7853bfa8788cb.tar.gz
refactor: simplifcation, change how rooms are stored in player yaml, remove legacy code
Diffstat (limited to 'internal/validate')
-rw-r--r--internal/validate/checks.go64
-rw-r--r--internal/validate/validate.go11
2 files changed, 73 insertions, 2 deletions
diff --git a/internal/validate/checks.go b/internal/validate/checks.go
index 1c87c67..6341108 100644
--- a/internal/validate/checks.go
+++ b/internal/validate/checks.go
@@ -9,6 +9,7 @@ import (
"sort"
"strings"
+ "gopkg.in/yaml.v3"
"thehouseoficarus/internal/behavior"
"thehouseoficarus/internal/casino"
"thehouseoficarus/internal/color"
@@ -132,6 +133,24 @@ func validateRooms(s Source) []Issue {
id, sp.ID),
})
}
+ // A non-positive quantity is silently skipped by every ground-item
+ // read path, so the spawn is dead content with no feedback.
+ if sp.Quantity <= 0 {
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "reference",
+ Message: fmt.Sprintf("Room %d: item spawn %q has quantity %d; it must be positive or the spawn can never appear",
+ id, sp.ID, sp.Quantity),
+ })
+ }
+ if sp.RespawnTicks < 0 {
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "reference",
+ Message: fmt.Sprintf("Room %d: item spawn %q has negative respawn_ticks %.1f",
+ id, sp.ID, sp.RespawnTicks),
+ })
+ }
}
var collEntries []roomObjEntry
@@ -1180,8 +1199,51 @@ func validateCourses(s Source) []Issue {
return issues
}
+// validateFlagTriggerFiles validates every file-backed flag trigger under
+// data/triggers. A file that sets neither on_player_flag nor on_global_flag is
+// loaded but indexed nowhere and can never fire, so it is an ERROR; the steps
+// are checked for dangling references like any other trigger block.
+func validateFlagTriggerFiles(s Source) []Issue {
+ var issues []Issue
+ roomIndex := s.World.RoomIndex()
+ itemIDs := s.Items.IDSet()
+ mobIDs := s.Mobs.AllDefIDs()
+ dropIDs := dropTableIDSet(s.DataDir)
+ dir := filepath.Join(s.DataDir, "triggers")
+ if err := behavior.WalkYAMLDir(dir, func(path, id string, data []byte) error {
+ var t behavior.Trigger
+ if err := yaml.Unmarshal(data, &t); err != nil {
+ // validateKnownFields already reports the decode error.
+ return nil
+ }
+ rel := strings.TrimPrefix(filepath.ToSlash(path), filepath.ToSlash(s.DataDir)+"/")
+ if t.OnPlayerFlag == "" && t.OnGlobalFlag == "" {
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "trigger",
+ Message: fmt.Sprintf("trigger %s: neither on_player_flag nor on_global_flag is set, so it is never indexed and can never fire", rel),
+ })
+ return nil
+ }
+ if t.Condition != nil {
+ issues = append(issues, validateCondition(rel, t.Condition, roomIndex, itemIDs)...)
+ }
+ for si := range t.Steps {
+ issues = append(issues, validateStep(fmt.Sprintf("%s: step[%d]", rel, si), &t.Steps[si], itemIDs, roomIndex, mobIDs, dropIDs)...)
+ }
+ return nil
+ }); err != nil {
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "trigger",
+ Message: fmt.Sprintf("triggers: could not scan trigger files: %v", err),
+ })
+ }
+ return issues
+}
+
// validateRoomTriggers validates the on_enter and on_exit trigger blocks on
-// every room. Flag-change triggers (data/triggers) are not validated here.
+// every room. Flag-change triggers (data/triggers) are validated separately.
func validateRoomTriggers(s Source) []Issue {
var issues []Issue
roomIndex := s.World.RoomIndex()
diff --git a/internal/validate/validate.go b/internal/validate/validate.go
index a7ce1b0..dbcf06f 100644
--- a/internal/validate/validate.go
+++ b/internal/validate/validate.go
@@ -110,6 +110,7 @@ func Run(s Source) []Issue {
issues = append(issues, validateDropTables(s)...)
issues = append(issues, validateCourses(s)...)
issues = append(issues, validateRoomTriggers(s)...)
+ issues = append(issues, validateFlagTriggerFiles(s)...)
issues = append(issues, validateTechs(s)...)
issues = append(issues, validateRoomWiring(s)...)
issues = append(issues, validateModuleDestinations(s)...)
@@ -127,13 +128,21 @@ func validateRoomFilenames(dataDir string) []Issue {
dir := filepath.Join(dataDir, "rooms")
var issues []Issue
if err := behavior.WalkYAMLDir(dir, func(path, id string, data []byte) error {
- if _, err := strconv.Atoi(id); err != nil {
+ n, convErr := strconv.Atoi(id)
+ if convErr != nil {
rel := strings.TrimPrefix(path, dataDir+"/")
issues = append(issues, Issue{
Level: "ERROR",
Type: "integrity",
Message: fmt.Sprintf("room %s: filename is not a numeric room ID and the file is ignored", rel),
})
+ } else if n <= 0 {
+ rel := strings.TrimPrefix(path, dataDir+"/")
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "integrity",
+ Message: fmt.Sprintf("room %s: room ID must be positive, got %d", rel, n),
+ })
}
return nil
}); err != nil {