If you are not familar with the system, you may want to take a look at in order for some of this to make sense.
Leanthar and I have been talking through some ways to make it PRR compatible. This post is designed to run by the proposed changes to any who are interested and who can put their two cents in.
Originally, the PRR system was designed to keep all the variables for a relationship on the NPC in question. We came up with the idea of placing all the vars on one object in the module. This would make it easier to iterate through during a save. It will also allow you to keep the vars if your NPC gets killed and then respawns.
I have been wrestling with the problem of how to iterate through the a large number of relationships the the prr system will create during a database save. For instance, if you use this for 50 NPCs and you have had 300 players jump on your world since it started, that right there is potentially 15000 relationships. Even if you only save to the database every 15 minutes, that is still a pretty big loop for your server to chug through. I was also looking for a way to keep the number of comparisons down when accessing the PRR variables in the game.
The solution that I came up with is to use the concept of numbered PAIRS to keep track of these relationships. When I am adjusting the relationship for one of these pairs, I do a quick check to see if the pair exists, and if not, I create it in such a way that will hopefully keep the performance hit on the low side.
Here is a block of code to demonstrate what I am talking about:
Again, the idea is to only create the pairs that are needed, not all potential pairs.
A side effect of this is that all NPCs that are named the same will share the same relationship. This might be useful for when you wanted to create a subgroup within a faction.
I have a different system. I don't know if it works FASTER, but it has always worked consitently with me. --Using George Zoeller's Data Base system.
These are neccessary files to make this system work, there is an explanation on how to use them further down. BTW downloading and aquiring Adam Miller's script files from Witch's Wake is crucial!! This will be explained later.
This file is called con_rep_b_gen and goes on the Actions Tab in the dialog where you want the notify the PC of their status of reputation with the NPC, it does not change anything.
This is one of many Starting Condional files I use that returns for the PC's status and gives appropiate response depending on the reputation with the NPC. For instance this script is checking for 5, which I consider as the starting rep, NPC's are fairly neutral in attitude and should represent that in their replies. This could be changed to whatever your liking, Morrowind's rep system appears to be anywhere from 0 - 100, that is quite large and since my systems adjusts by incriments of just 1, getting to 90 would take forever, therefor I have tailored my system to about 1- 18, where at 14 the NPC's practically love you, but it can definetly go higher. Create more files as needed, check for 1, 2, 3, 4 etc. or create others like LESS than 3 for a bad status <= 3, returns when the PC's rep is 3 or less, which is pretty bad, IMO.
This file is extremely important since it sets the difficulty rating that the PC must defeat inorder to increase his/her reputation with the NPC. As the PC successfully increase his/her rep with the NPC, naturally, it get tougher to do so. The RepTime keeps tracks of how many times the PC has done this, the more times the ahrder it gets. I am merciful though so if it seems to get out of hand it eventually sets the RepTime to 7 thereby if the PC has really screwed up their reputation they have some chance at correcting it.
This file is called con_rep_set and adjusts the PC reputation with the NPC if sucessful in doing so, place this on the Action Tab in the dialog file. As with the notes above, if you want to get closer to the Morrowind style and not have your PC's rep change by 1, you can do something like; int iChange = (d20()); SetCampaignInt(sPlayerID, "Rep_"+sSpeaker, iOpinion + iChange, oPC); However I'm not to sure if this works (will test it later ). Experiment for yourself on how you want to utilize this.
This file is called con_rep_set_br and does the same thing above, except that the PC has successfully BRIBED the NPC, I have this currenlty taking only 10 gp from the PC but as in Morrowind you can simply create 3 files out of this one, one for each of the settings, bribe 10g, bribe 100g, and bribe 500, or 1000 which ever. simply add a suffix to the file name, 1, 2 or 3.
This file is called con_rep_b_neg and goes on the Actions Tab in the dialog file where the PC has FAILED in their attempt to increase reputation with the NPC
Using the files. I generally like to have the general broadcast placed where the NPC first greets the PC. That way you know what your current pre is right off the bat. In my mod I have created about 10 different greetings, I have scaled them appropiately by the rep status as so; greeting 1: rep of 14+, greeting 2: rep of 12+, greeting 3: rep of 10+ and so forth until it all the way down to a nasty rating of 2 or less. Which case the PC cannot get any useful info out the NPC and must 'apologize' for their behavior. Greeting should represent their rep ratings, so write the dialog accordingly, if its high, make the greeting sound very welcome, if its low, make it sound busy, or irritated. For extra greeting ideas you can create for files checking for sex, or race, thus creating many more different and colorful greetings.
After the inital greeting break down the Dialog file similar to Morrowind's, I gave the PC four options, 1st option QUESTIONS, this is where the PC gets info, history, plots and so forth from the NPC. Again use the Starting Conditionals to check for reputation. If the NPC doesn't like the PC put something like "I'd rather not about this right now." Hide secret info until the PC has a good rep, or just flat out tell the PC lies if their rep is bad. Could make for interesting situations. 2nd option, INCREASE REPUTATION, use the Insert Token and then Action Highlight so it shows up in the dialog nicely. This is where the meat of this system begins. After the PC presses the Increase Rep line the NPC responds with, "something on your mind", place the con_rep_dset file on the Action Tab here. This sets the difficulty rating against which the PC will try to adjust his/her rep with the NPC. Next come the list of ACTIONs which the PC can perform inorder to adjust their rep with the NPC. My list is extensive, but we will use Morrowind's list here instead. Bribe: Admire: and Intimidate: Download Witches Wake module by Adam Miller. You will need all the files that begin with ww_ in the prefix. On the Bribe starting cond. put a check for gold script there. The PC cannot bribe if they don't have any gold! Check for 10, 100, or 500 and create three separate lines. Bribe: 10, Bribe: 100, and so forth. On the Actions Tab put this script: ww_abil_intellig. This does the dice rolls and broadcasts it nicely. After this there should be 2 responses that the NPC has avaliable. The 'you have succeeded' response, and the 'you have failed' response. Put this on the Succeed starting cond. ww_abil_success, and this on the fail, ww_abil_succfail. Put this on the Succeeded Actions Tab, con_rep_set_br, and this one on the Fail, con_rep_b_neg. The Admire Action is similar except it doesn't take any gold away from the player, change con_rep_set_br to con_rep_set.
Setting the int at the beginning of the module: This line here (SetCampaignInt(sPlayerID, "Rep_"+sSpeaker, ,oPC)); is what you want to use to set the int's to default so that all NPCs are neutral to the PC. My script is a nit clunky as I place the TAG of the NPC where sPeaker is and set the int to 5. Thus it should look like this;
This file could get big, depending on how many NPC you want to create reputations with. There could be a better way of setting the ints, but I have bothered to figure out a better way...yet
Play through Witch's Wake! Learn how Adam Miller uses those ww_ scripts. This is essential to understaind this system. In my mod I have many extra options to change the reputation with the NPC, I have Cast: Charm Person, Cast: Spook, Flirt, Persuade, Intimidate and many more depending on the situation. Anyway I hopes this helps. Please ask any questions if needed, I know this is a bit daunting at first but this system is very efficient and there is losts of room for improvment. You can also contact my through email, n.bixel@mailexcite.com
Your probably asking; where's the Advice part? I put that under the INCREASE REPUTATION. The Dialog file should look similar to this;
Question: Change Rep: Advice: Goodbye:
On the Advice tab the NPC replies with advice(duh!). This should look like this; Advice, then NPC replies <CUSTOM501>, PC replies "lets talk about somethins else", NPC replies, "what do you want to talk about?", then back to Question: Change Rep: Advice: and Goodbye:. What is <CUSTOM501>? Place this script on the On Actions Tab.
Thanks for the feedback and thanks for letting me look through the code. I'm at work right now, so I can't get too deep into it, but I do have some initial reactions. For the most part, it looks similar to what I am doing, but I also take into account alignment differences, faction standings and charisma bonuses to create a value between 0 and 100. I have one or two questions though.
It looks like you are saving everything to the Campaign Database on the fly. I save everything on a game object because of this quote from nwnlexicon.com :
"Moreover, database access in NWN is slow. It is a good idea to store variables locally, then transfer and read them to and from database only when you need to.
It is also important to remember that variables in the database are independent of the savegames. If a player goes back to a previous savegame, he'll still have all the latest variables in the database. While this is unlikely to actually help him, it can potentially corrupt the entire gaming experience for the player.
Thus, the real strength of the database is probably this: Ability to store variables in PWs on a regular basis, so a lot of information isn't lost (for instance, backup of variables can happen every hour or so). And it provides an easy way to transfer information between two or more modules, for instance in an official campaign type setting."
I wanted to keep from slowing things down in game with this, and I also want this to work for both PWs and one-play modules where there might be saving and loading.
While looking at this I did finally look at how campaign variables work though.
It looks like variables are tied to a PC object in the database. Does anyone know if the same name problem has been tested? (Again see the note on the lexicon.) Now that I know that, this might be easier.
Or do most people tie things to OBJECT_INVALID so as to make them global?
This is looking good. I agree you need to make it so that it does not save very often, database access kills servers quickly. Storing to a local object and then letting the PW's out there choose how often to save to a DB is the best way to do it. So long as you have function to save to the db writen we can make the call to it when needed.
As to the same name and DB thing that you asked about, that does cause a problem. You need to get the name of the PC and their NWN key... something like this
Looks good Vendalus, Can't wait til it's finshed. _________________ "I'll sit myself, behind this clock, and sing tunes on its belfry... I am the one you warned me of.."
Well it just goes to show that i should have been paying attention to the forums more often. I am glad for the replys. Hoping I can "smooth" out the code and having everything set locally. I've working on a PW world for about a year now. Most of the coding has been strickly NPC interactions, conversations, added NPC personality, and further NPC actions triggered by quests and such. I've only just begun to delve into PW server coding. I recently aquired NESS spawn system, and I am also looking at PW Party EXP and Treasure systems as well. So the DB system slows things down eh? What is your recommendation for saving the info? I currently have a Bind/Save Point system. However other PW worlds have a SaveOnExit, though I don't know how that works. And As of yet, I would need to see some code on how this Save Local Ints Later in Database when needed. I could try it myself, but it would be helpful if I had some examples. --As mentioned above, You like to have different reactions depending on Alignment. thats easily done I'm sure you know. The above code is just an example, I have special reactions for being a Half-Orc, or a Drow, or previously hurt/did harm/or stole from NPC's friend and such. Anyway this is a good thread. I'm curious to see some more replys.
Hey! I found a solution to the CampaignInt thing! Fear a slow reading speed from the DB? Well no longer, this system checks for LocalInts first, if it is not found THEN it checks the DB. Found this from EPOlson (eolson@cisco.com) the PWHelper Erf.
OK, I did some more coding tonight and I think I am pretty close now. I had to do some serious juggling because of the 32 character limit on variable names, but everything seems pretty solid. I will probably post what I have tomorrow evening.
Ok, here's the deal. I’ve been working on this way too much. I was thinking about how I was doing things and I started getting nervous. The more I thought about it all, the more I began to realize that the system as I had been coding it was going to cause huge memory problems. Without wanting to force people (especially non-PW modules) to use asp, etc, I spent some time seeing what I could find to help me out. I think I found a solution with Knat's Natural Bioware Database Extension.
Here is where I was:
I was storing all PRR variables on one object. Most of what I have read suggets trying to keep under 1000 variables for any one object. Even if I was just keeping one variable for each relationship between an NPC and a PC, the number of variables on a PW would get out of control pretty fast. Let's say that you had 50 NPCs you wanted use PRR with and let's just say that you've had 300 PCs romp through your system. That right there is a maximum possibility of 15,000 variables. Even a small PW would be crippled by using this system.
To compound matters, because of the 32 character limit on variables names, I was using more than one variable per relationship.
Not only that, but if you wanted to remember other things, like if the PC had introduced himself to the NPC, or if the PC had used a certain tactic to try and increase his relationship with that NPC, that is yet another set of variables exponentially increasing the memory load.
Obviously this would not work.
The solution:
After doing some digging around I came across this thread on the message boards concerning Knat's Natural Bioware Database Extension:
By storing all information on an object and then saving that object to the db, Knat might have solved all my problems. I did some testing with his code and it works great. Not only does the database save many variables with on just one object via one connection (instead of hundreds or thousands of database connections for each of your variables), it is easy to set up different database objects.
So now I am creating a different database object in the module for each NPC in question. For example, when the PC Bill talks to the NPC Aribeth, a variable for how he has adjusted her reaction to him gets saved on her database object. When Bill talks to Nasher, a variable gets set on his object. When the PC Sally comes along and talks to each NPC, her variable gets set on each NPC’s database object respectively. Hopefully this should spread out the load quite a bit.
Also, I am pretty sure that his system is set up to only save the info to the database when you specifically call for that, meaning that your in-game performance is even faster.
Admittedly, if I placed some of the other standard variables on there, commonly used NPCs could push the number of variables placed on their database objects into the red. (more on that below)
Saving objects to the database would occur via a GetNextItemInInventory call which would allow me to iterate through all the objects in the database container.
An additional boon of his system is that if it does not find the database object that it is looking for in the module, it loads it from the database, meaning that rarely used database objects do not load until they are needed. After that they stay there. Thus, there is no need to load the entire library of database objects onModuleLoad.
Questions:
1. If I am using multiple database objects like I have described above, am I creating extra overhead by connecting to different bioware databases when I am calling all of my SetCampaignObject calls? Or is it really just one database that I am connecting to and therefore it does not matter as much? Either way, I would have to think that placing 10 objects on 10 different databases to hold 1000 variables is better than placing 1000 variables directly into one.
2. Would it be a better system if I instead used the PC object to construct database objects and stored information on the NPC on each PC? This has the advantage of allowing me to set other variables that I might need for the PC, and allows PWs to set variables on this object as well. Because of the way the Extended database does not call for the database object until it is needed, PCs who only visit once never need to take up memory in the module. The down side is that you end up with a lot more database objects in the game. (which goes back to question one). While trying his test module, nwn locked up on me when I pulled the lever that saved 1000 varialbes directly to the bioware db, which leads me to believe that less objects is better.
3. If I did use the PC object to name the database object, I run into the 32 character limit issue again. What if I did something like this:
Well heck. I will have to see Knat's Natural Bioware Database Extension. Did you find this on nwvault? I'll check. I hope I don't have to do any more altering to the Mod I have been working on...its already 20megs unzipped if I have to aler the DataBase functions one more time....! Argh!
Quote: Posted 06/08/04 19:32:32 (GMT) by Vendalus The solution:
Also, I am pretty sure that his system is set up to only save the info to the database when you specifically call for that, meaning that your in-game performance is even faster.
you are right.. a db write only occurs once you call the NBDE_FlushCampaignDatabase() command. and each flush will stay <200-220 ms if you keep database size below <250kb (thats several thousand integers for example).
you can literally stay completely lag free if you don't flush more then once every few seconds (but i recommend you work out a specific flushing scheme for your system)
so in case you want to flush several databases, it's best to delay each flush at least one second, better two.. i will incorporate a FlushAll command soon...
Quote: An additional boon of his system is that if it does not find the database object that it is looking for in the module, it loads it from the database, meaning that rarely used database objects do not load until they are needed. After that they stay there. Thus, there is no need to load the entire library of database objects onModuleLoad.
Aye ! and you can unload each database simply by deleting the corresponding item in the database container. you can unload all databases simply by deleting the database container itself. it's easy to control memory usage that way...
Quote: 1. If I am using multiple database objects like I have described above, am I creating extra overhead by connecting to different bioware databases when I am calling all of my SetCampaignObject calls? Or is it really just one database that I am connecting to and therefore it does not matter as much? Either way, I would have to think that placing 10 objects on 10 different databases to hold 1000 variables is better than placing 1000 variables directly into one.
i think it does not maintain any logical connection at all. if you use a standard SetCampaign call, then it will open, write and close the database(-file) for each call to this function. yes, it's cleary better to use multiple databases for such an involved system. and it's better to work with small database (below 200kb). furthermore, keep your sVarNames as short as possible...
i will post a small hash function. hashing can tremendously reduce your database size....
Quote: 2. Would it be a better system if I instead used the PC object to construct database objects and stored information on the NPC on each PC? This has the advantage of allowing me to set other variables that I might need for the PC, and allows PWs to set variables on this object as well. Because of the way the Extended database does not call for the database object until it is needed, PCs who only visit once never need to take up memory in the module. The down side is that you end up with a lot more database objects in the game. (which goes back to question one). While trying his test module, nwn locked up on me when I pulled the lever that saved 1000 varialbes directly to the bioware db, which leads me to believe that less objects is better.
it's no problem to use hundreds or even thousands of databases... just be sure to defrag your drive regulary. and give them all a specific prefix to distinct them from other system's databases..
the lockup you experienced is no real lockup.. it just takes the standard bioware 1.5 minutes to write 1000 integers. that's 1.5 minutes of 100% cpu use. the same takes 0.2 seconds usind NBDE.
Quote: 3. If I did use the PC object to name the database object, I run into the 32 character limit issue again. What if I did something like this:
you can hash your database name to overcome the 32char limit. then just use something like sPrefix + IntToString(nHash) as your database name.
Well heck. I will have to see Knat's Natural Bioware Database Extension. Did you find this on nwvault? I'll check. I hope I don't have to do any more altering to the Mod I have been working on...its already 20megs unzipped if I have to aler the DataBase functions one more time....! Argh!
check the link in vendalus last post... it contains a test-case and example module with the full code.
i will release it on nwvault soon.. just had not much time the last couple of weeks to prepare the release.
Awesome. Thanks for the comments Knat. They really answered a lot of my questions. And thanks for the tip on the hashing.
I think that one last bit for me to optimize performance might be for me to actually create a bit of OnExitModule (sp?) coding that will flush the pc's database and destroy his/her database object. That should not only reduce the memory needed, but it will also help clean things up and reduce the amount of data that we are saving during a PWs 5, 10 or 15 minute saves.
I like where this is going now. It's pretty close to a system where all an author has to do to implement the basic system is to copy the _prr_example01 dialog file to an NPC and rewrite it. All of this will also allow me to keep a few extra PRR variables on the pc object that I had not even really started getting into yet.
Thanks again for the great work. I look forward to a full release.
In the meantime, I will hopefully be able to post, at the very least, a good chunk of my code by tomorrow night.
I'm still working on getting this out. I ran into some snags and had to do some serious reworking of some things.
I'm pretty close though. Beyond a few small bugs, the loading and unloading of all prr variables and faction standings are working well.
The only things I have a question about is an onClientLeave function that I am working on that will flush that pc's database and remove the object from the module. This will help with keeping the memory needs down, and it will keep the module from having to cycle through more pc databases than it needs to. Here is what I have so far:
Basically, I am just not familiar enough with how Knat is referring to the database objects. I think it is flushing the database ok, but then it is not getting rid of the object like I want it to.
Disclaimer: All visual imagery and written documentation on this page represents BioWare's
vision of final game implementation. Details are subject to change without notice. Visual
and written content on this page is not intended to duplicate Wizards of the Coast source
materials.