@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,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
|