Devlog/Level Editor

Exporting to EasyFPSEditor

Level Editor 6 min read
Exporting to EasyFPSEditor

Exporting to EasyFPSEditor, and drawing for it

The problem with exporting to a tile engine

Level Editor draws maps the way a portal engine wants them: sectors with arbitrary polygonal outlines, per-wall textures, sloped floors, heights anywhere you like. EasyFPSEditor wants something else entirely. An EFPSE map is a 3D grid of 64-unit cubes, and every cell is one of three things: empty, a solid cube, or a walkable floor quad. There is no diagonal, no half tile, no floor height between two levels.

So an export is not a file conversion — it is a rasterisation. And the honest part of the job is not the writing, it is telling the author what the rasterisation cost them.

The format

.eem has no magic bytes and no compression. It is a v2 header followed by five int16 planes over the grid, plane-major then y-major:

int16 version, floorCount, width, height int16 plane[5][floorCount][height][width]

Plane 0 holds tiles, and the value is a Textures.dat id plus one, so 0 can mean empty. What decides whether a cell is a wall cube or a walkable floor is not the plane — it is the type column of the texture row in Textures.dat: type 2 is a wall, type 3 is a floor. Plane 4 holds modifiers; planes 1–3 hold objects, entities and models.

Those three id tables are not documented anywhere, and I refuse to guess into a user's project. So the exporter writes only what it can justify: the player start (object id 9, which is what a freshly authored EFPSE map puts on its player cell), plus any Thing whose type string carries an explicit id — EFPSE_OBJ_56, EFPSE_ENT_…, EFPSE_MDL_…, EFPSE_MOD_…. That prefix is the escape hatch: anything the community identifies later is usable immediately, with no new build, and it is what a future .eem importer would round-trip through.

One happy accident: an EFPSE tile is 64 units on a side, which is exactly this toolchain's metre. Nothing is rescaled on the way out.

What the rasteriser does

Each cell is classified from its centre point: which sector contains it, and what the floor and ceiling heights are there — sampling the slope plane per cell is what turns a smooth LE ramp into an EFPSE stepped ramp, and nothing else in the pipeline has to know slopes exist. A cell whose ceiling is at or below its floor isn't a room, it's a pillar, so it stays solid.

Then three things get built that a hand-made EFPSE map also has:

Two limits get handled instead of silently breaking things. Negative floor heights are legal in the editor and don't exist in EFPSE, so the whole map slides up rather than losing its basement (and the report says by how many tiles). And the grid is a fixed 64×64 with the outer ring spent on the closing wall, so the map is centred in it — which both guarantees the wall margin and leaves 62×62 cells for actual geometry. Over that, the export fails with a real number in the message rather than writing a truncated level.

The report is the feature

Every export comes back with a count of partial cells: cells where the centre and the four inset corners disagree about which sector they're in. That is exactly the set of cells where the map is off the 64 grid, and exactly the geometry that came out stair-stepped. Zero partial cells means the conversion was exact.

The dialog runs the same rasteriser as a dry run before anything is written, so the two things worth knowing up front — does it fit, and how much stair-stepping is coming — are on screen while you can still cancel: "22 x 18 tiles, 4 floor(s). Fully on the 64-unit grid: the geometry converts exactly."

Not breaking someone's project

The export target is a whole EFPSE project folder — the one holding Textures.dat — because an .eem means nothing without the Textures.dat that numbers its tiles and the Maps.dat that lists it. Writing into a project someone has been building for months calls for some paranoia:

The mode: stop losing geometry at export time

The exporter knows all the rules above. The problem is that it learns them too late. By the time you export, the wall you placed at x=31 has already become a staircase and the floor you set to 100 has already been rounded away.

So the rules moved into the editor as EFPSE Editing Mode (View menu, sticky per user like a chosen grid size):

It is an editing constraint, not a file format. A map authored in this mode is an ordinary map, and turning the mode off leaves it untouched.

Get Level Editor Comment on itch.io Level Editor overview