Dekiro Dev Blog

Part 1 of 28

Building a Grindy Martial-Arts ARPG with an AI Developer

One person directs, an AI writes the code, and a rulebook keeps both of them honest.

What we are making #

Dekiro: Sanctuary of Shadows is a grindy hack-and-slash ARPG in the spirit of a popular Korean MMO. You mow down packs of monsters, level up, gear up and clear dungeons. Its signature target is the Shadow, our take on that MMO's famous stones: a corrupted monolith that stands in a hunting ground and keeps spawning waves of enemies until you break it. You choose one of four schools at level 5 (Blade, Iron Fist, Shadowstep, Sanctum). Skills are raised to Master and then Grandmaster with skillbooks. The blacksmith takes gear from +0 to +9, and the failure chance at the top end feels as cruel as it should.

The engine is Godot 4.7.2 with GDScript and the Forward+ renderer. The world is 3D with a third-person orbit camera; the characters in it are pixel-art sprites rendered from 3D models.1 The "HD-2D" look, which has its own post. Multiplayer is server-authoritative (single player runs as a local server), with dedicated servers and Steam lobbies.

This series is about how it gets built. The team is unusual: one human developer, and Claude Code as the developer who writes all of the code.

The White Fox Shrine in Sanctuary Vale on the latest build: a sprite character in a 3D world
An early basic attack against a Lesser Shadow stone, back when the player was a placeholder capsule

The roles #

The split has stayed the same since the first commit:

  • The developer (human) decides what the game is. They set direction ("[that MMO's] grind, Valheim-style hosting, no in-game chat, players use Discord"), make the design calls, sketch maps on paper, approve visual designs and playtest. Anything about feel, such as timing, responsiveness or whether a hit lands, gets judged by a human with a mouse in hand.
  • Claude Code (AI) writes the code, the data files and the content generators. It runs the test suite, renders screenshots and looks at them, fixes what it finds, and commits each step with a message that says what changed and why.

Every commit in the repository carries a Co-Authored-By: Claude line. The human never writes code; they steer.

The pace #

The first commit, "Milestone 1: movement, multi-target arc attack, training dummies", landed on an afternoon in early October. At the time of writing the repository has 199 commits. Here is how they spread over the days:

Day Commits
1 (afternoon only) 8
2 44
3 60
4 76
5 6
6 5

The last two rows look like a slowdown. They aren't. From day five on, most work was done by parallel subagents and committed by a coordinating session in large, reviewed batches.2 The coordinator post in this series explains how. One day-six commit alone touches 639 files and adds about 51,000 lines: NPC sprites, map mechanics and three dungeons. The days got fewer commits and much more content.

The committed history now holds about 61,000 lines of GDScript (17,500 of them tests) and about 42,000 lines of Python for the content tools: model builders, map and dungeon generators, a sprite renderer, a music synthesizer and a balance model. Those tools are covered in later posts.

Speed is not the point of this post. It matters for one reason: at this pace, nobody can keep the whole project in their head. Neither the human nor the AI, which starts every session cold. The project has to remember itself. That is what CLAUDE.md is for.

One day of commits (the 8th of October): 60 of them

CLAUDE.md: the rulebook #

Claude Code reads a file called CLAUDE.md at the root of the project at the start of every session. Ours started with milestone 1 and has grown with every system since. It holds the commands (how to run tests, smoke runs, exports, the Blender builders, the sprite renderer), a map of where things live, and, most importantly, architecture rules. Three of them go back to the first commit (quoted here in their current wording):

  • Controllers drive Actors only via command_* methods (command_move, command_attack, …). PlayerController, future AIController (companions/mobs) and any future network input must be interchangeable.
  • Combat is shape-based, never tab-target. Attacks query a shape (arc, circle, line) via Combat and hit everything valid inside it.
  • Logic is separate from visuals. Actors hold no meshes; a child visual node (currently PlaceholderVisual) reads Actor state and listens to its signals.

These rules paid off again and again. The command interface meant that AI companions (who join your party and fight with you) could use the same player scene with CompanionAI swapped in for PlayerController. Network input fit the same interface, and so did the bots of the combat simulation that later balanced the game. Keeping logic apart from visuals meant that the capsules could be replaced twice without touching gameplay: first by 3D monster models, then, for players and NPCs, by 8-direction pixel sprites. Both times one visual node was swapped.

The second kind of rule is the more interesting one: rules that come from bugs. When something breaks in a way that could happen again, the fix goes into code and the lesson goes into CLAUDE.md. Examples from the current file:

Dead mobs are freed while other code may still reference them (Shadow spawn lists, AI targets, delayed skill hits). Never pass a possibly-freed object into a typed parameter or typed lambda (func(a: Actor)): Godot errors with "Cannot convert argument 1 from Object to Object".

Never reparent or move bodies inside physics callbacks (body_entered etc.): defer it. […] Doing this inside a callback segfaulted Godot.

Headless runs never compile shaders: after any shader edit (incl. shader strings in .gd files) take a windowed screenshot of main.tscn and grep its log for ^ERROR (a uniform named like a local in a shared .gdshaderinc, e.g. shade vs clouds.gdshaderinc, once turned every sprite into a white wall).

That last one is two days old. Each of these cost time once. Written down, they don't cost time again, because the next session reads them before writing a single line.

The architecture rules in CLAUDE.md

The loop #

The working loop is short and strict. It's written into CLAUDE.md like this:

Run tests + smoke run after every change. […] Neither covers visuals or game feel, so after visual changes take a screenshot and look at it; ask the user to playtest anything about feel.

In practice a step looks like this:

  1. Implement the feature, data first where possible (more on that in the next post).
  2. Add tests to the headless suite. A test is a plain test_* method that calls _check().
  3. Run the suite. It currently runs 341 test functions with about 13,800 checks. Godot engine errors don't fail a test on their own, so the log is grepped for ERROR lines as well.
  4. Smoke-run the game headless for a few hundred frames, to catch errors that only show up when the whole scene is live.
  5. For networking changes, run bot clients against a host and a dedicated server.
  6. For anything visual, render a screenshot, look at it, fix it and look again. Claude Code reads the PNGs itself.
  7. Commit with a descriptive message.

The commit messages became the project's diary. A typical one:

Fix errors from freed mobs in Shadow spawn tracking

alive_count() filtered with a typed lambda; once a dead Shade was freed, Godot refused to pass it ('Cannot convert argument 1 from Object to Object') and the filter returned an untyped Array. Check validity on untyped values instead; same hardening for MobAI targets and delayed skill hits. Regression tests reproduce the original errors.

That message has everything: the symptom, the cause, the fix, the related places hardened at the same time, and the regression test. We wrote most of this series by reading the git log.

Changing course is cheap (if the rules hold) #

The second commit set up a 3D world with a fixed isometric camera. About seventy minutes later another commit replaced it with a full perspective orbit camera (right-drag or Q/E to rotate, wheel to zoom). The design doc records the reason: the game should feel like the old MMO, with a camera you rotate and zoom yourself. Because movement input was already "screen-relative, converted to world space in PlayerController", the change touched six files.

Bigger reversals followed. We built a seamless open world with land bridges between regions, then switched it off nine hours later in favour of separate maps linked by torii portals. The first big map was then shrunk by about two thirds twelve hours after it was built, because it felt too big and too empty. The player character went through six 3D drafts before the human picked a completely different technique: 3D models pre-rendered into pixel sprites. Each of these turns has its own post. What matters here is that the rules made them survivable. Nothing outside the world code knew whether maps were joined or separate, and nothing outside the visual node knew whether a character was a capsule, a model or a sprite.

What the human actually decides #

From the outside, "the AI writes all the code" can sound like "the AI makes the game". It doesn't. Some decisions from the history, all made by the human:

  • Valheim-style hosting: dedicated servers, characters stored on the player's PC and portable between servers, world events per server.
  • No in-game chat, no player shops, no "add bonus" scrolls (rerolls only).
  • The open world was switched off; every map keeps one town ("Keep safe zones to one per map").
  • Map 1 was drawn by hand on a grid (1 square = 25 m) and translated into data.
  • The Bulwark school's battleshields became fist weapons ("a shield to attack is hard to go for"), and the school became Iron Fist.
  • "Solid trees, but see in deep woods". That one is preserved in a code comment in flora.gd, which dissolves the leaves between the camera and your character but keeps trunks opaque.
  • Every visual direction: the Valheim-like pixel texture look at first, then the switch to HD-2D sprites, keeping the grass because they liked it, and the decision that +9 weapons sparkle rather than glow.
  • The target length: "about 40 hours of actual gameplay minimum" to the level cap, which drove a whole balancing pass.

The AI proposes, implements and verifies. The human chooses. When a question is really a design question, the AI asks; otherwise it keeps going. Asking too often would waste the human's time. Asking too rarely would build the wrong game. Some answers are short. The human's verdict on one batch of AI-drawn sprite mock-ups was unprintable, followed by: "the rendered 3d to 2d one look way better". That sentence set the art direction for everything after it.

Where it stands #

Six days in, the game has nine world maps (each with one town, waystones and landmarks), 20 dungeons from a generator with solvers, 177 monster types plus 11 kinds of Shadow stone, 203 items, 24 skills, 87 quests including a main questline that unfolds with your level, 43 NPCs and 66 townsfolk with daily routines, 30 original music tracks and a progression model that aims at about 40 hours to level 90. None of it has had a real playtest beyond the human's own sessions yet, and the posts say so where it matters.

Takeaways #

  • Write the rules down where the AI will read them. CLAUDE.md is not documentation for later. It is the memory that makes each session start where the last one ended.
  • Turn every painful bug into a rule. The fix stops the bug once. The rule stops the whole class of bug.
  • Tests are what make speed safe. Without the suite, 40 to 70 commits a day would be 40 to 70 chances to break something quietly.
  • Keep feel for humans. Tests can prove that the combo's finisher does 1.8x damage. They can't prove that it feels good.

What's next #

The next posts go system by system: data-driven skills and loot, multiplayer, the test suite, the Blender pipeline that builds every model from Python, the maps, performance, the art direction (and how it changed), the synthesized music, fishing, and how the work moved from one AI session to a coordinator with parallel subagents. After those come the newer pieces: the HD-2D sprite pipeline, the dungeon generator, the story, the 40-hour balance model, the living towns and the Fullmoon weapons.