Back to Game
Spellforge Arena - Game Design Guide
Game Concept
# Spellforge Arena
## Theme
You are a battle mage competing in the Spellforge Arena — a magical combat pit where wizards duel by crafting spells in real-time. The twist: you don't just cast pre-made spells. You COMBINE elemental runes during combat to create spell combinations, then send your constructed spells to battle automated guardians and rival mages. The arena is alive with elemental energy, and your spell combinations determine not just damage but battlefield control.
## Visual Game World
The Canvas shows a top-down arena with a magical rune circle at the bottom where the player assembles spells. The center of the arena displays your current active spell (a glowing combination of elemental icons) flying toward enemies. Enemies spawn from the top and move toward your rune circle — if they reach it, you take damage and lose mana. The background is a dark stone arena floor with glowing magical pentagram patterns that pulse with your current dominant element. Elemental particles drift across the screen — embers for fire, sparkles for arcane, droplets for water. When spells hit enemies, there's visual feedback: explosions, freezing effects, lightning arcs.
## Player Interaction
The player interacts DIRECTLY with the Canvas through rune slotting and spell targeting:
1. **Click Rune Slots**: At the bottom of the Canvas is a 3-slot spell builder. Clicking each slot cycles through elemental runes (Fire, Ice, Arcane, Void). The player physically sees their spell assembling as they click — a Fire rune adds orange glow, Ice adds blue crystals, etc.
2. **Cast Spell**: A large CAST button on the Canvas launches the constructed spell. The spell travels visibly from the rune circle toward the target. The player can aim by clicking on the Canvas — the spell flies toward where they clicked.
3. **Target Specific Enemies**: Click on any enemy to mark it as the target for your next spell. A reticle appears around the targeted enemy, and your spells will home toward it.
4. **Elemental Overdrive**: When your elemental charge meter fills (visible on Canvas as a glowing bar), click it to enter Overdrive mode — your next spell becomes a triple-strength version with visual aura effects.
5. **Rearrange Rune Order**: Drag runes between slots to change spell behavior. Fire+Ice is different from Ice+Fire — order matters and creates different spell patterns visible on screen.
## Entity Types
### Guardian
- **Sprite**: `knight` with color variant (steel gray armor, glowing eyes)
- **Appearance**: Medium-sized armored figure with a sword, moves with determined tread
- **Behavior**: Spawns at top of screen, marches steadily toward player's rune circle. Has moderate HP, drops iron shards on death
- **Interaction**: Player targets guardians with spells. Different spell combinations have different effectiveness (Fire does bonus damage, Ice slows them)
### Wisp
- **Sprite**: `ghost` with color variant (cyan/white glow)
- **Appearance**: Small, floating, translucent entity that drifts erratically
- **Behavior**: Moves quickly in zigzag patterns toward player. Low HP but hard to hit due to movement. Drops mana fragments
- **Interaction**: Arcane spells home in on wisps automatically. Other spell types require manual aiming
### Golem
- **Sprite**: `slime` with scale 2.0x and brown/gray color variant
- **Appearance**: Large, bulky, slow-moving creature that looms ominously
- **Behavior**: Very slow but massive HP. Takes up significant space on screen. Drops rare crystal shards
- **Interaction**: Void spells penetrate golems for full damage. Physical spell projectiles only deal partial damage
### Spell Projectile
- **Sprite**: `fireball` with procedural color based on elemental composition
- **Appearance**: Glowing orb with trailing particles. Color indicates element (orange=Fire, blue=Ice, purple=Arcane, black=Void)
- **Behavior**: Spawns from rune circle, travels toward target or click location. Speed varies by elemental mix
- **Interaction**: Created by player, travels to enemies. Multiple projectiles can be on screen simultaneously with upgrades
### Element Sprite
- **Sprite**: `spark` with procedural colors and sizes
- **Appearance**: Small particle that appears near rune slots, drifts slowly, and despawns
- **Behavior**: Spawns periodically based on current elemental affinity. Collectible by clicking or hovering near
- **Interaction**: Clicking element sprites grants temporary elemental charge and small resource boosts
### Elite Champion
- **Sprite**: `wizard` with red/purple color variant and slight scale increase (1.2x)
- **Appearance**: Robed figure with glowing staff, moves with purpose
- **Behavior**: Appears every 60 seconds. Has special abilities (summons minions, fires counter-spells). Drops champion essence
- **Interaction**: Requires strategic spell combinations. Weak to specific element that rotates each wave
## Core Loop
### Second-to-Second (ACTIVE GAMEPLAY)
- The player watches enemies spawn and move toward their rune circle
- They click rune slots to build spell combinations (Fire+Fire+Ice = Explosive Fireball that freezes)
- They click CAST or press Space to launch the spell toward a target they've clicked on
- Spells travel visibly across the Canvas, and the player sees them hit enemies
- Enemies die with visual effects, drop resource sprites that the player can click to collect
- The player decides moment-to-moment: fast weak spells or slow powerful ones? Target the dangerous golem or clear the fast wisps?
### Minute-to-Minute (STRATEGIC DECISIONS)
- Between waves, the player purchases rune slot upgrades (4th slot, 5th slot)
- They buy elemental mastery upgrades that change how spell combinations work
- They check their resource stores and decide which elemental affinities to invest in
- The Canvas gameplay changes: new spell combinations unlock, projectile speed increases, new enemy types appear
### Session-Level (META STRATEGY)
- The player pursues an elemental specialization build (Fire-focused for burst damage, Ice for crowd control, Void for boss killing)
- The arena evolves visually: their rune circle grows elaborate, background effects intensify, spell effects become more dramatic
- The push toward prestige: they've unlocked most spell combinations, enemies are scaling faster than upgrades, they're ready to reset for permanent rune slots
## The 30-Second Gameplay Description
A wisp drifts erratically from the top of the arena, zigzagging left and right. You click your first rune slot twice, cycling to Fire, then click the second slot to set it to Ice. A Guardian and two Wisps have spawned — the Guardian is moving straight down the center while the Wisps weave toward the edges. You click on the Guardian to target it, then hit CAST. An orange-blue streak erupts from your rune circle, trailing mixed particles, and slams into the Guardian. The Guardian flashes white and stumbles backward, slowed by the ice component. You quickly cycle your slots to Arcane-Arcane-Arcane, targeting a Wisp this time, and CAST. A purple bolt streaks out, curving mid-flight to home in on the Wisp, popping it instantly. An elemental spark drifts near your rune circle — you click it and your mana bar pulses. The Guardian has recovered and is closing in. You have seconds to craft the right combination.
## Currencies
### Mana
- **Role**: Primary currency
- **Earned by**: Killing enemies drops mana fragments that appear as blue sprites on Canvas. Click them to collect. Also regenerates slowly over time
- **Spent on**: Casting spells (each rune combination costs mana), buying basic upgrades, unlocking new rune elements
- **Feel**: The flow of battle. You're always balancing "cast now" vs "wait for more mana" — creates tension and rhythm
### Iron Shards
- **Role**: Secondary currency / Upgrade material
- **Earned by**: Guardians drop iron shards on death as gray crystal sprites. Also earned from elemental sprite clicks
- **Spent on**: Permanent rune slot unlocks, elemental mastery upgrades, spell damage multipliers
- **Feel**: Long-term progression. These are the "meat" of upgrades — you see them pile up and decide when to spend big
### Crystal Essence
- **Role**: Prestige currency / Rare upgrades
- **Earned by**: Elite Champions drop crystal essence (rare purple sprites), Golem boss kills grant bonus essence, can convert Iron Shards to Essence at poor rate
- **Spent on**: Permanent rune slots that persist across prestige, elemental attunement bonuses, prestige currency storage upgrades
- **Feel**: The precious reward. You see a purple drop and get excited — this is your long-term power
### Elemental Charge
- **Role**: Temporary boost currency
- **Earned by**: Casting spells of the same element builds charge (3 Fire spells in a row = Fire charge), clicking element sprites, killing enemies with their weakness element
- **Spent on**: Overdrive mode (next spell is 3x power), temporary elemental effects (Fire charge = burning damage over time)
- **Feel**: Combat momentum. You're building toward a big moment, then unleash it
### Currency Flow
Mana → Spells → Enemy kills → Iron Shards → Upgrades → More efficient spells → More mana per cast
Iron Shards → Convert → Crystal Essence (at 100:1 ratio) → Prestige upgrades → Bonus Iron Shard drop rate
Elemental Charge → Overdrive → More kills → More Mana → Faster spell casting → Faster charge build
## Progression Hooks
### Early Game (0-5 minutes)
- Hook: The first time you discover a spell combination (Fire+Ice = Explosive Freeze), enemies explode with satisfying visual feedback
- Canvas interaction: Immediately clicking rune slots, casting spells, targeting enemies
- "Aha" moment: Realizing rune ORDER matters — Fire+Ice is different from Ice+Fire
### Mid Game (5-15 minutes)
- New systems: 4th rune slot unlocks, Elite Champions appear with elemental weaknesses shown on Canvas, elemental overdrive becomes available
- Canvas expansion: More simultaneous projectiles, larger area of effect spells, new enemy types (Golems, Champions)
- Strategy: Player must specialize — will they focus on one element or diversify?
### Late Game (15-30 minutes)
- Final push: All 5 rune slots unlocked, mastery of spell combinations, prestige approaching
- Visual complexity: Arena is filled with projectiles, particle effects, multiple enemy types, elaborate rune circle animations
- Challenge: Enemy spawn rate is intense, player must master spell economy and target priority
### Key Milestones
1. **First Spell Cast**: Cast any spell — unlocks the core mechanic
2. **Combination Discovery**: Discover first spell combo — shows rune slot count increases
3. **Rune Slot 4**: Unlock 4th rune slot — spell complexity explodes
4. **First Elite Champion**: Elite appears with health bar and weakness indicator
5. **Elemental Overdrive**: Charge meter fills and becomes clickable
6. **Rune Slot 5**: Final rune slot — maximum spell complexity
7. **Golem Invasion**: First Golem boss — requires strategy to defeat
8. **Prestige Available**: Defeat champion wave 10 — prestige unlocks
## Meaningful Choices
- **Choice 1: Elemental Specialization vs. Versatility**
- Specialize in one element (Fire focus = high burst damage, but vulnerable to water-resistant enemies). Visually, your arena background, rune circle, and spell effects all shift to that element. The player becomes a master of that element but struggles with enemies resistant to it.
- Versatility (balanced elements = always have effective spell, but never maximum damage). Visuals are mixed and colorful. Player can handle any enemy but lacks peak power.
- Changes what the player DOES: Specialist cycles runes less often (stays in their element), Versatile player constantly adapts rune combinations
- **Choice 2: Rune Slot Configuration**
- More slots (5 slots = complex spells with multiple effects) OR fewer slots with mastery (3 slots but each rune is 2x powerful)
- More slots: Player engages in complex rune-cycling, creating elaborate multi-element spells. Canvas shows many projectiles with varied effects
- Fewer slots: Player casts simpler spells more frequently. Faster combat rhythm, less planning
- Changes Canvas gameplay: More slots = spectacular but slow combos, Fewer slots = rapid-fire casting
## Prestige Concept
- **Trigger**: Defeat Champion Wave 15 and reach Arena Tier 10
- **What resets**: Mana, Iron Shards, rune slot count, elemental mastery levels, current wave, arena tier
- **What persists**: Crystal Essence balance, permanent rune slots (purchased with Crystal Essence), elemental attunement bonuses, prestige tier
- **Prestige currency**: Crystal Essence (earned during run + bonus at prestige based on max wave reached)
- **Acceleration**: Start with +1 permanent rune slot (cap of 3), +10% damage to chosen element, faster mana regeneration. Run 2 involves discovering new spell combinations earlier, fighting stronger enemies from the start, and reaching higher waves faster
- **Visual transformation**: Arena background becomes more ornate with each prestige tier. Rune circle gains intricate glowing patterns. Spell projectiles gain trail effects. The arena visibly transforms from a crude stone circle to an elaborate magical sanctum
## Controls Display
- Left Click Rune Slot → Cycle element (Fire/Ice/Arcane/Void)
- Left Click Arena → Target spell location
- SPACE or Click CAST → Launch crafted spell
- Click Resource Sprites → Collect mana/iron/crystal
- Click Overdrive Bar → Activate elemental overdrive
## Getting Started Tutorial
• Click the rune slots at the bottom to craft your spell — combine elements for different effects
• Press SPACE or click CAST to launch your spell at enemies — stop them from reaching your rune circle
• Collect dropped resources by clicking them, and use mana to cast spells, iron to buy upgrades
• Discover spell combinations and survive as many waves as possible to unlock prestige
## Unique Selling Point
The core innovation is **real-time rune slotting** — you're not just clicking a cast button, you're actively assembling spell combinations during combat. The order and composition of your 3-5 rune slots creates actual strategic depth visible on screen, with immediate feedback in how your spells fly, hit, and affect enemies. It's a puzzle game where the puzzle pieces change every second.
## Visual Direction
Color palette: Deep purple and midnight blue background (mystical arena feel) with bright elemental accents — orange fire, cyan ice, violet arcane, black void with white rim-lighting. The arena feels like an underground mystical chamber, with stone textures and glowing magical lines. As the game progresses, the background becomes more elaborate with magical symbols and light rays. Spell effects are juicy with particles, screenshake on big hits, and color-coded explosions.
## Technical Scope Check
Implementable with the sprite framework:
- Fireball sprite can be color-varianted for all spell types using ProceduralSprite
- Knight, Ghost, Slime (scaled), Wizard (colored) cover all enemy types
- Spark sprite for element collectibles
- Canvas interactions are simple click detection on zones (rune slots, arena click areas)
- Spell projectile system is straightforward array of objects with position, velocity, element properties
- Enemy pathfinding is simple "move toward bottom center" with minimal collision
- UI elements are HTML overlay, not Canvas
Complex mechanics to simplify if needed:
- Start with 3 rune slots, expand to 4-5
- Implement 4 base elements first (Fire, Ice, Arcane, Void)
- Elite special abilities can be simple (just stat variations) initially
- No need for complex spell physics — simple projectiles and collision detection
Currencies
{
"gameName": "Spellforge Arena",
"currencies": {
"mana": {
"displayName": "Mana",
"icon": "💧",
"startingAmount": 20,
"cap": 100,
"capPerUpgrade": 20,
"decimals": 0,
"suffixes": [],
"persistsOnPrestige": false,
"regenRate": 2,
"regenPerMastery": 1
},
"ironShards": {
"displayName": "Iron Shards",
"icon": "⚙️",
"startingAmount": 0,
"cap": null,
"decimals": 0,
"suffixes": [
"K",
"M",
"B"
],
"persistsOnPrestige": false
},
"crystalEssence": {
"displayName": "Crystal Essence",
"icon": "💎",
"startingAmount": 0,
"cap": null,
"decimals": 0,
"suffixes": [
"K",
"M"
],
"persistsOnPrestige": true
},
"elementalCharge": {
"displayName": "Elemental Charge",
"icon": "⚡",
"startingAmount": 0,
"cap": 100,
"decimals": 0,
"suffixes": [],
"persistsOnPrestige": false,
"decayRate": 1,
"decayInterval": 3000
}
},
"currencySources": {
"wispKillMana": {
"currency": "mana",
"baseReward": 3,
"formula": "baseReward",
"collectible": "manaFragment",
"chance": 1
},
"guardianKillMana": {
"currency": "mana",
"baseReward": 2,
"formula": "baseReward",
"collectible": "manaFragment",
"chance": 1
},
"guardianKillIron": {
"currency": "ironShards",
"baseReward": 5,
"formula": "baseReward * (1 + 0.05 * waveNumber)",
"collectible": "ironShard",
"chance": 1
},
"golemKillIron": {
"currency": "ironShards",
"baseReward": 15,
"formula": "baseReward * (1 + 0.05 * waveNumber)",
"collectible": "ironShard",
"chance": 1
},
"golemKillEssence": {
"currency": "crystalEssence",
"baseReward": 1,
"formula": "baseReward",
"collectible": "crystalEssence",
"chance": 0.3
},
"championKillIron": {
"currency": "ironShards",
"baseReward": 20,
"formula": "baseReward * (1 + 0.05 * waveNumber)",
"collectible": "ironShard",
"chance": 1
},
"championKillEssence": {
"currency": "crystalEssence",
"baseReward": 3,
"formula": "baseReward + Math.floor(waveNumber / 5)",
"collectible": "crystalEssence",
"chance": 0.6
},
"waveCompleteMana": {
"currency": "mana",
"baseReward": 10,
"formula": "baseReward * waveNumber",
"direct": true
},
"elementSpriteCharge": {
"currency": "elementalCharge",
"baseReward": 2,
"formula": "baseReward",
"collectible": "elementSprite",
"chance": 1
},
"elementSpriteMana": {
"currency": "mana",
"baseReward": 1,
"formula": "baseReward",
"collectible": "elementSprite",
"chance": 1
},
"elementSpriteIron": {
"currency": "ironShards",
"baseReward": 1,
"formula": "baseReward",
"collectible": "elementSprite",
"chance": 0.2
},
"sameElementSpellChain": {
"currency": "elementalCharge",
"baseReward": 10,
"formula": "baseReward * (1 + 0.1 * chainLength)",
"direct": true,
"condition": "lastSpellElement === currentSpellElement"
},
"enemyWeaknessKill": {
"currency": "elementalCharge",
"baseReward": 5,
"formula": "baseReward",
"direct": true,
"condition": "spellElement === enemy.weakness"
},
"prestigeEssence": {
"currency": "crystalEssence",
"formula": "maxWaveReached * 0.5 + eliteChampionsKilled * 2",
"direct": true
}
},
"collectibles": {
"manaFragment": {
"sprite": "spark",
"color": "#00ffff",
"scale": 1.2,
"lifetime": 8,
"collectRadius": 20,
"bobAmplitude": 2,
"bobSpeed": 2,
"spawnAnimation": "popIn",
"spawnAnimationDuration": 200,
"collectAnimation": "burst",
"particleCount": 5,
"particleColor": "#00ffff",
"value": 3,
"sound": "mana_collect",
"magnetRadius": 100,
"magnetStrength": 200,
"showsValue": true
},
"ironShard": {
"sprite": "spark",
"color": "#808080",
"scale": 1.5,
"lifetime": 10,
"collectRadius": 22,
"bobAmplitude": 2,
"bobSpeed": 1.5,
"spawnAnimation": "popIn",
"spawnAnimationDuration": 250,
"collectAnimation": "burst",
"particleCount": 7,
"particleColor": "#a0a0a0",
"value": 5,
"sound": "iron_collect",
"magnetRadius": 120,
"magnetStrength": 180,
"showsValue": true,
"metallic": true
},
"ironShardLarge": {
"extends": "ironShard",
"scale": 2,
"value": 15,
"particleCount": 10
},
"crystalEssence": {
"sprite": "spark",
"color": "#9932cc",
"scale": 2,
"lifetime": 15,
"collectRadius": 25,
"bobAmplitude": 3,
"bobSpeed": 1,
"spawnAnimation": "popIn",
"spawnAnimationDuration": 400,
"collectAnimation": "burst",
"particleCount": 15,
"particleColor": "#b34dff",
"value": 1,
"sound": "essence_collect",
"magnetRadius": 150,
"magnetStrength": 250,
"showsValue": true,
"glow": true,
"neverDespawnOnScreen": true,
"celebrate": true
},
"elementSprite": {
"sprite": "spark",
"color": "varies",
"scale": 1,
"lifetime": 6,
"collectRadius": 18,
"bobAmplitude": 2,
"bobSpeed": 2.5,
"spawnAnimation": "fadeIn",
"spawnAnimationDuration": 300,
"collectAnimation": "burst",
"particleCount": 3,
"particleColor": "varies",
"value": 2,
"sound": "element_collect",
"magnetRadius": 80,
"magnetStrength": 150,
"showsValue": true,
"orbit": true,
"orbitRadius": 60,
"orbitSpeed": 1
}
},
"upgradeCosts": {
"runeSlot4": {
"baseCost": 100,
"costMultiplier": 1,
"currency": "ironShards",
"requires": "waveNumber >= 5"
},
"runeSlot5": {
"baseCost": 500,
"costMultiplier": 1,
"currency": "ironShards",
"requires": "waveNumber >= 10 && runeSlot4"
},
"fireMastery": {
"baseCost": 30,
"costMultiplier": 1.3,
"currency": "ironShards",
"maxLevel": 10,
"effect": {
"type": "damage",
"element": "fire",
"perLevel": 0.1
}
},
"iceMastery": {
"baseCost": 30,
"costMultiplier": 1.3,
"currency": "ironShards",
"maxLevel": 10,
"effect": {
"type": "damage",
"element": "ice",
"perLevel": 0.1
}
},
"arcaneMastery": {
"baseCost": 30,
"costMultiplier": 1.3,
"currency": "ironShards",
"maxLevel": 10,
"effect": {
"type": "damage",
"element": "arcane",
"perLevel": 0.1
}
},
"voidMastery": {
"baseCost": 30,
"costMultiplier": 1.3,
"currency": "ironShards",
"maxLevel": 10,
"effect": {
"type": "damage",
"element": "void",
"perLevel": 0.1
}
},
"spellDamage": {
"baseCost": 50,
"costMultiplier": 1.2,
"currency": "ironShards",
"maxLevel": 15,
"effect": {
"type": "damage",
"element": "all",
"perLevel": 0.05
}
},
"manaMastery": {
"baseCost": 30,
"costMultiplier": 1.3,
"currency": "mana",
"maxLevel": 5,
"effect": {
"type": "regen",
"perLevel": 1
}
},
"unlockElement": {
"baseCost": 25,
"costMultiplier": 1,
"currency": "mana",
"oneTime": true,
"requires": "!elementUnlocked"
},
"permanentRuneSlot": {
"baseCost": 50,
"costMultiplier": 1.5,
"currency": "crystalEssence",
"maxPurchases": 2,
"effect": {
"type": "permanent",
"property": "startingRuneSlots",
"value": 1
},
"persists": true
},
"elementalAttunement": {
"baseCost": 100,
"costMultiplier": 1.3,
"currency": "crystalEssence",
"maxLevel": 5,
"effect": {
"type": "damage",
"element": "chosen",
"perLevel": 0.1
},
"persists": true
},
"manaCapUpgrade": {
"baseCost": 75,
"costMultiplier": 1.3,
"currency": "crystalEssence",
"maxPurchases": 5,
"effect": {
"type": "cap",
"currency": "mana",
"value": 20
},
"persists": true
},
"startingManaUpgrade": {
"baseCost": 60,
"costMultiplier": 1.4,
"currency": "crystalEssence",
"maxPurchases": 3,
"effect": {
"type": "startingAmount",
"currency": "mana",
"value": 10
},
"persists": true
}
},
"spellCosts": {
"base": 3,
"perSlot": 1,
"formula": "baseCost + (runeSlots - 3) * perSlot",
"elementCosts": {
"fire": 1,
"ice": 1,
"arcane": 1.2,
"void": 1.5
}
},
"conversions": {
"ironToEssence": {
"input": {
"currency": "ironShards",
"amount": 100
},
"output": {
"currency": "crystalEssence",
"amount": 1
},
"unlockCondition": "true",
"cooldown": 0,
"oneWay": true
}
},
"balanceTargets": {
"firstSpellCost": 3,
"firstUpgradeAffordable": 10,
"firstRuneSlotUnlockTime": 240,
"firstEssenceDropTime": 300,
"prestigeTime": 1500
},
"formulas": {
"spellCost": "baseCost + (runeSlots - 3) * perSlot",
"upgradeCost": "baseCost * costMultiplier^level",
"enemyKillReward": "baseReward * (1 + 0.05 * waveNumber)",
"waveCompleteBonus": "10 * waveNumber",
"manaRegen": "2 + manaMasteryLevel",
"manaCap": "100 + (manaCapLevel * 20)",
"elementalChargeFromChain": "10 * (1 + 0.1 * chainLength)",
"elementalChargeDecay": "-1 per 3 seconds",
"conversion": "floor(ironShards / 100) → essence",
"prestigeEssenceBonus": "maxWave * 0.5 + eliteKills * 2",
"masteryCost": "30 * 1.3^level",
"spellDamageCost": "50 * 1.2^level"
}
}
Currencies
# Spellforge Arena - Currency System Design
## Economy Flow Diagram
```mermaid
flowchart LR
subgraph "Sources (Gameplay Actions)"
WispKill["Wisp Killed\n+3 Mana fragments"]
GuardianKill["Guardian Killed\n+5 Iron Shards"]
GolemKill["Golem Killed\n+15 Iron Shards\n+1 Crystal Essence"]
ChampionKill["Elite Champion Killed\n+20 Iron Shards\n+3 Crystal Essence"]
ElementSprite["Element Sprite Clicked\n+2 Elemental Charge\n+1 Mana fragment"]
WaveComplete["Wave Complete\n+10 Mana per wave"]
end
subgraph "Sources (Passive)"
ManaRegen["Mana Regen\nbaseRegen * masteryLevel/sec"]
ChargeDecay["Elemental Charge Decay\n-1 per 3 seconds"]
end
subgraph "Currencies"
Mana((Mana 💧\nPrimary\nstart: 20\nmax: 100 + upgrades))
IronShards((Iron Shards ⚙️\nSecondary\nstart: 0\nuncapped))
CrystalEssence((Crystal Essence 💎\nPrestige\nstart: 0\npersists))
ElementalCharge((Elemental Charge ⚡\nTemporary\nstart: 0\nmax: 100))
end
subgraph "Sinks"
CastSpell["Cast Spell\ncost: runeSlots * elementCost"]
UnlockRuneSlot["Unlock Rune Slot\ncost: base * 1.5^slotIndex"]
ElementalMastery["Elemental Mastery\ncost: base * 1.3^level"]
SpellDamage["Spell Damage\ncost: base * 1.2^level"]
ConvertToEssence["Convert to Essence\n100 Iron → 1 Essence"]
Overdrive["Overdrive Mode\ncost: 50 Charge"]
end
WispKill --> Mana
GuardianKill --> IronShards
GolemKill --> IronShards
GolemKill --> CrystalEssence
ChampionKill --> IronShards
ChampionKill --> CrystalEssence
ElementSprite --> ElementalCharge
ElementSprite --> Mana
WaveComplete --> Mana
ManaRegen --> Mana
ChargeDecay --> ElementalCharge
Mana --> CastSpell
IronShards --> UnlockRuneSlot
IronShards --> ElementalMastery
IronShards --> SpellDamage
IronShards --> ConvertToEssence
ConvertToEssence --> CrystalEssence
CrystalEssence -->|Permanent|RuneSlot
CrystalEssence -->|Permanent|ElementalBonus
ElementalCharge --> Overdrive
Overdrive -->|3x damage|CastSpell
```
## Currency Definitions Diagram
```mermaid
graph TD
subgraph "Mana 💧 [Primary Currency]"
M_display["Display: 💧 Mana\n0 decimals\nMax: 100 + 20 per mastery"]
M_earn["Earning:\nWisp kill: +3 Mana fragment (collect)\nGuardian kill: +2 Mana fragment\nWave complete: +10 * waveNumber\nPassive regen: 2 + masteryLevel/sec\nElement sprite click: +1 Mana"]
M_spend["Spending:\nCast spell: runeSlotCount * elementCost\nUnlock element: 25 Mana once\nMana mastery: cost * 1.3^level"]
M_inflation["Inflation Control:\nHard cap at 100 + upgrades\nSpell costs scale with rune slots\nPrestige reset to base"]
end
subgraph "Iron Shards ⚙️ [Secondary Currency]"
I_display["Display: ⚙️ Iron\n0 decimals\nUncapped\nSuffixes: K/M"]
I_earn["Earning:\nGuardian kill: +5 Iron shard (collect)\nGolem kill: +15 Iron shards\nElite Champion: +20 Iron shards\nElement sprite: +1 Iron (20% chance)"]
I_spend["Spending:\nUnlock rune slot 4: 100 Iron\nUnlock rune slot 5: 500 Iron\nElemental mastery: 30 * 1.3^level\nSpell damage: 50 * 1.2^level"]
I_inflation["Inflation Control:\nExponential upgrade costs\n100:1 conversion to Essence drains surplus\nPrestige reset to 0"]
end
subgraph "Crystal Essence 💎 [Prestige Currency]"
C_display["Display: 💎 Essence\n0 decimals\nPersists across prestiges\nSuffixes: K/M"]
C_earn["Earning:\nGolem boss kill: +1 Essence\nElite Champion kill: +3 Essence\nPrestige bonus: waveNumber * 0.5\nConversion: 100 Iron → 1 Essence"]
C_spend["Spending:\n+1 Permanent rune slot: 50 Essence\nElemental attunement +10%: 100 Essence\nMana cap +20: 75 Essence\nStarting mana +10: 60 Essence"]
C_inflation["Inflation Control:\nExpensive permanent upgrades\nCosts scale with purchase count\nNo cap - accumulation is slow"]
end
subgraph "Elemental Charge ⚡ [Temporary Boost Currency]"
E_display["Display: ⚡ Charge\n0-100 scale\nDecays: -1 per 3 sec\nResets on prestige"]
E_earn["Earning:\nSame-element spell chain: +10 per spell\nElement sprite click: +2 Charge\nEnemy weakness kill: +5 Charge\nOverdrive cooldown reset: +30 when full"]
E_spend["Spending:\nOverdrive activation: 50 Charge\nEffect: next spell 3x damage + visual aura"]
E_inflation["Inflation Control:\nHard cap at 100\nDecay prevents hoarding\nResets each combat session"]
end
```
## Conversion Rate Diagram
```mermaid
flowchart LR
IronShards["Iron Shards\n(100)"] -->|"100:1 ratio\nAlways available\nNo cooldown\nClick to convert"| CrystalEssence["Crystal Essence\n(1)"]
Mana["Mana\n(50)"] & IronShards2["Iron Shards\n(25)"] -->|"Combined Spell Crafting\nHigh-tier spells\nUnlocked at: Wave 5"| HighTierSpell["Premium Spell\n(Unlocks new combo)"]
```
## Pacing Timeline
```mermaid
gantt
title Currency Pacing (First 30 Minutes)
dateFormat mm:ss
axisFormat %M:%S
section Mana Milestones
First spell affordable (5 Mana) :milestone, 00:05, 0
First element unlock (25 Mana) :milestone, 00:45, 0
Mana mastery purchased (30 Mana) :milestone, 02:00, 0
Mid-game mana flow (1000+ total) :milestone, 12:00, 0
section Iron Shard Milestones
First shard earned :milestone, 00:30, 0
Rune slot 4 unlock (100 Iron) :milestone, 04:00, 0
First mastery purchased (30 Iron) :milestone, 03:00, 0
Rune slot 5 unlock (500 Iron) :milestone, 15:00, 0
section Crystal Essence Milestones
First Essence drop (Golem) :milestone, 05:00, 0
First permanent upgrade (50 Essence) :milestone, 20:00, 0
Prestige available (25+ Essence) :milestone, 25:00, 0
section Key Moments
First spell cast :milestone, 00:05, 0
Element sprite discovery :milestone, 01:00, 0
Overdrive first use :milestone, 02:30, 0
Elite Champion appears :milestone, 03:00, 0
First Golem boss :milestone, 05:00, 0
Prestige recommended :milestone, 25:00, 0
```
## Upgrade Cost Curves
```mermaid
graph LR
subgraph "Rune Slot 4 Unlock"
RS4_1["Base\nCost: 100 Iron\nUnlocks: 4th rune slot"]
RS4_2["After 1 prestige\nCost: 80 Iron\nPermanent bonus: -20%"]
end
subgraph "Rune Slot 5 Unlock"
RS5_1["Base\nCost: 500 Iron\nUnlocks: 5th rune slot\nRequires: Wave 10"]
RS5_2["After 1 prestige\nCost: 400 Iron\nPermanent bonus: -20%"]
end
subgraph "Fire Mastery Leveling"
FM1["Level 1\nCost: 30 Iron\nFire damage: +10%"]
FM2["Level 5\nCost: 86 Iron\nFire damage: +50%"]
FM3["Level 10\nCost: 392 Iron\nFire damage: +100%"]
FM1 --> FM2 --> FM3
end
subgraph "Spell Damage Upgrade"
SD1["Level 1\nCost: 50 Iron\nAll spell damage: +5%"]
SD2["Level 5\nCost: 124 Iron\nAll spell damage: +25%"]
SD3["Level 10\nCost: 307 Iron\nAll spell damage: +50%"]
SD1 --> SD2 --> SD3
end
subgraph "Mana Cap Upgrade"
MC1["Level 1\nCost: 75 Essence\nMana max: +20"]
MC2["Level 3\nCost: 171 Essence\nMana max: +60"]
MC1 --> MC2
end
```
## Collectible Entity State Machines
### Mana Fragment
```mermaid
stateDiagram-v2
[*] --> Spawning: Wisp/Guardian dies
Spawning --> Active: spawn animation (200ms)\nscale 0 → 1
Active --> Collected: player clicks within 20px
Active --> Expired: lifetime 8s exceeded
Active --> Attracting: magnetism unlocked\nwithin 100px of player
Attracting --> Collected: reaches player position
Attracting --> Expired: lifetime exceeded
Collected --> [*]: add Mana + floating "+3"\nparticle burst (blue)
Expired --> [*]: fade out (300ms)
note right of Active
Visual: cyan bob animation, glow
Clickable radius: 20px
Shows "+3" on hover
Drifts slowly downward
Sprite: spark (cyan, scale 1.2)
end note
note right of Collected
Triggers: currency.add('mana', 3)
Triggers: floatingText("+3💧", cyan)
Triggers: particle burst (5 blue sparks)
Triggers: ui.update('mana')
Sound: soft magical chime
end note
```
### Iron Shard
```mermaid
stateDiagram-v2
[*] --> Spawning: Guardian/Golem/Champion dies
Spawning --> Active: spawn animation (250ms)\nwith metallic flash
Active --> Collected: player clicks within 22px
Active --> Expired: lifetime 10s exceeded
Active --> Attracting: magnetism unlocked\nwithin 120px
Attracting --> Collected: reaches player
Attracting --> Expired: lifetime exceeded
Collected --> [*]: add Iron Shards + value\nfloating text
Expired --> [*]: fade out (400ms)
note right of Active
Visual: gray crystal bob, metallic sheen
Clickable radius: 22px
Shows value on hover (5/15/20)
Sprite: spark (gray, scale 1.5)
Golem/Champion shards: larger (scale 2.0)
end note
note right of Collected
Triggers: currency.add('ironShards', value)
Triggers: floatingText("+{value}⚙️", silver)
Triggers: particle burst (7 gray crystals)
Triggers: ui.update('ironShards')
Sound: metallic clink
end note
```
### Crystal Essence
```mermaid
stateDiagram-v2
[*] --> Spawning: Golem/Champion dies (rare)
Spawning --> Active: spawn animation (400ms)\nwith purple flash
Active --> Collected: player clicks within 25px
Active --> Expired: lifetime 15s exceeded
Active --> Attracting: magnetism always active\nwithin 150px
Attracting --> Collected: reaches player
Attracting --> Expired: lifetime exceeded
Collected --> [*]: add Crystal Essence + value\nmajor visual celebration
Expired --> [*]: fade out (500ms)\nwith sad sound
note right of Active
Visual: purple glow, pulsing aura
Clickable radius: 25px (generous)
Shows "💎" on hover
Sprite: spark (purple, scale 2.0)
Has trail particle effect
NEVER auto-despawns if on screen
end note
note right of Collected
Triggers: currency.add('crystalEssence', value)
Triggers: floatingText("+{value}💎", purple, large)
Triggers: screen flash (purple tint)
Triggers: particle burst (15 purple crystals)
Triggers: ui.update('crystalEssence')
Triggers: ui.celebrate()
Sound: magical chime + echo
end note
```
### Element Sprite (Elemental Charge Source)
```mermaid
stateDiagram-v2
[*] --> Spawning: periodic spawn\nevery 8-12 seconds
Spawning --> Active: fade in (300ms)
Active --> Collected: player clicks within 18px\nor hovers
Active --> Expired: lifetime 6s exceeded
Collected --> [*]: add Elemental Charge + bonuses
Expired --> [*]: fade out (200ms)
note right of Active
Visual: colored spark, matches element
Clickable radius: 18px
Element colors: Fire(orange), Ice(cyan), Arcane(purple), Void(black)
Orbits near rune circle
Sprite: spark (element color, scale 1.0)
end note
note right of Collected
Triggers: currency.add('elementalCharge', 2)
Triggers: currency.add('mana', 1)
Triggers: floatingText("+2⚡", element color)
Triggers: particle burst (3 colored sparks)
Triggers: ui.update('elementalCharge')
Sound: elemental fizz
end note
```
## Currency Event Flow
### Enemy Kill → Resource Drop → Collection
```mermaid
sequenceDiagram
participant Canvas as Canvas (Game World)
participant Entity as Entity System
participant EventBus as EventBus
participant Currency as CurrencyManager
participant Collectible as Collectible System
participant UI as HUD Display
Canvas->>Entity: enemy.takeDamage(spellDamage)
Entity->>Entity: hp -= spellDamage
Entity->>Entity: hp <= 0 → die()
Entity->>Canvas: death animation + particles
Entity->>EventBus: emit('enemy-killed', {type, position, wave})
EventBus->>Collectible: spawnCollectible({type, position, value})
Note over Collectible: Determine drop type based on enemy:\n- Wisp: Mana fragment (+3)\n- Guardian: Iron shard (+5) + Mana (+2)\n- Golem: Iron shards (+15) + Essence (30%)\n- Champion: Iron shards (+20) + Essence (60%)
Collectible->>Canvas: create sprite at position
Canvas->>Canvas: spawn animation (200-400ms)\nstart bob animation
Note over Canvas: Player clicks collectible\nwithin radius
Canvas->>EventBus: emit('collectible-collected', {type, value, position})
EventBus->>Currency: currency.add(type, value)
Currency->>Currency: validate + add to balance
Currency->>EventBus: emit('currency-changed', {type, newBalance})
EventBus->>UI: floatingText("+{value}{icon}", position, color)
EventBus->>UI: updateDisplay(type)
EventBus->>Canvas: particle burst(position, count, color)
EventBus->>Canvas: playSound('collect')
UI->>UI: count-up animation (200ms)\nupdate displayed value
```
### Spell Cast → Mana Spend → Effect
```mermaid
sequenceDiagram
participant Player as Player Input
participant Rune as Rune System
participant Currency as CurrencyManager
participant Spell as Spell System
participant Canvas as Canvas
participant EventBus as EventBus
participant UI as HUD Display
Player->>Rune: click CAST (or press SPACE)
Rune->>Rune: validate spell composition
Rune->>Currency: currency.canAfford('mana', spellCost)
alt Can Afford
Currency->>Currency: return true
Currency->>EventBus: emit('spell-cast-start', {slots, elements})
EventBus->>Currency: currency.add('mana', -spellCost)
EventBus->>UI: floatingText("-{cost}💧", runeCircle, cyan)
EventBus->>UI: updateDisplay('mana')
EventBus->>Spell: castSpell({elements, target})
Note over Spell: Build spell properties:\n- Damage: base * elements * mastery\n- Type: combination of elements\n- Speed: based on element weights
Spell->>Canvas: spawnProjectile({x, y, type, damage, target})
Canvas->>Canvas: animate projectile flight
Note over Canvas: Projectile hits target:
Canvas->>EventBus: emit('projectile-hit', {target, damage, elements})
EventBus->>Spell: applyDamage({target, damage, elements})
alt Enemy killed
Spell->>EventBus: emit('enemy-killed', {...})
EventBus->>Collectible: spawnCollectible(...)
end
else Cannot Afford
Currency->>Currency: return false
Currency->>EventBus: emit('spell-cast-failed', 'insufficient-mana')
EventBus->>UI: showError("Not enough mana!")
EventBus->>Canvas: shake rune circle
end
```
### Passive Mana Regeneration
```mermaid
sequenceDiagram
participant GameLoop as Game Loop
participant Currency as CurrencyManager
participant UI as HUD Display
participant EventBus as EventBus
Note over GameLoop: Every 1 second
GameLoop->>Currency: currency.regen('mana')
Currency->>Currency: calculate regen amount\nbaseRegen (2) + manaMasteryLevel
Currency->>Currency: currentMana < maxMana ? add regen : 0
Currency->>EventBus: emit('mana-regened', {amount, newBalance})
EventBus->>UI: updateDisplay('mana')
Note over UI: No floating text for passive regen\n(subtle update only)
```
### Overdrive Activation
```mermaid
sequenceDiagram
participant Player as Player Input
participant Charge as Charge System
participant Currency as CurrencyManager
participant Spell as Spell System
participant UI as HUD Display
participant Canvas as Canvas
Player->>Charge: click Overdrive Bar\n(charge >= 50)
Charge->>Currency: currency.canAfford('elementalCharge', 50)
alt Can Afford
Currency->>Currency: add('elementalCharge', -50)
Currency->>Charge: emit('overdrive-activated')
Charge->>Spell: setNextSpellMultiplier(3.0)
Charge->>Canvas: showOverdriveAura()
Charge->>UI: flashOverdriveIndicator()
Note over Canvas: Next spell cast within 5s\ngets 3x damage + visual aura
Canvas->>Canvas: rune circle glows with elemental color
UI->>UI: show "OVERDRIVE ACTIVE" text
Note over Charge,Spell: Spell cast happens normally\nbut damage *= 3
Spell->>Charge: emit('overdrive-consumed')
Charge->>Canvas: hideOverdriveAura()
Charge->>UI: hideOverdriveIndicator()
else Cannot Afford
Charge->>UI: showError("Need 50 Charge!")
Charge->>Canvas: shake charge bar
end
```
### Iron to Essence Conversion
```mermaid
sequenceDiagram
participant Player as Player Input
participant UI as Upgrade Panel
participant Currency as CurrencyManager
participant EventBus as EventBus
participant HUD as HUD Display
Player->>UI: click "Convert to Essence" button\n(Iron >= 100)
UI->>Currency: currency.canAfford('ironShards', 100)
alt Can Afford
Currency->>Currency: add('ironShards', -100)
Currency->>Currency: add('crystalEssence', 1)
Currency->>EventBus: emit('conversion-complete', {from: 'ironShards', to: 'crystalEssence', amount: 1})
EventBus->>UI: floatingText("-100⚙️", button, silver)
EventBus->>UI: floatingText("+1💎", button, purple)
EventBus->>HUD: updateDisplay('ironShards')
EventBus->>HUD: updateDisplay('crystalEssence')
Note over HUD: Crystal essence increase gets\ncelebration effect (flash + sound)
else Cannot Afford
UI->>UI: showButtonDisabled("Need 100 Iron Shards")
end
```
## CONFIG Spec: currencies Section
```javascript
// Currency System Configuration for Spellforge Arena
// This file defines all currencies, sources, sinks, and conversions
CONFIG.currencies = {
mana: {
displayName: 'Mana',
icon: '💧',
startingAmount: 20,
cap: 100, // Base cap, increases with upgrades
capPerUpgrade: 20, // +20 per Mana Cap upgrade
decimals: 0,
suffixes: [], // Mana doesn't use suffixes - capped values
persistsOnPrestige: false,
regenRate: 2, // Base mana regen per second
regenPerMastery: 1, // +1 per Mana Mastery level
},
ironShards: {
displayName: 'Iron Shards',
icon: '⚙️',
startingAmount: 0,
cap: null, // Uncapped
decimals: 0,
suffixes: ['K', 'M', 'B'],
persistsOnPrestige: false,
},
crystalEssence: {
displayName: 'Crystal Essence',
icon: '💎',
startingAmount: 0,
cap: null, // Uncapped
decimals: 0,
suffixes: ['K', 'M'],
persistsOnPrestige: true,
},
elementalCharge: {
displayName: 'Elemental Charge',
icon: '⚡',
startingAmount: 0,
cap: 100, // Hard cap
decimals: 0,
suffixes: [],
persistsOnPrestige: false,
decayRate: 1, // -1 per 3 seconds
decayInterval: 3000, // Decay every 3 seconds (ms)
},
};
CONFIG.currencySources = {
// Primary Gameplay Sources
wispKillMana: {
currency: 'mana',
baseReward: 3,
formula: 'baseReward', // Flat reward per Wisp kill
collectible: 'manaFragment',
chance: 1.0, // 100% drop chance
},
guardianKillMana: {
currency: 'mana',
baseReward: 2,
formula: 'baseReward',
collectible: 'manaFragment',
chance: 1.0,
},
guardianKillIron: {
currency: 'ironShards',
baseReward: 5,
formula: 'baseReward * (1 + 0.05 * waveNumber)',
collectible: 'ironShard',
chance: 1.0,
},
golemKillIron: {
currency: 'ironShards',
baseReward: 15,
formula: 'baseReward * (1 + 0.05 * waveNumber)',
collectible: 'ironShard',
chance: 1.0,
},
golemKillEssence: {
currency: 'crystalEssence',
baseReward: 1,
formula: 'baseReward',
collectible: 'crystalEssence',
chance: 0.3, // 30% chance
},
championKillIron: {
currency: 'ironShards',
baseReward: 20,
formula: 'baseReward * (1 + 0.05 * waveNumber)',
collectible: 'ironShard',
chance: 1.0,
},
championKillEssence: {
currency: 'crystalEssence',
baseReward: 3,
formula: 'baseReward + Math.floor(waveNumber / 5)',
collectible: 'crystalEssence',
chance: 0.6, // 60% chance
},
// Wave Completion Bonus
waveCompleteMana: {
currency: 'mana',
baseReward: 10,
formula: 'baseReward * waveNumber',
direct: true, // Added directly, not a collectible
},
// Element Sprite Collection
elementSpriteCharge: {
currency: 'elementalCharge',
baseReward: 2,
formula: 'baseReward',
collectible: 'elementSprite',
chance: 1.0,
},
elementSpriteMana: {
currency: 'mana',
baseReward: 1,
formula: 'baseReward',
collectible: 'elementSprite',
chance: 1.0,
},
elementSpriteIron: {
currency: 'ironShards',
baseReward: 1,
formula: 'baseReward',
collectible: 'elementSprite',
chance: 0.2, // 20% chance
},
// Same-Element Spell Chain (Charge Building)
sameElementSpellChain: {
currency: 'elementalCharge',
baseReward: 10,
formula: 'baseReward * (1 + 0.1 * chainLength)', // More for longer chains
direct: true,
condition: 'lastSpellElement === currentSpellElement',
},
enemyWeaknessKill: {
currency: 'elementalCharge',
baseReward: 5,
formula: 'baseReward',
direct: true,
condition: 'spellElement === enemy.weakness',
},
// Prestige Bonus
prestigeEssence: {
currency: 'crystalEssence',
formula: 'maxWaveReached * 0.5 + eliteChampionsKilled * 2',
direct: true,
},
};
CONFIG.collectibles = {
manaFragment: {
sprite: 'spark',
color: '#00ffff', // Cyan
scale: 1.2,
lifetime: 8, // seconds before expiry
collectRadius: 20, // pixels
bobAmplitude: 2, // pixels
bobSpeed: 2, // Hz
spawnAnimation: 'popIn',
spawnAnimationDuration: 200, // ms
collectAnimation: 'burst',
particleCount: 5,
particleColor: '#00ffff',
value: 3,
sound: 'mana_collect',
magnetRadius: 100, // px - starts attracting when close
magnetStrength: 200, // px/sec - move speed toward player
showsValue: true,
},
ironShard: {
sprite: 'spark',
color: '#808080', // Gray
scale: 1.5,
lifetime: 10,
collectRadius: 22,
bobAmplitude: 2,
bobSpeed: 1.5,
spawnAnimation: 'popIn',
spawnAnimationDuration: 250,
collectAnimation: 'burst',
particleCount: 7,
particleColor: '#a0a0a0',
value: 5, // Base value, varies by enemy
sound: 'iron_collect',
magnetRadius: 120,
magnetStrength: 180,
showsValue: true,
metallic: true, // Special property for visual effect
},
ironShardLarge: {
extends: 'ironShard',
scale: 2.0,
value: 15, // Golem/Champion value
particleCount: 10,
},
crystalEssence: {
sprite: 'spark',
color: '#9932cc', // Purple
scale: 2.0,
lifetime: 15,
collectRadius: 25, // Generous
bobAmplitude: 3,
bobSpeed: 1,
spawnAnimation: 'popIn',
spawnAnimationDuration: 400,
collectAnimation: 'burst',
particleCount: 15,
particleColor: '#b34dff',
value: 1,
sound: 'essence_collect',
magnetRadius: 150,
magnetStrength: 250,
showsValue: true,
glow: true, // Special glow effect
neverDespawnOnScreen: true, // Stay until collected if visible
celebrate: true, // Triggers celebration effect
},
elementSprite: {
sprite: 'spark',
color: 'varies', // Set by element type
scale: 1.0,
lifetime: 6,
collectRadius: 18,
bobAmplitude: 2,
bobSpeed: 2.5,
spawnAnimation: 'fadeIn',
spawnAnimationDuration: 300,
collectAnimation: 'burst',
particleCount: 3,
particleColor: 'varies',
value: 2, // Charge value
sound: 'element_collect',
magnetRadius: 80,
magnetStrength: 150,
showsValue: true,
orbit: true, // Orbits near rune circle
orbitRadius: 60,
orbitSpeed: 1,
},
};
CONFIG.upgradeCosts = {
// Rune Slot Unlocks
runeSlot4: {
baseCost: 100,
costMultiplier: 1.0, // Flat cost, not repeatable
currency: 'ironShards',
requires: 'waveNumber >= 5',
},
runeSlot5: {
baseCost: 500,
costMultiplier: 1.0,
currency: 'ironShards',
requires: 'waveNumber >= 10 && runeSlot4',
},
// Elemental Masteries (one per element)
fireMastery: {
baseCost: 30,
costMultiplier: 1.3,
currency: 'ironShards',
maxLevel: 10,
effect: { type: 'damage', element: 'fire', perLevel: 0.1 }, // +10% fire damage per level
},
iceMastery: {
baseCost: 30,
costMultiplier: 1.3,
currency: 'ironShards',
maxLevel: 10,
effect: { type: 'damage', element: 'ice', perLevel: 0.1 },
},
arcaneMastery: {
baseCost: 30,
costMultiplier: 1.3,
currency: 'ironShards',
maxLevel: 10,
effect: { type: 'damage', element: 'arcane', perLevel: 0.1 },
},
voidMastery: {
baseCost: 30,
costMultiplier: 1.3,
currency: 'ironShards',
maxLevel: 10,
effect: { type: 'damage', element: 'void', perLevel: 0.1 },
},
// Spell Damage (all elements)
spellDamage: {
baseCost: 50,
costMultiplier: 1.2,
currency: 'ironShards',
maxLevel: 15,
effect: { type: 'damage', element: 'all', perLevel: 0.05 }, // +5% all damage per level
},
// Mana System
manaMastery: {
baseCost: 30,
costMultiplier: 1.3,
currency: 'mana',
maxLevel: 5,
effect: { type: 'regen', perLevel: 1 }, // +1 mana/sec per level
},
unlockElement: {
baseCost: 25,
costMultiplier: 1.0,
currency: 'mana',
oneTime: true,
requires: '!elementUnlocked',
},
// Permanent Upgrades (Crystal Essence)
permanentRuneSlot: {
baseCost: 50,
costMultiplier: 1.5, // Each subsequent permanent slot costs more
currency: 'crystalEssence',
maxPurchases: 2, // Can only buy 2 permanent slots
effect: { type: 'permanent', property: 'startingRuneSlots', value: 1 },
persists: true,
},
elementalAttunement: {
baseCost: 100,
costMultiplier: 1.3,
currency: 'crystalEssence',
maxLevel: 5,
effect: { type: 'damage', element: 'chosen', perLevel: 0.1 },
persists: true,
},
manaCapUpgrade: {
baseCost: 75,
costMultiplier: 1.3,
currency: 'crystalEssence',
maxPurchases: 5,
effect: { type: 'cap', currency: 'mana', value: 20 },
persists: true,
},
startingManaUpgrade: {
baseCost: 60,
costMultiplier: 1.4,
currency: 'crystalEssence',
maxPurchases: 3,
effect: { type: 'startingAmount', currency: 'mana', value: 10 },
persists: true,
},
};
CONFIG.spellCosts = {
// Mana cost to cast spells based on rune slot count
base: 3, // Base cost per rune slot
perSlot: 1, // Additional cost per slot
formula: 'baseCost + (runeSlots - 3) * perSlot', // 3 slots = 3 mana, 4 slots = 4 mana, 5 slots = 5 mana
// Element-specific cost modifiers
elementCosts: {
fire: 1.0, // Normal cost
ice: 1.0,
arcane: 1.2, // +20% cost (more powerful)
void: 1.5, // +50% cost (most powerful)
},
};
CONFIG.conversions = {
ironToEssence: {
input: { currency: 'ironShards', amount: 100 },
output: { currency: 'crystalEssence', amount: 1 },
unlockCondition: 'true', // Always available
cooldown: 0, // No cooldown
oneWay: true,
},
};
CONFIG.balanceTargets = {
firstSpellCost: 3, // 15% of starting mana
firstUpgradeAffordable: 10, // seconds
firstRuneSlotUnlockTime: 240, // seconds (4 minutes)
firstEssenceDropTime: 300, // seconds (5 minutes - first Golem)
prestigeTime: 1500, // seconds (25 minutes)
};
```
## Exact Formulas
```
Spell cost: baseCost (3) + (runeSlots - 3) * perSlot (1)
Total spell cost: baseCost * elementMultiplier
Upgrade cost: baseCost * costMultiplier^level
Enemy kill reward: baseReward * (1 + 0.05 * waveNumber)
Golem essence chance: 30% fixed
Champion essence chance: 60% fixed
Wave complete bonus: 10 * waveNumber Mana
Mana regen: 2 + manaMasteryLevel (per second)
Mana cap: 100 + (manaCapLevel * 20)
Elemental charge from spell chain: 10 * (1 + 0.1 * chainLength)
Elemental charge from weakness kill: 5
Elemental charge decay: -1 per 3 seconds
Conversion: floor(ironShards / 100) → essence
Prestige essence bonus: maxWave * 0.5 + eliteKills * 2
Rune slot unlock costs:
- Slot 4: 100 Iron (flat)
- Slot 5: 500 Iron (flat)
Mastery costs: 30 * 1.3^level
Spell damage upgrade: 50 * 1.2^level
```
## Balance Targets
| Time | Mana | Iron Shards | Crystal Essence | Key Event |
|------|------|-------------|-----------------|-------------|
| 0:00 | 20 | 0 | 0 | Game start |
| 0:05 | ~12 | 0 | 0 | First spell cast (-3 mana) |
| 0:30 | ~25 | 5 | 0 | First Iron Shard (Guardian kill) |
| 1:00 | ~35 | 15 | 0 | Wave 1 complete (+10 mana) |
| 2:00 | ~50 | 45 | 0 | First mastery affordable (30 Iron) |
| 3:00 | ~60 | 80 | 0 | Multiple upgrades purchased |
| 4:00 | ~70 | 120 | 0 | Rune slot 4 affordable (100 Iron) |
| 5:00 | ~80 | 150 | 0-1 | First Golem (Essence possible) |
| 10:00 | ~150 | 400 | 0-2 | Wave 10 - Rune slot 5 visible |
| 15:00 | ~200 | 800 | 2-5 | Mid-game plateau |
| 20:00 | ~250 | 1500 | 5-10 | First permanent upgrade affordable |
| 25:00 | ~300 | 2500 | 10-20 | Prestige recommended |
| 30:00 | ~400 | 4000 | 15-30 | Late game, all unlocks |
## Edge Cases
- **Overflow protection**: Switch to BigNum at 1e6 for Iron Shards and Crystal Essence
- **Negative balance**: `canAfford()` check before all purchases; show error feedback
- **Mana cap at zero**: Spells disabled, show "Not enough mana" error, shake rune circle
- **Elemental charge decay only when above 0**: Don't show negative values
- **Collectible spawning overlaps**: Randomize spawn position slightly to prevent exact overlap
- **Collectible lifetime during pause**: Pause lifetime countdown when game is paused
- **Conversion when can't afford more essence upgrades**: Still allow - player might want to bank for future
- **Prestige with no essence**: Still allow - essence bonus on next run will accumulate
- **Iron shard overflow**: Ensure suffixes (K/M/B) display correctly; use BigNum library
- **Multiple collectibles on screen**: Performance check - limit to 50 active collectibles max
- **Element sprite spawning when none collected**: Continue spawning but limit to 5 active max
## Inflation Control Strategies
1. **Mana**: Hard cap prevents infinite accumulation; spell costs ensure constant drain
2. **Iron Shards**: Exponential upgrade costs (1.2-1.3x) eventually outpace linear income growth; 100:1 conversion drains surplus
3. **Crystal Essence**: Very expensive permanent upgrades (50-100 base with 1.3-1.5x scaling); slow accumulation rate
4. **Elemental Charge**: Hard cap at 100, decay mechanism, resets on prestige - designed to be spent, not hoarded
Progression
{
"gameName": "Spellforge Arena",
"progression": {
"unlocks": {
"firstSpellCast": {
"requirement": "spellsCast >= 1",
"unlocks": "spellSystem",
"toast": "Spell system active!",
"indicator": "rune-glow"
},
"wave1Complete": {
"requirement": "wavesCompleted >= 1",
"unlocks": "waveBonuses",
"toast": "Wave cleared! +10 Mana",
"indicator": "hud-flash"
},
"ironCollected": {
"requirement": "ironShards >= 10",
"unlocks": "upgradesPanel",
"toast": "Upgrades available!",
"indicator": "tab-glow"
},
"wave3Complete": {
"requirement": "wavesCompleted >= 3",
"unlocks": "elementSprites",
"toast": "Element sprites discovered!",
"indicator": "canvas-new-entity"
},
"wave5Complete": {
"requirement": "wavesCompleted >= 5",
"unlocks": "runeSlot4",
"toast": "4th rune slot unlocked!",
"indicator": "rune-slot-appear"
},
"chargeFilled": {
"requirement": "elementalCharge >= 50",
"unlocks": "overdrive",
"toast": "Overdrive ready!",
"indicator": "bar-glow"
},
"championKilled": {
"requirement": "championsKilled >= 1",
"unlocks": "crystalEssence",
"toast": "Crystal Essence discovered!",
"indicator": "hud-reveal"
},
"wave10Complete": {
"requirement": "wavesCompleted >= 10",
"unlocks": "runeSlot5",
"toast": "5th rune slot unlocked!",
"indicator": "rune-slot-appear"
},
"wave12Complete": {
"requirement": "wavesCompleted >= 12",
"unlocks": "prestigeTeaser",
"toast": "Prestige available...",
"indicator": "button-pulse"
}
},
"milestones": [
{
"id": "firstSpark",
"trigger": "spellsCast >= 1",
"reward": {
"mana": 0
},
"toast": "The battle begins!"
},
{
"id": "waveBreaker",
"trigger": "wavesCompleted >= 1",
"reward": {
"mana": 10
},
"toast": "First wave cleared!"
},
{
"id": "runeSmith",
"trigger": "runeSlot4Unlocked",
"reward": {
"ironShards": 0
},
"toast": "Spell complexity doubled!"
},
{
"id": "overcharged",
"trigger": "overdriveUsed >= 1",
"reward": {
"ironShards": 0
},
"toast": "UNLEASHED!"
},
{
"id": "championSlayer",
"trigger": "championsKilled >= 1",
"reward": {
"crystalEssence": 0
},
"toast": "Champion defeated!"
},
{
"id": "masterRunecaster",
"trigger": "runeSlot5Unlocked",
"reward": {
"ironShards": 0
},
"toast": "All combinations unlocked!"
},
{
"id": "elementalAdept",
"trigger": "anyMasteryLevel >= 5",
"reward": {
"crystalEssence": 1
},
"toast": "Mastery deepens!"
},
{
"id": "arenaTranscendence",
"trigger": "prestigesCompleted >= 1",
"reward": {
"crystalEssence": 5
},
"toast": "A new era begins!"
}
],
"earlyGame": {
"firstSpellCost": 3,
"firstUpgradeAffordable": 90,
"manaRegenStart": 2,
"wispManaDrop": 3,
"guardianIronDrop": 5,
"wave1Bonus": 10
}
},
"waves": {
"baseEnemyCount": 2,
"enemyCountGrowth": 1.15,
"spawnInterval": 2000,
"preWaveCountdown": 3,
"postWaveBreather": 5,
"bossWaveInterval": 5,
"waveTimeout": 90
},
"waveComposition": {
"description": "Wave-specific enemy compositions",
"getEnemies": "function(waveNum) { const baseCount = Math.floor(2 * Math.pow(1.15, waveNum - 1)); const enemies = []; if (waveNum <= 4) { const wispCount = Math.ceil(baseCount * 0.6); const guardianCount = Math.floor(baseCount * 0.4); for (let i = 0; i < wispCount; i++) { enemies.push({ type: 'wisp', delay: i * 1500 }); } for (let i = 0; i < guardianCount; i++) { enemies.push({ type: 'guardian', delay: (wispCount + i) * 2000 }); } } else if (waveNum <= 9) { const hasGolem = waveNum === 5; const hasChampion = waveNum >= 7; const wispCount = Math.ceil(baseCount * 0.4); const guardianCount = Math.floor(baseCount * 0.5); let delay = 0; if (hasGolem) { enemies.push({ type: 'golem', delay: 30000 }); delay += 2000; } if (hasChampion) { enemies.push({ type: 'champion', delay: 60000 }); delay += 2000; } for (let i = 0; i < wispCount; i++) { enemies.push({ type: 'wisp', delay: delay + i * 1200 }); } for (let i = 0; i < guardianCount; i++) { enemies.push({ type: 'guardian', delay: delay + (wispCount + i) * 1800 }); } } else { const hasGolem = waveNum % 5 === 0; const hasChampion = true; const wispCount = Math.ceil(baseCount * 0.35); const guardianCount = Math.floor(baseCount * 0.45); let delay = 0; if (hasGolem) { enemies.push({ type: 'golem', delay: 25000 }); delay += 2000; } enemies.push({ type: 'champion', delay: 50000 }); delay += 2000; for (let i = 0; i < wispCount; i++) { enemies.push({ type: 'wisp', delay: delay + i * 1000 }); } for (let i = 0; i < guardianCount; i++) { enemies.push({ type: 'guardian', delay: delay + (wispCount + i) * 1500 }); } } return enemies; }",
"getElementalWeakness": "function(waveNum) { const elements = ['fire', 'ice', 'arcane', 'void']; return elements[waveNum % 4]; }",
"getReward": "function(waveNum) { return { mana: waveNum * 10 }; }"
},
"entityUnlocks": {
"wisp": {
"unlockWave": 1,
"spawnCondition": "always",
"hp": 10,
"speed": 2.5,
"damage": 10,
"reward": {
"mana": 3
}
},
"guardian": {
"unlockWave": 1,
"spawnCondition": "always",
"hp": 30,
"speed": 1,
"damage": 15,
"reward": {
"mana": 2,
"ironShards": 5
},
"weakness": {
"fire": 0.2
},
"resistance": {
"ice": 0.2
}
},
"golem": {
"unlockWave": 5,
"spawnCondition": "waveNum % 5 === 0",
"hp": 150,
"speed": 0.3,
"damage": 25,
"reward": {
"mana": 5,
"ironShards": 15,
"crystalEssence": 0.3
},
"weakness": {
"void": 1
},
"resistance": {
"fire": 0.5,
"ice": 0.5
},
"isBoss": true,
"announce": 30000
},
"champion": {
"unlockWave": 7,
"spawnCondition": "waveNum >= 7",
"hp": 80,
"speed": 1.2,
"damage": 20,
"reward": {
"mana": 5,
"ironShards": 20,
"crystalEssence": 0.6
},
"weakness": "rotating",
"abilities": [
"summonMinions",
"counterspell"
],
"isElite": true,
"announce": 5000
}
},
"prestige": {
"teaserWave": 12,
"minWave": 15,
"recommendedWave": 18,
"formula": "floor(maxWaveReached * 0.5 + eliteChampionsKilled * 2 + golemsKilled * 0.5)",
"resets": {
"currencies": [
"mana",
"ironShards",
"elementalCharge"
],
"upgrades": [
"runeSlot4",
"runeSlot5",
"elementalMasteries",
"spellDamage",
"manaMastery"
],
"wave": 0,
"arenaTier": 0
},
"persists": {
"currencies": [
"crystalEssence"
],
"upgrades": [
"permanentRuneSlot",
"elementalAttunement",
"manaCapUpgrade",
"startingManaUpgrade"
],
"achievements": [
"totalPrestiges",
"maxWaveReached",
"totalKills"
]
},
"visualTiers": [
{
"tier": 0,
"minPrestiges": 0,
"background": "#1a1a2e",
"arenaPattern": "basic-pentagram",
"enemyPalette": null,
"runeCircle": "stone"
},
{
"tier": 1,
"minPrestiges": 1,
"background": "#2e1a3e",
"arenaPattern": "intricate-pentagram",
"enemyPalette": {
"hueShift": 30,
"saturate": 1.2
},
"runeCircle": "glowing-lines"
},
{
"tier": 2,
"minPrestiges": 3,
"background": "#3e1a4e",
"arenaPattern": "multi-layered",
"enemyPalette": {
"hueShift": 60,
"saturate": 1.4
},
"runeCircle": "floating-glyphs"
},
{
"tier": 3,
"minPrestiges": 5,
"background": "#4e1a5e",
"arenaPattern": "sanctum",
"enemyPalette": {
"hueShift": 90,
"saturate": 1.6
},
"runeCircle": "energy-vortex"
}
],
"upgrades": {
"permanentRuneSlot": {
"baseCost": 50,
"costMultiplier": 1.5,
"maxPurchases": 2,
"effect": {
"type": "permanent",
"property": "startingRuneSlots",
"value": 1
},
"description": "+1 permanent rune slot (persists across prestiges)"
},
"elementalAttunement": {
"baseCost": 100,
"costMultiplier": 1.3,
"maxLevel": 5,
"requiresUserChoice": true,
"effect": {
"type": "damage",
"element": "chosen",
"perLevel": 0.1
},
"description": "+10% damage to chosen element (persists)"
},
"manaCapUpgrade": {
"baseCost": 75,
"costMultiplier": 1.3,
"maxPurchases": 5,
"effect": {
"type": "cap",
"currency": "mana",
"value": 20
},
"description": "+20 maximum Mana (persists)"
},
"startingManaUpgrade": {
"baseCost": 60,
"costMultiplier": 1.4,
"maxPurchases": 3,
"effect": {
"type": "startingAmount",
"currency": "mana",
"value": 10
},
"description": "+10 starting Mana (persists)"
},
"chargeMastery": {
"baseCost": 80,
"costMultiplier": 1.3,
"maxPurchases": 3,
"effect": {
"type": "chargeCap",
"value": 20
},
"description": "+20 maximum Elemental Charge (persists)"
},
"overdriveEfficiency": {
"baseCost": 120,
"costMultiplier": 1.5,
"maxPurchases": 2,
"effect": {
"type": "overdriveCost",
"value": -5
},
"description": "Overdrive costs 5 less Charge (persists)"
}
},
"acceleration": {
"firstPrestige": {
"waveSpeed": 1.5,
"incomeMultiplier": 1.3,
"unlockCheaper": 0.8
},
"secondPrestige": {
"waveSpeed": 2,
"incomeMultiplier": 1.5,
"unlockCheaper": 0.7
}
}
},
"upgradeTiers": {
"tier1": {
"fireMastery": {
"baseCost": 30,
"costMultiplier": 1.3,
"currency": "ironShards",
"maxLevel": 10,
"effect": {
"type": "damage",
"element": "fire",
"perLevel": 0.1
},
"description": "+10% Fire damage per level",
"canvasChange": "fire-spell-trail-langer"
},
"iceMastery": {
"baseCost": 30,
"costMultiplier": 1.3,
"currency": "ironShards",
"maxLevel": 10,
"effect": {
"type": "damage",
"element": "ice",
"perLevel": 0.1
},
"description": "+10% Ice damage per level",
"canvasChange": "ice-spell-freeze-langer"
},
"arcaneMastery": {
"baseCost": 30,
"costMultiplier": 1.3,
"currency": "ironShards",
"maxLevel": 10,
"effect": {
"type": "damage",
"element": "arcane",
"perLevel": 0.1
},
"description": "+10% Arcane damage per level",
"canvasChange": "arcane-spell-homing-stronger"
},
"voidMastery": {
"baseCost": 30,
"costMultiplier": 1.3,
"currency": "ironShards",
"maxLevel": 10,
"effect": {
"type": "damage",
"element": "void",
"perLevel": 0.1
},
"description": "+10% Void damage per level",
"canvasChange": "void-spell-penetration"
},
"spellDamage": {
"baseCost": 50,
"costMultiplier": 1.2,
"currency": "ironShards",
"maxLevel": 15,
"effect": {
"type": "damage",
"element": "all",
"perLevel": 0.05
},
"description": "+5% all spell damage per level",
"canvasChange": "all-spells-larger"
},
"manaMastery": {
"baseCost": 30,
"costMultiplier": 1.3,
"currency": "mana",
"maxLevel": 5,
"effect": {
"type": "regen",
"perLevel": 1
},
"description": "+1 mana regen per second",
"canvasChange": "mana-bar-fills-visual"
}
},
"tier2": {
"requires": "waveNumber >= 3",
"choices": [
{
"id": "splashDamage",
"name": "Splash Spells",
"baseCost": 100,
"effect": {
"type": "splash",
"radius": 50,
"damagePercent": 0.5
},
"description": "Spells deal 50% damage in small area",
"canvasChange": "spells-have-explosion-radius",
"conflictsWith": [
"piercingShots"
]
},
{
"id": "piercingShots",
"name": "Piercing Shots",
"baseCost": 100,
"effect": {
"type": "piercing",
"maxTargets": 3
},
"description": "Spells hit up to 3 enemies in line",
"canvasChange": "spells-have-trail-piercing",
"conflictsWith": [
"splashDamage"
]
},
{
"id": "fasterCasting",
"name": "Rapid Casting",
"baseCost": 80,
"effect": {
"type": "castSpeed",
"multiplier": 0.8
},
"description": "20% faster spell casting",
"canvasChange": "cast-animation-faster",
"conflictsWith": [
"overchargedSpells"
]
},
{
"id": "overchargedSpells",
"name": "Overcharged Spells",
"baseCost": 80,
"effect": {
"type": "damage",
"all": 0.2
},
"description": "+20% spell damage, slower casting",
"canvasChange": "spells-have-glow-aura",
"conflictsWith": [
"fasterCasting"
]
}
]
},
"tier3": {
"requires": "waveNumber >= 8",
"elementalSynergy": {
"baseCost": 150,
"costMultiplier": 1.4,
"currency": "ironShards",
"maxLevel": 5,
"requires": "elementalSpecialization",
"effect": {
"type": "sameElementBonus",
"perLevel": 0.15
},
"description": "+15% damage when using same element twice",
"canvasChange": "same-element-spells-glow-brighter"
},
"chargeMastery": {
"baseCost": 120,
"costMultiplier": 1.3,
"currency": "ironShards",
"maxLevel": 3,
"requires": "overdriveUnlocked",
"effect": {
"type": "chargeCapacity",
"perLevel": 20
},
"description": "+20 Charge capacity",
"canvasChange": "charge-bar-langer"
}
}
},
"antiFrustration": {
"stuckDetection": "If no wave cleared in 90 seconds, boost player spell damage by 50% for 30 seconds",
"catchupMechanism": "Bad upgrade choices recover via passive mana regen - always gaining resources even when stuck",
"visualProgress": "Progress bars on locked milestones always visible - '90% to next unlock'",
"noDeadEnds": "Cannot lose all progress - can always collect mana from Wisps (fast, weak enemies)",
"forgivingEarlyGame": "First 3 waves have reduced enemy HP by 25% to ensure success",
"resourceMagnetism": "All collectibles magnet to player after 5 seconds - no frustration from missing drops",
"clearHorizens": "Always show 'Next unlock in X Iron/Mana/Y waves' - never wondering what to do"
},
"motivatorSummary": {
"next30Seconds": "Almost can afford next upgrade, watching spell effects, collecting drops",
"next2Minutes": "New rune slot or elemental mastery about to unlock",
"next5to10Minutes": "Boss wave approaching, Overdrive charge building",
"session": "Prestige for permanent power, new visual arena, specialization build"
}
}
Progression
# Spellforge Arena - Progression System Design
## Unlock Flow (MOST IMPORTANT)
```mermaid
flowchart TD
Start["Game Start\n0:00\n3 rune slots visible"] --> FirstEntities["First enemies spawn on Canvas\nWisps + Guardians appear\nCore gameplay visible immediately"]
FirstEntities -->|"First spell cast\n(~5 sec)"| SpellDiscovery["🔓 Unlock: Spell System\nRune slots active\nProjectiles visible on Canvas"]
FirstEntities -->|"First Wisp killed\n(~15 sec)"| FirstMana["+3 Mana fragment drop\nCollection mechanic revealed"]
SpellDiscovery -->|"Wave 1 complete\n(~45 sec)"| Wave1Reward["🔓 Unlock: Wave Bonus\n+10 Mana awarded\nMana collection visible"]
Wave1Reward -->|"10 Iron collected\n(~2 min)"| UpgradesRevealed["🔓 Unlock: Upgrades Panel\nIron Shard upgrades visible\nElemental mastery options"]
FirstMana -->|"25 Mana accumulated\n(~1 min)"| ManaAffluent["🔓 Unlock: Mana Mastery\nFirst passive upgrade\nRegen increased"]
UpgradesRevealed -->|"Wave 3 complete\n(~4 min)"| ElementSprites["🔓 Unlock: Element Sprites\nColored sparks orbit rune circle\n+2 Charge on collect"]
ElementSprites -->|"Wave 5 complete\n+100 Iron earned"| RuneSlot4["🔓 Unlock: 4th Rune Slot\nSpell complexity doubles\nNew combo possibilities"]
RuneSlot4 -->|"First charge filled\n(~6 min)"| OverdriveUnlocked["🔓 Unlock: Overdrive Bar\n50 Charge threshold\n3x damage ability"]
OverdriveUnlocked -->|"Elite Champion killed\n(~8 min)"| CrystalDrops["🔓 Unlock: Crystal Essence\nPurple drops visible\nPermanent upgrades teased"]
CrystalDrops -->|"Wave 10 complete\n(~12 min)"| RuneSlot5["🔓 Unlock: 5th Rune Slot\nMaximum spell complexity\nAll element combos available"]
RuneSlot5 -->|"Wave 12 complete\n(~15 min)"| PrestigeTeased["✨ Prestige Button Appears\n'Prestige Available' toast\nShows potential reward"]
PrestigeTeased -->|"Wave 15 complete\n(~20 min)"| PrestigeReady["✨ Prestige Recommended\nAll upgrades purchased\nEnemies outscaling defenses"]
RuneSlot5 -.->|"Parallel path"| MasteryDeepen["Elemental mastery levels 5+\nSpecialization becomes viable"]
MasteryDeepen -->|"50 Crystal Essence\n(~25 min)"| PermanentUpgrade["First permanent upgrade\nPersists across prestiges"]
style Start fill:#90EE90
style RuneSlot4 fill:#90D0FF
style RuneSlot5 fill:#90D0FF
style OverdriveUnlocked fill:#FFD700
style CrystalDrops fill:#9932CC
style PrestigeReady fill:#FF6B6B
```
## Gate Dependency Graph
```mermaid
graph TD
subgraph "Hard Gates (progress pauses)"
HG1["⛔ Gate: First Spell Cast\nRequirement: Cast any spell\nWait: ~5 sec\nBlocks: Mana spending, resource drops"]
HG2["⛔ Gate: Wave 1 Complete\nRequirement: Defeat all wave 1 enemies\nWait: ~45 sec\nBlocks: Wave bonuses, progression"]
HG3["⛔ Gate: 100 Iron Shards\nRequirement: Collect 100 Iron\nWait: ~4 min\nBlocks: Rune slot 4, mastery tier 2"]
HG4["⛔ Gate: Wave 10\nRequirement: Survive to wave 10\nWait: ~12 min\nBlocks: Rune slot 5, late-game content"]
HG5["⛔ Gate: Prestige Threshold\nRequirement: Wave 15 complete\nWait: ~20 min\nBlocks: Prestige action, permanent upgrades"]
end
subgraph "Soft Gates (progress slows)"
SG1["🟡 Gate: Early Game Plateau\nWaves 2-4\nIron shard income slows\nPlayer redirected to: Efficiency optimization"]
SG2["🟡 Gate: Mid-Game Wall\nWaves 7-9\nEnemy HP outpaces damage\nPlayer redirected to: Mastery upgrades"]
SG3["🟡 Gate: Pre-Prestige Grind\nWaves 12-14\nAll upgrades expensive\nPlayer redirected to: Prestige decision"]
end
subgraph "Choice Gates (strategic branching)"
CG1["🔵 Gate: Elemental Specialization\nRequirement: 30 Iron for first mastery\nForces: Fire/Ice/Arcane/Void focus\nChanges: Visual theme, spell effectiveness"]
CG2["🔵 Gate: Rune Configuration\nRequirement: 4th slot unlocked\nChoice: More slots vs. More power\nChanges: Combat rhythm, combo depth"]
end
HG1 --> SG1
SG1 --> HG2
HG2 --> CG1
CG1 --> SG2
SG2 --> HG3
HG3 --> CG2
CG2 --> SG3
SG3 --> HG4
HG4 --> SG3
SG3 --> HG5
```
## Progression Timeline
```mermaid
gantt
title Progression Pacing (30 Minutes to First Prestige)
dateFormat mm:ss
axisFormat %M:%S
section Onboarding (0-2 min)
First entities visible on Canvas :active, 00:00, 5s
First spell cast & projectile :active, 00:05, 10s
First resource drops (Mana/Iron) :active, 00:15, 30s
Wave 1 complete - first bonus :active, 00:45, 15s
First upgrade affordable :active, 01:30, 30s
section Foundation (2-5 min)
Upgrades panel revealed :active, 02:00, 60s
Element sprites discovered :active, 03:00, 60s
First elemental mastery purchased :active, 03:30, 30s
Mana mastery unlocks regen :active, 04:00, 60s
section Expansion (5-12 min)
4th rune slot unlock :active, 05:00, 90s
Overdrive system discovered :active, 06:30, 60s
First Elite Champion appears :active, 08:00, 120s
Crystal Essence drops visible :active, 09:00, 60s
5th rune slot unlock :active, 12:00, 120s
section Mastery (12-20 min)
All spell combinations available :active, 15:00, 180s
Elemental specialization viable :active, 17:00, 180s
First permanent upgrade affordable :active, 20:00, 120s
section Climax (20-30 min)
Prestige teased & recommended :active, 22:00, 300s
All mastery tiers purchased :active, 25:00, 300s
Optimal prestige point :milestone, 28:00, 0
```
## Tension Curve
```mermaid
graph LR
subgraph "Emotional Arc"
T1["0-2 min\n🟢 Excitement\nTension: 2/5\nFirst spell casts, discovering runes"]
T2["2-5 min\n🟢 Discovery\nTension: 3/5\nNew drops, upgrades revealed"]
T3["5-7 min\n🟡 First Plateau\nTension: 2/5\nSaving for 4th rune slot"]
T4["7-10 min\n🟢 Expansion\nTension: 4/5\nOverdrive, Champions, Essence"]
T5["10-15 min\n🟡 Optimization\nTension: 3/5\nMastering spell combinations"]
T6["15-20 min\n🟢 Peak\nTension: 5/5\n5th slot, specialization"]
T7["20-25 min\n🟡 Push\nTension: 4/5\nGrinding for prestige"]
T8["25-30 min\n🟢 Climax\nTension: 5/5\nPrestige decision!"]
end
T1 --> T2 --> T3 --> T4 --> T5 --> T6 --> T7 --> T8
```
## Milestone Rewards
```mermaid
graph LR
subgraph "Gameplay Milestones"
M1["🏆 First Spark\nCast first spell\nReward: Visual discovery\nToast: The battle begins!"]
M2["🏆 Wave Breaker\nComplete Wave 1\nReward: +10 Mana\nToast: First wave cleared!"]
M3["🏆 Rune Smith\nUnlock 4th rune slot\nReward: Combo discovery\nToast: Spell complexity doubled!"]
M4["🏆 Overcharged\nFirst Overdrive use\nReward: 3x damage preview\nToast: UNLEASHED!"]
M5["🏆 Champion Slayer\nDefeat Elite Champion\nReward: First Crystal Essence\nToast: Rare essence discovered!"]
M6["🏆 Master Runecaster\nUnlock 5th rune slot\nReward: Maximum complexity\nToast: All combinations unlocked!"]
M7["🏆 Elemental Adept\nReach mastery level 5\nReward: +50% element damage\nToast: Mastery deepens!"]
M8["🏆 Arena Transcendence\nFirst prestige\nReward: Permanent power\nToast: A new era begins!"]
end
```
## Tutorial / First-Time Flow
```mermaid
sequenceDiagram
actor Player
participant Canvas as Game Canvas
participant UI as Game UI
participant Rune as Rune System
Note over Player,UI: First 60 Seconds
Player->>Canvas: loads game
Canvas->>Player: enemies spawn automatically (Wisps/Guardians)
UI->>Player: HUD shows Mana (20/100), Iron (0), Charge (0/100)
UI->>Player: 3 rune slots visible at bottom
Canvas->>Canvas: enemies move toward rune circle
Player->>Rune: clicks first slot (cycles to Fire 🔥)
Rune->>Canvas: slot glows orange
Player->>Rune: clicks second slot (cycles to Ice ❄️)
Rune->>Canvas: slot glows blue
Player->>UI: presses SPACE to cast spell
Canvas->>Canvas: projectile spawns from rune circle
Canvas->>Canvas: projectile travels to target enemy
Canvas->>Canvas: HIT - enemy dies with particle effect
Canvas->>Player: Mana fragment appears (+3 💧)
Player->>Canvas: clicks Mana fragment
Canvas->>UI: floating "+3 💧" text
UI->>Player: Mana counter animates (20 → 17 → 20 via regen)
Player->>Canvas: continues casting, killing enemies
Canvas->>UI: Iron Shard drops appear
Player->>Canvas: clicks Iron Shard
UI->>Player: Iron counter: 0 → 5 ⚙️
Note over Player,UI: At 10 Iron (~90 seconds):
UI->>Player: 🔓 Upgrades button glows in bottom panel
UI->>Player: tooltip shows "Fire Mastery: 30 Iron"
Player->>UI: clicks Fire Mastery upgrade
UI->>Canvas: rune circle gets orange glow
UI->>Player: toast "Fire Mastery +1! 🔥"
Note over Player,Canvas: Player now deals +10% Fire damage\nvisible as larger fire effects on spells
```
## Entity Spawn/Unlock State Machines
### Player Spell Projectiles
```mermaid
stateDiagram-v2
[*] --> Locked: game start
Locked --> Unlocked: first spell cast\n(~5 seconds)
Unlocked --> Available: player has mana >= spell cost
Available --> Spawning: player presses SPACE/clicks CAST
Spawning --> Active: spawn animation (100ms)\nscale 0→1
Active --> Traveling: launch from rune circle
Traveling --> Impact: collision with enemy
Traveling --> Expired: off-screen (2s timeout)
Impact --> [*]: deal damage, spawn particles\ncheck enemy death
Expired --> [*]: fade out, cleanup
note right of Unlocked
Toast: "Spell System Active!"
Rune slots become interactive
Element cycling enabled
end note
note right of Active
Color: based on elemental composition
Size: base scale * mastery
Trail: particle effect by element
Target: homing if Arcane, straight otherwise
end note
note right of Impact
Triggers: enemy.takeDamage(damage)
Triggers: projectile.hit visual
If enemy dies: spawn collectibles
Element-specific effects: Ice (slow), Fire (burn)
end note
```
### Enemy: Wisp
```mermaid
stateDiagram-v2
[*] --> Locked: game start
Locked --> Unlocked: wave 1 begins\n(always available from start)
Unlocked --> Spawning: wave spawn timer
Spawning --> Active: spawn at top edge\nfade in (200ms)
Active --> Moving: zigzag pathfinding\ntoward rune circle
Moving --> Dying: hp <= 0 from spell
Moving --> Despawning: reaches rune circle\ndeals damage to player
Dying --> Dead: death animation (300ms)
Dead --> [*]: spawn Mana fragment\nremove entity
note right of Active
Sprite: ghost (cyan/white)
HP: 10 (low)
Speed: Fast (2.5x base)
Movement: Erratic zigzag
Reward: +3 Mana on kill
end note
note right of Dying
Triggers: death particle burst
Triggers: spawnCollectible('manaFragment', position)
Triggers: currencyCheck('mana')
Sound: ethereal wisp
end note
```
### Enemy: Guardian
```mermaid
stateDiagram-v2
[*] --> Locked: game start
Locked --> Unlocked: wave 1 begins\n(always available from start)
Unlocked --> Spawning: wave spawn timer
Spawning --> Active: spawn at top edge\nscale in (250ms)
Active --> Moving: straight path\ntoward rune circle
Moving --> Fighting: in spell range\n(no attack - just tank)
Fighting --> Moving: spell impact resolved
Moving --> Dying: hp <= 0 from spell
Moving --> Despawning: reaches rune circle\ndeals damage to player
Dying --> Dead: death animation (400ms)\nwith metallic flash
Dead --> [*]: spawn Iron Shard + Mana\nremove entity
note right of Active
Sprite: knight (steel gray armor)
HP: 30 (moderate)
Speed: Medium (1.0x base)
Reward: +5 Iron, +2 Mana on kill
Weak to: Fire (+20% damage)
Strong to: Ice (-20% damage, slowed)
end note
note right of Dying
Triggers: metallic death burst
Triggers: spawnCollectible('ironShard', position, 5)
Triggers: spawnCollectible('manaFragment', position, 2)
Triggers: checkChargeReward()
Sound: armor clatter
end note
```
### Enemy: Golem (Boss)
```mermaid
stateDiagram-v2
[*] --> Locked: game start
Locked --> Teased: wave 4 announced\n"Golem incoming in 3..."
Teased --> Unlocked: wave 5 begins
Unlocked --> Spawning: boss spawn trigger\n(30s after wave start)
Spawning --> Active: dramatic spawn\n(500ms) + screen flash
Active --> Moving: slow march\ntoward rune circle
Moving --> Dying: hp <= 0 from spell
Moving --> Despawning: reaches rune circle\nmassive damage (20)
Dying --> Dead: boss death (800ms)\nwith shockwave
Dead --> [*]: spawn Iron Shards (x3)\n+ Essence (30% chance)\nmilestone check
note right of Teased
UI: "⚠️ BOSS INCOMING"
Canvas: warning pulse
Player: 30s to prepare
end note
note right of Active
Sprite: slime (brown/gray, scale 2.0x)
HP: 150 (massive - 5x Guardian)
Speed: Very slow (0.3x base)
Reward: +15 Iron, 30% chance +1 Essence
Weak to: Void (full damage)
Strong to: Fire/Ice (50% damage)
end note
note right of Dying
Triggers: screen shake
Triggers: particle explosion (20 particles)
Triggers: spawnCollectible('ironShardLarge', position, 15)
Triggers: if(random < 0.3) spawnCollectible('crystalEssence', position, 1)
Triggers: waveComplete()
Sound: rumble + crash
end note
```
### Enemy: Elite Champion
```mermaid
stateDiagram-v2
[*] --> Locked: game start
Locked --> Teased: wave 6 announced\n"Champion approaching..."
Teased --> Unlocked: wave 7 begins (3 min after start)
Unlocked --> Spawning: champion spawn trigger\n(60s after wave start)
Spawning --> Active: teleport in\n(400ms) + purple flash
Active --> Moving: purposeful walk\ntoward rune circle
Active --> Casting: special ability\n(summon minions or counter-spell)
Casting --> Moving: ability complete
Moving --> Dying: hp <= 0 from spell
Dying --> Dead: champion death (600ms)\nwith energy release
Dead --> [*]: spawn Iron Shards (x4)\n+ Essence (60% chance)\nwave advancement
note right of Active
Sprite: wizard (red/purple, scale 1.2x)
HP: 80 (high - 2.5x Guardian)
Speed: Medium-fast (1.2x base)
Special: Summons 2 Wisps on spawn
Reward: +20 Iron, 60% chance +3 Essence
Weak to: Rotates each wave (shown on Canvas)
end note
note right of Casting
Every 5 seconds: cast ability
Ability 1: Summon 2 Wisps at top
Ability 2: Fire counter-spell toward player\n(visible warning for 1s)
end note
```
### Collectible: Element Sprite
```mermaid
stateDiagram-v2
[*] --> Locked: game start
Locked --> Unlocked: wave 3 complete\n(~4 minutes)
Unlocked --> Spawning: periodic timer\nevery 8-12 seconds
Spawning --> Active: fade in (300ms)\nat random orbit position
Active --> Orbiting: drifts around rune circle\n60px radius
Active --> Collected: player clicks within 18px
Active --> Expired: lifetime 6s exceeded
Collected --> [*]: add Charge + Mana\nparticle burst
Expired --> [*]: fade out (200ms)
note right of Unlocked
Toast: "Element Sprites Appeared!"
First spawn: 5s after unlock
Color: matches last-cast element
end note
note right of Orbiting
Sprite: spark (element color)
Value: +2 Charge, +1 Mana, 20% +1 Iron
Bob animation: 2px amplitude, 2.5Hz
Collect radius: 18px
Magnetism: 80px range at 150px/sec
end note
```
## Wave/Round State Machine
```mermaid
stateDiagram-v2
[*] --> PreWave: game start\nprevious wave complete
PreWave --> Spawning: countdown complete (3s)\n"Wave N incoming!"
Spawning --> Active: all enemies spawned\nplayer can cast spells
Active --> BossPhase: boss wave trigger\n(wave 5, 10, 15...)
Active --> WaveComplete: all enemies defeated\nor wave timer exceeded (90s)
BossPhase --> Active: boss defeated\nminions continue
BossPhase --> Failed: player died\nprestige option shown
WaveComplete --> Reward: +10 * waveNumber Mana\ncheck milestones
Reward --> PreWave: next wave countdown\n(5s breather)
Reward --> PrestigeCheck: wave >= 15
PrestigeCheck --> PreWave: prestige declined\ncontinue playing
PrestigeCheck --> [*]: prestige accepted\nrun complete
note right of PreWave
UI: "Wave {n} incoming!" (3s countdown)
Canvas: spawn indicators pulse at top
Player: last chance to spend, organize
end note
note right of Spawning
Wave 1-4: 2-4 enemies (Wisps + Guardians)
Wave 5-9: 4-6 enemies + 1 Golem at wave 5
Wave 10-14: 6-8 enemies + 1 Champion each wave
Wave 15+: 8-10 enemies + Golem + Champion
end note
note right of Active
Player casts spells at enemies
Enemies drop Mana/Iron on death
Element sprites orbit (unlocked at wave 3)
Boss health bar visible (if boss wave)
Charge meter visible (unlocked at wave 4)
end note
note right of WaveComplete
UI: toast "Wave {n} Complete!" + bonus
Canvas: brief flash (white)
Iron Shard collectibles magnet to player
Check: unlock conditions (rune slots, upgrades)
Check: milestone triggers
end note
```
## CONFIG Spec: progression and waves Sections
```javascript
// Progression System Configuration for Spellforge Arena
CONFIG.progression = {
// Unlock thresholds - all trigger on gameplay events
unlocks: {
firstSpellCast: {
requirement: 'spellsCast >= 1',
unlocks: 'spellSystem',
toast: 'Spell system active!',
indicator: 'rune-glow',
},
wave1Complete: {
requirement: 'wavesCompleted >= 1',
unlocks: 'waveBonuses',
toast: 'Wave cleared! +10 Mana',
indicator: 'hud-flash',
},
ironCollected: {
requirement: 'ironShards >= 10',
unlocks: 'upgradesPanel',
toast: 'Upgrades available!',
indicator: 'tab-glow',
},
wave3Complete: {
requirement: 'wavesCompleted >= 3',
unlocks: 'elementSprites',
toast: 'Element sprites discovered!',
indicator: 'canvas-new-entity',
},
wave5Complete: {
requirement: 'wavesCompleted >= 5',
unlocks: 'runeSlot4',
toast: '4th rune slot unlocked!',
indicator: 'rune-slot-appear',
},
chargeFilled: {
requirement: 'elementalCharge >= 50',
unlocks: 'overdrive',
toast: 'Overdrive ready!',
indicator: 'bar-glow',
},
championKilled: {
requirement: 'championsKilled >= 1',
unlocks: 'crystalEssence',
toast: 'Crystal Essence discovered!',
indicator: 'hud-reveal',
},
wave10Complete: {
requirement: 'wavesCompleted >= 10',
unlocks: 'runeSlot5',
toast: '5th rune slot unlocked!',
indicator: 'rune-slot-appear',
},
wave12Complete: {
requirement: 'wavesCompleted >= 12',
unlocks: 'prestigeTeaser',
toast: 'Prestige available...',
indicator: 'button-pulse',
},
},
// Milestones with rewards
milestones: [
{
id: 'firstSpark',
trigger: 'spellsCast >= 1',
reward: { mana: 0 },
toast: 'The battle begins!',
},
{
id: 'waveBreaker',
trigger: 'wavesCompleted >= 1',
reward: { mana: 10 },
toast: 'First wave cleared!',
},
{
id: 'runeSmith',
trigger: 'runeSlot4Unlocked',
reward: { ironShards: 0 },
toast: 'Spell complexity doubled!',
},
{
id: 'overcharged',
trigger: 'overdriveUsed >= 1',
reward: { ironShards: 0 },
toast: 'UNLEASHED!',
},
{
id: 'championSlayer',
trigger: 'championsKilled >= 1',
reward: { crystalEssence: 0 },
toast: 'Champion defeated!',
},
{
id: 'masterRunecaster',
trigger: 'runeSlot5Unlocked',
reward: { ironShards: 0 },
toast: 'All combinations unlocked!',
},
{
id: 'elementalAdept',
trigger: 'anyMasteryLevel >= 5',
reward: { crystalEssence: 1 },
toast: 'Mastery deepens!',
},
{
id: 'arenaTranscendence',
trigger: 'prestigesCompleted >= 1',
reward: { crystalEssence: 5 },
toast: 'A new era begins!',
},
],
// Early game generosity curve
earlyGame: {
firstSpellCost: 3, // 15% of starting mana
firstUpgradeAffordable: 90, // seconds - 1.5 min to 10 Iron
manaRegenStart: 2, // per second - keeps player in game
wispManaDrop: 3, // Guaranteed mana from kills
guardianIronDrop: 5, // Guaranteed iron from kills
wave1Bonus: 10, // Immediate reward
},
};
CONFIG.waves = {
baseEnemyCount: 2,
enemyCountGrowth: 1.15, // enemies per wave = floor(base * growth^(wave-1))
spawnInterval: 2000, // ms between enemy spawns
preWaveCountdown: 3, // seconds before wave starts
postWaveBreather: 5, // seconds between waves
bossWaveInterval: 5, // boss every N waves (5, 10, 15, 20...)
waveTimeout: 90, // seconds before wave auto-completes (stuck prevention)
};
CONFIG.waveComposition = {
// Wave-specific enemy compositions
// Returns: [{ type, count, delay }]
getEnemies: function(waveNum) {
const baseCount = Math.floor(2 * Math.pow(1.15, waveNum - 1));
const enemies = [];
// Waves 1-4: Wisps + Guardians only
if (waveNum <= 4) {
const wispCount = Math.ceil(baseCount * 0.6);
const guardianCount = Math.floor(baseCount * 0.4);
for (let i = 0; i < wispCount; i++) {
enemies.push({ type: 'wisp', delay: i * 1500 });
}
for (let i = 0; i < guardianCount; i++) {
enemies.push({ type: 'guardian', delay: (wispCount + i) * 2000 });
}
}
// Waves 5-9: Add Golem at wave 5, then Wisps + Guardians + Champions
else if (waveNum <= 9) {
const hasGolem = waveNum === 5;
const hasChampion = waveNum >= 7;
const wispCount = Math.ceil(baseCount * 0.4);
const guardianCount = Math.floor(baseCount * 0.5);
let delay = 0;
if (hasGolem) {
enemies.push({ type: 'golem', delay: 30000 }); // Boss at 30s
delay += 2000;
}
if (hasChampion) {
enemies.push({ type: 'champion', delay: 60000 }); // Champion at 60s
delay += 2000;
}
for (let i = 0; i < wispCount; i++) {
enemies.push({ type: 'wisp', delay: delay + i * 1200 });
}
for (let i = 0; i < guardianCount; i++) {
enemies.push({ type: 'guardian', delay: delay + (wispCount + i) * 1800 });
}
}
// Waves 10+: All enemy types
else {
const hasGolem = waveNum % 5 === 0;
const hasChampion = true;
const wispCount = Math.ceil(baseCount * 0.35);
const guardianCount = Math.floor(baseCount * 0.45);
let delay = 0;
if (hasGolem) {
enemies.push({ type: 'golem', delay: 25000 });
delay += 2000;
}
enemies.push({ type: 'champion', delay: 50000 });
delay += 2000;
for (let i = 0; i < wispCount; i++) {
enemies.push({ type: 'wisp', delay: delay + i * 1000 });
}
for (let i = 0; i < guardianCount; i++) {
enemies.push({ type: 'guardian', delay: delay + (wispCount + i) * 1500 });
}
}
return enemies;
},
// Elemental weakness rotation (for Champions)
getElementalWeakness: function(waveNum) {
const elements = ['fire', 'ice', 'arcane', 'void'];
return elements[waveNum % 4]; // Rotates: fire, ice, arcane, void, fire...
},
// Wave rewards
getReward: function(waveNum) {
return {
mana: waveNum * 10, // 10, 20, 30, 40...
};
},
};
CONFIG.entityUnlocks = {
// When each enemy type becomes available
wisp: {
unlockWave: 1,
spawnCondition: 'always',
hp: 10,
speed: 2.5,
damage: 10,
reward: { mana: 3 },
},
guardian: {
unlockWave: 1,
spawnCondition: 'always',
hp: 30,
speed: 1.0,
damage: 15,
reward: { mana: 2, ironShards: 5 },
weakness: { fire: 0.2 }, // +20% from fire
resistance: { ice: 0.2 }, // -20% from ice
},
golem: {
unlockWave: 5,
spawnCondition: 'waveNum % 5 === 0', // Every 5th wave
hp: 150,
speed: 0.3,
damage: 25,
reward: { mana: 5, ironShards: 15, crystalEssence: 0.3 },
weakness: { void: 1.0 }, // Void does full damage
resistance: { fire: 0.5, ice: 0.5 }, // 50% reduction
isBoss: true,
announce: 30000, // Announce 30s before spawn
},
champion: {
unlockWave: 7,
spawnCondition: 'waveNum >= 7', // Every wave from 7+
hp: 80,
speed: 1.2,
damage: 20,
reward: { mana: 5, ironShards: 20, crystalEssence: 0.6 },
weakness: 'rotating', // Changes each wave
abilities: ['summonMinions', 'counterspell'],
isElite: true,
announce: 5000, // Announce 5s before spawn
},
};
CONFIG.prestige = {
teaserWave: 12, // Prestige button appears grayed out
minWave: 15, // Prestige becomes clickable
recommendedWave: 18, // "Recommended" indicator
formula: 'floor(maxWaveReached * 0.5 + eliteChampionsKilled * 2 + golemsKilled * 0.5)',
resets: {
currencies: ['mana', 'ironShards', 'elementalCharge'],
upgrades: ['runeSlot4', 'runeSlot5', 'elementalMasteries', 'spellDamage', 'manaMastery'],
wave: 0,
arenaTier: 0,
},
persists: {
currencies: ['crystalEssence'],
upgrades: ['permanentRuneSlot', 'elementalAttunement', 'manaCapUpgrade', 'startingManaUpgrade'],
achievements: ['totalPrestiges', 'maxWaveReached', 'totalKills'],
},
visualTiers: [
{
tier: 0,
minPrestiges: 0,
background: '#1a1a2e',
arenaPattern: 'basic-pentagram',
enemyPalette: null,
runeCircle: 'stone',
},
{
tier: 1,
minPrestiges: 1,
background: '#2e1a3e',
arenaPattern: 'intricate-pentagram',
enemyPalette: { hueShift: 30, saturate: 1.2 },
runeCircle: 'glowing-lines',
},
{
tier: 2,
minPrestiges: 3,
background: '#3e1a4e',
arenaPattern: 'multi-layered',
enemyPalette: { hueShift: 60, saturate: 1.4 },
runeCircle: 'floating-glyphs',
},
{
tier: 3,
minPrestiges: 5,
background: '#4e1a5e',
arenaPattern: 'sanctum',
enemyPalette: { hueShift: 90, saturate: 1.6 },
runeCircle: 'energy-vortex',
},
],
upgrades: {
// Permanent upgrades purchased with Crystal Essence
permanentRuneSlot: {
baseCost: 50,
costMultiplier: 1.5, // Each subsequent slot costs more
maxPurchases: 2,
effect: { type: 'permanent', property: 'startingRuneSlots', value: 1 },
description: '+1 permanent rune slot (persists across prestiges)',
},
elementalAttunement: {
baseCost: 100,
costMultiplier: 1.3,
maxLevel: 5,
requiresUserChoice: true, // Player chooses element
effect: { type: 'damage', element: 'chosen', perLevel: 0.1 },
description: '+10% damage to chosen element (persists)',
},
manaCapUpgrade: {
baseCost: 75,
costMultiplier: 1.3,
maxPurchases: 5,
effect: { type: 'cap', currency: 'mana', value: 20 },
description: '+20 maximum Mana (persists)',
},
startingManaUpgrade: {
baseCost: 60,
costMultiplier: 1.4,
maxPurchases: 3,
effect: { type: 'startingAmount', currency: 'mana', value: 10 },
description: '+10 starting Mana (persists)',
},
chargeMastery: {
baseCost: 80,
costMultiplier: 1.3,
maxPurchases: 3,
effect: { type: 'chargeCap', value: 20 },
description: '+20 maximum Elemental Charge (persists)',
},
overdriveEfficiency: {
baseCost: 120,
costMultiplier: 1.5,
maxPurchases: 2,
effect: { type: 'overdriveCost', value: -5 },
description: 'Overdrive costs 5 less Charge (persists)',
},
},
// Run 2 acceleration - how much faster is the second run?
acceleration: {
firstPrestige: {
waveSpeed: 1.5, // Reach wave 10 in 2/3 the time
incomeMultiplier: 1.3, // 30% more resources
unlockCheaper: 0.8, // 20% cheaper to unlock things
},
secondPrestige: {
waveSpeed: 2.0, // 2x faster
incomeMultiplier: 1.5,
unlockCheaper: 0.7,
},
},
};
CONFIG.upgradeTiers = {
// Tier 1: Available from game start - Direct stat boosts
tier1: {
fireMastery: {
baseCost: 30,
costMultiplier: 1.3,
currency: 'ironShards',
maxLevel: 10,
effect: { type: 'damage', element: 'fire', perLevel: 0.1 },
description: '+10% Fire damage per level',
canvasChange: 'fire-spell-trail-longer',
},
iceMastery: {
baseCost: 30,
costMultiplier: 1.3,
currency: 'ironShards',
maxLevel: 10,
effect: { type: 'damage', element: 'ice', perLevel: 0.1 },
description: '+10% Ice damage per level',
canvasChange: 'ice-spell-freeze-longer',
},
arcaneMastery: {
baseCost: 30,
costMultiplier: 1.3,
currency: 'ironShards',
maxLevel: 10,
effect: { type: 'damage', element: 'arcane', perLevel: 0.1 },
description: '+10% Arcane damage per level',
canvasChange: 'arcane-spell-homing-stronger',
},
voidMastery: {
baseCost: 30,
costMultiplier: 1.3,
currency: 'ironShards',
maxLevel: 10,
effect: { type: 'damage', element: 'void', perLevel: 0.1 },
description: '+10% Void damage per level',
canvasChange: 'void-spell-penetration',
},
spellDamage: {
baseCost: 50,
costMultiplier: 1.2,
currency: 'ironShards',
maxLevel: 15,
effect: { type: 'damage', element: 'all', perLevel: 0.05 },
description: '+5% all spell damage per level',
canvasChange: 'all-spells-larger',
},
manaMastery: {
baseCost: 30,
costMultiplier: 1.3,
currency: 'mana',
maxLevel: 5,
effect: { type: 'regen', perLevel: 1 },
description: '+1 mana regen per second',
canvasChange: 'mana-bar-fills-visual',
},
},
// Tier 2: Unlock at wave 3+ - Build diversity (mutually exclusive paths)
tier2: {
requires: 'waveNumber >= 3',
choices: [
{
id: 'splashDamage',
name: 'Splash Spells',
baseCost: 100,
effect: { type: 'splash', radius: 50, damagePercent: 0.5 },
description: 'Spells deal 50% damage in small area',
canvasChange: 'spells-have-explosion-radius',
conflictsWith: ['piercingShots'],
},
{
id: 'piercingShots',
name: 'Piercing Shots',
baseCost: 100,
effect: { type: 'piercing', maxTargets: 3 },
description: 'Spells hit up to 3 enemies in line',
canvasChange: 'spells-have-trail-piercing',
conflictsWith: ['splashDamage'],
},
{
id: 'fasterCasting',
name: 'Rapid Casting',
baseCost: 80,
effect: { type: 'castSpeed', multiplier: 0.8 },
description: '20% faster spell casting',
canvasChange: 'cast-animation-faster',
conflictsWith: ['overchargedSpells'],
},
{
id: 'overchargedSpells',
name: 'Overcharged Spells',
baseCost: 80,
effect: { type: 'damage', all: 0.2 },
description: '+20% spell damage, slower casting',
canvasChange: 'spells-have-glow-aura',
conflictsWith: ['fasterCasting'],
},
],
},
// Tier 3: Unlock at wave 8+ - Synergy bonuses
tier3: {
requires: 'waveNumber >= 8',
elementalSynergy: {
baseCost: 150,
costMultiplier: 1.4,
currency: 'ironShards',
maxLevel: 5,
requires: 'elementalSpecialization',
effect: { type: 'sameElementBonus', perLevel: 0.15 },
description: '+15% damage when using same element twice',
canvasChange: 'same-element-spells-glow-brighter',
},
chargeMastery: {
baseCost: 120,
costMultiplier: 1.3,
currency: 'ironShards',
maxLevel: 3,
requires: 'overdriveUnlocked',
effect: { type: 'chargeCapacity', perLevel: 20 },
description: '+20 Charge capacity',
canvasChange: 'charge-bar-longer',
},
},
};
```
## Anti-Frustration Features
- **Stuck Detection**: If no wave cleared in 90 seconds, boost player spell damage by 50% for 30 seconds
- **Catchup Mechanism**: Bad upgrade choices recover via passive mana regen - always gaining resources even when stuck
- **Visual Progress**: Progress bars on locked milestones always visible - "90% to next unlock"
- **No Dead Ends**: Cannot lose all progress - can always collect mana from Wisps (fast, weak enemies)
- **Forgiving Early Game**: First 3 waves have reduced enemy HP by 25% to ensure success
- **Resource Magnetism**: All collectibles magnet to player after 5 seconds - no frustration from missing drops
- **Clear Horizons**: Always show "Next unlock in X Iron/Mana/Y waves" - never wondering what to do
## Motivator Summary
| Timeframe | What Drives Player |
|-----------|----------------------|
| Next 30 sec | Almost can afford next upgrade, watching spell effects, collecting drops |
| Next 2 min | New rune slot or elemental mastery about to unlock |
| Next 5-10 min | Boss wave approaching, Overdrive charge building |
| Session | Prestige for permanent power, new visual arena, specialization build |
Ui Ux
{
"gameName": "Spellforge Arena",
"ui-ux": {
"overview": "Dark mystical arena theme with deep purple/blue background and elemental accents (cyan, orange, violet). Canvas game world dominates 70% of screen showing rune circle at bottom, enemies descending from above, and spell projectiles flying across screen. UI is minimal and secondary - dark bottom panel for upgrades and top HUD bar for currencies.",
"colorPalette": {
"background": {
"primary": "#1a1a2e",
"secondary": "#141630",
"tertiary": "#2a2a4e",
"canvas": "#1a1a2e"
},
"text": {
"primary": "#e0e0e0",
"secondary": "#a0a0a0",
"muted": "#606060"
},
"accents": {
"mana": "#00ffff",
"iron": "#c0c0c0",
"charge": "#ffcc00",
"success": "#4caf50",
"warning": "#ff9800",
"danger": "#f44336",
"prestige": "#9932cc",
"spellFire": "#ff6b354",
"spellIce": "#00ffff",
"spellArcane": "#bf5eff",
"spellVoid": "#1a1a2e"
}
},
"typography": {
"fontFamily": "'Cinzel', 'Segoe UI', Tahoma, sans-serif",
"headings": {
"weight": "600-700",
"sizes": {
"title": "24px",
"section": "18px",
"card": "14px"
}
},
"bodyText": {
"weight": "400",
"size": "13px"
},
"numbers": {
"weight": "600",
"monospace": true
},
"buttons": {
"weight": "600",
"size": "14px"
}
},
"screenLayout": {
"canvasPercentage": 70,
"bottomPanelPercentage": 25,
"hudHeight": 50
},
"hudBar": {
"manaDisplay": {
"position": "left",
"content": "Icon + Current/Max + regen/sec",
"behavior": "animate on change, color cyan"
},
"ironDisplay": {
"position": "center-left",
"content": "Icon + Amount",
"behavior": "animate on change, color silver"
},
"chargeDisplay": {
"position": "center-right",
"content": "Icon + Current/Max",
"behavior": "fills visually, yellow-orange"
},
"waveCounter": {
"position": "center",
"content": "Wave X / Next milestone",
"behavior": "updates on wave change"
},
"eliteIndicator": {
"position": "right-top",
"content": "Rotating element icon",
"behavior": "shows current enemy weakness"
},
"settingsButton": {
"position": "right",
"content": "Gear icon",
"behavior": "opens settings modal"
}
},
"canvas": {
"backgroundColor": "#1a1a2e",
"layers": [
"background",
"entities",
"effects",
"hudOverlay"
],
"camera": "Fixed top-down view",
"interactions": {
"clickRuneSlot": "Cycle element (Fire/Ice/Arcane/Void)",
"clickCastButton": "Launch crafted spell",
"clickArena": "Set spell target location",
"clickCollectible": "Collect mana/iron/crystal",
"clickOverdriveBar": "Activate elemental overdrive"
}
},
"hudOverlay": {
"healthBars": {
"position": "Above each entity",
"content": "Small colored bar",
"behavior": "Updates with damage, fades on death (200ms)"
},
"floatingDamage": {
"position": "At hit location",
"content": "-X in red/white",
"behavior": "Floats upward 40px over 800ms"
},
"waveIndicator": {
"position": "Top center of canvas",
"content": "Wave X incoming!",
"behavior": "Appears 2s before wave, fades"
},
"targetReticle": {
"position": "Around clicked enemy",
"content": "Dashed circle ring",
"behavior": "Shows spell homing target"
},
"runeSlotHighlights": {
"position": "Around rune slots",
"content": "Glow by element",
"behavior": "Shows currently selected element"
}
},
"bottomPanel": {
"height": 180,
"collapsedHeight": 40,
"tabs": [
{
"id": "runes",
"label": "RUNES",
"icon": "✨",
"unlockedAt": "gameStart"
},
{
"id": "upgrades",
"label": "UPGRADES",
"icon": "⚡",
"unlockedAt": "waveComplete >= 3"
},
{
"id": "mastery",
"label": "MASTERY",
"icon": "⛇",
"unlockedAt": "waveComplete >= 3"
},
{
"id": "prestige",
"label": "PRESTIGE",
"icon": "✨",
"unlockedAt": "waveComplete >= 12"
}
],
"tabAnimation": "Slides in from right over 300ms with fade-in + scale-up effect"
},
"tabContent": {
"runes": {
"runeSlots": "3-5 slots shown horizontally, click to cycle elements",
"castButton": "Large centered button, SPACE or click to launch spell",
"spellPreview": "Shows current combination (e.g., Fire-Ice-Arcane)",
"slotStates": {
"available": "Full opacity, bright border, clickable",
"locked": "50% opacity, dim border, lock icon, hover shows unlock requirement",
"selected": "Glow animation with pulsing border, shows element icon",
"hover": "Scale 1.05x, brightness +10%"
}
},
"upgrades": {
"layout": "2 columns, scrollable",
"cardStates": {
"available": "Full opacity, green border (#4caf50), BUY button enabled",
"cannotAfford": "Full opacity, red border (#f44336), BUY button disabled, shake on click",
"maxed": "Full opacity, gray border (#606060), BUY button hidden, MAX badge",
"locked": "50% opacity, dim border (#2a2a4e), lock icon, details hidden"
}
},
"mastery": {
"fireMastery": "+10% Fire damage per level, makes fire projectiles larger with longer trails",
"iceMastery": "+10% Ice damage per level, makes ice spells freeze enemies (slow effect)",
"arcaneMastery": "+10% Arcane damage per level, makes arcane spells home toward targets",
"voidMastery": "+10% Void damage per level, makes void spells penetrate (full damage to all enemies in line)"
},
"prestige": {
"earnDisplay": "You will earn: +XX Crystal Essence, Current total: YY",
"resets": [
"Mana",
"Iron Shards",
"Rune slot count",
"Elemental mastery levels",
"Current wave"
],
"keeps": [
"Crystal Essence",
"Permanent rune slots",
"Elemental attunement bonuses"
],
"visualChange": "Arena background shifts to new tier, enemies get hue shift"
}
},
"notifications": {
"notificationArea": {
"content": "Toast messages",
"behavior": "Slide in from right, fade after 3s"
},
"milestonePopup": {
"content": "Achievement earned",
"behavior": "Center screen, dramatic, auto-dismiss 3s"
}
},
"components": {
"upgradeCard": {
"layout": "[Icon] [Upgrade Name] [Lvl X] / [Description] / [Effect] / [Cost] [BUY]",
"affordable": {
"border": "#4caf50",
"button": "same, white text"
},
"cannotAfford": {
"border": "#f44336",
"button": "#2a2a4e, grayed out"
},
"maxed": {
"border": "#606060",
"badge": "MAX red on card, button hidden"
},
"locked": {
"appearance": "Dimmed 50%, lock or ? icon, no details"
},
"hoverAffordable": {
"effect": "Scale 1.05x over 200ms, subtle shadow"
},
"clickAffordable": {
"effect": "Purchase animation 200ms, card flashes white, currency counts tick down"
},
"clickCannotAfford": {
"effect": "Shake animation, cost flashes red briefly"
}
},
"currencyDisplay": {
"format": "[Icon] 1,234 (+12.3/s)",
"numberAnimation": "Count-up 300ms when value changes",
"rateDisplay": "Per-second rate in smaller text, updates every second",
"largeNumbers": "K/M/B suffixes",
"color": "Match accent color for currency",
"earnedFlash": "Glow/pulse when currency earned from defeating enemies or completing waves"
},
"milestoneToast": {
"appearance": "Slide in from right/top",
"duration": "3000-5000ms visible",
"stack": "Multiple toasts stack vertically with 40px gap"
},
"prestigePanel": {
"layout": "Prestige title / You will earn / Current total / Resets / Keeps / Visual change / CANCEL / PRESTIGE!",
"available": "Button grayed out at Wave 12, clickable at Wave 15",
"accept": "Celebration effect, transitions to white, loads new run with visual changes",
"cancel": "Closes modal, returns to game"
},
"skillTree": {
"layout": "CSS Grid, nodes positioned absolutely in grid cells",
"nodeSize": "80px x 80px",
"connectionLines": "CSS borders on grid cells, 2px solid lines",
"nodeStates": {
"available": "White background, colored border by element, full opacity",
"maxed": "Gray background, desaturated border, MAX text",
"locked": "30% opacity, dashed gray border, no details visible",
"selected": "Pulsing glow effect with scale 1.05x animation"
}
},
"tooltip": {
"trigger": "Hover (desktop)",
"position": "20px above element, clamped to viewport",
"delay": "200ms hover before showing",
"content": "[Upgrade Name] (Level X) / [Full description] / Current: +Y damage / Next level: +Z damage / Cost: [amount]"
}
},
"controlsPanel": {
"position": "Bottom center of canvas, just above bottom panel",
"alwaysVisible": true,
"layout": "Single row, semi-transparent dark background (#141630 95% opacity)",
"content": [
{
"action": "clickRuneSlot",
"label": "Click Rune Slot",
"hotkey": "1-5",
"description": "Cycle element"
},
{
"action": "castSpell",
"label": "SPACE / Click CAST",
"hotkey": "SPACE",
"description": "Launch spell"
},
{
"action": "clickArena",
"label": "Click Arena",
"hotkey": "L-Click",
"description": "Set target"
},
{
"action": "clickCollectible",
"label": "Click Resource",
"hotkey": "none",
"description": "Collect drops"
},
{
"action": "clickOverdrive",
"label": "Click Overdrive",
"hotkey": "O",
"description": "Activate buff (when full)"
}
]
},
"tutorialOverlay": {
"trigger": "Shows automatically on first page load",
"dismiss": "Got it! button OR click anywhere outside",
"persistence": "localStorage to remember dismissal",
"content": [
"Click rune slots to craft your spell - Combine elements (Fire, Ice, Arcane, Void)",
"Press SPACE to launch spell at enemies - Stop them from reaching your rune circle",
"Click dropped resources to collect them - Use Mana to cast spells, Iron to buy upgrades",
"Discover spell combinations to unlock upgrades - Survive waves to unlock prestige"
],
"style": "Semi-transparent dark overlay (#1a1a2e 90% opacity), centered modal, large white text (18px), prominent dismiss button",
"zIndex": "Above everything including canvas"
},
"responsive": {
"minimumWidth": 1280,
"maximumWidth": "100% viewport",
"scaling": "Canvas scales to fit available width, bottom panel uses rem-based sizing",
"collapseBehavior": "Bottom panel can collapse to give Canvas more space, defaults to collapsed on narrow viewports (<600px height)"
},
"accessibility": {
"contrastRatios": "All text meets WCAG AA minimum 4.5:1",
"focusIndicators": "Visible focus outlines for keyboard navigation (2px solid outline)",
"screenReader": "Currency amounts have aria-live regions for updates",
"reducedMotion": "Respect prefers-reduced-motion media query - disable Canvas animations, use static sprites"
},
"settings": {
"notationFormat": {
"type": "Toggle",
"default": "Standard",
"options": [
"Standard",
"Scientific"
],
"effect": "Changes number display"
},
"animationSpeed": {
"type": "Slider",
"default": "1x",
"range": "0.5x-2x",
"effect": "Scales all animation durations and Canvas game speed"
},
"autoSaveInterval": {
"type": "Dropdown",
"default": "30s",
"options": [
"15s",
"30s",
"60s",
"off"
],
"effect": "How often game state saves"
},
"canvasQuality": {
"type": "Toggle",
"default": "High",
"options": [
"High",
"Low"
],
"effect": "Low disables glow effects and particles for performance"
},
"hardReset": {
"type": "Button",
"confirmation": true,
"effect": "Wipe all data (cannot undo)"
},
"exportSave": {
"type": "Button",
"effect": "Copy save string to clipboard"
},
"importSave": {
"type": "Button",
"effect": "Paste save string from clipboard"
}
}
},
"config": {
"canvas": {
"width": null,
"height": null,
"backgroundColor": "#1a1a2e",
"gridCellSize": 16,
"layers": [
"background",
"entities",
"effects",
"hudOverlay"
]
},
"ui": {
"tickRate": 20,
"autoSaveInterval": 30000,
"theme": "dark",
"hudHeight": 50,
"bottomPanelHeight": 180,
"bottomPanelCollapsedHeight": 40,
"canvasMinHeight": 400,
"tabs": [
{
"id": "runes",
"label": "RUNES",
"icon": "✨",
"unlockedAt": "gameStart"
},
{
"id": "upgrades",
"label": "UPGRADES",
"icon": "⚡",
"unlockedAt": "waveComplete >= 3"
},
{
"id": "mastery",
"label": "MASTERY",
"icon": "⛇",
"unlockedAt": "waveComplete >= 3"
},
{
"id": "prestige",
"label": "PRESTIGE",
"icon": "✨",
"unlockedAt": "waveComplete >= 12"
}
],
"notifications": {
"toastDuration": 3000,
"toastPosition": "top-right",
"milestoneDisplayDuration": 5000,
"stackLimit": 3
},
"controls": [
{
"action": "clickRuneSlot",
"label": "Click Rune Slot",
"hotkey": "1-5",
"description": "Cycle element"
},
{
"action": "castSpell",
"label": "SPACE / Click CAST",
"hotkey": "SPACE",
"description": "Launch spell"
},
{
"action": "clickArena",
"label": "Click Arena",
"hotkey": "L-Click",
"description": "Set target"
},
{
"action": "clickCollectible",
"label": "Click Resource",
"hotkey": "none",
"description": "Collect drops"
},
{
"action": "clickOverdrive",
"label": "Click Overdrive",
"hotkey": "O",
"description": "Activate buff (when full)"
}
]
},
"effects": {
"particles": {
"collection": {
"count": 5,
"sprite": "spark",
"lifetime": 500,
"spread": 20
},
"enemyDeath": {
"count": 5,
"sprite": "spark",
"lifetime": 400,
"spread": 15
},
"golemDeath": {
"count": 30,
"sprite": "spark",
"lifetime": 800,
"spread": 40
},
"championDeath": {
"count": 15,
"sprite": "spark",
"lifetime": 600,
"spread": 25
},
"spellcast": {
"count": 3,
"sprite": "spark",
"lifetime": 200,
"spread": 10
}
},
"floatingText": {
"duration": 800,
"riseSpeed": 30,
"fontSize": 14,
"fontWeight": "bold",
"colors": {
"mana": "#00ffff",
"ironShards": "#c0c0c0",
"crystalEssence": "#9932cc",
"damage": "#ff4444",
"heal": "#44ff44",
"xp": "#44aaff"
}
},
"screenFlash": {
"waveComplete": {
"color": "#ffffff",
"opacity": 0.3,
"duration": 200
},
"prestige": {
"color": "#ffffff",
"opacity": 1,
"duration": 500
},
"bossSpawn": {
"color": "#ff0000",
"opacity": 0.2,
"duration": 300
},
"damageTaken": {
"color": "#ff4444",
"opacity": 0.4,
"duration": 100
}
},
"placementFlash": {
"duration": 200,
"color": "#ffffff",
"opacity": 0.5
}
},
"colorPalette": {
"background": {
"primary": "#0f0f1a",
"secondary": "#1a1a2e",
"tertiary": "#2a2a4e"
},
"canvas": "#1a1a2e",
"text": {
"primary": "#e0e0e0",
"secondary": "#a0a0a0",
"muted": "#606060"
},
"accent1": "#00ffff",
"accent2": "#4FC3F7",
"accent3": "#E040FB",
"success": "#4CAF50",
"warning": "#FF9800",
"danger": "#F44336",
"prestige": "#E040FB"
}
}
}
Ui Ux
# Spellforge Arena - UI/UX Design
## Overview
Spellforge Arena is a mystical battleground where mages duel by crafting elemental spells in real-time. The visual identity is dark, arcane, and elemental - deep purples and midnight blues accented by glowing orange fire, cyan ice, violet arcane, and black void energies. The Canvas game world dominates the screen, showing the arena floor with a glowing rune circle at bottom center where the player assembles spells, enemies descending from above, and projectiles flying across the screen. The UI is minimal and secondary - a dark bottom panel for upgrades and a top HUD bar for currencies, never obscuring the primary Canvas action.
The color palette uses deep mystical blues (#1a1a2e) for the arena background, with elemental accents that shift based on the player's dominant element. Text is light-colored for readability (#e0e0e0) with muted secondary text (#a0a0a0) for less critical information. Primary currency (Mana) uses cyan accents, secondary currency (Iron Shards) uses metallic grays, and prestige currency (Crystal Essence) uses mystical purple - each color-coded for instant recognition.
## Color Palette
| Role | Color (hex) | Usage |
|------|-------------|-------|
| Background (primary) | #1a1a2e | Main background behind Canvas |
| Background (secondary) | #141630 | Panels, cards, bottom UI |
| Background (tertiary) | #2a2a4e | Disabled states, locked slots |
| Canvas background | #1a1a2e | The game world - dark mystical arena floor |
| Text (primary) | #e0e0e0 | Main text, labels, values |
| Text (secondary) | #a0a0a0 | Secondary labels, descriptions |
| Text (muted) | #606060 | Disabled buttons, unavailable items |
| Accent 1 (Mana) | #00ffff | Primary actions, Mana currency |
| Accent 2 (Iron) | #c0c0e0 | Secondary actions, Iron Shards currency |
| Accent 3 (Charge) | #ffcc00 | Tertiary elements, Elemental Charge |
| Success | #4caf50 | Affordable, completed, positive feedback |
| Warning | #ff9800 | Almost affordable, attention needed |
| Danger | #f44336 | Cannot afford, low mana warning |
| Prestige | #9932cc | Prestige currency, prestige UI elements |
| Spell Fire | #ff6b354 | Fire element - orange glow |
| Spell Ice | #00ffff | Ice element - cyan blue |
| Spell Arcane | #bf5eff | Arcane element - violet purple |
| Spell Void | #1a1a2e | Void element - black with white rim |
## Typography
- **Font family**: 'Cinzel', 'Segoe UI', Tahoma, sans-serif (system fonts)
- **Headings**: Bold 600-700, sizes: 24px (game title), 18px (section headers), 14px (card titles)
- **Body text**: Normal 400, size: 13px (descriptions, labels)
- **Numbers/currencies**: Normal 600, monospace option for aligned digits
- **Buttons**: Semibold 600, size: 14px
## Screen Layout
### Master Layout
```
+--------------------------------------------------+
| [HUD Bar - currencies, rates, wave/round info] |
+--------------------------------------------------+
| |
| [CANVAS - Main Game World] |
| (70% of screen height - ~450px at 720p) |
| Animated sprites, entities, effects, |
| rune circle at bottom, enemy spawn from top |
| |
+--------------------------------------------------+
| [Bottom Panel - rune slots, cast button, upgrade tabs] |
| (25% of screen height - ~180px at 720p) |
+--------------------------------------------------+
```
### HUD Bar (Always Visible, overlays top of Canvas)
| Element | Position | Content | Behavior |
|---------|----------|---------|----------|
| Mana display | Left | Icon 💧 + Current/Max + regen/sec | Cyan color, animates on change, shows "20/100" then counts up |
| Iron Shards display | Center-left | Icon ⚙️ + Amount | Silver-gray color, animates on change |
| Elemental Charge display | Center-right | Icon ⚡ + Current/Max | Yellow-orange color, fills visually, empties on overdrive |
| Wave counter | Center | Wave X / Next milestone | White text, updates on wave change |
| Elite indicator | Right-top | Rotating element icon (when Champion active) | Shows current enemy weakness |
| Settings button | Far right | Gear icon ⚙️ | Opens settings modal |
### Game World Canvas
- **Dimensions**: 100% of viewport width, flexible height (remaining space after HUD and bottom panel)
- **Background**: Solid color #1a1a2e (dark mystical blue), with subtle pentagram pattern overlay that pulses with current dominant element
- **Layers** (drawn in order):
1. Background layer (arena floor - stone texture with glowing pentagram lines)
2. Entity layer (enemies, projectiles, collectible sprites)
3. Effect layer (damage numbers, death particles, spell trails)
4. HUD overlay layer (health bars above entities, selection reticle, rune circle glow)
- **Camera**: Fixed top-down view. Arena is static, entities move within frame. No scrolling.
- **Click/touch interaction (CRITICAL)**:
- **Click rune slots** (bottom of Canvas): Cycles through elements (Fire → Ice → Arcane → Void)
- **Click CAST button** (bottom center): Launches crafted spell toward last clicked target
- **Click on arena**: Sets spell target location. Creates targeting reticle at clicked position
- **Click collectible sprites**: Collects dropped mana/iron/crystal sprites within radius
- **Click overdrive bar** (when full): Activates 3x damage buff for next spell
### Entity Visual Specs
For EVERY entity type from idea.md, provide a state machine diagram and a CONFIG entry.
#### Entity State Machine Template
Each entity type MUST have a state machine diagram showing all visual states and transitions:
```mermaid
stateDiagram-v2
[*] --> Spawning: created
Spawning --> Idle: spawn animation complete (300ms)
Idle --> Active: player/cursor interaction available
Active --> Dying: hp <= 0
Dying --> [*]: death animation complete (500ms)
note right of Spawning
Animation: scale 0→1
Duration: 300ms
Invulnerable: true
end note
note right of Idle
Frames: 0-1
Frame duration: 500ms
Facing: last direction
Animation: bob (gentle vertical movement)
end note
note right of Active
Multiple states possible based on entity type:
- Moving: Walking/flying toward target
- Attacking: Combat/ability in progress
- Dying: Playing death animation
end note
note right of Dying
Animation: fade opacity 1→0
Duration: 500ms
Spawns: death particles (spark sprite × 3-5)
On complete: remove from entity list
end note
```
Create one diagram per entity type (player units, enemy types, projectiles, collectibles). Customize states for each — e.g., structures don't have Moving, projectiles don't have Idle.
#### Entity Interaction Diagram
Show how entities interact with each other on Canvas:
```mermaid
flowchart LR
subgraph Player Entities
Spell["Spell Projectile\n(fires at enemies)"]
TargetReticle["Target Reticle\n(player click on arena)"]
end
subgraph Enemy Entities
Wisp["Wisp Enemy\n(moves to rune circle)"]
Guardian["Guardian Enemy\n(moves to rune circle)"]
Golem["Golem Boss\n(slow, high HP)"]
Champion["Elite Champion\n(moves to rune circle)"]
end
subgraph Collectibles
ManaFragment["Mana Fragment\n(+3 Mana)"]
IronShard["Iron Shard\n(+5 Iron)"]
CrystalEssence["Crystal Essence\n(+1 Essence)"]
ElementSprite["Element Sprite\n(+2 Charge)"]
end
Spell -->|"hits"| Wisp
Spell -->|"hits"| Guardian
Spell -->|"hits"| Golem
Spell -->|"hits"| Champion
Wisp -->|"reaches rune circle"| PlayerDamage
Guardian -->|"reaches rune circle"| PlayerDamage
Golem -->|"reaches rune circle"| PlayerDamage
Champion -->|"reaches rune circle"| PlayerDamage
TargetReticle -->|"follows mouse"| Spell
Wisp -.->|"on death → spawns"| ManaFragment
Guardian -.->|"on death → spawns"| IronShard
Golem -.->|"on death → spawns"| IronShard
Champion -.->|"on death → spawns"| IronShard
Golem -.->|"on death → spawns"| CrystalEssence
Champion -.->|"on death → spawns"| CrystalEssence
ElementSprite -.->|"periodic spawn"| Self
ManaFragment -->|"player clicks → collect"| Player
IronShard -->|"player clicks → collect"| Player
CrystalEssence -->|"player clicks → collect"| Player
ElementSprite -->|"player clicks → collect"| Player
```
### HUD Overlay (drawn ON Canvas)
| Element | Position | Content | Behavior |
|---------|----------|---------|----------|
| [Entity health bars] | Above each entity | Small colored bar | Updates with damage, fades on death (200ms fade) |
| [Floating damage numbers] | At hit location | "-15" in red/white | Floats upward 40px over 800ms |
| [Wave indicator] | Top center of canvas | "Wave 5 incoming!" | Appears 2s before wave, fades over 1s |
| [Target reticle] | Around clicked enemy | Dashed circle ring | Shows which enemy spell will home toward |
| [Rune slot highlights] | Around rune slots | Glow by element | Shows currently selected element in each slot |
| [Charge meter fill animation] | Above charge bar | Smooth fill | Animates from 0 to target value over 200ms |
| [Overdrive aura] | Around rune circle when active | Pulsing glow | 3x damage active - shows for 5s |
### Bottom Panel (Upgrade/Management Area)
```
+--------------------------------------------------+
| [Tab 1: RUNES] [Tab 2: UPGRADES] [Tab 3: MASTERY] [Tab 4: PRESTIGE] [COLLAPSE] |
+--------------------------------------------------+
| |
| [Active Tab Content Area] |
| - Rune slots display (3-5 slots shown horizontally) |
| - CAST button (large, centered) |
| - Current spell preview (shows combination) |
| - Upgrade cards, skill tree, or prestige panel |
| |
+--------------------------------------------------+
```
- **Height**: 180px at 720p viewport (25% of screen)
- **Collapse button**: Arrow icon ▲ at left edge to minimize panel (gives more Canvas space)
- **When collapsed**: Only tab bar visible (single row of icons/labels), panel height 40px
### Navigation / Tab Bar
| Tab | Icon/Label | Unlocked At | Content |
|-----|------------|-------------|---------|
| [RUNES] | 🌟 Runes | Game start | 3-5 rune slots, cast button, spell preview |
| [UPGRADES] | ⬆️ Upgrades | Wave 3 complete | Elemental masteries, spell damage, mana regen |
| [MASTERY] | 🔱 Mastery | Wave 3 complete | Fire, Ice, Arcane, Void mastery levels |
| [PRESTIGE] | ✨ Prestige | Wave 12 complete | Permanent upgrades, prestige button |
**Tab appearance animation**: New tab slides in from right over 300ms with fade-in + slight scale-up effect. Active tab gets brighter background.
### Tab Contents
#### Tab 1: RUNES
```
+---------------------------------------+
| [Rune Slot Display] |
| +----------------+--------+--------------+ |
| | [Slot 1] | [Slot 2] | [Slot 3] | [Slot 4] | [Slot 5] |
| +---+-------+---+-------+---+-------+---+ |
| | 🔥/❄️/✨/⚫ | Click to cycle | Fire | Ice | Arcane | Void | (locked) |
| +---+-------+---+-------+---+-------+---+ |
| | Current spell: Fire-Ice-Arcane (example) |
| +---------------------------------------+--------------+
| |
| +----------------+-------------------------+--------------+
| | [CAST BUTTON] (large, centered) |
| | SPACE or click to launch spell |
| | Target reticle shows last clicked arena location |
| +---------------------------------------+--------------+
| |
+---------------------------------------+
```
**Rune Slot states**:
- Available: Full opacity, bright border, clickable
- Locked: 50% opacity, dim border, lock icon overlay, hover shows "Unlock at Wave X"
- Selected: Glow animation (pulsing border), shows element icon
- Hover: Scale 1.05x, brightness +10%
#### Tab 2: UPGRADES
```
+---------------------------------------+
| [Upgrade Cards - 2 columns, scrollable] |
| +--------+--------+--------------+ |
| | [Fire Mastery] | [Ice Mastery] | [Arcane Mastery] | [Void Mastery] |
| +---+-----------+------+---------+------+---------+---+
| | Level: 3/10 | Level: 0/10 | Level: 2/10 | Level: 1/10 | (locked) |
| | +50% dmg | +50% dmg | +50% dmg | +50% dmg |
| | Cost: 86 Iron | Cost: 30 Iron | Cost: 30 Iron | (locked) |
| | [BUY] | [BUY] | [BUY] | [MAX] | [BUY] |
| +---+-----------+------+---------+------+---------+---+
| | [Spell Damage] | [Mana Mastery] | [Rune Slot 4] | [Rune Slot 5] |
| | Level: 2/15 | Level: 5/5 | (locked) | (locked) |
| | +10% all | +1 regen/s | Cost: 100 Iron | Cost: 500 Iron |
| | [BUY] | [BUY] | [LOCK] | [LOCK] | [BUY] |
| +---------------------------------------+--------------+
| |
+---------------------------------------+
```
**Upgrade Card states**:
- Available: Full opacity, green border (#4caf50), BUY button enabled
- Cannot afford: Full opacity, red border (#f44336), BUY button disabled with shake animation on click
- Maxed: Full opacity, gray border (#606060), BUY button hidden, MAX badge
- Locked: 50% opacity, dim border (#2a2a4e), lock icon, details hidden
#### Tab 3: MASTERY
Same layout as UPGRADES but shows elemental mastery levels with different effects:
- Fire Mastery: +10% Fire damage per level, makes fire-themed spell projectiles larger with longer trails
- Ice Mastery: +10% Ice damage per level, makes ice spells freeze enemies (slow effect)
- Arcane Mastery: +10% Arcane damage per level, makes arcane spells home toward targets
- Void Mastery: +10% Void damage per level, makes void spells penetrate (full damage to all enemies in line)
#### Tab 4: PRESTIGE
```
+---------------------------------------+
| [PRESTIGE PANEL] |
| +----------------+-----------------------------+ |
| | You will earn: +XX Crystal Essence |
| | Current total: YY Crystal Essence |
| | |
| | RESETS: |
| | - [item 1] |
| | - [item 2] |
| | - [item 3] |
| | - [item 4] |
| | |
| | KEEPS: |
| | - [permanent rune slot +1] |
| | - [elemental attunement +10%] |
| | - [mana cap +20] |
| | |
| | [VISUAL CHANGE:] |
| | - Arena becomes more ornate |
| | - Enemies get hue shift +30 |
| | |
| | [CANCEL] [PRESTIGE!] |
+---------------------------------------+
```
Prestige panel appears as modal overlay, dims background behind it.
### Notifications Area
| Element | Content | Behavior |
|---------|---------|----------|
| [Notification area] | Toast messages | Slide in from right, fade after 3s |
| [Milestone popup] | Achievement earned | Center screen, dramatic, auto-dismiss 3s |
## Component Specifications
### Upgrade Card
```
+----------------------------------+
| [Icon: 🔥] [Upgrade Name] [Lvl X] |
+----------------------------------+
| [Effect: +50% Fire damage] |
| [Cost: 86 Iron] [BUY] |
+----------------------------------+
```
- **Affordable state**: Border #4caf50 (success green), button background same, BUY button text "BUY" in white
- **Cannot afford state**: Border #f44336 (danger red), button background #2a2a4e (disabled), BUY button text grayed out
- **Maxed state**: Border #606060 (muted gray), "MAX" badge red on card, BUY button hidden
- **Locked state**: Entire card dimmed to 50% opacity, "🔒" or "?" icon instead of details, no BUY button
- **Hover (affordable)**: Scale 1.05x over 200ms, subtle shadow
- **Click (affordable)**: Purchase animation over 200ms, card flashes white, currency counters tick down
### Currency Display (HUD)
```
[💧] 1,234 (+12.3/s)
```
- **Number animation**: Count-up animation when value changes, duration 300ms
- **Rate display**: Per-second rate in smaller gray text below main number
- **Large numbers**: When switching to K/M/B suffixes, animate the transition
- **Color**: Match accent color for this currency (Mana = cyan)
- **Earned by gameplay flash**: Brief glow/pulse on currency icon when earned from killing enemies or completing waves, distinct from passive regen ticks
### Milestone/Achievement Toast
```
+----------------------------------+
| ★ [Achievement Name]! |
+----------------------------------+
| [Description text] |
| [+Reward] |
+----------------------------------+
```
- **Appearance**: Slide in from right edge over 300ms, stays for 3s, slides out
- **Duration**: 3000ms visible
- **Stack behavior**: Multiple toasts stack vertically with 40px gap between them
- **Colors**: Success = green border, Achievement = gold border
### Prestige Panel
[Layout for prestige confirmation screen]
```
+----------------------------------+
| ⟳ [PRESTIGE TITLE] |
| |
| You will earn: [+XX Crystal Essence] |
| Current total: [YY Crystal Essence] |
| |
| RESETS: |
| - [item 1] [item 2] |
| - [item 3] [item 4] |
| - [item 5] [item 6] |
| |
| VISUAL CHANGE: |
| - Arena background shifts to new tier |
| - Enemies get hue shift based on tier |
| |
| [CANCEL] [PRESTIGE!] |
+----------------------------------+
```
- **Prestige available**: Button appears grayed out at Wave 12, becomes clickable at Wave 15
- **Accept action**: Shows celebration effect, transitions to white, loads new run with visual changes
- **Cancel action**: Closes modal, returns to game
### Skill Tree Renderer
- **Layout approach**: CSS Grid using display:grid, nodes positioned absolutely within grid cells
- **Node size**: 80px × 80px per node
- **Connection lines**: CSS borders on grid cells, 2px solid lines connecting related nodes
- **Node states**:
- Available: White background, colored border matching element type, full opacity
- Maxed: Gray background, desaturated border, "MAX" text
- Locked: 30% opacity, dashed gray border, no details visible
- Selected: Pulsing glow effect with scale 1.05x animation
### Tooltip
```
+----------------------------------+
| [Upgrade Name] (Level X) |
+----------------------------------+
| [Full description] |
| Current: +Y damage |
| Next level: +Z damage |
| Cost: [amount] |
+----------------------------------+
```
- **Trigger**: Hover (desktop)
- **Position**: 20px above element, centered horizontally, clamped to viewport edges
- **Delay**: 200ms hover before showing
- **Style**: Semi-transparent dark background (#1a1a2e with 90% opacity), white text, 12px font
## UI Event Flow Diagrams
### Player Interaction → Canvas → UI Flow
```mermaid
sequenceDiagram
actor Player
participant Canvas as Canvas
participant RuneSystem as RuneSystem
participant EventBus as EventBus
participant UI as HUD/Panels
Note over Player,UI: Player clicks rune slot to change element
Player->>Canvas: click rune slot at (x, y)
Canvas->>RuneSystem: runeSlotClicked(slotIndex)
RuneSystem->>RuneSystem: cycleElement(slotIndex)
RuneSystem->>EventBus: emit('rune-changed', {slotIndex, newElement})
EventBus->>Canvas: updateSlotVisual(slotIndex, newElement)
Canvas->>Canvas: slot shows glow animation (200ms)
EventBus->>UI: updateSpellPreview()
Note over Player,UI: Player clicks arena to set target
Player->>Canvas: click arena at (targetX, targetY)
Canvas->>EventBus: emit('target-set', {x, y})
EventBus->>Canvas: showTargetReticle(targetX, targetY)
Canvas->>Canvas: create reticle sprite at target, pulse animation
Note over Player,UI: Player presses SPACE to cast spell
Player->>RuneSystem: pressSpace() or clickCastButton()
RuneSystem->>EventBus: emit('spell-cast-request')
EventBus->>Canvas: validateSpellComposition()
alt Invalid composition
EventBus->>UI: showInvalidWarning()
EventBus->>Canvas: shakeRuneCircle()
end
EventBus->>EventBus: emit('spell-cast', {elements, target})
EventBus->>Canvas: spawnProjectile(elements, target)
Canvas->>Canvas: projectile spawns from rune circle with animation
EventBus->>UI: deductMana(spellCost)
EventBus->>UI: floatingText("-{cost}💧", runeCircle, cyan)
```
```mermaid
sequenceDiagram
actor Player
participant Canvas as Canvas
participant EntitySystem as EntitySystem
participant EventBus as EventBus
participant UI as HUD/Panels
Note over Player,UI: Enemy dies → drops → player collects
EntitySystem->>EntitySystem: enemy.hp <= 0
EntitySystem->>Canvas: death animation (fade + spark particles)
EntitySystem->>EventBus: emit('enemy-killed', {type, position, reward})
EventBus->>Canvas: spawnCollectible({type, position, value})
Canvas->>Canvas: collectible bob animation
Player->>Canvas: clicks collectible (within radius)
Canvas->>EventBus: emit('collectible-collected', {type, value, position})
EventBus->>UI: floatingText("+{value}{icon}", position, color)
EventBus->>UI: currency count-up animation (300ms)
EventBus->>Canvas: removeCollectible()
EventBus->>UI: gameplay-earned flash/pulse on currency display
```
## UI State Machine
```mermaid
stateDiagram-v2
[*] --> Loading: page load
Loading --> Playing: assets loaded, tutorial dismissed
state Playing {
[*] --> PreWave: between waves
PreWave --> WaveActive: countdown complete (3s)
WaveActive --> PreWave: wave cleared
WaveActive --> GameOver: player died
state PreWave {
[*] --> Combat: player can interact
Combat --> [*]: Wave spawn starts
[*] --> Idle: no active targets
}
Playing --> Prestige: player clicks prestige
Prestige --> Playing: new run starts
Playing --> SettingsModal: gear icon clicked
SettingsModal --> Playing: modal closed
GameOver --> Playing: restart
}
note right of PreWave
UI: "Wave {n} incoming!" (3s countdown)
Canvas: spawn indicators pulse at top
Player: can interact with rune slots, no spells
end note
note right of WaveActive
Enemies spawning/moving
Player casts spells at enemies
Collectibles spawn and can be clicked
Charge meter builds from spell chains
end note
note right of GameOver
Canvas: red overlay "DEFEATED"
UI: "Retry?" button
End note
```
## Feedback Systems
### Visual Feedback Catalog
| Action | Canvas Feedback | UI Feedback | Duration |
|--------|----------------|-------------|----------|
| Enemy defeated | Death animation (fade + spark particles), floating "+5 ⚙️" | Sprite fades over 300ms, particles burst (5 sprites, 400ms) | 300ms |
| Wave completed | Brief screen flash, all enemies gone | Toast "Wave X Complete!" +3s, wave counter increments | 1s |
| Purchase upgrade | Upgraded entity glows briefly on Canvas (if applicable) | Card flashes green, cost deducted animation | 300ms |
| Cannot afford click | N/A | Card shakes, cost flashes red | 200ms |
| New unlock | N/A | Tab glow/pulse, notification dot, toast appears | Until clicked |
| Prestige | Canvas transition effect (fade to white, new world fades in) | Screen flash, transition animation | 1-2s |
| Spell cast | Projectile spawns from rune circle with trail | Mana deducts, floating "-X 💧" | 200ms |
| Collectible collected | Sprite scales up and disappears, particles burst | Currency counter pulses up | 300ms |
| Entity takes damage | Sprite flashes white briefly | N/A | 200ms |
| Overdrive activated | Rune circle gets intense elemental glow | Charge bar empties with burst | 500ms |
### Number Formatting Rules
| Range | Format | Example |
|-------|--------|---------|
| 0 - 999 | Whole number | 742 |
| 1,000 - 999,999 | With commas | 1,234 |
| 1M - 999.9M | Suffix | 1.5M |
| 1B - 999.9B | Suffix | 42.3B |
| 1T+ | Suffix | 1.2T |
| Rates (/sec) | 1 decimal | +12.3/s |
| Damage numbers (Canvas) | Whole number | -15 |
### Progress Bars
- **Style**: Rounded corners (6px border-radius), smooth fill animation (transition: background-color 0.3s ease-out), no label
- **Color**: Changes based on percentage (green > 50%, yellow 30-50%, red < 30%)
- **Label**: Percentage only (no "current/max" text)
## Controls Panel (MANDATORY - Always Visible)
The game MUST have a permanently visible controls panel showing all player interactions. This is NOT optional.
### Controls Panel Layout
```
+--------------------------------------------------+
| CONTROLS: [Click Rune Slot] | [SPACE Cast] | [Click Target] |
+--------------------------------------------------+
```
- **Position**: Bottom center of canvas, just above bottom panel (or integrated into bottom panel top edge)
- **Always visible**: Never hidden behind a tab or menu
- **Content**: List every player action with its trigger
- **Style**: Compact single-row, semi-transparent dark background (#141630 with 95% opacity), light white text, monospace for hotkeys
- **Responsive**: If space is tight, use icon + hotkey shorthand (e.g., "🌟 Click | ⚡ Cast")
### Controls Panel Content for Spellforge Arena:
| Action | Trigger | Description |
|--------|----------|------------|
| Click Rune Slot | Click on rune slot at bottom of Canvas | Cycles through Fire🔥/Ice❄️/Arcane✨/Void⚫ elements |
| SPACE / Click CAST | Press SPACE or click CAST button | Launches crafted spell toward target |
| Click Arena | Click anywhere on arena (Canvas) | Sets spell target location, shows targeting reticle |
| Click Resource | Click Mana/Iron/Crystal sprites on Canvas | Collects dropped resources |
| Click Overdrive Bar | Click charge bar when full (⚡ 50+) | Activates 3x damage buff for next spell |
## Getting Started Overlay (MANDATORY - Shows on First Load)
The game MUST show a "How to Play" overlay on first load. This appears ONCE, centered on screen, over Canvas.
### Tutorial Overlay Layout
```
+---------------------------------------+
| HOW TO PLAY |
| |
| • Click rune slots to craft your spell |
| • Combine elements (Fire, Ice, Arcane, Void) |
| • Press SPACE to launch spell at enemies |
| • Stop enemies from reaching your rune circle |
| • Click dropped resources to collect them |
| • Discover spell combinations to unlock upgrades |
| |
| [GOT IT!] |
+---------------------------------------+
```
- **Trigger**: Shows automatically on first page load
- **Dismiss**: "GOT IT!" button OR click anywhere outside
- **Persistence**: Use localStorage to remember dismissal. Don't show again after first dismiss
- **Content**: 4 concise bullet points
- **Style**: Semi-transparent dark overlay (#1a1a2e with 90% opacity), centered modal, large readable white text (18px), prominent dismiss button
- **Z-index**: Above everything including Canvas
## Responsive Considerations
- **Minimum width**: 1280px
- **Maximum width**: 100% of viewport
- **Scaling approach**: Canvas scales to fit available width; bottom panel uses rem-based sizing (1rem = 16px at default)
- **Panel collapse behavior**: Bottom panel can be collapsed to give Canvas more space. On narrow viewports (< 600px height), defaults to collapsed.
## Accessibility
- **Contrast ratios**: All text meets WCAG AA minimum 4.5:1 (light text on dark backgrounds)
- **Focus indicators**: Visible focus outlines for keyboard navigation (2px solid outline)
- **Screen reader**: Currency amounts have aria-live regions for updates (e.g., <span aria-live="polite">20</span>)
- **Reduced motion**: Respect prefers-reduced-motion media query - disable Canvas animations, use static sprites
## Settings Panel
| Setting | Type | Default | Effect |
|---------|------|---------|--------|
| [Notation format] | Toggle | Standard | Standard | Changes number display (1,234 vs 1.2k) |
| [Animation speed] | Slider (0.5x-2x) | 1x | Scales all animation durations and Canvas game speed |
| [Auto-save interval] | Dropdown | 30s | How often game state saves (15s, 30s, 60s, off) |
| [Canvas quality] | Toggle | High | High | Low disables glow effects and particles for performance |
| [Hard reset] | Button (with confirmation) | N/A | Wipes all data (cannot undo) |
| [Export save] | Button | N/A | Copies save string to clipboard (base64) |
| [Import save] | Button | N/A | Pastes save string from clipboard |
## CONFIG Spec: canvas, ui, and effects Sections
```javascript
// Canvas and UI Configuration for Spellforge Arena
CONFIG.canvas = {
width: null, // Null = 100% of viewport
height: null, // Calculated: viewportHeight - hudHeight - bottomPanelHeight
backgroundColor: '#1a1a2e', // Dark mystical blue
gridCellSize: 16, // For reference (not used in spellforge gameplay)
layers: ['background', 'entities', 'effects', 'hudOverlay'],
};
CONFIG.ui = {
tickRate: 20, // UI updates per second
autoSaveInterval: 30000, // ms
theme: 'dark',
hudHeight: 50, // px
bottomPanelHeight: 180, // px at 720p viewport (25%)
bottomPanelCollapsedHeight: 40, // px when collapsed (just tab bar)
canvasMinHeight: 400, // px minimum canvas height
tabs: [
{ id: 'runes', label: 'RUNES', icon: '🌟', unlockedAt: 'gameStart' },
{ id: 'upgrades', label: 'UPGRADES', icon: '⬆️', unlockedAt: 'waveComplete >= 3' },
{ id: 'mastery', label: 'MASTERY', icon: '🔱', unlockedAt: 'waveComplete >= 3' },
{ id: 'prestige', label: 'PRESTIGE', icon: '✨', unlockedAt: 'waveComplete >= 12' },
],
notifications: {
toastDuration: 3000, // ms
toastPosition: 'top-right',
milestoneDisplayDuration: 5000, // ms
stackLimit: 3, // max simultaneous toasts
},
controls: [
{ action: 'clickRuneSlot', label: 'Click Rune Slot', hotkey: '1-5', description: 'Cycle element' },
{ action: 'castSpell', label: 'SPACE / Click CAST', hotkey: 'SPACE', description: 'Launch spell' },
{ action: 'clickArena', label: 'Click Arena', hotkey: 'L-Click', description: 'Set target' },
{ action: 'clickCollectible', label: 'Click Resource', hotkey: 'none', description: 'Collect drops' },
{ action: 'clickOverdrive', label: 'Click Overdrive', hotkey: 'O', description: 'Activate buff (when full)' },
],
};
CONFIG.effects = {
particles: {
collection: { count: 5, sprite: 'spark', lifetime: 500, spread: 20 },
enemyDeath: { count: 5, sprite: 'spark', lifetime: 400, spread: 15 },
golemDeath: { count: 30, sprite: 'spark', lifetime: 800, spread: 40 },
championDeath: { count: 15, sprite: 'spark', lifetime: 600, spread: 25 },
spellcast: { count: 3, sprite: 'spark', lifetime: 200, spread: 10 },
},
floatingText: {
duration: 800, // ms
riseSpeed: 30, // px/sec
fontSize: 14,
fontWeight: 'bold',
colors: {
mana: '#00ffff', // Cyan
ironShards: '#c0c0e0', // Silver-gray
crystalEssence: '#9932cc', // Purple
damage: '#ff4444', // Red
heal: '#44ff44', // Green
xp: '#44aaff', // Yellow
},
},
screenFlash: {
waveComplete: { color: '#ffffff', opacity: 0.3, duration: 200 },
prestige: { color: '#ffffff', opacity: 1.0, duration: 500 },
bossSpawn: { color: '#ff0000', opacity: 0.2, duration: 300 },
damageTaken: { color: '#ff4444', opacity: 0.4, duration: 100 },
},
placementFlash: { duration: 200, color: '#ffffff', opacity: 0.5 },
};
CONFIG.colorPalette = {
background: { primary: '#1a1a2e', secondary: '#141630', tertiary: '#2a2a4e' },
canvas: '#1a1a2e',
text: { primary: '#e0e0e0', secondary: '#a0a0a0', muted: '#606060' },
accent1: '#00ffff', // Mana - cyan
accent2: '#c0c0e0', // Iron Shards - silver
accent3: '#ffcc00', // Elemental Charge - yellow
success: '#4caf50',
warning: '#ff9800',
danger: '#f44336',
prestige: '#9932cc',
spellFire: '#ff6b354',
spellIce: '#00ffff',
spellArcane: '#bf5eff',
spellVoid: '#1a1a2e',
};
```
## Engagement Self-Audit (MANDATORY)
Before finalizing your design, conduct this engagement audit:
### Is This Actually A Game? (MOST CRITICAL)
- ✅ Can you describe 30 seconds of gameplay without mentioning numbers, currencies, or upgrades?
- YES: Player clicks rune slots cycling through Fire/Ice/Arcane elements, watches spell projectiles fly toward enemies, sees impact effects, clicks collectible sprites - all visual gameplay
- ✅ Does player interact with Canvas directly (clicking, placing, directing) at least every 10 seconds?
- YES: Player constantly clicking rune slots (every 2-3 seconds to assemble spell), clicking arena to set target (every 3-5 seconds), or clicking collectibles (continuously)
- ✅ If you removed all upgrade panels, is there still a recognizable game on Canvas?
- YES: Even without upgrades, the core rune-crafting spell-casting gameplay is visually engaging - projectiles flying, enemies moving, explosions happening
- ✅ Would a bystander watching over player's shoulder understand what's happening?
- YES: Clear visual language - colored projectiles (orange=fire, blue=ice, purple=arcane), distinct enemy types (ghost=wisp, knight=guardian), glowing rune circle at bottom
Answer: **YES, this is a real game with Canvas-based visual gameplay.**
### Feedback Loop Completeness
| Action | Canvas Feedback (required) | UI Feedback (required) |
|--------|---------------------------|----------------------|
| Cast spell | Projectile spawns with trail effect | Mana counter animates down with floating "-X 💧" |
| Enemy dies | Death animation + particles | Currency sprite drops, counter pulses up |
| Buy upgrade | N/A (spell upgrades don't affect Canvas entities directly) | Card flashes green, cost deducted |
| Wave complete | Screen flash, wave counter increments | Toast appears |
| Collect resource | Sprite scales up and disappears | Currency counter pulses up |
| Cannot afford | N/A | Card shakes, cost flashes red |
| New unlock | Tab glows/pulses | Toast notification |
All major actions have BOTH Canvas and UI feedback where applicable.
### Reward Visibility Check
At any moment, player should see 2-3 things they're working toward:
- ✅ Next affordable upgrade (cost visible, progress bar on card)
- ✅ Next unlock threshold (e.g., "Unlock at Wave 5" shown on locked cards)
- ✅ Current production rate (Mana regen shown as "+12.3/s" in HUD)
- ✅ Wave progress (Wave X of Y shown in HUD)
### Variable Reward Check
- ✅ Wave completion rewards vary by wave number (10×wave Mana)
- ✅ Milestones give diverse rewards (new spell combinations, rune slots, elemental charge mechanics)
- ✅ First 3 waves have guaranteed unlocks to keep progression flowing
## Entity State Machines
### Spell Projectile
```mermaid
stateDiagram-v2
[*] --> Spawning: spell cast
Spawning --> Flying: launch from rune circle (100ms)
Flying --> Impact: collision with enemy
Flying --> Expired: lifetime exceeded (2s timeout)
Impact --> [*]: deal damage, spawn particles, remove
note right of Spawning
Animation: scale 0→1 from rune circle
Duration: 100ms
Color: based on elemental composition
end note
note right of Flying
Speed: 300px/sec (adjusted by elements)
Trail: particle effect based on element
Rotation: Fire/Arcane = rotate, Ice/Void = none
end note
note right of Impact
Triggers: enemy.takeDamage()
Spawns: 5 spark particles
Visual: burst effect at impact point
end note
note right of Expired
Fades out: opacity 1→0 over 500ms
On complete: remove from entity list
end note
```
### Enemy: Wisp
```mermaid
stateDiagram-v2
[*] --> Spawning: wave spawn trigger
Spawning --> Active: fade in (200ms), spawn animation complete
Active --> Moving: toward rune circle (zigzag pattern)
Moving --> Dying: hp <= 0
Dying --> [*]: death complete (300ms)
note right of Active
Sprite: ghost (cyan/white)
Animation: 4 frames, 200ms per frame
Movement: zigzag toward bottom
Speed: 80px/sec (fast)
HP: 10
end note
note right of Dying
Animation: fade opacity 1→0
Duration: 300ms
Spawns: mana fragment collectible
On complete: remove from entity list
end note
```
### Enemy: Guardian
```mermaid
stateDiagram-v2
[*] --> Spawning: wave spawn trigger
Spawning --> Active: scale in (250ms), spawn animation complete
Active --> Moving: toward rune circle (straight line)
Moving --> Fighting: in spell range
Fighting --> Moving: target dead/out of range
Moving --> Dying: hp <= 0
Dying --> [*]: death complete (400ms)
note right of Active
Sprite: knight (steel gray armor)
Animation: 4 frames, 200ms per frame
Movement: straight toward bottom
Speed: 50px/sec (medium)
HP: 30
end note
note right of Fighting
Triggers: attack animation (frame 2-3)
Spawns: projectile every 500ms
end note
note right of Dying
Animation: fade + metallic flash
Duration: 400ms
Spawns: iron shard collectible (guaranteed)
On complete: remove from entity list
end note
```
### Enemy: Golem (Boss)
```mermaid
stateDiagram-v2
[*] --> Spawning: wave 5 boss trigger
Spawning --> Teased: 3s countdown, "⚠️ BOSS INCOMING" toast
Teased --> Active: dramatic spawn (500ms) + screen flash
Active --> Moving: slow march toward rune circle
Active --> Dying: hp <= 0
Dying --> [*]: death complete (800ms)
note right of Teased
UI: "⚠️ BOSS INCOMING" toast for 3s
Canvas: spawn indicators pulse at top
end note
note right of Active
Sprite: slime (brown/gray, scale 2.0x)
Animation: 2 frames, 400ms per frame (very slow)
Movement: straight toward bottom
Speed: 20px/sec (very slow)
HP: 150 (massive)
Scale: 2.0x (48×48px)
end note
note right of Dying
Animation: fade opacity 1→0
Duration: 800ms
Spawns: iron shard collectibles (3× guaranteed) + crystal essence (30%)
Screen shake: 5px over 100ms
On complete: wave complete check
end note
```
### Enemy: Elite Champion
```mermaid
stateDiagram-v2
[*] --> Spawning: wave 7+ champion trigger
Spawning --> Teased: 5s countdown
Teased --> Active: teleport in (400ms) + purple flash
Active --> Moving: purposeful walk toward rune circle
Active --> Casting: special ability every 5s
Casting --> Moving: ability complete
Active --> Dying: hp <= 0
Dying --> [*]: death complete (600ms)
note right of Teased
UI: "Champion approaching" toast
Canvas: warning pulse
end note
note right of Active
Sprite: wizard (red/purple, scale 1.2x)
Animation: 4 frames, 200ms per frame
Movement: straight toward bottom
Speed: 60px/sec (fast)
HP: 80
Scale: 1.2x (38×38px)
Has health bar: 40px wide, 4px tall, -8px y-offset
end note
note right of Casting
Ability 1: Summon 2 Wisps at top
Ability 2: Fire counter-spell toward player
end note
note right of Dying
Animation: fade with energy release
Duration: 600ms
Spawns: iron shard collectibles (4× guaranteed) + crystal essence (60%)
On complete: wave advancement check
end note
```
### Collectible: Mana Fragment
```mermaid
stateDiagram-v2
[*] --> Spawning: enemy death
Spawning --> Active: spawn animation (200ms)
Active --> Collected: player clicks within 20px
Active --> Expired: lifetime 8s exceeded
Active --> Attracting: magnetism unlocked, within 100px
Collected --> [*]: add Mana + floating "+3"
Expired --> [*]: fade out (300ms)
Attracting --> Collected: reaches player position
Attracting --> Expired: lifetime exceeded
note right of Spawning
Sprite: spark (cyan, scale 1.2)
Position: at enemy death location
Animation: scale 0→1 (200ms)
Spawn: with burst effect (5 particles)
end note
note right of Active
Visual: cyan bob animation, glow
Clickable radius: 20px
Shows "+3" on hover
Drifts slowly downward (10px/sec)
Sprite: spark (cyan, scale 1.2)
Magnetism: when active, floats toward player at 150px/sec
end note
note right of Collected
Triggers: currency.add('mana', 3)
Triggers: floatingText("+3💧", cyan)
Triggers: particle burst (5 cyan sparks)
Triggers: ui.update('mana')
Sound: soft magical chime
end note
note right of Expired
Fade out: opacity 1→0 over 300ms
No sound
Remove from entity list
end note
```
### Collectible: Iron Shard
```mermaid
stateDiagram-v2
[*] --> Spawning: enemy death
Spawning --> Active: spawn animation (250ms) with metallic flash
Active --> Collected: player clicks within 22px
Active --> Expired: lifetime 10s exceeded
Active --> Attracting: magnetism unlocked, within 120px
Collected --> [*]: add Iron Shards + floating "+X"
Expired --> [*]: fade out (400ms)
Attracting --> Collected: reaches player position
Attracting --> Expired: lifetime exceeded
note right of Spawning
Sprite: spark (gray, scale 1.5)
Position: at enemy death location
Animation: scale 0→1 (250ms)
Spawn: with metallic flash effect
Value: shows 5/15/20 on hover
Sprite: spark (gray, scale 1.5 for small, 2.0 for large)
end note
note right of Active
Visual: gray crystal bob, metallic sheen
Clickable radius: 22px
Shows value on hover
Drifts slowly downward (8px/sec)
Magnetism: when active, floats toward player at 180px/sec
end note
note right of Collected
Triggers: currency.add('ironShards', value)
Triggers: floatingText("+{value}⚙️", silver)
Triggers: particle burst (7 gray crystals)
Triggers: ui.update('ironShards')
Sound: metallic clink
end note
```
### Collectible: Crystal Essence
```mermaid
stateDiagram-v2
[*] --> Spawning: golem/champion death (rare)
Spawning --> Active: spawn animation (400ms) with purple flash
Active --> Collected: player clicks within 25px (generous)
Active --> Expired: lifetime 15s exceeded
Active --> Attracting: magnetism always active, within 150px
Collected --> [*]: add Crystal Essence + celebration
Expired --> [*]: fade out (500ms) with sad sound
Attracting --> Collected: reaches player position
Attracting --> Expired: lifetime exceeded
note right of Spawning
Sprite: spark (purple, scale 2.0)
Position: at enemy death location
Animation: scale 0→1 (400ms)
Spawn: with dramatic burst effect (15 particles)
Has glow: true
NEVER auto-despawns if on screen
end note
note right of Active
Visual: purple glow, pulsing aura
Clickable radius: 25px (generous)
Shows "💎" on hover
Bob amplitude: 3px
Bob speed: 1 Hz
Magnetism: always active, floats toward player at 250px/sec
end note
note right of Collected
Triggers: currency.add('crystalEssence', value)
Triggers: floatingText("+{value}💎", purple, large)
Triggers: screen flash (purple tint)
Triggers: particle burst (15 purple crystals)
Triggers: ui.celebrate()
Triggers: ui.update('crystalEssence')
Sound: magical chime + echo
end note
note right of Expired
Fade out: opacity 1→0 over 500ms
Sound: sad magical tone
Remove from entity list
end note
```
### Collectible: Element Sprite (Charge)
```mermaid
stateDiagram-v2
[*] --> Spawning: periodic timer (every 8-12s)
Spawning --> Active: fade in (300ms) at random orbit position
Active --> Collected: player clicks within 18px or hovers
Active --> Expired: lifetime 6s exceeded
Collected --> [*]: add Charge + Mana + floating "+2⚡"
Expired --> [*]: fade out (200ms)
note right of Spawning
Sprite: spark (element-colored, scale 1.0)
Position: random orbit around rune circle
Element colors: Fire (#ff6b354), Ice (#00ffff), Arcane (#bf5eff), Void (#1a1a2e)
Animation: fade in 300ms
Spawn: when wave 3+ complete
end note
note right of Active
Visual: colored spark, matches element
Clickable radius: 18px
Shows "+2⚡ + 1💧" on hover (Charge + Mana)
Orbits near rune circle
Orbit speed: 60px orbit at radius 60px
Orbit duration: 8-12s before despawn
end note
note right of Collected
Triggers: currency.add('elementalCharge', 2)
Triggers: currency.add('mana', 1)
Triggers: floatingText("+2⚡ +1💧", element color)
Triggers: particle burst (3 colored sparks)
Triggers: ui.update('elementalCharge')
Triggers: ui.update('mana')
Sound: elemental fizz
end note
note right of Expired
Fade out: opacity 1→0 over 200ms
No sound
Remove from entity list
end note
```
## CONFIG Spec: entities Section
```javascript
// Entity Configuration for Spellforge Arena
// Each entity type has visual specs, state definitions, and behaviors
CONFIG.entities = {
// Player Spell Projectiles
spellProjectile: {
sprite: 'fireball',
scale: 1.5,
team: 'player',
states: {
spawning: {
type: 'scaleIn',
duration: 100,
invulnerable: true,
},
flying: {
type: 'animate',
frames: [0, 1, 2],
frameDuration: 50,
flipX: false,
rotation: true,
},
impact: {
type: 'burst',
duration: 200,
particles: 'spark',
particleCount: 5,
particleColor: 'elementBased',
},
},
speed: 300, // pixels per second
lifetime: 2, // seconds
pierceCount: 1,
trail: {
enabled: true,
sprite: 'spark',
interval: 100, // ms between particles
color: 'elementBased',
lifetime: 500,
},
glow: {
enabled: true,
color: 'elementBased',
strength: 0.3,
pulse: true,
},
},
// Enemies
wisp: {
sprite: 'ghost',
scale: 1.2,
team: 'enemy',
hp: 10,
damage: 0, // No attack damage
speed: 80, // pixels per second (fast)
states: {
spawning: {
type: 'fadeIn',
duration: 200,
invulnerable: true,
},
moving: {
type: 'animate',
frames: [0, 1, 2, 3],
frameDuration: 200,
flipX: true, // Flip based on horizontal direction
},
dying: {
type: 'fadeOut',
duration: 300,
particles: 'spark',
particleCount: 5,
particleColor: '#00ffff',
spawnCollectible: {
type: 'manaFragment',
value: 3,
chance: 1.0, // 100% drop
},
},
},
reward: {
mana: 3,
chance: 1.0,
},
healthBar: {
show: true,
width: 30,
height: 3,
yOffset: -8,
color: '#00ffff',
},
},
guardian: {
sprite: 'knight',
scale: 2.0,
team: 'enemy',
hp: 30,
damage: 15,
speed: 50, // pixels per second (medium)
states: {
spawning: {
type: 'scaleIn',
duration: 250,
invulnerable: true,
},
moving: {
type: 'animate',
frames: [0, 1, 2, 3],
frameDuration: 200,
flipX: true,
},
fighting: {
type: 'animate',
frames: [2, 3],
frameDuration: 100,
spawnProjectile: true,
},
dying: {
type: 'fadeOut',
duration: 400,
particles: 'spark',
particleCount: 7,
particleColor: '#c0c0e0',
spawnCollectible: {
type: 'ironShard',
value: 5,
chance: 1.0,
},
},
},
reward: {
mana: 2,
chance: 1.0,
ironShards: 5,
chance: 1.0,
},
healthBar: {
show: true,
width: 40,
height: 4,
yOffset: -6,
color: '#4caf50',
},
weakness: { fire: 0.2 }, // +20% damage from fire
resistance: { ice: 0.2 }, // -20% damage from ice
},
golem: {
sprite: 'slime',
scale: 2.0,
team: 'enemy',
hp: 150,
damage: 25,
speed: 20, // pixels per second (very slow)
isBoss: true,
states: {
teased: {
type: 'static',
duration: 3000,
animation: 'bob',
},
spawning: {
type: 'scaleIn',
duration: 500,
invulnerable: true,
},
moving: {
type: 'animate',
frames: [0, 1],
frameDuration: 400,
flipX: false,
},
dying: {
type: 'fadeOut',
duration: 800,
particles: 'spark',
particleCount: 30,
particleColor: '#a0a0a0',
screenShake: {
enabled: true,
intensity: 5,
duration: 100,
},
spawnCollectible: {
type: 'ironShard',
value: 15,
chance: 1.0,
},
spawnCollectible: {
type: 'crystalEssence',
value: 1,
chance: 0.3,
},
},
},
reward: {
mana: 5,
chance: 1.0,
ironShards: 15,
chance: 1.0,
},
healthBar: {
show: true,
width: 60,
height: 6,
yOffset: -10,
color: '#ff6b354',
},
weakness: { void: 1.0 }, // Void does full damage
resistance: { fire: 0.5, ice: 0.5 }, // 50% reduction from fire/ice
},
champion: {
sprite: 'wizard',
scale: 1.2,
team: 'enemy',
hp: 80,
damage: 20,
speed: 60, // pixels per second (medium-fast)
isElite: true,
states: {
teased: {
type: 'static',
duration: 5000,
uiIndicator: '⚠️ CHAMPION APPROACHING',
},
spawning: {
type: 'fadeIn',
duration: 400,
invulnerable: true,
},
moving: {
type: 'animate',
frames: [0, 1, 2, 3],
frameDuration: 150,
flipX: true,
},
casting: {
type: 'animate',
frames: [2, 3],
frameDuration: 250,
},
dying: {
type: 'fadeOut',
duration: 600,
particles: 'spark',
particleCount: 15,
particleColor: '#bf5eff',
spawnCollectible: {
type: 'ironShard',
value: 20,
chance: 1.0,
},
spawnCollectible: {
type: 'crystalEssence',
value: 3,
chance: 0.6,
},
},
},
reward: {
mana: 5,
chance: 1.0,
ironShards: 20,
chance: 1.0,
},
healthBar: {
show: true,
width: 50,
height: 5,
yOffset: -8,
color: '#9932cc',
},
abilities: {
summonWisps: {
cooldown: 5000,
count: 2,
},
counterspell: {
cooldown: 2000,
damage: 10,
},
},
weakness: 'rotating', // Changes each wave
},
// Collectibles
manaFragment: {
sprite: 'spark',
scale: 1.2,
color: '#00ffff',
lifetime: 8,
collectRadius: 20,
magnetRadius: 100,
magnetStrength: 200,
bobAmplitude: 2,
bobSpeed: 2,
value: 3,
spawnAnimation: 'popIn',
spawnAnimationDuration: 200,
collectAnimation: 'burst',
particleCount: 5,
particleColor: '#00ffff',
showsValue: true,
sound: 'mana_collect',
},
ironShard: {
sprite: 'spark',
scale: 1.5,
color: '#808080',
lifetime: 10,
collectRadius: 22,
magnetRadius: 120,
magnetStrength: 180,
bobAmplitude: 2,
bobSpeed: 1.5,
value: 5,
spawnAnimation: 'popIn',
spawnAnimationDuration: 250,
collectAnimation: 'burst',
particleCount: 7,
particleColor: '#c0c0e0',
metallic: true,
showsValue: true,
sound: 'iron_collect',
},
crystalEssence: {
sprite: 'spark',
scale: 2.0,
color: '#9932cc',
lifetime: 15,
collectRadius: 25,
magnetRadius: 150,
magnetStrength: 250,
bobAmplitude: 3,
bobSpeed: 1,
value: 1,
spawnAnimation: 'popIn',
spawnAnimationDuration: 400,
collectAnimation: 'burst',
particleCount: 15,
particleColor: '#b34dff',
glow: true,
showsValue: true,
sound: 'essence_collect',
neverDespawnOnScreen: true,
celebrate: true,
},
elementSprite: {
sprite: 'spark',
scale: 1.0,
color: 'varies', // Set by element type
lifetime: 6,
collectRadius: 18,
magnetRadius: 80,
magnetStrength: 150,
bobAmplitude: 2,
bobSpeed: 2.5,
value: 2, // Charge value
orbitRadius: 60,
orbitSpeed: 1,
spawnAnimation: 'fadeIn',
spawnAnimationDuration: 300,
collectAnimation: 'burst',
particleCount: 3,
particleColor: 'varies',
showsValue: true,
sound: 'element_collect',
orbit: true,
},
};
```