Cancelling wait execution on moving room

tl;dr don't use wait - use play

My scenario involves a perodically recurring event while in a room that needs to run some code, wait 2 seconds then repeat. The repeat behaviour isn't crucial - this applies equally to a single wait that you want to cancel on moving rooms.

Using play instead of wait

You can spend some time trying to correct for how wait will execute even after moving room... but forget all about it, because play actually behaves exactly how we want it to already!

  • play can delay execution of code until some amount of time has passed
  • Unlike wait, on moving room play will stop execution
  • Like wait, play will pause while a text box or menu is active

A (simple) solution:

  • On room enter, an event is called
  • At the end of the event, play tile animation lasting x seconds then call the event again

The caveat is that you will have to pick a tile in your room that the play will target. Luckily in my game Resonant Tale I have a permanent HUD drawn over the top and bottom rows of the screen, which are otherwise always black tiles, so I can target play at one of these tiles (e.g. the bottom left corner 0,14) with it being invisible to the player.

If you can't get away with that, hopefully you have a static world tile in your room that you can duplicate the appearance of for your play timer.

The simplest way to create a "timer" tile for use with play is to create a tile with a single frame. While wait uses seconds, in Pulp tile animations are given in fps (frames per second), so for a single frame tile just enter the reciprocal e.g. if you want to wait 2 seconds, set the fps as 0.5 (1/2).

The room script:

on enter do
  call "recur"
end

on recur do
  // do some recurring code
  tell 0,14 to
    play "timer recur" then
      tell event.room to
        call "recur"
      end
    end
  end
end

Before I realised I could just do the above I spent too long fighting with some code to ensure waits would be "cancelled" on moving room. There might be some interesting tidbits in here at least...

How to cancel wait execution on moving room and why you shouldn't

The initial setup I had for a periodically recurring event in a room (e.g. spawning a mole in this post):

  • On room enter, an event is called
  • At the end of the event, wait for x seconds then call the event again

The room script:

on enter do
  call "recur"
end

on recur do
  // do some recurring code
  wait 2 then
    call "recur"
  end
end

Problem 1 - the code after the wait will execute even after moving room.

Problem 1 solved by:

  • In game script, assign current_room = event.room on enter
  • In room event, after the wait conditionally run code on current_room==event.room

The potential surprise there is that event.room doesn't return the current room, but the room that was current when the event was triggered. So if you have code delayed by a wait, move room in the interim, then that delayed code uses event.room, it will return the previous room still. If your delayed code calls another event though, that event's room member will be the current room. In general this is an important nuance to bear in mind!

The game script:

on enter do
  current_room = event.room
end

The room script:

on enter do
  call "recur"
end

on recur do
  // do some recurring code
  wait 2 then
    if current_room==event.room then
      call "recur"
    end
  end
end

Problem 2 - quickly moving out and back in to the room can result in multiple recurring events.

If you move out of the room then back in within the wait window, then the delayed wait code will execute (as the current room is correct) calling the event and the event will be called by the enter event handler. Keep moving in and out of the room and this will get worse still.

Problem 2 solved by:

  • Immediately before waiting, assign event.frame to a variable e.g. waiting_from_frame
  • Instead of recursively calling the event from the delayed wait code, call another event which can conditionally call the original event
  • In this new event, find the difference df between event.frame and waiting_from_frame. Only call the original event if df equals the intended delay

While in the room, the wait delay should match and the event should recur. When leaving then reentering, because the event is called immediately on enter, waiting_from_frame is reassigned to align with the new wait and the previous wait still to execute will mismatch and end.

The game script:

on enter do
  current_room = event.room
end

The room script:

on enter do
  call "recur"
end

on prerecur do
  df = event.frame
  df -= waiting_from_frame
  if df==40 then
    call "recur"
  end
end

on recur do
  // do some recurring code
  waiting_from_frame = event.frame
  wait 2 then
    if current_room==event.room then
      call "prerecur"
    end
  end
end

Problem 3 - text boxes and menus pause wait execution, breaking the above solution

The above solution relies on df matching the intended wait delay, but triggering a say, ask or menu will pause a wait while event.frame will continue to count upwards.

Problem 3 solved by:

  • Assign wait_delay to 0 immediately before waiting
  • Before any say (or ask or menu) assign say_start_frame to event.frame
  • After any say (or ask or menu) calculate say_delay as the difference between event.frame and say_start_frame. Add say_delay to wait_delay
  • When calculating df, add wait_delay

The game script:

on enter do
  current_room = event.room
end

on accountForSayDelay do
  say_delay = event.frame
  say_delay -= say_start_frame
  wait_delay += say_delay
end

The room script:

on enter do
  call "recur"
end

on prerecur do
  df = event.frame
  df -= waiting_from_frame
  df -= wait_delay
  if df==40 then
    call "recur"
  end
end

on recur do
  // do some recurring code
  waiting_from_frame = event.frame
  wait_delay = 0
  wait 2 then
    if current_room==event.room then
      call "prerecur"
    end
  end
end

Some sprite script:

on interact do
  say_start_frame = event.frame
  say "Hello world" then
    tell event.game to
      call "accountForSayDelay"
    end
  end
end

This works... but it's very brittle. In every possible place say, ask or menu might be called while in the room you have to add all of that boilerplate to account for the wait delay. It's also possible other things might put df out of sync with the expected delay and I just haven't come up against them yet!

(When searching I found this similar thread suggesting the partial solution of checking the room name after a wait)

1 Like