Add the campaign shell, pause-safe checkpoint resume, post-shift campaign continue, confirmed campaign reset, difficulty presets, searchable Help, content-matrix validation, recovery events, contextual dialogue, policy/debrief context and eligible-event previews. Extend persistence with atomic separate files and add campaign/checkpoint/tutorial smokes. Headless 247/247, Python 94/94, tutorial/campaign/session/scenario smokes green.
61 lines
2.2 KiB
GDScript
61 lines
2.2 KiB
GDScript
class_name ScenarioValidator
|
|
extends RefCounted
|
|
## Validates authored venue data before a shift. Keeps content-matrix failures
|
|
## out of runtime: every event must reference a known profile and a valid phase,
|
|
## event ids must be unique, and the matrix must cover enough kinds/phases.
|
|
|
|
const Rules := preload("res://src/core/rules.gd")
|
|
|
|
static func validate(scenario: Resource) -> Array[String]:
|
|
var problems: Array[String] = []
|
|
if scenario == null:
|
|
return ["scenario is null"]
|
|
var profile_types := {}
|
|
for profile in scenario.npc_profiles:
|
|
if profile != null:
|
|
profile_types[int(profile.npc_type)] = true
|
|
var ids := {}
|
|
var routine := 0
|
|
var scripted := 0
|
|
var recovery := 0
|
|
var phases := {}
|
|
for event in scenario.event_pool:
|
|
if not (event is Dictionary):
|
|
problems.append("event is not a dictionary")
|
|
continue
|
|
var event_id := str(event.get("id", ""))
|
|
if event_id == "":
|
|
problems.append("event has no id")
|
|
elif ids.has(event_id):
|
|
problems.append("duplicate event id: %s" % event_id)
|
|
ids[event_id] = true
|
|
var phase := int(event.get("phase", -1))
|
|
if phase < Rules.Phase.EARLY or phase > Rules.Phase.LATE:
|
|
problems.append("%s has invalid phase %d" % [event_id, phase])
|
|
var npc_type := int(event.get("npc_type", -1))
|
|
if not profile_types.has(npc_type):
|
|
problems.append("%s references missing profile type %d" % [event_id, npc_type])
|
|
if str(event.get("intent", "")) == "":
|
|
problems.append("%s has no intent" % event_id)
|
|
var kind := str(event.get("kind", "routine"))
|
|
if kind == "routine":
|
|
routine += 1
|
|
elif kind == "scripted":
|
|
scripted += 1
|
|
elif kind == "recovery":
|
|
recovery += 1
|
|
elif kind != "routine":
|
|
problems.append("%s has unknown kind %s" % [event_id, kind])
|
|
phases[phase] = true
|
|
if scenario.event_pool.size() < 12:
|
|
problems.append("event matrix has %d events; need at least 12" % scenario.event_pool.size())
|
|
if routine < 6:
|
|
problems.append("event matrix needs at least 6 routine events")
|
|
if scripted < 2:
|
|
problems.append("event matrix needs at least 2 scripted events")
|
|
if recovery < 1:
|
|
problems.append("event matrix needs at least 1 recovery event")
|
|
if phases.size() < 2:
|
|
problems.append("event matrix must cover at least 2 shift phases")
|
|
return problems
|