All work
GodotIn Development2026

ClockWork

A 2D action-combat game whose enemy learns how you fight: a persistent memory layer reads your habits across every encounter and reshapes the AI to answer them.

An underground encounter. Health, stamina and mana sit top-left and the ability wheel bottom-right, all driven by signals from the player data singleton rather than polled each frame. The enemy's bar is the vessel's combat pool: deplete it and the role either recasts into a new vessel or dies for good.
An underground encounter. Health, stamina and mana sit top-left and the ability wheel bottom-right, all driven by signals from the player data singleton rather than polled each frame. The enemy's bar is the vessel's combat pool: deplete it and the role either recasts into a new vessel or dies for good.
Client
Fiverr (@grayster001)
Role
Gameplay Programmer
Platforms
PC
Year
2026

ClockWork is a 2D action platformer built around melee combat: a light and heavy attack chain, dash, shield block, parry, and a magic set, all driven through hierarchical state machines for both the player and the enemy.

What makes it more than an HP bar is what each hit records. Every hit also reads how the player is fighting, whether that is brutal, precise, or repetitive, and those readings accumulate in a place the enemy itself cannot reach.

That split is the core of the design. The enemy vessel holds the state of a single life: its composure, its combat pool, how intact its role is, how much the audience favours it. The vessel keeps no memory of its own. A separate persistent layer does that, tracking behavioural patterns across every encounter and pushing the accumulated weight back onto the vessel. A player who wins by mashing the same heavy attack finds the enemy getting faster and more erratic over time, because the game has been reading those habits rather than nudging a hidden difficulty slider.

What I built

  • Built the player and enemy state machines on a hierarchical FSM addon, with legal transitions declared in a single dictionary rather than scattered across state scripts: adding or restricting a transition is a one-line change
  • Centralised all player input-to-transition logic in the state machine instead of duplicating it per state, so each state opts into the subset of inputs valid from it
  • Designed the two-layer enemy model: a per-life vessel holding poise, combat pool, role integrity and audience favour, and a separate persistent layer that records player behaviour across all encounters
  • Wired the combat feedback loop: attack hitboxes activate on specific animation frames, each landed hit both deals damage and records a brutal/precise/repetitive reading, and the accumulated pattern derives concrete AI behaviour: move speed, attack cooldown, jitter, heavy-attack chance, stagger and recover durations
  • Implemented defensive play: shield block and parry states that intercept incoming damage, with a successful parry reflecting damage back at the attacking vessel
  • Moved all gameplay tuning into autoload data singletons (speeds, jump and dash config, damage values, health/stamina/mana with regen delays, coyote time and jump buffering) so numbers are edited in one place, never inside state logic
  • Built the traversal and level kit: ledge grabs, breakable tiles and walls, checkpoints, teleporters, camera focus and limit zones, kill areas, and secret foliage
  • Handled the front end and presentation: main menu, pause menu, options, loading screens, and a dialogue system with signal-driven UI juice reacting to resource changes

Gallery

Ledge grab, with contextual prompts bound to the live input map so they follow rebinds and controller layouts.
Ledge grab, with contextual prompts bound to the live input map so they follow rebinds and controller layouts.
Traversal between camera zones, which clamp and reframe as the player crosses them.
Traversal between camera zones, which clamp and reframe as the player crosses them.
Title screen.
Title screen.

Breakdown

Transition legality lives in one dictionary mapping each state to the states it may enter. The machine walks that map at startup and registers an event per transition, so states change by dispatching a named event rather than by reaching into each other. Restricting a move: say, blocking dash-out-of-parry, is an edit in one place.

Player input never lives inside individual states. Every input-to-transition check sits together in the state machine, and each state calls only the subset legal from it. Adding a new input-driven move means writing one helper and referencing it from the states that should honour it.

Attack hitboxes activate on specific animation frames rather than on a timer, connecting to the sprite's frame signal on entry and disconnecting on exit. On a landed hit the hitbox does double duty: it applies damage and files a behavioural reading of the attack that produced it.

The enemy's difficulty comes out of the simulation rather than from a setting. Volatility and poise from the vessel feed a function that outputs move speed, attack cooldown, aim jitter, heavy-attack probability, and how long stagger and recovery last. Sustained brutal or repetitive play raises volatility, and the fight visibly tightens in response.

What the client said

5 out of 5
Very good freelancer, would work with again.
grayster001, Australia · 6 weeks
5 out of 5
Very good communication, fast work, great code
grayster001, Australia · 5 weeks

Built with

  • Godot 4.7
  • GDScript
  • Hierarchical state machines
  • Jolt physics
  • Phantom Camera
  • Forward+ renderer