Tuesday, 30 June 2009

Prelude C# Excerpt #1

Prelude, if you didn't already know, is my proto-Roguelike in C#. I'm using it as a testbed to develop some concepts for importing into Kharne, as well as developing (hopefully) an enjoyable RL in C#.

To whet your appetites, this is the current version of the main AI loop for each creature. Its not a particularily complicated AI, but it is sufficient to givc the appearance at least of intelligent enemies.


// Namespace: Kharne.Creatures.Classes
// Class/Method: Creature.DoStuff()

///
/// The main creature AI behaviour routine
///

public void DoStuff()
{
// Paranoia Check
if (_alive && _awake && _energy > 0)
{
// If the creature is carrying an item that it can use or wants to use, then use it instead of doing
// something else (e.g. drinking a potion if it is at low health)
if (!_UseCarriedItem())
{
// If there is an item on the current square, and the creature judges it to be of value then
// pick it up and do nothing else
if (!_PickupItem(new Item()))
{
// TODO: also allow orders to influence what a creature does, for example, if a creature is
// a minion of the player or another creature

// The status of the creature governs what it does. Beserking differs from normal
// behaviour in that the creature will not cast spells nor will run away if brought
// to low health but will rather will stand and fight
switch (_status)
{
case CreatureStatus.BERSERK:
case CreatureStatus.NORMAL:
{
// Do we have a direct line of sight from the creature to its target?
if (_CanSee(_target))
{
// Update the target location since we can see the target
_targetLocation = _target._location;

// If the creature has mainly melee-based attacks and isn't adjacent
// to its target then attempt to move one square closer (movement is
// calculated using an A* algoritim). Note that the _Move routine deals
// with opening and closing doors on a creature-by-creature basis
if (_AI == CreatureAI.MELEE || !_Adjacent(_target))
{
if (_status == CreatureStatus.BERSERK)
{
// If we're beserking, then simply move.
_Move(_target.Location);
}
else
{
// Some creatures can cast spells, but there is only a certain
// chance of this happening, so check to see if we can cast a
// spell, if we want to, and if so, cast it
if (_CanCastSpell() && !_CastSpell(_ChooseSpellToCast()))
{
// Don't cast a spell so move towards the target
_Move(_target.Location);
}
else
{
// Can't cast a spell, so just move
_Move(_target.Location);
}
}
}
else if (_AI == CreatureAI.MELEE || _Adjacent(_target))
{
// Next to its target so let rip with melee attacks!
_Attack(_target);
}
else if (_AI == CreatureAI.RANGED || _Adjacent(_target))
{
// The first priority of creatures that have ranged attacks is to
// move to their preferred distance. In both cases below, if we can't
// actually move (due to another creature or the terrain) just attack
// anyway with ranged attacks
if (_Distance(_target) < _preferredrange)
{
if (!_Move(_GetMoveAwayLoc())) { _Attack(_target); }
}
else if (_Distance(_target) > _preferredrange)
{
if (!_Move(_target.Location)) { _Attack(_target); }
}
else
{
// Like the melee situation, check to see if we can cast a spell
// and if so, cast one. If we don't do any spells, just attack
// anyway with ranged attacks
if (_CanCastSpell() && !_CastSpell(_ChooseSpellToCast()))
{
_Attack(_target);
}
else
{
_Attack(_target);
}
}
}

}
else
{
// We can't see the target, thus we move to the last location that we hold
// for the target - of course the target may have moved
_Move(_targetLocation);
}
}
break;
case CreatureStatus.AFRAID:
{
// If the creature is afraid, then highest priority is to move away from the
// source of its fear, but if it can't then turn and prepare to fight
if (!_Move(_GetMoveAwayLoc())) { _status = CreatureStatus.NORMAL; }
}
break;
}
}
}
}
}

Sunday, 28 June 2009

Richard Stallman: Sage or Misunderstood Genius?

"The danger is that Microsoft is probably planning to force all free C# implementations underground some day using software patents"

Richard Stallman, giving his reasons not to use C#.

Incidentally, he also has some...unusual views regarding web browsing:

"For personal reasons, I do not browse the web from my computer. (I also have not net connection much of the time.) To look at page I send mail to a demon which runs wget and mails the page back to me. It is very efficient use of my time, but it is slow in real time."

Back in my university days (somewhere in the Pre-Cambian era), writing just such a program as this was our introduction to C (granted, we didn't use good old wget but instead we built our own process to do the same thing that used the Berkeley Sockets Library instead).

UPDATE 29/06/09: A member of the Debian Mono Group responds.

Saturday, 27 June 2009

The saga of TurboPower

Up until about six years ago, one of the big players in the Delphi 3rd party component market was Turbopower. Their component suites extended Delphi functionality significantly and were used by thousands of developers. Then, in early 2003, Turbopower closed. The scuttlebutt was that Turbopower had been bought by a Casino Software company who developed not in Borland*, but in various Microsoft languages (remember, this was the era before C# gained the traction it now has) and all the Borland* programmers in Turbopower were now reassigned elsewhere.

This sort of thing is remarkably common in the software industry, and as commenters to my last post on this topic pointed out, a powerful disincentive to choose commerical products to base development on. The theory is obvious: unlike open-source products, there is the risk of development resources drying up or stopping, with the worst case scenario being withdrawl of the products (such as the recent brouhaha over Oracle buying and then eviscarating Virtual Iron in favour of their own virtualization solution).

What makes Turbopower different to the usual tale of woe is that they released all their components as open source, and development has continued to this day (the Abbrevia suite of compression components, for example, has 20 registered and active developers on SourceForge, and support and development of them is still going strong).

As I've mentioned before, I'm not dogmatic on the whole closed vs open source war - both have their uses and their advantages, and anyway, a good rule of thumb for us developers is to use whatever tools allow us to get the job done on time and without too much drama, but I do think what happened with Turopower components was probably the best solution.

Incidentally, I'm considering using the aforementioned Abbrevia to implement compression of save files, when I get round to implementing them.

*I use 'Borland' deliberately here instead of Delphi, as Turbopower catered for much more than Delphi, e.g. with C++ Builder and Kylix versions of their components available.

Monday, 22 June 2009

Crawl 0.5

I can't believe I missed this. Dungeon Crawl Stone Soup 0.5 is out. Has anyone played it and can summarise the changes from the previous version? How does it compare to 0.4.5?

Saturday, 20 June 2009

MnemonicRL Trailer




I hope this isn't vapourware!

Wednesday, 17 June 2009

Commercial vs Non-Commercial Development Tools

In the comments to my earlier post, Mario Donick rightfully reminds me of an important factor in choosing development tools: price.

I'd like to ask a followup question: assuming the same price-point for a commercial and non-commercial tool, and all other things being equal (e.g. featureset, license terms, multi-platform compilation and so on), are there any non-ideological reasons why someone would choose the non-commercial tool?

(I want to specify non-ideological reasons because to many developers, supporting smaller companies or the open source development paradigm are obviously important)

And as a second followup, how do you think the answers to this question given by Roguelike developers compare to those given by developers in other areas of software development?

Monday, 15 June 2009

Dynamically bringing the dungeon alive

Its been a good few years since I played Everquest 2, but one of the gameplay ideas that struck me as easily transferrable into the typical dungeon of a roguelike is that of the infamous (if you've played EQ2 that is) "distressed merchants".

To summarise, a distressed merchant appears near other groups of mobs, begging you to help them. Once you have killed the surrounding groups of mobs, then they change into a "gratified merchant", from which you can now interact with (and buy/sell items as the reward for killing the mobs).

They are an example of what in general terms EQ2 called "dynamic adventure camps":


"Dynamic Adventure Camps (DACs) can be found throughout the lands of Norrath, especially in the large overland zones. They are usually structured around an object in the world such as the gnoll tents in Antonica, or the orc siege towers in the Commonlands. DACs are dynamic because the same camp can have many different types of events happen at the same camp, and new events can be added by designers into the mix of the other events already in place. An event is decided at the spawning of the entire camp and will stay in place until a group has completed all the stages of the chosen event. Upon successfully completing the chosen event, the entire camp will despawn. Some camps choose to respawn in the same spot, while others have multiple spawn areas to choose from."

Implementing such a concept in a typical roguelike shouldn't be too hard - since most roguelikes, unlike some MMOs, don't have to worry about the concept of phasing. It does require, as a minimum, that the map layer is able to store and associate data, and that the map can execute triggers based upon some conditional logic.

The problem comes when you end up making allowances for such events across your entire dungeon, perhaps as a result of adding handling for such events to every map cell, and you end up massively inflating the amount of memory (and hence disc space) required for such functionality. The alternative approach of hand-crafting such logic (so beloved of programmers in the days when 64K was a big deal) is obviously not extensible, but with judicious implementation of null pointers (or rather, nil pointers, to be pedantically correct in terms of Object Pascal syntax) mininum overhead can be achieved.

Sunday, 14 June 2009

Delphi for Mac and Linux?

This is massive news, if it pans out. However, it does have its downside - in a stroke it could wipe out Lazarus/Freepascal, and the focus on a multi-platform Delphi could end up abandoning the lead role in Windows development even more to Microsoft.

"Micheal Swindell...showed a nice "Embarcadero loves Delphi" logo and discussed they key goal for Delphi in the next few years: "Native Delphi Everywhere", mentioning Mas OS X and Linux, and also mobile, RIA, the cloud...Project Delphi "X" is focused on cross-platform support for MacOS and Linux, both for building servers and clients on Linux and the Mac..."