Chapter 3
Skills

A character is standing at the edge of a swift-flowing river. How is the player going to get him across? He could swim, although it would be risky due to the current and the weight of his armor and equipment. He could try hopping across the stones that are lying in the river. Of course, they may be slippery and he’ll risk falling and possibly injuring himself or drowning. He could try to use the rope in his backpack to lasso a tree stump across the river and pull himself across. If he’s tied to the end of the rope, he won’t risk getting swept away.

The one common feature of these different ways for the character to try to cross the river is that they all require him to use a skill — swimming, balancing, or rope handling. If the character can’t swim well or at all, he’ll drown if he tries to swim across. If he’s an Olympic-caliber swimmer, he’ll probably be able to swim across even if he’s carrying all of his equipment. If he can barely walk a straight line without losing his balance, he probably won’t be able to jump from rock to rock without falling in. If he can’t even tie a knot in a rope, he more than likely won’t be able to lasso a stump across the river. The better the character is at one or more of these skills, the quicker and easier he’ll be able to get across.

So how will the player know which is the best way to get the character across? He checks the character’s skill list. The skill list is built by the player to reflect the abilities that this type of character would normally have. A fighter would have skills with weapons such as sword, ax, and bow, as well as armor skills, allowing him to fight in heavy armor without difficulty. A thief would have skills in stealth, climbing, tumbling, lock picking, and setting and detecting traps. A ranger would have woods-based skills — tracking, outdoor survival, and foraging and animal skills such as taming, identifying footprints, etc. A mage would probably know languages, lore related to magical items, and how to study to learn spells easier.

Usually a character’s skills get better as they are used, and they stop increasing and possibly even decrease a little if the character doesn’t use them for an extended period of time. This is the way the games in the Dungeon Siege series work. If the player decides during the course of the adventure that it would be better to start using a bow instead of a sword so enemies can be dispatched from a distance and not have his character take damage, he can. He just has the character find or buy a bow and start using it. He may miss a lot at first and not do much damage when he does hit something, but after a while he’ll get better, hitting his target more often for more damage and at a greater range.

Another popular method of skill advancement is to give a character a set amount of points to buy skills after a level increase. Skills are bought in terms of “ranks” — a number that denotes how many times the player has bought that skill. Each rank costs a certain number of skill points, based on several things such as the class of the entity, stats, race, etc. (I’m using “ranks” here instead of “levels,” which you might see in some RPGs, to avoid confusion with levels in which a character advances.) Allowing the player buy skills means that a character could improve in skills that he didn’t even use during the adventure, depending on how you set up your skill system. While this isn’t the most realistic way of handling skills, it is popular. It’s also much easier to implement, as you don’t have to track everything the player does to determine if a skill should go up or down. If you look at it as the character practicing a skill during downtime between adventures, then a character learning a skill that he doesn’t already have is feasible.

One option to using a point-based system is to implement a skill tree, requiring a character to be proficient with one skill before learning a more complicated, related skill. A classic example of this is the Diablo series. We can see how this works in this example of a fighter’s skill tree:

Figure 3-1

Early on in the game, most of the skills are blocked. My character has learned the Bash skill and has three ranks in it, increasing the damage he can do with a bash attack. However, the other skills are still unavailable as the character’s level isn’t high enough.

Using a skill tree means a character can only learn specific skills even when using a point-based system. However, skill trees can be difficult to implement and even more difficult to balance. If you create an adventure that assumes a character will have a certain skill down one branch of the tree and the player learns skills down another branch, the game could become unwinnable.

We’ll use the point-based system without the skill tree but with some other restrictions during the course of this book because of its ease of implementation. But feel free to implement the usage-based method if you feel comfortable enough in your coding skills.

The Skill Class

We’ll keep track of skill data with a global list of class objects:

Listing 3-1


public class Skill
{
private int _id;
private string _name;
private string _description;

private SkillType _type;

private int _opposingSkill;

private bool _alwaysOn;
private Dictionary<string, string> _costs;
private Dictionary<string, int> _classBonuses;
private List<MinMaxBonus> _levelBonuses;
private Dictionary<string, int> _raceBonuses;
private Dictionary<string, MinMaxBonus> _statBonuses;

private string _iconFilename;

[NonSerialized]
private Texture2D _icon;
}

The _name and _description members I believe are self-explanatory. A description of the skill would be shown to the player in order for him to determine what the skill does so he’ll know if it’s something he wants his character to learn. Depending on the depth of your skill system, you might implement skills with names that some players might not know, say “Spelunking.”

The _id property is used as a lookup for the skills that a character knows. We’ll see how that works shortly.

We’ll have three types of skills, shown in the SkillType enum:

Listing 3-2


public enum SkillType
{
Defensive,
NonCombat,
Offensive
}

For most RPGs, these three would probably be all you need. I’m sure you could break down noncombat skills more, but for our purposes this is enough. The system we’re building is meant to be pretty generic to allow you to use it as a base for your own systems, not as a system for every RPG that could be developed.

Some skills will be matched up against other skills in determining the success or failure of the action that uses the skill. For example, a thief using his Hide skill would be opposed by the Perception skill of anything that comes within visual range. The longer other entities are around the hiding thief, the higher the chance that the thief will be discovered. The _opposingSkill member will help with this. If a value for the _opposingSkill property isn’t set, we can use the opposing bonus for the target entity (Defensive for an offensive skill and vice versa, and Miscellaneous for noncombat skills).

The _alwaysOn member denotes a skill that the player doesn’t have to specifically select and use. The skill is always being used by the character. The aforementioned Perception skill is a good example. The character is assumed to be always looking around and has a chance to notice something that he’s looking for or something out of the ordinary. In addition, you could use the Perception skill to give a character the ability to find traps, or notice footprints or markers set by other entities to give information to his friends (stones laid out in the shape of an arrow to give a direction to follow, for example).

The_costs member is a list of pairs of key/value strings. The value item tells how many skill points one rank of the skill costs. In this case, the key is the name of an entity class, such as Fighter, which can be used to look up information in another list. The cost for a skill can take several forms as shown below (where <n> is a number):

<n> — One rank of the skill may be bought each time the character gains a level

<n>/* — As many ranks of the skill may be bought as the player desires (as long as the character has skill points remaining) each time the character gains a level. Each rank costs the same amount as defined by <n>. This allows a character to quickly gain a skill (at the cost of not learning other skills usually). Usually skills with this type of cost are the primary skills for an entity class — melee weapons for fighters, spellcraft for characters that use magic, or pickpocketing or lock picking for thieves.

The _classBonuses member allows the developer to give bonuses to entities of certain classes when using a skill. You would probably use this for general skills like Perception rather than the primary skills for a class since the bonuses for those skills would quickly add up and potentially cause balancing problems. A thief or mage would probably be better at Perception than a fighter for example, even if both had the same ranks in the skill. You can use this member to give those classes a bit of an edge in a skill.

The _levelBonuses member is similar to _classBonuses except it works off the character’s level. A bonus amount is specified for a level range so you could set up a skill with a bonus of 1 for levels 1 to 5, 2 for levels 6 to 10, etc.

The _raceBonuses member works exactly like _classBonuses, and the _statBonuses member works exactly like _levelBonuses. With all these bonuses, there’s a lot of flexibility and options for you to enhance skills. With this flexibility, however, comes additional time that you’ll have to spend balancing to make sure no skill allows the character to be overpowered at higher levels.

Each of the five members just discussed has four accessor functions — Add<x>, Remove<x>, Get<x>, and a function to return the entire list or dictionary. The last is needed for serialization purposes. These functions are implemented almost exactly the same for the five members, so let’s take a look at just one, the functions for the _costs member:

Listing 3-3


public bool AddCost(string id, string cost)
{
if (_costs == null)
_costs = new Dictionary<string, string>();

if (!_costs.ContainsKey(id))
_costs.Add(id, cost);
else
return false;

return true;
}

public void RemoveCost(string id)
{
if (_costs != null)
_costs.Remove(id);
}

public string GetCost(string id)
{
string cost = "";

if (_costs != null)
_costs.TryGetValue(id, out cost);
return cost;
}

public Dictionary<string, string> Costs
{
get { return _costs; }
}

The AddCost function initializes the member if it hasn’t already been done and, if a cost for that class hasn’t already been added, adds a new Dictionary item. If a cost already exists, the function returns false; otherwise it returns true.

The RemoveCost function and property accessor are straightforward. GetCost is interesting only in the use of the TryGetValue method. Instead of having to use exception handling to trap using an invalid key, the method handles that for you. We initialize a string variable and pass it to the method. If the key doesn’t exist, we end up with an empty string. It’s up to the code that calls the function to deal with that empty string. Since the cost could take on several forms, it has to parse the string anyway.

The_iconFilename member will allow the skill to be accessed during the game by specifying the name of a graphic that will be placed in the game’s GUI. The player can click that graphic or press a key corresponding to that graphic’s location in the GUI. When the game is played, the _icon member will hold the loaded graphic.

The main function for the class is the Use function. There’s a lot going on in it, so let’s take a look:

Listing 3-4


public bool Use(ref Object target, ref Entity entity, Difficulty
difficulty, out int result)
{
int roll;
int bns = 0;

roll = GlobalFunctions.GetRandomNumber(DieType.d100);

roll += (short)difficulty;

if (_opposingSkill > 0)
{
if (((Entity)target).HasSkillByID(_opposingSkill))
roll -= ((Entity)target).GetSkillValueByID(_opposingSkill);
}
else
{
switch (_type)
{
case SkillType.Defensive:
{
if (target is Entity)
roll -= ((Entity)target).GetTotalOffBonus();

break;
}
case SkillType.NonCombat:
{
if (target is Entity)
roll -= ((Entity)target).GetTotalMiscBonus();

break;
}
case SkillType.Offensive:
{
if (target is Entity)
roll -= ((Entity)target).GetTotalDefBonus();

break;
}
}
}

foreach (KeyValuePair<string,int> kvp in _raceBonuses)
{
if (entity.RaceID == kvp.Key)
{
bns += entity.Level * kvp.Value;
break;
}
}

foreach (KeyValuePair<string, int> kvp in _classBonuses)
{
if (entity.ClassID == kvp.Key)
{
bns += entity.Level * kvp.Value;
break;
}
}

//Calculate level bonus
if (_levelBonuses != null)
{
foreach (MinMaxBonus bonus in _levelBonuses)
{
if (bonus.IsValueInRange(entity.Level))
{
bns += bonus.Amount;
break;
}
}
}

//else
//{
// short level = entity.Level;
// //use default
// switch (level)
// {
// case 1:
// case 2:
// case 3:
// case 4:
// case 5:
// {
// bns = (short)(10 * level);
// break;
// }
// case 6:
// case 7:
// case 8:
// case 9:
// case 10:
// {
// bns = (short)(50 + (5 * (level - 5)));
// break;
// }
// case 11:
// case 12:
// case 13:
// case 14:
// case 15:
// {
// bns = (short)(75 + (3 * (level - 10)));
// break;
// }
// case 16:
// case 17:
// case 18:
// case 19:
// case 20:
// {
// bns = (short)(90 + (2 * (level - 15)));
// break;
// }
// default:
// {
// bns = (short)(100 + (1 * (level - 20)));
// break;
// }
// }
//}

roll += bns;

result = roll;

return (roll >= 100);

}

The function is passed the entity attempting to use the skill, the object against which the skill is being used, the difficulty of the attempt, and a reference to a variable that will hold the result of the attempt. The calling code will use the attempt result however it wishes; all we do is calculate the result. The difficulty of the attempt modifies the calculation by adding the value of that difficulty from the Difficulty enum:

Listing 3-5


public enum Difficulty
{
Impossible = -50,
VeryHard = -25,
Hard = -10,
Normal = 0,
Easy = 10,
VeryEasy = 25
}

Since we are going to define a skill as having succeeded if the result of the calculation is equal to or greater than 100, the more difficult the attempt, the lower the number for the difficulty of that attempt. This number is added to a random number that is generated from 1 to 100.

Next, we use the type of skill being used to get the opposite value from the target of the attempt and subtract that value from the number rolled. For example, for an offensive skill like an attack, we get the total value of the _defBonuses list:

Listing 3-6


public short GetTotalDefBonus()
{
short total = 0;
Bonus bns;

if (_defBonuses != null)
{
for (short i = 0; i < _defBonuses.Count; i++)
{
bns = (Bonus)_defBonuses[i];
total += bns.Amount;
}
}

return total;
}

The Race and Entity class bonuses are calculated if they are set for the skill. We’ll see how both of these are set up a bit later in the chapter, but for now it’s sufficient to know that certain races and entity classes get a small bonus (usually only 1 or 2) per level of the entity to signify the advanced knowledge or training in that skill that a character of that race or entity class would have. For example, an elf growing up in the woods would normally learn tracking, moving silently, and other woodcraft skills as part of his childhood, playing games with his friends and such. The race bonus would allow that to be expressed in the game without having to require a complete background of the character.

We then calculate the total of any level bonuses and add that to the current result. If there is no level bonus specifically for that skill and you would like to apply a generic level bonus, simply uncomment the code in the first part of the if statement and modify it as you wish.

If the result of all these values is 100 or greater, the use of the skill succeeds. We assign the value to the result variable and return a boolean so the calling code knows how to apply the value in the result variable and anything else it needs to do.

So we have a Skill class that will contain the data for the skill, but how do we track what skills the entity has learned? This is fairly easy, actually. We’ll add some new members to our Entity class, along with functions to deal with the member that will hold these skills:

Listing 3-7


private Dictionary<int, int> _skills;

private int _curSP;
private int _baseSP;

public void AddSkill(int id, int ranks)
{
if (_skills == null)
_skills = new Dictionary<int, int>();

_skills.Add(id, ranks);
}

public void SetSkills(Dictionary<int, int> skills)
{
_skills = skills;
}

public bool HasSkill(int key)
{
if (_skills != null)
return _skills.ContainsKey(key);
else
return false;
}

The _skills member uses the _id member of the Skill class as the key, and the value for each entry in the dictionary is the number of ranks the character has in that skill. One modification to this setup could be to use a structure as the Value member of the Dictionary item that contains two integers — one for the base number of ranks and one with the current number of ranks. This would allow the number of ranks to temporarily change, say, by magic of some type. A Potion of Battle Frenzy could increase all the combat skills of an entity, for example. We’re not going to go to that level of intricacy in our code, but feel free to implement something like that if you wish.

The total number of skill points for a character is calculated by dividing each stat by 20 and adding these values together. You can adjust this number to your liking, but it seems to give a good number of points. As with everything in an RPG, a lot of testing will have to be done to make sure the character has enough skills and enough ranks in those skills to get through whatever adventure you put together.

Now that we’ve got some code in place to handle skills, let’s take a look at a list of skills that might be available for the player to choose from:

Melee Weapons — The ability to fight and defend with a weapon. This will be used to calculate hitting an opponent with a weapon. It could also be used to allow a character to parry an attack, and could be broken down into types of weapons or even specific weapons. You could even allow a character to use a similar weapon with some or all of the skill that he has with another weapon. For example, if a character has six ranks of skill with a long sword, you could allow him to use a broadsword with a percentage of the ranks he has with the long sword, since the weapons are similar. This makes tracking the skills more complex, but allows for a little more realism in your skill system.

Ranged Weapons — The ability to use any type of bow or crossbow. The more skill a character has in this, the better the chance of hitting a target at a greater range. This skill could be broken down between crossbow and regular bows since their operation is so different. For the purposes of the game that we are creating, the two types of ranged weapons will not be differentiated.

Armor — The ability to perform actions in armor, such as fighting, riding, climbing, etc. This will only apply to fighting in our case. A character may fight in armor, but will do so at a penalty without Armor skill. The heavier the armor, the more Armor skill a character will need to offset the penalty.

Shield Use — The ability to fight and block attacks with a shield. This will add to the character’s defensive ability. A character may fight with a shield without having Shield Use skill, but will do so at a significant penalty.

Body Development — This represents a character’s training to develop his body to allow him to perform better in battle. This doesn’t represent training like weight lifting (which would increase the character’s Strength stat), but things like endurance training and the ability to resist pain and still function. In our game, this will mean an increase in the hit points a character has.

Unarmed Combat — The ability to fight and defend without a weapon. This skill encompasses all unarmed combat disciplines. If you want a more realistic combat system, you could implement specific skills for the various styles, such as karate, kung fu (“I know Kung Fu!” — sorry, couldn’t resist), boxing, wrestling, etc. This would mean a significant amount of testing and balancing to ensure no specific style was over- or underpowered, however. It would also mean a lot more work for your artist or modeler/animator to make the visuals match the style being used.

Dodge — The ability to dodge an attack, be it from either a melee or ranged weapon. This skill will give the character a significant increase in his defensive ability for one round of combat but will mean the character cannot attack. It will also add a small amount during regular combat as it’s assumed that even while attacking, a character will attempt to dodge, and his Dodge skill will make him better at it than a character without training in the skill.

Spellcraft — The ability to learn and cast spells. Unlike some other skills, a character without at least one rank in Spellcraft cannot cast spells. The more a player invests in this skill, the less chance the spell will fail.

Stalk — The ability to follow another character without that character noticing. This is a kind of combination Hide and Move Silently. This requires the character to avoid the line of sight of the entity being stalked. If the entity being stalked turned to face the direction of the stalker he would have a chance to know he’s being stalked by using his Perception skill.

Hide — The ability to avoid detection by blending into the environment. Since a thief spends most of his time practicing this skill where he works (cities), it may be learned by a thief for use only in a city environment. The opposite is true for rangers, as they spend most of their time in the woods or rural environments. You could modify this to allow a thief or ranger to use some portion of this skill in whatever environment they don’t normally use it in, but we’ll leave the restriction in for our purposes.

Detect Trap — The ability to notice a trap. This will give the character a chance to notice a trap whenever he passes near it. How this skill is displayed in your game could vary. The area around the trap might glow, a message could be displayed, etc. For our game, we’ll show a graphic that the player can click on to disarm the trap. In a multiplayer game, you could require the player to somehow inform other players of the trap’s existence. If the player is playing an evil character, he might not inform the group, leading them into it and allowing them to be weakened or killed to allow the player to gain their loot.

Disarm Trap — The ability to render a trap harmless. The character learns how different traps work and therefore how to circumvent their operation. Note that a trap must be detected before this skill can be used and the character must have the proper tools to disarm the trap (for our purpose, a universal toolset will do the job).

Move Silently — The ability to move without making noise. This includes knowing how to silence equipment that is being carried and how to traverse terrain so that no noise is made by features of the terrain (branches on the ground, dried leaves, creaky floorboards, etc.). This will slow down the rate at which a character moves. This is useful for sneaking past a guard with his back toward the character. It may be used in conjunction with Hide to get past a guard that has line of sight of the character’s path, but will mean the character has to move at a crawl.

Pick Lock — The ability to pick a nonmagically locked door, chest, etc. This presumes that the thief has the requisite lockpicks or similar tools available, although using the latter will incur a penalty.

First Aid — The ability to stop bleeding or set broken bones, although the specifics are not necessary to state. This will allow a character to help another character regain a limited number of hit points.

Perception — The ability to notice something. This will be used in the game as needed to see if a character notices something out of the ordinary — traps, secret doors, etc. As with Detect Trap, how the information is made known to the player could vary.

Many RPGs include dozens or possibly even a hundred or more skills for a character to learn. Normally the list is automatically limited somehow, as we’ve briefly touched on already, so the player isn’t overwhelmed by the total number.

Any character can attempt to learn any skill, but as we’ll see from the following table it will be anywhere from somewhat difficult to nearly impossible for some characters to learn some skills depending on their class.

Table 3-1

Let’s add some race bonuses to some of the skills:

Table 3-2

Note that not all races get modifications to skills, and not all skills have racial modifications. Next, some stat bonuses:

Table 3-3

Notice that there are bonuses as well as penalties for skills depending on how high or low the stat is. This is only our first stab at this data. Based on play testing, the data will more than likely need to be modified to maintain balance.

Creating and Storing Skill Data

As usual, XML files will be used to store the data for the skills. The data will be loaded and saved using class serialization as we did with the Entity object in the last chapter. As with the data for classes, races, stats, etc., these XML files will be incorporated into our sample game later on and will be compiled by the content pipeline into binary files that the game will use. For now, though, keeping the data in XML files will allow us to tweak the data without needing to load it into an editor. This will be useful during testing when the data will change to balance everything.

To create skills, a new form is added to the toolset:

Figure 3-2

The button and menu item to access this form are only enabled if there is class and race data available since the cost for a skill is based on the entity class and we’ve established that races affect skills. Since races and classes can’t be created without the existence of stats, we don’t need to check for stats.

The form works similarly to the ones used to create the previous sets of data. A combo box holds the skills that have been created and allows navigation to these items if changes need to be made. The tabs holding costs and bonuses are arranged to allow selection and entering of values on the left and items that have been added to the current skill on the right. For as much data as is needed for a skill, I think the interface is fairly easy to use and allows skills to be created quickly.

Collections of the data created in each of the tabs are kept as members of the form along with a temporary Skill object and the ID of the skill being worked on:

Listing 3-8


private Dictionary<string, string> _costs;
private Dictionary<string, int> _classBonuses;
private List<MinMaxBonus> _levelBonuses;
private Dictionary<string, int> _raceBonuses;
private Dictionary<string, MinMaxBonus> _statBonuses;

private List<KeyValuePair<string,int>> _opposingSkillControlSource;

private Skill _skill;

private int _curID;

This is done so that the data being stored in the controls on the form does not have to be parsed when saving the Skill object to the list of skills. Saving the data for the skill being worked on becomes fairly easy since we have all the data at hand:

Listing 3-9


private void FillSkillObject()
{
_skill.Name = txtName.Text;
_skill.Type = (SkillType)cboType.SelectedIndex;
_skill.Description = txtDescription.Text;
_skill.OpposingSkill = (int)cboOpposingSkill.SelectedValue;
_skill.AlwaysOn = chkAlwaysOn.Checked;
_skill.IconFilename = txtIcon.Text;

//add costs
foreach(string key in _costs.Keys)
_skill.AddCost(key,_costs[key]);

//add class bonuses
foreach (string key in _classBonuses.Keys)
_skill.AddClassBonus(key, _classBonuses[key]);

//add level bonuses
foreach (MinMaxBonus bns in _levelBonuses)
_skill.AddLevelBonus(bns);

//add race bonuses
foreach (string key in _raceBonuses.Keys)
_skill.AddRaceBonus(key, _raceBonuses[key]);

//add stat bonuses
foreach (string key in _statBonuses.Keys)
_skill.AddStatBonus(key, _statBonuses[key]);

}

private void btnAdd_Click(object sender, EventArgs e)
{

FillSkillObject();

if (_skills == null)
_skills = new List<Skill>();

_skills.Add(_skill);

cboSkills.Items.Add(_skill.Name);

_curID++;

_skill = new Skill(_curID);

SetupFormForEntry();

_saved = false;
}

Saving the list of skills looks almost exactly like saving the other collections of data:

Listing 3-11


private void SaveSkills()
{
try
{
XmlWriter writer;

XmlWriterSettings settings = new XmlWriterSettings();

settings.Indent = true;

foreach (Skill skill in _skills)
{
writer = XmlWriter.Create(Application.StartupPath +
@"Games" + _gameName + @"skills" + skill.Name +
".xml", settings);

IntermediateSerializer.Serialize(writer, skill, null);

writer.Close();
}

_saved = true;

}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}

Loading the skills when the form is displayed is just as easy. A new function needs to be added to the GlobalFunctions class:

Listing 3-11


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

Skill skill = IntermediateSerializer.Deserialize<Skill>
(reader, null);

reader.Close();

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

This function is then called for each skill file that exists in the skills folder for the game:

Listing 3-12


string[] files = Directory.GetFiles(Application.StartupPath +
@"Games" + _gameName + @"skills");

_skills = new List<Skill>();

foreach (string file in files)
{
Skill skill = GlobalFunctions.LoadSkill(file);

if (skill != null)
{
_skills.Add(skill);

cboSkills.Items.Add(skill.Name);

if (skill.ID > _curID)
_curID = skill.ID;
}
}

The rest of the code in the SkillsForm class is GUI navigation handling code. The actual code to handle the data is fairly minimal, as it should be. Working with objects helps us with keeping our code outside the object class to a minimum.

Learning Skills

There are now skills for entities to learn, but we need a way to allow them to learn. Two interfaces will need to be created to do this, one to allow the developer to have NPCs, monsters, and creatures learn skills (yes, even creatures found in the wilderness “learn” skills to allow them to attack the poor, hapless player characters) and one to allow the player to select the skills for his character during the course of the game. The latter will be covered later on in the book once we have a game up and running and a GUI to allow the player to select the skills for his character. The former is fairly simple to implement; we simply add one form and a couple of controls to the EntitiesForm we already have.

The EntitiesForm shown in Figure 3-3 has been updated to add the new controls necessary for showing the selected skills.

The base and current number of skills points are shown above the Skills combo box. The combo box lists the skills that have been learned and the number of ranks for each. Clicking the button next to the combo box displays the SkillsSelectionForm, as shown in Figure 3-4.

Figure 3-3

Figure 3-4

Simply select a skill to learn and click the >> (Add) button for each rank. As long as the entity has enough skill points remaining to learn a rank of the selected skill, the >> button will be enabled. Since this is a developer tool, there might be a need to remove ranks from a skill for an entity. Simply click the skill in the Entity Skills list box and click the << (Remove) button to remove ranks.

A couple of pieces of data get passed to this form in order for it to do the necessary calculations:

Listing 3-13


private void btnSkills_Click(object sender, EventArgs e)
{
SkillsSelectionForm frm = new SkillsSelectionForm(_skills,
_classes[cboClass.SelectedIndex - 1].Name, _entitySkills,
Convert.ToInt16(txtCurSP.Text));

if (frm.ShowDialog() == DialogResult.OK)
{
_entitySkills = frm.SelectedSkills;
txtCurSP.Text = frm.SkillPointsLeft.ToString();

cboSkills.Items.Clear();

foreach (KeyValuePair<int, int> kvp in _entitySkills)
cboSkills.Items.Add(_skills[kvp.Key].Name + " - " +
kvp.Value.ToString());
}
}

Since we’ve already loaded the skills for the current game, we simply pass them to the form so it can show the available skills to learn. The EntityClass value for the current entity is needed to calculate the cost for each rank of whatever skill is selected. Any skills that the current entity already has need to be shown, so the variable holds the collection of current skill IDs and the number of ranks for each skill is passed. Finally, the amount of skill points the entity currently has is needed in order to limit what skills and the number of ranks of them the character can learn.

If skills are selected and ranks are learned, the necessary variables and controls are updated. When the entity is added to the entity list in the EntitiesForm or updated if it already exists, the data in these variables are used.

Using Skills

Okay, so we have an entity that has learned some skills. How is he going to use them? Obviously we need to write some code to do so, but what’s the best way? If the game is to run on the Xbox 360 (and we’ll assume that’s the case at some point down the road), note that scripting is basically out of the question since much of the .NET Reflection namespace we can use to execute text as code at run time is not available in the modified compact framework that XNA GS games run under. Also, nonmanaged code can’t be run in XNA GS games, so using one of the many scripting languages is not an option. We’ll need to stick with plain C# code. The job is easily done with C#, but it means we’ll have to go with a less flexible solution.

We’ll add a SkillManager class with some stub functions that will be filled in as we go through the upcoming chapters. The members of this class will be used to figure out what happens when a skill use succeeds. One function will be called and from there the specific skill will be used to call a function specifically for that skill. The function that will be called first looks like this at the moment:

Listing 3-14


public static void UseSkill(int key, ref Object target,
ref Entity entity, int result)
{
string name = GlobalData.Skills[key].Name;

switch (name.ToLower())
{
case "unarmed combat":
{
UseUnarmedCombat(ref target, ref entity, result);
break;
}
case "melee weapon":
{
UseMeleeWeapon(ref target, ref entity, result);
break;
}
case "ranged weapon":
{
UseRangedWeapon(ref target, ref entity, result);
break;
}
case "pick lock":
{
UsePickLock(ref target, ref entity, result);
break;
}

}
}

We’ll call this function from the EntityClass:

Listing 3-15


public void UseSkill(int key, Difficulty difficulty)
{
int result;
Entity entity = this;

if (GlobalData.Skills[key].Use(ref _target, ref entity,
difficulty, out result))
{
SkillManager.UseSkill(key, ref _target, ref entity, result);
}
}

We’ll be keeping a static list of skills. The class that contains this list is in the GlobalFunctions.cs file:

Listing 3-16


public class GlobalData
{
public static Dictionary<int, Skill> Skills;
}

This list will be loaded when the player is ready to start the game. The list can contain all the skills that can be learned or just the skills that the entities in the current level know. You would load the rest of the skills when you need to (for example, when a character has gained a level and wants to learn a new skill). This can be determined based on the number of skills in your game. If you have hundreds of skills, you probably won’t want to load them all.

We pass in the result from the skill check to the SkillManager so that we can adjust what happens if the skill use either succeeded spectacularly or failed miserably.

We won’t need functions for every skill since using some skills means what’s supposed to happen doesn’t (for example, a successful Dodge means an attack misses). Some things we can guess what’s going to happen if the skill use succeeds, so we’ll add some commented out placeholder code that we’ll implement in coming chapters:

Listing 3-17


private static void UseUnarmedCombat(ref Object target,
ref Entity entity, int result)
{
int damage = 0;

//we'll adjust damage in the Combat chapter

((Entity)target).Damage(damage, DamageType.Crushing);
}

private static void UseMeleeWeapons(ref Object target,
ref Entity entity, int result)
{
//get damage from weapon entity is using
//need to figure out if weapon is piercing or slashing
//((Entity)target).Damage(damage, DamageType.);
}

private static void UseRangedWeapons(ref Object target,
ref Entity entity, int result)
{
//get damage from weapon entity is using
//((Entity)target).Damage(damage, DamageType.Piercing);
}

private static void UsePickLock(ref Object target, ref Entity entity,
int result)
{
//((Chest).target).Locked = false;
}

You should be able to see the way we’re going here. Since we know what skill is being used, we can make some assumptions in some cases on things; like the target being passed to the UsePickLock function being a Chest object or what should happen in the function (UseUnarmedCombat is always going to cause crushing damage to the target). We’ll also have to assume that the code calling the functions is only going to do certain things, such as not allowing the player to attack something that’s not an entity. Since we’ll be writing the code calling these functions, it’s a pretty safe assumption. If this code were going to be released for others to use, we might want to be a little more careful in our assumptions.

Summary

Now that our entities have learned some skills, they need something to use them with. In the next two chapters, we’ll learn how to do this by creating a magic system that allows entities to learn to cast spells and by developing classes that allow all sorts of items to be created for entities to use. We’ll then add some functionality to allow the entities to use these spells and items on one another.

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

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