Added Read Me
This commit is contained in:
parent
9d77149b41
commit
db258bdcae
|
|
@ -0,0 +1,267 @@
|
|||
# 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)
|
||||
|
||||
```bash
|
||||
./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](https://phaser.io/) (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:
|
||||
|
||||
1. **Draw** — both sides draw up to a hand limit (MAX_HAND = 3)
|
||||
2. **Deploy** — cards are placed into open lanes (MAX_LANES = 4)
|
||||
3. **Activation** — `on_turn_start` skill triggers fire
|
||||
4. **Rupture** — wound/DoT stacks deal damage
|
||||
5. **Jam** — jammed cards are skipped this turn
|
||||
6. **Commander Skills** — commander passive/active skills resolve
|
||||
7. **Attack** — each lane attacks the opposing lane; skills fire on pre-attack/on-attack/on-kill triggers
|
||||
8. **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.
|
||||
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
|
||||
```js
|
||||
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 `.js` file and refresh the browser — ES modules are loaded directly.
|
||||
- **No linting or test suite** is configured.
|
||||
- **Phaser version** is pinned in `index.html` via CDN. Upgrade by changing the script URL.
|
||||
- **Adding a card:** add an entry to `data/cards.json` and place the card art in `assets/images/cards/`. The card ID is used as the image key in `BootScene`.
|
||||
- **Adding a skill:** implement the effect in `SkillProcessor.js` and add the reference entry to `data/skills.json`.
|
||||
- **Adding a scene:** create the file in `src/scenes/`, then register it in `src/game.js`.
|
||||
- **Animation timing:** stat changes must never appear before their animations. Use snapshot-before / refreshStatsOnly / restore-reapply patterns — see `feedback_animation_timing.md` in project memory.
|
||||
Loading…
Reference in New Issue