I made some solid progress towards adding more content this week, providing a scaleable foundation.

  • Added a new device and card combo with a new mechanic (evade)
  • A global persistent player now exists, along with an abstracted file-based configuration
  • The engine now supports multiple opponents with a dynamic layout and repositioning
  • Added deck counters such as draw, discard, and spent
  • Added hover hints to improve ease of use and improved internal naming for debugging
  • Controller Focus is much more complete and user friendly, including sticky targeting
  • Intents are smaller, more accurate (off by one), and have a shader effect
  • Mouse controls are cleaner on the backend and less buggy
  • Implemented a JSON schema for all my data types, facilitating adding more content
  • GUT was broken in CI, which I didn’t notice because it was failing silently
  • Removed some clutter and old prototypes from the codebase (they’re still in Git)

Sticky targeting is important to me from a usability perspective; I want the game to be smart about minimizing the amount of effort the player needs to put into performing actions. For example, if there are two opponents, you aim at the second and attack, then defend yourself, then go back to attack, assume they still want to attack the second opponent. Same goes for using cards; don’t default to the first card, default to the nearest card of the same type, else the closest card. This is supposed to be a strategy card game, not a clicker.

GUT was an unexpected gotcha; since CI was passing, I didn’t realize there was a problem. Turns out that because it’s running through Godot, it’s always returning Exit 0. Here’s a working GUT Unit Test GitHub Action configuration that will fail the job if tests fail:

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.1"

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Import project
        run: godot --headless --import --quit

      - name: Run Tests
        run: |
          # Run godot and capture all output (stdout and stderr) to a log file
          godot --headless -d --path "$PWD" -s addons/gut/gut_cmdln.gd \
            -gconfig=.gutconfig.json \
            -gcompact_mode \
            -gexit 2>&1 | tee gut.log

          if grep -q "\[Failed\]" gut.log; then
            echo "GUT reported test failures."
            exit 1
          fi

          if grep -q "Debugger Break" gut.log || grep -q "Parse Error" gut.log; then
            echo "Godot encountered a script or parse error."
            exit 1
          fi

          if grep -q "0 tests passed" gut.log && ! grep -q "Passed" gut.log; then
            echo "No tests were executed."
            exit 1
          fi

I’ve mentioned devices before, and I’ll be more explicit about what they do. Broadly, they consume resources and create cards and affect gameplay. The intent is to make it too expensive to have all devices on all the time, but be able to change strategies mid-game.

This week, I’m going to:

  • Add a passive shield device (so far, devices have only generated cards)
  • Add a generator device (no controls, just passively provides extra energy)
  • Add another opponent type (I’ve put this off, but I think I can do this now)
  • Add a piercing weapon that ignores shields (variant of existing damage)
  • Add a device that mutates cards (this will require some architecture changes as cards are currently immutable)
  • Add a recycling device (this is instead of the “drag to recycle” mechanic I ditched)
  • Add scenario configurations, including opponent sequence offset
  • Play specific scenarios (choose from list)
  • Play random scenarios

With this, I can start thinking about an overworld navigation, which will be an important part of a vertical slice.


This past Friday, I watched a wonderful concert - Tigran Hamasyan: Manifeste - which was absolutely beautiful and inspiring. During the concert, I imagined how to implement an opening sequence. I can’t prioritize it quite yet, but it’s coming…


Meta: I’ve re-organized this site’s news to include the date in the URL, which should help with SEO and makes it easier for me to organize on the backend. Hugo supports aliases which create meta redirects, which aren’t the same as a 301, but I’m using GitHub Pages to host the site statically, so this is the best I’ve got. It’s sufficient for now, and since the sitemap reflects the current location, it should be fine.