Next week is the midterm evaluation, so I think this is a good opportunity to take a look at things that have been done, and what is on the docket for the near future. To begin, I’ll quickly outline what I did the past week, and then I will briefly go over what I’ve done in total according to milestones, and lastly I’ll mention what I plan to do next.
This past week
I spent the past week finishing up various things before the evaluation. I added and filled out quite a few functions, with the Logic skeleton being the most important of those. I translated the functions in logic.cpp which don’t extend into Level, and which don’t require an external set of definitions (in this case from Story.GS, which I will discuss more next week since that will be a focus). With that done, the game engine now has a frame loop, where it calls the main function in logic, which in turn acts as the game state loop. This game state loop can run cleanly, as the code surrounding the calls to Level is filled out. In essence, I was making sure the skeleton of the Engine layer of the game was complete, before I extend into the next layer, which I am referring to as the Object layer. This is where Level, the interface between the engine layer and object layer, resides. In that same pursuit, I also filled out more of the kernal.cpp functions that were stubs, but which could at this point get filled out. In particular, this would be the drawUniv() function calls, as these are sort of the end of the engine skeleton (loosely consisting of the engine init + loop, the logic skeleton, and the univ function at the end). These functions are somewhat complicated, dealing with how to put together parts of the engine into the visible area. Most of this could be done without the screen drawing Driver routines (blit(), superSprites(), etc.), as it focuses on sorting the visible components by priority, determining where to draw each of them, what should be visible based on position, etc. In doing so, I also added the drawChr functions file, because it contains the final step before drawing the tile bitmap data itself. However drawChr will be done as part tackling the tile/sprite drawing in general. I also finished some non-code things as well however. I filled out an outline of the functions that have been encountered, and whether they are implemented or not. This went hand in hand with creating a map of the game engine as a whole, something I mentioned having worked on a lot the week prior. This week I added and changed a few things about it, and put it together in a way that can be easily understood. This will help (and has already) in the future for deciding how to structure data types and planning the next steps.
What’s been done so far
From a perspective of ‘milestones’, these are what I’ve done so far:
- ProDOS file system implementation
- Source engine skeleton in driver/kernal <- continually being added to, but especially the past week
- Palette system implementation
- Skeleton of logic <- lots from the last two weeks
- Game engine scope outline and plan <- lots from the last two weeks
However without context that doesn’t mean a ton. So I’ll go over each a little bit.
- ProDOS file system implementation
The Immortal on the Apple IIGS was stored with the ProDOS file system, which means that accessing assets from the provided game executable requires ScummVM being able to read from a ProDOS file. Since ScummVM did not already have an interface for this, the first big task was to create one. This involved researching and experimenting with ProDOS files until I could implement an object that could output the individual files stored in a ProDOS file. However ScummVM uses an ‘Archive’ class to wrap unique file types like this together and make the process consistent across different file types. So, after making the engine able to read ProDOS files, I converted the class into an implementation of Archive, which required implementing specific methods in the class for the file system and the file itself, as well modifying some of the data structures and types used along the way.
- Engine skeleton from Driver/Kernal.GS
With the assets being accessible, I could start translating the game engine code. This required organizing and reading through the source code until I found the right entry point, and what backend interaction could be ignored (hardware specific initialization for example). I found the entry to the game engine by following the routines loaded when the Apple IIGS boots a disk with ProDOS, until I found the JMP instruction that takes it out of Driver.GS and hardware initialization, into the ‘main’ function of the engine in Kernal.GS. I used the main function in Kernal.GS as an anchor to build the main engine skeleton. Which is to say, I implemented that main function into the ImmortalEngine class by translating anything that did not branch off into a subsystem, leaving them as stubs. This skeleton could then be used as reference for what routine to start translating next, and what subsystems are connected to the main loop. By this milestone I had also translated Compression.GS, which was done early because it is self contained and related to the asset files, which I was just working with for ProDOS handling.
- Palette system
This isn’t a big milestone on the surface, as it is not a huge amount of code involved. However, implementing the palette loading, applying, and manipulating routines required me to get a much better sense of how ScummVM draws the screen, in terms of both the graphics and the palette. And crucially, what the process of converting the game asset to the ScummVM surface is.
- Logic skeleton from Logic.GS
Logic is the game state loop, and as a result it is the through-line from the main engine skeleton, to the code which creates and manages everything within the game itself. This step was crucial not just because it is one of the routines that runs every single frame, but because it helps gives perspective for the purposes of organizing the model of the game engine as a whole. It allowed me to identify the main path the game logic takes. From ScummVM -> ImmortalEngine -> Logic -> Level -> Room -> Monster.
- Game engine scope outline and plan
With the insight gained from engine -> logic, I was able to put together a map of the game engine as a whole, and the flow of logic from input to output in general terms. This gives better perspective on the connections between subsystems, and makes planning the next steps much easier. This was done by identifying the ‘scope’ of each subsystem, based on how the game treats the subsystem in terms of access and data manipulation.
I found when working with logic.cpp, that I needed a number of definitions for the certificate related routines. These definitions come from Story.GS, and after looking into that file, I found that it is made up of essentially three sections of data: 1. ‘Story’ segments, consisting of the properties and unique data for each room within a level. 2. ‘Definitions’, which is a block of global definitions, many of which are used by the story segments, because they define strings, object types, animation cycles, and more. 3. ‘Action’ tables. These seem mostly related to enemy and character attacks and the animation frame timing, as well as damage values.
So the next step will be to implement the ‘Definitions’ segment of Story.GS as a part of the engine class, in the form of a sequence of structs.
After this, the next major steps I have identified are Level (and the skeleton of Room), and sprite/tile drawing. I am not sure at this time what order I will do those in.
I will also have another blog post in the near future (possibly tonight actually, depending on how long it takes), which goes over the conceptual model of the game engine structure in a way that is hopefully clear and easy to understand.
Until next time, thanks for reading!