Skip to content

Adventure: dialog gates can be walked through by bumping them twice (resetPosition keeps old positions) #12565

Open

Description

@TheSAguy

Describe the bug
In Adventure mode, a gate that is a type="dialog" object, such as a locked door placed with gate.tx, can be walked through without opening it. Bump the gate, keep holding the direction into it, and the player ends up inside it on the second bump and walks out the far side.

To Reproduce

  1. Open a map with a locked door placed by gate.tx (for example map/graveyard_crypt/crypt.tmx), without whatever opens it.
  2. Walk into the door. The dialog shows and the player is stepped back.
  3. Close the dialog and hold the direction into the door straight away, so the player gets back to it in about 3 frames.
  4. The second bump puts the player inside the door.
  5. Keep walking. No dialog shows and nothing stops the player leaving by the far side.

The timing matters: a return of 3 frames gets through, 4 or more does not.

Expected behavior
Every bump leaves the player outside the gate, and the gate can only be passed through its dialog.

Platform
Not platform specific; the code is in shared forge-gui-mobile. Tested on Windows.

Additional context

Cause, with line numbers from master at b2a603deb28:

  1. MapStage adds the player's position to positions every frame and keeps the last 4 (lines 1088-1090).
  2. DialogActor.onPlayerCollide shows the dialog and calls stage.resetPosition().
  3. MapStage.resetPosition (line 1226) moves the player to positions.peek(), the oldest of the 4, but never clears the queue. The queue still holds this bump's first overlapping position, which is inside the gate.
  4. On a quick second bump, that inside position has become the oldest, so the player is stepped back into the gate.
  5. MapActor.collideWithPlayer (line 216) only calls onPlayerCollide() when contact starts. A player already inside gets no new dialog and no step back, so they walk straight out.

Suggested fix:

     public void resetPosition() {
-        if (positions.peek() != null){
-            player.setPosition(positions.peek());
+        Vector2 back = positions.peek();
+        if (back != null) {
+            player.setPosition(back);
+            positions.clear();
+            positions.add(player.pos());
         }
         stop();
     }

Starting the queue again from the stepped-back spot means a later reset can't return a position recorded before this one. The positions.contains(player.pos()) check in freezeAllEnemyBehaviors still holds, because the queue contains the current position. The other callers of resetPosition() (the enemy contact in MapStage, QuestActor, DummySprite) also want the player back outside.

Testing: I found and fixed this in a Forge fork I work on, after a player reported it. Before the fix, the player walked through a gate on the third bump. After it, there were 0 passes in 78 bumps across three gates, walking and sprinting. I haven't run this on a stock Forge build, but the code involved is the same in master.

Activity

  1. added theissue type on Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions