agentleFS
Ar9avAr9av/game-exa/skills/pixel-action-adventure/SKILL.md

pixel-action-adventure

Skill4 starsChanged 7 months agoMITView on GitHub

Generates Zelda/Hyper-Light-style top-down action adventures with zero image assets — every sprite, prop, tile edge and effect is rasterized in code as 3-colors-plus-outline pixel art. Multi-level campaigns across distinct biomes (coast, jungle, snow, volcano), a weapon roster, kill quotas with off-screen reinforcements, a boss, title and game-over screens. Use when the description asks for an 8-bit/16-bit action adventure, a "game like <pixel-art tutorial/reference image>", or when art must be generated without an image API (no FAL_KEY/OPENAI_API_KEY). Sibling to rpg-overworld (NPC dialogue/quests, GPT Image 2 sprites) — pick this one when the loop is combat + exploration and the art should be procedural.

  • Reads credentials

The file

We split this skill into the rules it gives an agent

1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Leave ≥150 ms between synthetic key presses in menu tests — two presses in one frame is a test artifact, not a game bug.

    line 71

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Pixel Action Adventure

Reference implementation: examples/ember-isle (Phaser 3 + Vite, ~1.5k lines across main.js / sprites.js / biomes.js, no image files). Built from SLYNYRD's Pixelblog 36 — 8-bit Adventure and iterated against a reference screenshot, so the art rules below are the ones that closed the gap between "procedural placeholder" and "reads as real pixel art".

Genre tag

Set gdd.genre to "pixel-action-adventure". Win = beat the last level's boss; lose = hearts reach 0 (retry current level with progress kept). QA must drive a full campaign run (see QA), not just level 1.

Art: procedural pixel art that doesn't look procedural

  • One tiny rasterizer, not a drawing library. A Pix class (w, h, color-per-pixel array) with fill((x,y) => color|null), set, outline(color) and fromGrid(rows, palette). makeTex(scene, key, frames) blits frames side by side into one canvas texture and registers frame indices. Every sprite, icon, prop and effect goes through these two functions.
  • Auto-outline is the single biggest quality lever. Fill shapes with 2–3 flat shades (light top-left, base, dark bottom rim), then outline() adds a 1px dark outline around every non-empty pixel (4-neighbour). Use a tinted dark per subject (indigo #1d1b3a for characters, deep green for foliage, near-black red for volcanic props) — never pure black.
  • Characters: hand-authored grids (16×16, 3 colors + outline, '.' = transparent). Validate every row's width at load (fromGrid throws) — an off-by-one row silently shears the sprite otherwise. Share rows between frames by array spread ([...head, ...face, ...legsA]) instead of duplicating whole frames.
  • Props/effects: shape math. Palm = segmented curved trunk + 7 leaf quads split light/dark along the midrib (sign of a cross product). Bush/canopy = disc with a repeating 4×3 "scallop" mask. Pine = 3 stacked triangles with a 2px snow band along each slanted edge. Volcano = shaded cone (rel = dx/halfWidth → light/mid/dark thirds) + strata lines + sinuous lava streams + elliptical glowing crater.
  • Recolor, don't redraw, for variety. recolor(pix, {srcHex: dstHex}) over a per-biome map turns green slimes into purple poison / ice / magma slimes. Keep source palettes disjoint across enemy kinds so one map serves all.
  • Pixel font for anything big. Phaser Text at 8px blurs under FIT scaling. A 5×7 bitmap font (35-char bitstrings per glyph) rendered through Pix at integer scale, with a vertical color gradient, outline, and a darker 3px "extrude" underneath gives crisp logos ("GAME OVER", title) and menus. Cache textures by style + string; count-up stats just swap cached textures.

Terrain: noise + distance fields, not autotiles

Paint the whole map once per level into one ImageData (e.g. 1024×640 px) instead of tiling it — this is what makes coastlines and grass patches organic instead of square.

  1. Type per pixel from smooth value-noise fbm: liquid ('coast' = diagonal sea with noisy edge, 'river' = sinuous band with 2-tile bridge columns, 'lakes' = fbm threshold blobs kept away from spawn), secondary ground (grass/ice/ash) by a second fbm threshold with a margin from liquid, else base ground. Raised plateaus/mesas are tile-grid rects ([x0,x1,height] along the top edge, [x0,x1,y0,y1] freestanding); the 2 tiles under an elevated tile become a cliff FACE.
  2. Distance fields by multi-source BFS (4-neighbour, capped at 8, typed arrays, runs in ms): distance-to-land inside liquid, distance-to-liquid on land, distance-to-edge inside grass and plateau tops.
  3. Bands from distances: liquid ≤2px from land → foam; land ≤1 → dark rim, ≤4 → wet tan, ≤5 → soft; grass edge ≤1 → light rim and a dashed contour at distance 4 ((x+y)%5<3) — that dashed inner line is the signature look of the reference art; plateau top ≤1 → rim, ≤3 → lip highlight. Cliff faces: horizontal strata w = fy + round(2*sin(x*π/8)), 7px bands cycling 3 shades, a lip line every band, dark columns at run ends, near-black base line.
  4. Stamps last: pebble clusters on ground, tufts (or glowing cracks) on secondary ground, only where the target pixel type matches.
  5. Liquid is a separate scrolling tileSprite (16×16 pattern of scale-shaped "cups") under the ground image, which is transparent wherever liquid is. Lava = same pattern in orange/yellow with sparkle dots, scrolled slower.
  6. Collision = tile type at each tile's center → a hidden tilemap layer (setVisible(false), setCollision(1)). Props get small static bodies at their base only (trunk footprint), so the canopy overlaps the player naturally with y-sorted depth.

A biome is only data: a color table for these bands (+ plateau top/face shades), a liquid [base, wave, sparkle], an enemy skin map, a weather kind, and flags (slippery, lavaBombs, redGlow). A new level = one LEVELS entry (biome, seed, liquid mode, thresholds, plateaus, props, foes, quota, hidden weapon).

Combat feel (the article's timings are load-bearing)

  • Sword: 3 frames — strike / follow-through / overshoot — at 80 ms each, then 320 ms before the next attack. Draw the swing as a white crescent annulus sector per frame (thickness sin of angle across the arc), pivoted at the hero so 90° rotations cover all four directions.
  • Idle and walk share the same 2 frames, only the frame rate differs (2 fps vs ~6.7 fps).
  • Hit: enemy flashes red tint-fill then white tint-fill (80 ms each), a flashing X burst (show / blank / inverted), 45 ms physics hit-stop + small camera shake, knockback; kill → 3-frame vaporize (disc → ring → specks) + loot.
  • Weapons as one dispatch + one shared resolver: attack() switches on the equipped weapon; every melee/projectile hit goes through strike(dir, hitSet, inRange(x,y,pad), opts) which damages enemies once per attack, bats projectiles back, and cuts bushes. Ember Isle's roster: sword, spear (line hit ≤42px, perp ≤9px), boomerang (out 110px or until a wall, homes back, stuns, drags pickups home), bombs (1.1 s fuse, 2-frame big blast, hurts the player too), fire wand (projectile + 3 burn ticks). Each level hides the next weapon at a reachable tile; the HUD shows owned weapons in numbered slots (1–5, Q/E cycles).
  • Dash with i-frames and fading tinted afterimages every 30 ms.

Level loop

  • Quota < population: each level shows FOES k/goal; spawn more foes than the goal, and every 2.5 s while under quota spawn one reinforcement ≥10 tiles away and outside the camera rect. Leftover foes stay after the goal (coin farming).
  • Reachability first: BFS the tile grid from spawn (walls = solid tiles + non-cuttable prop footprints) and place foes, the weapon pickup and reinforcements only on reachable tiles. With lakes/rivers/plateaus, unreachable spawns otherwise make quotas unwinnable.
  • Clear → heart container + portal on the nearest open tile near the player; portal overlap fades out and restarts the scene with {level, hearts, maxHearts, coins, unlocked, weapon, runKills}.
  • Boss = scaled (×3) recolored base enemy + crown sprite + HP bar; sheds minions when hit, speeds up below half HP, ends in a chained 2-frame explosion.
  • Hazards per biome: slippery ice (velocity lerps toward input at 0.05 instead of snapping), volcano lava bombs (blinking red target ellipse, rock arcs in from the crater, blast hurts player and enemies). Put a set-piece like the volcano on a freestanding stepped mesa mid-map — against the top edge the camera clamp means players only ever see its base.
  • Weather is screen-space rectangles (snow falls, embers rise, fireflies blink) wrapped at the viewport edges; crater adds a pulsing red overlay.

Screens

Scenes: Title → Game → End. Title: pixel-font logo with a periodic glint, the camera slowly drifting over level 1's painted map, the cast bobbing, a heart-cursor menu (New Game / Continue from a localStorage save written at each level start, wrapped in try/catch / How to Play). Death: stop music, flash + spin-shrink the hero into a vaporize puff, cam.zoomTo(1.8) + pan, cam.postFX.addColorMatrix().grayscale(), then scene.launch('End') + scene.pause(). End: bouncing title, stats that count up one row at a time, a random tip, Retry / Title (victory variant in gold).

Bugs this genre reliably produces (check each one)

  1. Phaser.Input.Keyboard.JustDown drops taps whose keydown and keyup land in the same frame (onUp clears _justDown). Queue actions from keydown-SPACE events instead and consume the flag in update.
  2. Scene restart reuses the scene instance: any this.* state (portal, boss, over/leaving flags, HUD refs) leaks into the next level's first updateHud(). Reset per-level state at the top of create(), and clear camera zoom/postFX there too.
  3. Physics sprites ignore setPosition for teleports (body overwrites the transform next step); use body.reset(x, y).
  4. Set-piece off-camera: anything drawn above y ≈ viewportHeight/2 from the world's top edge is never fully visible during play because the camera is bounded. Check set pieces from a walkable camera position, not with a free camera.

QA

Headless Playwright against window.__gameState (phase, level, kills, goal, weapon, unlocked, enemiesAlive, hearts, coins) and __titleReady / __gameReady / __endReady:

  • Title → New Game → play; every level: walk to the weapon pickup, verify it auto-equips, then meet the quota using that weapon (teleport foes next to the player via body.reset, grant i-frames), take the portal; boss → victory screen.
  • Assert enemiesAlive > goal at every level start, and that reinforcements appear off-screen after culling foes.
  • Death → End screen → Retry lands on the same level with full hearts; Title shows Continue.
  • Leave ≥150 ms between synthetic key presses in menu tests — two presses in one frame is a test artifact, not a game bug.
  • Screenshot each biome and the set-pieces; compare against the reference image the user gave, not against "looks fine".

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.