Repository navigation
KeeperFX: Tux Edition 1.4.0.6010 alpha
Pre-releaseWarning
Saved games from earlier versions cannot be loaded by this release — for the last time.
Saved games used to be a direct copy of the engine's memory, so any change to
it made every older save unreadable. From this version on a saved game
describes its own layout and is read field by field, so saves made with this
release keep loading in later ones. Saves from earlier versions are kept on
disk and listed greyed out; they still open in the version that wrote them.
Campaign progress is not affected: Continue, completed levels, bonus levels
and the creatures carried to the next level all carry over, as do high scores.
Finish a level in progress on your current version before updating, or replay
it from its start.
Saved games from earlier versions cannot be loaded by this build — for the last time. With the
team's new save format (below) a saved game describes its own layout and is read field by field, so
saves made with this build keep loading in later ones. Saves from 1.4.0.5762-alpha, from stable
1.4.0.5660 and from every earlier build are kept on disk and listed greyed out under their titles;
they still open in the version that wrote them, and saving into one of their slots moves the old
save to the trash instead of writing over it. Finish a level in progress on your current version
first, or replay it from its start. The release notes repeat this automatically. Campaign
progress — Continue, completed levels, bonus levels and the creatures carried to the next level —
carries over, and so do high scores (see below).
No more Windows in this repository.
- The team's Windows content is gone, and the weekly sync cannot bring it back. Their Visual Studio
project, the MinGW/MSVC CMake build and vcpkg, a Windowsgdb.exe, CUnit's Visual C++ solutions,
the Windows-only platform code, their Windows CI, and every#ifdef _WIN32branch in the engine and
the vendored libraries — 281 files, about 101,000 lines. The sync no longer merges the team's
commits directly: it takes their tree, removes the Windows content (packaging/linux-only/), and
merges that snapshot, so Windows files are on neither side of a merge and never conflict again.
Proven not to change the game: every file the Linux build compiles was preprocessed with and
without the filter — 315 identical, one with identical machine code — and the sync repeats that
proof every week. A guard workflow fails any pull request that reintroduces Windows content. - The sync also adds the team's new source files to
linux.mkby itself (previously a manual edit
after every sync that added one), generates the fonts and runs the unit tests, and when it cannot
finish it updates the open sync issue instead of opening a new one every Monday. Upstream's
Makefilestays: the release builds use it to regenerate the artwork.
The team's changes through 9 October (48 commits, merged through the new Linux-only sync).
Among them: every campaign you play keeps its own progress, and a saved game that fails to load
resumes that campaign instead; replays can be zoomed, paused, turned off, and survive resyncs;
multiplayer gains spectators, keeps lobbies listed while playing, and fixes several desyncs; the
parchment-map fade is on by default at every resolution (PARCHMENT_MAP_FADE=OFF turns it off);
creatures and objects get configurable inertia and fall acceleration; Lua scripts are sandboxed and
read the date and time without desyncing multiplayer; plus fixes to flying over lava, boulders,
hatcheries, the abyss, possession and camera stutter with VSync. Two of these needed Linux fixes:
- Campaign progress carries over. The team moved progress from one
fx1contn.savto a file per
campaign and imports the old file once — but only in the layout 32-bit Windows writes. Every Linux
build wrote it 160 bytes longer (its campaign flags were 8-bytelongs), so the import would have
skipped it and the Continue button would have vanished with every player's campaign position. The
64-bit layout is now imported too, checked field by field against a file written by stable
1.4.0.5660 itself. The old file is also left in place instead of deleted, so going back to a
stable build that predates the change keeps that build's own Continue. - The new multiplayer matchmaking passed curl a 4-byte timeout where curl reads 8 bytes on 64-bit
Linux, leaving half of it undefined.
The team's changes through 19 October (48 commits, 13–19 October). The big one is the new save
format: saved games, the continue link and campaign progress describe their own layout and are read
field by field, so a change to the engine no longer makes every older save unreadable — after this
one last time (the box above). Among the rest: hold Backspace to rewind a replay, and replays skip the
intro cinematic; claimed rooms merge with adjacent rooms of the same kind; in multiplayer you pick your
colour in the lobby, lobby buttons respond at once, disconnect messages are clearer, and spectators are
shared among the players, switch view with Space or a click on a name, and can sync again; a faster
software parchment-map fade; a destroyed heart reveals the map after its explosion, not before;
tooltips no longer hide behind the hand; possession no longer slides or turns the creature north, and
the hand's light is off while possessing; chickens no longer breathe fire; the computer no longer
reuses, as defenders, creatures it has just dropped; Shift+M recording shows a timer;
CHAT_FONT_SIZE in keeperfx.cfg; USE_SPECIAL_STEAL_HERO and alternate player names in script
locations. It also brings fixes of theirs this fork had already taken early, among them a
temple that counted a creature leaving it twice, and so shielded fewer creatures from enemy scavenging
than were praying in it, and drag and shaped room tools that could start one slab outside the shape.
Some of it needed work here:
- The hand can pick creatures up. The team's new config check limits
longsettings to 32 bits, as
on Windows. On 64-bit Linux the hand's, Slap's and Pick Up Object's "what can it touch" flags are such a
setting and go past 32 bits, so they were refused at every level start and the hand picked up
nothing. Those settings take their full 64-bit values again, as in every earlier Linux build. - Campaign progress still carries over. The new code again imports the old
fx1contn.savonly in
the 32-bit layout (67,241 bytes). Every Linux build wrote 67,401, stable 1.4.0.5660 included, so a
5660 player would have lost Continue and the progress percentage, and the campaign, its bonus
levels, carried creatures and flags would have started over. The 64-bit import is back, the
old file is still kept for stable, and Continue falls back to it only while that campaign has no
progress file of its own. - Saves from earlier Linux builds are listed under their titles, not as
fx1g0003.sav. The new
code reads a save's title after a 12-byte header; every save 64-bit Linux builds wrote before
1.4.0.5751, stable 1.4.0.5660 included, has 24-byte ones. Checked against a save stable 5660 wrote. - The message for a save that cannot be loaded is not blank. The download's game text came from
the team's July release, so every message added since showed as nothing: that one, and the newer
spectator and lobby messages. Every release now compiles the text from the team's translations, as
it already regenerates the artwork. - A save in a deep folder loads. The new loader cut a save's path to 144 bytes, so with a long
home directory or a Flatpak data directory the file read as missing and its slot was disabled. - A merged room keeps the creatures heading for it. Creatures on their way to a room that merges
into its neighbour, or that last worked there, still pointed at the absorbed room, whose number then
goes to the next room built.
Ten of the team's fixes from the week after, taken early: creatures stop fighting when their
keepers ally; creatures no longer ask for a lair while you cannot build one; moving a library's books
no longer turns that spell off; navigation is updated when a neighbouring slab changes; possession
camera fixes; rewinding a replay restores the maps and event boxes; no crash when the host leaves after
a failed join; a possible wrong slab display; a creature's previous work room survives changes to its
current one; and the hand's pickup priority. Next to the books fix, ours: another keeper losing a spell
— claiming or robbing an enemy library, or a neutral one shrinking — no longer clears your own
selection of it.
Creatures, rooms and imps. Most of these were found in players' logs from stable 1.4.0.5660.
- Workers keep working in their own room. A worker crossing a slab of another room between spots —
a bridge through a training room, the training room beside the workshop — quit: about a hundred
"is not valid ROOM_ROLE_TRAIN_EXP" lines in one log, most of them imps dropped in to train. On copies
of that player's saves: 9 and 37 such quits on 5660, none with the fix; in a U-shaped training room
around a bridge, all 8 trainees gone within 30 seconds before, all 8 reaching level 2 after. A worker
stepping off its room's edge for a moment no longer pays the training fee for no experience. - A creature dropped into training once and scavenging later was taken out of its scavenger room
every 20 turns and enrolled again, for the rest of the game: a computer keeper's vampire on original
level 20 did it 382 times in one session. - The computer's creatures the training room cannot take (level 10, or training it cannot afford)
go to the room they were picked for, not onto its heart: 355 errors in one level 20 log; on a staged
level 20, 69 in about 4000 turns before, none after. - Imps and traps. An imp that finds no route on to an empty trap passes over it for 1000 turns
instead of fetching a crate for it about every 130 turns (one log: a single trap failing 63 times over
17,000 turns; staged, 37 failed deliveries in about 5000 turns before, 5 after); arming the trap
clears the pause, and saved games keep it. An imp whose trap is sold or destroyed on the way no
longer takes whatever is given the trap's place for it (a possessed imp would have "re-armed" it).
The workshop tab's count of placeable traps is worked out from the crates and traps themselves,
instead of a running tally that crept up and offered traps there were no crates for. - Hatcheries. Shrinking a full hatchery left more chickens than it holds while counting fewer (8 in
a room for 5, then 7 counted as 1 of 2); the count now matches the room. A hungry creature whose
chicken is eaten on the way chooses another, instead of leaving hungry or eating whatever took the
chicken's place. - A creature reborn at its lair starts afresh, instead of carrying on in the state it died in (a
vampire killed at a door came back still "in door combat"). - Fights. A magic door that holds a boulder uses it up instead of leaving it 1–7 health; an
explosion or poison cloud hits everything on a subtile even when one hit removes another thing; a
creature pushed into the abyss credits its pusher with the kill again (only a fall ending within 15
turns of the push counted, and the fall takes at least 28). Call to Arms no longer sweeps a
black strip across the view after each cast: its charge bar ran past full, since the spell's
duration is 1. - A creature going for its pay no longer chooses its route from leftover memory, which can differ
between machines (a possible multiplayer or replay divergence). A script objective at a destroyed
dungeon heart points where it stood.
Multiplayer and replays.
- Chat and replays can no longer act on your machine. A command typed in chat runs on every
machine that sees it, replays included, and none checked who typed it: a replay containing
!game.save 1overwrote the viewer's own save, a recorded!game.loadcould crash the viewer, and
one player's!game.save Noverwrote slot N on every machine in the game. Commands acting on this
machine — its files, its settings, quitting — now run only when typed on it, never for another
player's chat or a replay's. Gameplay commands replay as before. - A chat message must come from the player it names. Any peer could speak as another: issue
console commands as the host's player (which pass the cheat check when the host allows cheats), or
start the game from the lobby as if the host had chosen a level. - A damaged or hostile replay file is refused instead of crashing the game or reading past a buffer.
Old replays are trimmed however long the install path (from about 125 characters nothing was
removed); a size limit of 4 GB or more no longer stops recording at once; a resync read as you stop
a replay is no longer applied to the next one.
64-bit fixes. KeeperFX was built for 32-bit Windows, where a long is 4 bytes; on 64-bit Linux
it is 8, and these behaved differently from Windows:
- Creature shadows and other textured polygons in the software renderer (the default) sampled the
wrong texture rows: the rasteriser's carry helpers worked on 64-bit values. Checked against a
32-bit model on 54,000 random triangles in all 27 drawing modes: 14,943 differences before, none after. - Level scripts:
ADD_EFFECT_GENERATOR_TO_LEVELplaced nothing,CREATE_EFFECTS_LINEaimed at a
garbage location,DISPLAY_VARIABLE_WITH_LABELread past the end of an array — script arguments
packed for 4-byte slots overlapped in 8-byte ones. The slots are 32-bit now, as on Windows, which
also retires the fork's earlier per-command workarounds. - Script timers and bonus time go negative again after
ADD_TO_TIMERwith a negative value, instead
of reading about 4.29 billion and firingIF(...TIMERn >= X)at once. Sight of Evil recast in the
first minute of a level moves the sight instead of closing it. Lua'sunsummon_durationreads 0 for a
creature that is not summoned. - Movie recording (Shift+M) wrote files nothing could open and crashed on the second frame.
- High-score files in the team's new format and in Windows' 72-byte layout are read as well as
the 80-byte one Linux builds write. Saving keeps the 80-byte layout for now, because stable
1.4.0.5660 shares the game folder and reads only that one: given anything else it would replace the
whole table with Bullfrog's defaults the next time a player switched back. - The light table in saved games was twice its saved size: saving copied 256 bytes into a 128-byte
field, over the light counters and 100 bytes of game state after them, in the running game and in
the file, and loading put dynamic lights in the wrong shadow-cache columns.
Mods and maps made on Windows. Windows does not care about upper/lower case in file names or which
slash is used; Linux does. Now handled the Windows way:
- custom sounds, a level's or campaign's Lua scripts (and
require), map and sprite zips, and map-pack
folders (LEVELS_LOCATION = levels/MyPackbeside a reallevels/mypack) are found whatever the case; \in a path (creature\maiden\hit1.wav,campgns\mycamp) is read as a separator;- a folder name containing
[,?or*(an install at/games/DK [GOG]) is no longer a wildcard; - Korean text is decoded as code page 949, as on Windows (EUC-KR lacks most Hangul), and one bad
character no longer empties a whole text file; MUSIC_FROM_DISK=OFFor-cdplay the music folder (there is no CD audio on Linux) instead of nothing.
A downloaded map's scripts can no longer change your files. Level and campaign Lua comes with
maps and Workshop items, and the team's new sandbox still let a script overwrite or delete any file
you own, or load a native library shipped beside it. Scripts can now read files and nothing more:
writing, appending and deleting are refused the way a failed open always was (so a script that checks
carries on), and require no longer loads native code. Nothing the game ships or the team's tree
contains uses any of that; the game's own Lua, including how it saves and restores script state, is
unaffected. It closes the one-line attacks, not every route: the full write-up is in
src/lua_readonly_io.c.
Map scripts.
- Lua scripts keep
os.date,os.time,os.clockandos.difftime, which the team's sandbox had
removed: the Pandaemonium campaign's second level builds its welcome message withos.date, so its
setup stopped there — no start money, creature limit, rooms, traps, allies or triggers. Multiplayer
still replaces them with its "not synchronised" error. SET_CREATURE_CONFIGURATION:INERTIAandFALLACCELERATIONreach the creatures already on the map
(a map places its creatures before its script runs, so they never did), andHEALTHandBASESPEED
reach neutral ones; leaving outINERTIA's air value or the height ofSIZEandTHINGSIZEkeeps
the current value instead of setting 0; a missingLairEnemyis no longer stack garbage (a random
lair enemy, and a possible multiplayer divergence). A creature rescued from the abyss keeps its own
fall acceleration.MOVE_CREATUREforALL_PLAYERSno longer spawns a stray effect at both ends of the move; removing
a sacrifice recipe from a full table no longer leaves the last one in two slots;DESCRIPTION_GEO
boxes on a campaign's land view are drawn where they are told, and a level without a description no
longer shows the previous one's text.- A box tooltip (
SET_BOX_TOOLTIPor Lua) containing%sno longer crashes the game when a possessed
creature picks the box up: map and config text is never used as a format any more. A long
SACRIFICED[...]name from Lua or the API no longer overflows the stack.
Saved games.
- A damaged save (truncated by a full disk, a block of the wrong size) is refused before anything is
loaded, instead of half-loading into the running game; the team's new save code now checks and
decodes a save before it touches the game. A refused load from the in-game menu restores the pause
state it found. - Esc closes the delete-save confirmation instead of leaving it over the game with "Yes" still armed.
- A save named in capitals (
FX1G0003.SAV, from a DOS or GOG install) can be deleted; the trash never
overwrites a file already in it, and an incomplete copy never costs the original.
Smaller fixes. In a music folder that mixes keeperNN files with other songs, every song plays once
(one repeated and another never played). A mod's misspelt icon shows the checkerboard placeholder again
rather than an empty button. A movie in a pixel format the converter cannot handle is skipped instead
of crashing. The game's API server no longer exits silently when a client disconnects (SIGPIPE).
TRASH_MAX_COUNT/TRASH_MAX_DAYS moved out of the spots upstream edits every sync, which removes one
of the files that conflicted in five of the last seven syncs. With the OpenGL renderer, a level load
finishing at the wrong moment no longer leaves the old or a mixed tileset. The API's get_level_info
no longer crashes the game for a level outside the campaign. Undefined behaviour is gone from reading
the FPS setting, from the creature job tables (jobs 33 and up aliased jobs 1 and up) and from a
navigation mesh with a missing link.
Quieter logs. These are ordinary play and no longer warnings: an imp finding a box it may pick up
(59 lines in one level 20 log), selling nothing, placing a trap with none left, an original-format level
without a .lgtfx file, a hurt imp with nowhere to flee resting where it stands, a destroyed heart's
last update, a slapped or spell-casting creature not yet free to fight, an imp's backwards move whose
route closes on the way, a player who has already lost, and the doors of a level started after another
(whose navigation was also rebuilt for nothing). A room with too many things to put back warns when one
is lost, instead of losing it silently; the out-of-range creature-state warning names the states around
it; the hatchery-capacity and door-combat messages print their values in order.
Packaging.
- AUR: what is published is exactly what the checksums were computed from (a script edited after
tagging used to ship with a stale hash, failingmakepkgfor everyone); a version pacman would never
offer — lower than, or equal to but different from, the live one — now fails loudly instead of
"publishing" to nobody. The launcher's self-update is no longer undone on every start. On a fresh
install, opening the launcher first (the "KeeperFX" menu entry) now sets up the game directory, so it
offers to copy in your Dungeon Keeper files instead of offering to download all of KeeperFX again.
Publishing now waits until the AUR actually lists the new version (it can lag its own git repository
by minutes, so a green run used to be followed byyay -Syufinding nothing), and records the version
bump on the branch the release came from rather than always onalpha. - AppImage: an unbundled library now fails the build (the check only warned); the current
directory is no longer on the library search path. linux.mkchecks the prebuilt dependencies against pinned SHA-256s and never caches a failed
download. The fonts are generated by one script instead of five copies. The monthly Flatpak finds
its stable release past 20 alphas.- The release guard checks the save format's field tables with the team's own tool and policy check,
instead of the size of the game state: a field missing from the tables would never be saved, and
nothing else would notice. The team's save tests run with ours. A failing step in the sync or
sanitizer builds no longer passes as green, and re-running an AUR publish whose only change is the
launcher pin no longer fails.