ruffle-rs / ruffle

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

Problems with Electricman 2 #9594

Open n0samu opened 1 year ago

n0samu commented 1 year ago

With the panic in #4932 soon to be fixed by #9447, I figured now is a good time to list the remaining issues with Electricman 2. This issue describes the state of the game with the current code in #9447 (at the time of writing).

The first noticeable issue with the game is the one described in #6261. Rapidly clicking the level buttons does work, at least for me.

Compared to the original Electricman 2, Electricman 2 HS is far more playable in Ruffle. That's because Electricman 2 HS allows you to skip the tutorial, which is the part of the game that is most buggy in Ruffle.

If you try to play through the tutorial, all of the lessons after the first "defeat the hologram" exercise will be skipped, but the enemies from the skipped exercises will still be spawned - into the menu! And when you start the first level, you have to fight the enemies that were supposed to be in the tutorial, too. Those enemies start attacking you even before the level countdown finishes.

https://user-images.githubusercontent.com/71368227/219381282-503a4311-12be-4a40-9bdd-01a2e11da890.mp4

If you skip the tutorial in Electricman 2 HS, the game works much better at first. But it's possible to trigger a very similar bug by leaving a match in the middle, then starting a different match:

https://user-images.githubusercontent.com/71368227/219384283-187eff54-e271-4f02-a57c-49f65092619a.mp4

If you skip the tutorial, the first 4 matches should work fine as long as you don't quit out of any of them. However, match 5 (the first match of round 2) will be impossible to complete because the game will not recognize when you have defeated all of the enemies.

When problems are occurring or about to occur, one or both of these warnings always seems to be spammed: WARN run_frame: ruffle_core::avm1::activation: SetProperty: Invalid target WARN run_frame: ruffle_core::avm1::globals::movie_clip: MovieClip.swapDepths: Invalid target

n0samu commented 1 year ago

Copying https://github.com/ruffle-rs/ruffle/issues/4932#issuecomment-1254457259 for greater visibility:

@Aaron1011 has a branch that might help understand the issues with this game: https://github.com/Aaron1011/ruffle/tree/rewind_depth Here are his messages on Discord explaining the issue:

It looks like you get avm1 to get the same clip at two different depths, by using 'swapDepths' and then rewinding The rewind is not to the frame where the timeline object is added - it's just to a slightly earlier frame where nothing happens ok, it looks like === is just doing something odd - calling getDepth on each object returns different values, as expected So, https://www.kirupa.com/developer/actionscript/depths2.htm seems correct (as far as I've tested) - a rewind in avm1 wipes out the timeline, and forcibly overwrites anything at the timeline depth for every frame from frame 1 to your target frame plus some weirdness where === doesn't seem to do pointer equality for MovieClip and Electricman2 actually depends on this behavior It tries and fails to remove a clip with a negative depth, but then that clip ends up getting overwriten due to a subsequent rewind Because of the way it uses swapDepths, subsequent enemies end up with positive depths - this only happens when you kill the first tutorial enemy So I suspect the author was completely unaware of this behavior, and it just happened to work for an entirely unrelated reason

And later conversation between Aaron and kmeisthax:

Aaron:

The test I linked shows that === evaluates to true But they behave like two different objects you can call getDepth on them and it returns different values My guess is that === is actually checking some hidden properties (movie clip symbol ID or something) which looks like an object identity check in all other situations

kmeisthax:

the instance number, perhaps?

Aaron:

Yeah, maybe

kmeisthax:

hmm... so instead of if !child.placed_by_script() in run_goto we need if depth < AVM1_DEPTH_BIAS or whatever we call it

Aaron:

we also need logic to remove other objects with depth < AVM1_DEPTH_BIAS

Since then, Aaron has added a test in #9583 to demonstrate the rewind issue.

sombraguerrero commented 1 year ago

It is not always the case that the [previous] panic or the non-ending stage problem occurs at the same fights reproducibly. It tends to vary based on the sequence in which you play.

As a matter of record, I will note that one behavior that was also fixed with these changes is that when you are defeated, it is now a proper defeat. Prior (when the panic was still happening), if you were defeated, it was still responded to with a victory and you were allowed to continue.

n0samu commented 1 year ago

More details about Flash's timeline rewind behavior can be found at the following sources:

ZaydTheCoder commented 2 months ago

This issue is not fixed? I am confused.

Sapo-san commented 2 months ago

just checked, still not fixed :/

sombraguerrero commented 2 months ago

We don't claim this issue is fixed. Aspects of it have made progress, which is what the previous discussion is, but the issue is still open as there is still work to do on it. It may not seem like it from a user perspective, but the way this game is written is extremely convoluted and difficult to solve for.