aboutsummaryrefslogtreecommitdiff
path: root/internal/validate
diff options
context:
space:
mode:
Diffstat (limited to 'internal/validate')
-rw-r--r--internal/validate/checks.go101
1 files changed, 71 insertions, 30 deletions
diff --git a/internal/validate/checks.go b/internal/validate/checks.go
index aee46f2..1c87c67 100644
--- a/internal/validate/checks.go
+++ b/internal/validate/checks.go
@@ -56,6 +56,13 @@ func validateRooms(s Source) []Issue {
Message: fmt.Sprintf("Room %d: invalid color %q", id, room.Color),
})
}
+ // A conditional description whose condition references a missing room
+ // or item silently blanks the room text at runtime.
+ for i := range room.Description {
+ if c := room.Description[i].Condition; c != nil {
+ issues = append(issues, validateCondition(fmt.Sprintf("Room %d: description[%d]", id, i), c, roomIndex, itemIDs)...)
+ }
+ }
for dir, exit := range room.Exits {
switch dir {
@@ -249,6 +256,14 @@ func validateLocalObject(roomID int, robj world.RoomObject, itemIDs map[string]b
})
}
+ // A conditional description whose condition is malformed silently hides
+ // the object at runtime; validate it like any other condition.
+ for i := range def.Description {
+ if c := def.Description[i].Condition; c != nil {
+ issues = append(issues, validateCondition(fmt.Sprintf("%s: description[%d]", prefix, i), c, roomIndex, itemIDs)...)
+ }
+ }
+
if len(def.OnLook) > 0 {
issues = append(issues, validateTriggerBlock(prefix+": on_look", def.OnLook, itemIDs, roomIndex, mobIDs, dropIDs)...)
}
@@ -698,6 +713,12 @@ func validateObjects(s Source) []Issue {
issues = append(issues, validateGather(fmt.Sprintf("Object %q: gather", id), obj.Gather, itemIDs, dropIDs)...)
}
+ for i := range obj.Description {
+ if c := obj.Description[i].Condition; c != nil {
+ issues = append(issues, validateCondition(fmt.Sprintf("Object %q: description[%d]", id, i), c, roomIndex, itemIDs)...)
+ }
+ }
+
if obj.Talk != nil {
issues = append(issues, validateTalkConfig(fmt.Sprintf("Object %q: talk", id), obj.Talk, itemIDs, roomIndex, mobIDs, dropIDs)...)
}
@@ -803,6 +824,17 @@ func validateCraftBlocks(s Source) []Issue {
craftLabel = c.Type + "/" + c.Subtype
}
+ // SuccessChance clamps to Cap; an omitted cap makes the recipe
+ // fail every time (see craftFailureReachable, which would then
+ // mask it as an intentional always-fail recipe).
+ if c.Success != nil && c.Success.Cap <= 0 {
+ issues = append(issues, Issue{
+ Level: "WARN",
+ Type: "config",
+ Message: fmt.Sprintf("Item %q (craft: %s): success.cap is %v; every success roll clamps to zero", item.ID, craftLabel, c.Success.Cap),
+ })
+ }
+
for _, e := range c.Ingredients {
if len(e.Items) == 0 {
issues = append(issues, Issue{
@@ -1367,12 +1399,8 @@ func validateRoomGrid(s Source) []Issue {
}
}
- for _, origin := range seeds {
- if placed[origin] {
- continue
- }
-
- grid := world.BuildGrid(origin, load, include, nil, func(c world.GridConflict) {
+ onConf := func(origin int) func(c world.GridConflict) {
+ return func(c world.GridConflict) {
switch c.Kind {
case "twist":
issues = append(issues, Issue{
@@ -1391,33 +1419,18 @@ func validateRoomGrid(s Source) []Issue {
c.Target, c.From, c.Dir, c.Want[0], c.Want[1], c.Want[2], c.Occupier, origin),
})
}
- })
-
- for rid := range grid.Coord {
- placed[rid] = true
}
}
- onConf := func(origin int) func(c world.GridConflict) {
- return func(c world.GridConflict) {
- switch c.Kind {
- case "twist":
- issues = append(issues, Issue{
- Level: "ERROR",
- Type: "integrity",
- Message: fmt.Sprintf(
- "Grid twist: room %d (via %d %s) maps to (%d,%d,%d) but was already placed at (%d,%d,%d) [origin %d]",
- c.Target, c.From, c.Dir, c.Want[0], c.Want[1], c.Want[2], c.Existing[0], c.Existing[1], c.Existing[2], origin),
- })
- case "overlap":
- issues = append(issues, Issue{
- Level: "ERROR",
- Type: "integrity",
- Message: fmt.Sprintf(
- "Grid overlap: room %d (via %d %s) wants (%d,%d,%d), already used by room %d [origin %d]",
- c.Target, c.From, c.Dir, c.Want[0], c.Want[1], c.Want[2], c.Occupier, origin),
- })
- }
+ for _, origin := range seeds {
+ if placed[origin] {
+ continue
+ }
+
+ grid := world.BuildGrid(origin, load, include, nil, onConf(origin))
+
+ for rid := range grid.Coord {
+ placed[rid] = true
}
}
@@ -1487,6 +1500,16 @@ func validateTechs(s Source) []Issue {
func validateGather(prefix string, cfg *behavior.GatherConfig, itemIDs map[string]bool, dropIDs map[string]bool) []Issue {
var issues []Issue
+ // SuccessChance clamps to Cap, so an omitted (zero) cap makes every roll
+ // fail forever. Warn instead of letting the content silently never pay.
+ if cfg.Success.Cap <= 0 {
+ issues = append(issues, Issue{
+ Level: "WARN",
+ Type: "config",
+ Message: fmt.Sprintf("%s: success.cap is %v; every success roll clamps to zero (set cap to the intended maximum chance)", prefix, cfg.Success.Cap),
+ })
+ }
+
if cfg.Bait != "" && !itemIDs[cfg.Bait] {
issues = append(issues, Issue{
Level: "ERROR",
@@ -1558,6 +1581,9 @@ func validateTalkConfig(prefix string, cfg *behavior.TalkConfig, itemIDs map[str
if node.Action != nil {
issues = append(issues, validateTalkStep(nodePrefix, node.Action, itemIDs, roomIndex, mobIDs, dropIDs)...)
}
+ if node.Condition != nil {
+ issues = append(issues, validateCondition(nodePrefix, node.Condition, roomIndex, itemIDs)...)
+ }
if node.Goto != "" {
if _, ok := cfg.Nodes[node.Goto]; !ok {
issues = append(issues, Issue{
@@ -1572,6 +1598,21 @@ func validateTalkConfig(prefix string, cfg *behavior.TalkConfig, itemIDs map[str
if opt.Action != nil {
issues = append(issues, validateTalkStep(optPrefix, opt.Action, itemIDs, roomIndex, mobIDs, dropIDs)...)
}
+ if opt.Condition != nil {
+ issues = append(issues, validateCondition(optPrefix, opt.Condition, roomIndex, itemIDs)...)
+ }
+ // An unknown option goto silently ends the conversation at
+ // runtime (act_talk.advanceTalk), so it is an error here just
+ // like a node-level goto.
+ if opt.Goto != "" {
+ if _, ok := cfg.Nodes[opt.Goto]; !ok {
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "reference",
+ Message: fmt.Sprintf("%s: goto points to nonexistent node %q", optPrefix, opt.Goto),
+ })
+ }
+ }
}
}