Friday, November 20, 2009

Ighalsk - releases 0.1.9 and 0.1.10

I've just released Ighalsk v0.1.10: you can download the source code here. This blog post also covers the changes in v0.1.9, since I didn't write a separate blog post about those.

Changes in v0.1.9:
* Weapons, attack powers and monsters now all do random damage (using a new Dice class). As discussed in the previous post, a Dice object can be created with a string such as "4d5+6". There's a little bit more to it than that; for example, when returning the string (which is used in the character screen or when examining a weapon), zeroes are dropped: "2d3" looks much nicer than "2d3+0".
* Savefiles now use compressed versions of the Hero and the current level. This reduces the size of the savefiles by a factor of about 8, and should improve savefile stability between releases (e.g. changes to methods in a class shouldn't affect the savefile). More savings are possible: currently (in v0.1.10, I should say) the walls in a level are saved as a list of booleans, which takes a number of characters per wall. They could instead be packed into 16-bit integers (just for saving and restoring - for speed, they'd still be a list of booleans during play).

Changes in v0.1.10:
* Refactored Level to use a list of booleans for walls. This speeds up level generation and also simplifies the code: there was a Square object for each square, doing nothing but holding and making available a boolean (whether or not that square contained a wall).
* Printing level to screen is now done in 2 passes for speed: the first for walls, the second for everything else. This is because everything else is sparse: that is, stored as a list of objects with positions, while the walls have one value per position. (Checking the list of monsters in a level for each square to see if it has a monster in it was taking some time.)
* Extracted resists of Beings, Families and Armour to a separate Resists class for ease of adding resists and returning the resist for a given damage type. This removed duplicate code between Being and Adventurer, and also simplified the size of both classes.
* Added BoostPowers which improve resistances temporarily, and Boosts to hold current improvements. Any Being can have boosts theoretically, though in practice only the Hero can, if she or he uses a boost power on herself or himself.
* Blessed and Mighty Heroes can now use a boost power: "Tenacity" and "Courage" respectively.
* Fixed bug 2889190 where up and down were reversed when using keys on the numeric keypad: thanks to Genosha for raising this bug.
* Added 7 new Monsters: Lesser Haunt, Paranoia, Zombie, Ivy, Flatworm, Ghost Spider, and Greater Huntsman. (The Zombie here is a common, stock-standard zombie; variants will be added later.)
* Added Monsters for level 3 of each quest.
* Characters saved with release v0.1.9 *can* be loaded using this release! (I've only tested this for one character, but I don't expect any problems for special cases.)

I've now implemented most major pieces of functionality that I was planning to add before releasing v0.2.0. The exceptions are adding a Level Editor and special levels (level 5 of each quest) - the plan is to use the Level Editor to generate the special levels :). Of course, there's a bunch of tidying up and refactoring to do, plenty of minor functionality that could be added, content to add (new armour and monsters are the most critical parts) and no doubt bugs to fix.

Tuesday, September 29, 2009

Ighalsk - release 0.1.8

I've just released Ighalsk v0.1.8: you can download the source code here.

Changes since the last release include:
* Throughout the code, Profession has now been replaced with Epithet. That is, Heroes now have an Epithet, which summarises their powers. This brings out more clearly that the powers and abilities of a Hero are inherent rather than the result of training. It also implies (at least in my mind) that the Hero is unique, and shouldn't expect to run into opponents with similar powers and strengths when following Quests.
* The available Epithets for Heroes are now "Mighty", "Philosophical", and "Blessed" (as described here). That is, instead of "Joni the Warrior", a Hero would now be called "Joni the Mighty".
* When generating a level, corridors are now checked whether they overlap with existing open spaces before they are placed (and won't be placed if they do overlap). This makes evading monsters considerably easier - previously, the corridors were too tightly packed together and often did overlap; now, monsters often get stuck and have to wait until the Hero comes closer. (Yes, I guess I could implement path-finding (or at least a look-ahead of a few moves) when I get a spare couple of months, but I'm concerned that that would also slow down the game, which seems to have slowed down already over the last release or two for some reason.)
* Increased the size and number of rooms and corridors in levels, while keeping the number of monsters the same. This goes with the above change - now there can be more rooms and corridors without them being tightly clustered together. Reducing the monster density also means that there's not such a desperate scramble for survival when the Hero first enters the level. I'm still not altogether happy with how the rooms look on a level - the BSP algorithm mentioned on Roguebasin looks like it would generate better-looking levels.
* A Hero now has different physical, mental and spiritual strengths, depending on their epithet; a strength may add to the weapon damage, depending on the weapon. In particular, Mighty Heroes have physical strength of 4 and other strengths of 0; Philosophical Heroes have mental strength of 3 and other strengths of 0; Blessed Heroes have spiritual strength of 2, physical strength of 1 and mental strength of 0. So for example, a Mighty Hero using any bladed, blunt or piercing weapon will get 4 extra damage with that weapon; a Blessed Hero with such a weapon would get 1 extra damage, and a Philosophical Hero would get 0 extra damage. The exact values, as with so much else, will get balanced later, when the combat system is stable.
* Added a Character screen which displays the Hero's attributes, including the total resists (due to armour worn) for different weapon (damage) types such as fire, blade, or blunt, and the damage that the current wielded weapon will do, due to both the weapon itself and the Hero's strengths. This screen also displays the gold that the Hero is carrying; previously, the only way to know how much gold the Hero had was to try to buy something from a shop.
* Added descriptions of quests, which can be accessed from the Palace screen. This involved a few unexpected fixes, in that the description is read in from a text file and reformatted - but then spaces have to occur in the right places, I wanted to be able to have paragraphs in a description, and a couple other minor problems. Still, hopefully I'll be able to reuse the same format in other places (like monster and epithet descriptions).
* I also replaced one system test with a unit test for speed and simplicity - the unit test should still catch the same bug that the system test was designed to catch.
* Similarly, I refactored the Movement class to simplify it: it now only sends messages to one viewer object, where previously it also sent them to each viewer object in a list. (In the current implementation, there was only ever one viewer object in that list.) If necessary, with the refactoring, the sole viewer object could always forward messages to other viewers on a list - but I doubt that that will be necessary for this game.

Next I might focus on monsters again. I'd also like to introduce variable damage for weapons, probably using a Dice class. The constructor method for this class would take a string of the form "4d5+6", which would represent 4 dice, each with 5 sides, with 6 added to the result. (Or if you'd prefer, each die could have 20 sides, and each number could be repeated 4 times.) Each dice object would then have a "roll" method to randomly generate a value.

I would like to also introduce monster memory sometime soon - that's memory that the Hero has of monsters, not memory the monsters have of the Hero. It might be helpful to know what a Bladeworm or Nightcat looks like, though hopefully a Vampire Moth is self-explanatory ...

Tuesday, September 1, 2009

Ighalsk - release 0.1.7

I've just released Ighalsk v0.1.7 (you can download the source code from here). A lot of little things have changed since the last release:

  • The Monster Editor can now be navigated using buttons as well as keys; it also supports more than 12 monsters.

  • The Hero (PC) can now die - yes, this is a necessary feature - perhaps unfortunately; I've been playing "Lego Star Wars" with my son, and it is refreshing that characters in that game just fall apart and quickly come back together again after dying. There have been some interesting discussions of permadeath in roguelikes recently here and here.

  • After examining an item, the next screen is either the Inventory or the Equipment screen (whichever screen the item was examined from). Previously the next screen had been the map of the current level; I've now implemented a stack of states, pushing the old state onto it every time the state changes, and popping off states after examining an item. This should be useful for other screen transitions as well.

  • Treasures are now generated and placed randomly on quest levels, and can even be picked up by the Hero.

  • There's also more to buy with treasure: I've added 3 new, more powerful and more expensive weapons in the Weapon Shop, one for each type of Hero (profession).

  • I've added 9 new monsters, using the Monster Editor, and 2 new families of monsters (worm and plant).

  • This gave me enough monsters to fill in level 2 of each of the 3 quests.


There were also a couple of small refactorings that shouldn't affect game play but will make the code easier to maintain and extend.

These aren't present in v0.1.7, but here are some draft descriptions of the different types of Hero (originally mentioned here):

*Mighty*
You were born with strength and athleticism far surpassing those of ordinary men and women. You are trained in the use of an incredible variety of weapons, and in the tactics of war. You have wrestled bears to the ground, defeated a squad of enemy soldiers single-handed, and executed flawless tactical retreats.

*Blessed*
You are divinely blessed. You have been given the gifts of healing and protection. You are called on to fight evil and bring peace to the land. At your approach, flowers blossom, birds sing more sweetly, and undead gibber in terror.

*Philosophical*
You have spent many hours studying books and in the company of your own thoughts, and thereby learnt many mysteries. You can influence the universe and shape matter and energy using only the power of your mind. You have also practised the arts of argumentation, and can use them to reduce untrained and unshielded interlocutors to a state of helpless confusion. You know the names of stars, the greater and lesser syllogisms, and the secret delights of pyromania.

A friend also suggested another type of Hero, which I'm thinking of calling *Cunning*, but this type needs more features to support it and so might not make it into Ighalsk for a few releases yet ...

Wednesday, July 22, 2009

Ighalsk - release 0.1.6

I've just released v0.1.6 of Ighalsk here. (Yes, I skipped v0.1.5 - I actually made a tag for it in Subversion, but then discovered when I tested the zip file that I'd forgotten to check in a new file ... so it got superseded by v0.1.6 immediately.)

As planned, Ighalsk now has quests. True, each quest has only one level at the moment (there will eventually be 5 for each), but it's a start. One of the quests is to "defeat Lord Apathy in the Tunnels of Lost Dreams". For those who lack motivation for this quest, here's some background.

Lord Apathy is the least powerful of the Lords of Illusion. He sits (or, more often, sleeps) in a half-completed maze of tunnels filled with lost dreams, brooding, bitter and unkempt. From here, he spreads despair, hopelessness and, yes, apathy across the countryside. He should probably be stopped.

Another quest is "Defeat Elannonen the Lost in the Necropolis of Zezun". Queen Miala points out (will point out), "Why do we keep on building these cemetaries and tombs and graveyards if they just fill up with undead? Why don't we start cremating our dead?"

The final quest present in this release is "Defeat the Witch-Spider Skkshryx in Arachnaxos", about which I have little to say except that the witch-spider is not fond of vowels.

Tuesday, July 7, 2009

My Own Personal Sadness Troll

The inspirational Havi Brooks has several posts about talking to your monsters (fears, anxieties, stresses) about what they need and why they're doing what they're doing. She even suggests bringing in an (imaginary) negotiator if that would help.

My biggest problems at the moment aren't fears so much as sadness and anger. So I set up an interview with my own personal Sadness Troll. I picture him as huge and hairy, with big, weepy eyes - in fact, he looks a lot like the Muppet who helps to sell used cars in "The Muppet Movie". (Thanks to Wikipedia, I've now discovered that this Muppet is called "Sweetums" - more about Sweetums here!)

This was how the interview went. (I'm not sure exactly what the interviewer looked like, but let's assume he looked a bit like Kerry O'Brien.) There has been some post-production editing, but the basic insights were there at the start.

Interviewer: Why do you make Luke so sad?
Sadness Troll: I have to keep reminding him that he needs to be successful! So yes, he gets sad when he doesn't measure up, but he needs reminding!
Int: Why does he need to be successful?
ST: Hmmm ... not sure. Because a lot of his identity has been bound up with that ever since school? Because people expect it of him?
Int: Could he try being different instead? "Dropping out" in some metaphorical sense? Become a "virtual" hippy or pilgrim? You know, he's got the hair to be a hippy ... What about "be the change you want to see"? How about if he tried to be compassionate instead?
ST: Hmmm ... I'll have to think about that, but it might work.
(Sadness Troll wanders off, looking less depressed for the first time in a long time.)

Another part of the interview that got cut from the program was this:

ST: We need to see results, or we'll never know if he's getting anywhere!
Int: Isn't that negative reinforcement? Don't you remember that exercise that Luke did where he listed all the positive motivational techniques that worked for him?
ST: ... oh yeah. That's right. How about that.

Besides Havi, it looks to me like the interviewer also drew on Personal Alignment from the Core Protocols - identifying a goal, working out what's blocking you, what you'd need to overcome that block, and repeating as necessary. Finally, the exercise that I've done previously to list postive motivational techniques came from Barbara Sher's "Live the life you love And Stop Just Getting By". For the record, at the top of the list were:

spirituality and praying; competition (including against myself); receiving praise; getting help from friends; starting small; deadlines (if well-defined and realistic).

I gave these an A for how well they work for me; shaming got a D, and scolding, built and lecturing got an F.

So maybe there are some techniques that the Sadness Troll could pick up ...

Ighalsk - in which I discover StringIO

The suite of unit (more or less) tests that I run for Ighalsk currently takes about 11 seconds to run 281 tests (for the current version with Subversion revision number 58). This is not too bad, but I've read about projects running more than 1000 tests in less than 10 seconds, perhaps even less than 1 second. In particular, the suggestion is to avoid file accesses in unit tests (see for example Michael Feathers here).

When using Java, I think the idea is to accept an abstract Reader object as a parameter, which can be a FileReader object in the actual program, and can be mocked as a StringReader (or is it ByteArrayInputStream? so many choices!) in test code for speed. (On checking, I found that the example I was thinking of is actually for .NET ...)

I was planning to implement my own FileReader and StringReader objects in Python, but then I'd have to unit test those as well ... when, reading through the Python manual, I discovered a class called StringIO!

Its description begins, "This module implements a file-like class, StringIO, that reads and writes a string buffer (also known as memory files). See the description of file objects for operations ..." - which is exactly what I wanted!

I've started using this approach (mocking file objects using StringIO) in new classes, but I still need to switch to it for existing classes. In most cases, it should be a matter of opening a file and passing in the file object before calling a function instead of passing in a filename and opening the file inside the function, though this still means that there'll be at least one class somewhere that will still have to open some files ... but separating out this functionality should mean that file I/O only needs to happen in a small number of unit tests. Yet another treasure hidden away in the depths of the standard Python libraries!

Tuesday, June 23, 2009

Ighalsk - Adding a List of Buttons

Disclaimer: All code discussed in this post is covered by the GPL. It can be browsed through this link (to the latest release).

About a month ago, I decided to move away from a strict interpretation of "roguelike" for Ighalsk, and add a window with buttons that can be pressed to carry out actions. A friend had pointed out that it was hard to remember which keys did what - something which I've found true for many of the roguelikes that I've played over the years. I'll say a bit about my solution, but first some background.

In Ighalsk, I've taken a perhaps overly literal interpretation of the "Model-View-Controller" pattern. The source code is organised into directories called "Model", "View" and "Controller" (there's also a "Common" directory for classes used by both Model and Controller classes). For each screen of Ighalsk (each state of the controller), there are corresponding View and Controller classes. For example, there are ShopViewer and ShopController classes which handle viewing items on display in a shop and buying an item. (There's also a Shop class, in the "Model" directory, which holds item names and prices and can return an item if it is bought.)

Which View class is active (displaying text on the screen) at any given time is handled by a high-level class called IghalskViewer; similarly, which Controller class is called is handled by IghalskController.

Back to my recent changes. Stating the obvious, I called the class implementing the window with the buttons "ListOfButtons". All it really does is display a list of labeled buttons, and call methods when the buttons are pressed; but what the buttons say and do changes as the controller state changes throughout the game.

Each Controller class now has a "getEvents" method which returns a list of strings. The ListOfButtons class takes this list and adds a button to its window for each string. Each button is configured so that when it is pressed, the "sendEvent" method of the currently active Controller will be called, with that string as a parameter to the method.

(Rather than add the full functionality to each Controller class at once, in a huge, big-bang mountain of coding, I did it step by step. I started by creating an AbstractController class and made each Controller class a subclass of it: the AbstractController has a default getEvents method which returns an empty list, and a sendEvent method which does nothing. Thus at this stage, there was a window for the list of buttons, but there were no buttons on it. I then filled in the details of getEvents and sendEvent one class at a time, so that gradually more and more of them supported the list of buttons, and the list of buttons was populated more and more often while playing the game. As I went, I added a unit test for the class I was modifying.)

Each Controller also needs to respond to keys being pressed, by implementing a "processKey" method. This method now calls "sendEvent" with an appropriate event description. (This is to reduce duplication, following the "Don't repeat yourself" principle - see the Ighalsk Coding Guidelines.)

The game should be able to change screens (display a different view of the character, their possessions, or the world around them) as requested by the player, either through pressing a key or clicking on a button. In the source code I've called this "changing the state" of the controller and the view, and it happens through calling the "changeState" method of IghalskController. (I'm of the view that it's impossible to be too obvious with names.) This is done through a call-back type thing.

So there's a connection between events and states, in that for some events (buttons), the game (controller) should change to a different state. There's also a connection between keys and events, in that pressing a key is equivalent to sending a particular event.

Again exercising my gift for stating the obvious, I wrote a class called "KeyEventStateDict" to hold these connections, available to all controller classes. It's initialised with a list of tuples [(key, event, state)] and stores these internally. It then provides a "getEvent" method which is passed a key, and a "getState" method which is passed an event. It also provides a "getEvents" method, which returns a list of events in the same order as the initial list of tuples. (Internally it holds two dicts, hence the "Dict" suffix to its name.)

These methods abstract away a lot of the code needed to handle both pressing keys and clicking on buttons. But not all of it: several of the controller classes still need some code to handle special cases - to take actions, move, change what the character is wearing and so on. Otherwise, all the player would be able to do would be to change from one screen to another.

Adding a list of buttons to each state (screen) was not only adding a useful piece of functionality; it also gave me the chance to simplify the controller classes and to add unit tests for each, thus setting up infrastructure to make refactoring easier in future.

This was the biggest change between release 0.1.3 and 0.1.4. Next will come a Palace holding a Queen who asks our Hero to perform Quests ...