Chapter 2
RPG Entities — Characters, NPCs, and Monsters

The character is the player’s extension into the game world. Unfortunately (or fortunately, depending on how you choose to look at it) he’s as helpless as a baby without the player (scripts/AI notwithstanding). The player must do everything for him — tell him where to go and what to do. His virtual life is literally in the player’s hands. The player also determines his path in life. Should he be a warrior? If so, what kind? The strong, dumb brute who plows through everything and thinks with his sword or one who plans before going into battle? How should he fight — toe-to-toe or hit and run? Maybe he should be a mage — weak in body but with immense powers of magic at his disposal. Maybe the player doesn’t want to play as what would normally be considered a good character. Perhaps a thief or assassin is more his style. The possibilities can be almost endless depending on what you give him. If you design it right, the player could even go through the game more than once, playing as a different character each time and having a completely different experience. You could also allow the player to have a character with more than one class. Some paper and pencil RPG systems allow multi-class characters, such as a fighter/mage or mage/assassin. Some classes could be built as a multi-class type, like a ranger or druid — a fighter who specializes in nature-related magic. A bard could also have some fighting skill and music-related magic. A ninja could be a thief with magic relating to stealth. There’s a third possibility — the classless character. This type of character is usually found in a skill-based system where he can do anything (albeit not well at first) and gets better as he uses skills.

How you design the character is up to you — all types have their positives and negatives. Designing your system to be as open as possible will allow you to go whichever way you want. We’ll take a middle-of-the-road approach — classes and skills. The character can do almost anything he wants, but it will be more difficult to learn some skills than others depending on his class. For example, a mage could learn to use a sword, but wouldn’t be as good with it as a fighter. This also allows us to give bonuses for certain skills based on the character’s class.

The code that we’ll use to represent our character is used to represent any entity, be it a player character, nonplayer character (NPC), or monster. Although a character may be referred to, almost everything in this chapter, and most of the others, will apply to all types of entities.

Stats

One of the main defining features of an entity is its stats. These are the values that tell us how strong a character is, how intelligent he is, how much punishment he can take in battle, etc. Here are some typical stats that are used in many RPGs and what they mean:

Strength (STR) — How strong a character is and how much weight he can carry. It is also used for calculating damage bonuses when fighting with melee weapons and limiting the weapons and armor a character can use. A 98-pound weakling wouldn’t be able to lift and fight with a two-handed sword or wear full plate mail. This would be a primary stat for melee classes like fighters.

Primary stat: A stat that should be higher than all others, allowing the character to better perform skills related to what he does for a living.

Dexterity (DEX) — This is used in determining how well a character performs in combat and can be used to limit the weapons available to a character. It can also be used to determine how well a character performs skills that require manual dexterity, such as lock picking, playing an instrument, etc. It would be a primary stat for thieves, assassins, and monk-type characters.

Agility (AGI) — While DEX is used in hitting a foe, this is used in determining how well a character can avoid being hit and perform other skills that require coordination of the entire body, such as tumbling. Some systems may use one stat for both, but I prefer to split them this way. It just seems more realistic to me and allows more flexibility. This is another important stat for characters with a DEX primary stat.

Constitution (CON) — This is a measure of how quickly a character recovers from injury or how resistant he is to poison and disease, and helps in calculating how much damage a character can take before losing consciousness. This is another important stat for melee characters.

Intelligence (INT) — How quickly a character can learn such things as magic and languages and figure out problems. This would be a primary stat for characters that use magic.

There are several other stats that could be used: Wisdom and Charisma, for example. Some systems use Wisdom for cleric/priest type character classes and Charisma is used in relating to people. Bards, for example, would depend on Charisma.

To hold the information on a stat, we’ll define a class with the following members:

Listing 2-1


public class Stat
{
private StatType _type;
private string _name;
private string _abbreviation;
private string _description;
private string _statCalculation;

private static readonly char[] _operators = { '+', '-', '*', '/' };

public const short MaxStatValue = (short)DieType.d100;
public const int PoundsPerStatPoint = 3;

}

In order to make the RPG library as flexible as possible, a developer will be able to create whatever stats he likes. We need to have a way to easily find one stat in the list of stats, however. We’ll use the _name member as it will be unique for each stat. This will be done for some classes in the library for which we can basically guarantee that the name is unique. Other classes where the name member would be not so easy to deal with (skills for example) will have another member to allow an item to be found. We’ll see how this works in the next chapter.

The _type member can be one of two values:

Listing 2-2


public enum StatType
{
Regular,
Calculated
}

Some game systems have stats that are based on the values of other stats or involve calculations such as the average of two stats. The _type and _statCalculation members allow you to do this. We’re only allowing four operators to be used for calculated stats as you can see from the _operators array. If you need more than this, simply add them to the array in the declaration and add the logic to deal with them in the code that calculates the stat.

Listing 2-3


public Stat()
{
}

public Stat(string name)
{
_name = name;
}

public StatType Type
{
get { return _type; }
set { _type = value; }
}

public string Name
{
get { return _name; }
set { if (!string.IsNullOrEmpty(value)) _name = value; }
}

public string Abbreviation
{
get { return _abbreviation; }
set { if (!string.IsNullOrEmpty(value)) _abbreviation = value; }
}

public string Description
{
get { return _description; }
set { if (!string.IsNullOrEmpty(value)) _description = value; }
}

public string StatCalculation
{
get { return ConstructStatCalculation(); }
set
{
if (_type == StatType.Calculated)
{
if (!string.IsNullOrEmpty(value))
{
_statCalculation = value;
}
}
else
_statCalculation = value;
}
}

Two constructors are provided for a bit of flexibility, but only the first is required because of the way we’re going to handle dealing with the files that will store the data for our stats. We have our accessors for the members for the same reason. I chose not to make the members public because we’re doing a bit of validation to ensure none of them can be set to empty or null values in most cases. If the stat is not a calculated stat it doesn’t matter if we set it to empty or null, but if it is we have to ensure there’s a calculation there or we’ll get an exception when we attempt to use it.

We’ll be using a 1-100 range for our stats. We note this with the MaxStatValue member, which allows code outside the Stat class to tell what the greatest value for a stat can be. Other commonly used ranges are 1-10, 1-18, or 1-20. I like the bigger range since it allows more variation. In fact, we’ll be making everything we can 1-100 based if it makes sense, as you’ll see later. Although we’ll use the 1-100 range the most, we will have others, so we’ll create an enum to define them:

Listing 2-4


//Enum for types of dice
public enum DieType
{
d4 = 4,
d6 = 6,
d8 = 8,
d10 = 10,
d12 = 12,
d20 = 20,
d100 = 100,
MaxDie = 100
}

You generate a number in the range by calling one of the static, overloaded GetRandomNumber functions:

Listing 2-5


public class GlobalFunctions
{
static Random rnd = new Random();

public static short GetRandomNumber(DieType die)
{
return (short)rnd.Next(1, (int)die + 1);
}

public static int GetRandomNumber(int min, int max)
{
return rnd.Next(min, max + 1);
}
}

We’ll be adding more functionality to this class as we go on.

The parameters of the second version of the function allow you to generate a number in any range. For stats, this will always be from 1 to 100, but other parts of the game will use different ranges, such as the damage caused by a weapon.

The PoundsPerStatPoint member tells us how many pounds a character can lift for each point in his Strength stat. We’ll see this used a bit later in the chapter in a calculation.

So we have our Stat class, but we don’t need to store all this data in every entity. For that we have another class called, amazingly enough, EntityStat:

Listing 2-6


public class EntityStat
{
private string _stat;
private short _value;
private List<Bonus> _bonuses;

public EntityStat()
{
}

public EntityStat(string stat, short value)
{
_stat = stat;
_value = value;
}

public string StatName
{
get { return _stat; }
set { if (!string.IsNullOrEmpty(value)) _stat = value; }
}

public short CurrentValue
{
get
{
short val = _value;

foreach (Bonus bonus in _bonuses)
val += bonus.Amount;

return val;
}

set{}
}

public short BaseValue
{
get { return _value; }
set { _value = value; }
}

public List<Bonus> Bonuses
{
get { return _bonuses; }
set { _bonuses = value; }
}

public void IncreaseValue(short val)
{
_value += val;
if (_value > Stat.MaxStatValue)
_value = Stat.MaxStatValue;
}

public void ReduceValue(short val)
{
_value -= val;
if (_value < 1)
_value = 1;
}

public void AddBonus(Bonus bonus)
{
if (_bonuses == null)
_bonuses = new List<Bonus>();

_bonuses.Add(bonus);
}

public void Update(float time)
{
if (_bonuses == null)
return;

Bonus bonus;

for (int i = _bonuses.Count - 1; i >= 0; i--)
{
bonus = _bonuses[i];

bonus.ElapsedTime += time;

if ((int)bonus.ElapsedTime >= bonus.Duration)
_bonuses.Remove(bonus);
else
_bonuses[i] = bonus;
}
}
}

We have our _stat member, which will be the name of the stat, and _value, which is the normal unmodified value of the stat. The list of Bonus objects allows us to modify the stat for a short period of time. This could be anything from poison, which would reduce the stat, to a magic spell, which could increase or reduce it. If we want to check this modified value, we use the CurrentValue function. If we want to change the normal value, we can either set it directly using BaseValue or add to or subtract from it using the IncreaseValue or ReduceValue methods. Note that the two functions have validation to ensure the value of the stat can’t be set outside of the 1-100 range. The IncreaseValue function does this by using the static MaxStatValue member of the Stat class. If you want to change your stat range to, say, 1-20, you would change this member, and all the code that deals with checking for maximum stat values should work without modification. This is a good thing and something you should do whenever possible.

The Bonus structure looks like this:

Listing 2-7


public struct Bonus
{
public BonusType Type;
public short Amount;
public int TimeStarted;
public int Duration;
public float ElapsedTime;
}

Every time through the game loop when the game is running each stat’s Update method will be called, passing the elapsed time since the last update. We’ll use this value to update the ElapsedTime member of every Bonus object in the list. If the ElapsedTime member is greater than or equal to the Duration member, the object is removed from the list.

Bonus durations must take into account when the game is paused and unpaused or when the player quits. When the game is paused or ended, the Update method is not called. This is done outside of the class in the code that handles the interface and input. We’ll see that in action later on.

Although bonuses are the only way stats can be affected in our game, some systems allow stats to increase in other ways, most notably by checking every level to see if they increase. Although some stats like Strength or Constitution could theoretically increase during the course of an adventure, I don’t believe every stat has to have this capability. A full-grown man’s dexterity is unlikely to get significantly better just from being involved in battles or performing skills like lock picking. A mage’s Intelligence stat is unlikely to increase just from casting spells. If you want to allow a character’s Strength or Constitution stat to increase through training, feel free to implement it. Such a thing isn’t significant in creating an entity so we won’t go over it here.

Every stat is saved in its own XML file, which during development looks like this:

Listing 2-8


<?xml version="1.0" encoding="utf-8"?>
<XnaContent>
<Asset Type="XNA_RPG_Library.Stat">
<Type>Regular</Type>
<Name>Strength</Name>
<Abbreviation>STR</Abbreviation>
<Description>The ability to lift or move objects and to
increase melee attack damage</Description>
<StatCalculation Null="true" />
</Asset>
</XnaContent>

Saving our classes to XML like this is easily done using the IntermediateSerializer class built into the XNA Framework:

Listing 2-9


private void SaveStats()
{
try
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;

XmlWriter writer;

foreach (Stat stat in _stats)
{
writer = XmlWriter.Create(Application.StartupPath +
@"Games" + _gameName + @"stats" + stat.Name +
".xml", settings);

IntermediateSerializer.Serialize(writer, stat, null);

writer.Close();
}
_saved = true;
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}

If you have any experience with serializing XML data, you may wonder why the XmlSerializer class isn’t being used. The IntermediateSerializer class has the following benefits over XmlSerializer:

Compact XML representation for lists of numeric and vector types

Handles arbitrary polymorphic data without requiring subtypes to be predeclared

Handles shared resources, for serializing cyclic data structures

Handles external references, automatically fixing up relative filename links from one file to another

Supports dictionaries

XML representation can be customized without directly modifying the target types (by implementing a ContentTypeSerializer)

There are some downsides to using this class, however. The main one is that it doesn’t work on the Xbox 360. If you’re using the IntermediateSerializer to save data to an XML file that will be compiled using the content pipeline and thus read in using the ContentManager class, this isn’t much of a problem. There are a couple of other differences between the two classes. I would refer you to Shawn Hargreaves’ blog posts on the subject for more information (http://blogs.msdn.com/shawnhar/archive/2008/07/28/intermediateserializer-vs-xmlserializer.aspx). He’ll give you all the information you’ll ever need to know about the IntermediateSerializer.

You’ll see this same code used for just about every class in our game. Note that the stat’s name is used for the name of the file. This will usually be the case for other classes as well. The EntityStat objects are saved when the Entity object is saved, as we’ll see shortly.

To load a stat file, another function is added to the GlobalFunctions class that again uses the IntermediateSerializer class:

Listing 2-10


public static Stat LoadStat(string filename)
{
try
{
XmlReader reader = XmlReader.Create(
new FileStream(filename, FileMode.Open));

Stat stat = IntermediateSerializer.Deserialize
<Stat>(reader, null);

reader.Close();

return stat;
}
catch (Exception ex)
{
return null;
}
}

The code to load most other classes will look just like this; simply change the class type that’s passed and the return type of the object that’s filled with the data from the file.

That covers all the code we’ll need for stats. Once we have some stats saved, we’ll be able to create races.

Races

How many fantasy or sci-fi RPGs do you know of that allow you to play as a human only? Not many, I would imagine. Not only does it make the game more fun and interesting when you have different races, it can also increase the replay value of your game. Players can play through it as one race, then start a new game as a different race and potentially have a totally new experience. MMORPGs usually start players in different locations based on their race, usually the main city in the area where that race is dominant. This makes the game somewhat different for as long as it takes the player to venture out into the rest of the world, which, depending on how detailed the developers built that section of the world, could be weeks or even months.

The main use of races (other than having them as part of a realistic fantasy world) is to modify an entity’s stats and skills. For instance, elves in your world may be slight, beautiful creatures very in tune with nature. This is probably the classic view. To represent this in your game you may make some modifications along the following lines:

Table 2-1: Elf stats and skills

Depending on your view, elves could either be so nature-oriented that their intelligence suffers or so disinterested in associating with other races that they have time to dedicate themselves to studying the world around them and all areas of knowledge. In the latter case you would probably want to make the Intelligence bonus a positive number, say +5 or +10. You could even have several different types of elves — the intelligent type and the nature type. For the world we’ll create as part of our game, we’ll go with the latter type of elf.

The full list of races and the modifications they receive are as follows:

Human — Humans are the “average” race, neither strong nor weak at any particular skill and having no stat modifiers and no skill bonuses. They can be any class and alignment. This flexibility is their strength.

Elf — Elves are one of the long-lived races and the embodiment of nature, making their homes in the forests. Being a solitary race, they have not wasted their time in the petty bickering and quarreling common among most races and have been free to study the world around them. They are stronger at magic than any other race but much weaker physically. They make excellent mages and rangers.

Dwarf — The hardiest of all the races, dwarves are short, stocky, hairy, and extremely strong. Their constitution is unmatched as many people have found out when entering into a drinking contest with a dwarf. They aren’t the smartest race as a whole, but they make excellent fighters. Dwarves are normally armed with huge battle axes, but some of the more dexterous dwarves can be found wielding two normal axes as if they were small hand axes. Their bonuses are shown in Table 2-2.

Table 2-2: Dwarf stats and skills

Half-Elf — Usually the product of violence or a bored female elf ’s dalliance with a human, half-elves are usually looked on with disdain by both races. This means they are often solitary individuals and as such make excellent rangers. They are not as long-lived as full elves but are more physical. They are not quite as disciplined as their elf kin, and thus are not quite as good at discipline-related skills. They are a good alternative for players who don’t want to play a “plain” human or take the physical penalties of a full elf.

Table 2-3: Half-elf stats and skills

Halfling — Distantly related to dwarves, halflings are a diluted cross with the human race. As short as their cousins but of normal proportions, halflings are quick and dexterous. These attributes, combined with a natural curiosity, make them excellent thieves. They are as nonmagical as dwarves, though a rare halfling will manifest a wild magical talent.

Table 2-4: Halfling stats and skills

This is a small sample of the races you can have in your RPG. You can have other “half ” races — orcs, ogres, trolls — or variations of the halfling race — gnomes, hobbits, etc. You can even get wild and have creatures such as minotaurs, centaurs, and pixies. Some MMORPGs allow the player to play as a demon or dragon. It’s all up to you, limited only by your imagination. Of course, you have to consider the time it would take to create the graphics for each race and to balance the races so that they are all relatively equal overall. If you don’t do this, players will probably end up playing as the most powerful race and ignore the others. With each additional race you add, it takes exponentially longer to balance — it’s not a linear increase.

We’ll be starting players in the same location regardless of the race selected. Since we’re not building a full game we don’t really need to explain why the player is starting where he is. For a full game you would want to provide some kind of backstory to tell the player how he wound up at the starting location.

Entity Classes

As with the EntityStat class, there’s not a lot to the EntityClass class, so let’s take a look at the whole thing:

Listing 2-11


public class EntityClass
{
private string _name = "";
private string _description = "";
private DieType _hpDice;

private Dictionary<string, int> _statModifiers;

public string Name
{
get { return _name; }
set { if (!string.IsNullOrEmpty(value)) _name = value; }
}

public string Description
{
get { return _description; }
set { if (!string.IsNullOrEmpty(value)) _description = value; }
}

public DieType HPDice
{
get { return _hpDice; }
set { _hpDice = value; }
}

public Dictionary<string, int> StatModifiers
{
get { return _statModifiers; }
set { _statModifiers = value; }
}

public int StatModifiersCount
{
get { return (_statModifiers != null) ? _statModifiers.Count : 0; }
}

public EntityClass()
{
//Empty constructor for use with the toolset.
//During the game the constructor below is used.
}

public EntityClass(string name)
{
_name = name;
}

public int GetStatModifier(string name)
{
int value = 0;

if (_statModifiers != null)
_statModifiers.TryGetValue(name, out value);

return value;
}

public void AddStatModifier(string name, int value)
{
if (_statModifiers == null)
_statModifiers = new Dictionary<string, int>();

_statModifiers.Add(name, value);
}

public void RemoveStatModifier(string name)
{
if (_statModifiers == null)
return;

_statModifiers.Remove(name);
}

public void ClearStatModifiers()
{
if (_statModifiers != null)
_statModifiers.Clear();
}

public Dictionary<string, int> GetStatMods()
{
return _statModifiers;
}
}

The _name and _description are standard here with one possible unique feature for the name; while we won’t implement it here, some RPG systems allow a character to be more than one class at a time, such as fighter/thief or thief/mage. This doesn’t include specific examples of these combinations like rangers (kind of a fighter/thief/mage), druids (a fighter/mage), or paladins (a fighter/cleric).

These multi-class characters sometimes change classes at a point in time, switching focus and training from one to another. This is usually the case for nonspecific combinations where the first class is what the entity began his adventuring career as and the second is what he switched to. In some RPG systems, the character may have the skills of his previous class decrease in effectiveness as he shifts the focus of his training and usage to the skills of his new class.

In the case of the specific multi-class characters mentioned above, the character normally is equally proficient in all the skills possessed by whatever combination of classes makes up his specific class.

Feel free to add either the specific examples or the combination classes if you wish. You could even create expansion packs for your game in which you add new entity classes along with the skills, spells, and items that go with the classes. This gives you the benefit of increased replayability of your game and means you don’t have to spend as much time up front balancing all of the classes before releasing your game. Note that a good bit of code would need to be changed in order to handle multi-class entities.

The _hpDice member is used to give certain classes more or less hit points than others to simulate the training or lack thereof in the physical aspects of the character. The combat-oriented classes will usually be tougher than the noncombat-oriented classes, and we use this member to show that.

Entities

We now have everything we need to create an entity. There are a lot of members, so hang on and dive in:

Listing 2-12


public class Entity
{
#region Members

private EntityType _type;

private string _name;
private string _classID;
private byte _level;
private int _experience;

private string _raceID;

private short _baseHP;
private short _curHP;

private byte _diseaseResistance;
private byte _poisonResistance;
private byte _magicResistance;

//Affects is empty for these
private List<Damager> _damageResistances;
private List<Damager> _damageWeaknesses;

//Any misc, offensive, or defensive bonuses
//(i.e., spells cast on character, items, etc.
//May be positive or negative)
private List<Bonus> _defBonuses;
private List<Bonus> _offBonuses;
private List<Bonus> _miscBonuses;

private EntityAlignment _alignment;

private EntitySex _sex;
private short _age;

private List<EntityStat> _stats;

private string _portraitFileName;

private bool _isVendor;

#endregion

We’ll have four types of entities in our little game, although you could probably get by with three:

Listing 2-13


public enum EntityType
{
Character,
NPC,
Creature,
Monster
}

We need to differentiate between the player’s character and the nonplayer humans that normally populate cities in the game world, so we have two members of the enum for human and humanoid entities. While you might be able to get away with only one additional member for the nonhuman or humanoid (elf, dwarf, etc.) entities, you may want to have normal, nonmagical creatures (lions, and tigers, and bears, oh my!) in some areas and magical types (such as orcs, goblins, centaurs, and dragons) only in places like dungeons. This is one reason for the Creature and Monster values. Another reason might be cases in which you have entities randomly picked for encounters in the woods. For instance, you may only want to pick from creatures, not monsters. You might also have characters whose interaction with animals could help or hurt them. An example of this is if we want to penalize a ranger or druid class character if he kills a creature. Since those classes are supposed to be in touch with nature, they may temporarily lose some abilities or bonuses if they kill an animal. That little bit of flexibility might make for a more “real” game world.

The _name member is standard. It could be anything from something like “Jonathan the Blacksmith” to “Black Bear” to “High Demon.” The player will be entering a name, so you might want to have some kind of validation to keep players from entering a character that doesn’t make sense, such as a symbol or number, or maybe even a vulgarity if you have an online component to your game and you’re concerned young kids might be playing. This would take a bit of work and is never perfect, but anything may be better than nothing. Preventing the entry of symbols and numbers is easy; simply check the keystrokes as they’re entered and ignore anything you don’t want to allow. For something like vulgarity, you could create a list of words and validate what the player enters against the list. If you have player registration information sent to you, you could update the list when you see something you may have forgotten.

The _classID and _raceID members are the _name members of the EntityClass and Race classes selected for that entity or could be empty in the case of creatures or monsters. This is another case where validation could be used to ensure valid data is entered for character or NPC entities.

The _level and _experience members work together in advancing the character or making him more powerful. An entity could get experience for killing other entities, finding treasure, or performing any action that you decide. Upon reaching a certain number of experience points, an entity would increase his level. This would give him skill points to increase his skills, and possibly allow his stats to increase if you’re using that option. Usually the number of experience points needed to reach a new level is a cross between a linear progression and an almost exponential one. At first you might see something like:

Table 2-5

Level

Experience Required

1

1000

2

2000

3

3000

4

4000

5

5000

Once you start reaching higher levels, the amount required to reach the next level increases greatly:

Table 2-6

Level

Experience Required

20

200000

21

210000

22

225000

23

250000

24

300000

25

325000

26

350000

27

400000

28

450000

29

500000

30

1000000

The higher the level, the more experience is required to advance, which means the character will have to fight tougher monsters in order to get more experience per fight. Usually the monsters have to have a level somewhat near the character’s level in order to get experience. Sometimes the amount of experience decreases with the increase of the difference between the monster and character level. That is, the monsters that the character fought at level 5 probably won’t give him any experience at level 20, so he wouldn’t be able to just fight them and expect to advance.

The _baseHP and _curHP members are the character’s base amount and current amount of hit points. Hit points are a standard technique used in RPGs to determine how much damage a character can take. When a character reaches one-tenth of his base hit points, he becomes unconscious. When he reaches zero hit points, he dies. This also is a bit different from most RPGs. In some, when you reach zero you become unconscious and a negative number means you die. In others, you never lose consciousness; you just die when you reach zero. I dislike these for two reasons. First, you start dealing with negative numbers when there’s no reason to. Second, dying without losing consciousness is unrealistic. There should be an opportunity to survive an encounter by losing consciousness. If a player is fighting an unintelligent creature that assumes the character is dead when he loses consciousness, the player might survive the encounter. It’s a very slim chance, but at least it’s there.

There are many ways to calculate an entity’s hit points. The calculation I’m using is to take the average of the entity’s Strength and Constitution stats and add a random number from 1 to the DieType value for the entity’s class for each level of the character. Expressed as a formula, this would look something like the following:

 

Many RPG systems only give the character a couple of hit points to start off, especially if it’s a nonfighting type such as a magic user. You could almost blow on them and they would die. Any character hardy enough to take on the adventuring life would have to be a pretty rugged individual if he was combat oriented. Even for noncombat characters, you would expect them to at least be able to get bitten by a rat without dying.

The next three members of the Entity class (_diseaseResistance, _poisonResistance, and _magicResistance) are meant to help deal with things that you almost always encounter in RPGs, especially the last one. A character will have a certain chance to resist the effects of disease, poison, and magic. This chance may be modified by his race and possibly his class. The members hold those modifiers, rather than access the character’s class and race each time and do the calculation. A druid or ranger may be more immune to disease. A mage may be better able to resist magic. A cleric may be better able to fight off poison due to his healing nature. The value of the variable will be subtracted from a die roll to determine if the character succumbs to the influence of whatever he is facing.

Diseases and poisons can be found in many places and contracted in many ways through plants and other environmental objects, creatures both normal and magical (you could even call something like zombification a disease if you want, since in most movies if you get bitten by a zombie the effects are like a disease), and specific spells by evil magic users. The _magicResistance member you can expect to be used most often though. Any combat or noncombat encounter with magic will usually provide the chance to nullify or reduce the effects or results of spells, traps, weapons damage (if the weapon has magical properties), or anything else with a magical component. How you come up with a value for these members could be dependent on a number of factors, with the stats you use for your entities likely being the biggest. For disease and poison, you would expect an entity’s Constitution stat to figure into the value. We’ll make it somewhat simple and easy, dividing the Constitution stat by 10 to use as the value for these two, and the average of the Constitution and Intelligence stats divided by 10 for the _magicResistance member.

The Damager structure that makes up the objects in the lists looks like this:

Listing 2-14


public enum DamageType
{
Crushing,
Piercing,
Slashing,
Fire,
Water,
Magical,
Disease,
Poison
}

public struct Damager
{
public DamageType Type;
//Format = "n,n"
public string DamageAmount;
//The type of entity the damage affects, empty for all types
public string Affects;
}

We’ll be using the Damager structure later on with items, thus the reason for the Affects member. Since the Damager objects in the two lists are specific to the entity, the string will be empty in these cases.

The _damageResistances and _damageWeaknesses members provide a bit of uniqueness for entities. The items in these two lists can come from any number of places — spells cast on the entity, potions the entity might have drunk, genetic mutations or other background explanations, etc. Unlike bonuses, these are meant to be permanent modifiers, although you could have bonuses that have no duration and are treated as permanent.

It should be obvious, but for the items in the _damageResistances list, the DamageAmount is used to reduce damage of a specific type, not increase it. If you wanted to make sure there was no misunderstanding, you could create a Resister struct with the same members, but that would be a waste in my opinion so I didn’t bother. I figured anyone who looked at it would figure it out, but I felt it worth mentioning anyway.

Examples of resistances could be a skeleton’s resistance to piercing weapons. As you can probably imagine, it’s difficult to really do damage to a skeleton with these kinds of weapons since they don’t bleed and have no internal organs to pierce. Even if the weapon did hit a bone, the tiny hole would probably not be noticed. Examples of weaknesses could be werewolves being affected by silver; vampires and holy water or sunlight (normal or magical in nature); mummies or zombies and fire; and water-based creatures and fire or fire-based creatures and water. Just as a skeleton might be resistant to piercing weapons, it could have a weakness against crushing weapons.

The next three members are similar to the last two, but they apply to specific times, two of them during combat and the other in noncombat situations. The _defBonuses list contains bonuses that apply to an entity’s defense and the _offBonuses list applies to the offensive capabilities. The _miscBonuses are used out of combat for things like healing, tracking, hiding, etc. The bonuses can be the result of spells cast on the entity, potions, or natural abilities.

The _alignment member holds a value for one of three different alignments:

Listing 2-15


public enum EntityAlignment
{
Good,
Neutral,
Evil
}

The uses for a character’s alignment are varied and depend on how detailed you want your system to be. Alignment can affect the experience a character gets from killing entities and might cause the character to actually lose experience in some situations (a paladin-type character who is noble might lose experience by attacking an entity from behind, for example, whereas a thief or assassin might get more experience). Alignments can affect the spells a character can learn, the items a character can use (magical weapons with an intelligence could have an alignment and prevent a character from taking advantage of its abilities or actually damage the character if he attempts to wield or use it), and the willingness of NPCs to help or interact with the character. All of these things will have to be determined by the developer, taking into consideration many factors — time of development, realism of the system, experience of the developers, etc. In general, alignments can be useful in making the world more interesting and rewarding the player for having his character perform as a real-life person of that type would.

The alignment of a character is his moral orientation and defines how a character will conduct himself and act in certain situations.

Good and Evil are fairly straightforward. For our purposes, Neutral describes a character that is mainly interested in himself and doing things that benefit him. Although he’s totally self-centered, he can shift slightly to Good or Evil as the situation permits if it benefits him in the end. Although this might seem to be the best alignment, we’ll balance it out by offering very little in the way of items specific to this alignment. Also, NPCs of Neutral alignment are rare and (depending on the NPC) will be slightly less willing to help out a Neutral character than a Good or Evil character.

The _sex and _age members don’t hold any surprises, except that the _sex member could be ignored for nonhumanoid entities such as creatures or monsters, no matter what you set it to. While animals usually are either male or female, it might not matter for your game and creating two entities where the only difference is sex might not make sense. You could always randomly pick the sex at run time if you wish.

The age of a character is usually dependent on his race. Some races have extremely long lives on average. Age can also affect a character’s performance in combat and other situations if you want to enhance the combat resolution code by adding this kind of calculation. A 20-year-old human fighter is usually more likely to win when in combat with another human fighter who is 50 or older. On the other hand, a magician who is 40 is likely to have picked up more arcane trivia than one who is 20, and might have a better chance of recognizing an obscure book or rune. For the purposes of this book we won’t take age into effect, but it’s available for anyone who wishes to incorporate it into their system.

The _portraitFileName member is used only in your GUI. Some RPGs display a picture of the character that the player can click on to access information about him such as stats, equipment, skills, etc. You can see the pictures of the characters in the group in the upper-right corner of the following screenshot:

Figure 2-1

You might allow the player to import his own portrait. This gives him a little more attachment to his character, which is important. If the player doesn’t feel an attachment, he may not really care about what happens to the character and may recklessly charge into situations that could kill him. If the player feels attached to the character and cares what happens to him, he might play the game differently, thinking before charging in. Something this simple could result in an entirely different experience.

Later on in the game we’ll be adding the ability for the player to buy and sell items at a store. In order to determine when the player is interacting with a vendor, the _isVendor property is checked. If the value of the property is set to true, a GUI is displayed to allow the player to buy and sell.

You can mix and match classes, races, and alignments to restrict how a person plays a character. For example, a good thief is kind of a contradiction, unless you want to imagine a Robin Hood type character. Also, a dwarf thief is probably a rarity as dwarves aren’t usually known for their finesse and subtlety, two things a thief needs to be successful. I can almost imagine a drunken dwarf thief stomping around in the dark, cursing, and trying to sneak up on someone to mug him. He’s not likely to surprise anyone that way. Also, a halfling fighter or ogre monk would be a very rare thing to see. The following tables show the possible combinations we’ll use:

Table 2-7: Race/class combinations

Table 2-8: Race alignment combinations

Table 2-9: Class alignment combinations

Let’s take a look at one additional function in our class:

Listing 2-16


public int MaxWeight()
{
int str = 0;

//find the strength stat
foreach (EntityStat stat in _stats)
{
if (stat.StatName.ToLower() == "strength")
{
str = stat.CurrentValue;
break;
}
}

return str * Stat.PoundsPerStatPoint;
}

This simply lets us know whether or not an entity can lift a particular weight without having to access the Strength element of the Stats collection. Code outside of the class could access the data directly and do the calculation itself, but if you decided to change the multiplier to another number, you would have to find every line of code that did the lookup and calculation and change it. Helper functions like this mean code only needs to be changed in one place. All code that calls the function gets the updated value automatically.

The Entity class will expand as we implement other objects in coming chapters. Things like skills, spells, and inventory items will round out our Entity class. If you thought there was a lot of data to keep track of with up to this point, you haven’t seen anything yet.

The Toolset

Throughout this book, we will construct a toolset to aid in the creation and management of various objects that can be used in the game. The first part of the toolset will allow the user to create and manage entities — PCs, NPCs, and creatures. Before we can do that though, we need to create the data for stats, entity classes, and races. Here’s what the toolset will look like when it’s launched:

Figure 2-2

Before we can start creating our data, we need to create a game for it to be attached to. For now, all we need to know is the name of the game and a description for it:

Figure 2-3

We’ll store this data in a class, of course:

Listing 2-17


public class RPGGame
{
private string _gameName;
private string _description;

public string GameName
{
get { return _gameName; }
set { if (!string.IsNullOrEmpty(value)) _gameName = value; }
}

public string Description
{
get { return _description; }
set { if (!string.IsNullOrEmpty(value)) _description = value; }
}
}

We’ll be adding more to this class in later chapters.

All the data for the different objects will be created in folders underneath a folder created for the game. The folder structure will look like the following figure:

Figure 2-4

Once we have our game created, you’ll notice that the only button that’s enabled is the one to create stats. That makes sense since stats drive pretty much everything in the game. Click the Show stats editor form button () and you’ll see the following:

Figure 2-5

We’ll be creating five stats — Strength, Dexterity, Constitution, Agility, and Intelligence. Enter the following information (or open the files named <stat name>.xml in the companion code, found in the Chapter 2XNA RPG ToolsetinDebugGamesTeststats folder, clicking the Add button after each item:

Table 2-10

Stat Name

Strength

Abbreviation

STR

Description

The ability to lift or move objects and to increase melee attack damage

 

 

Stat Name

Constitution

Abbreviation

CON

Description

Ability to withstand and recover from damage, illness, disease, etc.

 

 

Stat Name

Agility

Abbreviation

AGI

Description

Full body dexterity, used in dodging, tumbling, etc.

 

 

Stat Name

Dexterity

Abbreviation

DEX

Description

Manual dexterity, ability to use weapons

 

 

Stat Name

Intelligence

Abbreviation

INT

Description

Ability to learn things, such as spells, languages, etc.

After you have stats entered, you can enter races. The form to do this looks like the following:

Figure 2-6

As with stats information, you can open the files in the various folders under the Chapter 2XNA RPG ToolsetinDebugGamesTest folder to see what information will be used in the demo game that’s created along the way in this book.

After races, entity classes can be added:

Figure 2-7

And, last but not least, once you have all these, you’re ready to start creating entities. A lot of information is needed, as you can see from Figure 2-8.

Most of the data is entered by using the text boxes and combo boxes. There are several exceptions:

Sex — I chose to use radio buttons for this since there are only two choices for human and humanoid entities. The property is set to EntitySex.None if the entity type is set to something other than Character or NPC. If you want to specifically set this for monsters and creatures, I would recommend using a combo box instead.

Base/Cur HP — When creating a player character, these text boxes are disabled. When creating any other entity, you can change the values that are calculated by generating stats.

Figure 2-8

In later chapters, we’ll add text boxes to base and current values for skill points (SP) and mana, which will also be disabled.

Def/Off/Misc Bonuses — The entry for these stats is done in a separate dialog that is launched by clicking the button with the ellipsis next to the combo box. The combo boxes are used to show the data that was entered in the dialogs. Here’s the dialog that’s used:

Figure 2-9

Damage Resistance/Weakness — Another dialog is used to input these:

Figure 2-10

Portrait Filename — Clicking the button next to the text box displays a standard file selection dialog. The filename that is chosen is put into the text box.

Stats — Clicking the button next to the Stats combo box displays a form to randomly generate stats:

Figure 2-11

Any race and class modifications are passed to the form, along with any existing stats:

Listing 2-18


private void btnStats_Click(object sender, EventArgs e)
{
Dictionary<string, int> raceStatMods = null;
Dictionary<string, int> classStatMods = null;

if (cboRace.SelectedIndex > 0)
raceStatMods = _races[cboRace.SelectedIndex –
1].GetStatMods();

if (cboRace.SelectedIndex > 0)
classStatMods = _classes[cboClass.SelectedIndex –
1].GetStatMods();

StatsGeneratorForm frm = new StatsGeneratorForm(_stats,
raceStatMods, classStatMods);

if (frm.ShowDialog() == DialogResult.OK)
{
_entityStats = frm.GetStats();

cboStats.Items.Clear();

for (int i = 0; i < _entityStats.Count; i++)
{
cboStats.Items.Add(((Stat)_stats[i]).Name + ": " +
_entityStats[i].CurrentValue.ToString());
}

//get strength and constitution values
int str = 0;
int con = 0;
DieType hitDice = DieType.d6; //default hit dice

if (cboClass.SelectedIndex > 0)
hitDice = _classes[cboClass.SelectedIndex - 1].HPDice;

txtBaseHP.Text = (GlobalFunctions.GetRandomHPDieTotal(
Convert.ToInt16(txtLevel.Text), hitDice) + ((str + con) /
2)).ToString();

txtCurHP.Text = txtBaseHP.Text;

}

frm.Close();
}

Notice that you can force the random number generator to give you values within a certain range. You’ll probably want to keep the maximum value, assuming you use the 1-100 system, but you can create entities that have a chance of being relatively weak or strong simply by changing the value in the Minimum Stat Amount text box. Clicking the Generate button fills in the grid and you can see the final stat values in the rightmost column.

As entities are added, their names are added to the combo box at the top of the child form. Navigation from entity to entity is done using this combo box.

Notice that you can load objects from another game using the Load menu item on the main form or any child form. This allows you to reuse stats, races, classes, etc., from another game.

Summary

This concludes our introduction to entities. Your game is only going to be as interesting as the people or monsters that populate your game world, so make sure you spend as much time as possible getting them the way you want them.

Feel free to take a look at the sample entities that are provided in the folder for Chapter 2 in the book’s downloadable files, as we’ll be using them in the future. Over the course of the next several chapters, we’ll expand the class to incorporate skills, items, and magic — all the things that make an RPG cool. We’ll start in the next chapter with skills, so our entities can actually do things.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.111.208