From f412d9ba2543e13d5ca93a7d62795f782ebd4591 Mon Sep 17 00:00:00 2001 From: "Z. Cliffe Schreuders" Date: Wed, 26 Nov 2025 22:42:19 +0000 Subject: [PATCH] feat: Add comprehensive Objectives and Tasks Guide with examples and best practices --- docs/OBJECTIVES_AND_TASKS_GUIDE.md | 360 +++++++++++++++++++ scenarios/ink/test2.ink | 1 + scenarios/npc-sprite-test3/scenario.json.erb | 2 +- 3 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 docs/OBJECTIVES_AND_TASKS_GUIDE.md diff --git a/docs/OBJECTIVES_AND_TASKS_GUIDE.md b/docs/OBJECTIVES_AND_TASKS_GUIDE.md new file mode 100644 index 0000000..ab70307 --- /dev/null +++ b/docs/OBJECTIVES_AND_TASKS_GUIDE.md @@ -0,0 +1,360 @@ +# Objectives and Tasks Guide + +This guide covers how to add achievements and tasks to Break Escape scenarios using the objectives system. + +## Overview + +The objectives system allows you to: +- Define **Aims** (high-level goals) containing multiple **Tasks** +- Track player progress through dialogue-triggered tags +- Lock/unlock aims and tasks dynamically via NPC conversations +- Trigger NPC conversations when objectives are completed +- Sync progress with the server for persistence + +## Quick Start + +### 1. Define Objectives in scenario.json + +```json +{ + "objectives": [ + { + "id": "main_mission", + "title": "Primary Mission", + "description": "Complete the main objectives", + "status": "active", + "aims": [ + { + "id": "gather_intel", + "title": "Gather Intelligence", + "status": "active", + "tasks": [ + { + "id": "talk_to_alice", + "title": "Talk to Alice", + "status": "active" + }, + { + "id": "talk_to_bob", + "title": "Talk to Bob", + "status": "locked" + } + ] + }, + { + "id": "secret_mission", + "title": "Secret Mission", + "status": "locked", + "tasks": [ + { + "id": "secret_task_1", + "title": "Complete secret objective", + "status": "locked" + } + ] + } + ] + } + ] +} +``` + +### 2. Control Objectives from Ink Dialogue + +Use these Ink tags in your `.ink` files: + +```ink +=== first_meeting === +Great to meet you! This task is now complete. +#complete_task:talk_to_alice + +You should go talk to Bob next - I just unlocked that task. +#unlock_task:talk_to_bob + +-> hub + +=== reveal_secret === +I'll let you in on a secret mission... +#unlock_aim:secret_mission + +The first task is now available. +#unlock_task:secret_task_1 + +-> hub +``` + +## Ink Tags Reference + +| Tag | Description | Example | +|-----|-------------|---------| +| `#complete_task:task_id` | Marks a task as completed | `#complete_task:talk_to_alice` | +| `#unlock_task:task_id` | Unlocks a locked task | `#unlock_task:secret_task_1` | +| `#unlock_aim:aim_id` | Unlocks an entire aim | `#unlock_aim:secret_mission` | + +## Task Statuses + +| Status | Description | +|--------|-------------| +| `active` | Task is visible and can be completed | +| `locked` | Task is hidden until unlocked | +| `completed` | Task has been finished | + +## Event-Triggered Conversations + +NPCs can automatically start conversations when objectives are completed: + +```json +{ + "id": "alice", + "displayName": "Alice", + "storyPath": "scenarios/my_scenario/alice.json", + "eventMappings": [ + { + "eventPattern": "objective_aim_completed:secret_mission", + "targetKnot": "final_debrief", + "autoTrigger": true, + "background": "assets/backgrounds/office.png" + } + ] +} +``` + +### Event Patterns + +| Pattern | Fires When | +|---------|------------| +| `objective_aim_completed:aim_id` | Specific aim is completed | +| `objective_task_completed:task_id` | Specific task is completed | +| `objective_aim_completed` | Any aim is completed | +| `objective_task_completed` | Any task is completed | + +## Global Variables for Cross-NPC State + +Define variables that sync across all NPC conversations: + +```json +{ + "globalVariables": { + "alice_talked": false, + "bob_talked": false, + "secret_revealed": false + } +} +``` + +In Ink files, declare and use these variables: + +```ink +// alice.ink +VAR alice_talked = false +VAR bob_talked = false + +=== hub === ++ {not alice_talked} [Nice to meet you] + -> first_meeting + ++ {alice_talked and bob_talked} [What's next?] + -> next_steps +``` + +**Important:** Declare global variables in BOTH Ink files that use them. + +## Complete Example + +### scenario.json.erb + +```json +{ + "scenario_brief": "Investigate the facility", + "startRoom": "lobby", + + "globalVariables": { + "alice_talked": false, + "mission_complete": false + }, + + "objectives": [ + { + "id": "investigation", + "title": "Investigation", + "status": "active", + "aims": [ + { + "id": "gather_info", + "title": "Gather Information", + "status": "active", + "tasks": [ + { + "id": "talk_to_alice", + "title": "Speak with Alice", + "status": "active" + }, + { + "id": "find_evidence", + "title": "Find the evidence", + "status": "locked" + } + ] + } + ] + } + ], + + "rooms": { + "lobby": { + "type": "room_office", + "npcs": [ + { + "id": "alice", + "displayName": "Alice", + "npcType": "person", + "position": { "x": 5, "y": 5 }, + "storyPath": "scenarios/my_scenario/alice.json", + "eventMappings": [ + { + "eventPattern": "objective_task_completed:find_evidence", + "targetKnot": "evidence_found", + "autoTrigger": true + } + ] + } + ] + } + } +} +``` + +### alice.ink + +```ink +VAR alice_talked = false + +=== start === +Hello there! Welcome to the facility. +-> hub + +=== hub === ++ {not alice_talked} [I need your help] + -> first_meeting + ++ {alice_talked} [Any updates?] + -> check_progress + ++ [I need to go] + See you later! + #exit_conversation + -> hub + +=== first_meeting === +Of course! Let me tell you what I know... +#complete_task:talk_to_alice +~ alice_talked = true + +There's evidence hidden in the server room. I've unlocked that task for you. +#unlock_task:find_evidence +-> hub + +=== check_progress === +Have you found the evidence yet? Keep looking! +-> hub + +=== evidence_found === +// Triggered automatically when find_evidence task is completed +You found it! Excellent work, agent. +~ mission_complete = true +The investigation is complete. +#exit_conversation +-> hub +``` + +## Best Practices + +### 1. Use the Hub Pattern +Never use `-> END` in NPC dialogues. Always return to a hub: + +```ink +=== hub === ++ [Option 1] -> do_thing_1 ++ [Option 2] -> do_thing_2 ++ [Goodbye] + See you! + #exit_conversation + -> hub +``` + +### 2. Place Tags After Dialogue +Put objective tags after the dialogue line they relate to: + +```ink +// ✅ Good - tag fires after player sees the text +Great work completing that task! +#complete_task:my_task + +// ❌ Avoid - tag fires before text displays +#complete_task:my_task +Great work completing that task! +``` + +### 3. Chain Tasks Logically +Unlock the next task when completing the current one: + +```ink +=== complete_first_task === +You've finished the first part! +#complete_task:task_1 +#unlock_task:task_2 +Now you can move on to the next step. +-> hub +``` + +### 4. Use Conditional Choices +Show different options based on task status using global variables: + +```ink +=== hub === ++ {not task_1_done} [Start the mission] + -> begin_mission + ++ {task_1_done and not task_2_done} [Continue the mission] + -> continue_mission + ++ {task_2_done} [Finish up] + -> wrap_up +``` + +### 5. Provide Feedback +Always give the player clear feedback when objectives change: + +```ink +=== unlock_secret === +I've got something special for you... +#unlock_aim:secret_mission +Check your objectives - a new mission has appeared! +#unlock_task:secret_task_1 +The first task is ready. Good luck! +-> hub +``` + +## Debugging Tips + +1. **Check browser console** for objective-related logs: + - `🎯 Task completed: task_id` + - `🔓 Task unlocked: task_id` + - `🔓 Aim unlocked: aim_id` + +2. **Verify global variables** are declared in all Ink files that use them + +3. **Test event triggers** by watching for: + - `📡 Emitting event: objective_task_completed:task_id` + - `⚡ Event-triggered conversation: jumping to knot` + +4. **Use the objectives panel** (press `O` in-game) to see current status + +## Files Reference + +| File | Purpose | +|------|---------| +| `js/systems/objectives-manager.js` | Core objectives logic | +| `js/minigames/helpers/chat-helpers.js` | Tag processing | +| `js/systems/npc-conversation-state.js` | Global variable sync | +| `docs/GLOBAL_VARIABLES.md` | Global variables documentation | diff --git a/scenarios/ink/test2.ink b/scenarios/ink/test2.ink index 1a181b6..9de06ac 100644 --- a/scenarios/ink/test2.ink +++ b/scenarios/ink/test2.ink @@ -36,3 +36,4 @@ Player: + diff --git a/scenarios/npc-sprite-test3/scenario.json.erb b/scenarios/npc-sprite-test3/scenario.json.erb index 77018c9..5f90d57 100644 --- a/scenarios/npc-sprite-test3/scenario.json.erb +++ b/scenarios/npc-sprite-test3/scenario.json.erb @@ -99,7 +99,7 @@ "currentKnot": "hub", "timedConversation": { "delay": 0, - "targetKnot": "group_meeting", + "targetKnot": "hub", "background": "assets/backgrounds/hq1.png" } },