|
|
||
|---|---|---|
| .claude | ||
| assets | ||
| data | ||
| src | ||
| CLAUDE.md | ||
| README.md | ||
| index.html | ||
| start_web.sh | ||
README.md
Tyrants Edge
A browser-based deck-building card game built with Phaser 3. Command five factions across a tactical battlefield — deploy assault cards, trigger skill chains, and defeat your opponent's commander in turn-based 1v1 combat.
Quick Start
Requirements: Python 3, a modern browser (Chrome/Firefox/Safari)
./start_web.sh
# or
python3 -m http.server 8000
Open http://localhost:8000. No build step — the game runs directly from source as ES modules. Refresh the page after any code change.
Game Modes
| Mode | Description |
|---|---|
| Campaign | Five faction story campaigns, each with ~7 missions and lore. Defeat enemy commanders to unlock new campaigns and earn card rewards. |
| Skirmish | Standalone battles. Choose a card level (1–5) and difficulty (Easy / Medium / Hard / Legendary), then either pick a premade deck or draft one from a budget card pool. |
| Deck Builder | Construct custom decks: 1 commander + 3–10 assault cards, max 3 copies of any card. |
| Collection | Browse and filter your owned cards by faction and rarity. |
| Store | Spend gold on faction-specific booster packs (5 factions, 4 cards per pack). |
| Fusion | Sacrifice 3 cards of the same rarity to forge 1 card of the next rarity (common → rare → epic → legendary). |
Architecture
Vanilla JavaScript with Phaser 3 (v3.90.0, loaded via CDN). No bundler, no npm, no build step.
Scene Flow
BootScene → IntroScene → MainMenuScene
│
┌───────────────┼───────────────────┐
▼ ▼ ▼
CampaignSelectScene SkirmishSetupScene DeckBuilderScene
│ │
CampaignScene SkirmishDeckSelectScene
│ │
└───────────────┘
▼
BattleScene
│
BattleResultScene
Other scenes: CollectionScene, StoreScene, FusionScene
BootScene preloads all JSON data and card art, then initialises global managers before handing off to the intro video.
Global Managers
Stored in Phaser's game registry; access anywhere with this.registry.get(key).
| Key | Class | Purpose |
|---|---|---|
cardManager |
CardManager |
Card definitions and instance creation |
packManager |
PackManager |
Booster pack opening and drop-rate logic |
save |
SaveManager |
Versioned localStorage persistence |
deckManager |
DeckManager |
Deck validation and construction |
tutorial |
TutorialManager |
Tutorial stage progression tracking |
missions |
raw JSON | Campaign mission data |
Source Layout
src/
├── game.js # Phaser config & scene list
├── combat/
│ ├── CombatEngine.js # Turn loop and phase resolution
│ ├── SkillProcessor.js # Skill effect evaluation
│ └── CombatAI.js # AI card selection
├── managers/
│ ├── CardManager.js
│ ├── PackManager.js
│ ├── SaveManager.js
│ ├── DeckManager.js
│ └── TutorialManager.js
├── objects/
│ ├── CardObject.js # Card rendering (Phaser Container)
│ ├── BattleField.js # Lane layout and positioning
│ ├── CardTooltip.js # Hover tooltip
│ └── HealthBar.js # Health/armor bar
├── scenes/ # One file per scene (14 total)
└── utils/
└── RNG.js # Seeded deterministic random
Combat System
Turn Phases
Each turn runs the following phases in order:
- Draw — both sides draw up to a hand limit (MAX_HAND = 3)
- Deploy — cards are placed into open lanes (MAX_LANES = 4)
- Activation —
on_turn_startskill triggers fire - Rupture — wound/DoT stacks deal damage
- Jam — jammed cards are skipped this turn
- Commander Skills — commander passive/active skills resolve
- Attack — each lane attacks the opposing lane; skills fire on pre-attack/on-attack/on-kill triggers
- Cleanup — dead cards are removed, state is prepared for next turn
CombatEngine.js maintains an event queue so the UI can animate each step sequentially without the logic blocking.
Skills
SkillProcessor.js resolves all skill effects. Skills are attached to cards with an optional value, trigger, and all flag.
Skill Reference
| Category | Skills |
|---|---|
| Offense | strike, swipe, pierce, wound, mortar, flurry, berserk |
| Control | jam, enfeeble, weaken |
| Defense | protect, counter, wall, armored, carapace |
| Support | heal, rally, siphon, drain, cleanse, sanctify |
| Special | burrow, molt, hive_link, evolve, legion, valor, tribute |
Triggers
| Trigger | When it fires |
|---|---|
preBattle |
Once, at the start of the match |
on_turn_start |
At the beginning of each turn |
preAttack |
Before this card attacks |
on_attack |
When this card attacks |
on_defend |
When this card is attacked |
on_kill |
When this card destroys an enemy |
passive |
Continuous effect |
Win Conditions
- Commander Kill — reduce the opponent's commander health to zero
- Overwhelm — triggered when certain conditions are met (see overwhelm visual effect in
BattleScene)
Deterministic RNG
RNG.js provides a seeded pseudo-random number generator. Passing the same seed to CombatEngine and CombatAI produces an identical, replayable battle.
Data Model
All game content lives in data/ as JSON. No server needed — BootScene fetches these files directly.
cards.json
104 cards across 5 factions. Each card supports multiple level tiers with independent stats and skills.
{
"id": "imp_cmd_1",
"name": "Warlord Voss",
"faction": "imperial",
"type": "commander",
"rarity": "legendary",
"levels": [
{
"attack": 0,
"health": 30,
"armor": 2,
"delay": 0,
"skills": [
{ "name": "strike", "value": 4, "trigger": "on_attack" }
]
}
]
}
Factions: imperial, raider, bloodthirsty, xeno, righteous
Types: commander, assault
Rarities: common, rare, epic, legendary
missions.json
37 missions across all campaigns.
{
"id": "mission_001",
"name": "First Contact",
"chapter": 1,
"campaignId": "campaign_raider",
"enemyLevel": 1,
"opponent": {
"commander": "raider_cmd_1",
"cards": ["card_id", "..."]
},
"rewards": { "gold": 100, "cards": ["card_id"] }
}
campaigns.json
5 faction campaigns. Each references an ordered list of mission IDs and a starter card reward.
packs.json
5 faction packs at 400 gold each. Each pack has 4 card slots — 3 guaranteed commons and 1 weighted slot (35% common / 50% rare / 10% epic / 5% legendary).
skills.json
Skill reference data (name, description, trigger, category). Skills are also defined inline on cards in cards.json.
skirmish_decks.json
Predefined decks used in skirmish mode, annotated with difficulty and faction summary.
Save Data
Stored in localStorage under the key tyrants-edge-save. The current schema is version 3; SaveManager handles forward migration automatically.
{
"version": 3,
"gold": 300,
"level": 1,
"collection": { "card_id": 2 },
"decks": [
{
"id": "deck_1",
"name": "My Deck",
"commander": "imp_cmd_1",
"cards": ["card_id", "card_id", "..."]
}
],
"campaignProgress": {
"completedMissions": ["mission_001"],
"currentCampaign": "campaign_raider"
},
"unlockedCampaigns": ["campaign_raider"],
"tutorialProgress": { "completedStages": [1, 2] },
"starterDeckReceived": false
}
To reset save state, run in the browser console:
localStorage.removeItem('tyrants-edge-save');
Assets
assets/
├── fonts/ # RaiderCrusader (headers), Audiowide (body)
├── video/ # intro.mp4, menu_bg.mp4, campaign/mission backgrounds, forge video
├── audio/ # 6 battle music tracks, skill/action SFX, UI sounds
└── images/
├── cards/ # Card art (one image per card)
├── packs/ # Booster pack artwork
└── ui/ # vs icon, attack indicator, battle backgrounds, spritesheets
Development Notes
- No build step. Edit any
.jsfile and refresh the browser — ES modules are loaded directly. - No linting or test suite is configured.
- Phaser version is pinned in
index.htmlvia CDN. Upgrade by changing the script URL. - Adding a card: add an entry to
data/cards.jsonand place the card art inassets/images/cards/. The card ID is used as the image key inBootScene. - Adding a skill: implement the effect in
SkillProcessor.jsand add the reference entry todata/skills.json. - Adding a scene: create the file in
src/scenes/, then register it insrc/game.js. - Animation timing: stat changes must never appear before their animations. Use snapshot-before / refreshStatsOnly / restore-reapply patterns — see
feedback_animation_timing.mdin project memory.