avm2: Fix handling of completed timers

It's possible to call 'start()' on a timer
that has currentCount >= repeatCount. This will
cause the timer to tick exactly once, and then stop agian.

We were incorrectly reporting 'timer.running' in such a scenario:
'running' should be reported as 'true' up until just before the
'TimerEvent.TIMER_COMPLETE' is fired.

This fixes gaining money from bloon popping / level completion
in BTD5.
This commit is contained in:
Aaron Hill 2023-03-07 15:01:17 -06:00
parent 92776645b8
commit b27bcf367c
7 changed files with 49 additions and 1 deletions

View File

@ -49,7 +49,7 @@ package flash.utils {
} }
public function get running(): Boolean { public function get running(): Boolean {
return this._timerId != -1 && (this.repeatCount == 0 || this._currentCount < this._repeatCount); return this._timerId != -1;
} }
public function reset():void { public function reset():void {
@ -65,6 +65,8 @@ package flash.utils {
this._currentCount += 1; this._currentCount += 1;
this.dispatchEvent(new TimerEvent(TimerEvent.TIMER, false, false)); this.dispatchEvent(new TimerEvent(TimerEvent.TIMER, false, false));
if (this.repeatCount != 0 && this._currentCount >= this._repeatCount) { if (this.repeatCount != 0 && this._currentCount >= this._repeatCount) {
// This will make 'running' return false in a TIMER_COMPLETE event handler
this._timerId = -1;
this.dispatchEvent(new TimerEvent(TimerEvent.TIMER_COMPLETE, false, false)); this.dispatchEvent(new TimerEvent(TimerEvent.TIMER_COMPLETE, false, false));
return true; return true;
} }

View File

@ -64,6 +64,9 @@ pub fn start<'gc>(
activation, activation,
)? )?
.coerce_to_object(activation)?; .coerce_to_object(activation)?;
// Note - we deliberately do *not* check if currentCount is less than repeatCount.
// Calling 'start' on a timer that has currentCount >= repeatCount will tick exactly
// once, and then stop immediately. This is handeld by Timer.onUpdate
let id = activation.context.timers.add_timer( let id = activation.context.timers.add_timer(
TimerCallback::Avm2Callback { TimerCallback::Avm2Callback {
closure: on_update, closure: on_update,

View File

@ -0,0 +1,31 @@
package {
import flash.events.TimerEvent;
import flash.utils.Timer;
import flash.utils.setTimeout;
public class Test {
public function Test() {
var timer = new Timer(100, 2);
timer.addEventListener(TimerEvent.TIMER, function(e) {
trace("TimerEvent.TIMER: timer.running=" + timer.running + " timer.currentCount=" + timer.currentCount + " timer.repeatCount=" + timer.repeatCount);
});
timer.addEventListener(TimerEvent.TIMER_COMPLETE, function(e) {
trace("TimerEvent.TIMER_COMPLETE: timer.running=" + timer.running + " timer.currentCount=" + timer.currentCount + " timer.repeatCount=" + timer.repeatCount);
})
timer.start();
setTimeout(function() {
trace("After 300ms: timer.running=" + timer.running + " timer.currentCount=" + timer.currentCount + " timer.repeatCount=" + timer.repeatCount);
trace("Starting timer again");
timer.start();
setTimeout(function() {
trace("After 200ms: timer.running=" + timer.running + " timer.currentCount=" + timer.currentCount + " timer.repeatCount=" + timer.repeatCount);
trace("Stopping/Starting timer again");
timer.stop();
timer.start();
}, 200);
}, 300);
}
}
}

View File

@ -0,0 +1,11 @@
TimerEvent.TIMER: timer.running=true timer.currentCount=1 timer.repeatCount=2
TimerEvent.TIMER: timer.running=true timer.currentCount=2 timer.repeatCount=2
TimerEvent.TIMER_COMPLETE: timer.running=false timer.currentCount=2 timer.repeatCount=2
After 300ms: timer.running=false timer.currentCount=2 timer.repeatCount=2
Starting timer again
TimerEvent.TIMER: timer.running=true timer.currentCount=3 timer.repeatCount=2
TimerEvent.TIMER_COMPLETE: timer.running=false timer.currentCount=3 timer.repeatCount=2
After 200ms: timer.running=false timer.currentCount=3 timer.repeatCount=2
Stopping/Starting timer again
TimerEvent.TIMER: timer.running=true timer.currentCount=4 timer.repeatCount=2
TimerEvent.TIMER_COMPLETE: timer.running=false timer.currentCount=4 timer.repeatCount=2

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
num_frames = 16