Last week ended with Déjà Vu playable from beginning to end, and two things left on the side: the elevator doors and the console pagination. This week was spent turning the rest of my playtesting notes into patches, most of them already merged, and finally crossing off both of those lingering issues.
Squashing the crashes
Three of the crashes I kept running into all came from the same corner of the engine: the inventory windows.
One was a simple issue of the engine assuming windows are closed in the exact order they were opened. Close one in the middle, and it would ask for a reference that no longer existed. Another was a nasty use-after-free: the window callback deleted its own data when closed, but the engine kept dispatching events through a dangling pointer until the next cleanup sweep. Whether the game crashed or not depended entirely on what reused that memory, making it look completely random. The third happened when dragging an object past the edge of the screen, which wrapped an unsigned coordinate and caused the engine to try and allocate a massively oversized surface.
I also tracked down a crash in the shared Mac GUI code. The MacVenture output console keeps the whole session’s scrollback, and once you passed about 680 lines of text, the rectangle calculations overflowed a 16-bit Common::Rect and tripped an assertion. Fixing this required teaching the shared drawing code to handle destination coordinates and clamping properly.
Taming the inventory windows
New inventory windows were piling up on top of each other and growing out of control. The placement code was inheriting both the size and offset of the previous window, so by the time you opened a third window, it was already rendering below the bottom of the screen. Now, the size comes strictly from the settings, and the offset is based on the number of open windows.
Lasso selection inside these windows was also picking up the wrong objects. This is the exact coordinate problem I ran away from last week! The mouse position was relative to the outer window, but the objects were placed relative to the content area, with hardcoded vertical corrections sprinkled in. Having the two coordinate systems written down explicitly was what finally made it fall into place.
Pacing the game correctly
I ran into two completely opposite timing problems this week.
First, the overall game was chugging along at about 17 fps instead of the intended 50. The GUI was forcing a full screen refresh and redrawing the contents of every window on every single frame, eating up 60 ms. Now, window contents are only redrawn when an event, a command, or a script has actually changed them.
On the other end: remember the elevator doors that animated too fast? I finally fixed them. Instead of trying to draw mid-script (which proved unsafe last week), the main loop now paces frames by actual elapsed time rather than a fixed 50 ms delay. The elevator doors now open exactly as they should.
(Bonus: I also fixed a bug where dragging an item ran the command a second time with the destination as the source, printing a nonsensical “X does not have any effect on X”.)
Signing the diploma
At the end of Déjà Vu, you are handed a diploma and asked to type in your name. The original game puts your name right on the diploma itself, but nothing in the engine handled this. It turns out the resource describing the name line (kDiplomaGeometryID) was already defined in the sources but never actually read. Reading it was enough to sign the diploma, and the Print button now correctly hands the signed document to the printing manager.
(This also came with a one-line parser fix nearby, where zero-length strings could leave uninitialized pointers on the stack).

Bringing back “Click to continue”
This was the second issue that got away from me last week. The original game stops printing when the output window is full and waits for a click. ScummVM had the code for the prompt, but it almost never appeared, and when it did, the game froze permanently.
Fixing this took three separate changes: preventing internal state resets from clearing the pending pause counter, ensuring the main loop continues running to process the click, and scrolling the console one windowful at a time instead of dumping the whole message past the player. For that last part I had to add an absolute scrollTo() function to the Mac GUI, which also marks the text as dirty so the window actually redraws at the new position.
Next week
Déjà Vu is finally done as far as playtesting goes. Next up is Déjà Vu II using the exact same approach: play it through, write down everything that looks wrong, and work through the list.
It feels incredibly surreal to say this, but there is only one more week to go in the GSoC program! I’ll be spending it polishing up the rest of the MacVenture titles and getting everything ready for the final submission.
As always, thanks to my mentors for the steady guidance and patience — onward to the final stretch!