[SUG] Level top and sides

Started by WillLem, May 12, 2023, 03:10:43 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

WillLem

#30
Added a poll; I could do with some help making a decision if you have a moment.

At the moment, deleting the top row of pixels from the level seems like the best way to achieve the top-of-level behaviour that I'd most prefer for SLX; i.e. the top is open, and accessible, but lems must remain visible if/when they access it. This is infinitely preferable to the pseudo-forcefield behaviour in current SLX, which is messy, buggy and unreliable. A blank top row limits all top-of-level behaviour handling to a handful of edge cases (namely Ballooners, Swimmers, Builders, Freezers and Stackers), eliminating the need to also handle many other lemming actions and manually inhibit top-of-level skill assignments.

Namida has already suggested adding a blank row rather than subtracting an existing row; a good idea in principle, and one I considered at length before deciding on subtract. Reasons here.

If people don't like the idea of a blank pixel at the top of the level, it is actually possible to make the top pixel non-solid, but still render it. This preserves the appearance of the level whilst still making any lems at Y = 0 visible.

So, the question becomes: should we continue to render the top row of pixels, and only have it blank in-physics (CPM reveals this), or absolutely delete it so that it matches physics?


Verson A - pixel is removed in-physics and deleted in rendering


Verson B - pixel is removed in-physics, but rendered

Version A is visually consistent with what will happen when a lem reaches the top of the level, but modifies the level in order to achieve this. Version B is potentially misleading and involves essentially fake pixels, but preserves the level's aesthetic.

I've voted for "delete" because ultimately I don't want to mislead players, and the game should be visually clear. However, I can see a case for "render" in that it's top-of-level behaviour: the effect could be seen as the lem reaching a forcefield, and we generally don't want lems getting to this point anyway.

Please vote, discuss, etc. I have no strong preference either way at the moment, the poll will help me to decide what to do. Or, if you don't like either option and you have any other suggestions for top-of-level behaviour, please put them forward. Thanks! :lemcat:

WillLem

#31
This one's definitely a tail-chaser.

A blank row of pixels along the top is a good fix for flat terrain, but it makes much less sense when the terrain could reasonably be expected to continue outside of the level area. Some visuals, for reference:


All could possibly be flat, but the middle one is less likely to be than the left one, and the right one is the least likely to be

Ironically enough, since terrain pieces can be grouped and re-shaped, their possible existence (or not) outside of the level is actually irrelevant, but only when we can't see it or access it. Once we can access it, we need to address this particular concern, and the blank row of pixels seems like the best way to achieve this: make it certain that the terrain across the top of the level is flat, regardless of how it may appear. However, this feels too similar to the consistent-but-ridiculous "delete steel when it's behind terrain, regardless of how irregular the resulting shape may be", and so is probably something to avoid.

I also wonder... do we want lems walking across the top of the level? On the one hand, it makes physical sense (to some extent) and becomes another possible way to traverse a level. On the other, it becomes the eternal backroute that can only be fixed by adding deadly objects to the top row of pixels. Not that backroute prevention should ever be a decisive factor when it comes to physics, but this one is basically impossible to ignore.

So, perhaps we're back to wanting a force-field again; we want pieces to be perceivable as possibly continuing outside the level regardless of what is actually the case (if it's not visible, we shouldn't have to worry about it anyway), but if we don't want lemmings to access this area, we need to use a force-field. The sides are a force-field, so maybe the top should continue to be one.

Sleep and reflection needed. Please vote in the poll (currently, I'd still like to sit with "let lems access the top, but show it happening" for a few more days), and please reply.

WillLem

#32
OK, after giving this a few days' background thought, I think it would probably be best to just keep the top of the level as a forcefield, same as the sides.

However, I'd like to implement it as concisely as possible. At present, the side forcefields are only 3 lines of code each, and deleting the top row of pixels is a small block of code comprising only 4 lines. The top forcefield should be similarly non-verbose.

I've found a much simpler way of disallowing skill assignments at Y = 0: simply add the lemming to the existing list of non-assignable lems (i.e. Zombies, Neutrals, etc) whilst at this position. Much better than handling it per-skill.

Meanwhile, the forcefield turns Walkers, and nudges all other movement actions (Swimmers, Gliders-in-Updrafts, Ballooners, even Jumpers!) downwards into the visible frame. Climbers are cancelled around 7px from the top to prevent them from entering the Hoisting state.

Freezers at the top-edge and bottom-edge have also been considerably bugfixed and refactored. I've removed some fiddly Freezer code which previously prevented them from falling out of the bottom of the ice cube when assigned off the bottom edge of the level, and fixed a bug which caused them to ascend out of the cube when assigned too close to the top for enough of the ice cube to exist; in these cases, the assignment is now simply disallowed. This might seem a bit heavy-handed, but it eliminates a lot of fiddly code and fixes the associated bugs.

So far so good; it's still not quite as concise as just deleting the top pixel, but it feels much more consistent with what's happening at the level edges.

All of the above implemented/re-implemented in commit c0433cf6b




Question: What should we do about lemmings with countdown timers?

It is still possible to reach the top of the level if this particular setup is achieved:



Note that the purple bar along the top has been destroyed enough from the bottom in order to create a 6px ascendable step from the Builder bridge, but remains intact along the top so that when lems accessing the top are turned around, they simply walk along the still-complete bar.

Since skills are not assignable to any lemmings at this position, getting them here isn't necessarily particularly useful. However, since we now have Timebombers, Slowfreeze and Radiation, it is theoretically possible to send a lemming with a timer above the level, to have them later detonate.

Slowfreeze is, again, not much use here: any terrain above the level is always automatically destroyed, so detonating a Slowfreeze lem here is a waste.

Timebombers and Radiators, on the other hand, are much more potentially problematic: they can destroy the terrain from above. Whether or not this is a problem depends entirely on the layout of the level, of course, but it does raise the question of whether or not we want to allow this.

I would tend towards not allowing it, for the simple reason that the lems are not visible from the top of the level. Whilst I'm all for encouraging the skill of judging a countdown timer to get a "just right" Bomber assignment, once the player can't see the timer digit or the lemming, it verges on the unfair.

The question then becomes what to do about it. We can do one of 3 things:

1) Set the timer to 0 immediately, so that the lem detonates instantly on contact with the top of the level.

2) Suspend the timer until the lem re-enters the visible area.

3) Cancel the timer altogether, so the lem is no longer a Timebomber/Radiator.

The first of these options is more or less what would happen if we remove the currently-implemented "LemHasTurned" flag, which tracks that a lem has already been turned by the forcefield, so shouldn't turn again. This would result in a lem infinitely turning on the same pixel point, and eventually detonating.

The second of these options is elegant, and keeps everything visible, but could potentially result in some very questionable level design. Levels that make use of this could be interesting, but would ultimately feel too much like "glitch levels", i.e. relying on some obscure mechanic for the level's solution - not really what we're aiming for here.

The third of these options would likely be very confusing to the player and feel like an unintended bug, and so is very unlikely to be chosen above one of the other two options. I've mentioned it simply for completeness.

The first option seems, then, to be the best. Let's sit with it for a day or two and see if it feels right.

Suggestions and feedback very welcome.

WillLem

#33
Decision time for top-of-level behaviour!

Finally got around to making some decisions about Top-of-Level behaviour, and implementing it as best as I possibly can.

In short, the Top Pixel (i.e. Y = 0) is now a virtual downwards-forcefield, much like the sides are one-way forcefields.

Here's how it works:

If a lem somehow reaches (Y <= 0), they cannot be assigned skills. So, the forcefield nudges them downwards into the level by 1px, regardless of whether or not there is terrain there; this is so that the lems remain visible, active in the level, and assignable-to. Pending extensive testing by people more determined to break the game than me, this situation is currently almost impossible* and should never happen anyway.

Meanwhile, any pre-existing terrain pixels along the top row are virtually extended upwards, so that Walking lems encountering these pixels will act as if they've met a wall, and turn around (this can be regarded as a side effect of the force-field, essentially preventing access to the topmost pixel).

Brick-based construction skills (Builders, Platformers, Stackers, Ladderers) are prevented from adding a pixel along the top row: Platformers and Ladderers can't get up there to do this anyway, whilst Builders and Stackers are cancelled just before they place a brick along the top row.

Freezers have additional top-of-level checks to prevent Frozen lems accessing the top by stepping out of the partially-erased ice cube (a current bug in 2.7.3, now fixed). Additionally, Freezers' bottom-of-level checks have been re-implemented, simplified and improved. So, Freezers are once again assignable anywhere in the level.

Jumpers, Swimmers and Gliders-in-Updrafts are nudged down to keep them within the visible level area (and, to keep them assignable-to).

Climbers and Hoisters are cancelled (and turned) mid-action before they reach the top.

Ballooners bob around at the top (they are turned by the forcefield sides, and nudged down by the forcefield top) until the balloon is popped, as per current behaviour.

Fencers are also cancelled once they reach the top of the level.

All other skill actions are unaffected by the forcefield, since they are either sidewards, downwards, or in-place oriented and therefore don't (necessarily) interact directly with the top pixel.

I'm about 99% sure that this is as glitchproof as it can possibly be, and all skills/actions behave as you might expect given that the top pixel is now a non-solid forcefield. If anything does come up during testing/gameplay that seems broken, I'll do my best to fix it.

CheckLevelBoundaries and various other parts of LemGame have been refactored for readability and to implement the above - see Commit 70b045b25 for changes.




*There are currently 2 known ways to get a lem to Y = 0:

1) Assign a Freezer such that any nearby lems will ascend out of the Freezer cube, and thus arrive at Y = 0. Possible fixes: i) move these lems away horizontally instead (could cause terrain phasing bugs elsewhere), ii) apply Blocker field to lems in the "Freezing" state (more preferable since Freezers are often used for this purpose anyway, but doesn't work for falling lems), iii) disallow Freezer assignments at (LemY <=11) (easy but too heavy-handed, and won't work for Slowfreeze lems), iv) erase top pixel of Freezer cube if LemY (<=11) (easy, consistent with other construction skills, looks wierd), v) set an ice cube map wherever there is a Freezer, handle accordingly (most versatile solution, but more work and more rendering load).

2) Pre-place a lem outside the level area. This has been addressed in Commit d8df70e5b - if a lem is pre-placed outside the level area on any edge, they are automatically saved!
;P