Building the Core Deckbuilder Loop
I’ve been working on one overarching task for over a week, in Codecks the card is labeled as “Gameplay.” I’ve got a GDD, sketches, wireframes, and a couple of proof-of-concepts, but the real question I want to answer is whether or not it’s fun. In order to do that, I need to build something minimal to actually play the thing. Right now, “Gameplay” is like the Malcolm in the Middle bit where Hal tries to change a lightbulb, but is met with a series of blockers:
Blockers may be a strong word of what I’m working through, they’re prerequisites. It is a little waterfall, but as I complete each step of the journey, I have foundation.
I haven’t been as disciplined with myself in creating individual tasks for this, but I do have a very specific goal: be able to, entirely in code (no UI), start a conflict and play it through deterministically.
To that end, I did plan the states, I’ve got the test framework GUT (Godot Unit Test) running, and I’ve got the placeholder steps as comments.
So, here’s the first public teaser of what I’m working on:
Godot Engine v4.6.stable.official.89cea1439 - https://godotengine.org
OpenGL API 3.3.0 NVIDIA 580.76.05 - Compatibility - Using Device: NVIDIA - NVIDIA GeForce RTX 2070 SUPER
StateLoadout enter(), round 1
At: res://state_manager/states/state_loadout.gd:7:enter()
Choose Devices
At: res://state_manager/states/state_loadout.gd:8:enter()
[Device: device_beam_1:ON, RDY (GOOD@30) ]
At: res://state_manager/states/state_loadout.gd:19:handle_input()
StateLoadout exit(), round 1
At: res://state_manager/states/state_loadout.gd:13:exit()
StatePlayerEffects enter()
At: res://state_manager/states/state_player_effects.gd:6:enter()
StateDraw enter() not implemented
At: res://state_manager/states/state_draw.gd:6:enter()
--- Debugging process stopped ---
This is an iceberg view; underneath, there’s:
- Resources for all data structures
- Game Data and a JSON loader
- Game Manager
- An abstract State Manager and States (which I may open source) that includes transitions with enter/exit hooks and flow validation
- Game States (partially implemented)
- A sensible amount of test coverage
- CI/CD static analysis and testing
Right now, there are 8 states and some actions within each state. I just started on the third state, so there’s definitely more to go. Here’s a visualization with Mermaid Live Editor:

In the meantime, here’s how to run GUT Unit Tests as a Github Action; you’ll need to export your GUT config to match the editor’s configuration.
name: GUT Unit Tests
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
test:
runs-on: ubuntu-latest
container:
image: "barichello/godot-ci:4.6"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Import project
run: godot --headless --import --quit
- name: Run Tests
run: |
godot \
--headless \
-d \
--path "$PWD" \
-s addons/gut/gut_cmdln.gd \
-gconfig=.gutconfig.json \
-gcompact_mode \
-gexit
And some Static Checks:
name: Static Checks
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
static-checks:
name: "Formatting and Linting"
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Install godot-gdscript-toolkit
uses: Scony/godot-gdscript-toolkit@master
- name: Check GDScript Formatting
run: gdformat --check .
- name: Check GDScript Linting
run: gdlint .
- name: Check JSON Linting
run: find data -name "*.json" -exec sh -c 'echo "Linting $1" && jq empty "$1"' _ {} \;
My goal this week is to try to get through all the states. Let’s see how far I get!
Yesterday, Godot 4.6 was released; the godot_4_6 branch of GUT is required. GUT’s changes looked to be mostly compatibility fixes and I didn’t have any problems upgrading.