@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.
Files changed (125) hide show
  1. package/.agent/agents/ai-asset-factory.md +700 -0
  2. package/.agent/agents/ai-audio-factory.md +503 -0
  3. package/.agent/agents/game-developer.md +4 -4
  4. package/.agent/agents/orchestrator.md +113 -3
  5. package/.agent/agents/project-planner.md +67 -0
  6. package/.agent/agents/unity-mobile-master.md +949 -0
  7. package/.agent/mcp/config/registry.json +65 -51
  8. package/.agent/mcp/servers/notebooklm/README.md +114 -0
  9. package/.agent/mcp/servers/notebooklm/package.json +35 -0
  10. package/.agent/mcp/servers/notebooklm/src/auth/chrome.ts +225 -0
  11. package/.agent/mcp/servers/notebooklm/src/auth/index.ts +1 -0
  12. package/.agent/mcp/servers/notebooklm/src/index.ts +516 -0
  13. package/.agent/mcp/servers/notebooklm/src/services/index.ts +3 -0
  14. package/.agent/mcp/servers/notebooklm/src/services/library.ts +217 -0
  15. package/.agent/mcp/servers/notebooklm/src/services/notebooklm.ts +380 -0
  16. package/.agent/mcp/servers/notebooklm/tsconfig.json +15 -0
  17. package/.agent/mcp-gateway/README.md +169 -20
  18. package/.agent/mcp-gateway/package.json +22 -7
  19. package/.agent/mcp-gateway/src/auth/index.ts +55 -0
  20. package/.agent/mcp-gateway/src/auth/middleware.ts +242 -0
  21. package/.agent/mcp-gateway/src/auth/oauth.ts +462 -0
  22. package/.agent/mcp-gateway/src/auth/scopes.ts +227 -0
  23. package/.agent/mcp-gateway/src/index.ts +252 -105
  24. package/.agent/mcp-gateway/src/observability/index.ts +5 -0
  25. package/.agent/mcp-gateway/src/observability/otel.ts +405 -0
  26. package/.agent/mcp-gateway/src/transports/index.ts +5 -0
  27. package/.agent/mcp-gateway/src/transports/streamableHttp.ts +235 -0
  28. package/.agent/rules/CODEX.md +89 -0
  29. package/.agent/rules/CODE_RULES.md +73 -0
  30. package/.agent/rules/GEMINI.md +25 -0
  31. package/.agent/rules/MEMORY_STATE.md +110 -0
  32. package/.agent/rules/REFERENCE.md +33 -141
  33. package/.agent/rules/REF_SKILLS.md +116 -0
  34. package/.agent/rules/REF_WORKFLOWS.md +81 -0
  35. package/.agent/scripts/ak_cli.py +106 -5
  36. package/.agent/scripts/memory_manager.py +48 -9
  37. package/.agent/skills/anti-hallucination/SKILL.md +295 -0
  38. package/.agent/skills/anti-hallucination/scripts/check_hallucination.py +299 -0
  39. package/.agent/skills/bifurcation-analysis/SKILL.md +56 -0
  40. package/.agent/skills/brainstorming/SKILL.md +80 -6
  41. package/.agent/skills/decision-memory/SKILL.md +317 -0
  42. package/.agent/skills/emergence-detector/SKILL.md +230 -0
  43. package/.agent/skills/emergence-detector/scripts/check_emergence.py +265 -0
  44. package/.agent/skills/explained-qa/SKILL.md +142 -0
  45. package/.agent/skills/explained-qa/game-terminology.md +214 -0
  46. package/.agent/skills/game-development/ai-dialogue-engine/SKILL.md +442 -0
  47. package/.agent/skills/game-development/ai-graphics-generator/SKILL.md +463 -0
  48. package/.agent/skills/game-development/ai-playtest-framework/SKILL.md +570 -0
  49. package/.agent/skills/game-development/camera-systems/SKILL.md +607 -0
  50. package/.agent/skills/game-development/card-battle-engine/SKILL.md +618 -0
  51. package/.agent/skills/game-development/character-controller-3d/SKILL.md +908 -0
  52. package/.agent/skills/game-development/cloud-save-sync/SKILL.md +527 -0
  53. package/.agent/skills/game-development/combat-system/SKILL.md +748 -0
  54. package/.agent/skills/game-development/compliance-rating/SKILL.md +277 -0
  55. package/.agent/skills/game-development/crossplatform-build/SKILL.md +386 -0
  56. package/.agent/skills/game-development/cultivation-progression/SKILL.md +520 -0
  57. package/.agent/skills/game-development/data-driven-balance/SKILL.md +535 -0
  58. package/.agent/skills/game-development/game-analytics-integrator/SKILL.md +410 -0
  59. package/.agent/skills/game-development/game-audio-advanced/SKILL.md +646 -0
  60. package/.agent/skills/game-development/game-economy-designer/SKILL.md +375 -0
  61. package/.agent/skills/game-development/game-marketing/SKILL.md +85 -0
  62. package/.agent/skills/game-development/game-state-manager/SKILL.md +883 -0
  63. package/.agent/skills/game-development/hybrid-game-spec/SKILL.md +220 -0
  64. package/.agent/skills/game-development/inventory-quest/SKILL.md +747 -0
  65. package/.agent/skills/game-development/liveops/SKILL.md +308 -0
  66. package/.agent/skills/game-development/localization/SKILL.md +286 -0
  67. package/.agent/skills/game-development/mobile-input-patterns/SKILL.md +343 -0
  68. package/.agent/skills/game-development/monetization-strategy/SKILL.md +94 -0
  69. package/.agent/skills/game-development/multiplayer-master/SKILL.md +727 -0
  70. package/.agent/skills/game-development/narrative-branching/SKILL.md +593 -0
  71. package/.agent/skills/game-development/procedural-level-ai/SKILL.md +367 -0
  72. package/.agent/skills/game-development/prototyping-rapid/SKILL.md +205 -0
  73. package/.agent/skills/game-development/spec-ecosystem/SKILL.md +155 -0
  74. package/.agent/skills/game-development/spec-ecosystem/decision-log-format.md +129 -0
  75. package/.agent/skills/game-development/spec-ecosystem/templates/PLAN-template.md +178 -0
  76. package/.agent/skills/game-development/spec-ecosystem/templates/SPEC-template.md +110 -0
  77. package/.agent/skills/game-development/spec-ecosystem/templates/TASKS-template.md +156 -0
  78. package/.agent/skills/game-development/survival-systems/SKILL.md +493 -0
  79. package/.agent/skills/game-development/testing-qa/SKILL.md +270 -0
  80. package/.agent/skills/game-development/unity-mobile-optimization/SKILL.md +271 -0
  81. package/.agent/skills/intent-capture/SKILL.md +65 -0
  82. package/.agent/skills/mcp-composition/SKILL.md +362 -0
  83. package/.agent/skills/mcp-observability/SKILL.md +323 -0
  84. package/.agent/skills/mcp-security/SKILL.md +314 -0
  85. package/.agent/skills/trust-spectrum/SKILL.md +291 -0
  86. package/.agent/skills/vibe-coding-guard/SKILL.md +328 -0
  87. package/.agent/templates/AGENTS.game.md +63 -0
  88. package/.agent/templates/docs/WORKFLOW_GUIDE.en.md +100 -0
  89. package/.agent/templates/docs/WORKFLOW_GUIDE.vi.md +100 -0
  90. package/.agent/workflows/ai-agent.md +2 -0
  91. package/.agent/workflows/autofix.md +1 -0
  92. package/.agent/workflows/brainstorm.md +1 -0
  93. package/.agent/workflows/context.md +1 -0
  94. package/.agent/workflows/create.md +39 -8
  95. package/.agent/workflows/dashboard.md +1 -0
  96. package/.agent/workflows/debug.md +14 -0
  97. package/.agent/workflows/deploy.md +14 -0
  98. package/.agent/workflows/enhance.md +44 -0
  99. package/.agent/workflows/gamekit-init.md +177 -0
  100. package/.agent/workflows/gamekit-launch.md +338 -0
  101. package/.agent/workflows/gamekit-plan.md +204 -0
  102. package/.agent/workflows/gamekit-qa.md +153 -0
  103. package/.agent/workflows/gamekit-spec.md +243 -0
  104. package/.agent/workflows/gamekit-tasks.md +208 -0
  105. package/.agent/workflows/marketing.md +2 -0
  106. package/.agent/workflows/next.md +1 -0
  107. package/.agent/workflows/orchestrate.md +12 -0
  108. package/.agent/workflows/pentest.md +2 -0
  109. package/.agent/workflows/plan.md +42 -0
  110. package/.agent/workflows/preview.md +1 -0
  111. package/.agent/workflows/quality.md +1 -0
  112. package/.agent/workflows/saas.md +2 -0
  113. package/.agent/workflows/spec.md +42 -0
  114. package/.agent/workflows/status.md +1 -0
  115. package/.agent/workflows/test.md +14 -0
  116. package/.agent/workflows/ui-ux-pro-max.md +1 -0
  117. package/bin/cli.js +411 -111
  118. package/package.json +1 -2
  119. package/.agent/agents/game-asset-curator.md +0 -317
  120. package/.agent/agents/game-narrative-designer.md +0 -310
  121. package/.agent/agents/game-qa-agent.md +0 -441
  122. package/.agent/workflows/game-prototype.md +0 -154
  123. package/docs/AI_DATA_INFRASTRUCTURE.md +0 -288
  124. package/docs/CHANGELOG_AI_INFRA.md +0 -141
  125. package/docs/MIGRATION_GUIDE_V1.9.md +0 -55
@@ -0,0 +1,747 @@
1
+ ---
2
+ name: inventory-quest
3
+ description: RPG systems. Inventory management, equipment, quest systems, crafting, loot tables, and dialogue integration.
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, WebSearch
5
+ ---
6
+
7
+ # Inventory & Quest Systems Skill
8
+
9
+ > Build complete RPG progression systems.
10
+
11
+ ---
12
+
13
+ ## When to Use
14
+
15
+ - Implementing inventory systems
16
+ - Building equipment/gear systems
17
+ - Creating quest trackers
18
+ - Designing crafting systems
19
+ - Setting up loot tables
20
+
21
+ ---
22
+
23
+ ## Inventory System
24
+
25
+ ### Item Definition
26
+
27
+ ```csharp
28
+ [CreateAssetMenu(fileName = "New Item", menuName = "RPG/Item")]
29
+ public class ItemData : ScriptableObject
30
+ {
31
+ [Header("Basic Info")]
32
+ public string Id;
33
+ public string DisplayName;
34
+ [TextArea] public string Description;
35
+ public Sprite Icon;
36
+ public ItemRarity Rarity;
37
+ public ItemType Type;
38
+
39
+ [Header("Stacking")]
40
+ public bool IsStackable = true;
41
+ public int MaxStackSize = 99;
42
+
43
+ [Header("Value")]
44
+ public int BuyPrice;
45
+ public int SellPrice;
46
+
47
+ [Header("Usage")]
48
+ public bool IsUsable;
49
+ public bool IsEquippable;
50
+ public bool IsDroppable = true;
51
+
52
+ public virtual void Use(Character character) { }
53
+ public virtual void Equip(Character character) { }
54
+ public virtual void Unequip(Character character) { }
55
+ }
56
+
57
+ public enum ItemRarity
58
+ {
59
+ Common,
60
+ Uncommon,
61
+ Rare,
62
+ Epic,
63
+ Legendary
64
+ }
65
+
66
+ public enum ItemType
67
+ {
68
+ Consumable,
69
+ Equipment,
70
+ Material,
71
+ Quest,
72
+ Currency,
73
+ Key
74
+ }
75
+
76
+ // Specific item types
77
+ [CreateAssetMenu(fileName = "New Weapon", menuName = "RPG/Items/Weapon")]
78
+ public class WeaponData : ItemData
79
+ {
80
+ public int BaseDamage;
81
+ public float AttackSpeed;
82
+ public DamageType DamageType;
83
+ public WeaponType WeaponType;
84
+
85
+ public override void Equip(Character character)
86
+ {
87
+ character.Stats.AddModifier(StatType.Attack, BaseDamage);
88
+ }
89
+
90
+ public override void Unequip(Character character)
91
+ {
92
+ character.Stats.RemoveModifier(StatType.Attack, BaseDamage);
93
+ }
94
+ }
95
+
96
+ [CreateAssetMenu(fileName = "New Consumable", menuName = "RPG/Items/Consumable")]
97
+ public class ConsumableData : ItemData
98
+ {
99
+ public ConsumableType ConsumableType;
100
+ public float Value;
101
+ public float Duration;
102
+ public StatusEffectType EffectType;
103
+
104
+ public override void Use(Character character)
105
+ {
106
+ switch (ConsumableType)
107
+ {
108
+ case ConsumableType.HealthPotion:
109
+ character.Health.Heal(Value);
110
+ break;
111
+ case ConsumableType.ManaPotion:
112
+ character.Mana.Restore(Value);
113
+ break;
114
+ case ConsumableType.Buff:
115
+ character.StatusEffects.ApplyEffect(EffectType, Duration, Value);
116
+ break;
117
+ }
118
+ }
119
+ }
120
+ ```
121
+
122
+ ### Inventory Manager
123
+
124
+ ```csharp
125
+ public class Inventory : MonoBehaviour
126
+ {
127
+ [SerializeField] private int maxSlots = 40;
128
+
129
+ private List<InventorySlot> _slots = new();
130
+
131
+ public event Action<InventorySlot> OnSlotChanged;
132
+ public event Action OnInventoryChanged;
133
+
134
+ public IReadOnlyList<InventorySlot> Slots => _slots;
135
+ public int MaxSlots => maxSlots;
136
+ public int UsedSlots => _slots.Count(s => !s.IsEmpty);
137
+
138
+ void Awake()
139
+ {
140
+ // Initialize empty slots
141
+ for (int i = 0; i < maxSlots; i++)
142
+ {
143
+ _slots.Add(new InventorySlot(i));
144
+ }
145
+ }
146
+
147
+ public bool AddItem(ItemData item, int quantity = 1)
148
+ {
149
+ if (item == null || quantity <= 0) return false;
150
+
151
+ int remaining = quantity;
152
+
153
+ // Try to stack with existing items first
154
+ if (item.IsStackable)
155
+ {
156
+ foreach (var slot in _slots.Where(s => s.Item == item && s.Quantity < item.MaxStackSize))
157
+ {
158
+ int canAdd = Mathf.Min(remaining, item.MaxStackSize - slot.Quantity);
159
+ slot.Quantity += canAdd;
160
+ remaining -= canAdd;
161
+
162
+ OnSlotChanged?.Invoke(slot);
163
+
164
+ if (remaining <= 0) break;
165
+ }
166
+ }
167
+
168
+ // Add to empty slots
169
+ while (remaining > 0)
170
+ {
171
+ var emptySlot = _slots.FirstOrDefault(s => s.IsEmpty);
172
+ if (emptySlot == null)
173
+ {
174
+ Debug.Log("Inventory full!");
175
+ return remaining < quantity; // Partial success
176
+ }
177
+
178
+ int toAdd = item.IsStackable ? Mathf.Min(remaining, item.MaxStackSize) : 1;
179
+ emptySlot.SetItem(item, toAdd);
180
+ remaining -= toAdd;
181
+
182
+ OnSlotChanged?.Invoke(emptySlot);
183
+ }
184
+
185
+ OnInventoryChanged?.Invoke();
186
+ return true;
187
+ }
188
+
189
+ public bool RemoveItem(ItemData item, int quantity = 1)
190
+ {
191
+ int remaining = quantity;
192
+
193
+ foreach (var slot in _slots.Where(s => s.Item == item).OrderBy(s => s.Quantity))
194
+ {
195
+ int toRemove = Mathf.Min(remaining, slot.Quantity);
196
+ slot.Quantity -= toRemove;
197
+ remaining -= toRemove;
198
+
199
+ if (slot.Quantity <= 0)
200
+ {
201
+ slot.Clear();
202
+ }
203
+
204
+ OnSlotChanged?.Invoke(slot);
205
+
206
+ if (remaining <= 0) break;
207
+ }
208
+
209
+ OnInventoryChanged?.Invoke();
210
+ return remaining <= 0;
211
+ }
212
+
213
+ public int GetItemCount(ItemData item)
214
+ {
215
+ return _slots.Where(s => s.Item == item).Sum(s => s.Quantity);
216
+ }
217
+
218
+ public bool HasItem(ItemData item, int quantity = 1)
219
+ {
220
+ return GetItemCount(item) >= quantity;
221
+ }
222
+
223
+ public void SwapSlots(int indexA, int indexB)
224
+ {
225
+ if (indexA < 0 || indexA >= maxSlots || indexB < 0 || indexB >= maxSlots) return;
226
+
227
+ var slotA = _slots[indexA];
228
+ var slotB = _slots[indexB];
229
+
230
+ // Can we stack?
231
+ if (slotA.Item == slotB.Item && slotA.Item != null && slotA.Item.IsStackable)
232
+ {
233
+ int total = slotA.Quantity + slotB.Quantity;
234
+ if (total <= slotA.Item.MaxStackSize)
235
+ {
236
+ slotB.Quantity = total;
237
+ slotA.Clear();
238
+ }
239
+ else
240
+ {
241
+ slotB.Quantity = slotB.Item.MaxStackSize;
242
+ slotA.Quantity = total - slotB.Item.MaxStackSize;
243
+ }
244
+ }
245
+ else
246
+ {
247
+ // Swap
248
+ var tempItem = slotA.Item;
249
+ var tempQty = slotA.Quantity;
250
+
251
+ slotA.SetItem(slotB.Item, slotB.Quantity);
252
+ slotB.SetItem(tempItem, tempQty);
253
+ }
254
+
255
+ OnSlotChanged?.Invoke(slotA);
256
+ OnSlotChanged?.Invoke(slotB);
257
+ }
258
+
259
+ public List<string> GetItemIds()
260
+ {
261
+ return _slots
262
+ .Where(s => !s.IsEmpty)
263
+ .SelectMany(s => Enumerable.Repeat(s.Item.Id, s.Quantity))
264
+ .ToList();
265
+ }
266
+
267
+ public void LoadItems(List<string> itemIds)
268
+ {
269
+ // Clear first
270
+ foreach (var slot in _slots) slot.Clear();
271
+
272
+ foreach (var id in itemIds)
273
+ {
274
+ var item = ItemDatabase.GetItem(id);
275
+ if (item != null)
276
+ {
277
+ AddItem(item, 1);
278
+ }
279
+ }
280
+ }
281
+ }
282
+
283
+ [System.Serializable]
284
+ public class InventorySlot
285
+ {
286
+ public int Index { get; private set; }
287
+ public ItemData Item { get; private set; }
288
+ public int Quantity { get; set; }
289
+
290
+ public bool IsEmpty => Item == null || Quantity <= 0;
291
+
292
+ public InventorySlot(int index)
293
+ {
294
+ Index = index;
295
+ }
296
+
297
+ public void SetItem(ItemData item, int quantity)
298
+ {
299
+ Item = item;
300
+ Quantity = quantity;
301
+ }
302
+
303
+ public void Clear()
304
+ {
305
+ Item = null;
306
+ Quantity = 0;
307
+ }
308
+ }
309
+ ```
310
+
311
+ ---
312
+
313
+ ## Equipment System
314
+
315
+ ```csharp
316
+ public class EquipmentManager : MonoBehaviour
317
+ {
318
+ [SerializeField] private EquipmentSlotType[] slotTypes;
319
+
320
+ private Dictionary<EquipmentSlotType, ItemData> _equipped = new();
321
+
322
+ public event Action<EquipmentSlotType, ItemData> OnEquipmentChanged;
323
+
324
+ private Character _character;
325
+ private Inventory _inventory;
326
+
327
+ void Awake()
328
+ {
329
+ _character = GetComponent<Character>();
330
+ _inventory = GetComponent<Inventory>();
331
+
332
+ // Initialize slots
333
+ foreach (var type in slotTypes)
334
+ {
335
+ _equipped[type] = null;
336
+ }
337
+ }
338
+
339
+ public bool Equip(ItemData item, EquipmentSlotType slot)
340
+ {
341
+ if (item == null || !item.IsEquippable) return false;
342
+
343
+ // Unequip current
344
+ if (_equipped[slot] != null)
345
+ {
346
+ Unequip(slot);
347
+ }
348
+
349
+ // Remove from inventory
350
+ _inventory.RemoveItem(item, 1);
351
+
352
+ // Equip
353
+ _equipped[slot] = item;
354
+ item.Equip(_character);
355
+
356
+ OnEquipmentChanged?.Invoke(slot, item);
357
+ return true;
358
+ }
359
+
360
+ public void Unequip(EquipmentSlotType slot)
361
+ {
362
+ var item = _equipped[slot];
363
+ if (item == null) return;
364
+
365
+ item.Unequip(_character);
366
+ _inventory.AddItem(item, 1);
367
+ _equipped[slot] = null;
368
+
369
+ OnEquipmentChanged?.Invoke(slot, null);
370
+ }
371
+
372
+ public ItemData GetEquipped(EquipmentSlotType slot)
373
+ {
374
+ return _equipped.GetValueOrDefault(slot);
375
+ }
376
+ }
377
+
378
+ public enum EquipmentSlotType
379
+ {
380
+ Head,
381
+ Chest,
382
+ Legs,
383
+ Feet,
384
+ Hands,
385
+ MainHand,
386
+ OffHand,
387
+ Ring1,
388
+ Ring2,
389
+ Necklace
390
+ }
391
+ ```
392
+
393
+ ---
394
+
395
+ ## Quest System
396
+
397
+ ### Quest Data
398
+
399
+ ```csharp
400
+ [CreateAssetMenu(fileName = "New Quest", menuName = "RPG/Quest")]
401
+ public class QuestData : ScriptableObject
402
+ {
403
+ [Header("Info")]
404
+ public string Id;
405
+ public string Title;
406
+ [TextArea] public string Description;
407
+ public QuestType Type;
408
+
409
+ [Header("Requirements")]
410
+ public int RequiredLevel;
411
+ public QuestData[] Prerequisites;
412
+
413
+ [Header("Objectives")]
414
+ public QuestObjective[] Objectives;
415
+
416
+ [Header("Rewards")]
417
+ public int ExperienceReward;
418
+ public int CurrencyReward;
419
+ public ItemReward[] ItemRewards;
420
+
421
+ [Header("Dialogue")]
422
+ public string StartDialogueId;
423
+ public string CompleteDialogueId;
424
+ public string ProgressDialogueId;
425
+ }
426
+
427
+ [System.Serializable]
428
+ public class QuestObjective
429
+ {
430
+ public string Description;
431
+ public ObjectiveType Type;
432
+ public string TargetId; // Enemy type, item type, location, etc.
433
+ public int RequiredCount;
434
+ }
435
+
436
+ [System.Serializable]
437
+ public class ItemReward
438
+ {
439
+ public ItemData Item;
440
+ public int Quantity;
441
+ }
442
+
443
+ public enum QuestType
444
+ {
445
+ MainStory,
446
+ Side,
447
+ Daily,
448
+ Repeatable
449
+ }
450
+
451
+ public enum ObjectiveType
452
+ {
453
+ Kill,
454
+ Collect,
455
+ Talk,
456
+ Reach,
457
+ Escort,
458
+ Defend
459
+ }
460
+ ```
461
+
462
+ ### Quest Manager
463
+
464
+ ```csharp
465
+ public class QuestManager : MonoBehaviour
466
+ {
467
+ public static QuestManager Instance { get; private set; }
468
+
469
+ private Dictionary<string, QuestProgress> _activeQuests = new();
470
+ private HashSet<string> _completedQuests = new();
471
+
472
+ public event Action<QuestData> OnQuestStarted;
473
+ public event Action<QuestData, QuestObjective> OnObjectiveProgress;
474
+ public event Action<QuestData> OnQuestCompleted;
475
+
476
+ public IReadOnlyDictionary<string, QuestProgress> ActiveQuests => _activeQuests;
477
+
478
+ void Awake()
479
+ {
480
+ Instance = this;
481
+ }
482
+
483
+ public bool CanStartQuest(QuestData quest)
484
+ {
485
+ // Already active or completed
486
+ if (_activeQuests.ContainsKey(quest.Id)) return false;
487
+ if (_completedQuests.Contains(quest.Id) && quest.Type != QuestType.Repeatable) return false;
488
+
489
+ // Level requirement
490
+ if (PlayerManager.Instance.Level < quest.RequiredLevel) return false;
491
+
492
+ // Prerequisites
493
+ foreach (var prereq in quest.Prerequisites)
494
+ {
495
+ if (!_completedQuests.Contains(prereq.Id)) return false;
496
+ }
497
+
498
+ return true;
499
+ }
500
+
501
+ public void StartQuest(QuestData quest)
502
+ {
503
+ if (!CanStartQuest(quest)) return;
504
+
505
+ var progress = new QuestProgress(quest);
506
+ _activeQuests[quest.Id] = progress;
507
+
508
+ OnQuestStarted?.Invoke(quest);
509
+ Debug.Log($"Quest started: {quest.Title}");
510
+ }
511
+
512
+ public void UpdateObjective(ObjectiveType type, string targetId, int count = 1)
513
+ {
514
+ foreach (var kvp in _activeQuests)
515
+ {
516
+ var progress = kvp.Value;
517
+
518
+ for (int i = 0; i < progress.Quest.Objectives.Length; i++)
519
+ {
520
+ var objective = progress.Quest.Objectives[i];
521
+
522
+ if (objective.Type == type && objective.TargetId == targetId)
523
+ {
524
+ progress.ObjectiveProgress[i] += count;
525
+
526
+ OnObjectiveProgress?.Invoke(progress.Quest, objective);
527
+
528
+ // Check completion
529
+ if (IsQuestComplete(progress))
530
+ {
531
+ CompleteQuest(progress.Quest);
532
+ }
533
+ }
534
+ }
535
+ }
536
+ }
537
+
538
+ private bool IsQuestComplete(QuestProgress progress)
539
+ {
540
+ for (int i = 0; i < progress.Quest.Objectives.Length; i++)
541
+ {
542
+ if (progress.ObjectiveProgress[i] < progress.Quest.Objectives[i].RequiredCount)
543
+ {
544
+ return false;
545
+ }
546
+ }
547
+ return true;
548
+ }
549
+
550
+ private void CompleteQuest(QuestData quest)
551
+ {
552
+ if (!_activeQuests.ContainsKey(quest.Id)) return;
553
+
554
+ _activeQuests.Remove(quest.Id);
555
+ _completedQuests.Add(quest.Id);
556
+
557
+ // Give rewards
558
+ PlayerManager.Instance.AddExperience(quest.ExperienceReward);
559
+ PlayerManager.Instance.AddCurrency(quest.CurrencyReward);
560
+
561
+ foreach (var reward in quest.ItemRewards)
562
+ {
563
+ PlayerManager.Instance.Inventory.AddItem(reward.Item, reward.Quantity);
564
+ }
565
+
566
+ OnQuestCompleted?.Invoke(quest);
567
+ Debug.Log($"Quest completed: {quest.Title}");
568
+ }
569
+
570
+ public List<string> GetCompletedQuestIds() => _completedQuests.ToList();
571
+
572
+ public void RestoreCompletedQuests(List<string> ids)
573
+ {
574
+ _completedQuests = new HashSet<string>(ids);
575
+ }
576
+ }
577
+
578
+ public class QuestProgress
579
+ {
580
+ public QuestData Quest { get; private set; }
581
+ public int[] ObjectiveProgress { get; private set; }
582
+
583
+ public QuestProgress(QuestData quest)
584
+ {
585
+ Quest = quest;
586
+ ObjectiveProgress = new int[quest.Objectives.Length];
587
+ }
588
+ }
589
+ ```
590
+
591
+ ---
592
+
593
+ ## Loot Tables
594
+
595
+ ```csharp
596
+ [CreateAssetMenu(fileName = "New Loot Table", menuName = "RPG/Loot Table")]
597
+ public class LootTable : ScriptableObject
598
+ {
599
+ [System.Serializable]
600
+ public class LootEntry
601
+ {
602
+ public ItemData Item;
603
+ [Range(0, 100)] public float DropChance;
604
+ public int MinQuantity = 1;
605
+ public int MaxQuantity = 1;
606
+ }
607
+
608
+ public LootEntry[] Entries;
609
+ public int GuaranteedDrops = 1;
610
+ public int MaxDrops = 5;
611
+
612
+ public List<(ItemData item, int quantity)> Roll()
613
+ {
614
+ var drops = new List<(ItemData, int)>();
615
+ var sortedEntries = Entries.OrderByDescending(e => e.DropChance).ToList();
616
+
617
+ // Guaranteed drops (highest chance items)
618
+ for (int i = 0; i < GuaranteedDrops && i < sortedEntries.Count; i++)
619
+ {
620
+ var entry = sortedEntries[i];
621
+ int qty = Random.Range(entry.MinQuantity, entry.MaxQuantity + 1);
622
+ drops.Add((entry.Item, qty));
623
+ }
624
+
625
+ // Chance drops
626
+ foreach (var entry in Entries)
627
+ {
628
+ if (drops.Count >= MaxDrops) break;
629
+ if (drops.Any(d => d.item == entry.Item)) continue;
630
+
631
+ if (Random.Range(0f, 100f) <= entry.DropChance)
632
+ {
633
+ int qty = Random.Range(entry.MinQuantity, entry.MaxQuantity + 1);
634
+ drops.Add((entry.Item, qty));
635
+ }
636
+ }
637
+
638
+ return drops;
639
+ }
640
+ }
641
+
642
+ // Usage
643
+ public class Enemy : MonoBehaviour
644
+ {
645
+ [SerializeField] private LootTable lootTable;
646
+
647
+ void OnDeath()
648
+ {
649
+ var drops = lootTable.Roll();
650
+
651
+ foreach (var (item, quantity) in drops)
652
+ {
653
+ SpawnLootDrop(item, quantity);
654
+ }
655
+ }
656
+
657
+ private void SpawnLootDrop(ItemData item, int quantity)
658
+ {
659
+ // Spawn pickup in world or add directly
660
+ }
661
+ }
662
+ ```
663
+
664
+ ---
665
+
666
+ ## Crafting System
667
+
668
+ ```csharp
669
+ [CreateAssetMenu(fileName = "New Recipe", menuName = "RPG/Recipe")]
670
+ public class CraftingRecipe : ScriptableObject
671
+ {
672
+ [System.Serializable]
673
+ public class Ingredient
674
+ {
675
+ public ItemData Item;
676
+ public int Quantity;
677
+ }
678
+
679
+ public string RecipeId;
680
+ public string RecipeName;
681
+ public Ingredient[] Ingredients;
682
+ public ItemData Result;
683
+ public int ResultQuantity = 1;
684
+ public float CraftTime = 1f;
685
+ public int RequiredLevel;
686
+ }
687
+
688
+ public class CraftingManager : MonoBehaviour
689
+ {
690
+ [SerializeField] private CraftingRecipe[] knownRecipes;
691
+
692
+ private Inventory _inventory;
693
+
694
+ void Awake()
695
+ {
696
+ _inventory = GetComponent<Inventory>();
697
+ }
698
+
699
+ public bool CanCraft(CraftingRecipe recipe)
700
+ {
701
+ foreach (var ingredient in recipe.Ingredients)
702
+ {
703
+ if (!_inventory.HasItem(ingredient.Item, ingredient.Quantity))
704
+ {
705
+ return false;
706
+ }
707
+ }
708
+ return true;
709
+ }
710
+
711
+ public bool Craft(CraftingRecipe recipe)
712
+ {
713
+ if (!CanCraft(recipe)) return false;
714
+
715
+ // Consume ingredients
716
+ foreach (var ingredient in recipe.Ingredients)
717
+ {
718
+ _inventory.RemoveItem(ingredient.Item, ingredient.Quantity);
719
+ }
720
+
721
+ // Add result
722
+ _inventory.AddItem(recipe.Result, recipe.ResultQuantity);
723
+
724
+ return true;
725
+ }
726
+
727
+ public List<CraftingRecipe> GetAvailableRecipes()
728
+ {
729
+ return knownRecipes.Where(CanCraft).ToList();
730
+ }
731
+ }
732
+ ```
733
+
734
+ ---
735
+
736
+ ## Anti-Patterns
737
+
738
+ | ❌ Don't | ✅ Do |
739
+ |----------|-------|
740
+ | Store items as strings | Use ScriptableObjects |
741
+ | Hard-coded quest logic | Data-driven quests |
742
+ | No item stacking | Support stacking |
743
+ | Instant crafting | Show progress feedback |
744
+
745
+ ---
746
+
747
+ > **Remember:** Players love collecting and progressing. Make it satisfying.