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.

- 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



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
Very good freelancer, would work with again.
Very good communication, fast work, great code
Built with
- Godot 4.7
- GDScript
- Hierarchical state machines
- Jolt physics
- Phantom Camera
- Forward+ renderer