EMI is completable!

I finally got around to do a proper playthrough of EMI in ResidualVM with all the fixes and improvements made during GSoC. I’m happy to say the game can now be played from start to finish with only some minor issues 🙂

The biggest issue that remains right now is a bug with the lava boat puzzle on Monkey island. The boats on the lava field are not visible, which makes the puzzle difficult to complete. JoseJX is working on this issue though, so hopefully it will be resolved soon.

Other than that, there are a few graphical issues. Animations occasionally still seem to snap to the wrong frame. This happens for example when jumping out of the bank window on Lucre island, and when Guybrush gets a grog at the Micro-Groggery on Jambalaya island. Also, the text in the end credits flickers and the text color doesn’t match the original.

Pathfinding is not perfect yet. Actors tend to zig-zag around before reaching their destination, which looks silly sometimes.

On the audio side, footstep sounds are sometimes wrong. For example, when walking on the Jambalaya island beach, it sounds like Guybrush is walking on wood. Also, voiceovers sometimes tend to get cut short, which is a bit annoying.

These issues will have to be resolved after GSoC though. I’m pleased that all of the issues I spotted are fairly minor, and the game is very much playable in the current state. I’m confident that support for EMI could be included in the stable release of ResidualVM fairly soon.

As GSoC soon comes to a close, I want to thank my mentors and the ScummVM/ResidualVM community for your support and for providing me this great opportunity to bring another classic adventure back to life. This has been a fun summer!

Bug squashing

With all the major feature implementation tasks completed, this week I focused mainly on fixing any remaining bugs I could find. Klusark was kind enough to do a full playthrough of the game in ResidualVM, which revealed a number of issues of varying severity. Some of these issues were blockers which prevented progression in the game at certain points. These should all be resolved now, and on current master the game should be completable from start to finish again.

Here’s a brief summary of the issues I resolved this week:

  • Guybrush got stuck when attempting to lure the fish on Lucre island into the scupperware box, preventing progression in the game
  • The game got stuck when giving the earrings to the Dainty Lady figurehead
  • Saving and then restoring a savegame caused lighting to break
  • Saving and restoring caused a crash due to broken shadow initialization
  • Guybrush faced the wrong way after the Voodoo Lady summons the items from the heirloom chest
  • Some of the ships and the water at the harbor on Melee island were rendered incorrectly
  • Guybrush was invisible in the heirloom cave, and the light shaft effects were drawn incorrectly

The last two issues were the most interesting ones, so I’ll discuss those a bit next. Let’s look at a before-and-after comparison from the Melee Island harbor.

Several issues can be seen in the screenshot of the old version on the left. The color of the waves is too dark, the water splash sprites are drawn over the ship and the rowboat, and the bottom of the ship is not clipped correctly.

In debugging these issues, I used Apitrace in order to figure out how exactly the original game forms the final picture. Since the game has an OpenGL renderer (activated with the parameter -gl), we can use Apitrace to extract the list of OpenGL function calls as well as the OpenGL state on each drawn frame. By examining the trace I found that ResidualVM was drawing some of the sprites in a different order than the original game, and for some sprites depth testing was enabled while it wasn’t in ResidualVM. Also, the original game draws mask sprites to clip the ship and the rowboat to the correct shape, which were not drawn in ResidualVM at all.

The mask sprites visualized. With additive blending the black bits become transparent.

When actors (including sprites) are about to be drawn in EMI, they are first sorted into descending order by their sort order, which is a per-actor integer field that can be controlled from the game scripts. When an actor is attached to another, the actor’s sort order will be overridden by the parent actor’s sort order. This is what happens when the water splash sprite is attached to the ship sprite. However, after the sprite is attached, the scripts then set a new sort order for the sprite. In the original engine this value overrides whatever was derived from the parent, but in ResidualVM the sort order was not updated, which caused the sprites to be drawn in wrong order. Updating the sort order even if the actor is attached fixed this issue.

The rest of the issues were mostly related to incomplete parsing of sprite and model data files. I discovered flag fields in the sprite data, which control whether depth and alpha testing should be enabled for that sprite, and whether additive blending should be used or not. I also found a similar flag that controlled the blending mode in the model data. I discovered these mostly through trial-and-error, by changing suspicious-looking bits in the data files slightly and then loading up the original game to see how the changes effect the end result. Setting the correct blend mode for models fixed the water color.

To enable drawing of the mask sprites, I had to disable a piece of old, temporary code that simply skipped drawing of any sprites with “mask” in the name. I assume this was done because if incorrectly rendered, the mask sprites looked ugly and only cluttered the image (see the image above). Now with the blending and sort order fixes in place, the masks did produce the correct result, though.

This is the heirloom cave before and after my changes. The left one is fairly obviously broken: Guybrush is invisible, the light shaft effects look ugly, and shadows are missing. Fortunately the light shaft effect was corrected by the previous model blend mode fix.

Guybrush’s invisibility was caused by another seemingly temporary hack in ResidualVM, which disabled drawing to the color buffer if an actor’s sort order was greater or equal to 100. In the cave, Guybrush’s sort order is set to 100 in the scripts, which caused him to become invisible. The original engine doesn’t seem to do anything similar, so I concluded that this behavior was wrong.

The cave set also led me to discover another previously unknown field in the set shadow data. The field is a string, which specifies a name of a light in the set. When this string is present, the shadow projection point should be the light’s position, instead of whatever is stored in the shadow data. After parsing the light name and implementing this behavior in ResidualVM, the shadows now work correctly in the cave also.

These fixes can currently be found in this branch.