ruffle-rs / ruffle

A Flash Player emulator written in Rust
https://ruffle.rs
Other
15.44k stars 800 forks source link

Can Your Pet: Soccer ball minigame not working correctly #10013

Open n0samu opened 1 year ago

n0samu commented 1 year ago

Describe the bug

Link to SWF

(Warning for those unaware, this is a shock/horror game, so don't play it past the point where you can see the bug, unless you're prepared for that!)

In Can Your Pet, you're supposed to be able to pass a soccer ball back and forth with your pet by clicking on it while it's in the air. But in Ruffle, the ball disappears when you try to pass it back.

https://user-images.githubusercontent.com/71368227/224529775-f7898a3b-067a-47fa-9560-0b2ad87d1a65.mp4

Expected behavior

In Ruffle builds 2023-02-24 and before, the minigame worked correctly:

https://user-images.githubusercontent.com/71368227/224529842-0131f1dc-f5c7-49e4-8b59-b68c7a424fc0.mp4

Affected platform

Desktop app

Operating system

Windows 10

Browser

No response

Additional information

When the soccer ball disappears, this error is shown in the terminal:

ERROR run_frame:run_all_phases_avm2: ruffle_core::avm2::activation: TypeError: Error #1009: Cannot access a property or method of a null object reference. (accessing field: y)
        at framework::GameLogic/ballSky()
        at flash.events::EventDispatcher/flash::events::EventDispatcher::dispatchEvent()
        at fl.transitions::Tween/set time()
        at fl.transitions::Tween/nextFrame()
        at fl.transitions::Tween/onEnterFrame()

The error happens on the first line of the ballSky function: var _loc2_:Number = this.ball_mc.y; ball_mc is a MovieClip. It was originally instantiated like this: this.ball_mc = new Ball_MC(); It doesn't make sense that ball_mc is null here.

A bisect yields an unsatisfying result:

There are only 'skip'ped commits left to test.
The first bad commit could be any of:
aa7dc52b82b27b398f2433b056914bb8550bcaa8
d31c686360535b318472a0da08736c57d5bd4f04
d996aecb041f860f05b7ee60938b91796438c520
39063a5a04be4f6c5fac4283c3c23998b7bc68f1
bc240db517d6ab70772d863ed9e553c7b56bd43e
337149ff0ee897f5b14e3bb55d482d8a47ea48a5
8831042e8fd0243797d4ba12abc94cd65124e9f5
24e231ae06ba49dba2a07cf7e2c2c1d61995d86e
be16286407259219e114a7f53dfa6aa50d37f94f
62767213a6c042a2fc0bbc08c38805af53e129c8
6ce7bd3277da9e35892d6c14a545a1370c7dcd39
82a3f473d970cd3ab601204e477c419ef1a198c2
We cannot bisect more!

The problem is caused by something from #9680, but since so many of those commits cannot run content without panicking, it is impossible to test it more.

Lord-McSweeney commented 1 year ago

I've come to the conclusion that this is the affected piece of code:

      private function initMiniGame() : void
      {
         Particle.gravity = 0.25;
         this.ball = new Circle(65);
         this.ball.x = this.ball_mc.x;
         this.ball.y = this.ball_mc.y;
         this.ball.display = this.ball_mc;
         this.ball.addEventListener(MouseEvent.MOUSE_DOWN,this.onBallDown);
         this.ball.addEventListener("bound_right",this.onBallRightBound);
         this.ball.addEventListener("bound_bottom",this.onBound); // This event listener is fired when it shouldn't be. It calls removeMiniGame(), which sets ball_mc to null.
         this.world = new PhysicsWorld(this.root.area.rect);
         this.world.addChild(this.ball);
         this.ballTose();
      }

      private function onBound(param1:Event) : void
      {
         this.removeMiniGame();
      }

      private function onBallRightBound(param1:Event) : void
      {
         this.root.area.map.door.gotoAndStop(2);
      }

      private function removeMiniGame() : void
      {
         this.world.remove();
         this.ball.dispose();
         this.root.area.removeChild(this.ball_mc);
         this.ball.removeEventListener(MouseEvent.MOUSE_DOWN,this.onBallDown);
         this.ball.removeEventListener("bound_right",this.onBallRightBound);
         this.ball.removeEventListener("bound_bottom",this.onBound);
         this.ball = null;
         this.ball_mc = null;
         this.switchState(GROW);
         this.pet.state = PetEvent.HAPPY;
      }

What I'm confused about is that the onBallDown function adds an event listener for ballSky but nothing removes it, ever. So I don't understand why it can run at all on earlier versions.

Lord-McSweeney commented 1 year ago

I tried manually checking every single function parameter/return type. The only mistake I found was that Stage.stageWidth/stageHeight should be an int, not a Number. Changing it to an int does not fix the issue, so type coercion is not the issue in this case.

waspennator commented 1 year ago

Still occurs as of latest nightly.