maniator / maniator/verticopolis

[Bug][P1]: every shaft written after an EXPRESS slot is lost by the 1994 game

Open
#740 10 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
5
Forks
1
PR merge metrics
No merged PRs in 30d

Description

Isolated on the Wine harness 2026-07-31 while fixing the spanned-floor payload stride.

What happens

In a .TDT we export, the retail 1994 game loads the first express shaft correctly (it renders, with its stop settings) and then loses every elevator written after it. Shafts written BEFORE the express are unaffected.

Isolation (minimal two-shaft towers, one screenshot each)

tower slot 0 slot 1 result
CHAIN express 1-30, 27 skips standard 30-45 slot 1 lost
CHAIN3 express 1-30, no skips standard 30-45 slot 1 lost
CHAIN2 standard 1-30, 27 skips standard 30-45 both render
CHAIN4 standard 30-45 express 1-30 both render

So it is not stop-skipping (CHAIN2 skips 27 of 30 floors and is fine) and not span. It is the express record itself.

What it is NOT

The obvious guess is that an express slot's built-shaft payload is a different size than 194 + 3140 + 324 * spannedFloors + 348. Six byte-level probes on CHAIN3 (splicing the express payload only) all still lost the following shaft:

  • longer: +348, +9,720 (324 x 30), +29,160 (324 x 90, i.e. a full 120-floor block)
  • shorter: -348 (no car block), -3,140 (no fixed block), -9,720 (no per-floor entries)

Six misses across that range suggests the game is rejecting the express entry and abandoning the rest of the table, rather than mis-sizing it. If so the defect is in a HEADER field of the express record, not its length. Candidates to bisect next: the capacity byte (we write 42), the width/x (express is 8 tiles wide in our engine), the car count, the 56-byte schedule block, and the serviced bitmap for a whole-tower span.

Impact and current mitigation

This is why the owner's 4-star tower rendered only a handful of its 23 shafts: its express sits in slot 5, so slots 6-22 were lost, and floors served only by those shafts became unreachable.

Mitigated in tdtEncoder.ts by writing express shafts LAST (stable sort). Measured on the owner's tower: shaft count recovered from a handful to a dozen-plus. A tower with ONE express now keeps every other shaft; a tower with TWO or more still loses everything after the first express, so this stays open until the express record itself is understood.

Story id: tdt-express-desync

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in tdtEncoder.ts and reproduce the minimal CHAIN3 two-shaft case against the retail 1994 game. Compare the express record's header fields, including capacity, width/x, car count, schedule block, and serviced bitmap, rather than changing payload length. Done means shafts after an express remain loadable, including when a tower has multiple express shafts.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
game-dev, reverse-engineering
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.