A mod for the Pokémon Gen 1 Recompilation Project.
The overworld as a 3D diorama. Terrain is extruded into real geometry, occlusion comes from a depth buffer rather than a y-sort, characters stand as leaning sprite slabs, a shadow map throws real cast shadows across whatever they land on, and an optional tilt-shift pass sells the miniature-model look.
Water is a surface rather than a texture lying in a hole. It is a field of one-pixel-wide voxel columns, each standing a whole number of pixels tall and rising and falling as waves — found by walking the view ray through them in the shader, so a crest hides what is behind it and shows you its lit side, with no extra geometry anywhere.
And it reflects. The sky it stands under, in the same bands, the same dither and off the same clock, so the lake and the sky above it meet at the waterline with no seam. The sun or moon hanging in it, at the size the painted disc is drawn, craters and all. Whoever is standing beside it — walkers, NPCs, the two Pokémon in a staged battle. And on FULL, a screen-space ray march adds the rest of what is on screen: the shoreline, the trees behind it, the buildings across the bay. How much of it shows is Fresnel, so the top rung is a mirror and a looking-straight-down rung is a pond, off the same water.
And battles fought on that world rather than on a white field. When something picks a fight the map's NPCs are culled, the engine's own wipe plays over the empty map, and the battle draws over the nearest patch of clear ground — shot over the shoulder, the player's mon low and left and the enemy high and right, with a slow parallax drift behind them and a depth-of-field pass that keeps both of them sharp.
And the whole thing from inside. The ladder's top rung, 1ST, dives the camera into the player's own head: free look on the mouse (captured while the rung is on — left click is A, right click is B), the right stick, or a touch dragged across open screen; free movement that goes where you look, at any angle, sliding along walls — the left stick's raw deflection, the touch d-pad's true vector, or WASD as forward/backpedal/strafe. NPCs turn to face the eye wearing the frame their pose shows this viewer — walk behind someone and you see their back — and the sky, the shadows and the water reflections all carry over, because the head rides the same placed camera the battle shot proved out.
Presentational, with one deliberate exception. Every rung but 1ST changes what the world looks like and nothing about what it is; the battle arena is where the camera goes, not where anybody goes. 1ST replaces the grid walk with a free one while it is selected — but even there the game is untouched: the walk asks the engine's own collision the same questions a grid step asks, keeps the player's cell synced, and runs the engine's own landing pipeline per cell crossed, so warps, encounters, ledges, gates and scripts all fire exactly as themselves. Step off the rung and the grid walk is back.
Every key is free-roam only, and each one is also a row on the OPTIONS menu.
| control | does |
|---|---|
3, or the VOXEL options row |
OFF → 15 → 35 → 50 → 75 → 1ST → OFF (camera pitch or first-person view) |
5, or the V-GRID options row |
OFF / ON — a one-pixel wireframe on every voxel |
6, or the T-SHIFT options row |
OFF → 1 → 2 → 3 → OFF (miniature blur) |
7, or the V-CURVE options row |
OFF → 1 → 2 → 3 — bend the world over the horizon |
8, or the 3D-BTL options row |
ON / OFF — fight on the map instead of on a white field |
| the BATTLE ART options row | STATIC / ANIMATED / ROM — use optional battle-only art, with a direct ROM fallback when a file or atlas is absent |
| the TRAINER ART options row | GEN 1 / GEN 2 / GEN 3 — choose a static opponent-trainer collection |
| the PLAYER ART options row | PNG / GEN 1–5 / ASH / GARY / BOY / LASS / HILBERT / ROM — choose the static player trainer battle-intro portrait; BATTLE ART: ROM pins it to ROM |
| the PLAYER ANIM options row | PNG / GEN 1–5 / ASH / GARY / RED / ASH FRONT / MISTY FRONT / BROCK FRONT / BULMA FRONT / GARY FRONT / ROM — choose a static player.png or five-pose player intro while ANIMATED is selected |
| the ANIM FRONT GEN options row | GEN 1 / GEN 2 / GEN 3 / GEN 4 / GEN 5 — choose a single-frame Gen 1 compatibility set or an animated Gen 2–5 collection |
| the BACK ART SET options row | GEN 1 / GEN 2 / GEN 3 / GEN 4 / GEN 5 — STATIC always uses generation PNGs; ANIMATED uses Gen 3/5 atlases and Gen 1/2/4 PNGs |
| the DUPLICATE FIX options row | BATTLE ART / MODDED — use the Gen 2 DV formula to route actual shinies through Battle Art's matching imported shiny collections, or leave shiny pictures to another mod/ROM; ordinary Pokémon retain their selected normal Battle Art in either mode; replaces both old SHINY FIX rows |
| the PLAYER options row | FRONT SPRITES / BACK SPRITES — supplied art is world-placed; a missing selected back uses the ROM's UI-attached pic |
| the FLIP FRONT SPRITE options row | BATTLE ART / DEFAULT — mirror ordinary Battle Art on the player side, or preserve an already-oriented picture supplied by a sprite mod |
| the BACK PLACEMENT options row | AUTO / WORLD / OG UI — use the mode-aware default or force every player back onto one layer |
| the HUD COLOR options row | COLOR keeps black HUD glyphs and coloured HP gauges with a light shadow; INVERTED uses white glyphs with a dark shadow. Gauge health remains bright green / amber / red in either mode |
| the SHADOWS options row | ON uses cast shadows (with the flat fallback on unsupported hardware); OFF removes both paths in free roam and staged battles. UNLIT battle sprites neither receive nor cast shadows even while this is ON |
9, or the WATER options row |
FULL / SKY / OFF — waves and reflections on water. SKY gives the surface its pixel-tall wave columns and puts the sky, the sun, the moon and the cast in them; FULL adds a screen-space ray march that also reflects the shoreline, the trees and the buildings standing behind it |
| the AA options row | OFF / 2X / 4X — smooth the stair-stepped edges of the 3D world by rendering the diorama larger than the window and folding it back down. The ladder is samples per display pixel: 2X is a canvas root-two wider and taller, 4X one exactly twice the size. Every edge in the projected picture softens with the silhouettes — the tileset's own texels are quads in a perspective view and cross the pixel grid at the same arbitrary angles — so the diorama reads smoother rather than sharper. The most expensive row in the mod, so it is OFF by default and FULL leaves it alone |
| the DAYTIME options row | SYNC / DAY / NIGHT / DUSK / DAWN / CYCLE — what time it is outdoors, on the diorama and on the flat 2D world; held at SYNC (and off the menu) while VOXEL is FULL |
Fresh installs start at VOXEL: FULL, with PLAYER: BACK SPRITES and the other defaults listed above. Existing saved choices, including VOXEL: OFF, are preserved across updates.
3D-BTL is on by default and is independent of VOXEL: battles draw on the world whether or not the free-roam camera is pitched over.
The title menu's PRECACHE item opens GENERATE PRECACHE before gameplay. It cooperatively prepares every persistent mesh variant the renderer can ask for, shows live progress and disk use, and remains cancellable with B. Running it again resumes: records whose exact input fingerprint is still valid are counted as EXISTING rather than rebuilt.
The generator writes only beneath
mod-derived/BATTLE_ART_VOXEL_FORK/static-mesh-cache-v2 in the game's save
directory. At mods.loaded it takes a private snapshot of the final map and
tileset geometry, after content mods have patched it but before gameplay can
change it:
- one
MAP.full.terrain.bavcfor every loadable map, containing terrain and the separately drawn water surface, with that map's connection masks; - one
MAP.body.terrain.bavconly for maps participating in seamless outdoor connections, because only neighbour rendering requests the body-only form; - one shared
MAP.aux.bavcper generated map, containing tall grass, flowers, and authored figure geometry.
These records contain only geometry derived from that immutable snapshot: terrain, water sheets, buildings, trees, static grass/flowers and figures authored into the tileset. Runtime NPCs and spawned overworld Pokemon remain ordinary sprite billboards and never enter a disk record or fingerprint. If a script, Cut or a door changes a live block, that one live map is meshed in RAM; it neither reads nor replaces the canonical static file. Returning to the canonical layout reuses the disk mesh again.
For auditing, the mod builds
mod-derived/BATTLE_ART_VOXEL_FORK/static-cache-exclusions.tsv. It records the
map, component and asset key for runtime objects and noncanonical live geometry
which were deliberately refused by background/persistent precaching. It is a
human-readable exclusion ledger, not a permanent map blacklist: a spawned
Pokemon can never poison its town's canonical terrain cache.
BAVC is a small documented container, not a serialized GPU object or ROM dump:
a BAVC/version/fingerprint header followed by LZ4 chunks of interleaved
position.xyz, uv.xy, shade float vertices. Standard GLB was evaluated for
inspectability, but the measured 744 MiB test cache contains 2.73 GiB of raw
vertex data; without shipping a mesh-compression decoder, GLB would increase
mobile storage about 3.7 times. Corrupt/truncated records fail open to the
ordinary cooperative mesher. Runtime meshes are released between maps, so
generating the whole cache does not hold the whole world in RAM.
The completion screen reports map, file, FULL/BODY/AUX counts and total MiB or
GiB. Exact size depends on the imported ROM and installed map/tileset content;
large routes dominate and a complete cache can occupy hundreds of MiB or more.
The directory is disposable: deleting it only makes the mod regenerate static
meshes. The older mesh-cache-v1 directory is no longer read and may be removed
manually after confirming the v2 build.
Battle Art automatically yields its native battle HUD, text/menu layer and
panels to the installed gen3_battle_ui while that mod's revampedBattleUI
option is enabled. Mimic, Safari and the scripted demo retain native text
because Gen 3 UI v0.1 does not replace those phases.
The older gen1_modern_ui adapter is also recognised when its experimental
battleUiWip option is explicitly enabled.
Other replacement presenters can wrap
battle.presentation.suppress_native.v1. The request reports API version 1,
source ID BATTLE_ART_VOXEL_FORK, the requested hud, text, or panels
surface, and the current battle when available. Return exactly true only
when the consumer will draw that complete surface. The descriptor is exported
as mod.find("BATTLE_ART_VOXEL_FORK").exports.battlePresentation; absent,
throwing or false consumers fail open to Battle Art's native presentation.
BATTLE ART: STATIC is the default. Drop a front PNG named for the species
into assets/battle/front-static, or a back PNG into the selected
assets/battle/back-static/gen1 through gen5 folder; for example,
caterpie.png, farfetchd.png, or mr-mime.png. No Lua sidecar or fixed
resolution is required, and the image is not resized. Existing alpha is
preserved. For a fully opaque PNG, the corner-coloured background connected
to the image border is keyed transparent while enclosed matching pixels are
left intact.
Enemy front art is used as authored, facing left. Player front art is mirrored to face right; authored player back art already faces right and is not mirrored. Every imported sprite remains a card in the 3D world, so hit flash, depth occlusion and alpha-shaped shadows apply. Static species front illustrations preserve their authored brightness instead of receiving the day/night colour tint; other art continues to follow the hour. OG, inverted and CLASSIC display filters still apply to imported art. Missing or unreadable files fall straight back to the ROM art.
The four battle-art directories are intentionally ignored by Git except for
their README contracts. tools/package_mod.ps1 nevertheless includes local
PNGs from them in a test ZIP, so artwork can stay private and uncommitted.
Use tools/package_clean_mod.ps1 for a shareable install ZIP that preserves
the documented battle-art folder layout but excludes every PNG below
assets/battle. The clean ZIP includes the complete public tools folder, so
users who receive only the archive can import their own art and rebuild it
without cloning the repository. Generated Python __pycache__/.pyc files
remain excluded.
BATTLE ART: ANIMATED reads independent ANIM FRONT GEN and BACK ART SET
choices. GEN 1 fronts are ordinary single-frame PNGs from
front-animated/gen1; Gen 2–5 fronts remain animated atlases, including
converted Diamond/Pearl APNGs for GEN 4. Animated back GEN 3 and GEN 5 read
atlases from back-animated/gen3 and back-animated/gen5. Back GEN 1, GEN 2
and GEN 4 read ordinary species PNGs from their back-static generation folders. A
loaded back is world-placed, lit, depth-occluded and shadowed. If its selected
PNG or atlas is missing or malformed, the unmodified ROM backsprite remains in
the original UI layer, including its normal battle motion and filtering.
BACK ART SET also appears under STATIC. In that mode every choice, including
GEN 5, reads only back-static/<generation>/<species>.png; it never inspects
an animated atlas. GEN 1 remains selectable when its directory is absent so
users can create it for ROM-hack art. ANIM FRONT GEN remains exclusive to
ANIMATED, and ROM ignores both selectors.
Run python tools/import_emerald_back_sprites.py --root . to import both the
animated Emerald Spr b 3e backs and the static Spr b 3r backs for the
first 151 species. The generated atlases and ordinary PNGs remain local,
ignored artwork; only their shared atlas metadata is committed.
Run python tools/import_crystal_back_sprites.py --root . to import the first
151 static Crystal backs. In ANIMATED mode, BACK ART SET: GEN 1 and GEN 2
still select the static Yellow SGB and Crystal folders respectively; only GEN
3 and GEN 5 invoke an animated back-atlas decoder.
Run python tools/import_platinum_back_sprites.py --root . to populate the
static GEN 4 back set. It follows the Platinum category's mixture of 4p and
reused 4d files and chooses the male image for a dimorphic species.
Run python tools/import_black_white_static_back_sprites.py --root . to
populate static GEN 5 with the first 151 Black/White back-normal PNGs. This
does not replace or alter the animated GEN 5 atlas collection.
BACK PLACEMENT: AUTO keeps STATIC-mode player backs in the world, including
ROM fallbacks. Under ANIMATED, supplied Gen 1–4 PNGs and Gen 5 atlases stay in
the world while a missing selection leaves the ROM backsprite on its OG UI
anchor. ROM mode also uses OG UI. WORLD and OG UI override that decision
for testing any art set; large supplied art may crop when forced onto OG UI.
Opponent trainer cards are always static. TRAINER ART chooses fronts from
front-static/gen1, front-static/gen2, or front-static/gen3, using class
filenames such as youngster.png, cooltrainer-f.png, and
jessie-james.png. A missing class falls directly back to its ROM portrait;
generations are never silently mixed. In STATIC mode, PLAYER ART selects
player trainer backs
such as back-static/gen1player.png, back-static/ashplayer.png,
back-static/boyplayer.png, back-static/lassplayer.png,
back-static/hilbertplayer.png, or ROM.
PNG is the default and reads back-static/player.png; every missing named
choice tries that same generic PNG before falling back to ROM.
In ANIMATED mode, PLAYER ANIM: PNG reads the static
back-static/player.png. Named choices select the corresponding five-frame
strip from back-animated; the five *frontplayer.png choices retain their
authored front-facing poses, while ROM retains the engine portrait. Frame one
holds during the stationary entrance pose. Frames two through five advance
once with the engine's leftward intro slide and then stop; they do not loop.
AUTO placement keeps this intro on OG UI, while WORLD and OG UI remain
explicit overrides.
Both the original five-by-64 strips and normalized five-by-80 strips are read
at native resolution. Unlike the ROM's half-resolution player back, a custom
static or animated trainer remains 1x when attached to OG UI. Use
tools/player-animation-template-400x80.png as the authoring guide, then
remove its coloured dividers from the finished atlas.
back-static/oak.png and back-static/old-man.png remain the scripted demo
portraits. The folder READMEs carry
the complete trainer-class list and the four exceptional Pokémon filenames.
Two of the engine's own rows are taken away while this mod is installed: TILT, which is the flat fake of what this mode does for real, and GBC FX, a full-screen present pass over the top of the diorama. Both are held at off rather than merely hidden — a row that is not there cannot switch off a value an older save arrived with. Uninstall and both come back, at whatever they were last set to.
The two battle HUD blocks sit on frosted glass over the world. TEXTBOX FILL
controls the engine's own text and menu paper independently: WHITE (the
default), translucent HALF, opaque BLACK, or OFF. Dark and transparent modes
use white ink with a one-pixel shadow, while ARENA FILL: WHITE forces the
guaranteed-readable white box. The engine still owns the box geometry, so its
border, corners and ink remain aligned under both fixed and fill scaling.
HUD COLOR: COLOR (the default) keeps black names, levels and HP text with
the original green/yellow/red bars and a bright one-pixel shadow. INVERTED
uses white HUD ink with a dark shadow. The setting affects only the opponent
and player status blocks, never textbox ink; ARENA FILL: WHITE forces COLOR.