Saturday, February 27, 2010

The joys of software activation. (/Sarcasm)

(This post has nothing to do directly with magecrawl nor programming. Blogger doesn't apparently like pseudo-xml tags in the title or the post).

So this morning I woke up hoping to get some work done on magecrawl before I had to leave for Houston for the weekend. I log into my machine to find the background is black and a popup telling me my copy of Windows isn't "genuine". I find this amusing, since I have MSDN at work and am pretty blasted sure I used one of those images for this machine. It's the machine I use to VPN into work when I work from home.

Well, I so find the spot on "My Computer"'s properties to ask it to try to validate again, and it fails. Alright, I'm not sure why it's failing, I'll go grab another valid product key off MSDN and try again. Which I proceed to do, still doesn't validate. Sigh, so I kickoff another download of the 4 gigabyte Window ISO image off of MSDN.

So on Sunday when I return, instead of programming I get the fun of reinstalling Windows and all my applications. Less hacking on magecrawl time for me.

(Rant)

I understand the concept that Microsoft is a business and it needs to make money to keep paying people to improve things. I didn't pirate Windows, I have a blasted valid product key. Well, up to of them 10 it seems. What is frustrating is that me, one who didn't steal anything, is being called everything but a thief.

I love C#, I think it is the best programming technology out there right now. Using visual studio when programming C# is so easy that I don't even reach for gvim most of the time. But now, because Microsoft decided to audit my machine overnight without my permission, I get a black background, an annoying popup, and an afternoon of work, even though I didn't do anything wrong. If I would have used a pirated version of Windows, I bet I wouldn't be having this problem now.

I'm in the laptop market sometime this year, and I was deciding between a new Dell an a new Macbook Pro. Getting a Macbook Pro and installing Mono Develop on it is starting to sound very good right about now. They might cost more, but I hear they never call your a thief.

(/Rant)

Thursday, February 25, 2010

Review: Dance of Death

As is every so often when I come across a game that looks interesting, this is a review of "Dance of Death", a flash game that was mentioned on RGRD. It's a "tech demo" at this point. As a flash game, it's very difficult to have issues getting the game to run, point your browser at the link and go.

The first thing one comes across when they start the game is in the title screen, and then the character creation screen. I like the tabbed panels with shading, which makes it somewhat obvious when one is done. One issue I came across is that one can tab over a panel without spending its points and type a name to finish. Doing this will create a massively underpowered character, and being able to do so is most likely a bug.

Since this game follows the standard practice of asking for name/race/stats/skills before the game starts, please don't consider it a criticism of it in particular. Games that do this ask the new player to answer questions they most likely don't have answers to yet. If I've never played before, how do I know what stat distribution I should use. Without playing the game, I have no idea of the usefulness of various skills, and so figuring out a skill distribution is difficult. I'm planning on the future of posting an entry entry on this point, so I digress.

Beyond that, the character creation does a decent job at explaining what skills do as I distributed stat and skill points, so I created a melee fighter.

The game has the feel of nethack in terms of room layout and color scheme (mostly white on black). Starting off on floor 0 was a bit humorous, as all real programmers number arrays that way (but maybe it should be floor 1). Controls seemed easy and expected. There was no diagonal movement, bump to attack was obvious and expected. Bump to open doors also worked as expected.

Field of view, monsters with pathfinding, and equipment all are there. Each "slot" of equipment seems to have a single item type, and I'm unsure it does anything at this point. It seems that inventory "Examine" is broken, but beside that everything works as expected.

The map generator seems to generate reasonable rooms with doors and stairs. Monsters were a bit sparse for my taste. 

There is a "Skills" pane that show your skills and attributes, along with an exp pool. It appears to be just stubbed out for now, and shows some of my stats as negative, which is amusing. 

For the first "tech demo" of this game that I've heard of, consider me pretty impressed. Development can be followed at the development blog here. I'm looking forward for further development, fleshing out what this game has to offer beyond infrastructure.

Wednesday, February 24, 2010

It's amazing I wrote some of this old code...and yet I don't regret it.

Another of the mini-goals of this iteration was to provide a "Welcome/Title" screen to magecrawl, allowing the player a place to enter a character name and choose between save files. As I might have mentioned before, this "attempt" at Magecrawl is not my first. I've written parts of my game before in C/C++/python before a settling on C#. My wonderful wife has heard more than one time about me throwing everything away and starting again (or wanting to). I had the title/welcome screen from a previous attempt laying around in an old svn account, so I figured I'd salvage that as a good place to start.

My goodness, if I didn't know for sure I was the author, I'd want to disown this code. Ignoring the horrible style of capitalization and spacing I must have once liked, I've found multiple WTF moments. See this. One involved a corner cases with the color scheme that sometimes overflows into an exception. One was a half a dozen lines of code trying to reinvent Path.GetFileNameWithoutExtension(), and doing it poorly. The other memorable one was me declaring an enum to record what menu item we were pointing to, then ignoring it in a half a dozen places and comparing directly against (0,1,2). I hadn't given the enum a numerical set of values, so it just "happened" to work.

There is a proverb in Go that says, "Lose your first 50 Games as quickly as possible". The idea is that in the beginning, you don't know what you are doing, so don't overthink your moves. After 50 games, you can begin to see what looks like a good move and why people play as they do.

I'm starting to think the same idea might be valid for programming a roguelike as well. Experience has taught me, and while I wouldn't suggest throwing all your work away over and over again (like I did), just getting out there and coding will make you a better coder. While I haven't entered a 7 Day Roguelike context myself, it sounds like something I'd suggest and should do myself in the future.

Sunday, February 21, 2010

Lightning Bolts, Fireballs, and Blasts of Winter - New spells types

This weekend I did get a day to hack on Magecrawl some more, this time with user visible features. I added new spell types and targeting feedback to help you aim and you cast primal arcane might upon your foes.

The first type of spell isn't technically new. Lightning bolts (or any type of possibly bouncing line blasts) were in the tech demo. However, the aiming "halo" makes aiming them much easier.


"Cones" are easier seen than explained:


And what self respecting mage wouldn't have a fireball spell up their sleeve.

Thursday, February 18, 2010

The evils of duplication and DRY...

For those who've taken formal software engineering education or have programmed as a career, this post might something you've already heard of. However, I found myself coming back to this principle again while working on some code, and I figured it deserved a post nevertheless.

DRY: "Don't repeat yourself". Originally formulated in Pragmatic Programmer (a great book I read as an intern), this guideline for software development is simple and to the point. The idea is that you should never have two places that contain duplicate representations of any given fact.

The most obvious implication of this is that you shouldn't copy code. If two places need a given chunk of functionality, abstract it to a base class, static method, helper class, etc. At work, I've heard this copying known as the "editor inheritance" pattern, the "editor" being your text editor.

Even if you don't copy paste your way into this situation, it is easy to run into in other ways. In magecrawl, the two ways to invoke a spell-like affect is via the "Cast Spell" screen and selecting an inventory item and selecting the use verb. I found that keyboard handler for both these screens were 99% identical. In the beginning, spells and spell like affects had no targeting possibilities, they always targeted the player. As I added targeting, I added it to both places not realizing the duplication. This became a problem when I wanted to add a new targeting type later on and nearly missed one of the two copies.

I find that when I find these areas, the best thing to do is address it either "right now" if your in development, or as soon as the next release occurs if your stabilizing for a release. I shelve whatever changes I was working on, and figure out how to remove the duplication. In my case above, I created a base class that contained the common code and had both classes inherit it. I then tested it thoroughly and submitted it before working on new features. Your refactoring hat and your new feature hat shouldn't be on at the same time, you look silly when you try. :)

The more advanced implications of DRY involve not repeating yourself in more than just near identical code. The book mentioned above handles some of those cases, and maybe I'll come back to them in some future post (when it isn't nearly midnight as well).

Taking damage, how to represent health and armor in Magecrawl

Since I've got some downtime tonight and don't have access to my programming computer, I figured it'd be a good time to write down my thoughts on health and armor. But before that, I must ask that you read this:

TVTropes - Critical Existance Failure

This is one of the things I'd like to not do. Nearly every roguelike has a system that boils down to this. In Crawl and Nethack, and nearly every game in between, there is no difference between 100/100hp and 1/100hp. While convenient in its simplicity, it bugs me. On the other extreme end are games that track damage to every single limb, with slow healing that while makes the game "tatical", it also makes many unplayable in my opinion.

Beyond the "one hp issue", there are other issues that need working out. Another is the randomness of combat in current Magecrawl. Armor has a chance to reduce the damage taken, and when added to the evade percentage, the player can go many rounds without taking damage and then suddenly loose half their hitpoints in a round to a "lucky" double strike.

I haven't fleshed out all the repercussions of this idea, but here's what I'm thinking. The health of the player is divided into three parts over two bars:
  • Health - The lower end of the first bar. These are hitpoints that don't naturally regenerate over time. Only magical healing or long rest could heal these. They represent actual wounds, broken bones, and the such.
  • Stamina - The upper end of the first bar. These are the hitpoints that regenerate outside of battle. The outside of battle part is to prevent Pillar Dancing (which beyond Elbereth is one of the cheesiest tactics in my opinion). Better armor will increase this amount (almost with some type of damage reduction and/or increased MP cost for spells). These represent ones ability to stay out of true mortal harm for some part of combat.
  • Shields (for lack of a better name) - This would be the second bar, only shown when you have an affect that provides it. In most RPGs, spells like "Mage Armor" act the same as regular armor. I like the idea of it being forcing back damage due to your arcane will alone. When a spell that provides protection is active, this bar would be populated with a set of temporary hitpoints that take (some? all?) of the damage before stamina/health is taken from. It's length would be based upon spell proficiency, intelligence/willpower. I like the idea of it sapping from your mp during and after battle to recharge itself, giving it different flavor than stamina. I'm not sure how to get around Pillar Dancing if I do that however, unless it only does that when you don't move.
Beyond this, there has to be some penalty to taking some damage, beyond chance of death. I like the idea of stamina damage either slowing down your CT (longer time before turns), increasing MP cost of spells, or both. Health damage should be more crippling, such as slower movement, penalty to MP regeneration, and such.

I'm trying to balance this with keeping the game fun. I'm hoping in this iteration to get some implementation of this system in and see how it plays. Any thoughts?

Getting bogged down a bit...

This week I've been getting bogged down and not finding much time to work on magecrawl. Some reasons for that include:
  • Dragon Age - which for the linear story-driven RPG genre is an amazing game. 
  • Winter Olympics
  • Plant vs Zombie - humorous and well polished tower defense game.
  • Illness, my wife wasn't feeling well this last week and I've been fighting a bug as well.
Beyond that, it looks like the next bits I'll work on in magecrawl will include more fleshing out of magic and equipment. The magic work will add "Fireball" like exploding spells, "Cone of Cold" like spells, and the like. In equipment, I'm looking to handle hp in a more unique way, add shields, and one/two handed weapons.

Sunday, February 14, 2010

Frustration with "stitch" map generator...

As I continue work on iteration 8 when I get a chance, I'm becoming somewhat frustrated with my "stitch" map generator. To understand my frustration, here is the 10000ft view of how it works.

A graph of "nodes" is created, using probabilities from a map. That graph is dropped one node at a time onto a large "map", with the nodes next to each other in the graph next to each other. "Dropping" a node involved looking up one of multiple templates from a text file of that type, and placing it so the seams line up. If a node can't be dropped, we add it to a list of nodes we can drop later if we run into a room that doesn't have an output assigned. There is come complicated logic to prevent us from dropping a long group of halls and then being unable to place the endpoint room from leaving the hall. Once we have the graph generated into rooms, the map is "trimmed" so it isn't larger than it needs to be, checked for connectivity, and in/out stairs are placed.

The flaw in the system that keeps biting me over and over again is the separation between the "graph" and the actual map. It makes some things that you'd think were easy, such as dropping doors between rooms difficult. The latest thing I was trying to fix was the fact that the map looked like a spreading tree (since it was). Once you went down a hallway, you'd never reach any of the rooms in any of the other hallways. My idea was to patch this by finding empty "seams" in rooms and trying to draw hallways between them if possible. After burning an hour, I'm frustrated since by the time I've reached the hallway placement stage, I've lost all the information on the map structure and which nodes I'm working on.

I've booted the feature to iteration 9, and in that I'll decide if I want to refactor this map generation to something easier to work with or rewrite it new. Does anyone have any references to map generators that produce great looking maps with hallways, rooms, and such?

Tuesday, February 9, 2010

Targetting, bump to attack, and player's preferences...

Some of the feedback I received during the last tech demo includes a few complaints that when they'd hit 'a' to attack, the screen centering on the first available target was disorienting. While I personally didn't have this issue (or else I'd have coded it differently), I could see where they were coming from. Since I'm handling "low hanging fruit" early in this iteration (or procrastinating, depending on your point of view), this seemed like an easy thing to handle.

I've covered this issue with two different fixes. The first being the inclusion of "bump to attack". This is a common feature in roguelikes, where moving into the same space as an enemy invokes a melee attack. I personally don't care for it. I've lost more than one character in games without "run in direction" when I held down an arrow key for a half a second too long and melee'd myself into oblivion.

Magecrawl also has weapons with "reach" such as the spears, and bump to attack can prevent people from noticing their longer reach. In addition, some weapons either can't attack certain directions, or attack at lower strength; the attack key shows overlays making both these facts obvious, while bump to attack doesn't. Despite this, it is a convenient option, so I added it as a preference one can turn on.

The second fix is a preference to disable all "auto targeting". When you request an action that requires targeting, magecrawl will try to pick an intelligent initial targeting position. This preference just skips all this code, and all targetting starts at the player's position.

I'd like to spend a minute talking about preferences. Magecrawl isn't even a full game yet, and in addition to keyboard settings for every key, magecrawl has more than 15 preferences so far. A few are debugging settings, but most are documented in Preferences.xml. Each of these brings different behavior to magecrawl in some manner, which in theory requires additional testing and maintenance. The trick is to find a balance of providing great defaults, so people don't have to touch the preferences, and enough knobs that people can change the settings that are important to them.

Monday, February 8, 2010

libtcod-net 1.5.0rc1-2 released (fixes reported TCODRandom issue)

The issue reported here has been debugged and fixed. The short answer for those not interested in the details is that I was using an API in libtcod wrong and that API was doing the wrong thing and corrupting its internal state. Get the newest libtcod-net here.

For those who are more technically minded, here's the story.

In between libtcod 1.5.0b2 and 1.5.0rc1, the C API changed for creating TCOD_Random's. Previously, one could call TCOD_random_new(), however in this release they added two different RNG implementations. This function now take a paramater. The way to get the "default" RNG is to call TCOD_random_get_instance, which I did happily. I also added a new TCODRandom constructor that took the enum for those who cared, but that's beside the point.

This worked, and seemed to be all I needed to do to fix libcod-net TCODRandom for the new API. However, there was an underlying issue. Sometimes in magecrawl, monsters and players would get into long stretchs (4000+ turns) of continual misses.

After some detective work, the issue was found. TCODRandom, like all libtcod-net wrappers that handle unmanaged resources, implement IDisposable. The idea is that you, or the runtime, will call Dispose() on it to free the allocated memory. The issue is that the new api had a note that you weren't supposed to call TCOD_random_delete on the default RNG. When you did this, you free'ed the memory for the global instance, but not null it out. Due to the implementation of the RNG, it'd happily use the garbage memory giving random answer most of the time. Some times however, some of the garbage pointed to a segment of zero'ed out memory, and I'd see the long stretch of zero's.

The solution was twofold. I updated libtcod-net to follow the API's rules and not delete the default RNG if that was how we implemented it. jice updated libtcod to not internally corrupt itself if you happened to do this. The joys of maintaining libtcod-net is that sometimes you get to track down memory corruption issues, even if you're written in c#. That is the fun of interfacing with unmanaged code.

Sunday, February 7, 2010

libtcod-net 1.5.0rc1 seems to have an issue with TCODRandom, beware.

Update: See this for a solution.

I've spent half of today debugging this issue. It appears that there is an issue I'm hitting in magecrawl where a TCODRandom gets stuck in a state where it returns 0's for every GetRandomInt call. I'm working with the maintainers of libtcod, but it appears the issue is one underlying libtcod itself.


The workaround is to use the constructor that takes an enum and pass is the MersenneTwister value.


I'll update more when we find out more. I just didn't want anyone else to try to debug into their code for a few hours looking for sensor ghosts.

libtcod-net 1.5.0rc1 released!

libtcod-net 1.5.0rc1 has been released (same day as base library libtcod itself may I add :) ).

The major changes include TCODPathFinding being removed. It's been split up into TCODAStrPathFinding and TCODDijkstraPathFinding. This is obviously an API break, but one that can be replaced with a simple find a replace. Beyond that:
  • TCODSystem::GetCurrentFontSize
  • New random number generator type and new default (ComplementaryMultiplyWithCarry)
    • New functions - GetGaussianFloat, GetGaussianInt, Save
    • Removed functions - GetIntFromByteArray
  • ResetCreditsAnimation, which fixes an outstanding bug in Magecrawl.
Get it while it's hot. Thanks jice and team for another awesome release to build upon.

http://code.google.com/p/libtcod-net/

Saturday, February 6, 2010

Tooltip Descriptions

In the last few days, I've been knocking out a lot of the "nice to have" or refactoring issues that have appeared due to tech demo II. Some of these include:
  • On "stitch" maps, doors now are placed in sane positions.
  • Treasure chests now add items to your inventory when opened.
  • Casting an enchantments multiple times no longer stacks them, just extends duration.
  • "Tooltips" on view mode to describe the currently selected cell.

Here's an example of the new "tooltip"

Wednesday, February 3, 2010

Multiple Keybinding Presets...

The single largest source of comments on the tech demo II had to do with setting up keystroke bindings. Although I had setup what I through was a good default and provided a way to change it, people did not think this was adequate. It seems that other roguelike players are very particular about their keyboard settings.

To address this, I've split the key bindings into two sections. The first section, KeyMappings.xml, will still contain most of the keystrokes. The second section contains the settings for the 8 cardinal direction, with three sets of defaults already picked out. Preferences.xml contains a preference to choose between these default. If you don't like any of them, you can choose "Custom" and provide the location of your own xml file.

The three defaults so far are:
  • Arrows (and insert/delete/home/end)
  • Keypad
  • VIM
If anyone has a good suggestion for either different common categories or more specifically a good laptop set, feel free to provide it in the comments.

Tuesday, February 2, 2010

Crossplatform issues with MEF Preview 8

While far from perfect, Magecrawl has a decent amount of modularity. The GameEngine with all map generation and the details of the running world have a hard boundary between it and the GUI. However, I feel that the GameEngine has gotten a bit too big and I'd like to split out of parts of it into separate components.

One of my first tasks this release was to investigate MEF. MEF, to put it shortly, let's you part a part of your class as [Import] and then say, "Go find a library in this directory which implements IGameEngine, load it, and fix up this variable." I implemented the first baby steps of using it late night and things were going well. That was, until I tested in under Linux.

MEF preview 8 as it stands does not work with mono/Linux. I'm not talking about a single specific issue that my code happens to trip, the built in examples don't even work. Examples bugs can be found here and here. One of them mentions this as won't be fixed until after the end of March at the earliest (post .Net 4 release). This leaves me with the unhappy situation of either:
  • Use MEF, and for at least the next two month have no chance of having a Linux version of Magecrawl
  • Don't use MEF, and either use another IoCish framework or wait until MEF works.
The only reasonable choice is the latter, punt on using MEF until the it can at least pretend to care about Linux/mono enough to get its built in examples working.

As a side note, this is why I test under Linux before each tech demo and whenever I'm about to make a large architectural change. If your writing something on one platform and expect it to work elsewhere, even if the library/language you're using claims to work there, testing early and often is a good idea.