Aller au contenu

Photo

nwn database help - basic select statement


  • Veuillez vous connecter pour répondre
25 réponses à ce sujet

#1
Calgacus

Calgacus
  • Members
  • 122 messages
Hi,
I want to fetch a bunch of strings relating one of several puzzles, however I want many puzzles stored in the same database.  In sql I would do something like this:
select strName from puzzletable where puzzleid = 23;
in NWN database I can use a " where puzzleid = 23 " clause so how can I select a particular puzzle?  The puzzles get entered into the database manualy so I won't be storing any player info with them so each will just have player=OBJECT_INVALID stored instead.

Or will I just need to store each puzzle in its own database?

PS: with the GetCampaignString(  string sCampaignName,
    string sVarName,
    object oPlayer = OBJECT_INVALID ) function, does it treat sVarName as a column name or as a key and some other field as the value?
Here is what my isitial attemp at making a database file looks like, how could I fetch the value of column intr1 if I had ultiple rows?   or even if I had just one row?
http://calgacus.game...atabasefile.bmp

Modifié par Calgacus, 08 novembre 2010 - 01:19 .


#2
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
You will just need to make a unique name for each string you want to place in the DB and use that name to retrive the string.



So if puzzle 1 had say 5 strings in it you could call them.



Puzzle1_Str1

Puzzle1_Str2

Puzzle1_Str3

Puzzle1_Str4

Puzzle1_Str5



If you have a differant number of strings in each puzzle you may want to also give a entry into the data base for that. Puzzle1_Entrys or smmething.



then getting your data out of the DB you would just need to build the name for the entry you want to retrive.



string sEntryName = "Puzzle"+IntToString(nPuzzleNumber)+"_Str"+IntToString(nStringNumber);

string sPuzzleString = GetCampaignString( sBDName, sEntryName);



Hope that helps.




#3
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
You do know you can use SQL if you're building for a PW, right? Just use NWNX with ODBC:

http://www.nwnx.org/



Nwn's native database is a joke when it comes to stuff like this.



Funky

#4
Calgacus

Calgacus
  • Members
  • 122 messages
Thanks Lightfoot8,

So the sVarName parameter then refers to a value in a column inside the DBF file? Since I am making these files manually what column names should I give my fields?



BTW, what about those CDX files, how who and when do they get generated?

#5
Calgacus

Calgacus
  • Members
  • 122 messages
Funky,

Yes but I am modding for nwn single player now, and i am looking forward to dragon age modding just for this reason.

#6
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
Either I didn't  understand  the question or you did not understand the answer. I am not really sure wich.
The nwn DB does not have any collumns.  It is a simple VarName = value.  All entrys in the DB have to have a unique VarName.  You can simulate collumns by the method I showed above, But they are really just a bunch of single entrys. 

As far as when a CDX file gets created.  It is created along with two other files(.ftp and .dbf)  to make up the NWN DB by the game.  All three files make up the single DB.  

  Since it is starting to sound to me like you are trying to create the DB outside of the game. I can honstly say I have no Idea how to do that.  I have only looked at the file formats once a long time ago and never spent enough time to figure them out.

I can say that what you are trying to do is going to be a pain in the *bleep* to try and do with the nwn DB.  fixing bugs or spelling would be more work then it would be worth. At least by the ways I would know how to go about it.  
A second option would be to just create a 2da file for your data.  not knowing the length of the strings you plan on creating or the content of the strings( would they contain carrige returns?), I do not know if this would be a good option or not. 

A 2da would be simple if the strings where not two complex.

2DA V1.0

     nString     String1     String2     String3     String4  String5
0     3       "string1"   "this is string 2"  "and string 3"   ****  ****
1      5      " This would "  "Be puzzle 1 "    " Or the second puzzle"   " in the 2da with 5 strings"   "Puzzle 0 had 3 strings" 

If needed You can find the 2da file format at the link below.   Since they are plain text files they are easy to create and edit.

Documentation: 2DA File Format

Modifié par Lightfoot8, 08 novembre 2010 - 03:11 .


#7
GhostOfGod

GhostOfGod
  • Members
  • 863 messages

Calgacus wrote...
I want to fetch a bunch of strings relating one of several puzzles, however I want many puzzles stored in the same database


Normally one might use the NWN database in this particular situation with one database per puzzle with all the different strings stored under that one database name.

SetCampaignString(string sCampaignName, string sVarName, string sString);

The first parameter would be the Database name(CampaignName).
The second parameter would be the ID of the string we are going to strore.
And the third parameter would be the actual string itself that we stored.

To retrieve the string you you look in that Database for the particular ID and get the string.

There is one way of doing it so that you could store many strings under one ID but it requires a bunch of string parsing with scripting. It can be a bit of a pain in the you know what and in game can cause some lag. If you are intent on doing this with the NWN database I could post some examples of how you could use string parsing to do what you need.

Lightfoot offers an excellent alternative as well.

If you are already familiar with sql then perhaps you may want to go with Funky's suggestion and look into using NWNX. It is much faster and might be better for your needs.

Good luck.

Modifié par GhostOfGod, 08 novembre 2010 - 03:47 .


#8
Calgacus

Calgacus
  • Members
  • 122 messages
Thanks,
I think I figured it out, the nwndb uses a standard table template like this http://calgacus.game...ardtemplate.bmp, if you ask for an int it returns the value in the "int" column, if you ask for a string it returns the value in the "memo" column, it uses the "varname" column values to match with the sVarName param and the playerid column values to match with the PC.

To have more than one puzzle in the same table I need to use Lighfoot8's suggestion. probably just as easy and quick to use one table per puzzle but either seems easy now.

esentially a standard cal to
GetCampaignString(string sCampaignName, string sVarName, string sString, object oPC); translates to a select like so:
select memo from sCampaignName where varname == sVarName and playerid == oPC;

***** So I cannot add my own columns to the database. :-( *****
also
GetCampaignInt(string sCampaignName, string sVarName, string sString, object oPC); translates to a select like so:
select int from sCampaignName where varname == sVarName and playerid == oPC;

PS: I was thinking of 2DA files but my strings are kinda long - maybe 1000 characters, and I also I want people to just be able to download and use new databases full of fresh puzzles without having to get a new copy of the mod file.  I know  I could  give them 2das they could drop in their override folder so thats still an option.  

So what do you think is a better option, I guess it might be easier to work wit the 2DAs.  
How will the read performance compare? 
The database files I will at least be able to update ingame - say to mark a given puzzle as used - so it won't get reused the next time someone plays the mod or to update  a puzzle counter to indicate what puzzle to use next. 
Will my override folder 2DAs take effect if dropped in after a mod has been loaded and a game saved?


Thanks

Modifié par Calgacus, 08 novembre 2010 - 04:15 .


#9
Calgacus

Calgacus
  • Members
  • 122 messages
of course I could use both, a 2DA to store the puzzles, a tiny DB to store info about which puzzle was used, which to use next, etc
So I could always add puzzles to a 2DA, people could just drop it into the override and the DB would still be valid and no user would reuse a puzzle after refreshing the 2DA.
When do 2DA files get loaded?  on module load?  only when called on for a value?

Modifié par Calgacus, 08 novembre 2010 - 04:29 .


#10
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Do the puzzle states need to be persistent across saves, or are you just trying to store their data in a convenient way? If the former, I'd go with locals. If the latter, I'd simply store the data in script. NWN DB calls are EXTREMELY slow, though reads are quite a bit faster than writes.



Oh, just read your post again - getting late here, sorry. 2da performance will FAR exceed any other option, including coding into script, since the new caching was put in. Just don't use more than say, 5-8 2das for your puzzles. Remember, the new caching defaults to 10 cached 2das, and you may occasionally want to pull up another. If you read them out of caching, they are slow as molasses compared to anything BUT the NWN DB (which puts molasses and snails to shame, especially on writes - actually, exaggeration aside, its read speeds might exceed uncached 2das, but that discussion is largely academic).



The string length problem is solved by using StrRefs to tlk table entries - you would use a custom tlk to go with your 2da.



I don't know how 2das will take to being hot-dropped into a loaded game. I don't work much in SP.



You WOULD need Persistants in order to track data across games. I would suggest only using them for tracking which puzzles have been done, and I would suggest a flagset to keep reads to a minimum.



Funky

#11
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
I see you twigged to the same notion in your latest post. With a flagset you can store up to 32 different binary puzzle states (done/not done) if you don't have an ordering. If you do have a fixed ordering (ie puzzle 1 done, puzzle 2 is next, then puzzle 3 when 2 is done), you can store over 2 billion, no flagset needed.

Funky

Modifié par FunkySwerve, 08 novembre 2010 - 04:39 .


#12
Calgacus

Calgacus
  • Members
  • 122 messages
How big can a 2DA string be?  If I use a custom tlk, can the users drop it into the override?

Modifié par Calgacus, 08 novembre 2010 - 04:54 .


#13
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Short answer: no idea.



Longer answer: it doesn't matter, put it in a tlk. :P



What are you looking to store? I know you mentioned 1k lines, which is going to be VERY inconvenient when trying to do column spacing - nevermind the spaces between words that would have to be parsed in and out.



Funky

#14
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
One thing to keep in mind is that the PlayerId is equal to the characters name. This can become unreliable if two players have characters with the same name.



VarType will determine the collumn that is retrived by the call with GetCampaign???? with GetCampaingnstring returning the what you have as the memo collumn(assumeing that you didn't add the memo collumn yourself). Since the vartype is hard coded in the game, I don't see where you will be able to add collumns to the DB without hak into the game.



If I am off base here and you can get a VarName to hold more then one data type and get the game to read it durning play, I would sure like to hear about it.

#15
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

If I am off base here and you can get a VarName to hold more then one data type and get the game to read it durning play, I would sure like to hear about it.

Of course you can. Just concatenate, with a separator, in string format. I used to do this quite a bit before I swapped to NWNX.

Funky

#16
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
custom talk files would need to go into the tlk folder not the override folder. They would also have to be there at module load. not sure if you would even be able to replace the exsisting talk file durning play since it would most likely be in an open state by the game.



2da file droped in the override folder durning play would not take effect untill the game was loaded again. The resource list the game draws from is created at module load.

#17
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages

FunkySwerve wrote...

If I am off base here and you can get a VarName to hold more then one data type and get the game to read it durning play, I would sure like to hear about it.

Of course you can. Just concatenate, with a separator, in string format. I used to do this quite a bit before I swapped to NWNX.

Funky


Thanks funky,

But that was not really the subject.  A string is still a string even if it is packed data.    If you look at the link he had there.  It looked like he was trying to use more then one collumn from the nwn DB at a time. 

#18
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Yes, I know what he's trying to do. You can accomplish that by packing data in either the var name (key) or the value, or both. You then convert the string to whatever other types you need. This is, in fact, precisely how NWNX-MySQL works - EVERYTHING passing in and out is a string until converted. You simply use StringToInt, StringToFloat, etc etc.



Funky

#19
Calgacus

Calgacus
  • Members
  • 122 messages

Lightfoot8 wrote...

custom talk files would need to go into the tlk folder not the override folder. They would also have to be there at module load. not sure if you would even be able to replace the exsisting talk file durning play since it would most likely be in an open state by the game.

2da file droped in the override folder durning play would not take effect untill the game was loaded again. The resource list the game draws from is created at module load.


I don't need to the new 2DAs to be refreeshed during play, but at the load of a saved game would be good - not entirely necessary though.  The main thing is that after a play through of the mod, if  a person wanted to play again then they could get a new puzzle DB  or 2DA file, drop it in to the override or database folder and when they play the game  the new contents would be available.  A tiny DB file would just contain a single integer which gives the puzzle number to use.  So i guess  the 2DA method makes the most sense, using one 2DA for each puzzle I could have any number of instances of that puzzle in the file, and each time i read one up i increment the integer in the DB so the next time one  is read up it gets the next puzzle.   The tlk option doesn't work unless i can drop them into the override.   I want the puzzles to exist outside the mod so I can distribute them separetly.

Lets see, lets say I am doing a Clue game, in a given mansion  I want a murder mystery puzzle so i store the puzzles for that location in a 2DA file.  Other puzzles for other places fget stored in their own 2DA.  The first time through a mod the mod uses puzzles with id==1, then on a second playthrough puzzles with id==2 etc.

But what if I want an area to refresh its puzzle during play?  I guess I should use a different databse for each puzzle so the id can be different for each.  ok, I guess as long as the 2DA's let me use long strings i'm ok with that.

A limited number of cached 2DAs eh?  does that limit include the standard 2DAs?
Any more thoughts?

Thanks.

#20
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages

Calgacus wrote...
But what if I want an area to refresh its puzzle during play?  I guess I should use a different databse for each puzzle so the id can be different for each.  ok, I guess as long as the 2DA's let me use long strings i'm ok with that.

A limited number of cached 2DAs eh?  does that limit include the standard 2DAs?
Any more thoughts?

Thanks.

You're putting way too much emphasis on hot-dropping updates, ESPECIALLY for a SP mod.

The default limit for cached 2das is 10. You CAN increase that, but I wouldn't by too much. 1 2da per game is far too many - like I said above, you want 5-8 total. I would suggest perhaps 1 2da per puzzle TYPE.

The caching limit includes any 2da you call up, standard or otherwise.

Funky

#21
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
Just out of curiosity..why not just store all the puzzle/s variables you need on objects that can be imported/exported as erfs or saved in the NWN database as well? Then you could store all the puzzles and puzzle info for one area on each object. You could put different puzzle "levels" on these objects..etc.

I'm pretty sure that reading(fetching) the information from in game objects is faster than any database method. And retrieving one object from the NWN database and then fetching and writing info from and to it is also fairly fast(err..well not as fast as NWNX+ODBC).

The first time through a mod the mod uses puzzles with id==1, then on a second playthrough puzzles with id==2 etc.

In this scenario for example you could simply put a variable on the player once he completed the first run through. Then on his second run through the area the puzzles would change based on the variable that is stored on the player.

Perhaps I am just not grasping what type of scenario would require you to go to the lengths that you are going to to go about this.

Modifié par GhostOfGod, 08 novembre 2010 - 07:15 .


#22
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Local reads are faster, by necessity, than NWNX-ODBC, since it hooks Set/GetLocalString to do its stuff. :P

There are other things to consider besides speed and hot-dropping, like ease of data entry/access/editing, for example. I for one wouldn't want to use the NWN DB for ANYTHING for that reason, though Knat's helps considerably. Simply storing puzzling data in a script and making use of override to update is probably the best solution for everything except tracking puzzle completion across playthroughs, which you should probably use the NWN DB for, despite the suck factor.

Funky

Modifié par FunkySwerve, 08 novembre 2010 - 07:45 .


#23
Calgacus

Calgacus
  • Members
  • 122 messages
GOG, you suggest I store a puzzle index var on the player, but what if they use a different character in a SP mod? Will that var it still be accessible?

Funky, the hot swap is important to me even if its not of truly great practical use, I just obsess over issues of replayablility and like the idea of distributing bundles of puzzles to players and even other modders for their use.
A puzzle needs to have:
1) an ID
2) a Name
3) an introduction - text maybe 1000 characters or so long
4) up to a dozen or so clues - each maybe up to a couple hundred characters long
5) a solution, not sure yet how I want to present it to scripting as it has to be interpreted by a script as it is at least a little more meaningfully than sticking some strings into a convo or book; eg if the solution is "Fire first, Cold second, Acid third" then some object needs to be hit by those effects in that order to be destroyed. The solution exists first in the puzzle, not hardcoded anywhere in the module.

I like the idea of keeping these puzzles together in an easily updated, easily read file and 2DA is starting to seem the easiest to copy-n-paste the puzzle info into from the web page where the finished puzzle is first made, even easier than the foxpro files now that I understand their guts.

Any more thoughts, keep em coming, they are helping!!

To do it in a script I'd have to make up variable names or maybe a linked list of structs if nwscript supports such things, how else would I store a bunch of puzzles in a script?

Modifié par Calgacus, 09 novembre 2010 - 02:27 .


#24
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Nwscript does NOT support nested structs (or pointers), but you don't need them. You can store basically anything you want in script - 2das are actually more limited, because scripts are much better suited to storing relational data. That's not a particular obstacle to 2da use, since they're always used in tandem with scripts - I'm simply pointing out the additional flexibility. Actually, if you want a prime example, look at the Legendary Level scripts - they were all done originally with all the feat, skill, etc, data coded into switches in script, because they were done before the improved 2da caching was implemented. When it was, I rewrote them to do 2da reads. I'll see if I can dig up an example.



Funky

#25
FunkySwerve

FunkySwerve
  • Members
  • 1 308 messages
Here's a chunk from a massive switch that replicates a column from a dozen different 2das:
//--------------------------------DECLARATIONS--------------------------------//

// This function returns the level at which the specified class gets the specified feat.
// If the feat is epic only, it will return -1 in most cases, though it will also return
// 21 on occasion, as with Epic Spells, which actually require 21 levels of the class rather
// than just epic character status. It replicates EXACTLY the cls_feat 2da for each of the
// respective classes, though the order of feats has been shuffled to accomodate the switch
// statements, and a few duplicate cases in the 2das were removed. If the feat input is not
// a class feat (and therefore not found in the 2da), this function will return -2. If the
// class input is not a valid class int, the function returns -3. Although Harper Scout could
// not possibly be a character's control class for legendary levels, it is included at the end
// of the switch so that the function can be of broader use to the community. Several feats were
// added as class feats for Palemasters, including Spell Focuses and Greater Spell Focuses
// (since they get Epic Focuses). They are marked in the function. The class switches are
// in aphabetical order by 2da file (which is slightly different than class name).
int GetclassLevelReqForFeat(int nFeat, int nclass, object oPC);
// This function returns TRUE if the feat specified is a general feat, avalable to all classes.
int GetIsGeneralFeat(int nFeat);

//----------------------------------FUNCTIONS---------------------------------//

int GetclassLevelReqForFeat(int nFeat, int nclass, object oPC)
{
int nInt = -2, nQuasi;

switch(nclass)
{
    case  class_TYPE_ARCANE_ARCHER:
        switch(nFeat/250)
        {
            case 0:
                switch(nFeat/25)
                {
                case 0: //0-24
                    switch(nFeat){
case  1 : nInt =  -1 ; break;
case  3 : nInt =  1 ; break;
case  4 : nInt =  1 ; break;
default : nInt =  -2 ; break;}; break;
                case 1: //25-49
                    switch(nFeat){
case  32 : nInt =  1 ; break;
case  41 : nInt =  -1 ; break;
case  44 : nInt =  -1 ; break;
case  45 : nInt =  1 ; break;
case  46 : nInt =  1 ; break;
default : nInt =  -2 ; break;}; break;
                case 2: //50-74
                    switch(nFeat){

The full thing is 6596 lines long, but it runs quite quickly, because of the switch structure. Here's the same thing, redone to use two 2da-based functions:

int GetIsFeatExceptionFrom2da(int nFeat, int nclass, object oPC) {

    int nRace = GetRacialType(oPC);
    switch(nFeat) {
        case 424: if (GetLocalInt(oPC, "Quasiclass") == QUASIclass_DWARVEN_WARCHANTER) return -1; break;//lingering song
        case 870: if (GetLocalInt(oPC, "Quasiclass") == QUASIclass_DWARVEN_WARCHANTER) return -1; break;//lasting inspiration
        //spell feats below:
        case 35:case 166:case 167:case 168:case 169:case 170:case 171:case 172: //spell foci
        case 36:case 401:case 618:  if (GetLocalInt(oPC, "Quasiclass")) return -1; break;//spell penetration feats
        //general feats below:
        case 290: //Creature Weapon Spec
        case 694: if (GetLevelByclass(class_TYPE_DRUID, oPC) > 0) return -1; break; //Epic CW Spec
        case 311:case 313:case 314:case 315:case 316:case 317:case 321:case 322:case 325: if (GetKnowsFeat(FEAT_EPIC_PLANAR_TURNING, oPC)) return -1; break; //cleric domains
        case HGFEAT_LEG_SKILL_AFFINITY_LISTEN:
        case HGFEAT_LEG_SKILL_AFFINITY_SPOT: if (GetRacialType(oPC) == RACIAL_TYPE_HALFELF) return -1; break;
        case HGFEAT_LEG_SKILL_AFFINITY_TAUNT: if (GetLocalInt(oPC, "SubraceID") == 37) return -1; break;
    }
    return -2;
}

int GetclassLevelReqForFeat_2da(int nFeat, int nclass, object oPC) {

    string s2da = Getclass2daName(nclass);
    string sVal = "Nothing";
    int nCount = 0, nVal, nRet;
    while (sVal != "") {
        sVal = Get2DAString(s2da, "FeatIndex", nCount);
        nVal = StringToInt(sVal);
        if (nVal == nFeat) {
            nRet = StringToInt(Get2DAString(s2da, "GrantedOnLevel", nCount));
            return nRet;
        }
        nCount++;
    }
    nRet = GetIsFeatExceptionFrom2da(nFeat, nclass, oPC);
    return nRet;
}

In this case, after the improvement in 2da caching, the 2da approach is by far the better one. Despite that, that massive function didn't take all that long to create, thanks to excel scripting. In your case, since you're able to determine structure up front, you can structure your code so that either option will work well, and you won't wind up with huge switch functions unless you REALLY have a lot of data to enter - it just doesn't sound like you do, far from it.

If I were coding functions for your puzzles, I would probably do something like:

string GetPuzzleName(int nPuzzle);
string GetIntroduction(int nPuzzle);
string GetPuzzleClue(int nPuzzle, int nClueNumber);
(solution)

You're going to wind up with the same functions either way; the only thing to decide is whether they're going to contain switches or 2da reads (or database, but I really don't recommend that). There are, of course, other options, like variable pseudohashes - look at OldManWhistler's spell system, for example (no good for strings like yours) - or real hashsets (only if using NWNX, not a good option for SP).

In summary, it sounds like you need to figure out EXACTLY what your puzzles will look like. My guess is you'll want a scripted solution, which is more flexible. You'll be able to hotdrop more easily as well. 2das, by contrast, will shine if your relational data is minimalistic and easy organization is more important.

Funky