About the game...
Project Proposal
Milestone 1
Milestone 2
Oroborous is a third-person action-adventure game (not unlike the N64 and Gamecube Zelda games). It it, you take the role of an intrepid adventurer out to cleanse the world from the evil of the Void Snake by delving into his temple and destroying his altar. Along the way, the player will have to use speed and strength to combat several monsters that he encounters along the way.
The game uses the Quake 3 BSP file format/space partitioning sceme to display and model the game world the player enters; character models are implemented and animated via MD2 from Quake 2. The player (as well as all other entities, such as power-ups, enemies, and doors) interacts with the world and other things via a system of sending messages about events (creation, destruction, others) and actions (events which are caused by some entity, which is to say some thing present in the game world). Enemies are driven by a very simple deterministic AI which chooses an action for the enemy depending on the state of the character.
Through the use of text events, Oroborous has the familiar feel of text based games, introducing a narrative quality which makes the game more effective as a storytelling tool.
There is also a keyboard-activated easter egg within the game. Feel free to search for it yourself; if you wish to know exactly what the controls for it are, please consult the end of the postmortem.
The code is organized as follows:
Everything that is in the engine directory is tied very closely to how the actual game intrinsically works. Things outside the engine directory are files which are specific to the layout of the actual game itself. The advantage of this system is that there is a huge intrisic difference between what parts of the code may interact with content, and what may not. A few hacks were made towards the end into the engine code (the player_is_dead flag) to make things run more smoothly, but other than that, the two were severely differentiated. This enabled content code to be enabled swiftly, correctly, and effectively.
At the heart of the engine is the World class. The World brings together several other important classes which actually run the game. These are typically given the extension "Manager", because they keep track of a lot of smaller structures. A few were never really used (LightManager, MaterialManager, EffectManager). There are also a number of other things that aren't managers, such as the bsp, the state, the ui, and the object creator. Many of these classes also have virtual methods, such that their code could be extended using different algorithms to improve effectiveness.
Aside from the map used in the demo, which was created by Calvin with the Quake 3 level editor, GTK Radiant, all models, textures, and sounds were non-original. Several of the models came directly from Quake 2; others were culled from the web at Planet Quake. The initial level music is "The Temper of Poseidon" by Project Pitchfork, and the boss music is "Timekiller" by the same group. The ending music, is "Thorns" by Wumpscut. The easter egg music is from Homestarrunner.com. Sounds were found in a variety of places.
One of the most important elements of gameplay in Oroborous is combat. Probably the most important parts of getting combat to actually happen is to realize when something actually hits something else. Thanks to the robustness of the messaging event/action system, actually implementing how combat works in general - where one side states where it is attacking and checks to see if it hits anything, making hit things take damage - was fairly easy. However, realistically doing this - that is to say, attacking such that the area of effect for the attack action models that of the attack as seen during the attack animation - turned out to be a non-trivial problem.
My initial thought on the matter was that it would be best to have a "horizontal slash" and a "vertical slash" attack type, with a "weapon" type available if e would be able to autogenerate bounding boxes for weapons. From there it seemed like what I would want to do would be make a box and rotate it over theta degrees (dependant on the monster) over time, effectively sweeping and area with the attack. After implementing this and finding that it was not working correctly regardless of how I tried to debug it, I found out that due to the way we handled the BSP structure, it is impossible to have non-axis-aligned bounding boxes in our game - which is exactly what the slash algorithms were generating. With that, a simple solution was suggested: instead of making a box, simply place a sphere some distance away from the player and sweep that around instead. This seemed to work adequately, so it stuck, though it would perhaps have been better if there was a subdivision such that several spheres would be drawn in the area from the end of the desired attack area to the point where the player is.
Once that was complete, it was simply a matter of picking an attack type, sweep area, and attack duration appropriate to the individual models used.
It has seemed to me that every tutorial that I have found is either massively specialized to downright buggy. The Quake 3 BSP tutorial that I found had a serious flaw in it which would make it impossible to render any map other than the one it came with and a few others from the actual Quake 3 game without butchering the level. Triangles were messed up and missing, nothing seemed to work. It was phenomenally confusing until I took the time to find another Quake 3 engine, called Diesel, compile it, and dig through the source until I found that the tutorial code was rendering everything as GL_TRIANGLE_FANs, and not using the triangle vertex indices. It furthermore gave that data from the BSP a very confusing name which was something like "Mesh Indices". (Especially confusing since the Quake3 BSP format supports triangle meshes...)
The Quake 2 Model loading code was also specialized to render only one specific model, storing all the frame and instance information in with the model itself. When I transcribed this, I basically rewrote the entire thing.
While using external code did save a lot of time, it always does seem to be the case that in order to understand code that someone has written, you need to write it for yourself. On the other hand, the sound engine FMOD was very easy to use, and worked seamlessly with everything.
I am hard pressed to come up with another thing that was technically difficult to implement, most of everything was fairly straightforward, to be honest. The engine has been in development since Lab2, which is to say that the engine started instead of Lab2... But, regardless of things, it has been slowly getting formed to look the way that it's come to now for a long period of time. Writing a game involves keeping track of a large ammount of different resources and parts of code, but the most important thing to do is to make a strong cohesive engine which can stand up to the frequent miscellaneous needs of the game that tend to undermine the structure. Often times there are little classes that need to one thing about one another in only one specialized context, and they aren't really meant to do so anytime otherwise, and this is when hacks are made.
Organizing the code was a significant endeavor even before it was written, and this occured over many sheets of paper with many and various arrows and bubble drawings. Our code has so few hacks in it because from every location, all of the important bits of information are fully accessible. Very little of the organizational structure is forced, relying instead on something of an unwritten style specification, this however is a good thing because it allows a lot of freedom of design on the more content oriented parts of the program.
(Evan) I think that the ease with which I was able to add in the easter egg really reflects how smoothly the code fit together organizationally. Most functions were in a quite logical place, and typically any unique thing had its own class and set of files, making it easy to find something based on knowing what it was in the game. This system of compartmentalization also made it very easy to add extensions to existing bases, like new actions, events, and monsters. This seemed to allow for very rapid addition of new ideas into the code base, which allowed us to make huge changes to the game very rapidly - rapid prototyping is a plus in my opinion.
(Calvin) One specific thing I am very pleased with is the camera, which is able to dance about very smoothly and follow the player around obstacles without getting stuck or going through anything solid. It operates because of a two spring system, that it is always springing to a location it wants to be at, and it's focal point is springing to where it's target is. The height and distance from the player are adjustable (though we didn't work this in, largely because it would need fine tweaking rather than being difficult to do.) The camera was also designed to cut at any time when it was getting hard to see the player. The cuts could be jerky at times, but tended to be reasonably clean overall.
(Calvin) I am very happy that we used the Quake3 BSP format for the world. In the past, we have done projects for which the world file was very constricting and prevented the games from having very interesting or expressive qualities. The BSP map format allows for a lot of character in the map, especially through use of lightmaps. Through this we could create a world that looked very ominous and dark toned using established tools that already exist. The occasional text popups give even more sense of character and purpose to the world, guiding the player to infer some things into the world that aren't there. I think our game was very expressive and was a very good thing to have.
(Evan) In retrospect, a lot more testing, especially with people who weren't me, would have been helpful. Near the end of when we were working on the game, I was testing how tough the monsters were, changing their health and damage to try to keep both a sense of challenge while not making the game impossible, and also trying to fiddle with the enemy AI and attack parameters to make them act more realistically. I think that by that time, I was sort of biased on the latter, as I had gotten fairly good at the game and would probably do better at it than someone trying it for the first time. As for the latter two points, had I had more time to play with them, some of the attack animations might have fit better with the damage they caused, and the enemies might have been somewhat more successful in attacking the player (instead of standing around most of the time). On the other hand, this would have made it necessary to test them more... user testing is a very circular process, it would seem.
(Calvin) I needed to put a lot more debugging related code into the game. Occasionally there were crashes (Not very often! But mysterious when they did occur...) And usually I would be baffled by what was going on. It would have been good to allow for a DEBUG constant which could be defined and then enable printing statements elsewhere. These tended to occur when the game didn't find models or textures or similar such things...
(Calvin) CVS. I need to say this because it's true. I know there aren't many better options out there, but when trying to work on a project where there are multiple directories and you're frequently adding, removing, renaming, and moving files from directory to directory, CVS will mutilate and mangle things until they are almost unusable. A number of problems occured regarding how textures would update in Evan's account, and in the beginning a significant problem occured when there was a maps folder and a bsp/maps folder, and everything that was in my maps folder was winding up in the bsp/maps folder in CVS. This caused us a great ammount of pain and greviance throughout the entire project.
(Evan) Personally, while I found the work I did do on this game more or less fine, I really would have changed how I worked on the project in general. Due to commitments in other classes, I was able to work on Oroborous much, much less than I wanted to - something I really do regret. The only thing that brightens me up on this a little is that I am fairly sure that in an industry setting this problem would be less likely to arise, as I probably wouldn't be trying to juggle five other main commitments with my job (as opposed to classes, which certainly do force you to do that). It's my opinion that had i had more time to focus on the game, there likely would have been a magic system (as originally intended) as well as several other intended features, like stat growth and multiple weapons. It's a good lesson, though: making a game takes a lot of time, and if you really want it to be good, you really need to focus on it.
Aside from that, I probably would have commented more, and make the multiple-attack-point-spheres improvement to hit detection that I noted in the technical challenges section earlier.
I also might have tried to get more and varied sounds for the player and the monsters - it might have been nice to have sound effects when things attack and get hit, for example. I suppose I'd generally try to devote more time to making the content prettier if I had to do this again.
(Calvin) I would say that the project was fantastically successful from my point of view. We now have an engine, and a game that works using it. There is not a lot that I think really needs to change at all. The game itself does not have all that much content, but it is a relatively trivial process of adding the content to it. What is important now is that there is a game engine which may be expanded easily and rapidly with comparatively little work.
************************************************************* *SPOILERS BELOW - READ IF YOU WANT THE EASTER EGG GIVEN AWAY* *************************************************************
Controls regarding to the Easter Egg (Strongbad's Dance Party):
Note that 'm' will not work from when it is pressed until when 'p' is pressed; none of the other controls listed here have an effect unless dance party mode is on.

Calvin Ashmore (coa@andrew.cmu.edu)
Evan Mandel (emandel@andrew.cmu.edu)
( Sunday, April 25, 2004 )
