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;
}
}
}
}
}

1 comment:

elig said...

I like it! I like having all the AI for different types of monsters in one, because it becomes easy to add functionality to all monsters at once. If one monster can be light on fire, they all might be able to! :D