Chapter 6
Combat (or “It ’s Clobberin’ Time”)

Taran looked at the oncoming horde of goblins and sighed. This was the third time they’d tried to ambush his group as they traveled through the Darian Forest. You would have thought that they could have come up with a better plan of attack than just rushing them from the front, even as stupid as goblins were reputed to be. “Oh well, at least they’re saving me the time to have to come up with a strategy,” he thought. He and Karil drew back on their longbows as Farthon started chanting a spell. Tomlin was trying to slide off to the side to get behind the group once they closed, as he always did. Taran had never seen the halfling thief attack an opponent head-on since he’d known him. Soral was off to the other side, lightly swinging his mace while reciting a prayer to his god. Taran never could remember the god’s name, but he always seemed to respond to Soral’s prayers, granting the group some extraordinary power during battle. The cleric’s healing powers had barely been necessary the last two times the group had encountered the bands of creatures.

As the goblins came within range, Taran and Karil let loose with their arrows and each quickly grabbed another arrow from his quiver to get in another shot before the band closed to melee distance. Farthon finished his spell at about the same time and a fireball soared over Taran’s head to explode in the midst of the group of goblins, leaving little more than a stinking mass of burned flesh. An eerie glow surrounded the small group of adventurers as Soral’s god again granted his blessing to the group. Two more goblins dropped from arrows as they closed to within sword range. Taran and Karil barely had time to pull their swords from their scabbards as the screaming goblins fell upon them.

The fight was short and bloody, at least for the goblins. The fighters’ swords chopped appendages from goblin bodies. The thief, Tomlin, managed to fell a couple of goblins with well-placed attacks from the rear. Soral’s mace whirled around him, crushing any goblin that happened to get close enough. Even the mage, Farthon, got into the action, sticking his long dagger into the chest of a goblin that dared approach him. Obviously the goblin was mistaken in his belief that the mage was defenseless without his magic.

After catching their breath and taking care of the minor wounds they had received during the encounter, the group continued on into the forest, keeping an eye out for more of the creatures that stood between them and their goal.

Since it seems that a character can’t take a dozen steps in some RPGs without encountering some type of creature or NPC that wants to bash in his skull, a character will need to know how to defend himself at the least and hopefully deal out some damage of his own. There are almost as many types of combat systems as there are RPGs. Some are fairly simple, while others are incredibly complex, almost requiring an advanced mathematics degree to figure out. Most hide the calculations from the player though, some even going so far as to show actual damage on the entities and hiding the numbers, leaving the player to figure out how badly hurt his character and opponent are in order to determine whether to run or continue fighting. You can normally divide them into two categories, however — real time and turn based.

Real time means that characters are able to attack as fast as they can, given the type of weapon they’re wielding and other factors. Depending on how deep the combat system is, there could be any number of additional factors that affect the character’s rate of attack, such as the type of armor the character is wearing (heavy armor will slow down the character), one or more of the character’s stats (Strength, Agility, and Dexterity are some of the favorites), and the amount of skill the character has with his weapon. With real-time combat the player normally only has to click on the target and the character will start attacking until that target is dead (or the character dies) unless you intervene. Some RPGs allow you to queue up special attacks or tactics while the character is fighting. For example, in the story at the beginning of the chapter several characters are fighting a group of goblins. Normally the character would only be able to attack one at a time. If the character has the ability to attack more than one target with a small penalty for each attack, the player may want to use this special attack when one or more of the goblins were hurt and less able to defend themselves. The player selects this attack at an appropriate time and as soon as the character is able he’ll use the special attack, and then go back to his normal attack. That’s assuming the character is able to use it at the time. If only one goblin were in range, the character would just do a normal attack. Neverwinter Nights is one example of an RPG that does this. In the following screenshot, the actions that are queued up are represented by icons in the upper left-hand corner.

Figure 6-1

A turn-based combat system usually means that one combatant will attack and then the other will have his turn. The order of combat is usually determined by a roll of the dice (called an initiative roll). This roll can be modified by stats or some other factor. Dungeons and Dragons worked this way. Combatants would compare their Dexterity stat, and whichever had the highest would strike first. Combat is very structured in a turn-based system. Usually magic spells are cast first and then any ranged combat is resolved. Lastly, melee attacks, if possible, are calculated. All of these actions together are called a round. In every round a player could decide to have his character do something different and usually has plenty of time to make that decision.

In this chapter we’ll create a turn-based system where the order of action for each turn is determined by multiple factors — the Dexterity stat of the entity, the type of weapon he’s using and its weight, any bonuses that are affecting the character positively or negatively, etc. We’ll also create a combat simulator application that can be used to test combat without having to start an actual game, allowing the developer to quickly fine-tune all the data that pertains to combat (item and entity stats, spell stats, etc.).

In our game we’ll represent the different actions an entity can take during combat using an enum:

Listing 6-1


public enum AttackTypes
{
Normal,
Parry,
Riposte,
CalledShot,
LongRangeShot,
Disengage
}

If a player doesn’t specify an attack type, it defaults to Normal. The remaining attack types affect the player’s offensive or defensive bonus, or both. Here’s a description of the attack types:

Normal — Nothing fancy, just the basic type of attack with the weapon being wielded. The attacker is just looking to do damage but not leave himself completely open for a counterattack.

Parry — The character sacrifices his upcoming attack to attempt to block his attacker’s blow. A bonus is added to the character’s defensive bonus for the current or next round, depending on when the character attacks.

Riposte — The character attempts to both block his attacker’s blow and get in a quick retaliatory attack of his own. A small bonus is added to the character’s defensive bonus and a slight penalty is added to the character’s offensive bonus. While this type of attack is normally done with a sword, we’ll use it for any melee weapon. Note that this attack and the Parry attack shouldn’t show up with ranged weapons.

CalledShot — The character attempts to attack a specific part of his attacker’s body. Usually this is done against an opponent who is weaker or when a player thinks he can kill the attacker with that shot. A CalledShot attack is done at a sizable penalty to the offensive bonus and leaves the character open for retaliation (a penalty to his defensive bonus), so it is a risky maneuver.

LongRangeShot — The character attempts to perform a ranged attack at a target out of the normal range for the weapon he is using. There is a large penalty to the offensive bonus for making this kind of attack. The one good thing about this attack is that the character will usually get to make at least one Normal attack before the attacker closes to within melee range, so the character doesn’t usually lose anything except the bolt or arrow he fires. This is the only attack specifically for ranged weapons.

Disengage — The character attempts to perform an attack and retreat so that his opponent cannot get in a counterattack. This doesn’t guarantee that his opponent will not follow the character and re-engage him, but it gives the character the opportunity to try to get away. As with the Parry and Riposte attacks, this attack wouldn’t be used with ranged weapons since the enemy wouldn’t have been engaged yet.

There is no problem using these attack types in a turn-based system, of course. For real-time combat, the use of attack types fits in nicely with a queue type system, as was explained earlier. One possible problem would be if the character performed the attack when the player wasn’t expecting it to occur. If you decide to use attack types in a real-time combat system, you need to make sure that this doesn’t occur and that how the system works is well explained to the player.

Ready… Fight

A player will initiate combat in one of several ways, but it always starts by selecting a target, which is done by simply clicking on the sprite on the screen. Some kind of visual indicator should be used to show the player that the target has been selected. There are a number of ways to do this:

Highlight the sprite — Draw it with alpha blending to make it display differently than it normally would, maybe by adding a glow around the sprite.

Display an indicator around/under the sprite — Neverwinter Nights used this effect by placing a broken blue circle around the entity in the middle of the screen. This is a more popular method and fairly easy to do, especially in a tile-based system since you’ll easily know where the sprite is located.

Show an icon for the sprite in the game’s GUI — You could also show other info about the entity: what weapon he’s armed with, armor, etc. How much you want to show is up to you. MMORPGs routinely show the strength of a mob relative to the character so the player knows how difficult the fight will be. This is only because the character is free to wander into just about any area in the world, so he could end up somewhere he shouldn’t be. This isn’t as much of a problem for single-player RPGs though.

After the player has selected a target he then usually chooses to attack, although sometimes he can do both at once. If both actions are done simultaneously, the attack will normally be the default for the character using whatever weapon he’s currently carrying. This could be problematic if the player has forgotten to equip a weapon. (I’ve done this more than once!) No matter how the player initiates the fight, once it starts you’ll have to change the game state to whatever combat mode the game uses. This could be an entirely different screen, as we see in the RPG Starter Kit that’s available for XNA Game Studio:

Figure 6-2

You may choose to use the same view for combat as you do for the noncombat state. Doing so means you’ll probably want to change or disable some things such as the ability to save and/or quit the game or perform actions that shouldn’t be done in combat (such as trade items with party members or chat with NPCs). More than likely you’ll find during testing that you can do some things that you don’t want the player to be able to do during combat. Players will take advantage of whatever bugs you miss during testing. Stories abound about bugs in MMORPGs that allow players to cheat. A search for “MMORPG exploits” on the Internet will show site after site that lists ways to use bugs to literally get away with murder in almost every big-name MMORPG.

Assuming you’re using a game state system for your RPG (and why wouldn’t you? Microsoft has even made a sample available on the Creators Club web site that shows how to do it), simply setting the game state to whatever value you use for combat and only allowing certain types of input in your code that checks input devices is a start. You’ll probably want to have substates or variables that allow you to check for things like whether it is the player’s turn, or whether the player has selected an attack from the interface provided and, if so, which attack, etc. The easiest way to do all of this is to wrap everything up into a combat engine that gets turned on when the game state changes to combat mode.

The Combat Engine

As stated earlier, we’ll be creating a turn-based combat system. The CombatEngine class will be used to resolve this combat system. It’s a fairly simple class for what it does:

Listing 6-2


public delegate void CombatEngineAttackEventHandler(
Entity attackingEntity, Entity defendingEntity);
public delegate void CombatEngineEntityKilledEventHandler(Entity entity);
public delegate void CombatEngineCombatCompletedEventHandler();

public class CombatEngine
{
private List<Entity> _players;
private List<Entity> _enemies;

private bool _isPlayersTurn;

//using an int here so that we can set it to 0
//to indicate an attack hasn't been selected yet
//we'll cast to AttackTypes as necessary
private int _attackSelected;

private bool _isCastingSpell;
private int _spellID;

private Random _rnd;

public event CombatEngineAttackEventHandler AttackResolved;
public event CombatEngineEntityKilledEventHandler EntityKilled;
public event CombatEngineCombatCompletedEventHandler CombatCompleted;

public CombatEngine(Entity player, Entity enemy)
{
_players = new List<Entity>();
_players.Add(player);

_enemies = new List<Entity>();
_enemies.Add(enemy);

_rnd = new Random();
}

public CombatEngine(List<Entity> players, List<Entity> enemies)
{
_players = players;
_enemies = enemies;

_rnd = new Random();
}
public string Start(bool randomStart)
{
string turnName = "";

//roll to see who goes first
//1-50 - enemy, 51-100 - player

int roll = _rnd.Next(1, 101);
_isPlayersTurn = (roll > 50);

if (_isPlayersTurn)
{
if (_players.Count == 1)
turnName = _players[0].Name;
else
turnName = "Players'";
}
else
{
if (_enemies.Count == 1)
turnName = _enemies[0].Name;
else
turnName = "Enemies'";
}

return turnName;
}

public string Start()
{
string turnName = "";

//figure out who goes first
//order of attacks is highest to lowest combination of AGI+DEX


return turnName;
}

public void AttackSelection(AttackTypes type)
{
_attackSelected = (int)type;

ResolveAttack();
}

public void SpellSelected(int id)
{
_spellID = id;

ResolveAttack();
}

private void ResolveAttack()
{
_curAttacker.SelectedAttack = (AttackTypes)_attackSelected;
_curAttacker.UseSkill(GlobalData.Items[_curAttacker.Inventory.
EquippedItem(ArmorArea.RightHand).ID].SkillRequired,
Difficulty.Normal);

//if entity being attacked is killed, call event
if (!((Entity)_curAttacker.Target).IsAlive)
EntityKilled((Entity)_curAttacker.Target);

//reset selected attack and spell id
_attackSelected = 0;
_spellID = 0;

AttackResolved(_curAttacker, (Entity)_curAttacker.Target);

if (_attackOrder.IndexOf(_curAttacker) == _attackOrder.Count-1)
_curAttacker = _attackOrder[0];
else
curAttacker = _attackOrder[
_attackOrder.IndexOf(_curAttacker) + 1];
}
}

The engine has two Start methods, but one is only really used in the combat simulator. In a game, the nonrandom start method would be called. I put the random start in to simulate things like sneak and surprise attacks.

Take a look at the code for the ResolveAttack function. Surprisingly it’s fairly short considering what it does. That’s because the real work is done in the UseSkill method of the Entity class.

Listing 6-3


private void ResolveAttack()
{
_curAttacker.SelectedAttack = (AttackTypes)_attackSelected;
_curAttacker.UseSkill(GlobalData.Items[
_curAttacker.Inventory.EquippedItem(
ArmorArea.RightHand).ID].SkillRequired, Difficulty.Normal);

//if entity being attacked is killed call event
if (!((Entity)_curAttacker.Target).IsAlive)
EntityKilled((Entity)_curAttacker.Target);

//reset selected attack and spell id
_attackSelected = 0;
_spellID = 0;

AttackResolved(_curAttacker, (Entity)_curAttacker.Target);

if (_attackOrder.IndexOf(_curAttacker) == _attackOrder.Count-1)
_curAttacker = _attackOrder[0];
else
_curAttacker = _attackOrder[_attackOrder.IndexOf( _curAttacker) + 1];
}

We’re assuming the entity was using his weapon in his right hand. If you implement something like weapons that can be wielded in either hand or ambidextrous entities, you’ll need to change this, but this will work for our purposes.

The skill for the weapon being used is retrieved and the Entity class’s UseSkill method called. We make another assumption that the attack has a difficulty value of Normal. This could be another area for improvement.

If the entity being attacked is killed as a result of the attack, we notify the form by firing the EntityKilled event. We then reset the _attackSelected and _spellID members for the next attacker.

The AttackResolved event is fired and the current attacker in the list of entities sorted by order of attack is incremented, resetting it to the first entity if all the entities have attacked.

The Combat Simulator

Now that we have a combat system, we need a way to test it out. Here’s a screenshot of the combat simulator that you’ll find the code for in this chapter:

Figure 6-3

Here we have Aragorn fighting a troll. It’s Aragorn’s turn and he is attacking using a Normal attack type with the Longsword weapon, having 18 ranks for the Melee Weapons skill. He has hit for 24 points of damage, even with a relatively low random die roll. We can also see that the option for him to cast a spell is disabled as he’s not a magic-using character. If he were a magic-using character and had selected a spell, the name of the spell would show in the Attack Selected box and the Weapon combo box would have been filled with the offensive spells he’s learned.

You can stop combat at any time and select different combatants. The simulator assumes the entity on the left is the player and the entity on the right is a creature or monster. When you click the Select button, you can select an entity in the Entities folder for either side. It doesn’t matter if you reverse the entities — putting the player on the right or the creature on the left — or even if you pit player against player or monster against creature.

Clicking either of the Start buttons creates the combat engine, passing it the selected entities. The associated Start method of the engine is called and the buttons are enabled or disabled appropriately.

Listing 6-4


private void btnStart_Click(object sender, EventArgs e)
{
_engine = new CombatEngine(_player, _enemy);

_engine.AttackResolved +=
new CombatEngineAttackEventHandler(AttackResolved);
_engine.CombatCompleted +=
new CombatEngineCombatCompletedEventHandler(CombatCompleted);
_engine.EntityKilled +=
new CombatEngineEntityKilledEventHandler(EntityKilled);

_engine.Start(true);

btnStart.Enabled = false;
btnStop.Enabled = true;
btnClose.Enabled = false;
}

You simply select the type of attack you want the attacking entity to make, and the combat engine’s ResolveAttack method is called. This is similar to the way the RPG Starter Kit works, as we can see in Figure 6-4:

Figure 6-4

When an attack is resolved, the form is notified via the delegate and the controls are updated with the results. The engine then waits for input from the entity whose turn it is. When combat is resolved one way or the other, the form is again notified. In an actual game, this would set the game state back to whatever it was before combat or normal mode.

You’ll probably spend a lot of time in the simulator, tweaking the numbers until combat is fair and somewhat realistic.

There’s Always Room for Improvement

The combat system we’ve developed is one of the simpler kinds. There’s lots of room to make it even better without making it overly complex though. Here are some options:

Vary the damage done by how much over 100 the result of the calculation is. Have a result of 100 be a normal value. For every five points over 100, you could add one point of damage. This could simulate hitting a vital part of the body or the hit being deeper than normal.

Right now an attack either hits or misses. If the attack misses, there’s no differentiation between the blow missing, being deflected by armor, or being blocked. You could randomly pick one. If the entity being attacked has a shield, the attack could be blocked with the possibility of the shield being broken.

Allow the entity being attacked a counterattack for a very bad result, say less than 25. You could also have other possibilities for bad results — dropped or broken weapons, the attacker stumbling and missing out on his next attack, etc.

For an attack that succeeds by a great margin you could give the attacker a free attack, possibly at a reduced chance or for less damage if it hits.

Implement situations like:

Surprise — The entity being attacked loses his first attack.

Stun — If the entity being attacked takes more than x amount of damage in one round, he’s stunned and unable to act. The amount of damage could be a set amount or a percentage of his current hit points.

Attacker invisible or attacking from rear — Attacker gets a bonus to his attack and/or certain defensive elements of the entity being attacked aren’t considered. An attack from the rear shouldn’t take a shield into account. If you allow invisible entities to make an attack, it’s normal for the invisible effect to be dispelled or for the entity to be visible for as long as the combat lasts in the case of entities whose normal state is being invisible (ghosts, for example, which normally reside in a different dimension or out-of-phase state).

The possibilities are numerous. It all depends on how realistic you want to make the system. You can always implement one feature at a time to gradually improve the engine. It’s always a good idea to plan out the improvements on paper before actually incorporating them into your game, however. You can usually tell how well the engine will work without wasting time writing code.

Summary

While having the character fight it out with all kinds of creatures and monsters is cool, an essential element is missing. The sounds of combat — the clang of a weapon on a shield or armor, the explosion of a magic spell hitting a group of monsters, the cries of the wounded — all are important to the realism of the gameplay. We could also use some cool music that plays while combat is taking place. We’ll take care of that in the next chapter, implementing a sound manager and importing three sounds for combat as well as some music to play when the game starts. Once you’ve completed that chapter, combat will take on a whole new dimension.

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

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