Sunday, 31 May 2009

In praise of libraries

I've just spent the last day and a half avoiding the fact I need to implement a tree view in Unangband.

Let me explain a little. I had planned to spend this weekend wringing out the last few show stopping bugs from the 0.6.3 Unangband release. But I made the mistake of reading this thread on angband.oook.cz discussing the monster list view command ('['). Big Al, one of the contributors to the Unangband code base has ported this useful little command from Angband - the source of the discussion - to allow you to see a list of visible monsters at any time.

So I got to improving it, based on some of the suggestions in the thread. Screenshot below (it's too easy to paste html screen dumps directly into the blog).

You are aware of 16 monsters: (by distance)      ##%xxxxxxxxxxxxxxxx{##########
4 Wild dogs ('C')/('C')x ####xxxxxxxxxxxxxxxx##########
White snake ('J')/('J')x ###%xxxxxxxxxxxxxxxx##########
2 Giant white centipedes ('c')/('c')# ####x%####%'%#################
2 Rock lizards ('R')/('R')# ####x######x##################
Cave lizard ('R')/('R')x ####x######x##################
Grey mold ('m')/('m')# ####x######x##################
5 Jackals ('C')/('C')# ####x######x###...############
####x######x###...############
You are aware of 290 objects: ###%.%####%.%%.....###########
99 Arrows (49 unknown) ('{')/('{')x .............'.@...###########
2 Sets of Leather Gloves (1 unknown) (']')/(']')############.#%.....###########
a Scroll titled "ninhe mik" ('?')/('?')x ###########x###...%###########
17 Iron Shots ('{')/('{')# ###########x###.............##
a Sickly Green Potion ('!')/('!')# ###########x#####%#########.##
5 Hard Leather Caps (2 unknown) (']')/(']')# ###########x###############xx#
a Buckler (')')/(')')# ###########x###############%x#
a Scroll titled "sef ple nagion" ('?')/('?')# ###########x######%#########x#
99 Bolts (22 unknown) ('{')/('{')# ###########xxxxxxxxxxxxxxxxxx#
a Scroll titled "elcos ta fuox" ('?')/('?')# ##################%###########
a Flask of Oil ('!')/('!')# ##############################
-- more --###########xxxxxxxxxxxxxxxxx%####################!!xxxxxxxxxxxxxxxxx#
#####################xxxx<xxxxxxxxx-xx################################%########
There's at least one visible bug in the screenshot and the underlying implementation is horrendous. I knew it was time to stop last night, when I spent five hours trying to figure out why the mouse (x, y) coordinates weren't mapping to grid (x, y) coordinates (hint: try using the translation macro I wrote a year ago instead of searching repeatedly through the entire code base - don't code tired).

I've spent the morning cleaning up the implementation to make it a lot more readable and extendable. Think of yesterday's code (in svn) as the prototype and today's (still to be checked in) as the final code.

But I've ended up in this position again:

/*
* Displays a list of sorted indexes
*/
key_event monlist_display_sorted_indexes(int sort_by, int max, int *total_count, int row, int *line, const char* index_name, int header_offset,
bool *intro, bool *done,
int *monlist_get_index(int idx), bool *monlist_check_grouping(int idx, int group_by),
bool *monlist_check_secondary(int idx), int *monlist_get_order(int idx, int sort_by),
int *monlist_copy_to_screen(int idx, int line, monlist_type *monlist_screen, u16b *index_counts, u16b *index2_counts)

Sound familiar?

The mistake here is similar to the moral simplification I wrote about last time, where I'm trying to mix user interface with game logic (actually data manipulation this time around). I really should prepare the underlying data, then hand it off to a user interface widget to handle the user interaction, and at the moment I'm still not doing this cleanly.

And a tree widget is probably the best for this particular data structure.

If you're thinking about writing a roguelike, you really want a user interface library on top of it...

Monday, 18 May 2009

I, Spy

When the dust settles about the secret Spy update, I wonder if Valve has slipped in hints as to future spy equipment in plain sight.

Check out the clearance items (all 47c) at the bottom left hand corner of the Spy update page. While the crab walking kit is a spy-check out to the hilarious spy crouched holding a disguise kit animation 'bug', I would like to sugges the remaining equipment might be viable.

Here's how (all on sale equipment replaces the revolver).

Truth Serum cigarettes: When equipped, any enemy spy is rendered visible in their original form. Breath smoke on them to prevent them from disguising or going invisible - the spy coughs and swears in French while prevented from doing so. Because French is the best language to swear in...

Underwater Hypnopen: Shoot an enemy while you're underwater with this weapon, and they temporarily change team colour and become vulnerable to their own team's attacks (as well as blocking their team mates' path). Outside of water, the hypnopen works the same way, but with much less range.

Flamethrower Lighter: This shoots out a single jet of flame to set an enemy alight but also makes the enemy invisible while they're on fire - looking just like a burning spy of your team. Use the Flamethrower lighter when Pyros are around to distract them while you go in for the kill.

Ski mask Grappling Hook: I've suggested grappling hooks in TF2 before, but the spy needs it more than most to get behind enemy lines. If the grappling hook hits the map or building, the spy is pulled towards the building, if it hits an enemy player, they are pulled towards the spy.

While we're at it, the perceptive of you will have noticed an alternate sapper used in the Meet the Spy video. I'd suggest a throwable sapper which only disables equipment for a short time instead of requiring an engineer remove it, but allows the spy to do so at range.

And the Medic stealth kill suggests that the spy has an alternate, perhaps easier to use take down, instead of the butterfly knife he normally requires. This savate technique allows the spy to take down another player and assume their disguise without requiring the exactness of a backstab, but requires longer than the butterfly knife to successfully use and is stopped by the spy being damaged while he attempts to do so. The incentive: the spy can use this flawlessly while alone with his target, but not when there's someone else to protect them. The animation goes out to 3rd person view (just like the taunts) while the Spy executes the kill.

Thoughts?

Doull's Law

The time you save using procedural generation will be lost running the algorithm as a screen saver.

Monday, 11 May 2009

Confessions of a Roguelike Developer

I've just played Rogue for the first time ever...

(The result: I died of hypothermia trying to fight an ice monster on level 3. Said ice monster, rendered in David Gervais' beautiful pixel art to the left, but not from my game).

The iPhone as a platform has many attractions, the main one of which is the App store. But what playing Rogue on the iPhone has taught me is that the a stylus free touch screen interface may not be ideal for games.

The main lesson I've walked away with after my brief, guilty journey back in gaming history is that its hard to design a user interface when your fingers obscure the display. Whoever developer the port (I've not been able to find any credits on the official site) has done a brilliant job capturing the ability to specify all commands in the game by diving the screen into a 3 by 3 grid and letting you trace commands directly to the screen. A single press on each grid moves you in one of the eight compass directions, and examples of other commands are in the screen shot below:

But at the same time, the whole endeavour still feels clumsy and unintuitive (not helped by Rogue's weird non-diagonal movement in corridors). I need transparent phalanges, damnit!

Note that by virtue of having a stylus, the Nintendo DS is less of an issue - the fact there is already a working *band port is a big plus. So with the reservations I have about the platform - and the licensing issues the writer of the official Rogue port notes - and despite me listing the iPhone port as a possible option in this week's poll - I don't think you'll be seeing an Unangband port on the iPhone any time soon.

Poll results for 'Should Ascii Dreams have web forums?'; new poll

Inertia wins the day! (Some call it laziness, I call it avoiding the downside of proactivity). Thanks to the 38 of you who votes. The results were:

Yes
12 (31%)
No
12 (31%)
Maybe
13 (34%)
I want to comment on this
2 (5%)

The next poll - with the 'final' release of Unangband 0.6.3 drawing near, thoughts turn again to what I should work on for the next version...

Sunday, 10 May 2009

Here be dragons

I've advocated the use of arrays elsewhere, but I appear to have tripped over a big array related issue in Unangband.

For a little history, I've included the following comments from the start of cmd6.c describing a known issue relating to the fact the inventory is an array of items:

* There may be a BIG problem with any "effect" that can cause "changes"
* to the inventory. For example, a "scroll of recharging" can cause
* a wand/staff to "disappear", moving the inventory up. Luckily, the
* scrolls all appear BEFORE the staffs/wands, so this is not a problem.
* But, for example, a "staff of recharging" could cause MAJOR problems.
* In such a case, it will be best to either (1) "postpone" the effect
* until the end of the function, or (2) "change" the effect, say, into
* giving a staff "negative" charges, or "turning a staff into a stick".
* It seems as though a "rod of recharging" might in fact cause problems.
* The basic problem is that the act of recharging (and destroying) an
* item causes the inducer of that action to "move", causing "o_ptr" to
* no longer point at the correct item, with horrifying results.
As it turns out, Unangband doesn't have a "staff of recharging". But it does have items which can cause reordering to items earlier in the inventory as an "effect". The example the bug was logged for was a scroll of fire proofing, which can be applied to a single magic book. Magic books occur earlier in the inventory than scrolls, so the unstacking process which automatically occurs to get a single magic book from a collection of such books causes the inventory to reorder. The only reference to the scroll is an array index, which is no longer valid, and causes some other object to be used up instead.

I initially thought that just eliminating any item from the game which could potentially effect items earlier in the inventory would be sufficient. But because I've made it possible for the player to hit themselves with spells, anything which could destroy items in the inventory qualifies. And that means a whole lot of items.

The correct solution is reimplementing the inventory as either a linked list, or an array of pointers to items. But that's a lot of work.

So I'm going to mark the item which needs to be consumed with a flag before applying the "effect" and then relocate the flagged item in the inventory afterwards. It's a hack, but I hope a forgiveable one.

Unangband 0.6.3 prerelease 2

This is the second pre-release of Unangband 0.6.3. It incorporates a large number of bugfixes and re-incorporates the trap region code.

For more details please see the official Unangband site.

Friday, 8 May 2009

ASCII Fighter II Super Hyper Text-Based Championship Edition

I believe this may silence the critics of ASCII graphics. It also appears to be turn based...

Thursday, 7 May 2009

Rogue History Lesson

It seems some fine chaps wrote an article on the history of development of Rogue, and then had Ken Arnold pop up in the comments section to correct them...

(Oh, and got it Slashdotted).