Where it is now
Tribal Auto Sticks is an asynchronous autobattler in development. A run starts in the shop, where the player builds a five-unit crew and chooses how to arrange it. The crew then fights a server-resolved battle that the client plays back event by event.
I drew all 91 units in the current roster and the artwork used around the game. The units belong to 12 tribes, while visually distinct characters can remain unaffiliated when forcing them into a tribe would make the roster less interesting.
Building the crew
The current playable slice has two deterministic shop slots. Players can buy units, weapons, and consumable food, then place them in five persistent formation slots. A unit can carry one weapon. Buying three copies creates a level-two unit; six copies produce level three.
There is no reserve bench hiding extra state. Purchases, swaps, merges, and formation changes all operate on the crew that will enter the next battle. The interface supports mouse, keyboard focus, and touch. Unit descriptions appear when a character is inspected, so the artwork remains readable during play.
The shop and battle currently rotate through three paired 1920 × 1080 themes: Ember Ruins, Moonlit Marsh, and Sunken Grove. Each pair gives the preparation screen and its arena a related setting without reusing the same image.
Keeping combat reproducible
An autobattler can look chaotic while still producing an exact result. Tribal Auto Sticks sends validated, immutable team snapshots and a server-owned seed into a framework-independent deterministic engine. The engine returns the winner together with a structured event log. The React client animates that log and never recalculates the fight for itself.
Front-line clashes apply damage from both units before resolving either defeat, which permits a mutual knockout. Abilities, tribe effects, summons, movement, and defeat triggers follow explicit ordering rules. Stored battle input can be run again and compared with the original result byte for byte.
Safety limits stop a broken interaction from creating an endless match. A battle is capped at 80 turns, 2,000 events, and a trigger depth of 20. Invalid targets become safe no-ops.
Project structure
- TypeScript npm-workspace monorepo
- React and Vite client shared by the web build, Tauri desktop shell, and Capacitor mobile shell
- Fastify API with Zod validation at content, state, and request boundaries
- PostgreSQL schema behind a repository boundary
- Platform adapters for browser, Steam, Android, and iOS services
The server owns the seed, shop state, currency, opponent selection, snapshots, battle result, and progression. An in-memory repository keeps local development quick. The PostgreSQL design adds transactions, revision checks, and persisted idempotency responses for a future production version.
Testing a system with many combinations
The automated suite covers combat order, target selection, damage, defeat, healing, prevention, statuses, summons, recursion limits, shop actions, tribe effects, progression, schemas, assets, input, replay accuracy, retry handling, and ownership boundaries.
Simulation runs record how often units and tribes appear, their win rates, battle length, trigger volume, summons, duration, and throughput. Chromium tests cover desktop, tablet, and phone layouts. Type checking, linting, builds, and migration checks run alongside the gameplay tests.
Current status
The game is still a private work in progress. The repository will remain private until the playable loop and persistence layer are ready to put in other people's hands. This case study is the public record of the work in the meantime.