aboutsummaryrefslogtreecommitdiff
path: root/internal/validate/checks.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-07-10 17:02:13 -0400
committerhistoria <[not public]>2026-07-10 17:02:13 -0400
commitd02f0d2cdf67f1445a9ccae86b99a2578adab585 (patch)
treefc2caae674d20b9ac31aa0da41f4f600f8f801a9 /internal/validate/checks.go
parent18d46f9c336d37d0282eb7191bb7ff930163d18f (diff)
downloadthehouseoficarus-d02f0d2cdf67f1445a9ccae86b99a2578adab585.tar.gz
feat(admin): update search to use standard condition/action framework rather than bespoke implementation
Diffstat (limited to 'internal/validate/checks.go')
-rw-r--r--internal/validate/checks.go74
1 files changed, 53 insertions, 21 deletions
diff --git a/internal/validate/checks.go b/internal/validate/checks.go
index 1a0a233..f89e52d 100644
--- a/internal/validate/checks.go
+++ b/internal/validate/checks.go
@@ -69,7 +69,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)...)
+ exit.OnTraverse, false, itemIDs, roomIndex, mobIDs, nil)...)
}
for _, rm := range room.Mobs {
@@ -221,7 +221,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)...)
+ issues = append(issues, validateTriggerBlock(prefix+": on_look", def.OnLook, false, itemIDs, roomIndex, mobIDs, nil)...)
}
return issues
@@ -486,7 +486,7 @@ func validateMobs(s Source) []Issue {
}
issues = append(issues, validateTriggerBlock(
- fmt.Sprintf("Mob %q: on_kill", id), def.OnKill, false, itemIDs, roomIndex, mobIDs)...)
+ fmt.Sprintf("Mob %q: on_kill", id), def.OnKill, false, itemIDs, roomIndex, mobIDs, dropIDs)...)
}
return issues
@@ -591,7 +591,7 @@ func validateObjects(s Source) []Issue {
}
issues = append(issues, validateTriggerBlock(
- fmt.Sprintf("Object %q: on_use", id), obj.OnUse, false, itemIDs, roomIndex, mobIDs)...)
+ fmt.Sprintf("Object %q: on_use", id), obj.OnUse, false, itemIDs, roomIndex, mobIDs, dropIDs)...)
if obj.Gather != nil {
issues = append(issues, validateGather(fmt.Sprintf("Object %q: gather", id), obj.Gather, itemIDs, dropIDs)...)
@@ -602,7 +602,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)...)
+ issues = append(issues, validateTriggerBlock(fmt.Sprintf("Object %q: on_look", id), obj.OnLook, false, itemIDs, roomIndex, mobIDs, dropIDs)...)
}
}
@@ -613,6 +613,8 @@ func validateItems(s Source) []Issue {
var issues []Issue
itemIDs := s.Items.IDSet()
dropIDs := dropTableIDSet(s.DataDir)
+ roomIndex := s.World.RoomIndex()
+ mobIDs := s.Mobs.AllDefIDs()
for id := range itemIDs {
def, err := s.Items.Load(id)
@@ -632,13 +634,9 @@ func validateItems(s Source) []Issue {
})
}
- if def.SearchTable != "" && !dropIDs[def.SearchTable] {
- issues = append(issues, Issue{
- Level: "ERROR",
- Type: "reference",
- Message: fmt.Sprintf("Item %q: search_table %q does not exist",
- id, def.SearchTable),
- })
+ if len(def.Search) > 0 {
+ issues = append(issues, validateTriggerBlock(
+ fmt.Sprintf("Item %q: search", id), def.Search, false, itemIDs, roomIndex, mobIDs, dropIDs)...)
}
if def.FarmProduct != "" && !itemIDs[def.FarmProduct] {
@@ -803,11 +801,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)...)
+ fmt.Sprintf("Room %d: on_enter", id), room.OnEnter, false, 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)...)
+ fmt.Sprintf("Room %d: on_exit", id), room.OnExit, false, itemIDs, roomIndex, mobIDs, nil)...)
}
}
@@ -1166,10 +1164,9 @@ func validateExitReciprocity(s Source) []Issue {
return issues
}
-// validateStep validates a single Step's effects. mobIDs may be nil when the
-// caller doesn't track mob ids (e.g. local-object on_look); spawn/despawn mob
-// references are then skipped.
-func validateStep(prefix string, step *behavior.Step, itemIDs map[string]bool, roomIndex map[int]bool, mobIDs map[string]bool) []Issue {
+// validateStep validates a single Step's effects. mobIDs and dropIDs may be nil
+// when the caller doesn't track those entities; validation is skipped for nil sets.
+func validateStep(prefix string, step *behavior.Step, itemIDs map[string]bool, roomIndex map[int]bool, mobIDs map[string]bool, dropIDs map[string]bool) []Issue {
var issues []Issue
if step == nil {
return issues
@@ -1241,6 +1238,41 @@ func validateStep(prefix string, step *behavior.Step, itemIDs map[string]bool, r
})
}
+ for i, e := range step.DropTable {
+ if e.ItemID == "" && e.Table == "" {
+ issues = append(issues, Issue{
+ Level: "WARN",
+ Type: "semantic",
+ Message: fmt.Sprintf("%s: drop_table[%d] has no item_id or table",
+ prefix, i),
+ })
+ }
+ if e.ItemID != "" && e.ItemID != "credits" && !itemIDs[e.ItemID] {
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "reference",
+ Message: fmt.Sprintf("%s: drop_table item %q does not exist",
+ prefix, e.ItemID),
+ })
+ }
+ if e.Table != "" && dropIDs != nil && !dropIDs[e.Table] {
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "reference",
+ Message: fmt.Sprintf("%s: drop_table table %q does not exist",
+ prefix, e.Table),
+ })
+ }
+ if e.Weight <= 0 {
+ issues = append(issues, Issue{
+ Level: "WARN",
+ Type: "semantic",
+ Message: fmt.Sprintf("%s: drop_table[%d] weight is %d (never drops)",
+ prefix, i, e.Weight),
+ })
+ }
+ }
+
if step.Condition != nil {
issues = append(issues, validateCondition(prefix, step.Condition, roomIndex)...)
}
@@ -1315,7 +1347,7 @@ func checkPlayerOnlyInCondition(prefix string, c *behavior.Condition) []Issue {
// 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) []Issue {
+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 {
var issues []Issue
for i := range block {
t := &block[i]
@@ -1369,7 +1401,7 @@ func validateTriggerBlock(prefix string, block []behavior.Trigger, isFlag bool,
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)...)
+ 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)...)
}
@@ -1384,5 +1416,5 @@ func validateTalkStep(prefix string, step *behavior.Step, itemIDs map[string]boo
if step == nil {
return nil
}
- return validateStep(prefix, step, itemIDs, roomIndex, nil)
+ return validateStep(prefix, step, itemIDs, roomIndex, nil, nil)
}