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.