Help on how to implement stories and quests on an RPG

Started by
6 comments, last by Eck 10 years, 1 month ago

First of all, I'd like to say that this is my first post here. I'm kind of new to the world of game developing, but I've been coding in C++ for years and am kind of familiar with the basic concepts of how to draw things on screen, how to handle movements, create maps and the likes of it.

I've read quite a few tutorials on how to create simple 2D games, but none of them could help me on my quest to create an RPG.

Basically what I don't know is, how can I implement the story in my game? It's not a matter of what lines of code I should use, it's more like a problem with the basic concepts.

I used to fiddle around a lot with RPG Maker an eternity ago and I'm familiar with the concept of events, but switching to C++ makes it kind of hard for me. I want to be able to put, say, random quests given from NPCs (for example, a carpenter might ask the player to go chop some trees and bring him wood every now and then, or the town guard might ask help for an upcoming monster invasion), give the player something to do other than the main quests. Now, hard-coding every event in the game seems the easy (and lazy) way to do everything, but there're obviously better ways. I'm fairly sure there's a nice and clean solution for this problem, but I just can't find it by myself.

Thanks in advance for any help you can give me!

Advertisement

Excellent first post by the way. :)

General Design Advice

Sometimes it's hard to see what a good abstraction might be for tough problems. When I'm not clear on a good design, I find that it helps me to code the specific solution first. Hardcoding a bunch of hacky code is bad in general, but the effort can be useful to show you what you need to think about and where commonality exists. You may have heard the term "Write one to throw away".

Questing System

You asked about two different (but related) problems. Randomly Generated quests and Story Line quests. Let's talk about randomly generated quests first. Spend a little bit of time thinking of the random quests you might want to create: Kill n enemies, Collect n items, Deliver quest item to NPC, etc. Spend a little bit of time thinking about what they have in common, and what's different. Then pick one type and try to get it working. Let's choose KillEnemies.

Joe the Carpenter: Hey player. I want you to go kill 5 goblins. I'll give you 10 gold and a splintering wood bow.

If we were going to turn this into a form letter, which things might change?

<QuestGiver>: Hey player. I want you to go kill <numberToKill> <enemyToKill>. I'll give you <goldReward> and <itemReward>.

// some pseudo code for our quest data
KillEnemiesQuest
    NPC QuestGiver
    string QuestFormatString // Hey player. I want you to go kill <numberToKill> <enemyToKill. I'll give you <goldReward> and <itemReward>
    int NumberToKill // random number? Maybe a choice of numbers 5, 10, 15, 20
    EnemyType EnemyToKill // random enemy.
    int goldReward // random gold
    GameItem itemReward // random item.

You'll quickly find if you go completely random it probably won't lead to very desirable quests. A level 1 player might pull a Dragon for his first random monster to slay... Or you might randomly generate 1 million gold as a reward for killing 5 rats. So you'll have to make your data a little bit smarter. You'd store a level range tied to your various enemy types and your random quest generator could take the player's level as a parameter. The same could be done for rewards.

Once the quest is generated and accepted by the player it would go into some kind of quest log where you'd want to keep track of his progress. Every time a monster is killed, you could check the player's quests if that gave him credit for anything and increment his enemy kill counter for that quest. Once he's killed his 5 goblins, he can go back to Joe and collect his reward.

After you get kill quests working, switch over to a new quest type like a Collect Item quest. Could be an item dropped from a monster or something that spawns on the ground or even bought from a vendor. Instead of an EnemyType EnemyToKill, you'd have an ItemType ItemToCollect. You might try to increment some counter every time the player picks up the correct item type... but they might be tricky and drop/pickup the same item 10 times. :) You could count the correct items they hold any time their inventory changes, or just calculate it every time they look in their quest log or try to complete it.

After you get a few quest types working you'll see where commonality exists and be able to refactor it into a base class. And your quest generator should be able to kick out a random Quest (instead of specifically a random KillEnemiesQuest). As you get further along you'll realize where you (or I) was wrong. For example in a DeliveryQuest you might want to turn in your quest at the delivery point (instead of the original quest giver). Or you might find that to be useful in other cases as well like to move them on to a different area. Kill these goblins and then report to Captain Ork Slayer. So then you'll add a QuestReceiver member to your Quest class.

And after you used your random quest system for a while you might want a quest where you kill 5 goblins and deliver a note. That's when you realize that the KillEnemiesQuest might be better thought of as a KillEnemiesObjective. So you spend a little more time refactoring. A Quest would hold the data about the quest (QuestGiver, Reward, etc) and have a list of objectives (Kill 5 goblins, Deliver note to Jill).

I'll try to post again about Story quests with detailed events a little later unless someone beats me to it. The short answer is that a lot of times it's done with some kind of scripting language like Lua.

I hope my post helps. It's kind of an overview of how I start out with an idea for a design and refine it over time.

- Eck

EckTech Games - Games and Unity Assets I'm working on
Still Flying - My GameDev journal
The Shilwulf Dynasty - Campaign notes for my Rogue Trader RPG

Indeed your reply was really useful, thank you!

So, story line quests/triggered events...

These usually have a lot more detail than randomly generated quests. You might have several independent quest chains scattered about in various towns, and a main story line quest with several acts. It would probably do you some good to take a look at some big existing games like Skyrim or Fallout: New Vegas. Looking at the hint books can help you see how they decided to break down the data. If you don't have a hint book for one of these games, I'm sure the wiki's might help as well. You might also take a look at their construction sets, especially the quest editors. This could be helpful, but it may be overwhelming too. :) I don't mean that you should try to build something as complex as these multi-million dollar games, but just seeing how they lay out a quest in a hint book can really help you visualize what's going on under the hood.

Anyway, these hand written quests will be very similar to the randomly generated quests. They'll have quest givers, names, objectives, quest text etc. But they'll also have state tracking, and possibly triggered events. You can hardcode everything like you said. You called it lazy, but I call it research! :) It's a lot easier to solve a concrete problem (specifically what quests/events are you supporting), than the nebulous abstract problem (an all inclusive, robust, quest system that can handle whatever we can think of). After you see a few hardcoded quests in action spend a little time to refactor it.

I think you're most interested in the triggered events so I'll gloss over the details of a Quest Chain and state management. You'll need a way of keeping track of what a player decided. It could be as simple as an integer like Fallout's "Quest Stage" that gets set based on dialog options.

Anyway, back to triggered events. There are lots of different "whens": Player moves into a certain area, player loots an item, player chooses dialogue option b, etc. And lots of different "whats": spawn monsters, npc says something, play sound effect, shake screen, etc. So let's make up a little triggered event. When the user walks into the room, play an explosion sound, shake the screen, and then spawn 2 zombies.

To do that in code, well... you write the code... But I think you want to do this through something like data files. For this trigger, I'd call this trigger type Location and for Location triggers, we would specify a zone(level/area), an x,y coordinate and a radius. This data could be part of some quest data file, or part of a level data file. It could be stored in some personal format you come up with. Or it could be stored in XML with a Trigger element and several Event child elements. Or a lot of people use a scripting language like Lua which can call functions directly that you expose to it. You could create a trigger and attach a Lua function (also defined in your script) to be called when the trigger takes place. On a given level, loop through the active Location triggers any time the player moves, and then fire off their events if the condition is met.

For the events, again, it's all about the data and then mapping the data to actual calls into code. You basically have to come up with an API into your game. Specify a data format for quests, dialogues, and events. Expose functionality that you want to be able to be used by the outside world. And make your game aware of these things.

Sometimes it feels like making data-driven applications is harder work, but other times it doesn't. When it's done properly, it's VERY cool. When everything is hardcoded, you have to make code changes, recompile, run the application, and come up with a way to get back into that same state to retest it. When it's data driven, you can make changes to the triggered event data file while the game is still running and try again. Just put in a feature that reloads the file.

Couple of Lua posts:

http://www.gamedev.net/topic/650341-should-i-use-a-lua-wrapper-class-in-my-engine/

http://www.gamedev.net/page/resources/_/technical/game-programming/using-lua-with-c-r2275

This is a very complex problem to solve with tons of decisions to make along the way. Start small and just keep building on that. And every once in a while take a step back and tweak the design.

If you have more specific questions, I can take another stab at it. :)

- Eck

EckTech Games - Games and Unity Assets I'm working on
Still Flying - My GameDev journal
The Shilwulf Dynasty - Campaign notes for my Rogue Trader RPG

Wow. I must say, this thread alone makes me sad that I hadn't visited these forums long ago. Thank you for such helpful and concise posts Eck!

I too have to thank you again, Eck! I'm going to study Morrowind's Construction Set to see how they handle quests.

Once the quest is generated and accepted by the player it would go into some kind of quest log where you'd want to keep track of his progress. Every time a monster is killed, you could check the player's quests if that gave him credit for anything and increment his enemy kill counter for that quest. Once he's killed his 5 goblins, he can go back to Joe and collect his reward.

I'm not sure if this is what you meant, but a comfortable way of checking for quest progress is making the quest hook to (or "listen for") game events itself. You register its event hook\listener at the moment of its creation.

This way you separate the player logic from any quest logic.

In the quest logic, you have an "onGameEvent" method that is the actual hook. Each quest will have its own hook logic, its own way of reacting to game events.

// Event listener method for the "KillEnemiesQuest"

Method onGameEvent( event:TEvent )

	Select event.id

		Case EVENT_KILLNPC // Something was killed.

			// If the killed NPC is an enemy.

			If TEnemy( event.data ) Then 

				/* Register a kill, check for quest completion,
				   verify which zone the enemy was killed in etc. */

			End If

		Case EVENT_CANCELQUEST	

			// If the quest that was cancelled is this one, remove this quest etc.			

	End Select

End Method
Any NPC, by making use of the dialogue system, should be able to query the state of a certain quest by using a global identifier for the quest. The dialogue system then uses the "getter" method of the quest object to query its state (inactive, incomplete, completed, cancelled etc.).
In RPGMaker, this is considered a "Switch" if I'm not mistaken - a branching or alteration of the course of a dialogue based on the state of a flag variable.

Wow. I must say, this thread alone makes me sad that I hadn't visited these forums long ago. Thank you for such helpful and concise posts Eck!

What an awesome compliment. Thanks. biggrin.png I'm happy the posts turned out so well.


I too have to thank you again, Eck! I'm going to study Morrowind's Construction Set to see how they handle quests.
My pleasure. smile.png I may take the time to turn this into an article once things settle down for me. How is your quest system coming along?

I'm not sure if this is what you meant, but a comfortable way of checking for quest progress is making the quest hook to (or "listen for") game events itself. You register its event hook\listener at the moment of its creation.

Nope. I intentionally didn't mention an event system yet. Even though it's probably the right way of doing things, I felt it added another level of complexity to get right. Jacho Mendt was having trouble with the core design, and I didn't want to confuse him with the more advanced concept of an event system. New programmers often times shutdown when they hear the term "function pointers" screaming INSANITY! MADNESS! At it's core, a function pointer is just a variable that holds a function, but the concept is very strange to most.

That being said, Jacho Mendt has had a month and a half to mess around with his quest system. If you haven't done so already, wiring it up via events like Kryzon suggested would be a good next step in making your quest system more robust. I saw a post today with a few related links that might help you along if you're interested.

http://www.gamedev.net/topic/654147-game-event-system/

I'm curious how things are shaping up Jacho Mendt. How are things going?

- Eck

EckTech Games - Games and Unity Assets I'm working on
Still Flying - My GameDev journal
The Shilwulf Dynasty - Campaign notes for my Rogue Trader RPG

This topic is closed to new replies.

Advertisement