slaywithfriends/docs/plans/2026-02-23-single-combat-design.md

182 lines
5.1 KiB
Markdown

# single combat encounter - design doc
first playable slice of slaywithfriends. one ironclad player vs one
enemy. proves the core card engine works.
## decisions
- rendering: html/css with card images (no canvas)
- framework: none. vanilla js, es modules, bun serve
- networking: client-only. all state in browser
- character: ironclad (simplest mechanics)
- interaction: two-tap (select card, tap target)
- mobile: design for 375px baseline, scales up naturally
## state model
single object drives everything. pure functions produce new state.
nothing mutates directly.
state = {
player: {
hp, maxHp,
energy, maxEnergy,
block,
strength,
drawPile: [...cardIds],
hand: [...cardIds],
discardPile: [...cardIds],
exhaustPile: [...cardIds],
powers: [...cardIds],
},
enemy: {
id, name, hp, maxHp, block,
strength, vulnerable, weak,
action,
actionTrack: [...],
trackPosition,
},
combat: {
turn,
phase: 'player_turn' | 'enemy_turn' | 'rewards' | 'ended',
dieResult,
selectedCard,
log: [...],
}
}
## card data schema
cards are json entries keyed by id. effects are typed arrays.
{
"strike_r": {
"name": "Strike",
"cost": 1,
"type": "attack",
"effects": [{"type": "hit", "value": 6}],
"image": "assets/images/ironclad/starter/0.png",
"keywords": [],
"description": "Deal 6 damage.",
"upgraded": "strike_r+"
}
}
effect types for first slice: hit, block, draw, strength, vulnerable,
weak, exhaust, lose_hp.
## enemy data schema
{
"jaw_worm": {
"name": "Jaw Worm",
"hp": 6,
"actionType": "die",
"actions": {
"1": {"intent": "attack", "effects": [{"type": "hit", "value": 3}]},
...
}
}
}
three action types: single (same every turn), die (keyed to die roll),
cube (ordered list with advancing pointer, gray actions don't repeat).
## rendering layout
three vertical zones:
enemy zone (~40%) enemy art, hp bar, block, status tokens, intent
info bar (fixed) energy pips, hp bar, block shield, strength, pile counts
hand (~35%) cards fan out, overlap if many, selected card lifts
pile overlays: tap draw/discard count to see scrollable card grid.
## two-tap interaction
1. tap card in hand - it lifts and highlights
2. enemy zone becomes valid target (highlighted border)
3. skills that don't need a target auto-play
4. tap enemy - card plays, effects resolve, card animates to discard
5. not enough energy - card shakes and drops back
6. tap selected card again to deselect
## combat loop
player turn:
1. reset energy to 3, block to 0
2. draw 5 cards (shuffle discard into draw if needed)
3. roll die (1-6), determines enemy intent
4. resolve start-of-turn triggers
5. play phase - player acts freely
6. "end turn" button ends play phase
7. end-of-turn triggers, discard remaining hand
enemy turn (auto-resolves):
1. remove enemy block
2. enemy executes action
3. advance cube-action pointer if applicable
4. brief pause (~1s) for player to read
combat ends when enemy hp hits 0 (victory) or player hp hits 0 (defeat).
## damage formula
1. start with base hit value
2. add attacker strength (+1 per token)
3. if target vulnerable, double result
4. if attacker weak, subtract 1
5. weak AND vulnerable cancel out - skip both
6. subtract target block, remainder hits hp
7. consume 1 vulnerable from target after attack
8. consume 1 weak from attacker after attack
9. multi-hit: loop N times, consume tokens only after all hits
10. aoe/multi-hit: only 1 weak consumed total
## enemy ai
data-driven. each enemy's action is an effects array resolved the
same way as card effects. intent displayed as icon + number after
die roll so player can plan.
## project structure
slaywithfriends/
index.html
style.css
src/
main.js entry point, init, render loop
state.js state creation, pure action functions
effects.js effect resolver
combat.js turn flow orchestration
enemy-ai.js resolve enemy actions
render.js state to dom
cards.js load/query card data
enemies.js load/query enemy data
die.js die roll
data/
cards.json card database
enemies.json enemy database
potions.json potion database
relics.json relic database
assets/ video game card images
StS_BG_assets/ board game assets
docs/ rules and plans
## out of scope
- map / room navigation
- deck building / rewards / card selection
- merchant / campfire / events
- potions and relics
- multiplayer / networking
- persistent state / save / load
- sound
- ironclad passive (heal 1 hp end of combat)
- tutorial
## data pipeline
board game card sheet images being extracted to json by agent.
video game wiki data (wiki.gg Module:Cards/data) available as
secondary reference. board game values are source of truth.