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