Don't exit on losing all lemmings (feature development)

Started by Simon, January 10, 2024, 11:42:06 AM

Previous topic - Next topic

0 Members and 2 Guests are viewing this topic.

namida

My reasoning on the option to disable it even when the save requirement is met is that it gives the same convenience to people who may be aiming for talismans or max-saved solutions etc.
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

OK, I went ahead and coded this as a full-fledged feature with all 3 end-of-play options, plus the option to pause the game (or not).

It's done as a radio group in the Interface Options tab (I've also moved the Graphics options to their own tab to keep the Config menu tidy):



N.B. We can of course change the exact wording of this in the menu if necessary

"Pause Game" is unavailable for the first option, since it's irrelevant in that case. For the other two options, with Pause Game checked, the game enters a paused state which must then be backskipped or restarted in order to resume normal gameplay.

At the moment, if the player presses pause to unpause the game after all lemmings are removed, the game simply pauses again (because the criteria for triggering the paused state still applies, i.e. there are no lemmings, and the user has opted to pause the game in this case). The same is true if we suspend gameplay rather than pausing (i.e. if gameplay is resumed, it immediately gets suspended again because the check for no lemmings is still returning true with each physics update). At the moment I can't think of a sane way to fix this, but I'll come back to it when I've had some sleep*.

With "Pause Game" unchecked, the game doesn't pause and instead continues to play as normal.

See attached files for all updated code.

*Note that in order to pause/suspend the game, LemGame needs to pass a flag to GameWindow - I'm hoping that there may be a better way to achieve the desired behaviour from within LemGame itself. This would likely fix the aforementioned bug whilst also keeping the feature as simple as possible.

namida

One possibility would be to have a variable in LemGame that is set to true once the message is sent to GameWindow, and prevents it being sent again. It should be either included in save states, or specifically set back to false if the player rewinds, or some other mechanism to ensure it will trigger again if the player rewinds then again runs out of lemmings.
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

Quote from: namida on February 12, 2024, 08:44:25 PM
specifically set back to false if the player rewinds, or some other mechanism to ensure it will trigger again if the player rewinds then again runs out of lemmings.

We already have this; the method in GameWindow sets the flag back to false after pausing the game, so that the player can rewind, restart, etc and the flag can be set back to true if relevant.

The issue is more to do with what happens after the game has paused. If the player tries to unpause (or, resume gameplay if we go with suspending physics), the flag is immediately set to true again since there are still no lemmings, and so the game instantly pauses (or suspends) again.

Quote from: namida on February 12, 2024, 08:44:25 PM
and prevents it being sent again.

Could you elaborate here? Is there a way to prevent the message being sent again once it's already been sent, whilst retaining the ability to be sent again if we rewind to before it was first sent? Perhaps this is where savestates come in, as you suggested...


Simon

Menu looks good to me. Looks like you can support namida's/Icho's ideas without overly much extra work or extra menu space. And you've already thought of disabling the Pause checkbox when we want to always exit.

You haven't attached binaries yet, therefore playtesting has to wait until you do. (I still haven't gotten a Delphi build toolchain to work on Linux.) I'll be happy to playtest during the weekend. Or you focus on development first, and I'll find more time later in the month.

Quote from: WillLemsuspended

Is suspend the same as pause? You didn't like the absolute prevention of further physics updates (= what I called a freeze, but I don't know a best name either). You wanted the regular pause, which the player can unpause.

Quote from: WillLemimmediately gets suspended again because the check for no lemmings is still returning true
sane way to fix this

If the check works well already, you can alternatively be happy with a freeze that you can't unpause. :P Assuming you really want to add more logic here:

You want to pause when you are running out of lemmings, not when you have run out of lemmings. Therefore, it's conceivable to move the origin of this information into/near physics, because that's when we run out of lemmings.

When, at the start of a physics update, you still have lemmings, and you've run out of lemmings after this physics update, then a result of this physics update is that the UI should react (to losing all lemmings). The physics update doesn't care of how exactly to react; the UI will look at the user options.

This avoids state (no need to remember whether we've already paused for loss) but puts UI-relevant logic into physics (which we usually want to avoid). The physics already tell the UI about many things, e.g., to play sound, and this would then be one more event that the UI receives from the physics.

I haven't checked the NL source if this is easy, or even if it's sensible at all for NL.

Even in Lix, where I have a dedicated class for this concept (EffectSink) of ferrying eye/ear candy (sound commands, commands to generate explosion debris, commands to make a flying pickaxe, assignment arrows, ...) from physics into the physics-ignorant part, I'd have to write extra code to pry the message from the EffectSink into the speed logic (which so far is completely independent of the effect sink).

namida shows sensible feeling already in the wording ("message") instead of treating this purely as a state check. It may be wrong in NL to put it in the physics, but even elsewhere, it's still good to think of this particular pausing as a message/event, not as a continuous situation.

-- Simon

Simon

Another downside to pause (vs. freeze): When you hold the 10-second skip, you will skip 10 seconds regardless of the pause. You'll overshoot far into dead state.

I skip 10 seconds more often in NL than in Lix when NL's regular fast-forward isn't fast enough. It's practically a stronger but fickler fast-forward.

I don't have playtesting results from NL + pause-at-end yet. I expect to reach the end with repeated 10-second skips moderately often. Better verdict will have to wait for playtesting.

Your argument against the freeze was that you suppose player confusion. Your argument was never about the usefulness of the dead state. Thus: Have you considered the freeze plus better feedback in the UI (instead of the pause)? If you improve the UI feedback, please avoid dialog box, it eats hotkeys.

-- Simon

WillLem

#21
Quote from: Simon on February 13, 2024, 01:23:43 AM
You haven't attached binaries yet

Apologies, I'm unsure as to what binaries are in this context? ??? :forehead:

The .pas files attached above can simply replace the existing ones in the source folder, as long as nothing else changes in these files between now and such time as a playtest build is released. I'm also happy to prepare an experimental build this evening, with namida's permission.

Quote from: Simon on February 13, 2024, 01:23:43 AM
Is suspend the same as pause?

No, it's a little bit more complicated. It does pause the game, but also prevents player input and releases the mouse. It's used for dialogs such as Save/Load/Edit Replay, and any error messages. It seems to be the closest thing we already have to a "physics freeze", and wants input from the dialog before gameplay can be resumed.

Quote from: Simon on February 13, 2024, 01:23:43 AM
If the check works well already, you can alternatively be happy with a freeze that you can't unpause

I see what you mean and I did consider this, but ultimately I think that limiting player control is undesirable. The dead state could be useful for some reason, so we want to give the player the choice to unpause and resume gameplay if they wish.

Quote from: Simon on February 13, 2024, 01:23:43 AM
You want to pause when you are running out of lemmings, not when you have run out of lemmings.

Currently, the game pauses at the frame the very last lemming is removed. Would you prefer something other than this?

Quote from: Simon on February 13, 2024, 01:23:43 AM
namida shows sensible feeling already in the wording ("message") instead of treating this purely as a state check

I need a bit more info/context on what's meant here. I'm familiar with messages, but would need namida to elaborate a bit more before I go ahead and try anything.

Quote from: Simon on February 13, 2024, 02:45:33 AM
Another downside to pause (vs. freeze): When you hold the 10-second skip, you will skip 10 seconds regardless of the pause. You'll overshoot far into dead state.

EDIT: OK, I see the problem here. A time skip of, say, 1000 frames overshoots by a significant amount and then the player has to backtrack to an unknown point. Should be possible to find a way around this.

Quote from: Simon on February 13, 2024, 02:45:33 AM
Your argument against the freeze was that you suppose player confusion. Your argument was never about the usefulness of the dead state. Thus: Have you considered the freeze plus better feedback in the UI (instead of the pause)?

Both arguments are equally valid in my view; the common concern is with giving the player as much control as possible over the paused/frozen state. If we pause the game, they can simply unpause if they wish gameplay to resume into the dead state for any reason, or they can backskip or restart. Either way, we're giving the player the choice as to what is the most useful course of action at that point (many actions are possible whilst the game is in a paused state), and not introducing any new input mechanics for the player to learn (i.e. they already know what to do when the game is paused).

If we freeze the game instead, then I agree visual feedback is absolutely necessary. A dialog would be the easiest solution (and would be consistent with all other gameplay suspensions in NL), gameplay can resume pending player input from the dialog, and we can put as much information as necessary into it. However, I agree it's generally undesirable (particularly if the player has to see it multiple times in a single gameplay session), so the least intrusive alternative would be to work with the limited skill panel display (we'd have about 40 characters) to alert the player to the "no lemmings" state and give instructions for how to proceed from there (bear in mind that this all has to be decided upon, coded in, and learned by the player).

Important question at this point: maybe there is some benefit to freezing physics (as opposed to pausing the game) that I haven't yet become aware of...?

Proxima

Quote from: WillLem on February 13, 2024, 08:33:38 PMImportant question at this point: maybe there is some benefit to freezing physics that I haven't yet become aware of...?

The benefit is that if time can't go forward past when the last lemming dies, it's always easy to go back to a known length of time before the last lemming dies -- and even if, for example, that death is from a fall into the void from a high place, you just need to tap "1 second back" a few times to get back to when that lemming was on safe ground.

WillLem

#23
OK, I understand the issue now with regards to overshooting the point at which no lemmings remain. The bigger the forward skip, the longer it takes to backstep into playable level.

It's a simple enough fix - we set the HyperSpeed target to the current frame if PauseWhenNoLemmings returns true. This allows us to keep pause behaviour (easier for the player to understand and from which to resume gameplay) whilst preventing overshooting.

Here's the modified copy of GameWindow which implements this fix.

WillLem

#24
With namida's approval, I've prepared an experimental release of NeoLemmix 12.13 which includes the proposed feature for testing.

So far, we have the full feature with all 3 available options.

If either "Never Exit" or "Only Exit if Save Requirement Met" are chosen, the option to "Pause Game" becomes available. This causes the game to enter a paused state on the exact frame that the last lemming is removed, enabling the player to proceed with either backstepping, restarting or unpausing from there as they wish, and preventing gameplay from overshooting too far into the unplayable state.

By all means take a look at the experimental and post any feedback here in this topic.




Main points for testing:

  • Are the options sufficient and clear enough? (see Config menu / Interface tab)
  • Does the feature behave as expectated depending on which option is chosen?
  • Anything not behaving as expected: Does it pause when it shouldn't, or does it ever not pause when it should? Does it interact properly with skips past the frame on which the last lemming is removed (in both directions)?
  • Is the paused state sufficient for halting gameplay, or should something else happen? Do we need any further visual cues?
  • Would you like to be able to unpause the game and continue into the "no lemmings" state? Currently, the game immediately pauses again - I'm working on a fix for this, but in the meantime would it be acceptable for this state to not be un-pausable? This has been fixed in V2 - it's now possible to unpause

Proxima

Quote from: WillLem on February 13, 2024, 10:26:56 PMZombies count as "removed" lems, so gameplay will end if only zombies remain (and the relevant option is chosen) - should this be the same for neutrals?

If only neutrals remain, the level may or may not be solved, depending on whether a path has been set up to allow them to exit. The only way to know whether to award victory is either to allow the player to play it out, or to automate the process.

Simon

Quote from: WillLem on February 13, 2024, 08:33:38 PM
what binaries are in this context?

Here: An NL executable. I can't build it myself from source here.

Generally: Executables, DLLs, static libraries. Program code in CPU-consumable form instead of human-readable source code.

Ah, you've released an entire experimental build. Thanks! I'll test it this weekend at latest, and you'll have feedback by Monday.

Quote from: WillLem on February 13, 2024, 08:33:38 PM
Quote from: Simon on February 13, 2024, 01:23:43 AM
Is suspend the same as pause?
prevents player input and releases the mouse.
wants input from the dialog before gameplay can be resumed.

Okay, you decide if this is appropriate. It sounds as if NL stops reading hotkeys during suspension, which would be a problem. We want hotkeys active throughout this; we usually want to rewind.

You say it wants input from the dialog, but we don't plan to add a dialog.

Quote from: WillLem on February 13, 2024, 08:33:38 PM
Quote from: Simon on February 13, 2024, 01:23:43 AM
pause when you are running out of lemmings, not when you have run
pauses at the frame the very last lemming is removed.
prefer something other than this?

Two answers.

1. It's indeed good to end when the last lemming is removed. It may be even nicer to end when ((the last lemming is removed) and (no triggered trap is eating anymore)).

2. It's possible you didn't get the nuance. To be safe, I'll rephrase: Your problem is that your code re-pauses too often. My reply was that this pausing should originate from actively running out of lemmings (which can only happen if we still had lemmings before advancing physics during this very iteration of the program main loop) instead of from having run out of lemmings (which is still true in future main loop iterations, regardless of whether we advanced physics again or not).

namida and I recommend nearly the same solution. The difference is: namida wants to track state (has he messaged you before). I want to generate the pause as a result of advancing physics, which won't happen again until you rewind. My solution needs no extra state, but ties the check to parts of the code (physics, or at least the game code that triggers physics advances) that don't care about UI. I don't know enough NL internals to tell you what's ultimately better.

Quote from: WillLem on February 13, 2024, 08:33:38 PM
skip of, say, 1000 frames overshoots by a significant amount

Yes, this is problem #1 that you should address.

Problem #2 arises from holding a skip hotkey down. The held key generates keyboard input for every iteration of the program loop. In 2023 NL, such skip input will advance physics even during pause, which is fundamentally good. But here, the skip will ignore your pausing. I hold the key down, I reach the end, your code pauses, my held key generates more inputs and completely bypasses your pause. Now I've overshot by many multiples of the single skip length.

Quote from: WillLem on February 13, 2024, 08:33:38 PM
freeze the game instead, then I agree visual feedback is absolutely necessary.
work with the limited skill panel display

Yes, 100 % agree that this is the biggest task in implementing a freeze. To make it lovely, you'll have to design visual feedback unlike anything in NL. I have no easy answer. More on this later.

Quote
benefit to freezing physics (as opposed to pausing the game) that I haven't yet become aware of...?

Also more on this later.

-- Simon

namida

Even more important than what WillLem mentioned as focuses, is looking for any bugs. In particular - does it pause when it shouldn't, or does it ever not pause when it should, does it interact properly with skips past the frame on which the last lemming is removed (in both directions), etc.

Neutrals should not be treated the same way as zombies. Although no skills can be assigned to them, they could still be saved if they have a path to the exit, or possibly via RR manipulation or nuking.
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

#28
Quote from: Simon on February 13, 2024, 11:20:21 PM
It sounds as if NL stops reading hotkeys during suspension, which would be a problem. We want hotkeys active throughout this; we usually want to rewind.

You say it wants input from the dialog, but we don't plan to add a dialog.

OK, we won't use SuspendGameplay and we agree that there shouldn't be any need for a dialog, or anything that would interfere with game hotkeys.

I had a look at what happens in Lix, and I've realised that what you're looking for with a "physics freeze" is for the game to stop altogether, and not allow any user input other than to backtrack into the level. SuspendGameplay and Pause, therefore, are not compatible with this.

I'll look closer at NL's codebase and see if there is a way to similarly halt the game.

Quote from: Simon on February 13, 2024, 11:20:21 PM
It may be even nicer to end when ((the last lemming is removed) and (no triggered trap is eating anymore))

Waiting for the animation to finish is definitely possible, might have to see if there are ways to check "is a trap animation currently playing?"

Quote from: Simon on February 13, 2024, 11:20:21 PM
namida and I recommend nearly the same solution. The difference is: namida wants to track state ... I want to generate the pause as a result of advancing physics, which won't happen again until you rewind. My solution needs no extra state, but ties the check to parts of the code (physics, or at least the game code that triggers physics advances) that don't care about UI

This is perhaps where a "message" rather than a true/false flag may be required to make the feature more sophisticated, and perhaps more malleable. Education needed, I'm more than prepared to do the necessary work.

EDIT: Note to self: Store current iteration in savestate when last lemming is lost, and then look to that iteration for whether or not to pause (namida's solution). See whether physics update can check if there were any lemmings available on the previous physics update, and only pause if there were and now there aren't (Simon's solution).

Quote from: Simon on February 13, 2024, 11:20:21 PM
Yes, this is problem #1 that you should address.

Problem #2 arises from holding a skip hotkey down ... my held key generates more inputs and completely bypasses your pause. Now I've overshot by many multiples of the single skip length.

The current fix addresses problem #1 well enough, but I can confirm that problem #2 still remains. The paused state halts the game long enough to alert the player that something has happened (about 2 seconds), but does indeed go ahead with generating more skips after that.

It looks like decisions are going to need to be made before we go ahead with anything, as it's likely that the feature will need some degree of re-writing in order to incorporate the various suggested nuances. Which nuances will determine how the re-write looks.

Quote from: Simon on February 13, 2024, 11:20:21 PM
Yes, 100 % agree that this is the biggest task in implementing a freeze. To make it lovely, you'll have to design visual feedback unlike anything in NL

Again, from looking at Lix I think the panel text is sufficient. We only need to display:

NO LEMMINGS LEFT! SKIP BACK OR RESTART

That's 38 characters, the panel allows 45.

Again again though, this is only if we decide to go with "halt, and allow no more input." My own feeling is that the player should have the option to unpause the game and continue into the "no lems" state if they wish. Anything else feels a bit "un-NeoLemmix," perhaps? I need more community feedback on this point, and namida's opinion would be particularly valuable as he will make the ultimate call on it.

Quote from: namida on February 13, 2024, 11:41:52 PM
looking for any bugs

Definitely important, but I'd say that was implied by "does it behave as expected?" ;P

Quote from: Proxima on February 13, 2024, 11:17:10 PM
If only neutrals remain ... The only way to know whether to award victory is either to allow the player to play it out

Quote from: namida on February 13, 2024, 11:41:52 PM
Neutrals should not be treated the same way as zombies ... they could still be saved if they have a path to the exit, or possibly via RR manipulation or nuking.

Yes, of course.

I've updated the testing points in response to these comments.

namida

You could achieve what Simon is talking about by simply having LemGame refuse to run the update routine if all of these conditions are met:
1. No non-removed lemmings (except zombies)
2. No traps currently animating (optional, but looks nicer if the animation can finish first; exiting currently takes this into account)
3. No lemmings (except zombies) still waiting to spawn

When I say "refuse to run", I don't mean as in gives an error; just as in calling LemGame.UpdateLemmings in this situation should do nothing. It's okay if GameWindow still attempts to re-render the new frame; this would just result in it redrawing the last one again, which is unnecessary but otherwise not a problem at all.

Specific places I would look for bugs with this implementation:
1. If the user does a large frameskip past the frame where the last lemming is lost, does the correct behavior result? Check this for conditional skips (like "skip to next shrugger") as well as for time-based ones.
2. Can the user still skip backwards and resume play properly afterwards?
3. Does mass replay check behave properly on replays where this occurs?
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)