Golems — a DOS Lemmings game engine

Started by Mindless, May 06, 2024, 08:33:32 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Mindless

I can finally announce the project I've been working on for the past year and a half.  Golems!

Golems is a DOS Lemmings game engine.  It represents an original reverse engineering effort that is not based on Lemmix.

Golems v1.0 (for Windows)

Golems does not come with any data files.  To play levels, run the Golems executable, install the .NET 8 Runtime if it isn't already installed, click GameLoad Game..., and select a "MAIN.DAT" file from a DOS Lemmings game.  The remaining data files will be loaded from the same directory that contains "MAIN.DAT".  The mode of the game engine (release X offset, splat height, etc.) will be determined automatically from this file.  The game engine assumes that your files are from the floppy disk version of the game, not the CD versions with the splat height that breaks WAFD.

You can also pick "CSTM.DAT" to play a Customized Lemmings level pack.

Or pick a file named "GOLEMS.DAT" (renamed from "CSTM.DAT") to play in the "Golems" game mode.  This game mode is similar to Customized Lemmings (which is the same as the Holiday game mode except for the red-blue palette swap) except that the one-way mining bug is fixed and the splat height is reduced by 3 for lemmings that start falling when walking or bashing.  I think this mode represents a good compromise between not breaking WAFD and not breaking most custom level packs.

There are a few bugs that are not and will not be implemented:

  • Pause for time – because the behavior depends on picking High Performance PC mode or not.
  • Nuke saved % – because the bug is arguably in the saved percentage calculation, and Golems doesn't use percentages. (Okay, it's mostly due to how much I dislike this bug and the percentages.)
  • Right-click-and-hold – because a faithful implementation is fundamentally incompatible with rewinding.
  • [Visual Only] Walking shrugger – because the visual part of this bug can cause out-of-bounds memory access.  The gameplay part is implemented.

I did also make some visual-only improvements to the control panel (status text, pause button, nuke button, and minimap) and to the level view (highlight selected lemming).  I limited these changes to what could have been done in the original game engines.

Aside from the above bugs, visual-only changes, and any bugs that result from out-of-bounds memory access, I hope to have implemented the game engine so that it behaves exactly as the original game engines.  Please let me know if you find any unexpected differences!  (Please note the game mode shown in HelpAbout.)

Also let me know if you encounter any other bugs.

Some keyboard controls:

  • Space – Pause
  • ~ – X-ray View
  • W – Fast/Step Backward
  • Left Shift + W – Faster Backward
  • E – Fast/Step Forward
  • Left Shift + E – Faster Forward

And one more thing:

Golems in your browser

namida

#1
One gotcha that Lemmix didn't reproduce (and this is only relevant to custom data files) - if a terrain piece has a pixel that uses one of the fixed 8 colors, that pixel will be visible but nonsolid.

EDIT: The browser player for the levels DB is really nice! One thing that would be nice - it's possible to set a page so that a tap and hold on mobile doesn't get treated as a right click (ish). I remember encountering that with some dumb Flash-style games I made at one point. I forget what the fix was though...
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

WillLem

#2
Great effort! The smoothness of the rewind feature is particularly impressive, might I ask how you achieve this in Golems?

Also, is this open-source? Not looking to do anything with it (I already have more than enough to be getting on with Lemmings-wise!), but would be interested to see how you've approached the reverse-engineering. As this is a relatively back-to-basics engine compared to the likes of Lemmix & co, Lix and even Lemmini & co, it would be great to see what's happening under the hood.

Mindless

#3
Quote from: namida on May 08, 2024, 11:04:06 AM
if a terrain piece has a pixel that uses one of the fixed 8 colors, that pixel will be visible but nonsolid.
I haven't tested it, but Golems should have this implemented at least half correctly.  It uses the most-significant bitplane for terrain existence, so gameplay should be correct.  It probably does not currently display the phantom terrain, however.

Quote from: namida on May 08, 2024, 11:04:06 AM
One thing that would be nice - it's possible to set a page so that a tap and hold on mobile doesn't get treated as a right click (ish).
This should be the case already?  The game screen area has touch-action: none;, which should suppress that.  Maybe I need to apply it to the black area outside the game screen as well in case the touch is just outside the game screen?

Quote from: WillLem on May 09, 2024, 01:50:22 AM
The smoothness of the rewind feature is particularly impressive, might I ask how you achieve this in Golems?
The game takes a snapshot of the game state every 17 frames (1 second of game-time).  Stepping back one frame is accomplished by going back to the previous game state snapshot and stepping forward however many frames necessary to end up one frame back.

All those snapshots would eat up memory pretty quickly, so I added a super fast/simple algorithm for compressing the terrain bitmap.  Any snapshot older than the latest snapshot gets compressed.  The game holds on to a copy of the original terrain bitmap to reference.  The compression algorithm produces a data stream that can be interpreted according to the following algorithm:

Set `refCursor` to the first pixel in the reference bitmap.
Set `dstCursor` to the first pixel in the destination bitmap.
While `dstCursor` is not at the end of the destination bitmap:
    Read a 16-bit unsigned integer from the stream and store it in `sameCount`.
    Read `sameCount` pixels from `refCursor` and write them to `dstCursor`; `refCursor` and `dstCursor` advance by `sameCount` pixels.
    Read a 16-bit unsigned integer from the stream and store it in `diffCount`.
    Read `diffCount` pixels from the stream and write them to `dstCursor`; `dstCursor` advanced by `diffCount` pixels.
    Advance `refCursor` by `diffCount` pixels.

The same compression scheme is applied to the effect map as well.  This way each snapshot basically only needs to store the accumulated changes to the terrain and effect maps along with the (uncompressed) creature states, gadget states, and various other small bits of game state.

It could probably be smarter and occasionally choose to create a new reference bitmap if the terrain changes a lot.

Quote from: WillLem on May 09, 2024, 01:50:22 AM
Also, is this open-source?
Not yet, but probably eventually.  But it is .NET and not obfuscated, so you could use ILSpy to easily peek at the code.

mobius

#4
some things here are very intuitive and I like; for example; having "plus X" for save requirement, easily lets even new users know what's going on better than a recolor.

Also want to add that I really like having the FF buttons be hold-down instead of toggle, how I thought they always should've worked
everything by me: https://www.lemmingsforums.net/index.php?topic=5982.msg96035#msg96035

"Not knowing how near the truth is, we seek it far away."
-Hakuin Ekaku

"I have seen a heap of trouble in my life, and most of it has never come to pass" - Mark Twain


Simon

#5
Quote from: mobius on May 12, 2024, 12:15:47 AM
FF buttons be hold-down instead of toggle, how I thought they always should've worked

Interesting observation. In Lix, I hold the 10-second framestepping, too, and it becomes fast-forward for me. Hard to say if it's worthwhile to turn toggle-on fast-forward into hold-to-fast-forward. I imagine that toggle-on is better for recording solution videos, but that's niche. Even in livestreaming, I prefer hold-to-framestep instead of the toggled fast-forward.

I haven't played Golems enough yet to decide if I want a super-fast hold-to-fast-forward in Golems.

Quote from: Mindless on May 09, 2024, 03:47:41 AM
snapshot basically only needs to store the accumulated changes to the terrain and effect maps along with the (uncompressed) creature states, gadget states, and various other small bits of game state.

There is wisdom here: Separation of all level state into an immutable and a mutable section. It's enough to store the immutable section once, at the beginning. We store the mutable section in regular intervals, possibly compressed.

We can push this further in all of our engines.

E.g, hatches and water don't need to be part of the mutable state at all. Their animation is only dependent on the number of physics updates since the beginning. We can keep them in the immutable section, and only prepare them just-in-time during the drawing step. It's common to have 10 to 50 water tiles in a level; Lix can omit many allocating deep copies here.

Traps must remain in the mutable section because they eat lemmings at unpredictable times, and both physics and graphics depend on such recent eating. Theoretically, traps can split into an immutable location and the mutable number of the physics update of recent eating, at cost of even more complication.

Thanks!

-- Simon