[FIXED] [GLITCH] [SUGGESTION] [PLAYER] Moving through walls with many builders

Started by Nepster, February 22, 2016, 05:01:35 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Nepster

I found two more glitches concerning builders, that might require slight changes in the game physics. Here is the first:

Suggestion:
Disallow assigning builders to lemmings, if their first brick would not add any terrain.

Reason:
1) Repeated assignment of builders allows to move lemmings diagonally through solid walls. Try the attached level or watch the replay.
2) This rule already applies to platformers, so this change would make the game mechanics more consistent.

Moreover I don't think that there are many existing levels/replays where lemmings completely encased in terrain are turned into builders, so this change should not have a huge impact on existing levels/replays.

namida

QuoteDisallow assigning builders to lemmings, if their first brick would not add any terrain.

Sounds reasonable and viable (note - my first thought was "wait, that will break every level that involves building against a slope to turn around!" - but in fact, this action does still add one pixel of terrain, so it wouldn't be affected).
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)

namida

Tried implementing this. One situation that your suggestion does not solve:
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)

Nepster

I see... What are the precise terrain checks for platformers? Perhaps one can use something similar for builders? I noticed that in your example level, one cannot assign platformers to the trapped lemming.

namida

The platformer is also prevented from platforming if his first brick would cause him to turn around, even if it would place terrain. For obvious reasons, doing this with the builder would break a lot of levels. Removing such a thing with the platformer is perhaps more viable; it would improve consistency with far less breakage, but still wouldn't really fix the issue at hand.

It's mostly LemCanPlatform and AssignPlatformer that are relevant here, but I've pasted HandlePlatforming too just in case it helps.

TLemmingGame.HandlePlatforming
function TLemmingGame.HandlePlatforming(L: TLemming): Boolean;
begin
  Result := False;

  with L do
  begin
    // sound
    if (LemFrame = 10) and (LemNumberOfBricksLeft <= 3) then
      CueSoundEffect(SFX_BUILDER_WARNING);

    // lay brick
    if (LemFrame = 9) then
    begin
      LemCouldPlatform := LemCanPlatform(L);
      if not CheckGimmick(GIM_UNALTERABLE) then
      LayBrick(L, 1);
      if CheckGimmick(GIM_HARDWORK) then
        LemNumberOfBricksLeft := 11;
      if (CheckGimmick(GIM_LAZY)) and (LemNumberOfBricksLeft > 4) then
        LemNumberOfBricksLeft := 4;
      Exit;
    end
    else if (LemFrame = 15) and not (LemCouldPlatform) then
    begin
      Transition(L, baWalking, TRUE);
      if (CheckGimmick(GIM_UNALTERABLE)) then Transition(L, baShrugging);
      if CheckGimmick(GIM_BACKWARDS) and HasPixelAt(LemX, LemY-1) and not HasPixelAt(LemX + LemDx, LemY - 1) then
          Inc(LemX, LemDx);
      CheckForLevelTopBoundary(L);
      Result := True;
      Exit;
    end
    else if (LemFrame = 0) or (LemFrame = 15) then
    begin

      Inc(LemX, LemDx);
      if (((HasPixelAt_ClipY(LemX+LemDx, LemY - 1, -1) = TRUE)
      or (HasPixelAt_ClipY(LemX+LemDx, LemY - 2, -1) = TRUE))
      and ((LemNumberOfBricksLeft > 1) or (LemFrame = 15))) then
      begin
        Transition(L, baWalking, TRUE);  // turn around as well
        if (CheckGimmick(GIM_UNALTERABLE)) then Transition(L, baShrugging);
        if CheckGimmick(GIM_BACKWARDS) and HasPixelAt(LemX, LemY-1) and not HasPixelAt(LemX + LemDx, LemY - 1) then
          Inc(LemX, LemDx);
        CheckForLevelTopBoundary(L);
        Result := True;
        Exit;
      end;

      if LemFrame = 0 then
      begin

      Inc(LemX, LemDx);
      if (((HasPixelAt_ClipY(LemX+LemDx, LemY - 1, -1) = TRUE)
      or (HasPixelAt_ClipY(LemX+LemDx, LemY - 2, -1) = TRUE))) then
      begin
        if (LemNumberOfBricksLeft > 1) then
        begin
          Transition(L, baWalking, TRUE);  // turn around as well
          if (CheckGimmick(GIM_UNALTERABLE)) then Transition(L, baShrugging);
          if CheckGimmick(GIM_BACKWARDS) and HasPixelAt(LemX, LemY-1) and not HasPixelAt(LemX + LemDx, LemY - 1) then
            Inc(LemX, LemDx);
          CheckForLevelTopBoundary(L);
          Result := True;
          Exit;
        end else if HasPixelAt_ClipY(LemX, LemY - 1, -1) then
          Dec(LemX, LemDx);
      end;

      Dec(LemNumberOfBricksLeft);
      if (LemNumberOfBricksLeft = 0) then
      begin
        Transition(L, baShrugging);
        CheckForLevelTopBoundary(L);
        Result := True;
        Exit;
      end;
      end;

      Result := True;
      Exit;
    end
    else begin
      Result := True;
      Exit;
    end;

  end; // with L
end;

TLemmingGame.LemCanPlatform
function TLemmingGame.LemCanPlatform(L: TLemming): Boolean;
var
  x, x2: Integer;
  c2: Boolean;
begin
  with L do
  begin
    x := LemX;
    if LemDX < 0 then Dec(x, 5);
    Result := false;
    c2 := true;
    for x2 := x to x+5 do
      if not HasPixelAt(x2, LemY) then
      begin
        if x2 < 3 then c2 := false;
        Result := true;
      end;
    if LemDX < 0 then Inc(x, 2);
    if c2 and Result then
      for x2 := x+1 to x+2 do
        if HasPixelAt(x2, LemY-1) then
          Result := false;
  end;
end;

TLemmingGame.AssignPlatformer
function TLemmingGame.AssignPlatformer(Lemming1, Lemming2: TLemming): Boolean;
const
  ActionSet = [baWalking, baShrugging, baBuilding, baStacking, baBashing, baMining, baDigging];
begin
  Result := False;

  if not CheckSkillAvailable(baPlatforming) then
    Exit;

  if (Lemming1.LemAction in ActionSet) and LemCanPlatform(Lemming1) then
  begin
    if (fCheckWhichLemmingOnly) then
      WhichLemming := Lemming1
    else
      begin
        Transition(Lemming1, baPlatforming);
        UpdateSkillCount(baPlatforming);
        Result := True;
        RecordSkillAssignment(Lemming1, baPlatforming);
        if not fFreezeSkillCount then
        begin
          OnAssignSkill(Lemming1, baPlatforming);
          GameResultRec.gScore := GameResultRec.gScore - SCS_PLATFORMER;
        end;
      end;
  end
  else if (Lemming2 <> nil) and (Lemming2.LemAction in ActionSet) and LemCanPlatform(Lemming2) then
  begin
    if (fCheckWhichLemmingOnly) then
      WhichLemming := Lemming2
    else
      begin
        Transition(Lemming2, baPlatforming);
        UpdateSkillCount(baPlatforming);
        Result := True;
        RecordSkillAssignment(Lemming2, baPlatforming);
        if not fFreezeSkillCount then
        begin
          OnAssignSkill(Lemming2, baPlatforming);
          GameResultRec.gScore := GameResultRec.gScore - SCS_PLATFORMER;
        end;
      end;
  end;

end;

One possible solution is simply to disallow both if there's a pixel at (LemX, LemY-1). Generally, in any state where this would be true, it would already be impossible to assign these skills anyway (as the lemming would generally be either climbing or jumping at such a time). The only exceptions are (a) the situation demonstrated in Glitchy Spikes, and (b) if a constructive skill had, in the frame immediately prior, placed a pixel at that location - most likely to happen with a stoner, but could theoretically happen with any of them.
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)

Nepster

Quote from: namida on February 22, 2016, 06:28:11 PM
One possible solution is simply to disallow both if there's a pixel at (LemX, LemY-1).
Sounds like a good idea.

PS: Your LemCanPlatform causes some irregular behavior as well. Will post a new topic with example map.

ccexplore

Hmm, it's my understanding that this wouldn't work in vanilla Lemmix (ie. DOS Lemmings mechanics).  The builder does advance horizontally one pixel at the frame it turns around and reverts to walker.  But, the walker then will always move horizontally one pixel (facing opposite direction now) on next frame before turning around again.  So there is no net gain horizontally (vertically of course you do gain one pixel per builder).

If I have to guess, NeoLemmix must have changed the second part ("the walker then will always advance horizontally one pixel...") if it enables horizontal movement through solid wall?  Or maybe if you assign a different skill to the walker that enables the second turnaround without any horizontal movement.

[edit: I should add one more clarification.  If your worker is a climber, then you do get a horizontal gain, because at the second part with the walker, it will trigger the climber to do its "climb and fall" thing which gains you 2 pixels horizontally.  This variation does work in vanilla Lemmix, but is arguably more a side effect of the climber's odd behavior.]