aboutsummaryrefslogtreecommitdiff
path: root/internal/validate/checks.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/validate/checks.go')
-rw-r--r--internal/validate/checks.go64
1 files changed, 58 insertions, 6 deletions
diff --git a/internal/validate/checks.go b/internal/validate/checks.go
index bfa8342..aee46f2 100644
--- a/internal/validate/checks.go
+++ b/internal/validate/checks.go
@@ -531,6 +531,10 @@ func validateMobs(s Source) []Issue {
}
}
+ if def.Talk != nil {
+ issues = append(issues, validateTalkConfig(fmt.Sprintf("Mob %q: talk", id), def.Talk, itemIDs, roomIndex, mobIDs, dropIDs)...)
+ }
+
if def.Combat != nil && def.Combat.FinishingBlow != "" && !itemIDs[def.Combat.FinishingBlow] {
issues = append(issues, Issue{
Level: "ERROR",
@@ -709,6 +713,7 @@ func validateObjects(s Source) []Issue {
func validateItems(s Source) []Issue {
var issues []Issue
itemIDs := s.Items.IDSet()
+ objIDs := s.Objects.IDSet()
dropIDs := dropTableIDSet(s.DataDir)
roomIndex := s.World.RoomIndex()
mobIDs := s.Mobs.AllDefIDs()
@@ -744,6 +749,17 @@ func validateItems(s Source) []Issue {
id, def.FarmProduct),
})
}
+
+ // The runtime derives "<farm_patch_type>_patch" and requires that
+ // object to exist; a type with no matching patch can never be planted.
+ if def.FarmPatchType != "" && !objIDs[def.FarmPatchType+"_patch"] {
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "reference",
+ Message: fmt.Sprintf("Item %q (seed): farm_patch_type %q has no matching %q object",
+ id, def.FarmPatchType, def.FarmPatchType+"_patch"),
+ })
+ }
}
return issues
@@ -1117,6 +1133,16 @@ func validateCourses(s Source) []Issue {
})
}
}
+ for _, rid := range cv.OnFailRooms {
+ if _, ok := roomIndex[rid]; !ok {
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "reference",
+ Message: fmt.Sprintf("Course %q: obstacle on_fail room %d does not exist (a failed obstacle would teleport the player into a void)",
+ cv.ID, rid),
+ })
+ }
+ }
}
return issues
@@ -1268,7 +1294,7 @@ func validateRoomWiring(s Source) []Issue {
// does not exist: the game assigns the id unconditionally, so the player is
// saved into a void with no exits.
func validateModuleDestinations(s Source) []Issue {
- if len(s.ModuleDestinations) == 0 {
+ if len(s.ModuleDestinations) == 0 && len(s.TransportModules) == 0 {
return nil
}
roomIndex := s.World.RoomIndex()
@@ -1289,6 +1315,24 @@ func validateModuleDestinations(s Source) []Issue {
Message: fmt.Sprintf("Module %q: destination room %d does not exist", id, dest),
})
}
+ // A transport module whose destination was omitted or <= 0 is not in
+ // ModuleDestinations (the source only records positive destinations), so
+ // report it explicitly: the runtime assigns the id unconditionally.
+ tids := make([]string, 0, len(s.TransportModules))
+ for id := range s.TransportModules {
+ tids = append(tids, id)
+ }
+ sort.Strings(tids)
+ for _, id := range tids {
+ if dest, ok := s.ModuleDestinations[id]; ok && dest > 0 {
+ continue
+ }
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "reference",
+ Message: fmt.Sprintf("Module %q: transport module has no valid destination room (the player would be saved into room 0)", id),
+ })
+ }
return issues
}
@@ -1691,14 +1735,14 @@ func validateStep(prefix string, step *behavior.Step, itemIDs map[string]bool, r
}
if step.Condition != nil {
- issues = append(issues, validateCondition(prefix, step.Condition, roomIndex)...)
+ issues = append(issues, validateCondition(prefix, step.Condition, roomIndex, itemIDs)...)
}
return issues
}
// validateCondition checks a Condition tree's Room references (structural room
// IDs) and recurses into all_of/any_of.
-func validateCondition(prefix string, c *behavior.Condition, roomIndex map[int]bool) []Issue {
+func validateCondition(prefix string, c *behavior.Condition, roomIndex map[int]bool, itemIDs map[string]bool) []Issue {
if c == nil {
return nil
}
@@ -1713,11 +1757,19 @@ func validateCondition(prefix string, c *behavior.Condition, roomIndex map[int]b
})
}
}
+ if c.HasItem != "" && !itemIDs[c.HasItem] {
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "reference",
+ Message: fmt.Sprintf("%s: condition has_item references nonexistent item %q (the gate can never pass)",
+ prefix, c.HasItem),
+ })
+ }
for i := range c.AllOf {
- issues = append(issues, validateCondition(prefix, &c.AllOf[i], roomIndex)...)
+ issues = append(issues, validateCondition(prefix, &c.AllOf[i], roomIndex, itemIDs)...)
}
for i := range c.AnyOf {
- issues = append(issues, validateCondition(prefix, &c.AnyOf[i], roomIndex)...)
+ issues = append(issues, validateCondition(prefix, &c.AnyOf[i], roomIndex, itemIDs)...)
}
return issues
}
@@ -1755,7 +1807,7 @@ func validateTriggerBlock(prefix string, block []behavior.Trigger, itemIDs map[s
})
}
if t.Condition != nil {
- issues = append(issues, validateCondition(entryPrefix, t.Condition, roomIndex)...)
+ issues = append(issues, validateCondition(entryPrefix, t.Condition, roomIndex, itemIDs)...)
}
for si := range t.Steps {