Professional Documents
Culture Documents
RPG Xna
RPG Xna
Back in 2006, the XNA team started working on a technology that would
evolve into the product youll soon be reading about XNA Game
Studio 3.0. At the heart of all the technical efforts we had going on, such
as prototyping, designing, and testing, we had a vision that we were
trying to achieve. That vision was to enableanyone to create games for
the Xbox 360 console, even if it was only one person and he or she
didnt have much programming experience. In essence, we wanted to
lower the barrier to entry for game development and, to put it simply,
bring back some of the fun of playing the roles of designer, artist, and
programmer on your own game.
For me, this vision brings back many fond memories of game
development and some of the reasons I wanted to become a game
developer in the first place. Like so many others, though, it was all
sparked by playing some great games. Role-playing games (RPGs) are
one of my favorite video game genres (no offense intended to my other
favorites the platformers and adventure games). I spent countless
nights with my Game Boy playing SquaresFinal Fantasy Legend II,
which is one of my favorites. To me there is something really incredible
about RPGs in that they pull together music, story, and gameplay in a
way that brings you fully into the games world.
As I continued to play more RPGs like Shadowrun and Final Fantasy
Mystic Quest on the Super Nintendo, andUltima on the PC, I found
myself wanting to tell my own stories. This was around the time I began
to learn programming in school, and my best friend and I worked on
stories for games that would make good RPGs. I loved being able to
incorporate aspects of my own life or experiences into a game that was
humorous and fun to play, such as an RPG I wrote that took place in our
middle school. All of my favorite teachers were hero characters, and
well, you probably know the other side. (Note that I am now mature
enough to say that all teachers are heroes!) These games were
entertaining to playeven if only for myself and my close friends.
While playing RPGs had the ability to capture my imagination and
attention, developing RPGs brought things to another level, and I was
completely captivated by the process. When I wasnt in class, I was
programming or creating content for my games. And when I was in
class, or otherwise not in front of my computer, I was writing down story
ideas or code that would allow me to have work queued up for when I
was back at my computer. For me, this created a perfect storm where I
was learning skills, creating something amazing, and having a lot of fun
every time I made something cool happen. This eventually led to my
career in game development, where I get to have fun as part of my job,
but has never truly allowed me to relive the same magical moments I had
while watching my own game come together.
As we continue to develop XNA Game Studio and the XNA Framework,
these kinds of memories are what inspire me and the rest of the team to
make XNA Game Studio better and easier to use for both professional
and hobbyist game developers. Were always working towards making
the process of getting your ideas into playable form as engaging and
easy as possible.
As you begin your journey into game development, using this book and
the RPG genre as a jumping-off point, I hope that you tap the creativity
and passion that is crucial to creating a successful game. More important
than commercial success, however, I hope you have fun and enjoy the
process of learning a new development technology and creating your
own games.
It is truly a great time (again) to be a game developer!
Michael Klucher, Program Manager
Acknowledgments
A lot of people had direct or indirect influence on this book. First, much
thanks has to go to the XNA Team for making such a fantastic product
and service available to game developers small and not-so-small. The
possibility of getting a game on a major console that anyone in the world
can buy is a huge step for the game development industry. As XNA
Game Studio matures, I can see it and Xbox LIVE Community Games
working together to become the YouTube of games thats been quoted
many times.
A huge thanks to my wife, Nancy, and son, Stephen, for their patience,
understanding, and support during the development of this book, without
which it would most likely never have seen the light of day.
Thanks also to Wordware for giving me the opportunity to take this
concept thats been brewing for years and get it into a form where it can
hopefully help other developers. Its been a pleasure working with
everyone.
To the XNA community thanks for giving us some great games as
well as supporting those who are getting their feet wet in the game
development waters. Its been an exciting and interesting journey so far
and Im sure the time ahead will be even more so.
Introduction
It seems almost everyone who wants to try their hand at game
development wants to create either a regular RPG (role-playing game) or
an MMORPG (massively multiplayer online role-playing game).
Conventions
The C# language will be used exclusively throughout this book. The
coding conventions for the .NET Framework will be followed with only
minor changes due to the style preferences of the author. These
conventions can currently be found at the following location
http://msdn2.microsoft.com/en-us/library/ms229042(VS.80).aspx.
Its recommended that, if you havent already done so, you adopt a set of
coding conventions and stick to them. It makes your code easier to read
and follow, especially if youre looking at code that youve put aside for
a while and gone back to. It will also make it easier for the people you
work with to follow your code.
System Requirements
The following is required to run XNA Game Studio:
Microsoft Windows XP SP2 or Windows Vista (Windows Server
2003 and older operating systems are not currently supported)
At least the DirectX 9.0c runtime (the SDK is not needed)
A video card that supports at least Shader Model 1.1 (2.0 support is
recommended as many samples and starter kits require it)
Visual C# Express 2008 or Visual Studio 2008
Chapter 1
RPG Basics
What is an RPG? In a role-playing game, the player assumes the role of
a character, or avatar, controlling his or her every aspect. Where he goes,
what he does its all up to the player. A character could spend all his
time traveling the countryside, sitting in a bar, farming, or engaging in
any number of other activities. Normally, though, a character will be
wandering through some type of dungeon, battling all manner of
creatures. While the player is the person who decides what the character
does, the developer is the person who decides what activities the player
has to choose from. At the very least, as a developer you need to give the
player some places for the character to explore, creatures to fight, and
treasure to find. These things are usually bound together in quests or
missions. (The name may vary depending on the genre of the game
quests in fantasy-based RPGs or missions in sci-fi RPGs, for example.)
A characters abilities in a role-playing game are usually based on stats
and skills. Stats are such things as Strength, Dexterity, and Intelligence.
These stats are represented by a number, which indicates how developed
that stat is. The higher a characters Strength stat, for example, the more
he can lift and the more damage he can cause with melee weapons. The
higher his Intelligence stat, the easier he can learn things such as spells
or languages. Most RPGs have these stats in one form or another, and
almost all have additional stats, such as Constitution, Agility, Wisdom,
and Charisma. You could also consider things like Health and Mana.
Mana is the source of supernatural power or the characters ability to use
this power. The definition can be whatever you make it inner power,
a magical aura in the world that the character taps into, psionic or mental
power in a sci-fi based RPG, etc. Its a fairly standard term in RPGs.
Skills are such things as using a sword, firing a gun, and walking a
tightrope. Almost every action the character performs, except normal
activities like walking and talking, will usually involve using a skill.
There are different ways these skills are represented in an RPG, but like
stats, skills are usually represented by some numeric value that indicates
how well the character can perform that skill. When performing a skill, a
value is also usually assigned to the difficulty of the effort. For example,
walking a tightrope is difficult. Doing so in a windstorm is much more
difficult. Doing so in a windstorm while being shot at by several enemies
is insanely difficult. Some activities will be so simple that they
automatically succeed. The sum of the characters skill score and the
value of the difficulty factor are used to determine if the effort succeeds.
Computer role-playing games (CRPGs, but called RPGs from this point
on) sometimes hide both stat and skill values, but most allow you to at
least see the stats and skills and determine how the character develops by
improving them.
graphics and also featured complicated gameplay. Games that are termed
Roguelike can still be found today.
Around 1979 the next era of RPGs began with the release of at least two
commercially published games. One is the aforementioned Akalabeth:
World of Doom, which featured first-person perspective and graphics
using wireframe for objects and the dungeon itself. The other
was Dunjonquest: Temple of Apshai.
Temple of Apshai was somewhat unique in that it used an on-screen
room number that could be looked up in the games manual to provide
the player with a description of the room. This description could give the
player hints about monsters that might be in the room before the player
enters or hidden objects in the room, such as secret doors. The game
spawned two add-ons, Upper Reaches of Apshai and Curse of Ra, which
were later packaged with the original as one game, The Temple of Apshai
Trilogy, featuring improved graphics and sound.
In 1980 gamers saw the release of Ultima I: The First Age of Darkness.
It was the first in the Ultima series, which would become one of the
premier series in the genre. The Wizardry series also debuted around this
time. Other games appeared during the next couple of years, but these
are probably the two most well known.
The rest of the 80s was a great time for RPG players. The Ultima series
continued, The Bards Tale series debuted, and Strategic Solutions, Inc.
(SSI) released several good games, including its Phantasie series.
As an aside, one of my personal favorites among the earlier computer
RPGs and the earliest I remember playing was The Bards Tale for the
Commodore 64. You viewed the world in kind of a first-person 3D
perspective, although the sprites were 2D as shown in Figure 1-1.
Like most RPG players, I spent hundreds of hours playing these early
RPGs and enjoyed almost every minute of the time. Having my
Figure 1-1
In 1988 the so-called Gold Box series of games saw its first release
with Pool of Radiance. SSI had managed to snag the very coveted
AD&D (Advanced Dungeons & Dragons) license and this game was the
first that used it to officially ship.
After Pool of Radiance came three sequels Curse of the Azure
Bonds, Secret of the Silver Blades, and Pools of Darkness. The same
engine that drove these games was also used on a series of games based
on the Dragonlance novels, several games set in the Forgotten Realms
world, and a pair of games set in the universe of Buck Rogers.
1992 saw the end of the engine used to drive the Gold Box games,
although game compilations were still released. And in 1993, SSI
published a tool created by MicroMagic called Unlimited
Adventures that players could use to create their own adventures. In the
meantime, SSI went on to release a new series of games, starting
with Eye of the Beholder in 1991.
Earlier we noted that fantasy-based RPGs werent the only game in
town. Science-fiction games also appeared in many different forms. In
1985 a game called Autoduel was released. Based on a pencil and paper
game called Car Wars, published by Steve Jackson Games, it had stats
for cars and players had skills like driving, mechanics, and
marksmanship. This was basically the beginning for games like Grand
Theft Auto and was a very popular andsuccessful game. And like all
popular and successful games it spawned similar games.
Autoduels post-apocalyptic setting was used in many other games
during this time, one of which was Wasteland. The very
popular Wasteland was the progenitor of what is thought by many to be
the most famous post-apocalyptic RPG (and possibly RPG in general) of
all time, Fallout. Wasteland was set in a world devastated by World War
III. Players controlled characters that had to overcome obstacles in an
interesting game world that could be developed using a fairly intricate
system. Characters learned from a set of 27 skills such as metallurgy,
lock picking, and sleight-of-hand and could use them to accomplish the
same task in more than one way.
The late 1980s saw the increase in popularity of games that used an
interface similar to that in The Bards Tale. Games like Dungeon
Master, released in 1987, and the aforementioned Eye of the
Beholder were highly successful and spawned follow-ups. Like many
advances in computer gaming, the increase in hardware power allowed
developers to do things that werent possible previously and gamers, for
the most part, snapped up the games that were released. There were a
few less-than-terrific games, but RPG fans were in general experiencing
games with increasingly complex stories, RPG systems, and graphics.
Probably the three most successful RPG series in the history of computer
gaming were released during these years. Theres probably no RPG fan
that hasnt at least heard of Ultima, Wizardry, and Might and Magic.
Between these series, several dozen games fed gamers RPG habit. Not
all of them were great, but the name alone was usually enough to ensure
the continued development of games in the series. The games brought
new elements to RPGs, such as the morality-based Ultima games.
Players didnt develop their character by assigning numbers to stats.
Instead, the direction the character took was based on answers to
questions about moral dilemmas. Much speculation has arisen as to why
Richard Garriot, the creator of the series, took this direction. No matter
the reason, this new direction was a hit with both players and reviewers
alike.
The Wizardry series took a different, if not as unique, approach in its
fourth installment. Rather than controlling a hero, players controlled a
villain, an evil wizard named Werdna (the wizard that players set out to
defeat in the first game in the series). Arguably the most difficult RPG
ever created, Wizardry IV: The Return of Werdna wasnt really an
evolution of the series except for this difference in the character being
controlled by the player.
The Might and Magic series, on the other hand, contributed several new
features to the RPG genre. The first game featured a huge world in
which players could wander around. There were over 4,000 locations
and 55 areas in the game, and players could explore it however they
wanted. Most other RPGs of the time guided the players through the
game in a much more fixed manner.
Also unique at the time was the element of having the race and gender of
the character affect the gameplay. Certain areas would not allow a party
if the members were all male or if they were of a certain alignment (i.e.,
good, neutral, or evil). This made the game more of a challenge, but the
overall difficulty of the game was less than that of its peers so this
element appealed to many who found other games too difficult to get
through.
them and increase skills by paying for training or finding and reading
books.
The one big problem, if you choose to look at it that way, was that
eventually the player could develop his character to a level sufficient to
make enemies no challenge for the character, even the most powerful
ones. There were also ways to level the character in ways not considered
normal, otherwise known as exploits. The player could make his
character more powerful without danger, eventually to a point where he
could only have died by not doing anything.
Two expansions for Morrowind were released in 2002 and 2003, both of
which had good reviews. The next game in the series, Oblivion, was
released in 2006 and was an overwhelming success. A new Might and
Magic game,Might and Magic VI: The Mandate of Heaven, showed up
in 1998, following the gameplay style of the Elder Scrolls games, but
with two major differences the player controlled four characters
instead of just one and the combat could be resolved in either real-time
or turn-based modes. The leveling system also deviated in that the player
could spend skill points in any way he wanted, distributing them
between four areas weapon, armor, magic, and miscellaneous.
Although the leveling system made character development a bit
different, it still required the player to spend money in order to train his
character to reach a new level. Since money wasnt easily obtained,
some thought had to go into how best to advance your character
should something be bought, such as a new weapon or magic scroll, or
should the money be spent to level the character?
One other way in which Might and Magic VI differed was in the relative
absence of bugs. This wasnt to last, however. The next game in the
series, For Blood and Honor, was released soon after the previous one,
with only a few improvements. The next game, Day of the Destroyer,
After a long wait by fans, Blizzard announced Diablo III at the Blizzard
Entertainment Worldwide Invitational in June 2008. The announcement
spawned a flurry of activity on the Internet, most of which centered
around the change in artistic direction. Many fans were extremely upset
by the change and an online petition was even started to try to convince
Blizzard to return to the style of its previous games.
The success of the Diablo series games meant the inevitable release of
clones, some bad, some good, and some greatly improving on the
gameplay, although the latter in at least one case, Sacred by developer
Encore, goes so far beyond that it almost changes the Diablo model. The
fantasy world wasnt the only setting for this style of game. Irrational
Games Freedom Force took a superhero turn on the style of play and
was fairly well received. It even spawned a sequel in 2005, Freedom
Force vs. the 3rd Reich.
One of the more popular of the action-RPG (as the gameplay style
became known) games was Dungeon Siege by Gas Powered Games.
Released in 2002, the game used a real-time 3D engine that allowed the
entire world to be traversed without any loading of data to be necessary.
The game also used a skill-based system for leveling the character,
meaning the player was free to change how the character developed at
any point in the game. The player could equip the character with a
different weapon or have him start using spells and the character would
eventually become proficient with whatever he used.
Another interesting feature of the game was the use of a mule as one of
maximum of eight the party members. This allowed the player to carry
more loot at the expense of a more skilled combat character (the mule
was capable of defending himself, although not well).
A lukewarm reception of the expansion Legends of Aranna the following
year didnt kill off the series. Dungeon Siege II was released in 2005,
Letting the player control only one character, but with the ability to
recruit up to five other characters, means that the player gets all the fun
of an adventuring party without the need for any other real persons. The
characters even interact depending on their different outlooks, going so
far as to be able to betray the group. For the true RPGer, this is part of
what its all about.
The game goes from real-time to a turn-based hybrid during combat,
which allows the player plenty of flexibility in how battles are fought.
The player could press the Spacebar to pause the game in the middle of
combat, specify actions for characters that they might not have otherwise
performed, and then let combat continue. This system appeals to both
novice and more experienced gamers, letting them manage the character
to whatever degree they want.
The game did include a multiplayer mode that allowed friends to take the
place of the AI characters in the party, but it was buggy and felt almost
tacked on. It did make the game attractive to the Diablo crowd that had
been using Blizzards servers for online gaming.
A year later an expansion pack was released. Tales of the Sword
Coast added new locations, spells, and items, and some gameplay
improvements. It also added new quests, the staple of RPGs. Like most
expansions, it served to tide gamers over until a true sequel came along.
Baldurs Gate II: Shadows of Amn was released in 2000 and became a
huge hit. An enhanced graphics engine upped the resolution to 800 x 600
and many new features were added. Additionally, several of the
characters from the first game made it into the new one. Creating a game
in which players form an attachment to the characters can help entice
fans into buying sequels to that game. Many people still
consider Baldurs Gate II the best RGP ever made.
The game picked up where the first one left off, which meant that if you
hadnt played the first game and enjoyed the sequel, you just might want
to go back and see how the characters got to where they were. It also
meant that players of the first game got to enjoy more of the story that
they had already experienced.
Like the first game, Baldurs Gate II received the expansion pack
treatment a year later with Throne of Bhaal. If you played either of the
first two games, this was almost a required purchase in order to get the
final chapter of the story. More new spells, items, and skills were added
along with a new dungeon that could even be accessed during part of the
original game.
Like Blizzard, BioWare has become known for solid, fun, and
interesting RPGs. So solid was the engine that drove the Baldurs Gate
series games that BioWare licensed it to Black Isle, the company that
made Fallout 2. Released in 1999, Black Isles Planescape:
Torment wasnt the huge hit that Baldurs Gate was, but it was a solid,
fun game that gained fans. Like the Ultima games, Planescape had a lot
to do with getting the player to think about morality, as the character had
the opportunity to join one of several factions and doing so gained him
friends as well as enemies. None of the factions could be labeled good or
bad; they were more varying shades of gray, which might have been the
whole point.
One point that could be looked at as a negative was the use of the abused
character suffering from amnesia device. Fortunately, it was done well
enough that it didnt drive away players. Another often used element
was to inject some comedic aspects into the game which, like the
amnesia device, was handled very well and probably was one of the
reasons the game did as well as it did. Getting the right mix of humor
and seriousness is difficult, butPlanescape managed to find it.
Black Isle struck again in 2000 with the release of Icewind Dale, again
set in TSRs Forgotten Realms. The big difference with this game was
the ability of the player to create not just one but six of the characters in
his group, a major difference from every game before it. Of course, this
added some pressure since the game was very combat-oriented, so a very
balanced party meant the difference between defeat and victory.
Unfortunately, Icewind Dale wasnt the rousing success that other games
using the BioWare Infinity Engine were. In focusing so much on
combat, the rich story of previous games was neglected. In addition, the
smash hits Diablo II and Baldurs Gate II provided stiff competition in
the RPG arena. Icewind Dale must have made a profit, however, as the
expansion pack Heart of Winter was released the following year. The
expansion was a quality product that was recommended for fans of the
game.
The 2002 release of Icewind Dale II was better received due to
improvements throughout the game as well as the move to the 3rd
Edition AD&D ruleset. It was still a mostly combat focused hack-andslash affair, but a worthy addition to the genre.
The Infinity Engine powered games mentioned so far were the only
TSR-licensed games to be released around this time period, but they
were better than several others that appeared. The bug-ridden,
plodding Pool of Radiance: Ruins of Myth Drannor, released in 2001 by
Ubisoft, and the more successful 2003 release by Troika of The Temple
of Elemental Evil: A Classic Greyhawk Adventure proved that a good
license isnt a guarantee of success. The latter has received some patches
even though the developer is no longer with us.
Although many dont consider it a true RPG, Bullfrogs Dungeon
Keeper (released in 1997) and the sequel,Dungeon Keeper 2 (1999) were
good games that had one major distinction from the rest of the crowd
the player was the bad guy. You could guess from the title what the
player did during the game create his or her own dungeon but that
was just the start. The player also had to defend his dungeon against
invading parties of adventurers. This unique twist on the classic
gameplay of the genre was intriguing enough to make the game a
relative success compared to monster hits like the Diablo games.
In 2000, Westwood released Nox, a quirky action/RPG mix that received
only average review scores. It starts out unlike any other RPG Ive seen.
The players character lives in a backwater Florida trailer park not
your typical hero. A wormhole opens up and sucks him into the world of
Nox. The player gets to choose from one of three different character
classes in his quest to defeat the evil sorceress Hebubah. One nice
feature of the game was an included multiplayer feature. This blend of
action and role-playing would become more and more popular
throughout the next half-dozen or so years.
BioWare continued its trend of quality releases in 2002
with Neverwinter Nights. Using a fully 3D engine dubbed the Aurora
Engine, the game was another to use the 3rd Edition ruleset and came
with an incredible toolset that allowed players to create their own
complete adventures that were limited only by their imaginations. If you
havent heard of Neverwinter Nights you should probably turn in your
RPG members card now.
The game received the expansion treatment with three packs released
from 2003 to 2005 Shadows of Undrentide, Hordes of the Underdark,
and Kingmaker. This is in addition to the tons of player-released
adventures. You could probably still be playing today if you tried to
make it through everything thats been released for the game.
Neverwinter Nights 2 was released in 2006, but didnt quite hit the bar
set by its predecessor (82.1% ranking compared to 88.8%, according
of the character, one of the best features in my opinion. Play the game as
a do-gooder and the character eventually takes on an angelic appearance,
glowing with light. Be a rotten, dirty scoundrel and the character will
become demon-like, even growing horns. The character is even affected
by eating and drinking, becoming overweight or getting sick and
vomiting. The townspeople also respond to the characters appearance,
which affects what happens during the game.
Fable won more than 50 awards and spawned the inevitable expansion
in 2005, Fable: The Lost Chapters, and it was only a matter of time
before a sequel was announced as well. Again highly hyped, Fable
II was supposed to be everything that Molyneux promised with the first
game and more. After its release in October 2008, it won three Best
Game awards and a Best Original Score award and had overall scores
of 89% and 88.7% on review aggregate websites Metacritic
and GameRankings.com, respectively.
Almost unknown outside of its native Poland, CD Projekt burst onto the
RPG world in October 2007 with the release of The Witcher. For a first
effort (the company mainly had done translations and localizations for
Polish gamers) the game succeeded spectacularly overall, even earning
an RPG Game of the Year award from PC Gamer. Reactions varied
around the globe, but with a GameRankings.com average of 81% and
user score of 9.1, it can be safely said that the game was a hit. Thats not
to say that the game didnt have problems. The biggest seemed to be the
use of a controversial copy protection system that modified the game
content to make it unwinnable. The problem was the systems conflict
with certain software and drivers, which caused it to run against even
legitimate copies of the game. Although a fix exists, its not perfect and
the game has to be started over in some cases.
the player during the journey. As usual, choices change the game and
there are a lot of them to make. Hidden behind the conversation system,
the choices make the game one that you can truly play over and over
again.
Even with the perfect scores and awards, reviewers had some minor
criticism. Item and squad management was overly complex, the player
has limited save slots (although I dont consider this a drawback as I
cant see returning to a point more than 10 saves ago), and the AI of the
players squad could have been better. Still, if youre a true RPG fan,
this is a must-have game. More than likely, the recently announced Mass
Effect 2, due out in 2010, will enjoy the same success.
Hellgate: London was released in 2007 and is kind of a cross between a
single-player and MMORPG. It was another highly hyped game and
with the pedigree of the developers working on it (former Blizzard
employees, some of whom worked on the Diablo, Warcraft, StarCraft,
and World of Warcraft games were behind Flagship Studios), there was
good reason to think it would be a smash hit. However, launch problems
caused concern among players and reviewers, as did the seeming
incompleteness of the game. Financial problems and legal issues caused
the company to shut down the games servers at the end of January
2009, closing off the online portion of the game.
The games weve looked at in this section range anywhere from kind of
similar to completely different in how they look and play. All RPGs,
however, have a lot of similar features when you open up the hood and
take a look inside. In the next sections, well look at these features and
figure out how theyll work in the game well build throughout this
book.
Some of these objects are represented on the screen by graphics, but all
are chunks of memory that the programmer manipulates as the game
progresses. They all work together to make the game an interesting, fun,
and exciting experience. If you get one of them wrong, it could ruin the
entire game.
Designing an RPG
In my opinion, designing and creating an RPG is one of the more
involved tasks you can undertake as a game developer. There are many
pieces that must be made to fit together, just like a jigsaw puzzle. Get a
single piece wrong and a good-sized chunk of the rest of the puzzle will
go unsolved. One of the best ways to ensure that youve covered every
aspect of the game is to have a complete design document done before
you even start writing code.
Decisions to Make
There is no one correct way to design an RPG, but here are some things
youll need to think about:
Turn-based or real-time combat Real-time is becoming the
standard, but I believe there is still room for a good turn-based RPG.
Some people dont like the click until the monster is dead or click on
the monster and watch the fight type of RPGs. They would rather plan
out each attack that the character makes. Of course, a turn-based RPG is
more interesting if a character has more than one type of attack to choose
from and the type of attack affects the characters defense or other areas.
If I use this attack and hit, the creature should be dead, but if I miss Im
leaving myself wide open. This just isnt something you have the
ability to do with most real-time RPGs.
Along with the type of combat, you may want to consider other aspects
like fatigue and breakage factors for items. Youll need to decide
whether to include these types of things when you make the decision
about what type of combat system to use. The more things you include
in your combat system, the more interesting or realistic it will become.
The downside is that it will take longer to develop and test with each
item you add. Youll have to take into consideration the number of
people you have working on the game and their experience level.
Graphics engine This decision will be made based on your skill
level and preference. I opted for 2D isometric for both this book and the
RPG Im working on. It was the style that was used when RPGs started
to become hits in the industry and, in my opinion, they look the best with
a minimum of work. Other types are 2D (top down using
square/rectangular or hex tiles), and 3D (first person or isometric). 3D
RPGs are starting to become the norm. Eventually I believe all PC,
noncasual games will be 3D, just to take advantage of the eye candy 3D
SDKs give you access to. This will occur when low-end, cheap
computers are equipped with good graphics cards. Many companies that
mass-produce PCs still use a graphics chipset on the motherboard, which
means less power and graphical features. For the independent developer,
2D still works just fine, and many people still play 2D games. One big
either type of game. Youll likely end up developing the type of game
you enjoy, and thats probably a good thing to do.
Save system How will the game save the players progress? RPGs
typically take anywhere from several dozen to hundreds of hours to
complete. Obviously this isnt going to happen in one session of play.
The player is going to want to quit at some point and be able to come
back and pick up where he left off. Youll have to decide if you want the
player to be able to save at any point during the game or if the progress
can only be saved at certain points (for example, only in noncombat
situations). Also, if you choose the latter, how often will the progress be
saved? If its not often enough, the player could get annoyed at having to
retrace his steps. If you allow the player to choose when he saves, how
many slots will he have to save to? If you give him unlimited slots you
run the risk of having the player save every couple of minutes, which
means theres really no risk in doing dangerous things. If you give him
only a certain number of slots, hell have to overwrite the older ones,
which could mean more risk if he needs to revert back to a time before
his oldest save.
Personally Im more in the let the player save to limited slots camp.
There may be times when the player has to stop playing at an
inconvenient time, like right before a save point, which means a lot of
retracing of progress. This reason alone is sufficient for me. I know if I
were the one playing Id be very annoyed at losing all that progress.
Tools Develop the tools for creating the game. Although not
necessary, certain tools could make the creation of the game much
easier. This could include a map editor at a minimum. Other tools might
be a script compiler or character editor. Well create a basic map editor
later on in the book. Youll have to evaluate this area carefully. Some
time spent up front may save you time in the end, but too much time
wasted on tools might mean that you never get anything done on the
game itself.
One thing to consider when developing your tools is whether or not you
want to release them to the public and allow players to create mods for
your game. It seems that almost every game out there is allowing players
to add content to the basic game. This is a good thing in that it draws
more people in since many players want this ability and will play a game
with mod capability over one without. The downside is that it usually
takes a good chunk of time to add this capability to a game. If your
toolset is not user-friendly, youll never hear the end of it from the
players. You also have to design the game to have the ability to use
player-created content. This could present some problems if not done
correctly.
My feeling is, since you have to design the tools anyway, why not try to
give the player more reasons to play your game by designing them well
enough that a player can use them with a minimum of problems. Some
companies have actually hired people based on mods that theyve
created for games. You may find someone that youll be able to work
with on a future game if you give the players the ability to show their
stuff.
Scripting system Along with the tools, you may want to decide if
youll include a scripting language with the game. I mention this outside
of the tools since its a huge task to develop even a basic scripting
system. You can use a scripting system for several different things,
depending on how deep you make it. In-game cut-scenes are one thing
that can be done with a scripting system. A scripting system can also be
used for things like conversations with NPCs, dynamic quests, and AI.
All of these can be done without a scripting system, but it may be more
difficult. Depending on how deep and rich you want to make your RPG,
Figure 1-2
The camera will track the character, keeping him centered on the screen
when possible, but not showing a blank area where the level ends.
The rendering system used in the game will also be similar to that of the
RPG Starter Kit. Square tiles will make up the level and some tiles will
be inaccessible for the character to walk on. Buildings and other features
will take up multiple tiles.
Full lighting will be used throughout the game. This is another area that
should probably be changed for a full game. Normally dungeons use
ambient lighting for a more realistic atmosphere.
Ambient lighting refers to using all existing light sources in the area to
calculate the degree of illumination of all objects in the area. Outdoor
areas would normally be fully lit during the daytime. Dungeon areas
would vary due to the light sources available (torches, magical lighting,
etc.). Only the areas around these light sources are fully lit, with the
intensity decreasing with distance from the source.
Game Characters
The player will control one character throughout the sample game. The
entity class will be selectable by the player. All stats for the character
will be generated randomly.
There will be one nonplayer character in the town level of the game.
This NPC will be the one that gives the player his quest.
There will be several types of creatures and monsters in the wilderness
and dungeon levels. In the wilderness, the type of creature will be
generated randomly whenever a random encounter occurs. The random
encounter will occur 25% of the time for each tile the character moves
to. Monsters in the dungeon level will be placed by hand.
User Interface
Several user interfaces will be used in the game, besides the menus that
will be modified from the Game State Management Sample.
Inventory interface This interface will show the items in the
characters inventory and allow the player to choose items for the
character to use.
Toolbar interface This interface will display the characters current
health and mana and allow the player to quickly select items (potions,
scrolls, etc.), skills, or spells to use. The number of slots will be limited
by a number to be determined.
Items
Next Up
So now that we know what an RPG is, what pieces well need to
implement to create one, and how to ensure we dont get lost in the
process of creating our masterpiece, were ready to take our first steps
into coding. Well start with the most important piece from the players
point of view the character he controls and the people and creatures
hell run into.
If you havent already done so, you might want to download the files for
each chapter (you can find them
athttp://www.wordware.com/files/XNARPG) in order to better follow
along with the text.
Chapter 2
RPG Entities Characters, NPCs, and
Monsters
The character is the players extension into the game world.
Unfortunately (or fortunately, depending on how you choose to look at
it) hes 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 players
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 doesnt 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
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 wouldnt 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, well define a class with the following
members:
Listing 2-1
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. Were 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()
{
}
{
_statCalculation = value;
}
}
else
_statCalculation = value;
}
}
Two constructors are provided for a bit of flexibility, but only the first is
required because of the way were 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 were 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
doesnt matter if we set it to empty or null, but if it is we have to ensure
theres a calculation there or well get an exception when we attempt to
use it.
Well 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, well be making everything we can 1-100 based if it
makes sense, as youll see later. Although well use the 1-100 range the
most, we will have others, so well create an enum to define them:
Listing 2-4
d4 = 4,
d6 = 6,
d8 = 8,
d10 = 10,
d12 = 12,
d20 = 20,
d100 = 100,
MaxDie = 100
}
public EntityStat()
{
}
{
get { return _stat; }
set { if (!string.IsNullOrEmpty(value)) _stat =
value; }
}
return val;
}
set{}
}
_bonuses.Add(bonus);
}
return;
Bonus bonus;
bonus.ElapsedTime += time;
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
cant 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
Every time through the game loop when the game is running each stats
Update method will be called, passing the elapsed time since the last
update. Well 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. Well 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 dont believe every stat has to have this capability. A
Saving our classes to XML like this is easily done using the
IntermediateSerializer class built into the XNA Framework:
Listing 2-9
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer;
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 isnt being used. The IntermediateSerializer
class has the following benefits over XmlSerializer:
try
{
XmlReader reader = XmlReader.Create(
new FileStream(filename, FileMode.Open));
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 thats passed and the return type of the object
thats filled with the data from the file.
That covers all the code well need for stats. Once we have some stats
saved, well 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 entitys 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
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 arent 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
alternative for players who dont want to play a plain human or take
the physical penalties of a full elf.
Table 2-3: Half-elf 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. Its all up to you, limited
Entity Classes
As with the EntityStat class, theres not a lot to the EntityClass class, so
lets take a look at the whole thing:
Listing 2-11
public EntityClass()
{
if (_statModifiers != null)
_statModifiers.TryGetValue(name, out value);
return value;
}
_statModifiers.Add(name, value);
}
if (_statModifiers == null)
return;
_statModifiers.Remove(name);
}
The _name and _description are standard here with one possible unique
feature for the name; while we wont 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 doesnt 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
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
#endregion
Well have four types of entities in our little game, although you could
probably get by with three:
Listing 2-13
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 doesnt make
sense, such as a symbol or number, or maybe even a vulgarity if you
have an online component to your game and youre concerned young
kids might be playing. Thiswould 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 theyre entered and
ignore anything you dont 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 youre 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
1000
2000
3000
4000
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 characters 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 wont give him any
experience at level 20, so he wouldnt be able to just fight them and
expect to advance.
The _baseHP and _curHP members are the characters 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 theres
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. Its a very slim chance, but at least its
there.
There are many ways to calculate an entitys hit points. The calculation
Im using is to take the average of the entitys Strength and Constitution
stats and add a random number from 1 to the DieType value for the
entitys 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 its 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 characters 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
Well 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 didnt 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 skeletons resistance to piercing
weapons. As you can probably imagine, its difficult to really do damage
to a skeleton with these kinds of weapons since they dont 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
entitys 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
The uses for a characters 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
The age of a character is usually dependent on his race. Some races have
extremely long lives on average. Age can also affect a characters
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 wont take age into effect, but its 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
doesnt 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 well 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 arent 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. Hes 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 well use:
Table 2-7: Race/class combinations
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 havent 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. Heres what the toolset
will look like when its 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
Well store this data in a class, of course:
Listing 2-17
Figure 2-4
Once we have our game created, youll notice that the only button thats
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 (
Figure 2-5
Well 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
2\XNA RPG Toolset\bin\Debug\Games\Test\stats folder, clicking the
Add button after each item:
Table 2-10
Stat Name
Strength
Abbreviation
STR
Description
Stat Name
Constitution
Abbreviation
CON
Description
Stat Name
Agility
Abbreviation
AGI
Description
Stat Name
Dexterity
Abbreviation
DEX
Description
Stat Name
Intelligence
Abbreviation
INT
Description
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 2\XNA RPG Toolset\bin\Debug\Games\Test folder to
see what information will be used in the demo game thats 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, youre 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, well 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. Heres the dialog thats 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
if (cboRace.SelectedIndex > 0)
raceStatMods = _races[cboRace.SelectedIndex
1].GetStatMods();
if (cboRace.SelectedIndex > 0)
classStatMods = _classes[cboClass.SelectedIndex
1].GetStatMods();
if (frm.ShowDialog() == DialogResult.OK)
{
_entityStats = frm.GetStats();
cboStats.Items.Clear();
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. Youll 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 books downloadable files, as well be using
them in the future. Over the course of the next several chapters, well
expand the class to incorporate skills, items, and magic all the things
that make an RPG cool. Well start in the next chapter with skills, so our
entities can actually do things.
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 hell 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 hes tied to the end of
the rope, he wont 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 cant swim well or at all,
hell drown if he tries to swim across. If hes an Olympic-caliber
swimmer, hell probably be able to swim across even if hes carrying all
of his equipment. If he can barely walk a straight line without losing his
balance, he probably wont be able to jump from rock to rock without
falling in. If he cant even tie a knot in a rope, he more than likely wont
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 hell be able to get
across.
So how will the player know which is the best way to get the character
across? He checks the characters 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 characters skills get better as they are used, and they stop
increasing and possibly even decrease a little if the character doesnt 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 hell 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.
(Im 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 didnt even use during the adventure, depending on how
you set up your skill system. While this isnt the most realistic way of
handling skills, it is popular. Its also much easier to implement, as you
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 characters level isnt 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.
Well 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.
[NonSerialized]
private Texture2D _icon;
}
For most RPGs, these three would probably be all you need. Im sure
you could break down noncombat skills more, but for our purposes this
is enough. The system were 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 isnt 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 doesnt 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 hes 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,
if (_costs == null)
_costs = new Dictionary<string, string>();
if (!_costs.ContainsKey(id))
_costs.Add(id, cost);
else
return false;
return true;
}
if (_costs != null)
_costs.TryGetValue(id, out cost);
return cost;
}
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;
}
}
}
//else
//{
//
//
//use default
//
switch (level)
//
//
case 1:
//
case 2:
//
case 3:
//
case 4:
//
case 5:
//
//
//
break;
//
//
case 6:
//
case 7:
//
case 8:
//
case 9:
//
case 10:
//
//
//
break;
//
//
case 11:
//
case 12:
//
case 13:
//
case 14:
//
case 15:
//
//
//
break;
//
//
case 16:
//
case 17:
//
case 18:
//
case 19:
//
case 20:
//
//
//
break;
//
//
default:
//
//
//
break;
//
//
}
}
//}
roll += bns;
result = roll;
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
VeryEasy = 25
}
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. Well see how both of these are set up a bit later in the chapter, but
for now its 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. Well add some new members to our Entity class, along with
functions to deal with the member that will hold these skills:
Listing 3-7
_skills.Add(id, ranks);
}
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. Were not going
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 characters 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 characters training to
develop his body to allow him to perform better in battle. This doesnt
represent training like weight lifting (which would increase the
characters 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, couldnt 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
its 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 hes 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 dont normally use it in, but well 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, well 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
traps 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
characters 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 weve briefly touched on already, so the player isnt
overwhelmed by the total number.
Any character can attempt to learn any skill, but as well 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
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.
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 weve established that races affect skills. Since races and
classes cant be created without the existence of stats, we dont 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 List<KeyValuePair<string,int>>
_opposingSkillControlSource;
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
//add costs
foreach(string key in _costs.Keys)
_skill.AddCost(key,_costs[key]);
FillSkillObject();
if (_skills == null)
_skills = new List<Skill>();
_skills.Add(_skill);
cboSkills.Items.Add(_skill.Name);
_curID++;
SetupFormForEntry();
_saved = false;
}
Saving the list of skills looks almost exactly like saving the other
collections of data:
Listing 3-11
settings.Indent = true;
_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
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
if (skill != null)
{
_skills.Add(skill);
cboSkills.Items.Add(skill.Name);
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
if (frm.ShowDialog() == DialogResult.OK)
{
_entitySkills = frm.SelectedSkills;
txtCurSP.Text = frm.SkillPointsLeft.ToString();
cboSkills.Items.Clear();
Since weve 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 whats
the best way? If the game is to run on the Xbox 360 (and well assume
thats 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 cant be run in XNA GS games, so using one of
the many scripting languages is not an option. Well need to stick with
plain C# code. The job is easily done with C#, but it means well have to
go with a less flexible solution.
Well 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
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;
}
}
}
Well be keeping a static list of skills. The class that contains this list is
in the GlobalFunctions.cs file:
Listing 3-16
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
wont 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 wont need functions for every skill since using some skills means
whats supposed to happen doesnt (for example, a successful Dodge
means an attack misses). Some things we can guess whats going to
happen if the skill use succeeds, so well add some commented out
placeholder code that well implement in coming chapters:
Listing 3-17
((Entity)target).Damage(damage, DamageType.Crushing);
}
{
//get damage from weapon entity is using
//need to figure out if weapon is piercing or slashing
//((Entity)target).Damage(damage, DamageType.);
}
You should be able to see the way were 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). Well 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 thats not an entity. Since well be writing the code calling
these functions, its a pretty safe assumption. If this code were going to
Summary
Now that our entities have learned some skills, they need something to
use them with. In the next two chapters, well 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. Well then add some functionality to allow the entities to use
these spells and items on one another.
Chapter 4
Magic
Anytime you talk about a fantasy-based RPG, you will eventually get
around to talking about magic. A fantasy-based RPG wouldnt be the
same without the obligatory wizard or mage. An RPG world would
probably be pretty boring without its magic. You wouldnt have any of
the magical creatures, and some of the races wouldnt exist, such as
elves, dwarves, or halflings (depending on how you explain their
existence). All in all, it might not be much fun to run around in such a
world. Luckily for you, this chapter will give you some ideas on how to
integrate a basic magic system into your game.
There are many ways to implement a magic system. Well use a fairly
standard mana system. Mana usually exists in one of two ways
entities have an inner reserve that allows magic users to cast spells, or
the magic user is able to tap into an external source that permeates the
world. When this reserve is empty or the character exhausts himself
attempting to manipulate the external source, he must allow some time
to pass before he can cast any more spells. All characters will have some
mana, although it may be difficult for them to learn to use spells.
Some systems base a characters ability to use magic on one of its stats,
such as intelligence or wisdom. I feel this is fairly unrealistic, although
probably easier for pen and paper RPGs. An intelligent character may be
able to learn spells more easily, but I dont believe this should have any
bearing on how much mana he has. A less intelligent character may have
an innate ability to use magic, but may not be smart enough to learn any
spells. The system in this book will treat mana as another stat, although
magic-using characters will recover mana quicker. We explain this by
saying that magic-using classes have learned how magic works to some
degree and how to train their bodies to be more in tune with the magic in
the world. Classes that dont use magic may be able to cast spells, but
without this knowledge and training they wont be able to recover mana
as quickly as magic-using classes and wont learn spells as easily. This
recovery rate will be handled by the game, so all the player has to worry
about is choosing a spell for the character to cast when hes able.
First off, well look at a class that will handle every aspect of an
individual spell and a class that will manage the spells a magic-using
character has available to him (think of this class as a spell book). Well
also look at how a magic user learns his spells and how spells are used,
both in and out of combat.
The _id member is the typical identifier for doing lookups into a list of
spells from other code. The _name and _description members are also
typical. The _type member tells us the type of spell. Its an enumerated
value that can be one of the following:
Listing 4-2
NonCombat
}
The spell type could be broken down into various subcategories that you
see in many RPGs, but I opted for a simpler system. A full-blown RPG
would probably expand on this.
The _difficulty member could be used to specify how difficult it would
be to learn and/or cast this spell. Some RPG systems break spellcasting
down into different types, sometimes combining them. Casting a spell
could involve a verbal component, such as a word or phrase; a gesture or
series of gestures using the hands or possibly the entire body; physical
components (the old eye of newt, wing of bat kind of stuff); or
something else. Doing one of these would be difficult enough in combat.
Doing more than one would require great concentration and effort. Since
we dont really care about how the spell is cast, we can simply use a
difficulty value as a substitute.
The _effectAmount member is a string that is used to determine how
many points to apply to the target. The points can either be damaging or
beneficial, depending on the type of spell. A healing spell will restore hit
points to the target, while a fireball spell will damage as many entities as
are in its area of effect, which the _areaEffected member is used to
determine. Both members can have a format of x-y. In the case of
_effectAmount, its the range of points that affect the target. In the case
of _areaEffected, its the number of horizontal and vertical tiles that are
affected. The _effectAmount member can also be a single number that is
applied to the target after taking into account any resistances or
weaknesses.
The _range member tells the maximum range of the spell. Normally the
spell will travel this entire distance unless its blocked by something
an entity, a wall or other structure, a defensive spell, etc. If the spell is
directed at an entity and the entity dodges the attack (if possible), the
spell will continue until it reaches the _range value or its blocked. In
some cases, the caster will be able to specify where the spell goes off.
For example, a magical shield spell or one that creates a wall of ice,
stone, or fire could be set to go off directly in front of the caster or some
distance away, depending on the range of the spell.
The _classesAllowed member notes the classes that are capable of
learning and using the spell. Depending on how you want to use this, the
list can be empty to denote that all classes are allowed, or every class can
be added to the list and the length of the list compared to the number of
classes in your game. The latter case saves having to go through the
entire list every time you need to make a check.
Duration can be formatted like _effectAmount, or it can be set up as
x*level of caster. To make things easy, well assume that the actual
time value is going to be in seconds. In a larger scale game this could be
something that might last hours, which the duration member of the
bonus structure that were using is capable of handling. Since the
checking of the duration is done automatically, we just set it up and
forget it, no matter how long it lasts.
Certain spells can be very difficult to learn and may potentially have
devastating effects if used or cast improperly. The _levelRequired
member helps take care of a character attempting to learn a spell that
hes not ready for. Its declared as a byte since we wont have nearly as
many levels as a byte is capable of handling.
Certain spells are also usually usable only by certain alignments. An evil
character, for example, normally wouldnt go around tossing off
SmiteEvil spells, as that would probably be viewed as
counterproductive. The _alignmentsAllowed member handles this.
Notice that its an enum that allows multiple values to be assigned to it:
Listing 4-3
[Flags]
public enum AlignmentFlags
{
None = 0,
Good = 1,
Neutral = 2,
Evil = 4
}
Many spells will be usable by any alignment. In this case, the value
assigned to the member would be 7, which is the sum of all the
alignments.
In mana-based systems (as opposed to systems in which a caster has a
set number of spells he can cast before having to take time to memorize
his spells again), spells take energy to cast. The _manaCost member is
used to tell us exactly how tiring that will be for a character. Expect to
play with this value as you test your game to ensure that a magic-using
character cannot cast enough spells to destroy an entire dungeon without
a problem or does not run out of magic fighting a small rat and the next
rat that comes along takes him out.
If a character has a virtual ton of mana, its probably not going to help
the balance of your game if he can cast, say, a fireball every second. If
youve ever played World of Warcraft, youre probably familiar with the
cooldown period required after casting a spell. After you cast a spell
in WoW, the little toolbar icon is grayed out and slowly returns to normal
in a clockwise fashion. You can click on that icon all you want, but until
its completely back to normal nothing will happen. Well have the same
kind of thing in our game. The _castRate member will be used to tell
//No restrictions
Round,
Minute,
Hour,
Day,
Week,
Month
}
Learning Spells
There are probably two typical but very different ways to have a
character learn a spell:
Character gains a level A fairly standard way to handle spells.
When the character gains a level, its assumed he spends time training to
merit the increases in whatever the system increases stats, skills, and
spells. This is the system well use as we touched on last chapter when
we learned about skills. When the character gains a level, hell get a
number of skill points that can be spent on learning skills and/or spells.
Magic-using characters will probably spend most of these points
learning spells.
Anywhere and anytime Given a chance to sit down and study, a
character can attempt to learn a spell whenever the player wants. If this
is allowed you usually wont be using a level-based system for learning
skills, but would instead use something more along the lines of the
Dungeon Siege series skill-based system.
With both skills and spells, there are checks to be made to determine
whether a character has learned what hes selected. Spells are a bit more
complicated than skills in this. There are several factors to take into
consideration as we saw when looking at the Spell class:
The characters class When attempting to learn a new spell you
probably will limit the players choice to only those the character is
allowed to learn (although well still do a check to verify this and the
next two factors).
The characters alignment Usually this isnt an issue with skills.
The characters level Again, usually the only way in which level
usually has any effect on skills is the bonus the character gets for how
many times it has bought that skill.
When a player has a character attempt to learn a spell, heres what
happens from a code standpoint:
Listing 4-5
if (!hasSpellcraftSkill)
return false;
spell.AllocatePoints(skillPointsAllocated);
_spells.Add(id, spell);
return spell.Learn(amount);
}
An instance of the EntitySpell class is created and the points the player
allocates to learning that spell are passed as a parameter to the classs
AllocatePoints method. The instance of the class is then added to the
Entity objects _spells member, even if the character fails to learn the
spell. We do this because the skill points that are allocated to learning
the spell remain, increasing the chance that the character learns the skill
the next time he tries. Theres not much to the EntitySpell class, so lets
take a look at the whole thing:
Listing 4-6
return _learned;
}
}
Since the spells an entity has learned are stored in a dictionary with the
spell ID as the key, we dont need the ID in the class. Whenever the
entity uses the spell, well get it from the key in the dictionary entry.
To learn a spell, the entity can allocate skill points to increase the chance
of learning the spell. Each skill point currently increases the chance by
5%. This could be modified by some factors the difficulty of the spell
(remember that _difficulty member?), the amount of time the entity
spends (were not tracking time for the game created in this book), or
just about anything else you want. All the various bonuses the entity may
have are passed into the Learn function and added to the random die roll.
If the roll is 100 or greater, the character learns the spell and the _learned
and _skillPointsAllocated members are set accordingly.
Depending on your system, you might require an entity to memorize a
spell every time he wants to use it. If so, theres the _memorized
property you can use to check whether the entity has memorized the
spell before allowing him to cast it. To memorize the spell, I added a
function instead of using the property setter. This is just a personal
preference. I usually try to have actions of an entity done via a function.
The code in the function could just as easily be inside a property setter.
If you feel this makes the code cleaner, just move it and get rid of the
function.
Just about every RPG out there allows the player to create shortcuts for
doing things casting spells, attacking, etc. The _hotKey member will
allow us to do this. Usually these keys are the number keys from 1 to 0,
but they could be anything you want to allow the player to use. An
interface will also have to be created to allow this, unless you allow a
config file to be modified by the player. Of course, this wont work on
the Xbox 360 since the player doesnt have access to any files.
Using Magic
So a character has a bunch of spells memorized, but he doesnt have a
way to use them yet. There are several ways in which the player could
cast a spell, but its up to you to write the code to make these available to
him:
Hotkey Assuming youve given the player a way to assign hotkeys,
he should be able to press a particular hotkey any time the character is
capable of casting the spell. When the character is capable depends on
the spell and whether or not the player has selected a target. Some spells
could be area-of-effect, meaning they dont have a specific target. The
player simply casts the spell and the game figures out what entities are in
the affected area, which is usually a certain radius around the player or
the point selected by the player in the case of a spell that has a range.
Mouse context menu Using the right mouse button, the player
could target an enemy and bring up a context menu with the things he
can do to that enemy attack, talk, or cast spells. Neverwinter
Nights used this technique to great effect: Here the player, whose
character is the woman in the top right picture, has clicked on an NPC,
which brings up a radial menu of actions. She has selected to talk to the
NPC. If this had been an enemy, however, a different set of menu items
would have been shown and had the player been able to use magic, icons
for the different spells that could be used would be available in
submenus, shown by the arrows radiating out of some of the icons.
Figure 4-1
Mouse gestures Not widely used, this technique gives the player
more of a feeling of actually casting the spell by having him move the
mouse in a specific pattern. One of the problems with this is that the
if (_target is Entity)
{
Entity target = (Entity)_target;
ret = GlobalData.Spells[spellID].Cast(ref target,
ref entity);
_target = target;
}
else
ret = GlobalData.Spells[spellID].Cast(ref _target,
ref entity);
return ret;
}
else
return false;
The entity finds the spell in the global spell list using the passed ID
and calls the spells Cast method, passing the _target member and itself,
in a roundabout way. Since the caster and target are passed by reference
so resolution can be performed in the Spell class, its necessary to both
create a new Entity object and set it to the player object and create a new
Entity object for the target, since it wont necessarily always be an
entity. If the target is an entity and you try to cast it as part of the call to
Cast, youll get a compile-time error.
Listing 4-8
switch (_type)
{
case SpellType.Defensive:
{
//normally you would just use the _defBonuses
list
((Entity)target).AddDefensiveBonus(bonus);
}
else
{
Bonus bonus = new Bonus();
bonus.Amount = Convert.ToInt16(amount);
bonus.Type = BonusType.Magic;
bonus.Duration =
Convert.ToInt32(_duration);
((Entity)target).AddDefensiveBonus(bonus);
}
break;
}
case SpellType.NonCombat:
{
if (!((Entity)target).InCombat)
{
//go through bonuses and add to target
foreach (Bonus bonus in _defBonuses)
((Entity)target).AddDefensiveBonus(bonus);
((Entity)target).AddOffensiveBonus(bonus);
break;
}
case SpellType.Offensive:
{
target.Damage(amount, DamageType.Magical);
break;
}
}
return true;
}
return false;
}
try to resist it), and the result is checked to see if its over 100. If so, the
target either takes damage, if its an offensive spell, or has whatever
bonuses are part of the spell added to it.
Its a fairly straightforward system that should work well for most basic
RPGs. For anything else youll have to write a lot of specific code to get
the spell system to do what you want. This could include using some
kind of scripting (assuming you dont want the game to run on the Xbox
360), which would offer a great deal of flexibility at the cost of increased
development and testing. (Note that part of the reason the system is so
easy is because we want to run it on the Xbox 360, and you cant use
scripting with the Xbox.) Testing any kind of scripting system is going
to be difficult and take longer than regular testing, so be prepared to
account for this in any development schedule you might have.
Figure 4-2
Usually the spells a player uses the most are going to be associated with
hotkeys or toolbar icons in the games interface, but at higher levels a
character may have dozens of spells and theres no way to keep them all
available. A character may have spells that are only useful in certain
circumstances and will swap out spells depending on the situation.
In order to allow the player to quickly and easily find the spells hes
looking for, some sort of organization is handy. For our purpose,
organizing the spells by type comes to mind immediately. Obviously
were not going to have World of Warcraft level art here, but it will be
functional:
Figure 4-3
The spell book will have six spells per page. The name of the spell will
be displayed next to the icon. Clicking on a tab to the left will change the
spells displayed to those for that type. Clicking on the triangles at the
bottom will turn the pages, with the page number displayed at the top.
As spells are learned, theyll show up in the next available spot for the
type of spell. The player will probably want to be able to move spells, so
well have to provide this functionality. This means well need to know
what spell is on what page. Luckily the ID of the spell will enable us to
keep track. Our spell book is pretty simple because of this:
Listing 4-9
If, for some reason, we ever had another spell type, wed have to change
the class, but that would be simple enough to do. If you decide to change
the number of spells per page, at least you only have one place to change
it, the SpellsPerPage constant.
Adding a spell to the book is fairly simple:
Listing 4-10
switch (type)
{
case SpellType.Offensive:
{
if (_offSpells == null)
_offSpells = new List<SpellBookPage>();
{
SpellBookPage page = new SpellBookPage();
page.AddSpel l(id);
_offSpells.Add(page);
}
break;
}
case SpellType.Defensive:
{
if (_defSpells == null)
_defSpells = new List<SpellBookPage>();
break;
}
case SpellType.NonCombat:
{
if (_miscSpells == null)
_miscSpells = new List<SpellBookPage>();
break;
}
}
}
If there are no spells of that type in the spell book yet, the member
holding those spells will be null, so well have to create it. We then go
through each page of that member, checking for an open spot by trying
to add the spell to that page. If the AddSpell function returns false for
every page, we know that all the pages are full, so we create a new one,
add the spell to it, then add that page to the member holding the pages of
that type.
Of course we need to allow the player to move a spell from one page to
another:
Listing 4-11
switch (type)
{
case SpellType.Defensive:
{
//check to see if page exists
if (!(_defSpells.Count -1 < fromPage))
break;
_defSpells[fromPage].RemoveSpell(id);
}
break;
}
case SpellType.NonCombat:
{
//check to see if page exists
if (!(_miscSpells.Count-1<fromPage))
break;
break;
}
case SpellType.Offensive:
{
//check to see if page exists
if (!(_offSpells.Count-1<fromPage))
break;
break;
}
}
return ret;
}
so the function should always succeed, but its good practice to never
assume anything.
The SpellBookPage class is just about as simple:
Listing 4-12
public SpellBookPage()
{
_spellIDs = new int[Spellbook.SpellsPerPage];
}
ret = true;
break;
}
}
return ret;
}
return ret;
}
return ret;
}
}
When the page is created, the array containing the spell IDs is created to
be the size of the SpellsPerPage constant. If you want to increase the
number of spells per page, changing the constant means you wont have
to change any other code as everything is based on the constant.
The three functions in the class all work similarly they take a spell ID
as a parameter and iterate through the array to determine if what the
function is supposed to do can be done. In the case of the RemoveSpell
and HasSpell functions, the function returns true if the spell exists in the
array. In the case of the AddSpell function, it returns true if the page is
not full.
Summary
A character can now do just about whatever he wants and can defend
himself either unarmed or with spells. He would look kind of silly just
running around naked though. Well take care of that in the next chapter.
Well add the capability of having all kinds of items in the world for the
player to find and equip his character with. Those items will also have
the ability to be magical since we now have spells that items can use.
Chapter 5
Items and Loot
Whats the first thing you see when you picture a fighter in your mind?
Do you see armor, a sword and shield, or maybe the fighter shooting an
arrow from a bow? Perhaps you imagine him on horseback, charging
with a lance. Regardless of the picture you have, more than likely the
first thing you noticed wasnt the person, but his equipment. That makes
sense, since a fighter without his gear is almost useless in an RPG
(unless hes a martial artist, but thats another chapter).
Most RPGs these days have so many different items in them that a
player cant move 20 feet without stumbling over a crate thats filled
with something, even if its mostly useless junk that he just sells for a
few coins so that he can buy whatever it is he really needs. Then theres
the MMORPG type of item distribution system where rats will drop full
pieces of plate armor. While that may not be the best way to go, youll
want to get items to players in some way. I think it does make sense for
NPCs and creatures to have items that the player can pick up after
theyre defeated, but it should be items that they would really have on
them; the armor and weapons that they used against the player, money
and other trinkets that they may be carrying around, etc.
Many gamers play RPGs because they like to collect all the highpowered gear. If your RPG doesnt have some cool stuff in it, youll lose
a good number of players. And lets face it all of that cool stuff is just
fun to play with! Fortunately its not that hard to get items into your
game.
In this chapter well implement classes that will be used to handle all
types of items armor, weapons, and miscellaneous things like scrolls,
rings, and potions. Well also develop an Inventory class to manage all
of the stuff a character will end up lugging around.
//used in
inventory/vendor dialogs
The first thing to notice is that the class is declared as abstract, meaning
instances of the class cannot be created. A class has to inherit from it and
instances of that inherited class created. Well do quite a bit of that
shortly, so dont worry. The second thing to notice is that the members
are declared as protected, rather than private, which we have to do if we
want the derived classes to be able to use them.
The _id member will be our link to the item from outside code. Since we
could potentially have items with somewhat longish and interesting
names like Flaming Platinum Sword of Vengeance vs. Trolls, we cant
really use the _name member as we do with other classes.
Speaking of the name, there are several ways to handle our flaming
sword example. Many RPGs use one or more adjectives in the name of
the weapon, but that doesnt mean you have to store those adjectives in
the name. You could create sets of metadata that you attach to various
items. Some metadata might not make sense for some items, so you
could restrict certain words from being used with those items. For
example, you might not want to allow a Flaming Platinum Potion of
Vengeance vs. Trolls to be created. If you did, I dont think I would
want my character to drink it.
Metadata: Simply put, this is a fancy word that means data about data.
An entity can only lift or carry so much weight, so our items have to
have a _weight member. A player would not be allowed to add items to a
characters inventory if the combined weight of all the items in the
inventory is over the max the character can handle. Well see this later in
the chapter when we create the Inventory class. There well also see how
the _picture member is used.
The _tag member will be used in various ways. For example, its a nice
way to allow a bit more flexibility in how items are handled and used.
Only one item will be derived directly from BaseItem the Money
class:
Listing 5-2
public class Money : BaseItem
{
public const int CoinsPerPound = 10;
amount changes
new public byte StrengthRequired
{
get { return (byte)((_amount / CoinsPerPound) /
Stat.PoundsPerStatPoint); }
}
public Money()
{
{
get { return GetWeight(_amount); }
}
}
}
}
We can see that, in the game world, 10 coins would weigh one pound.
This will be useful later on when we implement our inventory. Some
RPGs may allow the character to carry around millions of coins as well
as dozens of weapons, armor, and other items, but if you want to model a
realistic inventory, the CoinsPerPound member will help ensure that
characters cant carry around enough money to buy out a shopkeepers
entire inventory. Well add the capability to ignore weight into the
inventory, however, so that characters can carry around thousands of
pounds of gear if you wish to allow it.
If a developer needs to know how much strength is required to handle all
of the money a character is currently carrying or any amount of coins,
the StrengthRequired functions will tell him. There are also functions to
get the weight of the money a character is carrying or a certain amount
of coins.
The remaining four functions allow money to be given or taken away
from an entity in different situations, such as trading with or giving
money to another entity, buying or selling items, or just dropping it on
the ground.
Key,
Potion,
Scroll,
Shield,
Weapon,
Money
money
//in the editor
}
Of course, youll probably want to add more items for your game. The
nice thing is that you just have to add the item to this enum and youll be
able to create specific items of this type in the editor, as well see later
on in the chapter.
The _cost member is the amount in gold pieces needed to buy the item
from an NPC. The gold piece will be the standard unit of money. Many
RPGs go this route, as its easy to manage. Other RPGs and MMORPGs
use several denominations of coins. Balancing costs is much more
difficult when doing this, but gives greater flexibility.
The _magical member is set to true if an item possesses magical
qualities. This is only for items that are not normally magical, such as
weapons or armor. Scrolls, potions, and the like are already assumed to
be magical. The member will be ignored for items like these, although it
might be nice to explicitly set it to true. If the item is magical, the _spells
member is used to hold the magical properties of the item.
In some RPGs, the use of certain items is limited to specific races or
alignments. The _alignments member allows us to do this. The enum for
this member looks like this:
Listing 5-5
[Flags]
public enum AlignmentBitwise
{
None = 0,
Good = 1,
Neutral = 2,
Evil = 4
}
The next four members are used for a specific type of item one that is
the target or reward of a quest. Think King Arthurs quest to find
Excalibur to understand what these are used for. _questItem tells us if
the item is the target of a quest, _questItemPiece specifies the piece of
the quest item (say, Excalibur has been broken into pieces and has to be
found and reassembled), _questID is the ID of the quest the item belongs
to, and _questReward indicates whether the item is a reward that is given
to a character for completing a quest.
The next two members are used more often in MMORPGs than in
normal RPGs, but you might want to use the functionality in a nonMMORPG, especially if you implement multiplayer support. Rewards
from quests in MMORPGs are normally not tradeable between
characters and cannot be dropped on the ground for another character to
pick up. Once the player picks up or accepts a quest reward item, he can
use it, keep it in his inventory, or destroy it. The _droppable and
_tradeable members help implement this type of functionality.
Another feature of some RPGs is to make items wear out over time or be
able to be damaged or destroyed. In order to do the latter, we need to
know how much damage an item can take. The _baseHP member will
handle this. You may want to go even further and implement
functionality that makes the item less useful the more it gets damaged,
simulating an effect such as a sword losing its sharpness the more its
used in combat. If you do so, the _repairable member can be used to tell
whether the item can be restored to full effectiveness.
The _autoReady member will place the item either in the characters
hand if its a weapon or shield or at the proper location on the characters
body if its a piece of armor or jewelry and not just an item that exists in
that location.
The _unique member simply lets the player know if the item is the only
one of its kind in the world with those exact properties. In almost every
case, this items _droppable and _tradeable members will also be set to
false. Once the player picks up the item, he can keep it in his inventory,
put it in a private storage area (MMORPGs usually give players places
in a bank or something similar that no other player can access), or
destroy it.
The _skillRequired member should be fairly self-explanatory. There may
be cases where the player isnt quite sure whether a long sword can be
wielded one-handed, so he wouldnt know whether to train in one- or
two-handed sword skill (assuming you broke down skills that far). This
member helps alleviate any worry about that.
The Use function will be implemented in the classes that inherit from the
Item class, so well look at how each handles implementing it when we
look at those classes. For weapons, theyll be very similar, but other
types of items will differ a bit.
_damagers.Add(item);
}
{
Damager item = new Damager();
item.Affects = affects;
item.DamageAmount = amount;
item.Type = type;
if (_damagers == null)
_damagers = new List<Damager>();
_damagers.Add(item);
}
return damage;
}
return true;
}
The members of the WeaponType enum will be simple and can easily be
expanded upon:
Listing 5-7
public enum WeaponType
{
OneHandSword,
TwoHandSword,
Dagger,
Staff,
Mace,
Bow,
Crossbow
}
There is at least one type of weapon for each entity. Both Bow and
Crossbow are included so the differences in speed and damage can be
tested and chosen between.
Weapons will normally only use the first three values, but if the weapon
is magical, any of them may be added to the normal type of damage. The
Flaming Platinum Sword of Vengeance vs. Trolls we talked about
previously could deal both Piercing and Slashing damage (depending on
the type of attack used if you allow some swords to have more than one
attack type) as well as Fire damage.
The DamageAmount member can have one of two formats d<x> to
specify one of the DieType values, meaning the weapon does a random
amount of damage from 1 to some number, or <x>, meaning the weapon
does a set amount of damage if an attack with it succeeds.
The Affects member is a type of entity for our purposes (although you
could have a weapon that does special damage to inanimate objects, such
as Stonecutter from Fred Saberhagens Lost Swords series). Our
example sword would have this member set to Troll and deal the
damage specified by DamageAmount in addition to the regular damage
the weapon does.
_equipped = true;
}
else
_equipped = false;
return equipped;
}
}
Each piece of armor has only one location on the body where it can be
worn. The ArmorArea enum gives us the possible locations:
Listing 5-11
public enum ArmorArea
{
None=-1,
Head,
Neck,
Chest,
LeftUpperArm,
UpperArms,
LeftLowerArm,
LowerArms,
LeftWrist,
Wrists,
LeftHand,
//Could add fingers here for rings
Hands,
RightUpperArm,
RightLowerArm,
RightWrist,
RightHand,
//Could add fingers here for rings
Waist,
Groin,
LeftUpperLeg,
UpperLegs,
LeftKnee,
Knees,
LeftLowerLeg,
LowerLegs,
LeftFoot,
Feet,
RightUpperLeg,
RightKnee,
RightLowerLeg,
RightFoot,
LeftHandEquipped,
left hand
RightHandEquipped
right hand
}
if (_open)
return true;
_open = true;
}
return true;
}
if (ID > 0)
{
if (entity.HasSkillByID(ID))
{
Object chest = (Object)this;
return ret;
}
do
{
//get a random item type
}
while (value < _maxValue);
return _items;
}
else
{
if (!(_items == null))
return _items;
else
throw (new Exception("Invalid items list"));
}
}
}
There are a couple of boolean values that indicate whether the chest is
open, locked, or trapped. If its locked, the _keyRequired member tells
what key will open up the chest. You can use either the _name or _tag
property of the key to determine what key is needed.
If there is a trap around the chest, there are a couple of members to look
at. The _trapType can be one of the following:
Listing 5-13
public enum TrapType
{
None,
Missile,
//normal missile-type
Gas,
Magic,
//spell is cast
Explosion
}
If the trap is a Magic trap, the _trapSpell member will hold the spell to
be cast if the trap is tripped. The _trapLevel member is used against the
level of the Disarm Trap skill of the person attempting to disarm the
trap. The _damage member is used if the trap is set off and it is not a
Magic trap.
The items in the chest are contained in the _items member. This member
is set either at design time or, if the _randomlyGenerate member is set to
true, when the chest is opened and inspected. The _randomType member
is the type of item to pick from. For our purposes it can be one of the
following:
Listing 5-14
public enum ItemType
{
Armor,
Chest,
Key,
Potion,
Scroll,
Shield,
Weapon,
Money
money
//in the editor
}
Obviously, the Chest item in the enum wont be used unless the Chest
class is being used as some other kind of item. That would be up to the
level designer to determine.
The _maxValue member is used to limit the value of the items in the
chest if the contents are being randomly generated. The actual code to do
this will be filled in later once weve determined how well access items
in-game.
The Item classs Use method here is overridden to mean open the
chest. If the chest is already open, the function simply returns true. If
the chest is locked, the entity is checked to see if he has the key using the
HasItemByTagOrName function:
Listing 5-15
public bool HasItemByTagOrName(string tag)
{
foreach (Item item in _inventory.Items)
{
if (item.Name == tag || item.Tag == tag)
return true;
}
return false;
}
If the chest is locked and the entity does not have the key, the lock has to
be picked, which means the entity has to have the Pick Lock skill:
Listing 5-16
if (ID > 0)
{
if (entity.HasSkillByID(ID))
{
Object chest = (Object)this;
return ret;
}
obviously dont want to create instances of an item every time. The only
pieces of data we need are the ID and the current hit points for the item.
So we create a simple class that well store in the entitys inventory:
Listing 5-17
public class EntityItem : Item
{
private int _id;
private int _curHP;
public int ID
{
get { return _id; }
set{}
}
{
return (_curHP > 0);
}
if (_curHP < 0)
_curHP = 0;
}
If an items hit points drops to 0, the item is not usable until its repaired.
Alternatively, you could have the item be destroyed when its hit points
reaches 0 and remove it from the inventory. Additionally, the item could
be usable at a reduced effectiveness, perhaps expressed as a percentage
of _curHP to _baseHP. Many RPGs show the current state of the item in
the games GUI so the player knows when an item has to be repaired,
often turning that indicator from green to yellow to red to show the
items decreased state of repair.
Before allowing an item to be used, the Useable function should be
called. If the item is not usable, you could display a message to the
player and perhaps even move the item from being held to being stored
in the players inventory.
in his inventory. Rather than force you to accept what I think is the best
solution, however, a generic class will be implemented that can easily be
inherited from and made to function as you think best.
Listing 5-18
public class Inventory
{
//can't use a Dictionary here because the inventory
//could contain multiples of the same item, therefore the
same id
private List<EntityItem> _items;
private Dictionary<int, int> _equippedItems; //Key is an
ArmorArea
return weight;
}
return null;
}
break;
}
}
}
_items.Remove(_items[id]);
}
return null;
}
}
Summary
A character can now be created to have everything he needs to survive in
the game world. Thats good because in the next chapter were going to
allow the character to be tested by implementing a combat system.
Chapter 6
Combat (or It s Clobberin Time)
Taran looked at the oncoming horde of goblins and sighed. This was the
third time theyd 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 theyre 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 hed 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 gods name, but he always seemed to
respond to Sorals prayers, granting the group some extraordinary
power during battle. The clerics 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
Tarans 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 Sorals 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. Sorals 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 cant 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 theyre wielding and other factors. Depending
on how deep the combat system is, there could be any number of
additional factors that affect the characters 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 characters 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 hell use the special attack, and then go back
to his normal attack. Thats 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 well 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 hes using and its weight, any bonuses that
are affecting the character positively or negatively, etc. Well 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 finetune all the data that pertains to combat (item and entity stats, spell stats,
etc.).
In our game well represent the different actions an entity can take
during combat using an enum:
Listing 6-1
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 attackers blow. A bonus is added to the characters defensive
bonus for the current or next round, depending on when the character
attacks.
Riposte The character attempts to both block his attackers blow
and get in a quick retaliatory attack of his own. A small bonus is added
to the characters defensive bonus and a slight penalty is added to the
characters offensive bonus. While this type of attack is normally done
with a sword, well use it for any melee weapon. Note that this attack
and the Parry attack shouldnt show up with ranged weapons.
CalledShot The character attempts to attack a specific part of his
attackers 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 doesnt 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 doesnt
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 wouldnt be used with ranged
weapons since the enemy wouldnt 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 wasnt
expecting it to occur. If you decide to use attack types in a real-time
combat system, you need to make sure that this doesnt 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 youll easily know
where the sprite is located.
Show an icon for the sprite in the games GUI You could also
show other info about the entity: what weapon hes 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 shouldnt be. This isnt as much of a problem for singleplayer 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 hes currently carrying. This could be
problematic if the player has forgotten to equip a weapon. (Ive done this
more than once!) No matter how the player initiates the fight, once it
starts youll 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 thats 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 youll probably want to change or
disable some things such as the ability to save and/or quit the game or
perform actions that shouldnt be done in combat (such as trade items
with party members or chat with NPCs). More than likely youll find
during testing that you can do some things that you dont 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 youre using a game state system for your RPG (and why
wouldnt 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. Youll
probably want to have substates or variables that allow you to check for
things like whether it is the players 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.
{
private List<Entity> _players;
private List<Entity> _enemies;
_enemies.Add(enemy);
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;
}
return turnName;
}
ResolveAttack();
ResolveAttack();
}
_curAttacker.UseSkill(GlobalData.Items[_curAttacker.Inventory
.
EquippedItem(ArmorArea.RightHand).ID].SkillRequired,
Difficulty.Normal);
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 its
fairly short considering what it does. Thats because the real work is
done in the UseSkill method of the Entity class.
Listing 6-3
if (!((Entity)_curAttacker.Target).IsAlive)
EntityKilled((Entity)_curAttacker.Target);
AttackResolved(_curAttacker,
(Entity)_curAttacker.Target);
if (_attackOrder.IndexOf(_curAttacker) ==
_attackOrder.Count-1)
_curAttacker = _attackOrder[0];
else
_curAttacker = _attackOrder[_attackOrder.IndexOf(
_curAttacker) + 1];
}
Were 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, youll need to change this, but this will work for
our purposes.
The skill for the weapon being used is retrieved and the Entity classs
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.
Figure 6-3
Here we have Aragorn fighting a troll. Its Aragorns 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 hes 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
hes 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 doesnt 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
_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 engines ResolveAttack method is called. This is
similar to the way the RPG Starter Kit works, as we can see in Figure 64:
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.
Youll probably spend a lot of time in the simulator, tweaking the
numbers until combat is fair and somewhat realistic.
Right now an attack either hits or misses. If the attack misses, theres
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, hes 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
arent considered. An attack from the rear shouldnt take a shield into
account. If you allow invisible entities to make an attack, its 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. Its 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. Well 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 youve completed that chapter, combat will take on a whole new
dimension.
Chapter 7
Audio
You enter a dungeon and you realize you forgot to buy torches at the
shop. You cant see anything, but there are plenty of sounds that tell you
something about the area the sound of dripping water, the low growl
of some animal, the clicking of claws on stone, and the chittering of
many insects. Theres a lot of life and activity in the dungeon even
though you cant see it. Since your vision is useless you have to rely on
your sense of hearing to give you clues as to what to expect. Cautiously
you inch forward, waiting for the inevitable encounter and wondering
how youre going to fight in the dark.
All worlds are full of sound, and an RPG world is no different (at least it
shouldnt be if the developer did his job correctly). From background
sounds like a bubbling brook and birds chirping to the crackle of fire and
The easiest way to play sounds that dont require any modification to the
sound itself is to use the SoundEffect and SoundEffectInstance classes.
These classes are found in the Microsoft.Xna.Framework.Audio
namespace. For most of the sounds you normally need in a game, these
two classes provide all the functionality to play a sound with the ability
to modify the volume, panning, and pitch during playback.
To play a sound using these classes, you first load the sound using the
Content Manager:
Listing 7-1
SoundEffect soundEffect;
string soundName = "kaboom";
soundEffect = contentManager.Load<SoundEffect>(soundName);
There are three overloaded Play methods of the SoundEffect class that
you can use, depending on your needs:
Table 7-1
Name
Description
SoundEffect.Play ()
SoundEffect.Play (Single)
SoundEffect.Play (Single,
Single, Single, Boolean)
You can call the Play method that takes no parameters and modify the
Volume, Pitch, and Pan properties of the returned SoundEffectInstance.
You can also call the Pause and Stop methods of the
SoundEffectInstance if you need to.
These two classes are meant only for rudimentary sound playback. If
you need more advanced sound support, including 3D audio, you will
need to use XACT, which is covered in depth later in this chapter. For
most games, though, the SoundEffect class should be enough.
So youve got sound effects working in your game and now youre
wondering about doing things like playing a song when your game starts
or allowing the player to play his own playlist in place of what you have
in your game. Luckily, XNA Game Studio 3.0 has got you covered.
Playing Songs
New to XNA Game Studio 3.0 is support for playing songs in a media
library on the PC, Xbox 360, and Zune. The classes responsible for this
functionality can be found in the Microsoft.Xna.Framework.Media
namespace. On the 360 and Zune the media library is known, and songs
that are uploaded to the device are automatically added to the library. On
the PC, before you can use the functionality in the XNA Framework to
play songs in a media library, the library first has to be compiled using
Windows Media Player to search the PC for song files to add to the
library.
The main classes that enable song playback are:
MediaLibrary Provides access to songs, playlists, and pictures in
the devices media library.
MediaPlayer Provides methods and properties to play, pause,
resume, and stop songs. MediaPlayer also exposes shuffle, repeat,
volume, play position, and visualization capabilities.
Song Provides access to a song in the song library.
MediaQueue Provides methods and properties to access and
control the queue of playing songs.
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Media_Sample
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
MediaLibrary sampleMediaLibrary;
Random rand;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
MediaPlayer.Play(sampleMediaLibrary.Albums[i].Songs[0]);
base.Initialize();
}
base.Update(gameTime);
base.Draw(gameTime);
}
}
}
Between the new sound and song support that XNA Game Studio 3.0
offers, you should be covered. If youre not, however, you can dig into
XACT for more advanced functionality.
XACT
XACT uses compression to make sound files smaller, which is important
for Xbox games. Since your game files are stored on the Xbox hard
drive and you have limited space on it for your files, the less space your
files take up the more content you can include in your game. This isnt
as important on the PC since hard drives these days are many times
larger than the one in the 360. You also have the option of storing and
using files on CD or DVD. This isnt an option on the 360 since the
XNA Framework currently doesnt allow access to the DVD drive.
One good thing about using XACT is that it works the same for both PC
and 360, meaning you can use the same files and code for projects for
both systems. Before we get into the code though, some definitions
might be in order. The four main objects that youll be using for your
sound system are AudioEngine, WaveBank, SoundBank, and Cue, so
itll help to know what they are.
Before starting up XACT you might need to install the .NET Framework
1.1 Redistributable. Youll find the URL in the appendix. At the time of
this writing theres a known bug with a feature in XACT that causes an
error if the redistribution is not installed. After launching XACT, youll
be presented with a window that looks something like the following
figure:
Figure 7-1
Click the leftmost button on the toolbar (Creates a new project) and
give your project a name in the dialog that pops up. The next thing youll
need to do is create a wave bank to store the sounds for your game. Click
on the Wave Banks | New Wave Bank menu item or the New Wave
Bank button on the toolbar:
Figure 7-2
Youll also need to create a sound bank, so go ahead and click the Sound
Banks | New Sound Bank menu item or the New Sound Bank button on
the toolbar:
Figure 7-3
Click the Window | Tile Horizontally menu item to make it a little easier
to work with the Wave Bank and Sound Bank windows.
Now well add some wave files to the wave bank. We have four sounds
that were going to use an instrumental piece that well play in a loop
when the main menu is displayed, a sound for when the player character
attacks with a sword, a sound that plays when a monster attacks, and a
sound for when the players character dies. You can use either your own
sounds or the ones in the Content folder for this chapters project in the
downloadable zip file.
Just like other content, we will be referring to the sounds using the name
of the content:
Table 7-2
Original File Name
Content Name
song.wav
song
sword.wav
sword
attack.wav
attack
die.wav
die
If youre using your own sounds, just make sure theyre named properly.
Otherwise, youll have an exception thrown when you try to play the
sounds using the code well write a little later or youll end up playing
the wrong sounds.
Now that we have our wave bank, we need to add the sounds to the
sound bank. Select the four sounds and drag the items into the Cue
Name section of the Sound Bank window. You should end up with
something that looks like the following figure:
Figure 7-4
Now we want to set our title song to play repeatedly. Click on the sound
name called song in the Sound Bank window, and then click on the
Play Wave node under the Track 1 node to the right (you may need to
expand the node by clicking the plus sign to the left of Track 1 if its not
already expanded). In the properties window at the bottom left, find the
check box that says Infinite in the Looping group. Checking this will
cause the sound to loop until the cue is stopped.
Figure 7-5
Thats pretty much all we need to do for our game. Before building the
project, however, you might want to set the locations for where the files
are placed after building. Click on the Wave Bank and Sound Bank
nodes in the upper-left tree, and set the Windows Build Path property as
desired in the property window for each bank. Once youve done that
save the project by clicking the File | Save Project menu item. Build the
project by clicking the File | Build menu item or the Build button on the
toolbar:
Figure 7-6
Playing 2D Sound
The XACT project that was created can now be used to play the sounds
it contains. The first thing that needs to be done is to add AudioEngine,
WaveBank, and SoundBank objects and create them along with some
Cue objects:
Listing 7-3
AudioEngine audio;
WaveBank wavebank;
SoundBank soundbank;
GamepadState _curState;
GamepadState _lastState;
Cue songCue;
Cue swordCue;
Cue attackCue;
Cue dyingCue;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
content = new ContentManager(Services);
attackCue = soundbank.GetCue("attack");
dyingCue = soundbank.GetCue("die");
}
The only other thing thats needed is some code in the Update method:
Listing 7-5
if (_curState.Buttons.A == ButtonState.Pressed)
{
if (!songCue.IsPlaying)
{
songCue = soundbank.GetCue("song");
songCue.Play();
}
}
if (_curState.Buttons.B == ButtonState.Pressed)
{
if (!swordCue.IsPlaying)
{
swordCue = soundbank.GetCue("sword");
swordCue.Play();
}
}
if (_curState.Buttons.X == ButtonState.Pressed)
{
if (!attackCue.IsPlaying)
{
attackCue = soundbank.GetCue("attack");
attackCue.Play();
}
}
if (_curState.Buttons.Y == ButtonState.Pressed)
{
if (!dyingCue.IsPlaying)
{
dyingCue = soundbank.GetCue("dying");
dyingCue.Play();
}
}
{
soundbank.PlayCue("sword");
}
_lastState = _curState;
base.Update(gameTime);
}
Here we see the two different ways cues can be played. You can either
get a cue from the sound bank and keep it for playing over and over, or
you can have the sound bank play it directly.
Lets make a quick change to the buttons so that we can pause and
resume a cue. Make the following change to each button:
Listing 7-6
{
if (!songCue.IsPlaying)
{
songCue = soundbank.GetCue("title");
songCue.Play();
}
else if (songCue.IsPaused)
songCue.Resume();
else if (songCue.IsPlaying)
songCue.Pause();
Note that the order of your checks here is important. If you reverse the
checks for IsPaused and IsPlaying, youll end up pausing the sound and
never being able to get it started again, since IsPlaying will return true
when the sound is paused. We also need to check the last state of the
buttons to prevent the sound from immediately being paused. Try
removing the check of _lastState and see what happens when you press
the A button.
Lets make another modification to change the volume of the sounds.
Well replace the code in the DPad buttons to do this:
Listing 7-7
if (_curState.DPad.Up == ButtonState.Pressed)
{
volume = MathHelper.Clamp(volume + 0.01f, 0.0f, 5.0f);
}
if (_curState.DPad.Down == ButtonState.Pressed)
{
volume = MathHelper.Clamp(volume - 0.01f, 0.0f, 5.0f);
}
category.SetVolume(volume);
public Game1()
{
graphics = new GraphicsDeviceManager(this);
content = new ContentManager(Services);
category = audio.GetCategory("Default");
}
The Default category is what every sound is set to that you add to an
XACT project. You can create your own categories and group the
sounds as youd like. The one caveat here is that all the sounds in a
Summary
Theres a lot of functionality in the Audio and Media namespaces of the
XNA Framework. With XACT and the Audio namespace you can do
things like distort sounds, create a Doppler effect on a sound, or use the
events in XACT to handle sounds differently. With the Media
namespace you can do music visualization; access song properties like
the artist, genre, and user rating; and get the albums cover art. Feel free
to explore both XACT and the Audio and Media namespaces further.
As we add functionality to the game in later chapters, we can easily add
new sounds background sounds and different songs for various
environments; sounds for every weapon, item, and spell; even vocal
versions of the text. Dont just add sounds because you can, though.
Make your sounds memorable in a good way. Itll make your game more
interesting and therefore more enticing to potential players.
Chapter 8
Creating the Tile Engine and Level
Editor
Up until now, weve been dealing only with the data and objects that
make up an RPG. Finally well actually be able to see the fruits of our
efforts. The reason Ive held off with graphics is partly because Im of
the opinion that graphics are one of the least important parts of an RPG.
A lot of people might argue that point. They can feel free to write their
own book on the topic.
so long. If the guts on a game are weak the graphics wont hold it up.
Once the wow factor wears off, Ill get bored and possibly not even
finish it. On the other hand, I can put up with average graphics if theres
tons of data to play with stats to tweak and deciding what skills to
work on and what spells to learn. When Im about to go on an adventure
I like having to decide what items to take along, what armor to wear,
what weapons to use, and how much of what items to take.
Having said that, you have to have graphics or it would be hard to tell
where to go, what to do, or whats happening around you (unless youre
playing a text-based RPG, of course). In this chapter well implement a
fairly simple and straightforward 2D tile engine. While it wont have
cool spell effects and blood splatters from hacked up monsters and
dynamic, moving environments, itll do the job. As you learn how to do
these things, probably using shaders, you can implement them. Or you
can make the move to a full 3D engine. Several already exist in various
states of completion for use with the XNA Framework and Game Studio,
although some might not have made the upgrade to version 3.0. You can
check the links in the appendix for some sites that provide 3D engines.
Some are complete packages that include sound and network pieces as
well as the graphics engine.
Tiles
The basic building block of a tile engine is the tile, a usually square
(although sometimes rectangular, hexagonal, diamond, or octagonal
shaped) area of the level that holds information about that area, both
graphical and other. This information describes the terrain in that area as
well as whether the character can move onto or through the area,
whether an entity occupies that area, what other object might be in that
area (such as a building or items of any kind), and any other information
that affects either the world or an entity that moves into that area (a trap
of some kind, be it magical or nonmagical, an event trigger that fires
when an entity moves into the area, etc.).
In a level where the tiles were created with care and time, its usually
difficult to distinguish each individual tile. The artist must take the time
to create tiles that blend one area into another (water into a beach, beach
into grass, grass into rocky terrain, etc.).
Well represent a tile using the LevelSquare class:
Listing 8-1
public LevelSquare()
{
TileX = -1;
TileY = -1;
}
if (!(_triggers == null))
ret = (_triggers.Count > 0);
return ret;
}
_triggers.Add(trigger);
}
}
The class contains all the information well need to draw the level. The
first three members are used to get the specific tile in the tileset that was
used to set the graphical contents of this tile.
Tileset: A graphics file that contains many different individual graphics.
These graphics are stored in one file to make loading quicker and
handling the graphics when drawing easier.
Well be using three different tilesets in our editor two for buildings
and other structures and objects both man-made and not, and one for the
ground. Following are the tilesets that we have:
Figure 8-1
Figure 8-2
Figure 8-3
The background of each tileset is magenta so that the tiles can be drawn
over each other and not obscure the tile behind it. Magenta
(RGB[255,0,255]) is handled as a transparent color automatically by the
SpriteBatch object since its a color thats rarely used in sprites. Notice
that the buildings have shadows built into the graphic. This means were
somewhat restricted in the use of lighting, but thats not a big issue for
us. If you add support for an actual day/night cycle in your game, you
probably would have replaced the graphics beforehand anyway.
The next two members in the LevelSquare class are used to determine
whether or not the tile is trapped and, if so, what kind of trap is used. If
the tile is trapped, the trap member will be set to an instance of the Trap
class:
Listing 8-2
//normal missile-type
Gas,
Magic,
//spell is cast
Explosion
}
The Level and TrapDamager members are used to calculate the amount
of damage an entity suffers if he trips the trap, by either walking into the
tile or failing in an attempt to disarm the trap.
We looked at the Damager structure in an earlier chapter, but well
repeat it here for completeness:
Listing 8-4
[Serializable]
public struct Damager
{
public string Affects;
public string DamageAmount;
public DamageType Type;
}
ExplosionTrap = 20
}
The Range member is used if the trap damage extends beyond the tile
the trap is on. This value is a number of expanding tiles outward from
the trapped tile. A Range of 2 would affect the following tiles:
Figure 8-4
The center tile is the trapped tile. An entity in the gray tiles will also take
damage. Ive colored the tiles light and dark for a reason. Its probably
not very realistic to have entities in the surrounding tiles take full
damage. In this situation, you can have entities in the dark gray tiles take
50% damage and those in the light gray tiles take 25% damage. You
would have to do calculations based on the value of the Range member
and come up with some value if its not evenly divisible, but thats up to
you to determine how best to handle.
The Active member determines if the trap is still active. If the SingleUse
member is set to true, the trap could still exist after being tripped, but the
Active member would be switched to false.
Next in the LevelSquare class is the hasTrigger member. If set to true,
the Triggers member will contain any triggers in the tile. The triggers are
used as a sort of scripting mechanism in lieu of true scripting. The
Trigger class has just a few members:
Listing 8-6
spawn
Thats a lot of data stuffed into a tile. Put a lot of those tiles together and
you have a level.
The passable member is used in detecting collisions between an entity
and the type of terrain or object that may be on the tile. A building or an
entity standing at the bottom of a cliff would not be able to pass through
the tile. An enhancement to this collision detection could be to allow
varying types of passible tiles. Thick mud wouldnt prevent an entity
from passing through it, but would make for slow progress. Water would
require the entity to have some skill in swimming or he would drown.
[ContentSerializerIgnore]
private Dictionary<string, Texture2D> _tiles;
[ContentSerializerIgnore]
public int ViewportWidth
{
get { return _viewportWidth; }
set { _viewportWidth = value; }
}
[ContentSerializerIgnore]
public int ViewportHeight
{
get { return _viewportHeight; }
set { _viewportHeight = value; }
}
Point pt;
return squares;
}
set
{
_groundSquares = new LevelSquare[_width, _height];
Point pt;
return squares;
}
set
{
_structureSquares = new LevelSquare[_width,
_height];
}
}
break;
}
case Layer.Structure:
{
_structureSquares[x, y].TilesetName = tileset;
_structureSquares[x, y].TileX = tileCoordX;
_structureSquares[x, y].TileY = tileCoordY;
break;
}
}
}
break;
}
case Layer.Structure:
{
_structureSquares[x, y].TilesetName = "";
_structureSquares[x, y].TileX = -1;
_structureSquares[x, y].TileY = -1;
break;
}
}
}
Although we only have two layers in our level, I used a switch statement
in the methods to set and remove a tile in case you want to add more
layers. Some RPG levels have three or more layers. Just add another
value to the Layer enum and another member for the array of
LevelSquare objects, copy and paste one case, and youre done.
Listing 8-10
Structure
}
The only remaining method is the main one, the Draw method:
Listing 8-11
{
for (int y = 0; y <=
(int)(_viewportHeight / 32);
y++)
{
tileX = x + topX;
tileY = y + topY;
if (!string.IsNullOrEmpty(_groundSquares[tileX,
tileY].TilesetName))
sb.Draw(_tiles[_groundSquares[tileX,
tileY].TilesetName],
new Rectangle(x * 32, y * 32, 32, 32),
new Rectangle(_groundSquares[tileX,
tileY].TileX *
32, _groundSquares[tileX, tileY].TileY *
32,
32, 32), Color.White);
if
(!string.IsNullOrEmpty(_structureSquares[tileX,
tileY].TilesetName))
sb.Draw(_tiles[_structureSquares[tileX,
tileY].TilesetName], new Rectangle(x *
32, y * 32,
32, 32),
new Rectangle(_structureSquares[tileX,
tileY].TileX * 32,
_structureSquares[tileX, tileY].TileY *
32, 32, 32),
Color.White);
if
(!string.IsNullOrEmpty(_structureSquares[tileX,
tileY].EntityID))
{
if
(_entities.ContainsKey(_structureSquares[tileX,
tileY].EntityID))
{
sb.Draw(_entities[_structureSquares[tileX,
tileY].EntityID], new Rectangle(x *
32
_entities[_structureSquares[tileX,
tileY].EntityID].Width - 32, x * 32
_entities[_structureSquares[tileX,
tileY].EntityID].Width - 32,
_entities[_structureSquares[tileX,
tileY].EntityID].Width,_entities[
_structureSquares[tileX, tileY]
.EntityID].Height), Color.White);
}
}
}
}
}
As with many of our classes, a relatively small amount of code does a lot
of work, mainly due to the object-oriented nature of C#. A simple loop
through the LevelSquare arrays is all that is needed to get the level
drawn on the screen. Only whats visible in the viewport is drawn and
only if the ground tile and structure tile is set. The entity in the tile, if
one exists, is also drawn. Its important to draw the tiles in the correct
order from the top left to the bottom right and to draw the ground layer
before the structure layer. Youll quickly know if things are not being
drawn correctly.
Figure 8-5
Summary
We can now create just about whatever worlds we can imagine. Of
course we would have to add some new graphics to really get something
that seems like a living, breathing world, but the ability to create that
world is there. If you have a really good artist working for you, youll be
amazed at what you can create.
We now have a world, but no way for the player to really interact with it.
He cant even see what his character is wearing! In the next chapter,
well discuss what interface elements are needed to get the player to
where he needs to be as far as interacting with the world and everything
and everyone in it. Well implement a very rudimentary interface with
inventory and quest windows and a toolbar for allowing the player to
have quick access to items or skills.
Chapter 9
Interface
A games interface is the door between our world and the game world. A
good interface should be informative but unobtrusive. Too many game
interfaces are cumbersome and get in the way of playing the game. An
RPG is very complex, but some companies dont seem to try very hard
to make an interface that makes it any easier.
An interface covers such things as displaying a characters stats, skills,
inventory, and other vital information that must constantly be monitored.
It also usually offers a quick way to select and use spells and items such
as weapons and potions. Actions such as trading, buying, and selling are
also handled by the interface. Interactions with NPCs are sometimes
handled with dialogs, which are another part of the interface.
The interface can be designed in many different ways. Figures 9-1 to 93 are screenshots of parts of the interface of one RPG Neverwinter
Nights.
Neverwinter Nights also came up with a unique use for the right-click
function. Right-clicking on something brings up a set of general function
buttons around the object or entity. Clicking on one of these buttons
replaces the general buttons with more specific buttons if any are
available. The outward-pointing arrow next to a button indicates whether
or not it has more specific functionality. After spending a short time
using this, you learn how to quickly drill down to what you need. I like
this idea very much. It doesnt take up much space and, after you spend a
short amount of time learning it, you have quick access to almost
everything that pertains to what you click on.
Figure 9-1
Figure 9-2 is the character information window that is located in the top
right-hand corner of the game window. It gives you quick access to
basically everything to do with your character and any members of your
party. Immediately to the left of the characters picture is a bar that
shows the characters health. Having something like this on the screen at
all times is extremely convenient and almost necessary if your RPG is
quick moving, as the status of a character could change quickly. If you
have to stumble around the interface to find out if your character needs
healing because hes taking damage, its likely that your character could
be dead by the time you get to the information. Thats not a good way to
get people to like a game.
Figure 9-2
The piece of this interface that well be developing is a combination
inventory/character equipment window. It will show the armor and items
that the player has equipped the character with and the contents of the
characters inventory. The inventory piece will be very basic and wont
require the player to shuffle items around in the inventory. The player
will be able to drag an item thats in the inventory onto an area of the
characters equipped items, assuming its an item that can be equipped at
that location. Any item thats currently equipped at that location will be
dropped back into the inventory.
Figure 9-3 shows a couple of interface elements a status window to
give feedback to the player with information thats happening in the
game, a compass to show the direction in which the character is heading,
a small window to allow the player to talk to other players, and a toolbar
to allow quick access to items, skills, or other functionality.
Figure 9-3
The Toolbar
The ToolBar class well be using will provide the player with access to
10 items or skills. The graphic it draws will look like the following:
Figure 9-4
The toolbar in this figure has been equipped in the first two slots with a
sword and a potion from the characters inventory. The potion is a oneuse item that will be removed from the inventory and toolbar when its
used. The sword will remain as long as the character is holding it.
The code for the toolbar is fairly simple and straightforward:
Listing 9-1
int x;
base.LoadContent();
}
_lastState = state;
base.Update(gameTime);
}
sb.Begin();
sb.End();
base.Draw(gameTime);
}
}
game know which button is clicked. The code outside of the class would
be responsible for figuring out what skill, spell, or item is in that spot on
the toolbar and taking the appropriate action.
Its not very exciting, but it doesnt have to be. The code has two jobs
to show the player what items have been set to each slot and to let the
game know when a toolbar button has been clicked. The next piece of
interface the inventory window is a bit more complicated.
Figure 9-5
The InventoryWindow class that is used to display this works almost
exactly like the ToolBar class. As items are picked up, theyll fill the
slots on the right side of the screen, showing the icon for the item and
the item name next to it. The player can drag the icon from the right side
to its corresponding location on the body on the left side, and the class
will handle setting that item to be equipped on the character.
As with the ToolBar class, the InventoryWindow class inherits from
DrawableGameComponent:
Listing 9-2
base.Initialize();
}
int x = (this.Game.GraphicsDevice.Viewport.Width / 2)
(_graphic.Width / 2);
int y = (this.Game.GraphicsDevice.Viewport.Height /
2) (_graphic.Height / 2);
315,
476);
_armorRects.Add(ArmorArea.LeftLowerArm, new
Rectangle(25 + _rect.X,
175 + _rect.Y, 30, 30));
_armorRects.Add(ArmorArea.LowerArms, new Rectangle(33
+ _rect.X, 369
+ _rect.Y, 30, 30));
_armorRects.Add(ArmorArea.LeftWrist, new Rectangle(25
+ _rect.X, 212
+ _rect.Y, 30, 30));
_armorRects.Add(ArmorArea.Wrists, new Rectangle(33 +
_rect.X, 421 +
_rect.Y, 30, 30));
_armorRects.Add(ArmorArea.LeftHand, new Rectangle(25
+ _rect.X, 249 +
_rect.Y, 30, 30));
_armorRects.Add(ArmorArea.Hands, new Rectangle(110 +
_rect.X, 414 +
_rect.Y, 30, 30));
_armorRects.Add(ArmorArea.RightUpperArm, new
Rectangle(62 + _rect.X,
137 + _rect.Y, 30, 30));
_armorRects.Add(ArmorArea.RightLowerArm, new
Rectangle(62 + _rect.X,
175 + _rect.Y, 30, 30));
_armorRects.Add(ArmorArea.RightWrist, new
Rectangle(62 + _rect.X, 212
+ _rect.Y, 30, 30));
_armorRects.Add(ArmorArea.RightHand, new Rectangle(62
+ _rect.X, 249
_rect.X, 415 +
_rect.Y, 30, 30));
_armorRects.Add(ArmorArea.RightUpperLeg, new
Rectangle(264 + _rect.X,
200 + _rect.Y, 30, 30));
_armorRects.Add(ArmorArea.RightKnee, new
Rectangle(264 + _rect.X, 238
+ _rect.Y, 30, 30));
_armorRects.Add(ArmorArea.RightLowerLeg, new
Rectangle(264 + _rect.X,
275 + _rect.Y, 30, 30));
_armorRects.Add(ArmorArea.RightFoot, new
Rectangle(165 + _rect.X, 365
+ _rect.Y, 30, 30));
_font = this.Game.Content.Load<SpriteFont>("font");
base.LoadContent();
}
_lastState.LeftButton
== ButtonState.Released)
{
//figure out if an inventory item has been
clicked and set index
if (Rectangle.Intersect(_inventoryRect,
rectClicked) !=
Rectangle.Empty)
{
_inventoryIDSelected =
_inventory.Items[_topInventoryItemIndex +
(int)((state.Y _inventoryRect.Y) /
ItemInfoBarHeight)].ID;
_dragGraphic =
GlobalData.Items[_inventoryIDSelected].Picture;
}
else if (Rectangle.Intersect(_topButton,
rectClicked) !=
Rectangle.Empty)
{
if (_topInventoryItemIndex > 0)
_topInventoryItemIndex--;
}
else if (Rectangle.Intersect(_bottomButton,
rectClicked) !=
Rectangle.Empty)
{
if (_topInventoryItemIndex +
((int)(_rect.Height /
ItemInfoBarHeight)) <
_inventory.Items.Count - 1)
_topInventoryItemIndex++;
}
}
else if (state.LeftButton == ButtonState.Released &&
_lastState.LeftButton == ButtonState.Pressed)
{
//figure out if an inventory item has been
dragged
//onto an equip area
if
(GlobalData.Items[_inventoryIDSelected].GetType() ==
typeof(Armor))
{
foreach (KeyValuePair<ArmorArea, Rectangle>
kvp in
_armorRects)
{
if (Rectangle.Intersect(kvp.Value,
rectClicked) !=
Rectangle.Empty)
{
//see if item can be equipped in
this spot
if
(((Armor)GlobalData.Items[_inventoryIDSelected]).
Location == kvp.Key)
{
_inventory.EquipItem(_inventoryIDSelected,
kvp.Key);
}
}
}
}
else if
(GlobalData.Items[_inventoryIDSelected].GetType() ==
typeof(Weapon) &&
Rectangle.Intersect(rectClicked, new
Rectangle(_rightHandItem.X + _rect.X,
_rightHandItem.Y +
_rect.Y, _rightHandItem.Width,
_rightHandItem.Height)) !=
Rectangle.Empty)
{
_inventory.WeaponEquippedID =
_inventoryIDSelected;
}
else if
(GlobalData.Items[_inventoryIDSelected].GetType() ==
typeof(Shield) &&
Rectangle.Intersect(rectClicked,
_leftHandItem) != Rectangle.Empty)
{
_inventory.ShieldEquippedID =
_inventoryIDSelected;
}
_inventoryIDSelected = -1;
}
_lastState = state;
base.Update(gameTime);
}
sb.Begin();
int count = 0;
//draw items
foreach (EntityItem item in _inventory.Items)
{
sb.Draw(GlobalData.Items[item.ID].Picture, new
Vector2(_inventoryItemRects[count].X +
_rect.X + 4,
sb.DrawString(_font,
GlobalData.Items[item.ID].Name, new
Vector2(_inventoryItemRects[count].X +
_rect.X + 48,
_inventoryItemRects[count].Y + _rect.Y + 8),
Color.White);
count++;
if (count == 10)
break;
}
if (_inventory.EquippedItemID((ArmorArea)area) >
0)
{
sb.Draw(_inventory.EquippedItem((ArmorArea)area).Picture,
_armorRects[(ArmorArea)area],
Color.White);
}
if (_inventory.ShieldEquippedID > 0)
{
sb.Draw(GlobalData.Items[_inventory.ShieldEquippedID].Picture
,
new Rectangle(_leftHandItem.X + _rect.X,
_leftHandItem.Y +
_rect.Y, _leftHandItem.Width,
_leftHandItem.Height),
Color.White);
}
if (_inventory.WeaponEquippedID > 0)
{
sb.Draw(GlobalData.Items[_inventory.WeaponEquippedID].Picture
,
new Rectangle(_rightHandItem.X + _rect.X,
_rightHandItem.Y
+ _rect.Y, _rightHandItem.Width,
_rightHandItem.Height),
Color.White);
}
sb.End();
base.Draw(gameTime);
}
}
One thing youll probably notice right off is all the hard-coded Rectangle
objects. While Im not a big fan of hard-coding numbers, theres not
much way around it here. You have to know where the player can click
to drag and drop items.
The _inventoryRect member allows us to check whether the player
clicks in the area that displays the inventory items to see if hes
attempting to drag an item to equip it. We do this by creating a
Rectangle object for the pixel that the player has clicked on and seeing if
it intersects with the _inventoryRect member:
if (Rectangle.Intersect(_inventoryRect, rectClicked) !=
Rectangle.Empty)
If the player has clicked on an item, we find the ID associated with that
item and keep track of it in the _inventoryIDSelected member. Once the
player releases the left mouse button, we figure out where the player has
dropped the item and whether that spot is valid based on the type of item
he dragged:
Listing 9-3
{
foreach (KeyValuePair<ArmorArea, Rectangle> kvp in
_armorRects)
{
if (Rectangle.Intersect(kvp.Value, rectClicked)
!= Rectangle.Empty)
{
//see if item can be equipped in this spot
if
(((Armor)GlobalData.Items[_inventoryIDSelected]).
Location == kvp.Key)
{
_inventory.EquipItem(_inventoryIDSelected, kvp.Key);
}
}
}
}
else if (GlobalData.Items[_inventoryIDSelected].GetType()
==
typeof(Weapon) && Rectangle.Intersect(rectClicked,
new
Rectangle(_rightHandItem.X + _rect.X,
_rightHandItem.Y +
_rect.Y, _rightHandItem.Width,
_rightHandItem.Height)) !=
Rectangle.Empty)
{
_inventory.WeaponEquippedID = _inventoryIDSelected;
}
else if (GlobalData.Items[_inventoryIDSelected].GetType()
==
typeof(Shield) && Rectangle.Intersect(rectClicked,
_leftHandItem) != Rectangle.Empty)
{
_inventory.ShieldEquippedID = _inventoryIDSelected;
}
Dictionaries where ArmorArea values are the keys for the Rectangle
objects and item IDs so all that has to be done is a simple lookup to see
if an item is in that area. We do this, returning a value from the
EquippedItemID thats either an item ID or 1 if theres nothing
equipped in that area:
Listing 9-4
if (_inventory.EquippedItemID((ArmorArea)area) > 0)
{
sb.Draw(_inventory.EquippedItem((ArmorArea)area).Picture,
_armorRects[(ArmorArea)area], Color.White);
}
}
else
return -1;
}
Drawing the inventory items is even easier. We simply get each item in
the inventory. The icon and name of the item are drawn for up to 10 of
them, and the name of the item is offset to the right of the icon:
Listing 9-5
count++;
if (count == 10)
break;
}
The sample project for this chapter puts both of these interface elements
together in one game window, as seen inFigure 9-6.
Figure 9-6
GUI Libraries
Rather than creating your own GUI windows, you could take a look at
the various GUI libraries that exist for use with XNA Game Studio
games. Although they are all still in the early stages of their life (and
some have already died through starvation and neglect) and thus tend to
be buggy and incomplete in terms of features, using one of these
libraries may save you time on the front end of development in getting a
GUI up and running quickly. You may have to spend time later on in
development if you come upon a bug or need a feature that isnt
implemented. If the creator of the library has released the source code,
you can fix the bug or implement the feature yourself. If not, youll have
to report the bug or feature request and wait for the release of a new
version.
One GUI library that Ive found to be fairly full featured is the Neoforce
Controls library, which can be found
athttp://tomshane.cz/dnn/Default.aspx. It currently has 20 controls in it
and supports a lot of nice features:
Resizable windows
Movable windows
Auto-scrolling windows
Windows shadows
Transparent controls
Skinnable hardware cursor
Probably the nicest feature of this library is that its skinnable, meaning
you can change the way the controls look simply by creating a text file
that describes the appearance of the controls. You then tell the controls
to use this skin when you create them.
One problem you may run into when searching for a good GUI library
for XNA Game Studio games is that some of the ones that were built for
previous versions of XNA Game Studio have been abandoned and dont
necessarily indicate which version they were built for. If the latest
version of a library is over a year old, it will have been for an older
version and youll probably run into some issues with trying to get it
working for XNA Game Studio 3.0, especially if you want to get your
game running on the Xbox 360. Feel free to take a look at the code
though, as youll almost always learn something useful that can be
applied to what youre doing.
having an add-on for Visual Studio), I think its overkill for the average
indie developer.
As you gain more experience, you could begin constructing a GUI
library and replace pieces of the interface with a custom version built
using individual controls. Since this is a big area that XNA Game Studio
and the XNA Framework are lacking in, the community would definitely
welcome such a library, myself included.
Some specific things that could be improved:
The InventoryWindow class doesnt allow for unequipping items. You
can only replace an equipped item with another item. There are several
ways to fix this:
Implement code to check for a right-click on an area and unequip the
item
Implement code to handle dragging an item off an area and unequip
the item
Although the Item class has an _autoReady member, the functionality
to equip an item when the player picks it up is not implemented.
The _dragGraphic texture is being set for the item in the inventory
thats being dragged, but its not being drawn as the drag progresses.
This would be a simple matter of grabbing the X and Y coordinates from
the _lastState member and drawing the _dragGraphic texture as long as
the left mouse button is down. Checking the _inventoryIDSelected
member should suffice here as its set to 1 when the mouse button is
released.
The InventoryWindow class cannot be moved. The graphic could be
modified to have a title bar on which you can check for a click. You
could then allow the player to drag the window. Whether or not you
allow the player to drag the window partially off the screen is up to you.
If you choose not to, you have to implement boundary checks on the
edges of the screen.
Repairing (and damaging for that matter) items is not implemented,
although the members are there to handle it. This would probably require
more than just an interface to allow the player to select an item to repair,
since in RPGs you normally have to take the item to a black smith or
armorer, but the interface is important. You may want to charge the
player to repair items. In this case, you would have to let the player
know how much its going to cost and allow him to confirm that he
wants to have the item repaired.
Since a player can have more than one of the same item and these
items can have different properties, displaying only the name of the item
is not enough to allow the player to figure out which one he wants to
use. You could shrink the font used to display the item and include
enough of the properties of an item to allow the player to figure out
exactly what its capable of.
The toolbar doesnt currently support shortcut keys. Ideally this would
be configurable and saved in a configuration file of some kind.
Summary
This has been a very brief look at several basic pieces of an interface that
every RPG needs.
A good interface is a must. It will aid players without drawing them out
of the game world. Ideally, the interface should allow the player to
customize it to suit the way he plays the game and remember that
customization the next time he starts up the game. Theres plenty of
room for improvement in the small pieces of interface weve developed
in this chapter. It was never meant to be the ultimate interface
experience, just provide some basics in how to build one. You should
now have enough understanding to modify what was developed here so
that it works the way you determine it should work for your game. Try to
get some feedback from other gamers to see how well your interface
works before you get too far into development and are stuck with hardto-modify code. Keeping your interface code design open and modular
will benefit you when you get feedback that requires changes to the
interface.
In the next chapter well be developing code to create quests and
handling having a player work through them. You can use the
knowledge youve gained in this chapter to begin the creation of a
window in which to display these quests.
Chapter 10
Quests
The backbone of any good fantasy or sci-fi story is the quest the main
character undertakes. It may be the lone knight journeying through the
countryside and defeating all enemies who stand in his way until he gets
to the castle where the evil king/knight/wizard is holding the princess.
Or it could be a cave where the dragon that has been ravaging the land is
hiding. Whatever it is, you know that a huge battle is going to happen
near the end of the story.
Along the way, the character could have interesting little side
adventures, or subquests. They might not have any effect on the main
quest or they could be vital to its success. Saving a small village from a
pack of orcs is an example of a subquest that might not affect the main
quest. The character may think that undertaking the subquest could delay
him from completing the main one or he may consider the fact that he
might not survive a battle with the orcs so he decides not to accept it.
Accepting or declining this subquest may have no effect on his
completing the main quest, unless he dies, or there could be some side
effects of his decision not to accept it.
Perhaps hes a member of an organization that has made ridding the land
of orcs its goal. If word gets back that he didnt accept the subquest, he
could face being kicked out of the organization. Or say the character is a
paladin and one of his oaths is to protect the helpless. His church or god
may not like the fact that he abandoned the village. Even if the character
rescues the princess or kills the dragon, this fact may be overshadowed
by the penalty of not accepting the side-quest.
What if the character has set out to slay a dragon and hears a rumor that
its invulnerable to all weapons but a particular magic sword that has
been lost for centuries? The character would have to first find that sword
before he could face the dragon. This is an example of a subquest that
must be completed before the main one can be finished. Or the subquest
could be finding a key that unlocks the room holding the princess since
the room has been magically sealed against all entry without the key.
Subquests may just make the story more interesting or may be one or
more necessary steps in the completion of the main quest. Quests are a
staple of most stories either way. The same is true for most good RPGs,
whether theyre single player or multiplayer. Some of the MMORPGs
that Ive played EverQuest, World of Warcraft, and Dark Age of
Camelot among them had hundreds of different types of quests that
characters could undertake. The quests covered everything from running
notes between different NPCs to slaying a monster for an item. Most of
them had several steps that had to be completed before the quest was
finished. Completing quests was one of the things that made playing the
game so much fun. When you see a character run by with a flaming
sword you would most likely say, Wow, I have to get one of these!
and then spend the next couple of days or weeks figuring out the steps
// 0 means no limit
// array of FactionAdjust
private List<KeyValuePair<int,int> _factions;
// array of QuestStep
private List<QuestStep> _steps = new List<QuestStep>();
}
As with other classes, the _id member will be used in Entity objects to
find a quest. Why do I say used in Entity objects? Quests are usually
given to and undertaken by entities. Sure, you could have a quest just
lying around waiting for the player to come by and accept it (say, finding
a piece of paper with a map to a treasure on it or a Help Wanted job
board in the center of a town), but its easier to just have them come
from an entity. You could certainly expand quests to come from places
like the aforementioned map or job board, and I would encourage you to
do so if you plan on creating a real world in your RPG.
A FedEx quest, named after the delivery company, is a quest in which
the player has to deliver an item to an entity. The item could be given to
the player by the entity giving the quest or it might have to be found by
the player. In the latter case, the entity giving the quest could be who the
item is delivered to or it could be another entity.
The _type member can be used to allow filtering of quests by the
programmer, designer, or player. This can be useful in determining how
many of what types of quests are available to players. If youve designed
100 FedEx quests and only 25 Kill quests, you may want to come up
with more of the latter type quests. Currently this member may be one of
the following:
Listing 10-2
There are probably more types of quests that could be implemented, but
these will do for now and well only actually implement one of them in
our sample game.
Although a quest may be of a certain type, it can actually contain steps
or even subquests of totally different types. How complex you make
your quests is totally up to you. Thats not to say that the more complex
a quest is, the better it is. Sometimes more is less. You could construct a
quest that takes the player a month of playing time to complete, but
making it interesting enough to make the player want to complete it
could be difficult. If the player has to go all over the game world and
listen to NPCs ramble on and on about things before getting the next step
Item,
Money,
Spell,
Weapon
}
in level. Note that the _requiredClasses member allows for more than
one type of class to be given the quest. Its simply the names of the
classes separated by a comma. The logic for interacting with entities will
figure out if the character can be presented with the quest.
You could change the way these two members will be used in order to
allow different pairings of class and level. For instance, you could
specify that a low-level fighter can complete the quest, but the same
quest will require a higher level mage. This is a useful thing to
implement and isnt difficult to do, although it will take some effort to
ensure it works nicely.
If the quest is a generic one, you can allow the player to undertake it
more than once by setting the _isMultipleAllowed member to true. This
is usually done for the Item or KillMonster quests since the other two
types rely on something that is probably unique. You could create a
FedEx quest to, say, deliver items from a warehouse to a storekeeper,
however. It all depends on what you want to do. Thats the nice thing
about a flexible system.
If a quest is allowed to be undertaken more than once, the
_timeBetweenQuests member will need to be consulted to determine if
the player can be presented with the task when he interacts with the
entity after completing the quest the first time.
Although time isnt usually a factor in most non-MMORPGs, the
capability to require a quest be completed in a certain amount of time is
there. The _timeLimit member will be used along with two members of
another class to determine if the player has completed the quest in time.
This is actual playing time, not real time.
The _factions member is something I kind of borrowed from the
MMO genre. There are probably single-player RPGs that use this type of
idea, but its widely used in MMOs. A faction can be a race, a group of
the scores for the different factions so that this occurs. This assumes that
the player accepts and completes these quests, of course.
As was noted before, a quest is made up of one or more steps. The _steps
member controls how the quest proceeds, helping to inform the player
what he needs to do to move along in a quest. The QuestStep class that
makes up the items that fill this member looks like this:
Listing 10-4
way instead of just having the subquest as additional steps in the quest if
the subquest is one that can be obtained outside of the current quest. An
example is a quest where an entity wants an article of clothing made
from the skin of some type of animal. The character goes to the store to
buy the article, only to find out that the storekeeper doesnt have the
pelts to make the article. The character is then given the subquest to kill
<x> number of that animal and retrieve the pelts. This Kill quest might
also have been something the character could have obtained if hed gone
to the shopkeeper to buy or sell something before hed accepted the
current quest. Completing this subquest would then fulfill the
requirement of the _requiredItems member, the pelts.
While its unlikely that most RPGs would do this, you can include the
ability for a step to require a certain level, just like a quest itself could
require a certain level. The _minimumLevel member allows you to do
this. This level could be any number greater than either the previous step
or the _requiredLevel member of the quest itself.
The _timeLimit member works just like the one for the Quest class. Note
that this value isnt added to the _timeLimit member of the Quest class.
That member should be the time to complete the entire quest. The
elapsed time will be tracked for both the quest and the current step.
When a player accepts a quest, an AssignedQuest object is added to the
_quests list in the Entity object for the character. The AssignedQuest
class looks like this:
Listing 10-6
This class keeps track of the ID of the quest, the current step the player is
on, the time the quest and current step started and finished, the ID of the
entity that gave the player the quest, and whether or not the quest has
been completed. The last member is used instead of just checking the
_timeQuestFinished member because of the possibility of the quest not
being finished. When the _timeStepFinished member is filled in, the
value is compared with the time the step started and then the quest is
checked to see if there was a time limit on the step. If so and the player
exceeded that time, the quest is aborted, setting _questFinished to true
but not filling in the _timeQuestFinished member.
The first member holds the quest IDs for any quests an NPC may have to
give to the player and the second is the list of quests the player has
accepted, which is only used in the player character Entity object. This
list will be iterated through as part of the games update code to check
timing of steps and the overall quest.
Creating Quests
Designing quests is something youll want to give some thought to so
that theyre interesting and challenging, but actually creating them isnt
hard. Its a simple matter of firing up the Quest Editor, filling in some
controls, and saving. The following figure shows the application in the
middle of entering some data for a quest:
Figure 10-1
When you start the application, it will load any existing quests that it
finds in the application folders Quest folder and put the quest names in
the Quests combo box. This means we need another function in the
GlobalFunctions class to handle the loading of the quest data:
Listing 10-8
try
{
XmlReader reader = XmlReader.Create(new FileStream(
filename, FileMode.Open));
Quest quest =
IntermediateSerializer.Deserialize<Quest>(
reader, null);
reader.Close();
return quest;
}
catch (Exception ex)
{
return null;
}
}
The controls will be empty (the combo boxes will have items in them,
but nothing will be selected). You can start filling them out or select an
existing quest from the combo box and the data for the quest will be
filled in.
Clicking on the button to the right of the Factions Affected list box will
display a small form where you can edit the factions affected by the
quest:
Figure 10-2
If factions already exist, theyll be passed to the form when its created.
Clicking on a faction will display the Adjustment value in the Amount
text box. If you want to change it, simply change the value or clear it out
and go on to the next faction or click the OK button.
In looking at the quest shown in Figure 10-1, we would guess that it
wouldnt make dwarves very happy if they were to find out a character
was killing their kinfolk. In this case the Dwarves faction would get a
negative value for the amount. But if the dwarves were at war with the
goblins (and when arent they in almost every fantasy novel out there?),
the goblins would be happy with the character so that faction would get a
positive value.
The form keeps track of everything thats entered and, when you click
the OK button, the Quest Editor form will get those values and update
the Factions Affected list box.
The data on the Quest Editor form is just that data for the quest itself,
not for the steps that make up the quest. To edit the steps for the quest,
click the Edit Steps button, which displays the following form:
Figure 10-3
Note that any subquests have to be created prior to creating the quest that
uses them in order to populate the Subquests combo box. This should be
obvious, but its worth pointing out. You can always go back and edit a
step that requires subquests, but creating the subquests in advance will
save time, which is always a good thing when developing an RPG if you
havent figured that out by now.
Any items, either for a step or as a reward for the quest, will also have to
be created to populate the Give Item and Get Item combo boxes as well
as those in the Required Items area.
Make sure the Journal Entry data is clear and concise. The player will
probably be depending on it to remind him what he has to do, especially
if he has several quests going on at once.
Summary
Im sure you have a lot of ideas for exciting, challenging, and fun quests
and are anxious to get started creating them. Remember to take time
when designing them as theyre arguably the most important part of an
RPG. Before you go rushing out to create the next Fellowship of the
Ring quest, you might want to check out the next couple of chapters,
especially since the next one will help you give voice to your quests by
Chapter 11
Conversations/Scripting
When a player starts up an RPG, he expects a vibrant, living, breathing
world populated by people and creatures that he can interact with. These
interactions could include holding a conversation with an NPC or
fighting a monster. If a character were to walk up to a bartender, he
would expect to be able to order a drink and get the latest information
about what has been happening in the area. The same holds true for
being able to interact with a blacksmith to repair equipment, a
shopkeeper to buy supplies, or any other NPC. If the person just stands
there when the character tries to talk to him, it becomes obvious that the
NPC isnt a person at all, but just a graphic being drawn on the screen.
This breaks the immersion a player expects from an RPG. Fortunately,
with scripting its possible to simulate living beings to some degree. You
wont be able to discuss philosophy or politics with people or have a
creature that is very smart in its combat tactics, but youll be able to have
a degree of interaction, which goes a long way in making a game
interesting.
In this chapter, well construct and discuss a conversation editor and the
associated classes. Conversation scripts will allow the character to talk to
NPCs. This is normally done so that quests can be assigned, but they can
also be used just to make an NPC react to the character. Figure 111 shows the completed editor:
Figure 11-1
[NonSerialized]
private int _idCounter = 0;
[NonSerialized]
private ConversationNode _curNode;
public Conversation()
{
}
public int ID
{
get { return _id; }
set { _id = value; }
}
return node.Responses;
}
node.Text = text;
node.NodeFunction = functionType;
node.NodeCaseType = caseType;
node.FunctionName = functionText;
node.FunctionParams = paramText;
_nodes.Add(node);
return _idCounter;
}
node.Text = text;
node.NodeFunction = functionType;
node.NodeCaseType = caseType;
node.FunctionName = functionText;
node.FunctionParams = paramText;
AddNode(node, parentNodeID);
return _idCounter;
}
if (child.Responses.Count > 0)
{
parent = FindNodeByID(child, id);
if (parent != null)
break;
}
}
return parent;
}
public ConversationNode()
{
public int ID
{
get { return _id; }
set { }
}
Each node in the conversation has its own ID, mainly for determining
where in the conversation the two entities are currently or previously (in
the case of a conversation spanning completing a quest, which well get
to in Chapter 13). The text is whats displayed to the player. The three
function x properties are used to determine what course the conversation
takes. The same conversation could happen more than once and the
conditions are used to keep the conversation from being repeated exactly
the same each time or to keep the NPC from giving incorrect responses.
The _functionType property can have one of three values:
Listing 11-3
A node in the conversation can be set to have a check done before being
displayed, allowing you to filter responses based on selected criteria (a
PreFunction) or to have a check done if the player selects the response (a
PostFunction).
The _functionName property determines what function is called.
Currently, this is somewhat hard-coded to be one of the following:
Listing 11-4
one of the two responses. Since were not doing any checking of this
type, the function simply checks to see if the NPC has at least one quest:
Listing 11-6
_questIDs is another List<int> that provides indexes into the global list
of quests that would be created using the Quest Editor from the previous
chapter.
AssignQuest Almost self-explanatory. If the NPC has a quest and
the player agrees to undertake it, this function is called. Its passed a
couple of parameters to make sure the quest can be kept track of:
Listing 11-7
quest.QuestID = questID;
quest.CurStep = 1;
quest.TimeStepStarted = startTime;
quest.TimeQuestStarted = startTime;
quest.QuestGiverID= npcID.ToString();
quest.QuestFinished = false;
_quests.Add(quest);
}
return ret;
}
Listing 11-9
return ret;
}
You would only do this if you needed to call a function with some of the
responses, however. Thats the only reason for needing the CaseType.
Otherwise, you would just construct your conversation nodes as normal.
Thats really all there is to the conversation code. Well need some code
to manage conversations, though, so that we can test them and have
them work the same during the test as they will in a game. Lets take a
look at the ConversationManager class that will do that.
Figure 11-2
Heres the code behind the form. Some of the functionality will be
reused in the games GUI once its created:
Listing 11-13
ConversationManager _manager;
List<ConversationNode> _responses;
InitializeComponent();
_conversation = conversation;
_player = playerCharacter;
_npc = npc;
_responses = _manager.GetResponses();
SetResponses();
}
Button btn;
{
btn = new Button();
btn.Text = node.Text;
btn.Width = TextRenderer.MeasureText(btn.Text,
btn.Font).Width +
20;
btn.Click += new EventHandler(btnResponse_Click);
btn.Tag = node.ID.ToString();
btn.Location = new Point(10,
(grpResponses.Controls.Count *
btn.Height) + (15 *
grpResponses.Controls.Count) + 15);
grpResponses.Controls.Add(btn);
}
_manager.SelectResponse(Convert.ToInt32(((Button)sender).Tag)
);
List<ConversationNode> responses =
_manager.GetResponses();
lblTest.Text = "";
if (_manager.GetCurNode().NodeFunction ==
FunctionType.PostFunction)
{
switch (_manager.GetCurNode().FunctionName)
{
case "HasQuest":
{
if (_npc.HasQuest())
{
//filter responses
foreach (ConversationNode response in
responses)
{
if (response.NodeCaseType ==
CaseType.CaseTrue)
{
_manager.SelectResponse(response.ID);
lblTest.Text = response.Text;
}
}
}
else
{
foreach (ConversationNode response in
responses)
{
if (response.NodeCaseType ==
CaseType.CaseFalse)
{
_manager.SelectResponse(response.ID);
lblTest.Text = response.Text;
}
}
}
_responses = _manager.GetResponses();
SetResponses();
break;
}
case "AssignQuest":
{
_player.AssignQuest(Convert.ToInt32(
_manager.GetCurNode().FunctionParams[0]),
_npc.Name, 0);
break;
}
case "CheckForQuestCompletion":
{
//filter responses
foreach (ConversationNode response in
_responses)
{
if (response.NodeCaseType ==
CaseType.CaseFalse &&
!_player.CheckForQuestCompletion(
Convert.ToInt32(response.FunctionParams[0])))
{
_manager.SelectResponse(response.ID);
lblTest.Text = response.Text;
}
else if (response.NodeCaseType ==
CaseType.CaseTrue
&&
_player.CheckForQuestCompletion(
Convert.ToInt32(response.FunctionParams[0])))
{
_manager.SelectResponse(response.ID);
lblTest.Text = response.Text;
}
}
responses = _manager.GetResponses();
SetResponses();
break;
}
}
}
else
{
_responses = new List<ConversationNode>();
SetResponses();
}
}
}
The first time you test the conversation, the FirstTime function that is the
PreFunction for the first node in the conversation will return true, setting
the current node to the first node and displaying the text for the node.
The responses for the first node are returned by the call to GetResponses,
and buttons are dynamically created and the response text is used to set
the buttons Text property. A Click event handler is added to each button
in the list of responses to handle the selected response. All the buttons
use the same handler. The code in the event uses the sender object to get
the selected response.
When you click the Start button, the ConversationManagers Start
method is called to get the starting node. In most conversations this is
going to be the first node, but it doesnt have to be. The responses for
that node are then retrieved and used to create the buttons that you use to
select a response.
When you click a response, the Tag property of the button is issued to
get the selected response and continue the conversation. The selected
response is checked to see if theres a PostFunction attached to it and, if
so, calls it to resolve what node to show next, if any.
The ConversationManager is fairly small:
Listing 11-14
ConversationNode _curNode;
_conversation.Initialize();
_curNode = _conversation.GetCurNode();
if (text == "")
_curNode = _conversation.GetNextSibling();
}
return text;
}
(!_player.HasPlayerTalkedToNPC(_npc.Name))
text = _curNode.Text;
break;
}
case "QuestAssigned":
{
if
(_player.QuestAssigned(Convert.ToInt32(
_curNode.FunctionParams[0])))
text = _curNode.Text;
break;
}
case "CheckForQuestCompletion":
{
if
(_player.CheckForQuestCompletion(Convert.ToInt32(
_curNode.FunctionParams[0])))
text = _curNode.Text;
break;
}
}
}
else
text = _curNode.Text;
return text;
}
responses = _conversation.GetResponses(_curNode.ID);
//filter responses
foreach (ConversationNode response in responses)
{
if (response.NodeFunction ==
FunctionType.PreFunction)
{
switch (response.FunctionName)
{
case "FirstTime":
{
if
(_player.HasPlayerTalkedToNPC(_npc.Name))
responses.Remove(response);
break;
}
case "QuestAssigned":
{
if
(!_player.QuestAssigned(Convert.ToInt32(
response.FunctionParams[0])))
responses.Remove(response);
break;
}
}
}
}
return responses;
}
The biggest chunk of code is that which gets the responses for the
current node (the current node is what the NPC is saying). It has to
resolve any PreFunction criteria to determine what responses are valid to
return. The PreFunction criteria is also used to determine the initial
response for the NPC at the beginning of the conversation.
Summary
Were now into the home stretch. With everything thats been discussed
to this point, you have all the knowledge you need to put together a basic
single-player RPG. Whats that you say? What if you want to create an
RPG you can play with a friend? Hmmm, good question. Tell you what
turn to the next chapter and well see what kind of information we
can dig up on the issues involved with creating multiplayer RPGs and
what the XNA Framework and XNA Game Studio can do to help you
create one.
Chapter 12
Multiplayer Considerations
So there you are, alone in a dungeon and you realize that youre in an
area thats just a bit out of your league. Its time to call in the
reinforcements. You head back to town and stop by the local tavern
where you post a notice for other adventurers, letting them know what
youre planning and that you need some help. You stop by the next day
and there are a couple of people who saw your notice and would like to
join you, for a cut of the treasure that you recover, of course.
Adding multiplayer capability to a game is almost a requirement these
days. It used to be that adding multiplayer functionality was extremely
difficulty to do well. Part of that was an Internet that was young and not
quite up to the task of handling all the data that was passed through it,
resulting in gameplay that was less than ideal. With broadband
connections accessible to almost everyone at relatively cheap prices, its
become more expected to have some sort of multiplayer capability in
most games.
Figure 12-1
This is known as a peer-to-peer type of setup. Once the game is started,
every player will send all the necessary messages to every other player.
Any validation of data is done on every client. Its not the most secure
method, but its less work than writing a separate server application.
The server is the component that controls the gameplay to a certain
extent and allows clients to join. Its usually a separate piece of software
from the game application in order to keep it free from the prying eyes of
players seeking an exploit. The person controlling the server usually can
set certain options of the game time limits, the number of people that
can join, victory conditions, etc. The server is the boss. It enforces the
rules of the game and can set up certain rules the way it wants. The setup
for this type of architecture would look like this:
Figure 12-2
In some cases the server could be running on the same machine as a
player that is connecting to it. Such a setup would look like this:
Figure 12-3
The server controls communication between the players, passing
messages to the proper parties and sending out its own messages as
needed, such as notifying players when someone joins or leaves the
game. It validates player movements and changes what a player has tried
to do if it violates the games rules. You can think of part of the servers
responsibility as a referee at a sporting event.
If the server is the referee, the clients are the players, doing what the
server tells them when necessary, but otherwise playing the game the
way they want. The client doesnt know anything about the way the
multiplayer aspect of the game works; it just knows how to join and quit
a game and how to send and receive messages, either to other players or
through the server. It usually doesnt verify any messages it receives; it
just accepts them and continues doing what it was doing. It relies on the
server to make sure the other players are doing what they say theyre
doing.
So now we know what our options are from an architectural standpoint.
The various types of multiplayer games that can be created will use one
of these types of architectures. Which one is up to you. Some pairings
will make more sense than others once you look at them both.
Multiple-Player Party
The multiplayer party is the example that was used at the beginning of
the chapter, but it could also be random people that get together in a
game lobby or even in the game in the case of an MMORPG. This type
of game can use any of the network architectures, which in most cases
will determine how the players can find one another and join together.
Lionhead Studios added a multiplayer component to the sequel to its
mega-hit Fable. Fable 2 allows a player to jump right into a friends
game over Xbox LIVE, through System Link, or on the same console.
There are restrictions to this (for example, the joining player has to use a
premade character, not one he uses when he plays his single-player
game), but it at least allows players to have fun together. The player
hosting the game can set various rules how to split loot that is gained
between the players, whether friendly fire is active, etc.
Here are some other things youll need to consider when setting up a
multiplayer party type experience:
How is experience split between players? Do you split it equally? If
so, is a player that does 95% of the damage to an enemy or takes 95% of
the damage an enemy inflicts on the party (think MMORPG tank type
character, whose job is to take the punishment his character is built for
so others who cant take it dont have to) going to complain about how
unfair the experience is split? If you try to make it fair, youre adding
more code to do the calculations and thus more testing.
Its almost inevitable that a player will find loot that he cant use but
another player can. How do you handle getting the item from one player
to another? Do you force the player with the item to drop it and the other
player to pick it up? This is probably not the best solution. Do you allow
the player with the item to drag it from his inventory onto the other
players character? That would be nice, but now you need to implement
functionality to recognize when this occurs, which potentially opens up a
whole new can of worms. You would probably want to implement a
trade dialog of some kind, which means you need to plan for the
additional coding and testing that will be required. If you have a dialog
thats used for buying items from and selling items to an NPC
shopkeeper, you might be able to reuse it and cut down on the coding,
but it will have to be tested in both cases.
How do you handle level disparities between characters? If you allow
characters with a sizable level gap between them in the same party, you
could end up with the experience being too easy or too hard for players.
You could dynamically generate enemies that are of a level thats the
average of the entire party, but again youre adding more code to do so,
which takes more development time and means more testing. Are you
noticing a pattern here? The more functionality you add to your game,
the more time youll need to allocate for development and testing. This
might seem obvious, but its something you always need to keep in
mind.
GM-Controlled Game
There have been several computer games where one person takes the
role of the GM (Game Master), making the game more dynamic and
adaptive to the players and their actions. Vampire: The Masquerade
Redemption, published in 2000 by Activision and developed by
Nihilistic Software, featured a multiplayer mode that allowed one person
to become the Storyteller, changing the game as necessary to adapt to the
players and keep it interesting and challenging.
Neverwinter Nights had a dungeon master client feature that gave a
person complete control over the server portion of the game that allowed
people to join a semi-persistent world. A key piece of the DM client was
the ability for the DM to take control of an NPC, helping to make the
game seem more realistic by responding to players as they would expect
them to and engaging them directly in order to further the adventure in
case the players had strayed too far afield.
A GM-controllable game is much more difficult to create and test than a
multiplayer party game. Each piece of functionality that you allow the
GM to have adds another layer of complexity to the game. What if the
GM spawns dozens of high-level monsters? Can your game handle the
This is one area that RPGs typically havent gotten into, but coming up
with a solid game that has a unique feature like this would increase the
chance of your game doing well.
For our purposes well assume that the Live networking is enough and
well discuss various issues related to it. That said, theres a lot to
consider so lets jump right in.
Getting Started
Before any kind of networking can be done, the gamer services system
must be initialized. The gamer services system provides access to the
messages that the Guide raises, such as sign-in status changes and game
invitations. Since a player has to be signed in to play an XNA
Framework game, hell have to have access to the Guide, and in order to
have access to the Guide, the system that provides access to it has to be
active. Fortunately, its extremely simple to get the service up and
running:
Components.Add(new GamerServicesComponent(this));
GamerServicesDispatcher.Initialize(Services);
base.Initialize();
}
Youll then need to call the Update method of the class once per frame:
Listing 12-2
A game can now call any networking API function without an exception
being thrown.
In-Game Chat
Being able to communicate with the other players in a group is important
to ensuring the group survives in places like dungeons or any combat
situation. If the group has no way of communicating and all but one of
the group decides to run from an encounter, the remaining player is
probably going to be pretty upset to find himself facing a horde of
monsters by himself.
Having to type to communicate is one option, but can be problematic in
combat when a player has to concentrate on the enemy hes facing. A
multiplayer game cant be paused like a single-player game to type a
message to the other players. Providing this functionality is fine, but
having another solution would be ideal.
Luckily, voice support in XNA Framework games comes free of charge.
The voice data is automatically transmitted and replayed without having
to write one line of code. There are a couple of pieces of data that have
to be checked, however:
Figure 12-4
Are they currently talking?
If the player is currently talking (NetworkGamer.IsTalking) you could
use something like one of the icons shown above or highlight the player
in the games GUI.
Are they muted?
If you dont let the other players know a person is muted
(NetworkGamer.IsMutedByLocalUser) they could talk to the muted
player and if dont get a reply they may think something is wrong with
the game.
If you want to enable things like team chat or in-game location-based
chat theres the LocalNetworkGamer.EnableSendVoice method.
I sense a presence
So gamers are playing your game and their friends want to know exactly
what theyre doing in the game. If youve ever played on Xbox LIVE,
you know that your Friends List will have text next to a friend like
Playing Rock Band 2 - Band Quickplay - Smile Like You Mean It.
This is called Presence information.
Each game sets and displays pieces of the information during the game
to reflect whats going on. The first piece of information is set
automatically. The second and third pieces are general and specific,
respectively, and are set by the developer. Games created using the XNA
Framework also have the ability to specify information like this, but in a
limited fashion. Because XNA Game Studio games are not certified, any
information thats displayed has to be limited to something that would
fall under what would be a rating of E - Everyone. In order to be certain
that this is the case, Microsoft has provided a list of strings that can be
displayed, some with values that can be provided by the developer.
The Figure 12-5 shows the current list from which the developer can
select.
The first two lines of displayed information will be set to Xbox LIVE
Community Game and the games title, and the item will be displayed
on the third line. The following items can specify a value along with the
text by setting the PresenceValue property of the GamerPresence class:
GamerPresenceMode.Stage
GamerPresenceMode.Level
GamerPresenceMode.Score
GamerPresenceMode.CoopStage
GamerPresenceMode.CoopLevel
GamerPresenceMode.VersusScore
Figure 12-5
To set the gamers presence, you would do something like the following:
Listing 12-3
Session Management
Before any multiplayer game can begin, there has to be a session for the
players to join. As there are several reasons that creating a session could
fail, it would be a good idea to trap for them, as the following code
shows:
Listing 12-4
}
catch (NetworkException ne)
{
System.Console.WriteLine("Failed to create session: "
+
ne.Message);
}
catch (GamerPrivilegeException gpe)
{
System.Console.WriteLine(
"Insufficient privilege to create session: " +
gpe.Message);
}
}
Local
SystemLink
PlayerMatch
Ranked
Note that the Ranked value is only used for commercial games that have
earned Xbox LIVE certification.
If you want to be notified when the session is created, you can pass in a
callback as the fourth argument of the BeginCreate function. The object
returned from the call to BeginCreate can be used to monitor the
progress of the session creation, however.
You can simply check it each call to the Update method to see if its
finished:
if ((asyncResult != null) && asyncResult.IsCompleted)
Once the session is created, youll want to save the session object for
future use and set some properties for it:
Listing 12-5
{
System.Console.WriteLine("Failed to create session: "
+
ne.Message);
}
catch (GamerPrivilegeException gpe)
{
System.Console.WriteLine(
"Insufficient privilege to create session: " +
gpe.Message);
}
if (networkSession != null)
{
networkSession.AllowHostMigration = true;
networkSession.AllowJoinInProgress = false;
}
}
At this point you can display a screen to let the player who has created
the session wait to see if any players join the session. You would also
allow them to indicate that theyre ready for the game to start. See the
following figure for an example of a screen like this from the Net
Rumble Starter Kit:
Listing 12-6
In order for other players to find this session, they have to be given an
interface to see the available sessions. The code to do this looks
suspiciously familiar:
void FindSession(NetworkSessionType sessionType)
{
try
{
IAsyncResult asyncResult =
NetworkSession.BeginFind(sessionType, 1, null,
null, null);
}
catch (NetworkException ne)
{
System.Console.WriteLine("Failed to search for
session: " +
ne.Message);
}
catch (GamerPrivilegeException gpe)
{
System.Console.WriteLine(
"Insufficient privilege to search for session: " +
gpe.Message);
}
}
When the available sessions are found, you would display them to the
player in a GUI of some type:
Listing 12-7
try
{
availableSessions =
NetworkSession.EndFind(e.AsyncResult);
}
catch (NetworkException ne)
{
System.Console.WriteLine("Failed to search for session: "
+
ne.Message);
}
catch (GamerPrivilegeException gpe)
{
System.Console.WriteLine(
"Insufficient privilege to search for session: " +
gpe.Message);
}
if (availableSessions != null)
{
foreach (AvailableNetworkSession availableSession in
availableSessions)
{
if (availableSession.CurrentGamerCount <
World.MaximumPlayers)
{
Console.WriteLine(availableSession.HostGamertag +
" (" +
availableSession.CurrentGamerCount.ToString()
+ "/" +
World.MaximumPlayers.ToString() + ")"));
}
}
}
try
{
IAsyncResult asyncResult = NetworkSession.BeginJoin(
availableSessions[entryIndex], null, null);
}
catch (NetworkException ne)
{
System.Console.WriteLine("Failed to join session: " +
ne.Message);
}
catch (GamerPrivilegeException gpe)
{
System.Console.WriteLine(
"Insufficient privilege to join session: " +
gpe.Message);
}
If joining the session succeeds, youll get the network session that the
player is joined to:
Listing 12-9
The last couple of lines of code come from the Net Rumble Starter Kit,
which uses classes to represent the various screens needed to display the
network data to the player to allow him to get to this point. The
LobbyScreen class is what you saw back in Figure 12-6.
From here, you have a couple of events that need to be handled and the
information for those events displayed to the player:
GamerJoined You need to update the screen to display the data for
the player that has joined.
GamerLeft You need to remove the player from the list of players
that are in the session so his or her data is no longer displayed.
GameStarted Whatever needs to be done to start the gameplay
would happen. This code should only happen on the host and then be
transmitted to the other players.
SessionEnded You would need to let the player know the session is
ended and then take the player back to the appropriate screen.
Once the game has started, there are two class youll be constantly using
the PacketWriter and PacketReader. You would use the PacketWriter
class anytime something happens in the world that the other players need
to know about. This includes player movement or actions and events that
are caused by NPCs or the world itself. In order to transmit this data to
players, you first need to figure out what type of information it is that
you need to send. A common way to do this is to create an enum that
contains a value for every type of packet that you need to send. Heres a
sample enum:
Listing 12-10
You could get a lot more, depending on how much depth you want, but
making the values somewhat generic gives a little more flexibility. The
combination of GamestateChange and PlayerInput could be used for
anything from combat to conversation handling, for example. If you had
a game state of InitiateConversation, you could send this to display
the conversation dialog. The PlayerInput packets would then be used to
show the other players the selections made as the conversation goes on.
To send data, the easiest thing to do is to serialize a class that holds the
data for that packet type. For example, to send a change to an entitys
stat, you would do something like:
Listing 12-11
packetWriter.Write(_entities[0].Name);
_entities[0].Stats["Strength"].Serialize(packetWriter);
localGamer.SendData(packetWriter,
SendDataOptions.ReliableInOrder);
packetWriter.Write(_stat);
packetWriter.Write(_value);
}
The other players would get this data as part of the games update:
Listing 12-12
NetworkGamer sender;
networkSession.LocalGamers[0].ReceiveData(packetReader,
out sender);
// read the type of packet...
PacketTypes packetType =
(PacketTypes)packetReader.ReadInt32();
// ... and dispatch appropriately
switch (packetType)
{
case PacketTypes.PlayerData:
UpdatePlayerData(sender);
break;
case PacketTypes.WorldSetup:
// apply the world setup data, but only once
if (!Initialized)
{
Initialize();
}
break;
case PacketTypes.EntityData:
if ((sender != null) && !sender.IsLocal)
{
UpdateEntityData(sender);
}
break;
case PacketTypes.WorldData:
if (!networkSession.IsHost && Initialized)
{
UpdateWorldData();
}
break;
case PacketTypes.PlayerInput:
if (sender != null)
{
PlayerData playerData = sender.Tag as
PlayerData;
if ((playerData != null)
{
playerData.ProcessInputPacket(packetReader);
}
}
break;
case PacketTypes.GameWon:
gameWon = true;
if (networkSession.IsHost &&
(networkSession.SessionState ==
NetworkSessionState.Playing))
{
networkSession.EndGame();
}
break;
}
}
}
}
Summary
What weve gone over in this chapter is a far from exhaustive look at the
networking functionality thats offered by the XNA Framework. A
complete discussion of the subject would likely take up several chapters.
Whats been covered here should be enough, however, to give you some
information that you can use to start planning for offering multiplayer
functionality in your game. Offering multiplayer gameplay is a great
way to increase both your potential audience and the longevity of your
game. As with most games that offer a multiplayer component, once the
player completes the single-player portion of your game, he can then get
a potentially completely different experience by jumping into the
multiplayer portion of the game. Some games live on for years after their
release, based solely on the strength of the multiplayer experience. The
longer you can keep your game in the awareness of gamers, the more
your name will be known, and the easier it will be for you to get gamers
to pick up games you release from then on.
Chapter 13
Putting It All Together
At last were finally going to bring all the pieces that weve been talking
about together into a sample game. Because of all the work that weve
done in creating the pieces, its fairly simple to create a basic game.
After doing so, well discuss some areas where you would probably
want to flesh things out a bit to make it a game that you would be proud
to call your own.
Youll want to make sure you have the code for this chapter and youll
want to look through it for a while to make sure you understand how it
all works together and how you can use whats there to create your own
game. I purposely made it so that you couldnt just compile it, slap your
name on the front, and call it yours (well, you probably could, but it
wouldnt be something to be proud of).
If you compile and run the project, youll find that it starts up with a
character already created and standing in a small town. It shouldnt be
too hard to figure out what to do as theres only one other character in
the town. When you interact with him, youll start up the conversation
that leads to the main quest for the demo the NPC was attacked on his
way from another town to his home in this town by some creatures and,
although he managed to get away without being killed, he lost a family
heirloom, an ordinary ring. Its not worth anything except for the
sentimental value and hes willing to pay you to find it and return it to
him. He believes the creatures that attacked him picked it up and their
leader has it.
Hes found out that the creatures live in a cave not far from the path that
leads out of the town. Youll have to travel through a bit of wilderness to
get there, but you cant miss it. Lucky for you, since you dont have any
skill at tracking or any other skill except swinging the sword thats at
your waist. Thats to be expected, since you just decided to take up
adventuring and this sounds like a perfect way to start your career as a
famous adventurer. Once you accept the quest, youll head out of town,
find the cave (maybe fighting some creatures along the way), find and
kill the leader of the small clan of monsters, get the ring, and return it to
its owner. No problem!
Go ahead and play through the game. It shouldnt take more than a
couple of minutes. Ill wait here.
Okay, so it wasnt The Lord of the Rings. I never claimed it would be,
but you could turn it into something almost as exciting with a little work.
Lets talk about some of the code youll need to modify to get there.
Everything in the game is driven from the Game class. This isnt how
youll want to leave it, but it works. Ideally, you would have a menu
system to allow the player to do all the things you normally expect to be
able to do start a new game, load a previous game, save the current
game, adjust options such as resolution, sound, and controls, etc. Theres
not much going on in the class. Youve got an instance of the Level class
and an instance of the Entity class that represents the character. The
Game object creates and initializes these objects in the LoadContent
function:
Listing 13-1
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
Entity character;
Level level;
level = Content.Load<Level>("town");
character = Content.Load<Entity>("player");
character.Location = level.PlayerStartLocation;
level.Entities.Add(character);
this.Components.Add(level);
}
}
Making It Better
Obviously we cant create something during the course of this book that
most people expect of an RPG. Now that youve got the concepts down,
though, it should be a lot easier to create a full-fledged RPG that you
could submit to Xbox LIVE Community Games and actually expect
people to pay for. Notice that I said easier, not easy. Game development
isnt easy, and RPGs in particular are one of the more difficult genres to
work in. It is fun, however, at least in my opinion, and I hope to see a lot
of RPGs submitted to Xbox LIVE Community Games over the next year
or so.
Heres a list of things to consider when expanding what weve done here
into a full game:
Story While not strictly necessary, most RPGs have at least a basic
story behind the gameplay. MMORPGs are usually the type of games
that dump you into a world without any main guiding force, but you
could do the same thing in a regular RPG (although I wouldnt
recommend it). My personal feeling is that a good story is more
important than flashy graphics in an RPG. The story shouldnt interfere
with the gameplay, however. Try to use conversations and interactions
with NPCs to partially drive the storyline along if possible, without
having the NPCs seem like theyre reading the story from a book.
Speaking of books, they and other objects in the world can also be used
to provide background and other information that fleshes out the game
world and gives the players some idea of what theyre doing and why. If
the player is given a quest to help defeat some evil king, he may wonder
why he should bother. If he picks up a book and it turns out to be a
history of that king that describes how the king killed all but a few of the
characters race, he now has some incentive to see the king taken down.
Good storytelling is an art and even more so in games where you have to
do it in a medium different from those most people are used to. Dont
expect to win awards for the story in your game the first, second, or even
tenth time. Even award-winning authors have penned stories and scripts
for games that havent gotten great reviews. Do the best job you can
though, and players should notice the effort.
Levels Ideally you would have all your levels laid out before you
begin designing them. This could be done by designing your world first,
then creating levels that you can drop into areas of the world. Thats
assuming, of course, that you dont just make your world one big level
and dynamically load portions of it as the user explores it. That would be
an ideal situation and it can be done. The Dungeon Siege games were a
great example of doing this, but of course those had a huge team of
professionals. Most indie developers dont have those kinds of resources,
but theyre also not usually developing games on the level of a Dungeon
Siege.
Dont just create empty levels merely so you can say your game has 100
levels. If only a dozen of them are populated or interesting in some way,
you can expect the community feedback to be appropriately negative.
Compress the wilderness type levels to break things up a bit and give the
players a decent chance at nice random encounters. Make sure the
encounters are appropriate for the area as well. Players shouldnt be
fighting giant rats in the middle of a forest or bats in a desert.
As a player I could spend hours wandering around a town level as long
as there are interesting things to do there, especially if Im playing a thief
type character and thief skills are actually usable. If theres a thief guild
in the town, even better. You could have quests just based around it
pickpocket x amount of money, put a shop out of business by stealing
inventory because the owner wont pay for protection, etc. The
possibilities are almost endless. As long as theres enough content to
keep things fresh and interesting, one town can go a long way. This
could naturally lead to
More quests This goes with the story item but there are also sidequests to consider. The main story itself can have anywhere from one to
dozens of quests that have to be completed, but to keep the player on his
toes, you should have side-quests pop up from time to time. If the sidequests cause the player to think about accepting them and possibly
disrupting the main quest, making it more difficult to complete, or not
accepting them and losing out on whatever rewards can be obtained from
completing them well, thats part of what makes RPGs so interesting
and different from other genres.
If your game has dozens or even hundreds of quests, youre going to
have to ensure that the player has some way of managing them all. This
means not only a way to see all the quests hes currently working on but
also those hes completed, as well as possibly a way to reorganize them
in the GUI you use to display them. By default youll probably be
displaying them in reverse order of when they were accepted, moving
those hes completed to the bottom. If quests are tied together, youll
probably also want to give the player a way of easily seeing the thread
that leads from one to another.
More loot This goes without saying I think. Every RPG has tons of
items for the player to find, steal, whatever. Making the character more
powerful by getting new gear is part of the fun. Dont just throw loot at
the player for the sake of having loot in the game, though. There needs
to be a balance to when and where the player gets it and how much of it
they get. Avoid the Monty Haul campaign at all costs
(seehttp://tvtropes.org/pmwiki/pmwiki.php/Main/MontyHaul for an
explanation of this term if you havent heard it).
New classes, races, skills, spells, etc. One new skill that can
provide hours of gameplay is crafting. By allowing the player to create
his own items, you give him the ability to play the game he wants to
play, assuming that your implementation of crafting is fun, interesting,
and useful. Forcing the player to make his own arrows and having that
process be dull, boring, and take hours is not the kind of memorable
experience you want your game to be known for. You can look at most
MMORPGs for a way to not do crafting in my opinion. Ive yet to play
an MMORPG where crafting was interesting, fun, or immediately
useful. This is probably due to the way MMORPGs work. Developers
have to keep you busy doing something so you continue paying the
monthly fee. Making crafting easy would be counterproductive. Im sure
theres a way to make it fun, but I havent seen one yet. Fable 2s
smithing mini-game is about the closest to what I think crafting should
be.
Ive said it many times so far, but its so important Ill say it again.
Youll need to be careful with adding things like races, classes, or skills
to the game. Its very easy to create an unbalanced, and therefore unfun
(in my opinion anyway) game. If one race or class has superior power,
making it easier to complete the game when playing as that race or class
than as another race or class, most players probably will ignore the other
races or classes and possibly miss out on content youve spent time
creating for them, which means youve wasted time and possibly money
on all that content.
Try to plan all your content before you even begin developing,
prototyping on paper if possible. Create characters of all the race/class
combinations and playtest combat with some of the creatures you plan
on having in the game. If its obvious that one combination is better than
another, tweak values for stats, skills, or whatever until it takes about the
same effort with every combination. Yes, its going to take a lot of time,
but its better to do this on paper, where its easy to change things, than
in the game where you have to worry about breaking the game.
Saving and loading progress It wouldnt be very fun it you had to
start from the beginning every time you loaded the game, would it? I
havent bothered going over loading and saving an entire game because
its not much different than loading and saving a class from a code
perspective. You do have to think a little about what exactly needs to be
saved, however. Rather than saving the entire level, you really only need
to save any changes to the level. This includes things that have been
removed from the level (dead entities, loot thats been picked up, etc.).
In allowing the player to save his progress, youll have to answer the
question How many saves should the player have? Unfortunately,
theres no one right answer to this except It depends. You can give the
player the ability to save as many times as he wants, but if he can save
every time the character takes a step, theres no sense of danger in doing
anything. Fear of having the character die and losing something (be it
experience, items, temporary reduction of stats, etc.) is a good thing in
my opinion and something that should exist in the game experience.
Theres the checkpoint method of saving, but this usually turns out to be
more negative than positive from what Ive seen. Picking the right place
to save and ensuring that the state of the game at that point is always
such that the player can complete the game can be problematic. More
than a couple of games that have used the checkpoint system have been
patched to allow the player to save at any point. It takes a little more
work to allow the save anywhere feature, but its usually better than
the alternative. That being the case, restricting the number of saves while
allowing the player to save anywhere is usually a good compromise.
Theres still the possibility of the reduction of the danger of the character
dying, but theres not much that can be done about that from what Ive
seen. Keeping the player on his toes by not making encounters glaringly
obvious helps to heighten that sense of danger.
Options Give the players the option to change things that can be
changed. Some of these things include resolution, default controls, and
sound and music volume levels (with the option to turn them off
completely if they want). Make sure that changing or turning things off
doesnt affect the gameplay. (That quest where the character has to listen
outside a door? Yeah, that one. Thats not going to work if the players
turned off the sound!) One thing that Ive not seen much of is allowing
the player to adjust the brightness. Usually this isnt a problem, but in
dungeon-type levels there is the possibility of things being so dark that
on certain monitor and video card combinations the players ability to
make things out isnt as great as it could be. Making sure you test your
game on more than just your development machine can help spot this
problem, especially if you can do it on a low-end machine without
blazing graphical power.
Gamestate The demo doesnt have any real understanding of
gamestate things like pausing the game, pulling up a main menu to
change options or save the game, or even handling ending the game
gracefully. At a minimum, for the last item youd want to prompt the
user to save the game, not just shut down. Simply adding a menu system
means youre probably going to want to add some kind of gamestate
management. Luckily, a sample exists on the Creators Club web site that
Community Sites
Official XNA site http://creators.xna.com
Ziggyware http://www.ziggyware.com
XNA development http://www.xnadevelopment.com
The ZBuffer http://www.thezbuffer.com/
Reimer Grootjans site http://www.riemers.net/index.php
Blogs
Shawn Hargreaves blog
http://blogs.msdn.com/shawnhar/default.aspx
XNA Team blog http://blogs.msdn.com/xna/default.aspx
Michael Kluchers blog http://klucher.com
Virtual Realm http://www.virtualrealm.com.au/blogs/
Do as I say, not as I do http://amapplease.blogspot.com/
Nick Gravelyns blog http://nickgravelyn.com/
Random Chaos http://xna-uk.net/blogs/randomchaos/default.aspx
Catalin Zima http://www.catalinzima.com/
AI Resources
AI forums on GameDev.net
http://www.gamedev.net/community/forums/forum.asp?forum_id=9
Game AI for developers http://aigamedev.com/
AI Middleware Links
http://www.kynogon.com/products/technology/index.html
http://www.trusoft.com/ac_for_games_developers.html
http://www.simbionic.com/indexjs.htm
http://www.simbionic.com/games.htm
Index
A
2D sound, playing, 186-189
B
Baldurs Gate, 13-14
BaseItem class, 134-135
BaseTrapDamage enum, 197
Betrayal at Krondor, 8
BioShock, 19
C
CalledShot attack type, 161-162
camera, 24, 29-30
CaseType enum, 259-260
Cast method, 122-124
CastSpell method, 121-122
casual games, 24
character, 1
designing, 33-34
multi-class, 51-52
character classes, 25
and skills, 94
Charisma stat, 36
chat, in-game, 280-281
CheckForQuestCompletion function, 258
Chest class, 147-151
class alignment combinations, 64
class-based system vs. skill-based system, 25
client-server network, 271-272
combat, 158-161
real-time, 23, 159-160
turn-based, 23, 160-161
combat engine, 165-168
combat simulator, 169-171
combat system, features to add, 171-172
D
damage,
resistance, 59
specifying, 197
types of, 58-59, 144
weakness, 59
Damager structure, 58-59, 143, 197
DamageType enum, 58-59, 144
design document, 22
for sample game, 28-32
Detect Trap skill, 92
Dexterity stat, 35
Diablo, 11
DieType enum, 39
difficulty, specifying, 87
Difficulty enum, 87
E
Elf
race type, 47
stats and skills, 46-47
entities, 33-34
alignment of, 60-61
specifying type of, 54
Entity class, 53-62
EntityAlignment enum, 60
EntityClass class, 50-52
EntityItem class, 152-153
EntitySpell class, 117-119
EntityStat class, 40-42
EntityType enum, 54
F
Fable, 17-18
factions, 237-238
Fallout, 12-13
FedEx quest, 234
FillGroundLayer method, 206
FillSkillObject function, 98-99
FindSession function, 285
First Aid skill, 93
FirstTime function, 256
Freedom Force, 11
FunctionType enum, 255
G
Game class, 295
Game Master, 276
GamerJoined event, 287
GamerLeft event, 288
GamerPresence class, 282
GamerPresenceMode enum, 282
GameStarted event, 288
gamestate, 301-302
GetCost function, 83-84
GetRandomNumber function, 39
GetTotalDefBonus function, 88
graphics, 191-192
graphics engine, 23-24
griefing, 275
GUI, see interface
GUI libraries, 227-228
H
Half-Elf
race type, 48
stats and skills, 48
Halfling
race type, 49
stats and skills, 49
HasQuest function, 256-257
Hellgate: London, 20
Hide skill, 92
hit points, 56-57
calculating, 57
host, 270
hotkeys, using to cast spells, 119
Human race type, 47
I
Icewind Dale, 15
Icewind Dale II, 15
in-game chat, adding, 280-281
Initialize function, 279
Intelligence stat, 36
interactions, 247
checking, 255
specifying type of, 240
InteractionType enum, 240
interface, 211-213
creating inventory window, 217-226
creating toolbar, 214-216
features to add, 228-230
IntermediateSerializer class, 43-45, 99-100, 243
Inventory class, 154-156
inventory items, drawing, 225-226
inventory management, 153-154
inventory window, 217
creating, 217-226
InventoryWindow class, 217-226
Item class, 138-141
items, 133-134
adding, 298-299
distributing, 133
implementing damage to, 140
limiting use of, 139-140
managing, 153-154
specifying types of, 139, 150
ItemType enum, 139, 150
K
Knights of the Old Republic II: The Sith Lords, 17
L
Layer enum, 206
layers, specifying type of, 206
LearnSpell method, 116
Level class, 200-207
level editor, 208
M
magic, 109-110
using, 119-124
mana, 2, 109
Mass Effect, 19-20
MaxWeight function, 64
MediaLibrary class, 178
MediaPlayer class, 178
MediaQueue class, 179
Melee Weapons skill, 90
Message trigger type, 199
metadata, 135
Might and Magic series, 7
Might and Magic VI: The Mandate of Heaven, 10
MMORPG, xv
Money class, 136-138
Morrowind, 9
mouse gestures, using to cast spells, 120
Move Silently skill, 93
MoveSpell function, 128-129
N
NetworkSessionType enum, 283
Neverwinter Nights, 16-17
Neverwinter Nights 2, 17
Normal attack type, 161
Nox, 16
O
objects, specifying type of, 199
ObjectType enum, 199
P
PacketTypes enum, 288
PacketWriter class, 288
Parry attack type, 161
pedit5, 3
peer-to-peer network, 270-271
Perception skill, 93
Pick Lock skill, 93
PickLock function, 151
Planescape: Torment, 14-15
Playlist class, 179
point-based skill system, 78-79
Pool of Radiance, 5
Q
Quest class, 233-240
QuestAssigned function, 257
QuestRewardType enum, 235
quests, 22, 231-233
adding, 298
checking, 256-259
designing, 242-245
specifying type of, 234
QuestStep class, 239
QuestType enum, 234
R
race alignment combinations, 63
race bonuses for skills, 94-95
race types, 47-50
race/class combinations, 63
races, 46
Ranged Weapons skill, 90
ranks, 78
real-time combat, 23, 159-160
RemoveCost function, 83-84
RemoveTile method, 205
resistance to damage, 57-59
ResolveAttack method, 167-168
resources, 303-306
rewards, 140
specifying type of, 235
Riposte attack type, 161
Rogue, 3
RPG, 1
designing, 22-32
history of, 3-20
objects in, 21-22
system, 24-25
RPGGame class, 66
S
Sacred, 11
sample game,
camera, 29-30
characters, 30-31
combat simulator, 169-171
creating entity classes in, 70-73
creating races in, 69
creating skills in, 97
creating stats in, 67-68
design document, 28-32
designing quests, 242-245
features to add, 296-302
game world overview, 28-29
items, 31
learning skills in, 101-103
music, 31
putting it all together, 293-294
single-player, 32
testing conversation, 260-265
user interface, 31
save system, 26
SaveSkills function, 99-100
SaveStats function, 43-44
scripting system, 27-28
scripts, 22
server, 271-272
SessionCreated function, 284
SessionEnded event, 288
sessions,
creating, 283-284
displaying, 286
joining, 286-287
SetTile method, 205
SetTrap trigger type, 199
Shield Use skill, 91
skill-based system vs. class-based system, 25
Skill class, 80-84
skill data, storing, 96-101
skill points, 78-79
calculating, 89-90
skills, 2, 21, 77-80
cost of, 82, 94
learning, 94, 101-104
loading, 100
race bonuses for, 94-95
saving, 99-100
T
target, selecting, 163
The Bards Tale, 4-5
The Witcher, 18-19
tiles, 21, 192
creating, 193-200
removing, 205
setting, 205
tileset, 194
ToolBar class, 214-216
toolbar, creating, 214-216
tools, 27
toolset, see XNA RPG Toolset
track, audio, 181
trap, specifying type of, 150, 196
Trap structure, 196
TrapType enum, 150, 196
Trigger structure, 198
triggers,
specifying, 198
types of, 199
TriggerType enum, 198
turn-based combat, 23, 160-161
U
Ultima 1: The First Age of Darkness, 4
Ultima series, 6-8
Ultima Underworld: The Stygian Abyss, 7
Unarmed Combat skill, 91
Unlimited Adventures, 5
Update method, 187-188, 279
Use function, 84-87
user interface, 31
see also interface
UseSkill method, 104-105
UseUnarmedCombat function, 106-107
Warcraft, 10-11
Wasteland, 5
WaveBank object, 181
Weapon class, 141-144
weapons, specifying types of, 143
WeaponType enum, 143
weather, 29
Wisdom stat, 36
Wizardry series, 7
X
XACT, 176, 180-181
creating sounds with, 182-185
XNA RPG Toolset, 65-75
combat simulator, 169-171
creating skills in, 97
designing quests, 242-245
learning skills in, 101-103
testing conversation, 260-265