by date
A Questing we will go! (big images)
A Questing we will go! (big images)
| Name: | Gareth Fouche | |
|---|---|---|
| Date Posted: | Oct 16, 2006 | |
| Rating: | 4.0 out of 5 | |
| Public: | YES | |
| Comments: | YES | |
| RSS Feed: | or Subscribe with . | |
| Profile Page: | View profile page for Gareth Fouche |
Blog post
If the title didn't give it away, I have recently been working on my quest system. And its coming along rather nicely. :)
Questing is a major part of RPGs, the bread and butter which drives the gameplay. But what are quests, from a design/code point of view?
Lets take an example. You talk to some guy, and if you haven't done the quest before he asks you to go kill some monster, lets say an Orc ;) . Bog standard stuff.
Now how would this work in engine? Well, first you need some persistant variables. Say to keep track of whether you've done the quest, if the orcs dead, etc. These variables have to persist between sessions, so your game state is the same when you load up again.
Anyone who has been paying attention to these blogs will know my answer to that. A database table! :D Simple. Since my method of saving involves copying the db to the saves folder, if it has a table ScriptVariables containing variable names and values, those variables will be saved with it. Some helper functions to access them, and we're sorted.
Now we come to events. Events are...well, things that happen. Specifically, things that happen to the objects in your game. What events you set up are based on your game.
Take for instance a creature. When it is damaged, this fires an OnDamage event. Anyone who has done any programming in a graphics IDE will be familiar with this kind of concept. Or used the NWN editor ;)
So, in my table storing creature information, I have entries for OnDamage, OnDeath, and OnInteract (only those 3 at the moment). In these columns, I put some script. When these actions happen to the creature, it looks up the appropriate event script, then simply evals it. So now we can perform actions in response to game events, and its easy to add different responses for different creatures in the db. :)
One thing I should mention, which ties in with the event concept, is the idea of game scripts. Game Scripts differ from normal scripts in that they are scripts stored in the DB. The idea being to make the game as easy to mod as possible, and thus make it as easy for me to add functionality as possible. (A major design philosphy for me has been getting as much of the core of the game into the db as possible. Heavily inspired by the awesome morrowind editor.)
So for instance I have coded a DefaultDeath Game Script. All it does is display a message on the screen when a character dies. Likewise, I have a DefaultDamage script. In the appropriate event scripts for my creatures, I simply run those Game Scripts.

The potential for modding with this is awesome, I believe. I can put alot of the game behavior and functionality in the DB for modders to play with, while shielding them from the more complicated, core code I don't want touched. It also makes 'housekeeping' easier, since you can just sort through your db, no hunting through text files to find functions. I just need to add in a gui to edit these Game Scripts, and we're good to go.
Another example of a game script is the ExplodeOnDeath script (it just calls radiusDamage()). Set one of the creature types to call this one on death (note, you can call as many scripts as you like in the event handler), then, when he pegs it, BOOOOOM, eveyone goes flying.
An amusing thing happened when I was testing this. See, I have 2 groups of creatures in my test stage, one group of the "Good" faction, one of the "Evil". My simple faction system means if they come within range of each other, they fight.
Now, I have 2 exploding guys in my good group. I kill the one on the perimeter, and everyone goes flying to the right a ways. But one of the good guys lands in range of the evil group. They all charge him, but now they are range of the rest of Team Goody-Twoshoes, so they charge into the fray too. I sit on the hill, grinning, and count down...5...4...3...2...1...sure enough, after a few seconds, someone kills the 2nd exploder guy, and the mass of them go flying apart. Ahahaha, good times.

Alright, coming back to quests. Now, you could pop all the script necessary for running quests in those events I talked about. But...think about it for a second. Things can quickly become messy. The initiator guy needs to check variables to see if you've killed the orc. If you finish the quest, you need to set another variable, and keep track of it, so you don't give it to him again. What happens if the quest is more complicated, say killing 7 Orcs, 3 Orc chiefs, and then their little dog Rex, in that order? What happens if you come back a month later, having decided to change things around a bit, and you forget some little script hiding somewhere? What happens when you kill an Orc but aren't on the quest? Not fun, and not good object orientated design.
So how do we solve this? Well, quests are, if you think about it, state machines. A quest is in one state, an event happens, and it evaluates whether it should move to another state or stay in the same one. Instead of putting script in the creature event handlers, we just inform the quest state machine that something has happened (I call them QuestEvents), and let it decide if we move to the next phase.
This cleaner design not only keeps your code in one place, so you don't lose track of it, it is now easier to conceptialize the quest.
In terms of the DB, I just have a Quests table and a QuestState table, which stores script to run when the quest recieves an event while its in a particular state. When you want to communicate with the quest, you just call SendQuestEvent(%Quest, %Event). The state decides when to move on, when the quest is complete, etc. You can also query the current quest state.
Coming back to our earier simple quest example, now all that happens is when you talk to the quest Initiator, he gives you the quest so long as the db indicates it hasn't been started yet (GetQuestStatus()). :
[image]http://img215.imageshack.us/img215/5784/screenshot01900001jo0.jpg[/[image]]
All quests start in the StartState state. Then, when you kill the orc, it just fires a KillOrc event to the quest manager, not worrying about anything else. The state machine decides that it should now move to the next state (GetReward). Note, this state is the only quest state which responds to KillOrc events. Later, if you kill another orc, it will send the event again, but be ignored, because the quest has moved on to the GetReward state.

Now, when you talk to the guy, if the quest is active (ie in any state, but not complete), he asks you if you've killed the orc. He queries the quests state, and only gives you the option to answer yes if the quest state is GetReward.


Now the quest is done. If you ask him for it again, he checks, sees its complete, and tells you to bugger off.


And if, later, you come back and want to change the quest so you have to kill 7 orcs, all you need to do is keep track of and increment a counter in the StartState state. No modifications to anything else :D.
Woohoo! A real live quest! I'm that much closer to RPG-dom. Now all I need is to build a gui for easy editing of the quests and their states, like I did for dialogue, and to add in a journal. Its all good and well if they want you to rescue timmy who has fallen down the well, but what if you hit the bar for a while and can't remember where exactly they said the well is? You gotta have a journal. ;)
Questing is a major part of RPGs, the bread and butter which drives the gameplay. But what are quests, from a design/code point of view?
Lets take an example. You talk to some guy, and if you haven't done the quest before he asks you to go kill some monster, lets say an Orc ;) . Bog standard stuff.
Now how would this work in engine? Well, first you need some persistant variables. Say to keep track of whether you've done the quest, if the orcs dead, etc. These variables have to persist between sessions, so your game state is the same when you load up again.
Anyone who has been paying attention to these blogs will know my answer to that. A database table! :D Simple. Since my method of saving involves copying the db to the saves folder, if it has a table ScriptVariables containing variable names and values, those variables will be saved with it. Some helper functions to access them, and we're sorted.
Now we come to events. Events are...well, things that happen. Specifically, things that happen to the objects in your game. What events you set up are based on your game.
Take for instance a creature. When it is damaged, this fires an OnDamage event. Anyone who has done any programming in a graphics IDE will be familiar with this kind of concept. Or used the NWN editor ;)
So, in my table storing creature information, I have entries for OnDamage, OnDeath, and OnInteract (only those 3 at the moment). In these columns, I put some script. When these actions happen to the creature, it looks up the appropriate event script, then simply evals it. So now we can perform actions in response to game events, and its easy to add different responses for different creatures in the db. :)
One thing I should mention, which ties in with the event concept, is the idea of game scripts. Game Scripts differ from normal scripts in that they are scripts stored in the DB. The idea being to make the game as easy to mod as possible, and thus make it as easy for me to add functionality as possible. (A major design philosphy for me has been getting as much of the core of the game into the db as possible. Heavily inspired by the awesome morrowind editor.)
So for instance I have coded a DefaultDeath Game Script. All it does is display a message on the screen when a character dies. Likewise, I have a DefaultDamage script. In the appropriate event scripts for my creatures, I simply run those Game Scripts.

The potential for modding with this is awesome, I believe. I can put alot of the game behavior and functionality in the DB for modders to play with, while shielding them from the more complicated, core code I don't want touched. It also makes 'housekeeping' easier, since you can just sort through your db, no hunting through text files to find functions. I just need to add in a gui to edit these Game Scripts, and we're good to go.
Another example of a game script is the ExplodeOnDeath script (it just calls radiusDamage()). Set one of the creature types to call this one on death (note, you can call as many scripts as you like in the event handler), then, when he pegs it, BOOOOOM, eveyone goes flying.
An amusing thing happened when I was testing this. See, I have 2 groups of creatures in my test stage, one group of the "Good" faction, one of the "Evil". My simple faction system means if they come within range of each other, they fight.
Now, I have 2 exploding guys in my good group. I kill the one on the perimeter, and everyone goes flying to the right a ways. But one of the good guys lands in range of the evil group. They all charge him, but now they are range of the rest of Team Goody-Twoshoes, so they charge into the fray too. I sit on the hill, grinning, and count down...5...4...3...2...1...sure enough, after a few seconds, someone kills the 2nd exploder guy, and the mass of them go flying apart. Ahahaha, good times.

Alright, coming back to quests. Now, you could pop all the script necessary for running quests in those events I talked about. But...think about it for a second. Things can quickly become messy. The initiator guy needs to check variables to see if you've killed the orc. If you finish the quest, you need to set another variable, and keep track of it, so you don't give it to him again. What happens if the quest is more complicated, say killing 7 Orcs, 3 Orc chiefs, and then their little dog Rex, in that order? What happens if you come back a month later, having decided to change things around a bit, and you forget some little script hiding somewhere? What happens when you kill an Orc but aren't on the quest? Not fun, and not good object orientated design.
So how do we solve this? Well, quests are, if you think about it, state machines. A quest is in one state, an event happens, and it evaluates whether it should move to another state or stay in the same one. Instead of putting script in the creature event handlers, we just inform the quest state machine that something has happened (I call them QuestEvents), and let it decide if we move to the next phase.
This cleaner design not only keeps your code in one place, so you don't lose track of it, it is now easier to conceptialize the quest.
In terms of the DB, I just have a Quests table and a QuestState table, which stores script to run when the quest recieves an event while its in a particular state. When you want to communicate with the quest, you just call SendQuestEvent(%Quest, %Event). The state decides when to move on, when the quest is complete, etc. You can also query the current quest state.
Coming back to our earier simple quest example, now all that happens is when you talk to the quest Initiator, he gives you the quest so long as the db indicates it hasn't been started yet (GetQuestStatus()). :
[image]http://img215.imageshack.us/img215/5784/screenshot01900001jo0.jpg[/[image]]
All quests start in the StartState state. Then, when you kill the orc, it just fires a KillOrc event to the quest manager, not worrying about anything else. The state machine decides that it should now move to the next state (GetReward). Note, this state is the only quest state which responds to KillOrc events. Later, if you kill another orc, it will send the event again, but be ignored, because the quest has moved on to the GetReward state.

Now, when you talk to the guy, if the quest is active (ie in any state, but not complete), he asks you if you've killed the orc. He queries the quests state, and only gives you the option to answer yes if the quest state is GetReward.


Now the quest is done. If you ask him for it again, he checks, sees its complete, and tells you to bugger off.


And if, later, you come back and want to change the quest so you have to kill 7 orcs, all you need to do is keep track of and increment a counter in the StartState state. No modifications to anything else :D.
Woohoo! A real live quest! I'm that much closer to RPG-dom. Now all I need is to build a gui for easy editing of the quests and their states, like I did for dialogue, and to add in a journal. Its all good and well if they want you to rescue timmy who has fallen down the well, but what if you hit the bar for a while and can't remember where exactly they said the well is? You gotta have a journal. ;)
Recent Blog Posts
| List: | 08/19/08 - Korrinport Reloaded 07/18/08 - SoW - More concept art 07/09/08 - It may not be Oblivion... 06/24/08 - Celebratory 05/13/08 - The Gingerbread Villa 04/14/08 - SoW - Some concept art 04/02/08 - Blog of War 02/28/08 - Scars of Bloom |
|---|
Submit your own resources!| James Urquhart (Oct 16, 2006 at 15:27 GMT) Resource Rating: 5 |
It would be interesting if you could randomly generate quests on the fly - rpg's such as oblivion can get a bit boring when you've finished most of the quests. Granted, you could just make one up in your head, but that's not quite the same :)
| Sean H. (Oct 16, 2006 at 16:01 GMT) |
IMO event management and processing is the meat of game programming. coming up with a system which is flexible, catches all possible gameplay scenarios, and works exactly as you planned is a huge step in development and a great accomplishment.
sounds like youre having fun too which is also pretty important.
| Dave Young (Oct 17, 2006 at 01:50 GMT) |
| Gareth Fouche (Oct 17, 2006 at 07:00 GMT) |
@Dave : Yeah, I have myself a MMORPG kit licence, and took a look at how you did it in RC 1.5. (Good work btw, you guys are making a lot of progress, and your dedication is inspiring.)
I like to look at other peoples ideas for inspiration when designing my system, even if I don't use the exact code, the insight is invaluable. So I also had a look at the NWN editor, Morrowind, etc. I find the MMORPG kit a good source of ideas and code, and would recommend it to people trying to make RPGs. ;)
@James : I actually am toying with the idea of random generated tasks/side quests, but not so much the core quests. Random quests tend to be less engrossing story-wise and cookie-cutter.
For instance, I am thinking of having a monthly bounty hunt. Might & Magic 6 had this, every month you could enquire in town as to what creature there was a bounty on. If you killed it and returned to them before month end, you got a reward. Things like that I want to generate from a random table, but not main story quests.
I found Oblivions quests pretty boring overall. They lacked much in the way of *real* choice. One of the other things quest state machines will help me manage is good branching plotlines. They'd be a nightmare to keep track of otherwise.
| BrokeAss Games (Oct 18, 2006 at 08:34 GMT) |
Fun stuff!
Ari
You must be a member and be logged in to either append comments or rate this resource.


4.0 out of 5


