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 roomplay
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
betweenevent.frame
andwaiting_from_frame
. Only call the original event ifdf
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
(orask
ormenu
) assignsay_start_frame
toevent.frame
- After any
say
(orask
ormenu
) calculatesay_delay
as the difference betweenevent.frame
andsay_start_frame
. Addsay_delay
towait_delay
- When calculating
df
, addwait_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
)