Monday, November 21, 2011

Refactor Bonanza

Pride Goes Before a Fall

Those Twitter-enabled readers might remember me bragging about resisting the urge to refactor a system a while ago. Well, turns out hubris is still alive and well. Several days after my proclaimed victory, a use case failed, and four hours of tracing spaghetti code later, I was still no closer to fixing it.

That system, of course, was the inventory system. And the use case was stacking items. Several months of iterative development combined with changing requirements led to a system fraught with confusing methods. Methods with names like "TryFitItem," "CheckFitItem," and "ItemFitsInCapBox." Can you tell what each of those does differently? I can't. And I wrote them!

It turns out those functions were not only confusing to work with, but wasteful of the CPU as well. They did a lot of equipping and unequipping of items, shuffling them around in different containers, all in an effort to see where they fit best. And while all this was going on, the buffs/debuffs for the items were stacking and unstacking with each check. Then, when this rigmarole was over, all I got back was a boolean. Or in some cases, not even that explicit (e.g. a point that might be null). What does this boolean mean? Did the object fit where it was? Or did it stack? Or maybe the user wanted to swap it with the item underneath it?

I decided to refactor. It took several days (~2.5), but it's a much cleaner system now. The methods are more compartmentalized, so each has a clearer function. They do less actual moving of items, and more hypothetical testing of new positions (saving processing time). They do things in a more consistent way. And most of all, they return more contextual information in the result.

New Resolution

I also changed the game resolution from 1280x720 to 800x600. This change resulted from a performance test, but is meant to address a business issue. Originally, the game was done at 720p, with assets all scaled 2x for the retro "fat pixel" look. I figured I could get good screen coverage on a modern widescreen at 720p (even accommodate TV setups and eventual ports to HD consoles, if need be). And for portals that wouldn't allow that size, the 1x size would surely fit (640x360).

However, the 2x scaling takes its toll on some machines. And, as it turns out, nearly every Flash portal out there would need the small version, as 1280 is way too large. However, finding actual numbers for maximum resolution turned out to be difficult. In most cases, sites didn't list limits on their FAQs at all. For those interested, here are the best numbers I could find (or guess, based on existing games' widths or the site's frame size):


It was pretty clear the 720p version wasn't going to fly, even retooling the art to be scaled 2x to improve performance. So I started exploring resolutions compatible with the above, and settled on 800x600. Why 800x600? Mainly because the sponsors I've heard the best things about have that as an upper limit.

There's no way I'd fit everything into this new, smaller size of 800x600 though. Not unless I ditched the 2x scaled look, anyway. So after much hemming and hawing, I decided to bite the bullet and live with 1x scaling. It actually gives me more space to work with than 720p at 2x scaling, which alleviated some GUI issues I previously had. Here's a sneak peek:

Maybe 800x600 is more retro after all.
I did keep the encounter image at 2x size, just because there was room for it, and it seemed a shame to hide a full encounter illustration up in the corner. Normally, I'd rail against inconsistent graphics standards in a game, but in this case, it's the lesser evil.

New GUI!

You might also have noticed some new GUI elements. The status bars now have some snazzy overlays to make them more attractive and "finished" looking. Similarly, the message window was expanded to the same height as the attack mode, and shares its style.

Giving those elements some polish really made a difference, and I can't wait to follow-through with the rest of the GUI. But not until the layout is more stable. Still a few more days of work to go before the resolution shake-up settles down.

Everything's smaller now. Hopefully not too small.

New Font!

I also use a new font in this version. "What's wrong with the old font," you ask? I rather liked it too, but I was unsure as to its use license. After some time looking around for free to use fonts, I stumbled across FontStruct.com. It's an online font creation utility that allows you to draw, letter-by-letter, your own fonts, and then download them as ttfs. And, as it turns out, it doesn't take that long to do. In about 30 minutes, I had a custom font hooked up to my game.

I may still tinker with it at a later date. It's legible, and looks fine, but it's not very unique. Custom fonts can often lend a memorable character to a game. But for now, at least I'm not violating any copyrights or licenses.

Roll your own fonts in 30 minutes!

Creature AI

I also put some more work into creature AI. Creatures now use the visibility methods I worked on previously. And this means they can head towards distant objects (player, enemies, loot, and tracks). I've also started working on AI for choosing the best weapon, though that still produces some inventory bugs. When I'm done, though, hopefully I should get the occasional NPC rifle shot from afar.

Monday, November 7, 2011

There Goes the Neighbor- I Mean, Neighbourhood

Permanent Residency

Good news, everyone! I received a letter from CIC, and it appears they've made a decision! The letter was worded as dodgily as possible so as not to tip me off as to what that decision was, but indications point towards acceptance. I have a 1-hour appointment in a few weeks, and I am required to bring my documents and two government-approved photos. Trying not to get my hopes up too high, but after 11 months of waiting in the dark, it's good to have forward movement!

What does this mean for NEO Scavenger? Well, the short version is that with permanent residency, I can officially earn money. In a couple months, I should be able to incorporate, and license/sell NEO Scavenger without violating my visa.

The down side? Shiz just got real. I was sort of comfortably hiding behind my visa issues as an excuse to keep noodling with the game. After all, I couldn't do anything with it even if I finished it. But now, that excuse has an expiration date, and it looks like mid-January. So time to hunker down, take stock, and get this thing ready for market.

Audio Work

Almost as if I subconsciously knew time was running out, I decided to tackle audio in NEO Scavenger two weeks ago. I figured that was an area that had been neglected for too long, and probably one of the biggest missing features in the game, so I went at it.

As expected, audio is hard work! I managed to get mp3s playing in Flash/Flixel without too much trouble, but getting them to play and sound right is where things got difficult. As it turns out, mp3s don't loop well. For reasons I won't go into here, mp3s always have a silent gap at the beginning. And no amount of my workarounds were, ahem, working. I even went so far as sampling an mp3 stream's bits in real time to artificially cue past the gap each loop. Still an audible gap on loops.

The solution? Oddly enough, use Flash's IDE. I had been building everything in FlashDevelop, so I hadn't been using the IDE. Flash CS3/4/X does this thing where it bundles exported mp3s with information on the gap length at the beginning of the file, for seamless looping. Simply include the mp3s in an swc file at compile time, and voila! Seamless looping mp3s!

However, this meant one big change in the way I was loading assets...

Runtime Content and Piracy

Almost everything in the game, apart from the engine itself, is loaded dynamically at run time. I load images, game variables, the map, item definitions...you name it. "Why on Earth would you do that," you ask? Good question.

Originally, I thought I would be clever and avoid piracy issues this way. I had read Andy Moore's accounts of piracy and blacklisting, and I wanted to avoid the piracy issues he ran into. (Note that "piracy" here isn't the player getting the game without paying, since it's free anyway, but rather professional pirates decompiling the game, rebranding it as their own, and selling it to Flash portals for profit.)

I figured I could cleverly have my game ask my website for its guts on start-up. If the game was at a legit site, the game would get its guts. If not, or if the game was at a portal that blocked outgoing links, the game would probably default to demo content. Content which would only serve to whet one's appetite for the real deal. It was meant to be a sort of remote kill-switch, which would allow me to deny access if sites abused the game somehow (e.g. rebranded it without permission, or blocked links back to my site).

It sounded clever at the time. But there are some major drawbacks:

  1. Portals Like Single-File Games: Having a sprawl of files and web connections for a single game is a no-no in Flash game licensing. Particularly if the sponsor wants the game to be picked up by other portals, which multi-file games make difficult.
  2. Hosting Content: Having every instance of the game in the world ask my website for guts is a huge bandwidth and uptime concern. I could find a more reliable place to host the content, but even then, no portal owner wants to rely on a game that dies if I decide to stop hosting the files.
  3. Hurting Players: There's always the chance DRM hurts the player, and this would've been no exception. What if a problem causes the player's game to fail? And even working copies are going to have to rely on a centralized host to download data from, which could mean sluggish game loads for many users.
  4. Hypocrisy: I even promised myself I wouldn't use DRM. "Hello, I'm Dan from April 2011, remember me?"

So it seems pretty clear, the way forward: simplify. Just go with the game setup everyone else uses, to make accessibility as high as possible. Expect piracy, and try to use it to my advantage. I don't know what that means, yet. But it sure does sound good!

Meanwhile, Back In Actual Game Development

Notice anything new?

Attack modes!
That's right, new attack mode UI! I've changed the way attacks work in NEO Scavenger. Previously, anything you had equipped in an appropriate slot granted combat bonuses. This worked well enough when the only three weapons were bare hands, meat cleavers, and wrenches. Equipping two weapons simultaneously meant double bonuses. But that was ok, because we all have fantasies of marauding bandits in the wastelands with dual-wielded meat cleavers, don't we?

The problem came when I started adding ranged weapons. Suddenly, having a ranged weapon meant your bonuses from any other equipped melee weapon came along for the ride. While dual-wielding a hunting rifle and long-range throwing-cleavers sounds cool, it's sort of not what I intended.

Hence, attack modes. Now, every equipped weapon (when in the correct slot) grants one or more attack modes. They work kinda like Fallout or Silent Storm, in that you can see and change which attack mode is active via the graphic on the bottom of the screen.

The trusty old .308.
I also changed the UI a bit to accommodate the new attack modes. The message window is now thicker. It's an improvement in that the player can now see a bit more message history than before, but the down side is that it had to be hidden in the inventory screen to avoid obscuring important elements there. The sleep button was moved to the right, as it is an action and not a UI screen like the others. And I finally added a "Wait" action, for passing a turn without moving. I'm still on the fence about whether to use Rogue-like or Civ-like movements/turn, but for a Rogue-like method, a "wait" option is a necessity.

I also rewrote the visibility code to be faster and more reliable. You can especially see it in the first image, above. The player's line of sight gets blocked by certain hex types, even when on an elevated tile like the hill. What I had before worked, but wasted a lot of CPU on redundant hex calculations, and still made a few errors.

Since I wanted creatures to use ranged weapons, I needed visibility calculations to be cheaper. Otherwise, the AI would take too much time during a turn. And without visibility checks, creatures might attack the player from unfair locations (e.g. through a hill).

And since creatures and players were now sharing so much code, I finally took the plunge and made them both derive from a common class. (Yes, I know, they should've from the start. Bad Dan!) As you can see in the above two screenshots, now everybody knows when Dogman is comfortable. (Still working on message filtering)

One-Click Inventory

One last big change is to the inventory system. (Yes, again.) Enough testers have complained about dragging items around that I finally made auto-moving the default click behavior. Anything you so much as touch with a mouse click or drag pops automatically into an appropriate slot, if possible. Ground items go to inventory. Inventory goes to ground. Available skills go to player skills, and vice versa.

Everything you see here took one mouse click to equip/pick-up.
If the player wants to be more specific about where an item goes, they now must shift+drag. This is also true if the player wants to Tetris-rotate an item to fit.

In hindsight, it makes more sense this way. 90% of the time, users are picking up items and dropping them, and this saves the user the trouble of tedious clicking or dragging.

Next up: figuring out how to handle ammo, and making encounters and skills more intuitive. I should probably do another content pass soon, too. The skeletal story encounters need some clean-up and fleshing-out to be worth their weight.