diff options
Diffstat (limited to 'internal/validate/checks.go')
| -rw-r--r-- | internal/validate/checks.go | 128 |
1 files changed, 34 insertions, 94 deletions
diff --git a/internal/validate/checks.go b/internal/validate/checks.go index 6994654..9d09eef 100644 --- a/internal/validate/checks.go +++ b/internal/validate/checks.go @@ -1,3 +1,5 @@ +// Package validate runs semantic/reference checks over the game data +// directory (dangling ids, bad triggers, unreachable wiring) and reports issues. package validate import ( @@ -78,7 +80,7 @@ func validateRooms(s Source) []Issue { } issues = append(issues, validateTriggerBlock( fmt.Sprintf("Room %d: exit %q on_traverse", id, dir), - exit.OnTraverse, false, itemIDs, roomIndex, mobIDs, nil)...) + exit.OnTraverse, itemIDs, roomIndex, mobIDs, nil)...) } for _, rm := range room.Mobs { @@ -230,7 +232,7 @@ func validateLocalObject(roomID int, robj world.RoomObject, itemIDs map[string]b } if len(def.OnLook) > 0 { - issues = append(issues, validateTriggerBlock(prefix+": on_look", def.OnLook, false, itemIDs, roomIndex, mobIDs, nil)...) + issues = append(issues, validateTriggerBlock(prefix+": on_look", def.OnLook, itemIDs, roomIndex, mobIDs, nil)...) } return issues @@ -495,7 +497,7 @@ func validateMobs(s Source) []Issue { } issues = append(issues, validateTriggerBlock( - fmt.Sprintf("Mob %q: on_kill", id), def.OnKill, false, itemIDs, roomIndex, mobIDs, dropIDs)...) + fmt.Sprintf("Mob %q: on_kill", id), def.OnKill, itemIDs, roomIndex, mobIDs, dropIDs)...) } return issues @@ -604,7 +606,7 @@ func validateObjects(s Source) []Issue { } issues = append(issues, validateTriggerBlock( - fmt.Sprintf("Object %q: on_use", id), obj.OnUse, false, itemIDs, roomIndex, mobIDs, dropIDs)...) + fmt.Sprintf("Object %q: on_use", id), obj.OnUse, itemIDs, roomIndex, mobIDs, dropIDs)...) if obj.Gather != nil { issues = append(issues, validateGather(fmt.Sprintf("Object %q: gather", id), obj.Gather, itemIDs, dropIDs)...) @@ -615,7 +617,7 @@ func validateObjects(s Source) []Issue { } if len(obj.OnLook) > 0 { - issues = append(issues, validateTriggerBlock(fmt.Sprintf("Object %q: on_look", id), obj.OnLook, false, itemIDs, roomIndex, mobIDs, dropIDs)...) + issues = append(issues, validateTriggerBlock(fmt.Sprintf("Object %q: on_look", id), obj.OnLook, itemIDs, roomIndex, mobIDs, dropIDs)...) } } @@ -649,7 +651,7 @@ func validateItems(s Source) []Issue { if len(def.Search) > 0 { issues = append(issues, validateTriggerBlock( - fmt.Sprintf("Item %q: search", id), def.Search, false, itemIDs, roomIndex, mobIDs, dropIDs)...) + fmt.Sprintf("Item %q: search", id), def.Search, itemIDs, roomIndex, mobIDs, dropIDs)...) } if def.FarmProduct != "" && !itemIDs[def.FarmProduct] { @@ -866,9 +868,8 @@ func validateCourses(s Source) []Issue { return issues } -// validateRoomTriggers validates every trigger-shaped block on every room: -// on_enter, on_exit (verb blocks), and on_flag_change / on_global_flag_change -// (flag blocks, which require on_player_flag/on_global_flag). +// validateRoomTriggers validates the on_enter and on_exit trigger blocks on +// every room. Flag-change triggers (data/triggers) are not validated here. func validateRoomTriggers(s Source) []Issue { var issues []Issue roomIndex := s.World.RoomIndex() @@ -882,11 +883,11 @@ func validateRoomTriggers(s Source) []Issue { } if len(room.OnEnter) > 0 { issues = append(issues, validateTriggerBlock( - fmt.Sprintf("Room %d: on_enter", id), room.OnEnter, false, itemIDs, roomIndex, mobIDs, nil)...) + fmt.Sprintf("Room %d: on_enter", id), room.OnEnter, itemIDs, roomIndex, mobIDs, nil)...) } if len(room.OnExit) > 0 { issues = append(issues, validateTriggerBlock( - fmt.Sprintf("Room %d: on_exit", id), room.OnExit, false, itemIDs, roomIndex, mobIDs, nil)...) + fmt.Sprintf("Room %d: on_exit", id), room.OnExit, itemIDs, roomIndex, mobIDs, nil)...) } } @@ -1379,87 +1380,30 @@ func validateCondition(prefix string, c *behavior.Condition, roomIndex map[int]b return issues } -// checkPlayerOnlyInCondition warns when a global-scope condition uses -// player-dependent predicates that cannot be evaluated without a player. -func checkPlayerOnlyInCondition(prefix string, c *behavior.Condition) []Issue { - if c == nil { - return nil - } - var issues []Issue - if c.PlayerFlag != "" { - issues = append(issues, Issue{ - Level: "WARN", - Type: "semantic", - Message: fmt.Sprintf("%s: player_flag %q has no effect on global-flag triggers (no player)", prefix, c.PlayerFlag), - }) - } - if c.HasItem != "" { - issues = append(issues, Issue{ - Level: "WARN", - Type: "semantic", - Message: fmt.Sprintf("%s: has_item %q has no effect on global-flag triggers (no player)", prefix, c.HasItem), - }) - } - if c.MinCredits > 0 { - issues = append(issues, Issue{ - Level: "WARN", - Type: "semantic", - Message: fmt.Sprintf("%s: min_credits has no effect on global-flag triggers (no player)", prefix), - }) - } - for i := range c.AllOf { - issues = append(issues, checkPlayerOnlyInCondition(prefix, &c.AllOf[i])...) - } - for i := range c.AnyOf { - issues = append(issues, checkPlayerOnlyInCondition(prefix, &c.AnyOf[i])...) - } - return issues -} - -// validateTriggerBlock validates a list of Trigger entries for one event block. -// isFlag is true for on_flag_change / on_global_flag_change blocks (which -// require on_player_flag/on_global_flag and forbid item_id) and false for verb -// blocks (on_use/on_look/on_kill/on_enter/on_exit/on_traverse), where item_id -// is checked against itemIDs. -func validateTriggerBlock(prefix string, block []behavior.Trigger, isFlag bool, itemIDs map[string]bool, roomIndex map[int]bool, mobIDs map[string]bool, dropIDs map[string]bool) []Issue { +// validateTriggerBlock validates a list of Trigger entries for one verb event +// block (on_use/on_look/on_kill/on_enter/on_exit/on_traverse), where item_id +// is checked against itemIDs. Flag-change blocks (data/triggers) are not +// validated here. +func validateTriggerBlock(prefix string, block []behavior.Trigger, itemIDs map[string]bool, roomIndex map[int]bool, mobIDs map[string]bool, dropIDs map[string]bool) []Issue { var issues []Issue for i := range block { t := &block[i] entryPrefix := fmt.Sprintf("%s[%d]", prefix, i) - if isFlag { - if t.OnPlayerFlag == "" && t.OnGlobalFlag == "" { - issues = append(issues, Issue{ - Level: "WARN", - Type: "semantic", - Message: fmt.Sprintf("%s: flag-change trigger has no on_player_flag/on_global_flag — it can never fire", - entryPrefix), - }) - } - if t.ItemID != "" { - issues = append(issues, Issue{ - Level: "WARN", - Type: "semantic", - Message: fmt.Sprintf("%s: item_id is ignored on flag-change triggers", - entryPrefix), - }) - } - } else { - if t.OnPlayerFlag != "" || t.OnGlobalFlag != "" { - issues = append(issues, Issue{ - Level: "WARN", - Type: "semantic", - Message: fmt.Sprintf("%s: on_player_flag/on_global_flag are ignored on verb triggers", - entryPrefix), - }) - } - if t.ItemID != "" && !itemIDs[t.ItemID] { - issues = append(issues, Issue{ - Level: "ERROR", - Type: "reference", - Message: fmt.Sprintf("%s: item_id %q does not exist", - entryPrefix, t.ItemID), - }) - } + if t.OnPlayerFlag != "" || t.OnGlobalFlag != "" { + issues = append(issues, Issue{ + Level: "WARN", + Type: "semantic", + Message: fmt.Sprintf("%s: on_player_flag/on_global_flag are ignored on verb triggers", + entryPrefix), + }) + } + if t.ItemID != "" && !itemIDs[t.ItemID] { + issues = append(issues, Issue{ + Level: "ERROR", + Type: "reference", + Message: fmt.Sprintf("%s: item_id %q does not exist", + entryPrefix, t.ItemID), + }) } if t.Lock && len(t.Steps) == 0 { issues = append(issues, Issue{ @@ -1471,14 +1415,10 @@ func validateTriggerBlock(prefix string, block []behavior.Trigger, isFlag bool, if t.Condition != nil { issues = append(issues, validateCondition(entryPrefix, t.Condition, roomIndex)...) } - if isFlag && t.OnGlobalFlag != "" && t.Condition != nil { - issues = append(issues, checkPlayerOnlyInCondition(entryPrefix, t.Condition)...) - } + for si := range t.Steps { issues = append(issues, validateStep(fmt.Sprintf("%s: step[%d]", entryPrefix, si), &t.Steps[si], itemIDs, roomIndex, mobIDs, dropIDs)...) - if isFlag && t.OnGlobalFlag != "" && t.Steps[si].Condition != nil { - issues = append(issues, checkPlayerOnlyInCondition(fmt.Sprintf("%s: step[%d]", entryPrefix, si), t.Steps[si].Condition)...) - } + } } return issues |
