aboutsummaryrefslogtreecommitdiff
path: root/internal/validate/validate.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/validate/validate.go')
-rw-r--r--internal/validate/validate.go37
1 files changed, 27 insertions, 10 deletions
diff --git a/internal/validate/validate.go b/internal/validate/validate.go
index 6a05eb6..d77eafa 100644
--- a/internal/validate/validate.go
+++ b/internal/validate/validate.go
@@ -35,6 +35,18 @@ type CourseView struct {
ID string
StartRoom int
ObstacleRooms []int
+ // OnFailRooms are the authored on_fail destinations (>0). The runtime
+ // teleports to them unconditionally on an obstacle failure.
+ OnFailRooms []int
+}
+
+// KnownFieldTarget lets the game package contribute its own content types to
+// the KnownFields decode pass; the validate package cannot import game
+// without a cycle.
+type KnownFieldTarget struct {
+ SubDir string
+ Label string
+ NewDef func() any
}
// TechView is the subset of a tech definition needed for validation.
@@ -64,6 +76,13 @@ type Source struct {
// ModuleDestinations maps module id to destination room for transport
// modules. A destination that is not a real room soft-locks the player.
ModuleDestinations map[string]int
+ // TransportModules is the set of transport-category module IDs, so a
+ // transport module whose destination is omitted or <= 0 is still
+ // reported (the runtime saves the player into room 0).
+ TransportModules map[string]bool
+ // KnownFieldTargets are game-owned content types (techs, modules,
+ // courses, help) included in the strict KnownFields decode pass.
+ KnownFieldTargets []KnownFieldTarget
}
// Run executes all checks and returns the collected issues.
@@ -79,7 +98,7 @@ func Run(s Source) []Issue {
issues = append(issues, validateIDs(s.DataDir, dir.sub, dir.label)...)
}
- issues = append(issues, validateKnownFields(s.DataDir)...)
+ issues = append(issues, validateKnownFields(s.DataDir, s.KnownFieldTargets)...)
issues = append(issues, validateRoomFilenames(s.DataDir)...)
issues = append(issues, validateRooms(s)...)
@@ -126,31 +145,29 @@ func validateRoomFilenames(dataDir string) []Issue {
// silently discarded by yaml.Unmarshal. Only package-owned content types are
// covered (world/object/item/behavior); game-owned definitions are checked by
// their own loaders.
-func validateKnownFields(dataDir string) []Issue {
- targets := []struct {
- subDir string
- label string
- newDef func() any
- }{
+func validateKnownFields(dataDir string, extra []KnownFieldTarget) []Issue {
+ targets := []KnownFieldTarget{
{"rooms", "room", func() any { return new(world.Room) }},
{"mobs", "mob", func() any { return new(world.MobDef) }},
{"objects", "object", func() any { return new(object.ObjectDef) }},
{"items", "item", func() any { return new(item.ItemDef) }},
{"drops", "drop table", func() any { return new(behavior.DropTableDef) }},
{"hazards", "hazard", func() any { return new(world.HazardDef) }},
+ {"triggers", "trigger", func() any { return new(behavior.Trigger) }},
}
+ targets = append(targets, extra...)
var issues []Issue
for _, t := range targets {
- dir := filepath.Join(dataDir, t.subDir)
+ dir := filepath.Join(dataDir, t.SubDir)
_ = behavior.WalkYAMLDir(dir, func(path, id string, data []byte) error {
dec := yaml.NewDecoder(bytes.NewReader(data))
dec.KnownFields(true)
- if err := dec.Decode(t.newDef()); err != nil {
+ if err := dec.Decode(t.NewDef()); err != nil {
rel := strings.TrimPrefix(path, dataDir+"/")
issues = append(issues, Issue{
Level: "ERROR",
Type: "unknown-field",
- Message: fmt.Sprintf("%s %s: %v", t.label, rel, err),
+ Message: fmt.Sprintf("%s %s: %v", t.Label, rel, err),
})
}
return nil