@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,618 @@
1
+ ---
2
+ name: card-battle-engine
3
+ description: Card game and deck building systems. Card data structures, deck management, turn-based combat, effect stacking, synergies, gacha/pack opening, and hand management for Unity.
4
+ ---
5
+
6
+ # Card Battle Engine Skill
7
+
8
+ > **Purpose**: Build card-based RPG and strategy games.
9
+
10
+ ## When to Use
11
+ - Building card battle games
12
+ - Implementing deck building mechanics
13
+ - Creating turn-based card combat
14
+ - Designing gacha/pack systems
15
+
16
+ ---
17
+
18
+ ## 1. Card Data Structure
19
+
20
+ ### Core Card Definition
21
+ ```csharp
22
+ [CreateAssetMenu(fileName = "Card", menuName = "Game/Cards/CardData")]
23
+ public class CardData : ScriptableObject
24
+ {
25
+ [Header("Identity")]
26
+ public string cardId;
27
+ public string cardName;
28
+ public Sprite artwork;
29
+ public CardRarity rarity;
30
+ public CardType type;
31
+
32
+ [Header("Cost")]
33
+ public int manaCost;
34
+ public ResourceCost[] additionalCosts;
35
+
36
+ [Header("Stats (for Units)")]
37
+ public int attack;
38
+ public int health;
39
+
40
+ [Header("Effects")]
41
+ public CardEffect[] onPlayEffects;
42
+ public CardEffect[] onDeathEffects;
43
+ public CardEffect[] passiveEffects;
44
+
45
+ [Header("Keywords")]
46
+ public CardKeyword[] keywords;
47
+
48
+ [Header("Deckbuilding")]
49
+ public int maxCopiesInDeck = 3;
50
+ public CardFaction faction;
51
+
52
+ [Header("Flavor")]
53
+ [TextArea] public string flavorText;
54
+ public string artistName;
55
+ }
56
+
57
+ public enum CardRarity
58
+ {
59
+ Common,
60
+ Uncommon,
61
+ Rare,
62
+ Epic,
63
+ Legendary
64
+ }
65
+
66
+ public enum CardType
67
+ {
68
+ Unit, // Creatures/minions
69
+ Spell, // One-time effects
70
+ Equipment, // Attach to units
71
+ Structure, // Persistent on field
72
+ Trap // Triggered effects
73
+ }
74
+
75
+ public enum CardKeyword
76
+ {
77
+ Taunt, // Must be attacked first
78
+ Rush, // Can attack immediately
79
+ Stealth, // Can't be targeted
80
+ Divine Shield, // Blocks first damage
81
+ Lifesteal, // Heal on damage
82
+ Poison, // Destroy on damage
83
+ Freeze, // Skip next action
84
+ Silence, // Remove abilities
85
+ DoublStrike // Attack twice
86
+ }
87
+ ```
88
+
89
+ ---
90
+
91
+ ## 2. Card Effect System
92
+
93
+ ### Effect Architecture
94
+ ```csharp
95
+ [System.Serializable]
96
+ public class CardEffect
97
+ {
98
+ public EffectType type;
99
+ public TargetType target;
100
+ public int value;
101
+ public int duration; // For buffs/debuffs
102
+ public EffectCondition condition;
103
+
104
+ [Header("Advanced")]
105
+ public CardEffect[] chainedEffects;
106
+ public string customEffectId;
107
+ }
108
+
109
+ public enum EffectType
110
+ {
111
+ // Damage
112
+ DealDamage,
113
+ DealDamageToAll,
114
+ DealDamageRandom,
115
+
116
+ // Healing
117
+ Heal,
118
+ HealAll,
119
+
120
+ // Stats
121
+ BuffAttack,
122
+ BuffHealth,
123
+ DebuffAttack,
124
+ SetStats,
125
+
126
+ // Card Manipulation
127
+ DrawCards,
128
+ DiscardCards,
129
+ DiscardRandom,
130
+ ReturnToHand,
131
+ ShuffleIntoDeck,
132
+
133
+ // Summon
134
+ SummonUnit,
135
+ SummonCopy,
136
+ Transform,
137
+
138
+ // Control
139
+ Destroy,
140
+ Silence,
141
+ Freeze,
142
+ Stun,
143
+
144
+ // Resource
145
+ GainMana,
146
+ GainGold,
147
+ RestoreMana,
148
+
149
+ // Special
150
+ Custom // For complex effects
151
+ }
152
+
153
+ public enum TargetType
154
+ {
155
+ Self,
156
+ SingleEnemy,
157
+ SingleAlly,
158
+ AllEnemies,
159
+ AllAllies,
160
+ AllUnits,
161
+ RandomEnemy,
162
+ RandomAlly,
163
+ EnemyHero,
164
+ AllyHero,
165
+ Adjacent,
166
+ Chosen // Player chooses
167
+ }
168
+ ```
169
+
170
+ ### Effect Executor
171
+ ```csharp
172
+ public class EffectExecutor : MonoBehaviour
173
+ {
174
+ public IEnumerator ExecuteEffect(CardEffect effect, ICardTarget source, ICardTarget target)
175
+ {
176
+ // Check condition
177
+ if (effect.condition != null && !effect.condition.IsMet(source, target))
178
+ yield break;
179
+
180
+ switch (effect.type)
181
+ {
182
+ case EffectType.DealDamage:
183
+ yield return DealDamage(target, effect.value, source);
184
+ break;
185
+
186
+ case EffectType.Heal:
187
+ yield return Heal(target, effect.value);
188
+ break;
189
+
190
+ case EffectType.DrawCards:
191
+ yield return DrawCards(source.Owner, effect.value);
192
+ break;
193
+
194
+ case EffectType.SummonUnit:
195
+ yield return SummonUnit(source.Owner, effect.customEffectId);
196
+ break;
197
+
198
+ case EffectType.BuffAttack:
199
+ yield return ApplyBuff(target, StatType.Attack, effect.value, effect.duration);
200
+ break;
201
+
202
+ case EffectType.Custom:
203
+ yield return ExecuteCustomEffect(effect.customEffectId, source, target);
204
+ break;
205
+ }
206
+
207
+ // Execute chained effects
208
+ if (effect.chainedEffects != null)
209
+ {
210
+ foreach (var chained in effect.chainedEffects)
211
+ {
212
+ yield return ExecuteEffect(chained, source, target);
213
+ }
214
+ }
215
+ }
216
+ }
217
+ ```
218
+
219
+ ---
220
+
221
+ ## 3. Deck Management
222
+
223
+ ### Deck Data
224
+ ```csharp
225
+ [CreateAssetMenu(fileName = "Deck", menuName = "Game/Cards/Deck")]
226
+ public class DeckData : ScriptableObject
227
+ {
228
+ public string deckName;
229
+ public CardFaction primaryFaction;
230
+ public Sprite deckIcon;
231
+
232
+ [Header("Cards")]
233
+ public DeckEntry[] cards;
234
+
235
+ [Header("Validation")]
236
+ public int minCards = 30;
237
+ public int maxCards = 40;
238
+
239
+ public int TotalCards => cards.Sum(e => e.count);
240
+
241
+ public bool IsValid()
242
+ {
243
+ if (TotalCards < minCards || TotalCards > maxCards)
244
+ return false;
245
+
246
+ // Check max copies
247
+ foreach (var entry in cards)
248
+ {
249
+ if (entry.count > entry.card.maxCopiesInDeck)
250
+ return false;
251
+ }
252
+
253
+ return true;
254
+ }
255
+ }
256
+
257
+ [System.Serializable]
258
+ public struct DeckEntry
259
+ {
260
+ public CardData card;
261
+ public int count;
262
+ }
263
+ ```
264
+
265
+ ### Runtime Deck Manager
266
+ ```csharp
267
+ public class DeckManager
268
+ {
269
+ private List<CardData> _drawPile = new();
270
+ private List<CardData> _discardPile = new();
271
+ private List<CardData> _hand = new();
272
+ private List<CardData> _exhaustPile = new(); // Removed from game
273
+
274
+ public int HandSize => _hand.Count;
275
+ public int DrawPileSize => _drawPile.Count;
276
+ public int DiscardSize => _discardPile.Count;
277
+
278
+ public event Action<CardData> OnCardDrawn;
279
+ public event Action<CardData> OnCardDiscarded;
280
+ public event Action OnDeckShuffled;
281
+ public event Action OnDeckEmpty;
282
+
283
+ public void InitializeDeck(DeckData deckData)
284
+ {
285
+ _drawPile.Clear();
286
+
287
+ foreach (var entry in deckData.cards)
288
+ {
289
+ for (int i = 0; i < entry.count; i++)
290
+ {
291
+ _drawPile.Add(entry.card);
292
+ }
293
+ }
294
+
295
+ Shuffle();
296
+ }
297
+
298
+ public void Shuffle()
299
+ {
300
+ // Fisher-Yates shuffle
301
+ for (int i = _drawPile.Count - 1; i > 0; i--)
302
+ {
303
+ int j = Random.Range(0, i + 1);
304
+ (_drawPile[i], _drawPile[j]) = (_drawPile[j], _drawPile[i]);
305
+ }
306
+
307
+ OnDeckShuffled?.Invoke();
308
+ }
309
+
310
+ public CardData DrawCard()
311
+ {
312
+ if (_drawPile.Count == 0)
313
+ {
314
+ if (_discardPile.Count == 0)
315
+ {
316
+ OnDeckEmpty?.Invoke();
317
+ return null;
318
+ }
319
+
320
+ // Shuffle discard into draw
321
+ _drawPile.AddRange(_discardPile);
322
+ _discardPile.Clear();
323
+ Shuffle();
324
+ }
325
+
326
+ CardData card = _drawPile[0];
327
+ _drawPile.RemoveAt(0);
328
+ _hand.Add(card);
329
+
330
+ OnCardDrawn?.Invoke(card);
331
+ return card;
332
+ }
333
+
334
+ public void DiscardCard(CardData card)
335
+ {
336
+ if (_hand.Remove(card))
337
+ {
338
+ _discardPile.Add(card);
339
+ OnCardDiscarded?.Invoke(card);
340
+ }
341
+ }
342
+
343
+ public void ExhaustCard(CardData card)
344
+ {
345
+ _hand.Remove(card);
346
+ _exhaustPile.Add(card); // Removed from game entirely
347
+ }
348
+ }
349
+ ```
350
+
351
+ ---
352
+
353
+ ## 4. Turn-Based Battle System
354
+
355
+ ### Battle State Machine
356
+ ```csharp
357
+ public class CardBattleManager : MonoBehaviour
358
+ {
359
+ public BattleState CurrentState { get; private set; }
360
+ public Player ActivePlayer { get; private set; }
361
+ public int TurnNumber { get; private set; }
362
+
363
+ public event Action<BattleState> OnStateChanged;
364
+ public event Action<Player> OnTurnStarted;
365
+ public event Action<Player> OnTurnEnded;
366
+
367
+ public void StartBattle(Player player1, Player player2)
368
+ {
369
+ TurnNumber = 0;
370
+ ActivePlayer = DetermineFirstPlayer(player1, player2);
371
+
372
+ StartCoroutine(BattleLoop());
373
+ }
374
+
375
+ private IEnumerator BattleLoop()
376
+ {
377
+ SetState(BattleState.BattleStart);
378
+
379
+ // Initial draws
380
+ yield return DrawInitialHands();
381
+
382
+ while (!IsGameOver())
383
+ {
384
+ TurnNumber++;
385
+
386
+ // Player 1 turn
387
+ yield return RunTurn(ActivePlayer);
388
+
389
+ if (IsGameOver()) break;
390
+
391
+ // Switch active player
392
+ ActivePlayer = GetOpponent(ActivePlayer);
393
+ }
394
+
395
+ SetState(BattleState.BattleEnd);
396
+ DetermineWinner();
397
+ }
398
+
399
+ private IEnumerator RunTurn(Player player)
400
+ {
401
+ OnTurnStarted?.Invoke(player);
402
+
403
+ // Start of turn
404
+ SetState(BattleState.TurnStart);
405
+ yield return player.GainMana(TurnNumber);
406
+ yield return player.DrawCard();
407
+ yield return TriggerStartOfTurnEffects(player);
408
+
409
+ // Main phase - player actions
410
+ SetState(BattleState.MainPhase);
411
+ yield return WaitForPlayerActions(player);
412
+
413
+ // Combat phase
414
+ SetState(BattleState.CombatPhase);
415
+ yield return ResolveCombat(player);
416
+
417
+ // End of turn
418
+ SetState(BattleState.TurnEnd);
419
+ yield return TriggerEndOfTurnEffects(player);
420
+
421
+ OnTurnEnded?.Invoke(player);
422
+ }
423
+ }
424
+
425
+ public enum BattleState
426
+ {
427
+ BattleStart,
428
+ TurnStart,
429
+ MainPhase,
430
+ CombatPhase,
431
+ TurnEnd,
432
+ BattleEnd
433
+ }
434
+ ```
435
+
436
+ ---
437
+
438
+ ## 5. Card Synergies
439
+
440
+ ### Synergy System
441
+ ```csharp
442
+ [CreateAssetMenu(fileName = "Synergy", menuName = "Game/Cards/Synergy")]
443
+ public class CardSynergy : ScriptableObject
444
+ {
445
+ public string synergyName;
446
+ public SynergyType type;
447
+
448
+ [Header("Trigger")]
449
+ public SynergyTrigger trigger;
450
+ public int requiredCount = 3; // Cards needed to activate
451
+
452
+ [Header("Effect")]
453
+ public CardEffect[] bonusEffects;
454
+ public float statMultiplier = 1.2f;
455
+ }
456
+
457
+ public enum SynergyType
458
+ {
459
+ Tribal, // Same creature type (Dragon, Undead)
460
+ Elemental, // Same element (Fire, Ice)
461
+ Faction, // Same faction
462
+ Keyword, // Share keyword (Taunt, Rush)
463
+ Cost, // Same mana cost
464
+ Set // Specific card combination
465
+ }
466
+
467
+ public class SynergyTracker : MonoBehaviour
468
+ {
469
+ private Dictionary<CardSynergy, int> _synergyCounts = new();
470
+ private HashSet<CardSynergy> _activeSynergies = new();
471
+
472
+ public event Action<CardSynergy> OnSynergyActivated;
473
+ public event Action<CardSynergy> OnSynergyDeactivated;
474
+
475
+ public void OnCardPlayed(CardData card)
476
+ {
477
+ // Check all synergies this card contributes to
478
+ foreach (var synergy in GetApplicableSynergies(card))
479
+ {
480
+ _synergyCounts.TryGetValue(synergy, out int count);
481
+ _synergyCounts[synergy] = count + 1;
482
+
483
+ // Check activation
484
+ if (!_activeSynergies.Contains(synergy) &&
485
+ _synergyCounts[synergy] >= synergy.requiredCount)
486
+ {
487
+ ActivateSynergy(synergy);
488
+ }
489
+ }
490
+ }
491
+
492
+ private void ActivateSynergy(CardSynergy synergy)
493
+ {
494
+ _activeSynergies.Add(synergy);
495
+ OnSynergyActivated?.Invoke(synergy);
496
+
497
+ // Apply bonus effects
498
+ foreach (var effect in synergy.bonusEffects)
499
+ {
500
+ ApplySynergyBonus(effect);
501
+ }
502
+ }
503
+ }
504
+ ```
505
+
506
+ ---
507
+
508
+ ## 6. Pack/Gacha System
509
+
510
+ ### Pack Opening
511
+ ```csharp
512
+ [CreateAssetMenu(fileName = "CardPack", menuName = "Game/Cards/Pack")]
513
+ public class CardPackData : ScriptableObject
514
+ {
515
+ public string packName;
516
+ public Sprite packArt;
517
+ public int cardsPerPack = 5;
518
+ public int goldCost = 100;
519
+ public int gemCost = 0;
520
+
521
+ [Header("Rarity Distribution")]
522
+ public RarityDrop[] rarityDrops;
523
+
524
+ [Header("Pity System")]
525
+ public bool hasPitySystem = true;
526
+ public int pityThreshold = 10; // Guaranteed rare after X packs
527
+
528
+ [Header("Card Pool")]
529
+ public CardData[] availableCards;
530
+ }
531
+
532
+ [System.Serializable]
533
+ public struct RarityDrop
534
+ {
535
+ public CardRarity rarity;
536
+ public float dropRate;
537
+ public int guaranteedSlot; // -1 = no guarantee, 4 = slot 5 guaranteed
538
+ }
539
+
540
+ public class PackOpener : MonoBehaviour
541
+ {
542
+ private int _packsSinceRare = 0;
543
+
544
+ public List<CardData> OpenPack(CardPackData pack)
545
+ {
546
+ List<CardData> results = new List<CardData>();
547
+
548
+ for (int i = 0; i < pack.cardsPerPack; i++)
549
+ {
550
+ CardRarity rarity = RollRarity(pack, i);
551
+ CardData card = GetRandomCardOfRarity(pack, rarity);
552
+ results.Add(card);
553
+ }
554
+
555
+ // Pity system
556
+ bool hasRareOrBetter = results.Any(c => c.rarity >= CardRarity.Rare);
557
+ if (hasRareOrBetter)
558
+ {
559
+ _packsSinceRare = 0;
560
+ }
561
+ else
562
+ {
563
+ _packsSinceRare++;
564
+ if (_packsSinceRare >= pack.pityThreshold)
565
+ {
566
+ // Force upgrade last card
567
+ int lastIndex = results.Count - 1;
568
+ results[lastIndex] = GetRandomCardOfRarity(pack, CardRarity.Rare);
569
+ _packsSinceRare = 0;
570
+ }
571
+ }
572
+
573
+ return results;
574
+ }
575
+
576
+ private CardRarity RollRarity(CardPackData pack, int slotIndex)
577
+ {
578
+ // Check guaranteed slots
579
+ foreach (var drop in pack.rarityDrops)
580
+ {
581
+ if (drop.guaranteedSlot == slotIndex)
582
+ return drop.rarity;
583
+ }
584
+
585
+ // Roll based on drop rates
586
+ float roll = Random.value;
587
+ float cumulative = 0f;
588
+
589
+ foreach (var drop in pack.rarityDrops.OrderByDescending(d => d.rarity))
590
+ {
591
+ cumulative += drop.dropRate;
592
+ if (roll < cumulative)
593
+ return drop.rarity;
594
+ }
595
+
596
+ return CardRarity.Common;
597
+ }
598
+ }
599
+ ```
600
+
601
+ ---
602
+
603
+ ## Anti-Patterns
604
+
605
+ | ❌ Don't | ✅ Do |
606
+ |----------|-------|
607
+ | Hardcode card effects | Data-driven effect system |
608
+ | Mutable CardData | Treat CardData as immutable |
609
+ | Sync effects immediately | Use coroutines for animations |
610
+ | Ignore effect order | Clear priority/stack system |
611
+ | No pity system | Protect player investment |
612
+
613
+ ---
614
+
615
+ ## Related Skills
616
+ - `game-development/game-economy-designer` - Card economy
617
+ - `game-development/combat-system` - Combat calculations
618
+ - `game-development/liveops` - Card balance updates