Ship the 0.8.0 first-hour and campaign milestone
Full build / Import + headless tests (push) Successful in 41s
Full build / Full desktop build + optional release (push) Successful in 4m18s

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.
This commit is contained in:
zwitschi committed 2026-11-01 21:21:43 +01:00
1 parent 86332cd401
commit aa51ee331e
38 files changed
+1361 -1144

No files matched your search

+41 -1
View File
@@ -21,6 +21,11 @@ static func shared_profiles() -> Array[NpcProfile]:
tourist.npc_type = Rules.NpcType.TOURIST
tourist.temperament = 0.1
tourist.base_compliance = 0.9
tourist.context_lines = {
"refusal": ["I just want a photo, I'm not staying.", "I didn't know this entrance was restricted."],
"deescalation": ["Okay, I'll move to the sidewalk.", "Sorry, I'll put the camera away."],
"recognition": ["You remember me from the tour, right?"],
}
tourist.dialogue_lines = [
"Oh sure, sorry, just taking a picture.",
"Is there a better spot to stand?",
@@ -34,6 +39,10 @@ static func shared_profiles() -> Array[NpcProfile]:
confused.npc_type = Rules.NpcType.CONFUSED_VISITOR
confused.temperament = 0.2
confused.base_compliance = 0.85
confused.context_lines = {
"refusal": ["I'm sure the event is here. Let me check my phone."],
"deescalation": ["Oh, I understand. I'll check with the box office."],
}
confused.dialogue_lines = [
"Wait, this isn't the entrance?",
"Sorry about that, I'm a bit lost.",
@@ -389,6 +398,16 @@ static func hollywood_building() -> VenueScenario:
"base_weight": 1.0,
"requires_emergency": true,
},
{
"id": "building_apology_return",
"phase": Rules.Phase.EVENING,
"kind": "recovery",
"npc_type": Rules.NpcType.AUTHORIZED_GUEST,
"intent": "enter",
"time_range_s": Vector2(620, 800),
"probability": 0.22,
"base_weight": 1.0,
},
]
return scenario
@@ -451,7 +470,7 @@ static func joke_shop() -> VenueScenario:
scenario.npc_profiles = _filter_profiles([
"tourist", "confused_visitor", "authorized_guest", "authorized_staff",
"influencer", "intoxicated", "trespasser", "violent_threat",
"street_performer", "influencer", "intoxicated", "trespasser", "violent_threat",
])
scenario.event_pool = [
@@ -533,6 +552,17 @@ static func joke_shop() -> VenueScenario:
"probability": 0.5,
"base_weight": 1.0,
},
{
"id": "joke_denied_guest_return",
"phase": Rules.Phase.EVENING,
"kind": "recovery",
"npc_type": Rules.NpcType.AUTHORIZED_GUEST,
"intent": "enter",
"time_range_s": Vector2(650, 830),
"probability": 0.2,
"base_weight": 1.0,
"override_access": {"has_id": true, "has_ticket": true},
},
]
return scenario
@@ -648,6 +678,16 @@ static func tv_studio() -> VenueScenario:
"probability": 0.45,
"base_weight": 1.0,
},
{
"id": "studio_manager_check",
"phase": Rules.Phase.EVENING,
"kind": "recovery",
"npc_type": Rules.NpcType.AUTHORIZED_STAFF,
"intent": "enter",
"time_range_s": Vector2(620, 820),
"probability": 0.2,
"base_weight": 1.0,
},
]
return scenario