Fork the vendored engine into the playable replica
Engine changes vs the vendored upstream (full ledger in docs/upstream-patches.md): - Assets: replace the encrypted FILE000N.GLB archives with a catalog of pre-decrypted loose files (data/INDEX); GLB_SlotItems() drives mission-pack / registered-version detection; payloads never run the GLB cipher at runtime. - Platform: Linux/SDL2/ALSA only; Android, MSVC/MinGW, macOS packaging, bundled Windows SDL2 SDKs and the ENDOOM exit screen are gone; EXIT_Error now prints before exiting. - Rendering: decouple presentation from simulation -- sim stays at the original 70/3 Hz, frames render interpolated at display rate (player, map scroll, enemies, shots, bonuses, anims, shadows); menus keep 70 Hz pacing; vsync with a delay-guard fallback. - QoL: borderless window mode (Video/borderless), no menu cursor snapping onto default buttons. Fix four regressions from the render split: - Shield bars never redrew: RAP_ThinkStats latches g_oldshield each sim step before RAP_DisplayStats' dirty check could fire. Bars now draw unconditionally per rendered frame; g_oldsuper deleted, g_oldshield kept as sim-side damage-detection state only. - Dying ships despawned without exploding: the render loop never called ANIMS_DisplayHigh(); most air-death explosions register HIGH_AIR. Restored between the player and enemy shots, as upstream orders it. - Laser turret broke: reusing doneflag as a draw marker (it is hit/retire game state, and the turret retire path never frees its shot) hid the beam on hitting ticks and leaked one SHOTS pool slot per fire until no weapon could shoot. S_LINE tracers now carry an age counter: one Think pass, every render of their birth step, purged at the top of the next Think. - Muzzle flash was cleared per rendered frame, making it invisible at display refresh rates; now cleared once per simulation step.
This commit is contained in:
1 parent
f7c9b7a02d
commit
214789fe87
667 files changed
+1490
-279572
No files matched your search
@@ -0,0 +1,46 @@
|
||||
# RaptorReplica
|
||||
|
||||
A faithful, playable replica of **Raptor: Call of the Shadows** (Cygnus
|
||||
Studios, 1994) running the reconstructed original engine on Linux, with the
|
||||
original game data extracted into a modern, editable asset tree.
|
||||
|
||||
## Quick start
|
||||
|
||||
```sh
|
||||
./play.sh
|
||||
```
|
||||
|
||||
Builds the engine on first run, then launches fullscreen. Saves and
|
||||
`SETUP.INI` live in `~/.local/share/Raptor/` — nothing in the repo is
|
||||
written to.
|
||||
|
||||
## Repository layout
|
||||
|
||||
| Path | What it is |
|
||||
|-------------|------------|
|
||||
| `raptor/` | Vendored engine source (GPLv2+), trimmed to a Linux-only build. Patches against upstream are listed in [upstream-patches.md](upstream-patches.md). |
|
||||
| `data/` | The asset tree the engine loads: one `INDEX` catalog plus category directories (`music/`, `sprites/`, ...). Derived from the copyrighted originals — **gitignored, never commit**. See [asset-pipeline.md](asset-pipeline.md). |
|
||||
| `tools/glbextract.py` | Cracks the original `.GLB` archives into `data/`. See [glb-format.md](glb-format.md). |
|
||||
| `docs/` | This documentation. |
|
||||
| `play.sh` | One-command launcher (build if needed, run from `data/`). |
|
||||
| `PLAN.md` | Project status and roadmap. |
|
||||
|
||||
## Documentation index
|
||||
|
||||
- [architecture.md](architecture.md) — how the engine starts up and loads assets
|
||||
- [glb-format.md](glb-format.md) — the original archive format and its cipher
|
||||
- [asset-pipeline.md](asset-pipeline.md) — extraction, categories, the INDEX catalog
|
||||
- [building.md](building.md) — dependencies, building, config and save paths
|
||||
- [upstream-patches.md](upstream-patches.md) — every local diff against the vendored engine
|
||||
|
||||
## Status
|
||||
|
||||
Playable end to end: title/menu, hangar, store, ship computer, all mission
|
||||
phases, original OPL2 soundtrack and Sound Blaster SFX through PipeWire/ALSA.
|
||||
Verified on this machine at 1920x1080.
|
||||
|
||||
## Legal
|
||||
|
||||
- `raptor/` is GPLv2+; this project inherits GPL for everything built on it.
|
||||
- Game data is copyrighted. `data/` is derived from the user's own purchased
|
||||
copy and is never committed.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Engine architecture
|
||||
|
||||
The engine is the reconstructed original Raptor source (nukeykt's port,
|
||||
vendored under `raptor/`), patched for loose-file assets and Linux. All game
|
||||
code is C++11, built with CMake + SDL2 + ALSA.
|
||||
|
||||
## Startup flow
|
||||
|
||||
`main()` in `raptor/src/rap.cpp`:
|
||||
|
||||
1. `RAP_InitLoadSave()` — resolves the user data dir via
|
||||
`SDL_GetPrefPath("", "Raptor")` (`~/.local/share/Raptor/`). Saves,
|
||||
`SETUP.INI` and high scores live there; the repo stays read-only.
|
||||
2. **Data detection** — `GLB_SlotItems(n)` (from `src/glbapi.cpp`) parses
|
||||
`data/INDEX` and counts assets per archive slot:
|
||||
- slot 1 present -> `gameflag[0]` (mission pack 1)
|
||||
- slot 2 present -> `gameflag[1]` (mission pack 2)
|
||||
- slots 3 and 4 present -> `gameflag[2]`, `gameflag[3]`
|
||||
- slot 2 or 3 present -> `reg_flag = 1` (registered version)
|
||||
- slot 0 missing or no packs -> fatal "data files NOT FOUND"
|
||||
3. `GLB_InitSystem(RAP_GetPath(), 6, 0)` — loads the catalog, builds one
|
||||
item-descriptor array per non-empty slot (name + size per item).
|
||||
4. Sound, input, graphics init; palettes/fonts/UI load from the catalog.
|
||||
5. Main loop dispatches between intro, menu, hangar, store, ship computer
|
||||
and mission gameplay.
|
||||
|
||||
## Asset system
|
||||
|
||||
Assets are addressed by **handles**: `(archive slot << 16) | item index`.
|
||||
The constants live in `src/fileids.h` (e.g. `FILE100_PALETTE_DAT = 0x10000`
|
||||
= slot 1, item 0). At runtime:
|
||||
|
||||
- `data/INDEX` maps each handle to a file: `<hex id> <size> <path>`, sorted
|
||||
by id. `GLB_LoadCatalog()` parses it once; `GLB_CatFind()` binary-searches
|
||||
it.
|
||||
- `GLB_GetItem / GLB_LockItem / GLB_CacheItem` allocate and read the file
|
||||
through a VM/cache layer (`src/vmemapi.cpp`) — locked items stay resident,
|
||||
others are discarded under memory pressure.
|
||||
- `GLB_GetItemID(name)` resolves a NUL-terminated item name to a handle by
|
||||
scanning the per-slot descriptor arrays.
|
||||
|
||||
The "GLB" API names are historical — no `.GLB` archive is read at runtime.
|
||||
|
||||
## Archive slots
|
||||
|
||||
| Slot | Items | Content |
|
||||
|------|--------|---------|
|
||||
| 0 | 230 | Text files, help, credits, music (`*_MUS`), SFX (`*_FX`), `GENMIDI_OP2`, `DMXGUS_INI` |
|
||||
| 1 | 1538 | Fonts, palettes, UI pictures, SWD window layouts, animations |
|
||||
| 2 | 1708 | Mission pack 2 assets (tiles, maps, sprites, demos) |
|
||||
| 3 | 1321 | Mission pack 3 assets |
|
||||
| 4 | 1559 | Mission pack 4 assets |
|
||||
| 5 | 92 | Extra data (not referenced by `fileids.h`) |
|
||||
| 6 | 1 | Uninstaller leftover |
|
||||
|
||||
## Subsystems
|
||||
|
||||
- **Graphics** (`gfxapi.cpp`, `i_video.cpp`): original 320x200 VGA-mode
|
||||
renderer, palette-faded, scaled to the desktop by SDL. SWD (`swdapi.cpp`)
|
||||
is the window/widget system driving every menu.
|
||||
- **Music** (`i_oplmusic.cpp`, `opl3.cpp`): the original OPL2 FM soundtrack,
|
||||
emulated sample-accurately; `*_MUS` items are OPL register captures.
|
||||
- **SFX** (`fx.cpp`, `dspapi.cpp`): Sound Blaster digital effects, mixed
|
||||
through ALSA/PipeWire.
|
||||
- **MIDI** (`mpualsa.cpp` etc.): optional external MIDI path using
|
||||
`GENMIDI_OP2`/`DMXGUS_INI`.
|
||||
- **Movies** (`movie.cpp`): intro cinematics described by `*_MDS` scripts.
|
||||
- **Demos** (`demo.cpp`): attract-mode gameplay recordings (`*_REC`).
|
||||
|
||||
## Exit path
|
||||
|
||||
`ShutDown()` (`rap.cpp`) closes the window, frees the asset cache and quits.
|
||||
The upstream DOS-style ENDOOM farewell screen was removed — quitting exits
|
||||
immediately. See [upstream-patches.md](upstream-patches.md).
|
||||
@@ -0,0 +1,87 @@
|
||||
# Asset pipeline
|
||||
|
||||
`data/` is a plain directory tree the engine reads directly. It is derived
|
||||
from the copyrighted original archives, so it is **gitignored** — restore it
|
||||
with the extractor, never commit it.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
data/
|
||||
├── INDEX # catalog: "<hex asset id> <size> <relative path>", sorted by id
|
||||
├── music/ # *_MUS OPL2 register captures
|
||||
├── sound/ # *_FX, *_SFX Sound Blaster digital samples
|
||||
├── sprites/ # *_PIC, *_AGX pictures and animated sprites
|
||||
├── tiles/ # *_BLK, *TILES level tile graphics
|
||||
├── maps/ # *_MAP mission maps
|
||||
├── fonts/ # *_FNT bitmap fonts
|
||||
├── text/ # *_TXT, *_DIZ, *_DIX, *_ERR, *_HELP
|
||||
├── ui/ # *_SWD menu/window layouts
|
||||
├── movies/ # *_MDS cinematic scripts
|
||||
├── demos/ # *_REC attract-mode recordings
|
||||
└── misc/ # palettes, GENMIDI_OP2, DMXGUS_INI, item tables, labels
|
||||
```
|
||||
|
||||
Categories are derived from the GLib item-name suffixes (see `classify()`
|
||||
in `tools/glbextract.py`).
|
||||
|
||||
## INDEX catalog
|
||||
|
||||
One line per asset:
|
||||
|
||||
```
|
||||
<hex id> <size in bytes> <category>/<filename>
|
||||
10005 768 sprites/N0_PIC
|
||||
```
|
||||
|
||||
- `id` is the engine handle: `(archive slot << 16) | item index`, matching
|
||||
the constants in `raptor/src/fileids.h`. Example: `10005` = slot 1,
|
||||
item 5 = `FILE105_N0_PIC`.
|
||||
- Lines are sorted by id; the engine binary-searches them
|
||||
(`GLB_CatFind` in `raptor/src/glbapi.cpp`).
|
||||
- `size` is trusted by the engine; the extractor guarantees it matches the
|
||||
file on disk.
|
||||
|
||||
## Regenerating data/
|
||||
|
||||
From a copy of the original game (the seven `FILE000N.GLB` archives):
|
||||
|
||||
```sh
|
||||
tools/glbextract.py extract -o data FILE0000.GLB ... FILE0006.GLB
|
||||
```
|
||||
|
||||
The extractor decrypts everything, classifies by name suffix, resolves name
|
||||
collisions with numeric suffixes (`unnamed.3` -> `unnamed.3.1` when a second
|
||||
archive contributes the same name to the same category — suffixes are global
|
||||
per category so catalog sizes always match the file on disk), and writes the
|
||||
catalog.
|
||||
|
||||
`tools/glbextract.py list FILE0000.GLB` dumps an archive's descriptor table
|
||||
without writing anything.
|
||||
|
||||
## Editing content
|
||||
|
||||
Because the engine reads loose files through the catalog:
|
||||
|
||||
- **Replace an asset**: overwrite the file, keep the path. If the size
|
||||
changes, update the size in `INDEX` (the engine allocates and reads by
|
||||
that number).
|
||||
- **Add a mission pack**: add files under a category, append catalog lines
|
||||
with unused ids in the desired slot (slots 2-4 are the packs; slot ids are
|
||||
`0x2xxxx`, `0x3xxxx`, `0x4xxxx`). Presence of slots 2/3 flips the engine
|
||||
into registered mode.
|
||||
- **Rename**: never rename an existing catalog file without updating its
|
||||
`INDEX` line — the engine looks assets up by id, but item *names* (used by
|
||||
`GLB_GetItemID`) come from the path's basename.
|
||||
|
||||
## Engine-side contract
|
||||
|
||||
`raptor/src/glbapi.cpp` is the only code that touches `data/`:
|
||||
|
||||
- `GLB_LoadCatalog()` — parses `INDEX` (working directory first, then the
|
||||
directory the binary was launched from).
|
||||
- `GLB_SlotItems(slot)` — counts a slot's assets (used for pack detection).
|
||||
- `GLB_CatFind(id)` — binary search.
|
||||
- `GLB_Load()` — `fopen` + `fread` of the cataloged file.
|
||||
|
||||
Payloads are stored pre-decrypted; the runtime never runs the GLB cipher.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Building and running
|
||||
|
||||
## Requirements
|
||||
|
||||
- Linux with X11 (developed and verified on Arch, Cinnamon)
|
||||
- GCC or Clang with C++11
|
||||
- CMake >= 3.10
|
||||
- SDL2 development libraries (a user-local build in `~/.local` works —
|
||||
`play.sh` extends `LD_LIBRARY_PATH` accordingly)
|
||||
- ALSA (PipeWire's ALSA bridge is what this machine actually uses)
|
||||
|
||||
## Build and run
|
||||
|
||||
```sh
|
||||
./play.sh
|
||||
```
|
||||
|
||||
`play.sh` checks `data/INDEX`, builds `raptor/build/bin/raptor` on first run
|
||||
(`cmake -DCMAKE_BUILD_TYPE=Release`, then `make -j$(nproc)`), and executes
|
||||
the engine with `data/` as the working directory — the engine resolves the
|
||||
catalog and assets relative to the CWD.
|
||||
|
||||
Manual equivalent:
|
||||
|
||||
```sh
|
||||
cmake -E make_directory raptor/build
|
||||
cmake -S raptor -B raptor/build -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build raptor/build -j$(nproc)
|
||||
cd data && ../raptor/build/bin/raptor
|
||||
```
|
||||
|
||||
Targets:
|
||||
|
||||
- `raptor` — the game
|
||||
- `raptorsetup` — the original text-mode setup tool (audio/input settings)
|
||||
|
||||
## Runtime files
|
||||
|
||||
| File | Location |
|
||||
|------|----------|
|
||||
| `SETUP.INI` | `~/.local/share/Raptor/` |
|
||||
| Save games | `~/.local/share/Raptor/` |
|
||||
| High scores | `~/.local/share/Raptor/` |
|
||||
|
||||
Useful `SETUP.INI` settings (created on first exit): `fullscreen=1` for
|
||||
borderless 1080p, `aspect_ratio_correct`, `txt_fullscreen` (obsolete here —
|
||||
the text-mode exit screen was removed).
|
||||
|
||||
## Command-line options
|
||||
|
||||
The engine accepts the original demo flags:
|
||||
|
||||
```sh
|
||||
raptor REC demo.dmo # record a demo
|
||||
raptor PLAY demo.dmo # play one back
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **"All game data files NOT FOUND"** — `data/INDEX` missing or empty;
|
||||
restore the asset tree (see [asset-pipeline.md](asset-pipeline.md)).
|
||||
- **No audio** — check the ALSA/PipeWire sink; the original mixer expects a
|
||||
working default ALSA device. If a session-manager glitch leaves the wrong
|
||||
card profile active, `pactl set-card-profile` back to the real one.
|
||||
- **Silent early exit** — since the local patch, engine errors print to
|
||||
stderr before exiting; run from a terminal and read the message.
|
||||
|
||||
## Build layout
|
||||
|
||||
- `raptor/CMakeLists.txt` — Linux-only (Windows/Android/OSX support was
|
||||
stripped; see [upstream-patches.md](upstream-patches.md))
|
||||
- `raptor/build/` — gitignored build tree
|
||||
- `raptor/include/textscreen/` — text UI library (used by `raptorsetup`)
|
||||
- `raptor/include/TinySoundFont/` — SF2 MIDI fallback synth
|
||||
@@ -0,0 +1,74 @@
|
||||
# GLB archive format
|
||||
|
||||
The original game ships its data as `FILE0000.GLB` ... `FILE0006.GLB`,
|
||||
produced by the "GLib" library bundled with the commercial source. The
|
||||
format below was reverse-engineered from `raptor/src/glbapi.cpp` (upstream
|
||||
loader) and verified against all seven shipped archives (6449 items total).
|
||||
|
||||
## Container
|
||||
|
||||
A GLB file is a flat sequence of 28-byte records:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
uint32_t opt; // 0 = plain payload, 1 = encrypted payload
|
||||
uint32_t offset; // payload offset in the file
|
||||
uint32_t filesize; // payload size in bytes
|
||||
char name[16]; // NUL-padded item name, e.g. "GENMIDI_OP2"
|
||||
} KEYFILE; // little-endian fields
|
||||
```
|
||||
|
||||
- **Record 0 is the header.** Its `offset` field holds the *item count*;
|
||||
its other fields are unused.
|
||||
- Records `1 .. count` are the item descriptor table, starting at byte 28.
|
||||
- Item payloads live at each descriptor's `offset`, `filesize` bytes long.
|
||||
- Names are unique per archive only in practice; duplicates (e.g. dozens of
|
||||
`CHASE_AGX`, thousands of empty `unnamed` labels) are common.
|
||||
- Descriptor entries with `filesize == 0` are labels/placeholders.
|
||||
|
||||
## Cipher
|
||||
|
||||
Every record *and* every payload flagged `opt == 1` is scrambled with a
|
||||
rolling additive cipher keyed on the ASCII string `32768GLB`, seeded at 25
|
||||
(`SEED` in `glbapi.h`):
|
||||
|
||||
```
|
||||
kidx = 25 % len(key) # 25 % 8 = 1
|
||||
prev = key[kidx]
|
||||
|
||||
# decrypt (GLB_DeCrypt):
|
||||
for each byte b:
|
||||
out = (b - key[kidx] - prev) mod 256
|
||||
prev = b # chain uses the CIPHERTEXT byte
|
||||
kidx = (kidx + 1) mod len(key)
|
||||
|
||||
# encrypt (GLB_EnCrypt) is the inverse:
|
||||
out = (b + key[kidx] + prev) mod 256
|
||||
prev = out # chain uses the PLAINTEXT byte
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- The cipher state resets **per record** and **per payload**.
|
||||
- The original C uses signed `char`; the stored byte equals the
|
||||
mathematical result mod 256 either way, so unsigned arithmetic is
|
||||
byte-exact.
|
||||
- Payload encryption is independent of descriptor encryption: an archive
|
||||
typically mixes plain and encrypted items.
|
||||
|
||||
## Verification
|
||||
|
||||
`tools/glbextract.py` implements this format. Correctness was checked
|
||||
against known payloads:
|
||||
|
||||
- `GENMIDI_OP2` starts with the DMX magic `#OPL_II#` (11908 bytes)
|
||||
- `PALETTE_DAT` is exactly 768 bytes (256 x RGB)
|
||||
- `DMXGUS_INI` decodes to readable INI text
|
||||
- item counts per archive match the id ranges in `raptor/src/fileids.h`
|
||||
(e.g. slot 0: 230 items, last id `0x0e5`)
|
||||
- every payload lies within its archive's bounds (extractor warns otherwise)
|
||||
|
||||
## See also
|
||||
|
||||
- [asset-pipeline.md](asset-pipeline.md) — how archives become `data/`
|
||||
- `tools/glbextract.py` — reference implementation (Python, stdlib only)
|
||||
@@ -0,0 +1,139 @@
|
||||
# Patches against upstream
|
||||
|
||||
Base: nukeykt's reconstructed Raptor source (skynettx/raptor mirror),
|
||||
vendored in `raptor/` (GPLv2+, see `raptor/LICENSE` and `raptor/VENDOR.txt`).
|
||||
Everything below diverges from upstream on purpose. Keep this list current
|
||||
when touching engine code.
|
||||
|
||||
## Asset system: catalog of loose files (largest change)
|
||||
|
||||
Upstream reads seven encrypted `FILE000N.GLB` archives via
|
||||
`src/glbapi.cpp`. This fork replaces the archive reader with a catalog of
|
||||
loose files:
|
||||
|
||||
- `GLB_LoadCatalog()` parses `data/INDEX` (`<hex handle> <size> <path>`,
|
||||
sorted); `GLB_CatFind()` binary-searches it; `GLB_Load()` reads the file.
|
||||
- `GLB_SlotItems(slot)` (new public API, `src/glbapi.h`) counts a slot's
|
||||
assets; `src/rap.cpp` uses it for mission-pack and registered-version
|
||||
detection instead of probing for `.GLB` filenames.
|
||||
- `GLB_InitSystem` keeps its signature but ignores the archive-name prefix
|
||||
parameter.
|
||||
- Payloads are pre-decrypted at extraction time; the runtime never runs the
|
||||
GLB cipher. `GLB_EnCrypt/GLB_DeCrypt` remain for reference/tooling.
|
||||
- `FILEDESC` shrank to `{item, items}`; the archive file-handle cache
|
||||
(`GLB_FindFile/GLB_OpenFile/GLB_CloseFiles`) was removed.
|
||||
|
||||
Rationale and format docs: [glb-format.md](glb-format.md),
|
||||
[asset-pipeline.md](asset-pipeline.md).
|
||||
|
||||
## Exit: no ENDOOM screen
|
||||
|
||||
Upstream's `ShutDown()` (`src/rap.cpp`) loaded `LASTSCR*_TXT` and opened a
|
||||
second, text-mode "farewell" window (`I_LASTSCR`, `src/i_lastscr.cpp`)
|
||||
that blocked until a keypress. Removed: `ShutDown` now closes the game
|
||||
window and exits directly; `src/i_lastscr.cpp/.h` deleted.
|
||||
|
||||
## Diagnostics: EXIT_Error prints
|
||||
|
||||
Upstream's `EXIT_Error` (`src/common.h`) was a stub that called `exit(0)`
|
||||
without printing — every engine error died silently. It now prints its
|
||||
formatted message to stderr before exiting (still `exit(0)`, matching the
|
||||
original exit code behavior).
|
||||
|
||||
## Platform: Linux-only
|
||||
|
||||
- `CMakeLists.txt` reduced to the Linux/SDL2/ALSA build; the Android
|
||||
(FetchContent SDL), Windows (MSVC/MinGW + `rsrc/` resources) and macOS
|
||||
branches are gone.
|
||||
- Deleted upstream trees: `raptor/android/`, `msvc/`, `gcc/`, `pkg/`,
|
||||
`rsrc/`, `win32build.bat`, `osxbuild.sh`, the bundled Windows SDL2 SDKs
|
||||
under `include/`, and the `SETUP(*).INI` distribution templates.
|
||||
- `src/loadsave.cpp`: removed the `__ANDROID__` block that copied `.GLB`
|
||||
files to external storage (obsolete concept in this fork).
|
||||
|
||||
## Data detection
|
||||
|
||||
`src/rap.cpp`: mission-pack presence is derived from catalog slot counts
|
||||
(`GLB_SlotItems`) rather than `access("FILE000N.GLB")` probes; the fatal
|
||||
"data files NOT FOUND" check now keys off slot 0 / no packs.
|
||||
|
||||
## Rendering: decoupled presentation loop
|
||||
|
||||
The original game advanced simulation and presentation in lockstep at
|
||||
70/3 Hz (~23 fps): `Do_Game()` ran one think+draw pass, then waited three
|
||||
timer ticks. This fork keeps the **simulation** at exactly that rate but
|
||||
presents interpolated frames at display rate:
|
||||
|
||||
- Simulation is paced by a wall-clock accumulator (`RAP_SIM_STEP` in
|
||||
`src/rap.cpp`, units of 1/70000 s = 3 ticks). Input handling, all
|
||||
`*_Think()`, `DEMO_Think()` (one attract-mode record per step), palette
|
||||
fades and the stats state machine run once per step, preserving game
|
||||
speed, demo playback timing and RNG consumption order.
|
||||
- After each step the scene is redrawn and presented with an interpolation
|
||||
fraction until the next step is due (`GFX_SetInterp` /
|
||||
`GFX_InterpCoord`, `src/gfxapi.cpp`). When behind schedule (blocking
|
||||
menus ate wall time) renders are skipped so the simulation catches up,
|
||||
matching the old tick-wait behavior.
|
||||
- Draw positions are lerped between the previous and current simulation
|
||||
states for player ship, tile map scroll, enemies, enemy/player shots,
|
||||
bonuses, anims and shadows. The map scroll interpolates an unwrapped
|
||||
counter (`vscroll` in `TILE_Scroll`) because raw `tileyoff` wraps by
|
||||
32 px while the on-screen tile grid stays positionally continuous;
|
||||
`TILE_Scroll` runs before `TILE_Think` so wrap intervals render
|
||||
seamlessly.
|
||||
- State changes that used to ride inside draw calls were moved to
|
||||
simulation or gated per step: enemy engine-flame toggle (`eframe`,
|
||||
`ENEMY_Think`), beam-shot animation counter (`SHOTS_Think`),
|
||||
ion-scanner sweep (`OBJS_DisplayStats`, gated by `gl_cnt`), S_LINE
|
||||
tracers are purged by `SHOTS_Think` instead of being removed mid-draw.
|
||||
The S_LINE purge keys off a per-shot `age` counter (one simulation step
|
||||
of life, matching upstream's draw-then-remove); the turret's retire
|
||||
path never frees its shot, so the purge must not reuse `doneflag`,
|
||||
which is hit/retire game state — reusing it leaked one `SHOTS` pool
|
||||
slot per turret fire until `SHOTS_Get()` could service no weapon, and
|
||||
hid the beam on hitting ticks.
|
||||
- `RAP_DisplayStats` was split into `RAP_ThinkStats` (state: death
|
||||
sequence, wave-exit control, low-shield warnings) and the drawing half.
|
||||
The shield bars are redrawn unconditionally each rendered frame: the
|
||||
old `g_oldsuper`/`g_oldshield` dirty check can never fire when the
|
||||
latch runs in `RAP_ThinkStats` before the draw, which froze the bars.
|
||||
`g_oldsuper` is gone; `g_oldshield` survives as the sim-side
|
||||
previous-step shield used for damage detection. The muzzle-flash ship
|
||||
pic (`g_flash`) is likewise cleared once per simulation step, not per
|
||||
rendered frame, or it would be invisible at display refresh rates.
|
||||
- The above-player anim layer (`ANIMS_DisplayHigh`) is drawn between the
|
||||
player and enemy shots as upstream does; most air-death explosions
|
||||
register `HIGH_AIR`, so dropping that call made dying ships vanish
|
||||
without exploding.
|
||||
- Vsync is enabled when the display reports a refresh rate;
|
||||
`I_VsyncActive()` lets the game loop fall back to a delay guard when
|
||||
the renderer ignores the flag.
|
||||
- Menus keep their 70 Hz `GFX_DisplayUpdate` pacing; intro/story screens
|
||||
still pace at 4 ticks (~17.5 fps).
|
||||
|
||||
## Video: borderless window mode
|
||||
|
||||
New `Video/borderless` SETUP.INI option (`VIDEO_LoadPrefs`). When set,
|
||||
`SetVideoMode` creates a decoration-free window at the desktop resolution
|
||||
anchored at (0,0) (`SDL_WINDOW_BORDERLESS`). Alt+Enter cycles between the
|
||||
borderless desktop window and a normal decorated window while the mode is
|
||||
active. Exclusive `fullscreen_width/height` modes ignore it; if both
|
||||
`fullscreen=1` and `borderless=1` are set, `fullscreen` wins. The stale
|
||||
commented-out `-borderless` command-line snippet was removed.
|
||||
|
||||
## Input: no cursor snapping
|
||||
|
||||
Dialog/menu code no longer warps the mouse cursor onto the default
|
||||
button: `SWD_SetWindowPtr`/`SWD_SetFieldPtr` were deleted along with all
|
||||
callers (`store.cpp`, `windows.cpp`), as were the direct
|
||||
`PTR_SetPos(field-center)` calls when opening ask dialogs, the difficulty
|
||||
select, the hangar keyboard navigation and the ship computer. Keyboard
|
||||
selection still highlights fields without moving the pointer. The
|
||||
mission-start `PTR_SetPos(playerx, playery)` in `Do_Game` is kept — it
|
||||
seeds mouse ship control, not menu focus.
|
||||
|
||||
## Deliberately unchanged
|
||||
|
||||
- Game logic, mission data formats, audio synthesis — untouched.
|
||||
- `GLB_*` API names and handle layout (`slot << 16 | item`) — kept so
|
||||
`src/fileids.h` and all call sites stay identical to upstream.
|
||||
Reference in new issue
Block a user