Alright! The save game browser is now completely integrated over to the new wrapper library. I am definitely very pleased about this. It took a little bit of fancy hacking to get everything to work just right, but it's even saving out screencaps of the display when you save and then showing them all nice and scaled in the browser. Very, very cool. The best part about it is that the build worked in Windows with almost no code changes whatsoever. There were a few minor little one-liner changes, but that's nothing to write about. It turns out that all of my font changes worked well in Windows, too; something I hadn't tested until now after I made those initial changes.
This is a very big achievement. This means that all of the game has now been fully ported over to the new wrapper lib. Of course, I had to take out a few features such as the transitions between screens in the menu system -- but overall that was fancy eye-candy that wasn't really necessary for the game to operate like it should.
With the major porting done I think it's about time I go through and refactor a bunch of my code, again. While delving into the older portions of the code that weren't ported at all I noticed that a lot of it is actually completely undocumented. This is pretty bad. Thankfully, a lot of the code is straightforward with function and variable names that lend themselves to explaining what's going on. That isn't as helpful as a full set of comments, however. I plan on going through and beefing up the comments describing the really complex parts of code so that I don't stumble upon these issues some other time when I'm looking at the same code. The save game browser is definitely a big culprit in not being completely obvious how it works: it has some special features by which the exit option and the "save new game" option replace what would otherwise be a normal save game slot. This one got me more than once.
One big portion of the game I never ported to the new library goodness was the save game browser. This was already fairly broken to begin with, before the transition between libraries even started. Performing the actual port was painful, to say the least. Most of the problem stemmed from the fact that I had to, yet again, build more file system scrubbing functionality. I hadn't built in a function to simply return all files in a given directory, whether they are files or directories themselves. Before all of this, you could only query to find out if a specific file existed in a given directory structure. With that out of the way came the arduous task of figuring out just exactly what I was thinking when I coded this thing. This was one of the last chunks of code I put into the game before the major porting effort happened. Because of that, the save game browser code is almost entirely undocumented. This lead to major headaches.
The browser uses some fancy special values for things like exiting and selecting a new slot to be saved to. I had completely forgotten about this between the last time I looked at it and today, so let's just say that trying to actually use the browser was riddled with all sorts of problems. In addition to that, the browser made use of saving imagery out to disk and then loading it back up again. This is slightly problematic for me now because one can't necessarily load up any saved out image during runtime. Since I'm using OpenGL, all images need to be power-of-two dimensions, which can cause problems when you're trying to load a screen sized screen capture. Looks like I'll be writing out some "dump to image" functions that perform power-of-two padding.
In addition to all of that I tweaked out the already-existing options screen to work properly under the new system, and all is well. For the most part, today was a big game of catch-up. Now that I'm all caught up (almost), it's time to debug all the broken stuff from this initial port effort.
I also think I found a bug in my music playing code that can crash the game for what seems to be no apparent reason. So far it's not possible to reproduce it easily, so I'll just be playing it by ear until I can get some consistent crashes, or other people see it more frequently than I'd like.
I guess I need to shop a new demo around once I finish up a bit more grunt-work.
Stumbled upon a minor setback recently, but for the most part it's fixed. Since I'm using SDL + OpenGL for this project I've turned to FTGL for font rendering goodness. To cut a long story short, it turns out that I was completely braindead when I wrote some initial codework to do some generic gui elements. I failed to recognize the fact that all text in FTGL is drawn from the lower left corner (minus descender) onward. I'm used to systems that are able to draw text from what would appear to be its upper left corner. Needless to say, all of the Y-offets on my text were not correct. Previous to my fix, I was drawing text at a given position, and then subtracting off the text's height. Unfortunately for me, I was calculating the text's height by grabbing its bounding box from FTGL. This is unfortunate, as different text will have different heights, which is not what I wanted. I wanted to offset by the "universal" height.
For example, the strings "hello" and "jello" have completely different heights because of the descender due to the letter "j" in "jello". This means that if tried to draw these two lines of text along the same y position, the text displaying "jello" would appear lower than "hello", due to it's larger height. This was completely wrong. What I wanted to happen was that they'd be on the same y position, except the "j" in jello would hang lower than the rest of the text, as opposed to the entire word itself.
Many headaches, research, and testing later I realized that FTGL has some functions to let me approximate how tall text is without including the descender: by using the ascender. On a technical level, regarding fonts, I don't think using the ascender in this way is correct. From what I can tell, the ascender height is not the complete font height disregarding the descender -- but somehow in FTGL that's what it appears to be. In any case, using the ascender (taking away the descender) appears to render the text exactly where it should be. This required me to take into account descenders as opposed to full text height (dependent on string, as opposed to font) everywhere I was currently using fonts in my GUI system. It wasn't that much work, but it was a hassle enough to be annoying. Thankfully, there wasn't much of it.
This all came up as I was working diligently on converting my title and options menu system over to the new libraries. They need some more tweaking, but they are almost to the point where they were before. I just need something pseudo workable to allow configuration of keys as well as the gamepad. I also still need to reinstate the game saving and loading screens/browsers.
There's been a bit of a struggle in code land over the past few days. I discovered a pretty nasty problem with my implementation of ramps. Due to some shortcuts I was taking it was possible to eject yourself through what was almost a player's height worth of wall as if it were a ramp. This was clearly not my intention from the get go. Due to shortcuts I was taking, as well as assumptions I was making about what you could or could not walk up as a ramp, it was really difficult to fix the problem. Essentially, I had to undo my improper way of thinking about things and perform operations where they really should have been. Allow me to elaborate...
Initially, the ramp detection was built in such a way that when doing collision detection for falling through the ground, ramps would be taken into consideration and the player would be ejected upwards through them. In order to solve a few problems with being able to seamlessly move from ramps to solid tiles, there were a bit of hacks that made this approach result in the problems just described. I spent a good half of a day trying to punch this one out. Immediately as I stopped for the day I realized that I was really just going about things in the wrong way.
By implementing ejection code in the actual bounds checking code, I changed the fundamental operation of my bounds checking, causing all sorts of strange and terrible things to happen (like ejecting yourself out of the map if you jump into walls too much). So, long story short, I gutted out that code and turned it into its own function that is called only when applicable (moving left/right). In no other circumstance does it make any sense to be auto ejected upward when dealing with ramps.
In addition to ramps I also threw in some double jumping as well as wall jumping. The wall jumping took a great deal of time to get just right. My initial implementation was rudimentary at best, and didn't feature any sophistication in terms of usable controls. After that initial version I came back and refined the controls a bit, adding in some "nice to haves." Now, when going to perform a wall jump, if the user presses away from the wall the player will "stick" to it for a short amount of time before moving away. This gives the player a short timeframe within which they can press the jump button to perform the wall jump. Previously it had to be done immediately or else it would result in an instant miss. Additionally, when the wall jump is actually performed, before "kicking" away from the wall, the player will "hang" on the wall for 1 or 2 frames of animation, and then quickly dash off into the opposite direction. This helps give you a bit of bearings and makes it easier to chain wall jumps together.
All of this work spanned a bunch of days in a row. In retrospect, I really wish I posted about it as it was going on. That said, I was really in the zone while drilling out these functionalities in the game, so posting was the last thing on my mind. This was the first time in a long while I gave up some sleep for the sake of trying to smash down some bugs.
I've been doing a bunch of various different things with the game lately. For one, I have been procrastinating a bit in getting back to sketching game characters and creatures. I mostly have been putting it off because I simply didn't have a decent sketch-pad in which to draw. Now that I've squared that away, hopefully I'll be sketching more frequently and I can really pin down the designs of most of the races in the game world.
On the coding front I've been doing a bit more cleanup, as well as implementing much-needed features in my SDL-wrapping library. For starters, I needed to duplicate the ability to take a screenshot of what's currently happening in the game for the purposes of my save game visualization. It was less than straightforward; since I'm using OpenGL I had to jump through hoops (really, just a single function call) to pull out the current buffer contents and then pass them off to SDL so that they could be written to disk. The more complex part, and the one I haven't started yet, is reloading those back for viewing. While I can save out images in any dimension I want, loading them back in is restricted by power-of-two goodness.
Aside from dealing with image dump and reload, I took an initial stab at supporting gamepads. Of course, SDL has all the fancy hooks to read off of any gamepads that are connected to the system. The actual work I did was integrating it into the game engine such that you could use either the keyboard or the gamepad. There's still a ways to go, however. Currently all the button configuration is hard-coded. Once I start rebuilding the title and options screens I'll need to build in functionality to probe for devices and set which buttons you want to use for what, accordingly. This also includes stuff like handling any analog sticks. I'm still debating on whether there's even any point to that, or not, as for this type of game the D-Pad will suffice plenty. For now, I'm simply enjoying being able to move around and jump using a gamepad. All that polish stuff can come later when I buff out the rough edges.
It's about time I finally dug into those ramp tiles. I had been putting it off for a while, but I suddenly decided I'd take a crack at it and here we are. There are still a few things I could add to it, but overall it's working quite well.
In order to implement ramp tiles I needed the ability to query the alpha value of a given pixel in a texture. My method of ramp tiles uses a pixel-perfect detection method to allow for any shape of ramp (as opposed to only simply 45 degree slopes). It took me a little while to finally wrap my head around what was required to do this on a texture in OpenGL, but eventually I made my way through the unfamiliar territory. One detriment to pulling out pixel values for the RGBA components from a GL texture that is already pushed onto the card is that it's slow. Trying to do that several hundreds of times in a logic loop is a bad idea. To prevent major slowdown when querying pixel information I implemented a caching system where you can decide whether you want to retain the pixel buffer data for future use or not. If you retain the data, get pixel operation speeds are increased dramatically. The pixel getting function is all smart and figures out whether you've already done a pixel operation and retained the data or not and will choose the fastest operation possible if one exists according to how it was invoked (retain vs no retain).
With pixel-getting capabilities in the bag, ramped tiles were finally a go. The core functionality was easy to pin down. The real problem areas were the edge cases, like ramps that meet with full solid tiles and making the animation look smooth when walking down a ramp. A classic problem regarding ramped tiles that aren't specialized in terms of angle is that when the character moves "down" the ramp they move outward a little bit, and then when there are a void below them they begin their "falling" animation and fall into the space. Rinse and repeat as you go down the ramp. In reality, it should look very smooth and as the player walks down the ramp the animation is "stuck" to the walking/running animation, yet the player still moves in y position without triggering the falling status. My method for working around this is pretty simple: if you're not directly blocked, not falling, and are physically blocked half a tile's worth of space below you, don't perform the falling operation and instead just move downward.
As for deficiencies: any ramp tile that has a solid column of pixels that are the height/width of a tile are impassable at that point. This sort of makes sense, as if you have a solid ramp tile you do not want to be able to just "fly" right through it. This means that if you want a ramp to gradually raise up to a normal tile height, it must be drawn in such a way that it actually raises up to the tile height minus one, or else it will be impassable. This means that if a ramp should raise up to a normal tile hight and actually connect as one large ramp, the ramp tile must be adjacent to a normal "non ramp" tile.
Throughout this whole explanation of the ramp tiles I realize I made it seem like ramps and regular tiles are treated differently from one another. On the contrary, regular tiles are simply a special type of ramp: and entire solid ramp that completely blocks the player. The tiles are treated the same and there isn't special code to tell whether you're on a rampified tile or not. The code simply checks where the player should be according to while tiles are surrounding them and moves him/her accordingly.