Persistent Quest System

Ideas and suggestions for game mechanics and rules.
User avatar
indio
Ancient Red Dragon
Posts: 2810
Joined: Sat Jan 03, 2004 10:40 am

Persistent Quest System

Post by indio »

AL has found some holes in my quest system that I'd like help ironing out with the goal of releasing it (or another version someone else has got) for use by all builders.

I'll put the version for a simple bounty quest as an example.

NPC: Kill Jubilex and I'll give you a reward.
Text Appears When: Quest_Progress (the NPC checks all party members and only offers the quest if)
Variables:
string sQuest = Kill_Jubilex
NOT int nState = 3
#include "qst__pqj_nwnx_inc"

int StartingConditional(string sQuest, int nState)
{
object oCreature = GetPCSpeaker();
if(RetrieveQuestState(sQuest, GetPCSpeaker()) == nState)
return TRUE;
else
return FALSE;
}
PC: Accept
Actions Taken: Quest_Start
Variables:
string sQuest = Kill_Jubilex
int nState = 1
#include "qst__pqj_nwnx_inc"
void main(string sQuest, int nState)
{
object oPCMember=GetPCSpeaker();
if (!GetIsObjectValid(oPCMember))
oPCMember=GetFirstPC();
object oPCF = GetFirstFactionMember(oPCMember,FALSE);
while (GetIsObjectValid(oPCF))
{
AddPersistentJournalQuestEntry(sQuest, nState, oPCF, TRUE);
oPCF = GetNextFactionMember(oPCMember, TRUE);
}
}
PC seeks Jubilex and enters a trigger at the front door of his lair, spawning Jubilex only if they have the quest. The trigger is set up as follows:

Trigger OnEnter: Quest_Trigger_Encounter
Variables:
sQuest = Kill Jubilex, nGoal = 1, sObjectType = C, sTemplate = Jubilex, sLocationTag = wp_jubilex, bUseAppearAnimation = 1, fDelay = 3, nSpawn = 1, nDoOnce = kill_jubilex
#include "qst__pqj_nwnx_inc"
#include "ginc_var_ops"
#include "ginc_actions"

void main()
{

object oPC = GetEnteringObject();

string sTemplate = GetLocalString(OBJECT_SELF, "sTemplate");
string sLocationTag = GetLocalString(OBJECT_SELF, "sLocationTag");
int bUseAppearAnimation = GetLocalInt(OBJECT_SELF, "bUseAppearAnimation");
string sNewTag = GetLocalString(OBJECT_SELF, "sNewTag");
float fDelay = GetLocalFloat(OBJECT_SELF, "fDelay");
string sQuest = GetLocalString(OBJECT_SELF, "sQuest");
int nState = RetrieveQuestState(sQuest, oPC);
int nSpawn = GetLocalInt(OBJECT_SELF, "nSpawn");
int nGoal = GetLocalInt(OBJECT_SELF, "nGoal");

object oTarget;
object oSpawn;
location lTarget;
oTarget = GetNearestObjectByTag(sLocationTag);

if (GetIsPC(oPC))
{
if (RetrieveQuestState(sQuest, oPC)>= nSpawn)
if (RetrieveQuestState(sQuest, oPC)<nGoal>= 1)
if (RetrieveQuestState(sQuest, oPC)<= nGoal)

{
SetLocalIntOnAll(oPC, sQuest, nState);
FloatingTextStringOnCreature(sFloatingText, oPC);
AddPersistentJournalQuestEntry(sQuest, nState, oPC, TRUE);
}
}
}
You kill Jubilex, nState is bumped to 2, and return to NPC.

NPC: Kill Jubilex?
Text Appears When: Quest_Progress (the NPC checks all party members and only rewards the quest if)
Variables:
string sQuest = Kill_Jubilex
int nState = 2

When rewarding the party, Kill_Jubilex is bumped to 3 on all party members.

Actions Taken: Quest_Finish
Variables:
string sQuest = Kill_Jubilex
int nState = 3
#include "acr_xp_i"
#include "qst__pqj_nwnx_inc"
void main(string sQuest, int nState)
{
if (GetLocalInt(GetModule(),sQuest)<nState)
SetLocalInt(GetModule(),sQuest,nState);
if ((GetGlobalInt(sQuest)<nState))
SetGlobalInt(sQuest,nState);
object oPCMember=GetPCSpeaker();
if (!GetIsObjectValid(oPCMember))
oPCMember=GetFirstPC();
object oPCF = GetFirstFactionMember(oPCMember,FALSE);
while (GetIsObjectValid(oPCF))
{
AddPersistentJournalQuestEntry(sQuest, nState, oPCF, TRUE);
ACR_GiveXPToPC(oPCF, GetJournalQuestExperience(sQuest));
oPCF = GetNextFactionMember(oPCMember, TRUE);
}
}
The obvious advatage of this system is that it requires no scripting and consists of 5 template scripts. You get a template conversation, template trigger and template mob, and all the builder has to do is fill in variable names and values and add journal entries.

But AL's found some holes in it and I could use help sorting them out. It may be an ass-about way of doing things, so suggest alternatives at will.
Image
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

What holes did he come across in the system? The state machine concept is fine and probably a perfect fit for a quest system IMO.

Some observations:

* The NPC offers the quest to a PC if nState != 3 on him or his companions. However, when the PC accepts the quest, nState = 1. So, a PC will be able to accept this quest multiple times unless it's preempted with another conditional higher up in the conversation tree. It may or may not be a big deal for this quest, but it's worth noting that the possibility exists.

* Once the PC accepts, either the PC and their companions will get the quest assigned to them, or if they somehow manage to log off between accepting and being assigned the quest, the first player in the player roster will. While I think this is virtually an impossible condition to trigger (maybe in cases of extreme lag?), I don't think you want to assign quests to random players on the server. The dialog should probably just abort if the PC speaker is no longer available.

* The 3 consecutive IF statements in the trigger script are not constructed well, IMO. The lack of {} means that each is probably nested within the other. The syntax of the 2nd statement isn't correct - does this actually compile? - and the 3rd statement will always be true. Can you clarify what you intended with that whole sequence? I don't really understand how the quest state relates to any of those parameters on the trigger.

* Quest states are not persistent.

Also, what's the purpose of setting the quest state on the module and as a global int? Are they for ONE TIME quests?

Anyway, I'm not sure I've helped you any here Indio. Apologies if this is not the sort of critique you were looking for.


p.s. If you're not a programmer by trade but enjoy scripting, I'd highly recommend this primer on best coding practices on the lexicon. It's a more than adequate introduction for the novice coder and it's very brief.

http://nwnlexicon.com/compiled/article. ... index.html
User avatar
indio
Ancient Red Dragon
Posts: 2810
Joined: Sat Jan 03, 2004 10:40 am

Post by indio »

What holes did he come across in the system?

Too numerous to map out cleanly. Maybe you can put it in a nutshell AL. My summary is problems with party-wide persistency.
* The NPC offers the quest to a PC if nState != 3 on him or his companions. However, when the PC accepts the quest, nState = 1. So, a PC will be able to accept this quest multiple times unless it's preempted with another conditional higher up in the conversation tree. It may or may not be a big deal for this quest, but it's worth noting that the possibility exists.
I didn't include all aspects, but I've got conditions in place to stop repeatabilty. The problem has occurred periodically, but more in relation to oversights on my part than a system-wide problem.
* Once the PC accepts, either the PC and their companions will get the quest assigned to them, or if they somehow manage to log off between accepting and being assigned the quest, the first player in the player roster will. While I think this is virtually an impossible condition to trigger (maybe in cases of extreme lag?), I don't think you want to assign quests to random players on the server. The dialog should probably just abort if the PC speaker is no longer available.
I'm not sure yet how to do this but will include it.
* The 3 consecutive IF statements in the trigger script are not constructed well, IMO. The lack of {} means that each is probably nested within the other. The syntax of the 2nd statement isn't correct - does this actually compile? - and the 3rd statement will always be true. Can you clarify what you intended with that whole sequence? I don't really understand how the quest state relates to any of those parameters on the trigger.
I somehow managed to incorrectly copy the code of that script. Here it is in full:

Code: Select all

#include "qst__pqj_nwnx_inc"
#include "ginc_var_ops"
#include "ginc_actions"

void main()
{

object oPC = GetEnteringObject();

	string 	sTemplate = GetLocalString(OBJECT_SELF, "sTemplate");
	string 	sLocationTag = GetLocalString(OBJECT_SELF, "sLocationTag");
	int 	bUseAppearAnimation = GetLocalInt(OBJECT_SELF, "bUseAppearAnimation");
	string 	sNewTag = GetLocalString(OBJECT_SELF, "sNewTag");
	float 	fDelay = GetLocalFloat(OBJECT_SELF, "fDelay");	
	string 	sQuest = GetLocalString(OBJECT_SELF, "sQuest");
	int 	nState = RetrieveQuestState(sQuest, oPC);
	int 	nSpawn = GetLocalInt(OBJECT_SELF, "nSpawn");
	int 	nGoal = GetLocalInt(OBJECT_SELF, "nGoal");


	object oTarget;
	object oSpawn;
	location lTarget;
	oTarget = GetNearestObjectByTag(sLocationTag);


	if (GetIsPC(oPC)) 
 {
  	if (RetrieveQuestState(sQuest, oPC)>= nSpawn)
  	if (RetrieveQuestState(sQuest, oPC)<= nGoal)
	
	{
	if (!GetIsPC(oPC)) return;
	int DoOnce = GetLocalInt(oPC, GetTag(OBJECT_SELF));
	if (DoOnce==TRUE) return;
	SetLocalInt(oPC, GetTag(OBJECT_SELF), TRUE);

	lTarget = GetLocation(oTarget);
	
	oSpawn = CreateObject(OBJECT_TYPE_CREATURE, sTemplate, lTarget);
	}
}
}
Hopefully that makes more sense. Sorry for the confusion.
* Quest states are not persistent.
Persistence has been working ok through this include: #include "qst__pqj_nwnx_inc"

I'm looking for the ALFA versions at the moment but haven't found them yet. The include currently writes to NWNx4 and then to SQLite.
Also, what's the purpose of setting the quest state on the module and as a global int? Are they for ONE TIME quests?
Not sure. I appropriated code from lots of places, as I'm not a scripter. Whch part can I get rid of?
Anyway, I'm not sure I've helped you any here Indio. Apologies if this is not the sort of critique you were looking for.
t's pretty much exactly what I was looking for mate. Thanks. If I can get AL to chip in wih his observations (he's had quite a bit of exposure to the working and non working elements of the system, I'd love it if you could help refine it with me so any builder can use it to make persistent quests without scripting.
p.s. If you're not a programmer by trade but enjoy scripting, I'd highly recommend this primer on best coding practices on the lexicon. It's a more than adequate introduction for the novice coder and it's very brief.
:wink:

lol I've seen your code, and it's spotless. I better look at the manual before dragging my next dead rodent onto your doorstep.
Image
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

indio wrote:My summary is problems with party-wide persistency.
Is that also supposed to be handled by qst__pqj_nwnx_inc or are you trying to extend what's there to party wide persistence? Can you post the contents of that file or is it simply to big to post?
I'm not sure yet how to do this but will include it.
When the player logs off, I would imagine the dialog ends on its own. All you would need to do is simply end execution of the script that was running (for quest acceptance) with a "return" statement. So replace this:

Code: Select all

if (!GetIsObjectValid(oPCMember))
oPCMember=GetFirstPC();
with this:

Code: Select all

if (!GetIsObjectValid(oPCMember)) { return; }
I somehow managed to incorrectly copy the code of that script.
NP. Looks better now. :)
Persistence has been working ok through this include: #include "qst__pqj_nwnx_inc"

I'm looking for the ALFA versions at the moment but haven't found them yet. The include currently writes to NWNx4 and then to SQLite.
Feel free to peruse acr_db_persist_i if you like to peek behind the scenes, otherwise I think this is all you really need to set and retrieve persistent values:

http://www.alandfaraway.org/docs/Techni ... ersistence
Not sure. I appropriated code from lots of places, as I'm not a scripter. Whch part can I get rid of?
I can't safely say without seeing the whole kit and kiboodle, but I think the SetGlobal* can go as I believe that's used for persistence across multiple modules in a single campaign (like the way the OC was setup).
It's pretty much exactly what I was looking for mate. Thanks. If I can get AL to chip in wih his observations (he's had quite a bit of exposure to the working and non working elements of the system, I'd love it if you could help refine it with me so any builder can use it to make persistent quests without scripting.
I'd be more than happy to help and seeing as a Quest System is one of the things on our "wanted" listed, I guess I'd be killing two birds with one stone. Thanks for tackling this. :)
lol I've seen your code, and it's spotless. I better look at the manual before dragging my next dead rodent onto your doorstep.
lol, please consider it informational only. I certainly don't want people developing a complex about their code. :bath:
User avatar
indio
Ancient Red Dragon
Posts: 2810
Joined: Sat Jan 03, 2004 10:40 am

Post by indio »

I'm going to find another way to present this post, as it's a nightmare of code and it's altogether too long.
Last edited by indio on Thu Aug 16, 2007 12:27 am, edited 1 time in total.
Image
Thangorn
Haste Bear
Posts: 2081
Joined: Fri Oct 01, 2004 1:00 pm
Location: Queenstown, New Zealand

Post by Thangorn »

I am eagerly awaiting the development of this system. I'd love to help but I've got no knowledge of MySQL or NWNx4 at present.

Quick questions:
1. Does the system allow you to be on more than one quest at a time?
2. What are the chances we can get this working with a faction reputation system?
On indefinite real life hiatus

[22:52] <Veilan> obviously something sinister must be afoot if a DM does not have his social security number in his avatar name!
User avatar
indio
Ancient Red Dragon
Posts: 2810
Joined: Sat Jan 03, 2004 10:40 am

Post by indio »

1. Yeah, it does. But AL has found that there are inconsistencies, which he'll do a much better job of elaborating on than I can.

2. Rowell's PW system has an interesting faction/reputation system tied into it: http://nwvault.ign.com/View.php?view=NW ... tail&id=63

It's a little simple, but maybe that's exactly what we need.
Image
User avatar
indio
Ancient Red Dragon
Posts: 2810
Joined: Sat Jan 03, 2004 10:40 am

Post by indio »

Allright. Here's a better way of looking at this. I'll get to making an erf test system out of it once cipher thinks its worth taking that far.

http://www.thesilvermarches.net/uploads ... Quests.pdf
Image
User avatar
indio
Ancient Red Dragon
Posts: 2810
Joined: Sat Jan 03, 2004 10:40 am

Post by indio »

I'm still interested in getting this packaged so it can be shipped to other builders. Based on the PDF above, what needs to happen to begin that process?

Obviously there are two hurdles to overcome. The first is the lag this system is causing. The second is tidying it up and sealing any leaks.

It's no priority, but I know Thangorn is heading towards quests. Would be good to help him out.
Image
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

I've been looking at it this afternoon, Indio. It would be tremendously helpful if I could examine/run/test your module locally. Could I somehow obtain a copy of what you are running? I'll discuss script changes with you before we commit anything.

Thanks.
User avatar
indio
Ancient Red Dragon
Posts: 2810
Joined: Sat Jan 03, 2004 10:40 am

Post by indio »

http://www.thesivermarches.net/uploads/sept3.rar

Thanks cipher.

This is the version I uploaded for AL earlier today. The script "qst__pqj_nwnx_inc" needs to be re-compiled into the module. It's currently comented out.

Now the reason it's commented out is that while it's active, the quest system causes extreme lag on the server. AL is currently looking into why.

The small module contains the start area and the High Hold location...so when exiting the start location, ensure you choose High Hold. There are several quests in High Hold when you exit the boat, so choose perhaps the Ghoul Quest (admittedly I have't tested this one), but it *should* be working.
Image
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

I found one serious problem thus far. The column indexes for parsing the SQL results were off by one (should be base 0). I went ahead and optimized things as I went through the code as well, including the schema and database calls. This script assumes a generic install, and thus stores player account and character names in the quest table. That's extraneous. Since we already record that information elsewhere, the quest table just needs to record the character's ID.

I'm going to create a separate quest table and test this code in your module, Indio. I'll report my findings soon as I do.
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

Well it seems to work fine with the changes I made. I was able to login in, travel to high hold, accept a quest (logged hh_swamp_baroness in the database with state=1), walked around the river bank for a while, then logged off. There were some sporadic "jumps" on my screen but no more than usual with the server and client running on my PC simultaneously.

I then ran the module with the original file (uncommented and compiled) and I couldn't even move. Is this what you experienced? Just want to be sure I'm observing the same thing - it's late and I'm not discounting the fact that I could have just botched something. I'll try it again after I get some sleep and see if my test results change.

:sleepy:
User avatar
indio
Ancient Red Dragon
Posts: 2810
Joined: Sat Jan 03, 2004 10:40 am

Post by indio »

That is indeed what I was experiencing.

Is there nothing that between yourself and AL that cannot be solved? Moreover, to have such hands-on support and such a willingness to help, it's much appreciated.
Image
User avatar
ç i p h é r
Retired
Posts: 2904
Joined: Fri Oct 21, 2005 4:12 pm
Location: US Central (GMT - 6)

Post by ç i p h é r »

No worries Indio. You've built such a beautiful server that it was rather a pleasure to work on this, honestly.

I'll see if I can repackage the whole system a little more neatly - strip out what we don't need and tidy up what we do - then get it back to you and see how things fare. If all's well, I'll add it to our ACR codebase and we can issue another release so folks like Thangorn can get cracking on quests.

p.s. How many quests have you created already, Indio? There are measures we've taken with ACR to prevent accidental local data conflicts/corruption, but to pursue it here would mean you'd need to adjust variable names on quest triggers. I'm trying to gauge whether it's worth the effort to do that here.

p.p.s. There's an sChange configuration option for bounties that I don't quite understand. Any idea what purpose this serves or why it needs to be configurable? Can't we always increment by 1 and just not expose this as an option (for simplicity)?
Locked