Chainbound Duel - 1v1 mobile wizard duel with persistent dual hand-chains. Play at https://itchbox.tail018ac4.ts.net/game/chainbound-duel https://itchbox.tail018ac4.ts.net/game/chainbound-duel
Find a file
hermes 9295e1dcfb
All checks were successful
CI / build (push) Successful in 1m35s
Deploy to itchbox / build-and-deploy (push) Successful in 1m23s
ci: re-run via runner pickup verification
2026-09-26 20:06:47 -07:00
.forgejo/workflows ci: make itchbox deploy optional; skip gracefully when unreachable 2026-09-13 17:34:55 -07:00
.hermes/triage fix #56: chain-anchor anchors one hand and clears anchoredHands after a turn 2026-08-13 17:24:24 -07:00
public gsd: M010 S04 — checkpoint (PWA manifest + service worker) 2026-05-17 16:51:00 -07:00
server fix: add production deployment configuration for relay server 2026-06-15 16:45:06 -07:00
src fix #61: remove dead CounterType 'phase-disrupt' 2026-09-18 07:24:51 -07:00
test fix #56: chain-anchor anchors one hand and clears anchoredHands after a turn 2026-08-13 17:24:24 -07:00
.env.example fix: add production deployment configuration for relay server 2026-06-15 16:45:06 -07:00
.gitignore chore: gitignore .kilo/ working notes (#60) 2026-09-09 08:51:54 -07:00
.gsd feat: Phase 1 game engine — T01 through T04 2026-05-13 20:09:14 -07:00
.pre-commit-config.yaml chore: add pre-commit hooks (.pre-commit-config.yaml + lefthook.yml) 2026-06-03 17:27:22 -07:00
bun.lock fix: onboarding lock, modifier state persistence, lint + smoke script bugs 2026-08-11 12:05:14 -07:00
capacitor.config.ts gsd: M010 S03 — fix splash background color, remove androidSplashResourceName 2026-05-17 17:32:32 -07:00
chainbound-e2e-thorough.cjs fix: onboarding lock, modifier state persistence, lint + smoke script bugs 2026-08-11 12:05:14 -07:00
chainbound-smoke-deep.cjs fix: onboarding lock, modifier state persistence, lint + smoke script bugs 2026-08-11 12:05:14 -07:00
chainbound-smoke-final.cjs fix #19: [Bug] SymbolToken elements duplicated in DOM across chain panes and ha 2026-06-15 11:06:30 -07:00
chainbound-smoke-full.cjs fix: onboarding lock, modifier state persistence, lint + smoke script bugs 2026-08-11 12:05:14 -07:00
chainbound-smoke.cjs fix: onboarding lock, modifier state persistence, lint + smoke script bugs 2026-08-11 12:05:14 -07:00
components.json init: project bootstrap from archive 2026-05-13 17:12:18 -07:00
eslint.config.js fix #56: chain-anchor anchors one hand and clears anchoredHands after a turn 2026-08-13 17:24:24 -07:00
generate-placeholders.py fix: clean up generate-placeholders.py lint issues 2026-06-16 09:16:39 -07:00
index.html gsd: M010 S04 — checkpoint (PWA manifest + service worker) 2026-05-17 16:51:00 -07:00
lefthook.yml chore: add pre-commit hooks (.pre-commit-config.yaml + lefthook.yml) 2026-06-03 17:27:22 -07:00
package-lock.json fix: leech-tax uses actual caster index, install coverage-v8, boost quiet sounds 2026-06-15 11:06:30 -07:00
package.json fix: leech-tax uses actual caster index, install coverage-v8, boost quiet sounds 2026-06-15 11:06:30 -07:00
postcss.config.js M006: milestone complete 2026-05-15 06:55:16 -07:00
README.md fix #62: remove unimplemented SpellDefinition.flowTagsConsumed/Produced fields 2026-09-18 07:22:23 -07:00
tailwind.config.js M006: milestone complete 2026-05-15 06:55:16 -07:00
todo.txt M006: milestone complete 2026-05-15 06:55:16 -07:00
tsconfig.app.json M006: milestone complete 2026-05-15 06:55:16 -07:00
tsconfig.json M006: milestone complete 2026-05-15 06:55:16 -07:00
tsconfig.node.json init: project bootstrap from archive 2026-05-13 17:12:18 -07:00
vite.config.ts gsd: pre-implementation lint/format cleanup 2026-05-17 15:20:29 -07:00

Chainbound Duel

A dual-chain combat game where two duelists face off using symbolic magic. Build chains from seven base symbols and five composite sigils, cast devastating spells, manage two resource pools (vitality and will), and outmaneuver your opponent through tactical timing, flow control, and reactive conditions.

Tech Stack

  • Framework: React 19 + TypeScript
  • Build: Vite 7
  • Routing: React Router 7
  • Animation: GSAP, Framer Motion
  • UI: Radix UI primitives + shadcn/ui, Tailwind CSS
  • State/Forms: React Hook Form + Zod
  • Audio: Web Audio API with spatial sound and adaptive music

Getting Started

git clone https://forgejo.tail018ac4.ts.net/Mark/chainbound.git
cd chainbound
npm install
npm run dev

Game Overview

Core Mechanics

Dual Chains — Each duelist has a left hand and right hand, each building a chain of up to 5 slots. Chains are built simultaneously each turn before both sides reveal.

Symbols — Seven base symbols form the foundation of all magic:

  • Spark — Ignition energy for offensive spells
  • Veil — Concealment and protection
  • Knot — Binding and restriction
  • Fang — Direct, relentless pressure
  • Mirror — Duplication and reversal
  • Gate — Amplification and transition
  • Pulse — Sustained, lingering effects

Sigils — Adjacent symbol pairs compress into five composite sigils:

  • Seal (veil + knot) — Binding ward
  • Mask (mirror + veil) — Total misdirection
  • Current (gate + pulse) — Sustained flow
  • Snare (knot + fang) — Damaging restraint
  • Glint (spark + mirror) — Feinting strike

Resources — Two pools tracked per player:

  • Vitality (100) — Primary health; reach zero to lose
  • Will (80) — Secondary resource for certain spells and conditions

Turn Phases — plan → compose → commit → reveal → resolve → persist

Flow Tags — Every chain has a flow state that affects spell casting: open, lean, exposed, anchored, dead

Spells

Spells are crafted by matching symbol/sigil sequences. Each spell has a pattern (single-hand sequence or simultaneous two-hand pair) and effects (damage, heal, curse, or modifier application). Spells interact with the reactive condition system based on flow-tag transitions and chain state.

Notable spells include:

  • Turned Knife — Low damage with favorable flow tag economy
  • Blind Witness — Applies reflective shroud modifier
  • Ember Lance — High damage execution spell (12 vitality)
  • Paper Phoenix — Simultaneous heal + phoenix-ascent modifier
  • Cinder Storm — Highest damage spell (15 vitality), requires 4 slots
  • Iron Grasp — Applies chain-lock control modifier
  • Void Siphon — Boss-only: drains opponent will to heal self vitality

Conditions & Reactive System

Conditions are persistent effects applied to players or hands:

  • Veiled, Anchored, Marked, Delayed, Phased, Witnessed, Compelled, Echoed, Sealed, Inverted
  • Barbed, Grinning Seal, Funeral Courtesy, Black Ichor, Phoenix Ash

The reactive system allows spells to trigger effects on specific events: on-cast, on-interrupt, on-chain-advance, on-repeated-symbol, on-lane-collapsed, and more.

Counters & Modifiers

Phase 2 systems include counters (delay, weaken, redirect, tax, full-stop, cancel, reflect, disrupt-setup, phase-disrupt, change-ownership, change-target, re-source) and modifiers (delay, haste, out-of-phase, veil, permanence, echo, inversion, anchor) that alter spell resolution.

Game Modes

Mode Description
Casual Duel Single match vs AI opponent, any personality
Ranked Trials Best of 3 series, ladder ranking system
The Gauntlet Campaign with 5 tiers, boss fights, unlockable titles
Online Match Real-time PvP via WebSocket relay server
First Binding Interactive tutorial teaching core mechanics
Chronicles Replay viewer for past matches
Stats Win/loss analytics and performance metrics
Player Profile Titles earned, match history, preferences

Project Structure

src/
├── pages/              # Route pages
│   ├── TitleScreen.tsx     # Main menu
│   ├── CasualDuel.tsx      # Casual vs AI
│   ├── RankedMatch.tsx     # Ranked best-of-3
│   ├── Campaign.tsx         # Gauntlet mode
│   ├── OnlineMatch.tsx      # Real-time PvP
│   ├── Tutorial.tsx        # Interactive tutorial
│   ├── ReplayViewer.tsx     # Match replays
│   ├── Stats.tsx            # Analytics dashboard
│   ├── ProfileScreen.tsx    # Player profile
│   ├── Spellbook.tsx        # Full spell reference
│   └── Settings.tsx        # Options panel
├── components/         # UI components
│   ├── ui/              # shadcn/ui primitives
│   ├── ChainPane.tsx         # Chain display
│   ├── ChainResolveVFX.tsx   # Resolution effects
│   ├── ConditionMarker.tsx   # Condition icons
│   ├── SpellHorizon.tsx      # Spell display
│   ├── SymbolToken.tsx       # Symbol rendering
│   ├── HealthBar.tsx         # Resource bars
│   ├── OpeningEditor.tsx      # Opening book editor
│   ├── CutscenePlayer.tsx    # Campaign cinematics
│   └── ...
├── data/              # Game data
│   ├── spells.ts           # Spell definitions (19 spells)
│   ├── symbols.ts          # Symbols and sigils
│   ├── campaign.ts         # Gauntlet tier definitions
│   ├── tutorial.ts         # Tutorial progression
│   ├── dialogue.ts         # Story dialogue
│   ├── openings.ts         # Named opening strategies
│   ├── stats.ts            # Stats tracking
│   ├── replays.ts          # Replay storage
│   └── campaignStory.ts    # Campaign narrative
├── engine/            # Core game logic
│   ├── state.ts            # Game state pure functions
│   ├── resolver.ts         # Turn resolution
│   ├── spellGraph.ts       # Spell pattern matching
│   ├── conditions.ts       # Condition system
│   ├── reactiveConditions.ts # Reactive triggers
│   ├── modifiers.ts        # Modifier system
│   ├── counters.ts         # Counter system
│   ├── timing.ts           # Turn phase timing
│   ├── visibility.ts       # Per-player information
│   ├── ai.ts               # AI decision making
│   ├── aiPersonalities.ts  # AI behavior profiles
│   ├── GameContext.tsx     # React game state context
│   ├── sound.tsx           # Audio system
│   └── network/            # Online multiplayer
│       ├── relayClient.ts      # WebSocket client
│       └── useRelayClient.ts   # React hook for relay
├── hooks/             # Custom React hooks
├── lib/               # Utilities and shared logic
└── App.tsx            # Root with routing
server/
└── src/
    ├── main.ts         # Relay server entry
    ├── Room.ts         # Match room management
    └── types.ts        # Server types

Routes

Path Page Description
/ TitleScreen Main menu
/casual CasualDuel Casual vs AI
/ranked RankedMatch Ranked best-of-3
/online OnlineMatch Real-time PvP
/campaign Campaign Gauntlet mode
/tutorial Tutorial Interactive tutorial
/settings Settings Options panel
/replays ReplayViewer Browse replays
/replay/:id ReplayViewer Watch specific replay
/stats Stats Analytics dashboard
/profile ProfileScreen Player profile
/spellbook Spellbook Full spell reference

Scripts

Command Description
npm run dev Start dev server
npm run build Production build
npm run preview Preview production build
npm run lint Run ESLint
npm run test Run test suite

CI

CI is configured via Forgejo Actions — runs on every push.

Relay Server Deployment

The WebSocket relay server for Online Match is deployed automatically to Node Hub:

# .forgejo/workflows/deploy-relay.yml
on: push: paths: ['server/**']

Requires secrets: NODEHUB_TOKEN, FORGEJO_TOKEN. See server/README.md for details.

License

© Chainbound Studios