@musashishao/agent-kit 1.9.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/agents/ai-asset-factory.md +700 -0
- package/.agent/agents/ai-audio-factory.md +503 -0
- package/.agent/agents/game-developer.md +4 -4
- package/.agent/agents/orchestrator.md +113 -3
- package/.agent/agents/project-planner.md +67 -0
- package/.agent/agents/unity-mobile-master.md +949 -0
- package/.agent/mcp/config/registry.json +65 -51
- package/.agent/mcp/servers/notebooklm/README.md +114 -0
- package/.agent/mcp/servers/notebooklm/package.json +35 -0
- package/.agent/mcp/servers/notebooklm/src/auth/chrome.ts +225 -0
- package/.agent/mcp/servers/notebooklm/src/auth/index.ts +1 -0
- package/.agent/mcp/servers/notebooklm/src/index.ts +516 -0
- package/.agent/mcp/servers/notebooklm/src/services/index.ts +3 -0
- package/.agent/mcp/servers/notebooklm/src/services/library.ts +217 -0
- package/.agent/mcp/servers/notebooklm/src/services/notebooklm.ts +380 -0
- package/.agent/mcp/servers/notebooklm/tsconfig.json +15 -0
- package/.agent/mcp-gateway/README.md +169 -20
- package/.agent/mcp-gateway/package.json +22 -7
- package/.agent/mcp-gateway/src/auth/index.ts +55 -0
- package/.agent/mcp-gateway/src/auth/middleware.ts +242 -0
- package/.agent/mcp-gateway/src/auth/oauth.ts +462 -0
- package/.agent/mcp-gateway/src/auth/scopes.ts +227 -0
- package/.agent/mcp-gateway/src/index.ts +252 -105
- package/.agent/mcp-gateway/src/observability/index.ts +5 -0
- package/.agent/mcp-gateway/src/observability/otel.ts +405 -0
- package/.agent/mcp-gateway/src/transports/index.ts +5 -0
- package/.agent/mcp-gateway/src/transports/streamableHttp.ts +235 -0
- package/.agent/rules/CODEX.md +89 -0
- package/.agent/rules/CODE_RULES.md +73 -0
- package/.agent/rules/GEMINI.md +25 -0
- package/.agent/rules/MEMORY_STATE.md +110 -0
- package/.agent/rules/REFERENCE.md +33 -141
- package/.agent/rules/REF_SKILLS.md +116 -0
- package/.agent/rules/REF_WORKFLOWS.md +81 -0
- package/.agent/scripts/ak_cli.py +106 -5
- package/.agent/scripts/memory_manager.py +48 -9
- package/.agent/skills/anti-hallucination/SKILL.md +295 -0
- package/.agent/skills/anti-hallucination/scripts/check_hallucination.py +299 -0
- package/.agent/skills/bifurcation-analysis/SKILL.md +56 -0
- package/.agent/skills/brainstorming/SKILL.md +80 -6
- package/.agent/skills/decision-memory/SKILL.md +317 -0
- package/.agent/skills/emergence-detector/SKILL.md +230 -0
- package/.agent/skills/emergence-detector/scripts/check_emergence.py +265 -0
- package/.agent/skills/explained-qa/SKILL.md +142 -0
- package/.agent/skills/explained-qa/game-terminology.md +214 -0
- package/.agent/skills/game-development/ai-dialogue-engine/SKILL.md +442 -0
- package/.agent/skills/game-development/ai-graphics-generator/SKILL.md +463 -0
- package/.agent/skills/game-development/ai-playtest-framework/SKILL.md +570 -0
- package/.agent/skills/game-development/camera-systems/SKILL.md +607 -0
- package/.agent/skills/game-development/card-battle-engine/SKILL.md +618 -0
- package/.agent/skills/game-development/character-controller-3d/SKILL.md +908 -0
- package/.agent/skills/game-development/cloud-save-sync/SKILL.md +527 -0
- package/.agent/skills/game-development/combat-system/SKILL.md +748 -0
- package/.agent/skills/game-development/compliance-rating/SKILL.md +277 -0
- package/.agent/skills/game-development/crossplatform-build/SKILL.md +386 -0
- package/.agent/skills/game-development/cultivation-progression/SKILL.md +520 -0
- package/.agent/skills/game-development/data-driven-balance/SKILL.md +535 -0
- package/.agent/skills/game-development/game-analytics-integrator/SKILL.md +410 -0
- package/.agent/skills/game-development/game-audio-advanced/SKILL.md +646 -0
- package/.agent/skills/game-development/game-economy-designer/SKILL.md +375 -0
- package/.agent/skills/game-development/game-marketing/SKILL.md +85 -0
- package/.agent/skills/game-development/game-state-manager/SKILL.md +883 -0
- package/.agent/skills/game-development/hybrid-game-spec/SKILL.md +220 -0
- package/.agent/skills/game-development/inventory-quest/SKILL.md +747 -0
- package/.agent/skills/game-development/liveops/SKILL.md +308 -0
- package/.agent/skills/game-development/localization/SKILL.md +286 -0
- package/.agent/skills/game-development/mobile-input-patterns/SKILL.md +343 -0
- package/.agent/skills/game-development/monetization-strategy/SKILL.md +94 -0
- package/.agent/skills/game-development/multiplayer-master/SKILL.md +727 -0
- package/.agent/skills/game-development/narrative-branching/SKILL.md +593 -0
- package/.agent/skills/game-development/procedural-level-ai/SKILL.md +367 -0
- package/.agent/skills/game-development/prototyping-rapid/SKILL.md +205 -0
- package/.agent/skills/game-development/spec-ecosystem/SKILL.md +155 -0
- package/.agent/skills/game-development/spec-ecosystem/decision-log-format.md +129 -0
- package/.agent/skills/game-development/spec-ecosystem/templates/PLAN-template.md +178 -0
- package/.agent/skills/game-development/spec-ecosystem/templates/SPEC-template.md +110 -0
- package/.agent/skills/game-development/spec-ecosystem/templates/TASKS-template.md +156 -0
- package/.agent/skills/game-development/survival-systems/SKILL.md +493 -0
- package/.agent/skills/game-development/testing-qa/SKILL.md +270 -0
- package/.agent/skills/game-development/unity-mobile-optimization/SKILL.md +271 -0
- package/.agent/skills/intent-capture/SKILL.md +65 -0
- package/.agent/skills/mcp-composition/SKILL.md +362 -0
- package/.agent/skills/mcp-observability/SKILL.md +323 -0
- package/.agent/skills/mcp-security/SKILL.md +314 -0
- package/.agent/skills/trust-spectrum/SKILL.md +291 -0
- package/.agent/skills/vibe-coding-guard/SKILL.md +328 -0
- package/.agent/templates/AGENTS.game.md +63 -0
- package/.agent/templates/docs/WORKFLOW_GUIDE.en.md +100 -0
- package/.agent/templates/docs/WORKFLOW_GUIDE.vi.md +100 -0
- package/.agent/workflows/ai-agent.md +2 -0
- package/.agent/workflows/autofix.md +1 -0
- package/.agent/workflows/brainstorm.md +1 -0
- package/.agent/workflows/context.md +1 -0
- package/.agent/workflows/create.md +39 -8
- package/.agent/workflows/dashboard.md +1 -0
- package/.agent/workflows/debug.md +14 -0
- package/.agent/workflows/deploy.md +14 -0
- package/.agent/workflows/enhance.md +44 -0
- package/.agent/workflows/gamekit-init.md +177 -0
- package/.agent/workflows/gamekit-launch.md +338 -0
- package/.agent/workflows/gamekit-plan.md +204 -0
- package/.agent/workflows/gamekit-qa.md +153 -0
- package/.agent/workflows/gamekit-spec.md +243 -0
- package/.agent/workflows/gamekit-tasks.md +208 -0
- package/.agent/workflows/marketing.md +2 -0
- package/.agent/workflows/next.md +1 -0
- package/.agent/workflows/orchestrate.md +12 -0
- package/.agent/workflows/pentest.md +2 -0
- package/.agent/workflows/plan.md +42 -0
- package/.agent/workflows/preview.md +1 -0
- package/.agent/workflows/quality.md +1 -0
- package/.agent/workflows/saas.md +2 -0
- package/.agent/workflows/spec.md +42 -0
- package/.agent/workflows/status.md +1 -0
- package/.agent/workflows/test.md +14 -0
- package/.agent/workflows/ui-ux-pro-max.md +1 -0
- package/bin/cli.js +411 -111
- package/package.json +1 -2
- package/.agent/agents/game-asset-curator.md +0 -317
- package/.agent/agents/game-narrative-designer.md +0 -310
- package/.agent/agents/game-qa-agent.md +0 -441
- package/.agent/workflows/game-prototype.md +0 -154
- package/docs/AI_DATA_INFRASTRUCTURE.md +0 -288
- package/docs/CHANGELOG_AI_INFRA.md +0 -141
- package/docs/MIGRATION_GUIDE_V1.9.md +0 -55
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: narrative-branching
|
|
3
|
+
description: Branching narrative and dialogue systems. Dialogue trees, story branches, player choices, relationship systems, quest journals, and conditional content for Unity.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Narrative Branching Skill
|
|
7
|
+
|
|
8
|
+
> **Purpose**: Build games with meaningful choices and branching stories.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
- Building story-driven games
|
|
12
|
+
- Implementing dialogue systems
|
|
13
|
+
- Creating relationship mechanics
|
|
14
|
+
- Designing quest systems
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Dialogue Node System
|
|
19
|
+
|
|
20
|
+
### Core Data Structures
|
|
21
|
+
```csharp
|
|
22
|
+
[CreateAssetMenu(fileName = "DialogueTree", menuName = "Game/Narrative/DialogueTree")]
|
|
23
|
+
public class DialogueTree : ScriptableObject
|
|
24
|
+
{
|
|
25
|
+
public string treeId;
|
|
26
|
+
public string treeName;
|
|
27
|
+
|
|
28
|
+
[Header("Nodes")]
|
|
29
|
+
public DialogueNode startNode;
|
|
30
|
+
public DialogueNode[] allNodes;
|
|
31
|
+
|
|
32
|
+
[Header("Requirements")]
|
|
33
|
+
public Condition[] startConditions;
|
|
34
|
+
|
|
35
|
+
[Header("Metadata")]
|
|
36
|
+
public CharacterData speaker;
|
|
37
|
+
public bool isRepeatable = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
[System.Serializable]
|
|
41
|
+
public class DialogueNode
|
|
42
|
+
{
|
|
43
|
+
public string nodeId;
|
|
44
|
+
public NodeType type;
|
|
45
|
+
|
|
46
|
+
[Header("Content")]
|
|
47
|
+
public CharacterData speaker;
|
|
48
|
+
[TextArea(3, 10)]
|
|
49
|
+
public string dialogueText;
|
|
50
|
+
public AudioClip voiceover;
|
|
51
|
+
public float displayDuration = 0f; // 0 = wait for input
|
|
52
|
+
|
|
53
|
+
[Header("Choices")]
|
|
54
|
+
public DialogueChoice[] choices;
|
|
55
|
+
|
|
56
|
+
[Header("Auto-Continue")]
|
|
57
|
+
public DialogueNode nextNode; // Used when no choices
|
|
58
|
+
|
|
59
|
+
[Header("Effects")]
|
|
60
|
+
public DialogueEffect[] onEnterEffects;
|
|
61
|
+
public DialogueEffect[] onExitEffects;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public enum NodeType
|
|
65
|
+
{
|
|
66
|
+
Dialogue, // Normal dialogue
|
|
67
|
+
Choice, // Player makes choice
|
|
68
|
+
Branch, // Conditional branch (NPC decides)
|
|
69
|
+
Event, // Trigger game event
|
|
70
|
+
End // End conversation
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
[System.Serializable]
|
|
74
|
+
public class DialogueChoice
|
|
75
|
+
{
|
|
76
|
+
public string choiceText;
|
|
77
|
+
public DialogueNode targetNode;
|
|
78
|
+
|
|
79
|
+
[Header("Requirements")]
|
|
80
|
+
public Condition[] showConditions; // To display choice
|
|
81
|
+
public Condition[] enableConditions; // To select choice (grayed vs available)
|
|
82
|
+
|
|
83
|
+
[Header("Tooltip")]
|
|
84
|
+
public string disabledReason; // "Requires 5 Charisma"
|
|
85
|
+
|
|
86
|
+
[Header("Effects")]
|
|
87
|
+
public DialogueEffect[] onSelectEffects;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 2. Condition System
|
|
94
|
+
|
|
95
|
+
### Conditions for Branching
|
|
96
|
+
```csharp
|
|
97
|
+
[System.Serializable]
|
|
98
|
+
public class Condition
|
|
99
|
+
{
|
|
100
|
+
public ConditionType type;
|
|
101
|
+
public string targetId;
|
|
102
|
+
public ComparisonOperator comparison;
|
|
103
|
+
public int value;
|
|
104
|
+
|
|
105
|
+
public bool Evaluate()
|
|
106
|
+
{
|
|
107
|
+
int actualValue = GetActualValue();
|
|
108
|
+
|
|
109
|
+
return comparison switch
|
|
110
|
+
{
|
|
111
|
+
ComparisonOperator.Equals => actualValue == value,
|
|
112
|
+
ComparisonOperator.NotEquals => actualValue != value,
|
|
113
|
+
ComparisonOperator.GreaterThan => actualValue > value,
|
|
114
|
+
ComparisonOperator.LessThan => actualValue < value,
|
|
115
|
+
ComparisonOperator.GreaterOrEqual => actualValue >= value,
|
|
116
|
+
ComparisonOperator.LessOrEqual => actualValue <= value,
|
|
117
|
+
_ => false
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private int GetActualValue()
|
|
122
|
+
{
|
|
123
|
+
return type switch
|
|
124
|
+
{
|
|
125
|
+
ConditionType.HasItem => InventoryManager.GetItemCount(targetId),
|
|
126
|
+
ConditionType.QuestState => QuestManager.GetQuestState(targetId),
|
|
127
|
+
ConditionType.RelationshipLevel => RelationshipManager.GetLevel(targetId),
|
|
128
|
+
ConditionType.PlayerStat => PlayerStats.GetStat(targetId),
|
|
129
|
+
ConditionType.Flag => FlagManager.IsSet(targetId) ? 1 : 0,
|
|
130
|
+
ConditionType.DialogueHistory => DialogueHistory.TimesChosen(targetId),
|
|
131
|
+
ConditionType.TimeOfDay => TimeManager.CurrentHour,
|
|
132
|
+
ConditionType.Random => Random.Range(0, 100),
|
|
133
|
+
_ => 0
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public enum ConditionType
|
|
139
|
+
{
|
|
140
|
+
HasItem,
|
|
141
|
+
QuestState,
|
|
142
|
+
RelationshipLevel,
|
|
143
|
+
PlayerStat,
|
|
144
|
+
Flag,
|
|
145
|
+
DialogueHistory,
|
|
146
|
+
TimeOfDay,
|
|
147
|
+
Random
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public enum ComparisonOperator
|
|
151
|
+
{
|
|
152
|
+
Equals,
|
|
153
|
+
NotEquals,
|
|
154
|
+
GreaterThan,
|
|
155
|
+
LessThan,
|
|
156
|
+
GreaterOrEqual,
|
|
157
|
+
LessOrEqual
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 3. Dialogue Effects
|
|
164
|
+
|
|
165
|
+
### Effect System
|
|
166
|
+
```csharp
|
|
167
|
+
[System.Serializable]
|
|
168
|
+
public class DialogueEffect
|
|
169
|
+
{
|
|
170
|
+
public EffectType type;
|
|
171
|
+
public string targetId;
|
|
172
|
+
public int value;
|
|
173
|
+
public string stringValue;
|
|
174
|
+
|
|
175
|
+
public void Execute()
|
|
176
|
+
{
|
|
177
|
+
switch (type)
|
|
178
|
+
{
|
|
179
|
+
case EffectType.GiveItem:
|
|
180
|
+
InventoryManager.AddItem(targetId, value);
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case EffectType.TakeItem:
|
|
184
|
+
InventoryManager.RemoveItem(targetId, value);
|
|
185
|
+
break;
|
|
186
|
+
|
|
187
|
+
case EffectType.ChangeRelationship:
|
|
188
|
+
RelationshipManager.Modify(targetId, value);
|
|
189
|
+
break;
|
|
190
|
+
|
|
191
|
+
case EffectType.SetFlag:
|
|
192
|
+
FlagManager.Set(targetId, value == 1);
|
|
193
|
+
break;
|
|
194
|
+
|
|
195
|
+
case EffectType.StartQuest:
|
|
196
|
+
QuestManager.StartQuest(targetId);
|
|
197
|
+
break;
|
|
198
|
+
|
|
199
|
+
case EffectType.CompleteQuestObjective:
|
|
200
|
+
QuestManager.CompleteObjective(targetId);
|
|
201
|
+
break;
|
|
202
|
+
|
|
203
|
+
case EffectType.ChangePlayerStat:
|
|
204
|
+
PlayerStats.Modify(targetId, value);
|
|
205
|
+
break;
|
|
206
|
+
|
|
207
|
+
case EffectType.PlayCutscene:
|
|
208
|
+
CutsceneManager.Play(targetId);
|
|
209
|
+
break;
|
|
210
|
+
|
|
211
|
+
case EffectType.TeleportPlayer:
|
|
212
|
+
PlayerManager.Teleport(targetId);
|
|
213
|
+
break;
|
|
214
|
+
|
|
215
|
+
case EffectType.SpawnNPC:
|
|
216
|
+
NPCManager.Spawn(targetId);
|
|
217
|
+
break;
|
|
218
|
+
|
|
219
|
+
case EffectType.Custom:
|
|
220
|
+
GameEvents.OnCustomDialogueEffect?.Invoke(targetId, stringValue);
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
GameEvents.OnDialogueEffectExecuted?.Invoke(this);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
public enum EffectType
|
|
229
|
+
{
|
|
230
|
+
GiveItem,
|
|
231
|
+
TakeItem,
|
|
232
|
+
ChangeRelationship,
|
|
233
|
+
SetFlag,
|
|
234
|
+
StartQuest,
|
|
235
|
+
CompleteQuestObjective,
|
|
236
|
+
ChangePlayerStat,
|
|
237
|
+
PlayCutscene,
|
|
238
|
+
TeleportPlayer,
|
|
239
|
+
SpawnNPC,
|
|
240
|
+
Custom
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## 4. Dialogue Manager
|
|
247
|
+
|
|
248
|
+
### Runtime Controller
|
|
249
|
+
```csharp
|
|
250
|
+
public class DialogueManager : MonoBehaviour
|
|
251
|
+
{
|
|
252
|
+
public static DialogueManager Instance { get; private set; }
|
|
253
|
+
|
|
254
|
+
[SerializeField] private DialogueUI _dialogueUI;
|
|
255
|
+
|
|
256
|
+
public bool IsInDialogue { get; private set; }
|
|
257
|
+
public DialogueTree CurrentTree { get; private set; }
|
|
258
|
+
public DialogueNode CurrentNode { get; private set; }
|
|
259
|
+
|
|
260
|
+
public event Action<DialogueTree> OnDialogueStarted;
|
|
261
|
+
public event Action<DialogueNode> OnNodeEntered;
|
|
262
|
+
public event Action<DialogueChoice> OnChoiceSelected;
|
|
263
|
+
public event Action OnDialogueEnded;
|
|
264
|
+
|
|
265
|
+
public bool StartDialogue(DialogueTree tree)
|
|
266
|
+
{
|
|
267
|
+
// Check start conditions
|
|
268
|
+
if (!EvaluateConditions(tree.startConditions))
|
|
269
|
+
return false;
|
|
270
|
+
|
|
271
|
+
IsInDialogue = true;
|
|
272
|
+
CurrentTree = tree;
|
|
273
|
+
|
|
274
|
+
OnDialogueStarted?.Invoke(tree);
|
|
275
|
+
_dialogueUI.Show();
|
|
276
|
+
|
|
277
|
+
EnterNode(tree.startNode);
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private void EnterNode(DialogueNode node)
|
|
282
|
+
{
|
|
283
|
+
CurrentNode = node;
|
|
284
|
+
|
|
285
|
+
// Execute on-enter effects
|
|
286
|
+
foreach (var effect in node.onEnterEffects)
|
|
287
|
+
{
|
|
288
|
+
effect.Execute();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
OnNodeEntered?.Invoke(node);
|
|
292
|
+
|
|
293
|
+
// Update UI
|
|
294
|
+
_dialogueUI.DisplayNode(node);
|
|
295
|
+
|
|
296
|
+
// Handle node type
|
|
297
|
+
switch (node.type)
|
|
298
|
+
{
|
|
299
|
+
case NodeType.Dialogue:
|
|
300
|
+
// Wait for player input or auto-continue
|
|
301
|
+
if (node.nextNode != null && node.displayDuration > 0)
|
|
302
|
+
{
|
|
303
|
+
StartCoroutine(AutoContinue(node.displayDuration));
|
|
304
|
+
}
|
|
305
|
+
break;
|
|
306
|
+
|
|
307
|
+
case NodeType.Choice:
|
|
308
|
+
// Display available choices
|
|
309
|
+
var validChoices = GetValidChoices(node.choices);
|
|
310
|
+
_dialogueUI.ShowChoices(validChoices);
|
|
311
|
+
break;
|
|
312
|
+
|
|
313
|
+
case NodeType.Branch:
|
|
314
|
+
// Auto-select based on conditions
|
|
315
|
+
AutoBranch(node.choices);
|
|
316
|
+
break;
|
|
317
|
+
|
|
318
|
+
case NodeType.Event:
|
|
319
|
+
// Execute and continue
|
|
320
|
+
foreach (var effect in node.onExitEffects)
|
|
321
|
+
{
|
|
322
|
+
effect.Execute();
|
|
323
|
+
}
|
|
324
|
+
if (node.nextNode != null)
|
|
325
|
+
EnterNode(node.nextNode);
|
|
326
|
+
else
|
|
327
|
+
EndDialogue();
|
|
328
|
+
break;
|
|
329
|
+
|
|
330
|
+
case NodeType.End:
|
|
331
|
+
EndDialogue();
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
public void SelectChoice(DialogueChoice choice)
|
|
337
|
+
{
|
|
338
|
+
// Execute choice effects
|
|
339
|
+
foreach (var effect in choice.onSelectEffects)
|
|
340
|
+
{
|
|
341
|
+
effect.Execute();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Record in history
|
|
345
|
+
DialogueHistory.RecordChoice(CurrentTree.treeId, CurrentNode.nodeId, choice.choiceText);
|
|
346
|
+
|
|
347
|
+
OnChoiceSelected?.Invoke(choice);
|
|
348
|
+
|
|
349
|
+
// Navigate to target
|
|
350
|
+
if (choice.targetNode != null)
|
|
351
|
+
{
|
|
352
|
+
// Execute current node exit effects
|
|
353
|
+
foreach (var effect in CurrentNode.onExitEffects)
|
|
354
|
+
{
|
|
355
|
+
effect.Execute();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
EnterNode(choice.targetNode);
|
|
359
|
+
}
|
|
360
|
+
else
|
|
361
|
+
{
|
|
362
|
+
EndDialogue();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
private void EndDialogue()
|
|
367
|
+
{
|
|
368
|
+
IsInDialogue = false;
|
|
369
|
+
_dialogueUI.Hide();
|
|
370
|
+
|
|
371
|
+
OnDialogueEnded?.Invoke();
|
|
372
|
+
GameEvents.OnDialogueEnded?.Invoke(CurrentTree);
|
|
373
|
+
|
|
374
|
+
CurrentTree = null;
|
|
375
|
+
CurrentNode = null;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## 5. Relationship System
|
|
383
|
+
|
|
384
|
+
### Relationship Data
|
|
385
|
+
```csharp
|
|
386
|
+
[CreateAssetMenu(fileName = "Character", menuName = "Game/Narrative/Character")]
|
|
387
|
+
public class CharacterData : ScriptableObject
|
|
388
|
+
{
|
|
389
|
+
public string characterId;
|
|
390
|
+
public string displayName;
|
|
391
|
+
public Sprite portrait;
|
|
392
|
+
|
|
393
|
+
[Header("Relationship")]
|
|
394
|
+
public int defaultRelationship = 50; // 0-100
|
|
395
|
+
public RelationshipTier[] tiers;
|
|
396
|
+
|
|
397
|
+
[Header("Personality")]
|
|
398
|
+
public PersonalityTrait[] traits;
|
|
399
|
+
public string[] likes;
|
|
400
|
+
public string[] dislikes;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
[System.Serializable]
|
|
404
|
+
public class RelationshipTier
|
|
405
|
+
{
|
|
406
|
+
public string tierName; // Stranger, Acquaintance, Friend, Close Friend, Romance
|
|
407
|
+
public int minValue;
|
|
408
|
+
public DialogueTree[] availableDialogues;
|
|
409
|
+
public Sprite tierIcon;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
public class RelationshipManager : MonoBehaviour
|
|
413
|
+
{
|
|
414
|
+
private Dictionary<string, int> _relationships = new();
|
|
415
|
+
|
|
416
|
+
public event Action<string, int, int> OnRelationshipChanged; // characterId, oldVal, newVal
|
|
417
|
+
public event Action<string, RelationshipTier> OnTierChanged;
|
|
418
|
+
|
|
419
|
+
public int GetLevel(string characterId)
|
|
420
|
+
{
|
|
421
|
+
_relationships.TryGetValue(characterId, out int level);
|
|
422
|
+
return level;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
public void Modify(string characterId, int delta)
|
|
426
|
+
{
|
|
427
|
+
int oldValue = GetLevel(characterId);
|
|
428
|
+
int newValue = Mathf.Clamp(oldValue + delta, 0, 100);
|
|
429
|
+
|
|
430
|
+
_relationships[characterId] = newValue;
|
|
431
|
+
|
|
432
|
+
OnRelationshipChanged?.Invoke(characterId, oldValue, newValue);
|
|
433
|
+
|
|
434
|
+
// Check tier change
|
|
435
|
+
var oldTier = GetTier(characterId, oldValue);
|
|
436
|
+
var newTier = GetTier(characterId, newValue);
|
|
437
|
+
|
|
438
|
+
if (oldTier != newTier)
|
|
439
|
+
{
|
|
440
|
+
OnTierChanged?.Invoke(characterId, newTier);
|
|
441
|
+
GameEvents.OnRelationshipTierChanged?.Invoke(characterId, newTier);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
public void GiveGift(string characterId, ItemData item)
|
|
446
|
+
{
|
|
447
|
+
var character = GetCharacter(characterId);
|
|
448
|
+
|
|
449
|
+
int delta = 5; // Base
|
|
450
|
+
|
|
451
|
+
if (character.likes.Contains(item.itemId))
|
|
452
|
+
delta = 15;
|
|
453
|
+
else if (character.dislikes.Contains(item.itemId))
|
|
454
|
+
delta = -10;
|
|
455
|
+
|
|
456
|
+
Modify(characterId, delta);
|
|
457
|
+
GameEvents.OnGiftGiven?.Invoke(characterId, item, delta);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## 6. Quest Journal
|
|
465
|
+
|
|
466
|
+
### Quest System
|
|
467
|
+
```csharp
|
|
468
|
+
[CreateAssetMenu(fileName = "Quest", menuName = "Game/Narrative/Quest")]
|
|
469
|
+
public class QuestData : ScriptableObject
|
|
470
|
+
{
|
|
471
|
+
public string questId;
|
|
472
|
+
public string questName;
|
|
473
|
+
[TextArea] public string description;
|
|
474
|
+
|
|
475
|
+
public QuestType type;
|
|
476
|
+
public CharacterData questGiver;
|
|
477
|
+
|
|
478
|
+
[Header("Objectives")]
|
|
479
|
+
public QuestObjective[] objectives;
|
|
480
|
+
|
|
481
|
+
[Header("Rewards")]
|
|
482
|
+
public ItemReward[] itemRewards;
|
|
483
|
+
public int goldReward;
|
|
484
|
+
public int expReward;
|
|
485
|
+
|
|
486
|
+
[Header("Flow")]
|
|
487
|
+
public QuestData[] prerequisiteQuests;
|
|
488
|
+
public DialogueTree startDialogue;
|
|
489
|
+
public DialogueTree completeDialogue;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
[System.Serializable]
|
|
493
|
+
public class QuestObjective
|
|
494
|
+
{
|
|
495
|
+
public string objectiveId;
|
|
496
|
+
public string description;
|
|
497
|
+
public ObjectiveType type;
|
|
498
|
+
public string targetId;
|
|
499
|
+
public int requiredCount = 1;
|
|
500
|
+
public bool isOptional = false;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
public enum ObjectiveType
|
|
504
|
+
{
|
|
505
|
+
Kill, // Kill X enemies of type
|
|
506
|
+
Collect, // Collect X items
|
|
507
|
+
TalkTo, // Talk to NPC
|
|
508
|
+
GoTo, // Reach location
|
|
509
|
+
Escort, // Escort NPC
|
|
510
|
+
Defend, // Defend location for time
|
|
511
|
+
Deliver, // Bring item to NPC
|
|
512
|
+
Investigate, // Examine objects
|
|
513
|
+
Custom // Custom trigger
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
public class QuestManager : MonoBehaviour
|
|
517
|
+
{
|
|
518
|
+
private Dictionary<string, QuestState> _questStates = new();
|
|
519
|
+
private Dictionary<string, int> _objectiveProgress = new();
|
|
520
|
+
|
|
521
|
+
public event Action<QuestData> OnQuestStarted;
|
|
522
|
+
public event Action<QuestData, QuestObjective> OnObjectiveCompleted;
|
|
523
|
+
public event Action<QuestData> OnQuestCompleted;
|
|
524
|
+
|
|
525
|
+
public void StartQuest(string questId)
|
|
526
|
+
{
|
|
527
|
+
var quest = GetQuestData(questId);
|
|
528
|
+
if (quest == null || _questStates.ContainsKey(questId))
|
|
529
|
+
return;
|
|
530
|
+
|
|
531
|
+
_questStates[questId] = QuestState.Active;
|
|
532
|
+
|
|
533
|
+
// Initialize objective progress
|
|
534
|
+
foreach (var obj in quest.objectives)
|
|
535
|
+
{
|
|
536
|
+
_objectiveProgress[$"{questId}_{obj.objectiveId}"] = 0;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
OnQuestStarted?.Invoke(quest);
|
|
540
|
+
GameEvents.OnQuestStarted?.Invoke(quest);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
public void UpdateObjective(string questId, string objectiveId, int progress)
|
|
544
|
+
{
|
|
545
|
+
string key = $"{questId}_{objectiveId}";
|
|
546
|
+
_objectiveProgress[key] = progress;
|
|
547
|
+
|
|
548
|
+
var quest = GetQuestData(questId);
|
|
549
|
+
var objective = quest.objectives.First(o => o.objectiveId == objectiveId);
|
|
550
|
+
|
|
551
|
+
if (progress >= objective.requiredCount)
|
|
552
|
+
{
|
|
553
|
+
OnObjectiveCompleted?.Invoke(quest, objective);
|
|
554
|
+
CheckQuestCompletion(quest);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private void CheckQuestCompletion(QuestData quest)
|
|
559
|
+
{
|
|
560
|
+
bool allComplete = quest.objectives
|
|
561
|
+
.Where(o => !o.isOptional)
|
|
562
|
+
.All(o => IsObjectiveComplete(quest.questId, o.objectiveId));
|
|
563
|
+
|
|
564
|
+
if (allComplete)
|
|
565
|
+
{
|
|
566
|
+
_questStates[quest.questId] = QuestState.Completed;
|
|
567
|
+
GiveRewards(quest);
|
|
568
|
+
OnQuestCompleted?.Invoke(quest);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
public enum QuestState { NotStarted, Active, Completed, Failed }
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
## Anti-Patterns
|
|
579
|
+
|
|
580
|
+
| ❌ Don't | ✅ Do |
|
|
581
|
+
|----------|-------|
|
|
582
|
+
| Hardcode dialogue text | Data-driven dialogue trees |
|
|
583
|
+
| Binary choices only | Gradient of options |
|
|
584
|
+
| Ignore past choices | Track and reference history |
|
|
585
|
+
| One-time dialogues | Reactive, contextual dialogue |
|
|
586
|
+
| Linear quests | Branching objectives |
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
590
|
+
## Related Skills
|
|
591
|
+
- `game-development/ai-dialogue-engine` - LLM-powered NPCs
|
|
592
|
+
- `game-development/game-state-manager` - Save narrative state
|
|
593
|
+
- `game-development/localization` - Translate dialogues
|