Hello. My name is Craig. I am the other guy who made Office God. The developer.
I’m going to be talking about a few of the key features of Office God and their various interactions and ramifications on/with the rest of the game. In this post, and probably in another post (and possibly a third one also) I’ll be discussing the TIME MACHINE — yes, Office God has a time machine. The world of plastics is exciting.
My development blog posts will accidentally double as a postmortem for a game that was not so much designed as grew organically from a vaguely-defined base. The base only nominally resembles the end product. [Hey!!! It was indeed designed, just very poorly. —Blackwell]
The TIME MACHINE has two main facets/features:
- Key moments in the game can be revisited and shared with friends.
- The game runs while you’re not playing it.
This first game engine blog will start addressing feature 1… You know… because I called it 1 and it makes more sense to talk about it first.
TIME MACHINE: Revisiting and Sharing Moments
In Office God you can revisit and share key moments in the game — in the way that you would re-watch or share something you found on YouTube or Tumblr. That said, this is actually a throwback feature to an earlier incarnation of the game, where Blackwell had envisioned writing heavily branching plots such that the gameplay experience between players would vary significantly. Using this feature, the player would be able to share the results of reprimanding Morris or laying off Duc with other players who didn’t. Fun for everyone.
Anyway. That isn’t a thing anymore (at least not in Episode 1). Turns out writing branching content takes more time than writing the game engine and even the core gameplay mechanics, so the branching paths are now way toned down.
At the time, I determined that there was two effective methods for pulling this off:
- The event gets captured as it occurs in a special format, such as a video capture or data-driven capture that will record the event exactly as it occurred. We’ll refer to this scenario as “actually doing it“.
- Or, the event gets captured by recording the exact state of the gameworld at the time of the event, track back a minute, then let it play again – theoretically happening in exactly the same way it happened the first time! We’ll call this “faking it“.
Actually Doing It
Actually doing it has a few good things in its favour:
- Exact reproduction of events as experienced by the player: because it’s a capture, it’s going to be the same every time as it was originally seen.
- If it’s video, the sharing is very easy and accessible (via YouTube, etc.)
However, it comes with some significant overhead
- We have to introduce a distinct format and handling for that format (rather than leveraging something we already have, it’s going to be distinct).
- It requires significant server resources (especially if it’s a video capture) in the form of an ec2 instance with ffmpeg.
- It will require its own storage solution — though in the time since Office God was first conceived, the YouTube API took care of that problem.
- It won’t play nice with the second feature of the TIME MACHINE (the game runs when you’re not playing it) without a headless instance/browser (phantomJS) running the game solely for the purpose of capturing events for players — also at the time, this wasn’t really a thing. It is now. I’ve since implemented this feature in my professional life. It’s not a low-effort (or low-cost) solve.
This concept also has some good things going for it:
- It leverages existing mechanics: Basically, all we would be doing is saving the game before any key event started to run.
- It should be an exact replay without introducing any new capturing mechanics, playback formats, arcane server configurations or computation instances
It also has some drawbacks – the last of which turns out to be pretty significant
- You have to introduce automatic game saving – a LOT of automatic game saving. Every time a variable changes, it needs to be recorded.
- Sharing requires that all saved states be stored on the server (rather than on the client machine) such that they can be accessed for setting up shared events
- The event may not transpire exactly as it did the first time around, because the game/character logic is going to be re-run every time.
Those factors presented, I decided to go with the second option: faking it by leveraging existing mechanics and attempting to overcome the listed issues.
Saving the game on the server was not a major issue. Rather than keeping the files in the client cache, it all went into a database; one row per saved state. Done.
Guess what? Saving that frequently represents a LOT of data. Some months down the road, we encountered some significant payload size issues (where loading the current saved states for a user began to take a long time, or time out entirely). If each saved state is a verbose XML hunk, it adds up. I’ll cover this in another article.
The events DON’T play out exactly the same every time. This phenomena is observable in most games which involve both character AI and saved games: Have you ever saved in an action game before a fight, botched, reloaded, and had the fight play out entirely differently?
There is a small amount of random flux in the game which can cause events to play out differently each time; The most noticeable being in the office employee pathfinding (which I’ll also describe in another article). Sometimes characters end up standing in somewhat different positions, sometimes they’re late (getting hung up on the furniture and having to take less-direct paths around), or (the most catastrophic example) sometimes they simply give up and go back to their desks, or don’t go to their desks, or whatever you didn’t want them to do. The results range from dead on with all lines and actions delivered as expected (90% of the time), silly, with some conversations occurring across the office rather than face to face (9% of the time), or non-functional where the participants simply don’t show up and the event doesn’t happen (1% of the time).
The fix that we’ve toyed with involves forcing the employees into location using an easter-egg teleport animation.
That all said, it does work… Just not always as intended.
Let’s sum this up by talking about the current implementation:
The Past events interface shows a list of all the past significant events that have occurred in the player’s game thus far (largely skits and instant messages).
Each of these is a link to a playback build of the game (online) that contains the ID string of the associated saved state and the event.
The playback build loads the associated saved state, the associated event, and starts the game clock.
The event then plays out (hopefully as intended) and the link can be re-distributed.
Next time I’ll go into the “always-on” feature.