Add two design plans
This commit is contained in:
parent
759779b319
commit
541f29d64d
2 changed files with 1660 additions and 0 deletions
182
docs/plans/2026-02-23-single-combat-design.md
Normal file
182
docs/plans/2026-02-23-single-combat-design.md
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
# 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.
|
||||||
1478
docs/plans/2026-02-23-single-combat-plan.md
Normal file
1478
docs/plans/2026-02-23-single-combat-plan.md
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue