This is SPLASH CRUSH, a real-time match-3 versus I'm making in Godot 4.7 for Android. No turns: each player has their own board, and everything you match paints your floor and fills your ink bar. When the bar is full, a wave of ink is announced against your rival and lands 3 seconds later. During those 3 seconds they can slow it down by matching fast. Whoever has painted the most when the clock hits zero wins.
A few things that might be useful if you're building something similar:
- The whole match is a plain `RefCounted` simulation (`Partida` + one `Tablero` per player), with no nodes. The scene only draws it. That made it easy to run bot-vs-bot balance tests headless and to reuse the same code online.
- Numbers are small and readable: a match fills 2.5% of the bar per symbol, +50% per cascade step. A wave starts at 6 ink drops (max 16); a line of 4 adds 1 drop, a line of 5 adds 2, each cascade adds 1. Each of your matches while a wave is incoming removes 1 + (length - 3) + (cascade - 1) drops from it. Big cascades also send garbage blocks, Tetris style.
- Online, each client simulates only its own board. The other boards are mirrors rebuilt from snapshots, and the only thing that crosses the network is attacks (waves, cards). It goes through a tiny Python websockets relay, so nobody ever writes to the same board.
- Match rules are data: a base dictionary (duration, bar, wave size, hand size, cards...) and every mode or event is just a diff on top of it. A new mode is a dictionary, not new code.
- The footage is the real game recorded with Movie Maker (`--write-movie` at a fixed 30 fps, 1080x1920 through an `override.cfg`), and the cut, zoom and captions are done with ffmpeg and PIL from a Python script.
The captions in the video are in Spanish (I'm from Peru); the game ships in 6 languages, English included.
How would you handle the incoming wave: give the defender a fixed window like this, or let them push it back?