logo logo

 Back to main page

The NWNX Community Forum

 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 
 
Question about NWNX
Goto page 1, 2  Next
 
Post new topic   Reply to topic    nwnx.org Forum Index -> Scripts and Modules
View previous topic :: View next topic  
Author Message
etched_in_blood



Joined: 28 Apr 2009
Posts: 13

PostPosted: Tue Apr 28, 2009 9:49    Post subject: Question about NWNX Reply with quote

By many, I have been called a NWN scripting god. I'm not very well known to the community as a whole, because I tend not to make most of my scripts available, but I have accomplished some fairly amazing systems in my time. I say this not to brag, but to make a point. I have come across something that I simply can not do in NWN scripting.

I am writing my own spawning system that takes into account huge amounts of random chance along with terrain type, and a CR range of a particular area. All in all, the script that fires off is only about 100 lines long. But, I wrote a custom function for it which performs all the randomization, finally ending with which creature to spawn, and then it spawns it. This function is just barely under 19 thousand lines long. Needless to say, even though it is comprised mostly of simple if functions, if it fires in rapid succession, it crashes the game every single time.

Despite my high level of ability with NWN scripting, I have zero experience with NWNX. Another scripter told me that I could actually make my own plugin that would perform all the heavy lifting for that function, and then simply shoot back the res ref of which creature to spawn, and that this would cause me to be able to actually use my system without crashing my server.

Since I have absolutely no idea where I would even start with this, and because I am trying to hold to some sort of schedule, as the module I am creating will be a persistent world, which has players anxiously awaiting a date for Beta testing, I was wondering if you might be able to tell me if NWNx will perform this function or not. I can not afford to take the time to learn how to do it, and then actually create my own plugin, only to turn around and find that it doesn't work, and have to go back, and simply use either NWN default spawning, or import NESS and use that.

To give you a better idea of what the function does, it simply checks for a local int on the area, and then randomizes, and creates the object. I can create the object in the home script if I can get my function to return a string, and I can even supply the function with the information from the local int. The 19 thousand lines basically consist of a lot of randomization and organization. So my question is, should I be looking at NWNX to solve this problem for me, or should I simply scrap the project, and throw a different spawning system in there?

Thanks for your time and help.
Back to top
View user's profile Send private message
virusman



Joined: 30 Jan 2005
Posts: 1020
Location: Russia

PostPosted: Tue Apr 28, 2009 11:18    Post subject: Reply with quote

If you can use NESS, use NESS.
You can write a plugin to optimize performance, but you wouldn't be able to access the in-game objects from the plugin without low-level memory editing. For the most part NWNX is a bridge between NWScript and low-level things written in C++ as plugins.
If you're running on Linux, you can try NWNX Ruby as a replacement for NWScript (it imports all NWScript functions).
Back to top
View user's profile Send private message Visit poster's website Yahoo Messenger
FunkySwerve



Joined: 02 Jun 2005
Posts: 377

PostPosted: Tue Apr 28, 2009 17:32    Post subject: Reply with quote

Just use nwnx_odbc, to give you access to a MySQL database. Enter the creatures, their CRs, their environs, and whatever else you want to factor in. A few queries will provide all the randomness you need and whatever conditions you want to set.

Funky
Back to top
View user's profile Send private message
etched_in_blood



Joined: 28 Apr 2009
Posts: 13

PostPosted: Tue Apr 28, 2009 22:30    Post subject: Reply with quote

Well like I said, all I truly need the bulk of the script to do is organize the creatures by CR and terrain type, then have it randomize from those. If it can keep track of the creatures by those two conditions, and it can randomize between them, I can do everything else from the home script within the engine without crashing it.

Might anyone be able to perhaps assist me in getting this started? Because I have no idea what I'm doing with this.
Back to top
View user's profile Send private message
FunkySwerve



Joined: 02 Jun 2005
Posts: 377

PostPosted: Wed Apr 29, 2009 0:06    Post subject: Reply with quote

Be that as it may, handling of mass amounts of data is what mysql excels at. If you have any database experience at all, it's almost certainly the way to go. Or, if you prefer, use 2das - they're much more useful with the new caching improvement. Here's a post on the bioboards by someone with a similar situation, if less extreme:
http://nwn.bioware.com/forums/viewtopic.html?topic=640032&forum=47&highlight=nesting%20limit

In any event, at 19k lines, I can say with great certitude that, however you're doing what you're doing, you're doing it wrong. If, despite that, you're set on doing it the way you are now, no plugin is going to offer a magic panacea for what ails your script. There mere size shouldn't account for the crashes - scripts will TMI before they crash or hang a server. The legendary level scripts weigh in at over 15k lines of more convoluted code than simple if/else switches (to say nothing of the z-dilogue scripts they intertwine with), though I have as twin excuses for that that they were written for public release, and predate the improved 2da caching - today I could write them in about 1/20th the linecount. You almost definitely have some other problem. Though I cringe at the notion of asking you to post the script, it's going to be hard to diagnose without looking at it.

Funky
Back to top
View user's profile Send private message
etched_in_blood



Joined: 28 Apr 2009
Posts: 13

PostPosted: Wed Apr 29, 2009 0:55    Post subject: Reply with quote

The reason that the script is so huge is because it takes into account five different terrain types, and CR's 1 - 100. This, combined with the actual number of spawns in each case comes down to it being a not happy day. However, I can say that it isn't the script itself causing the server to crash, as I can actually execute it up to seven times consecutively before it causes the server to hang. So for example, if I only call it to execute 7 times, it won't crash the server. There's a small lag spike as it spawns the creatures, and boom, it's finished. But if I do it more times than that, it hangs and crashes.

You have to figure, if I have 6 different creatures for each terrain type and CR combination from 1 - 100, I need to be able to select through over 3000 creatures. 5 x 6 x 100 = 3000. And then I also have terrain nonspecific spawns as well. Instead of posting the entire function, I'll just post a portion of it, so you can see what I've done.

void SpawnRandomCR(object oArea, int iCreatureCR)
{
string sWayTag = GetLocalString(oArea,"WayTag");
int sWayTagNum = GetLocalInt(oArea,"SpawnTagNum");
sWayTagNum = sWayTagNum +1;
string sResRefToSpawn;
object sWaypoint55;
location lRandSpawn;
// CR 1 //
if(iCreatureCR >= 1 && iCreatureCR <=50)
{
if(iCreatureCR >= 1 && iCreatureCR <= 25)
{
if(iCreatureCR == 1)
{
if(GetLocalInt(oArea,"TerrainType") == 1)
{
int nRand = Random(6)+1;
switch (nRand)
{
case 1:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 2:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 3:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 4:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 5:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 6:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;
}

}
if(GetLocalInt(oArea,"TerrainType") == 2)
{
int nRand = Random(6)+1;
switch (nRand)
{
case 1:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 2:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 3:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 4:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 5:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 6:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;
}

}
if(GetLocalInt(oArea,"TerrainType") == 3)
{
int nRand = Random(6)+1;
switch (nRand)
{
case 1:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"zep_zebra_002",lRandSpawn);
break; return;

case 2:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 3:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 4:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 5:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 6:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;
}

}
if(GetLocalInt(oArea,"TerrainType") == 4)
{
int nRand = Random(6)+1;
switch (nRand)
{
case 1:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 2:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 3:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 4:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 5:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 6:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;
}

}
if(GetLocalInt(oArea,"TerrainType") == 5)
{
int nRand = Random(6)+1;
switch (nRand)
{
case 1:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 2:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 3:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 4:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 5:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;

case 6:
lRandSpawn = GetRandomLocation(oArea);
CreateObject(OBJECT_TYPE_CREATURE,"testspawn55",lRandSpawn);
break; return;
}

}
}
Back to top
View user's profile Send private message
etched_in_blood



Joined: 28 Apr 2009
Posts: 13

PostPosted: Wed Apr 29, 2009 5:57    Post subject: Reply with quote

Annnnnnd I fixed it. I added in some more IF statements to help the function narrow down faster, and now it operates almost completely lag free.
Back to top
View user's profile Send private message
FunkySwerve



Joined: 02 Jun 2005
Posts: 377

PostPosted: Wed Apr 29, 2009 9:31    Post subject: Reply with quote

Glad to hear you fixed it. Just realize that you could write the same script using a MySQL database in about 20 lines, with WAAAAAAAY lower load.

Funky
Back to top
View user's profile Send private message
Kosmous



Joined: 10 Jan 2005
Posts: 44

PostPosted: Wed Apr 29, 2009 10:55    Post subject: Reply with quote

I agree with FunkySwerve. Use MySQL, a carefully designed query and you can probably shrink this to far less than even 20 lines (not that its a contest or anything). As great as your scripting skills are, you'll be vastly more efficient if you have even a rudimentary knowledge of SQL.
Back to top
View user's profile Send private message
Fireboar



Joined: 17 Feb 2008
Posts: 323

PostPosted: Wed Apr 29, 2009 20:41    Post subject: Reply with quote

Doesn't even need a particularly complicated query - the obvious solution would work just fine. 9 lines, 2 semicolons people!

Code:
void SpawnRandomCR(object oArea, int iCreatureCR)
{
  SQLExecDirect("SELECT cr_resref FROM creatures WHERE cr_challenge='" + IntToString(iCreatureCR) +
   "' AND cr_terrain='" + IntToString(GetLocalInt(oArea, "TerrainType")) + "' ORDER BY RAND() LIMIT 1");
  if (SQLFetch())
  {
    CreateObject(OBJECT_TYPE_CREATURE, SQLGetData(1), GetRandomLocation(oArea));
  }
}


By the way... what are you doing with sWayTagNum, sWaypoint55 and sWayTag? They seem to be completely redundant.


EDIT: Another by the way...

Just trying to be helpful here really - what you wrote was pretty inefficient anyway. Example: You're using GetLocalInt(oArea,"TerrainType") an awful lot, and each time it has to do some extra processing. Much better would be to do the function once near the beginning, store it in a variable and then compare the variable. Same goes for your nRand variable - do it nearer the beginning then you wouldn't have to keep repeating that line of code. Also, why is nRand being set to Random(6) + 1? Why not just Random(6) and compare 0 through to 5? One less arithmetic operation, it won't have a noticeable effect but it is good form to eliminate unnecessary arithmetic ops.
Back to top
View user's profile Send private message
FunkySwerve



Joined: 02 Jun 2005
Posts: 377

PostPosted: Wed Apr 29, 2009 23:25    Post subject: Reply with quote

CONTEST!!!!!!!!!!!11

I was including the void main, no fair!

I had in mind something like this, which would allow different weightings. It's taken from our new augment scriptset, and pulls up a random augment according to input criteria, adjusting for weighting:

Code:
struct Augment GetRandomAugment(string sConditions) {
    string sSQL;
    struct Augment ret;

    //pull up all augments that are of acceptable IP and item type, totalling weight
    sSQL = "SELECT @ra_random := FLOOR(RAND() * SUM(ra_weight)) " +
           "FROM rip_augments WHERE " + sConditions;
    SQLExecDirect(sSQL);

    //select one of those augments by weight
    sSQL = "SELECT ra_id,ra_name,ra_block_aug,ra_block_item,ra_notes,ra_affix,ra_appearance FROM rip_augments WHERE (" + sConditions +
           ") AND (@ra_random := @ra_random - ra_weight) < 0 LIMIT 1";
    SQLExecDirect(sSQL);

    while (SQLFetch() != SQL_ERROR) {
        ret.id          = StringToInt(SQLGetData(1));
        ret.name        = SQLGetData(2);
        ret.blockaug    = SQLGetData(3);
        ret.allowitem   = StringToInt(SQLGetData(4));
        ret.notes       = SQLGetData(5);
        ret.affix       = StringToInt(SQLGetData(6));
        ret.appear      = StringToInt(SQLGetData(7));
    }
    return ret;
}

Where sCond is something like this:

Code:
string sCond = "ra_affix < 3 AND FLOOR(ra_rarity/1000) = " + IntToString(nRarity); //only allow augmenter items with single affixes and R rarity or lower



Funky
Back to top
View user's profile Send private message
etched_in_blood



Joined: 28 Apr 2009
Posts: 13

PostPosted: Thu Apr 30, 2009 2:02    Post subject: Reply with quote

I will definitely admit that my knowledge of SQL is fairly well nonexistent. As a matter of fact, even with it being simplified down in this nature, I've found that I can only throw out 20 spawns without hanging up. Which is fine for single player, but for multiplayer, which this module is supposed to be, it may actually become a problem fairly quickly if I'm not incredibly careful on how I implement it.

If I had the first clue as to how I could effectively break this down into the simplified versions that you all are throwing out there, I'd do it in a heartbeat. The code I posted actually only covers CR 1 creatures. Imagine that length of code repeated 100 times to cover CR's 1 - 100. I doubt I"ll ever have a CR 100 creature, but I like to cover my bases when writing out systems like this.

As for the reason that I calculate nRand every instance individually is because it will only ever fire that one time in a single execution anyways, and it also won't be the same every time. As you notice in the CR 1 section of code I posted, not all the entries are filled in with actual creatures, there's a single "filler" creature in there to hold placemarks while I was writing the script, so when I'm finished imputing the data, there will actually be different nRand numbers there that I'm entering in under each section, as some sections will have more or less creatures in it than others. There are a few things there in the beginning of the script that are defined but are not used. Those are leftovers that I haven't cleaned up yet from the original way in which I tried to work it.

I don't know that much about running SQL in a server like this, if I would have to buy the program, how I would run the scripts, and so on. If you look carefully, the code I posted isn't actually a script, it's a function that I wrote that fires from another script. If you guys think that this would seriously aid me in my problem, I would gladly post the home script, and even the entire function so that it could get looked at, and you guys could give me some feedback perhaps, and MAYBE with a little time and effort I could bring this up to function.
Back to top
View user's profile Send private message
FunkySwerve



Joined: 02 Jun 2005
Posts: 377

PostPosted: Thu Apr 30, 2009 3:16    Post subject: Reply with quote

The first thing you need to do is install MySQL. It's free. The install was a little tricky for me, took a couple hours, but I was a complete scripting novice with no database experience beyond bioware's.

Once that's complete, you'll want to build one or more custom tables to enter your data into. To do that, you'll need to know how best to set them up, and to help with that, we'll need a succint description of every factor you want to take into account - terrain, climate, cr, whatever else you can think of.

Then, we'll be able to help you set up your database tables, and learn how to input data into them, and to write the spaw script itself (you'll probably want to pull up all the creatures you're spawning in one database query, and just spawn them, will be hugely more efficient).

Anyway, start by installing NWNX (go to the home page, the downloads section, and install the core platform and odbc).

Then, install MySQL. You can find one version of it here for free download:
http://dev.mysql.com/downloads/mysql/5.1.html#downloads


Then, post that list of all the factors involved in your spawn script, and we'll go from there.

Funky
Back to top
View user's profile Send private message
etched_in_blood



Joined: 28 Apr 2009
Posts: 13

PostPosted: Thu Apr 30, 2009 6:20    Post subject: Reply with quote

Alright. I installed MySQL. Wasn't hard to install. I selected the following settings during Setup:

"Developer Machine"
"Multifunction Database"
"Online Transaction Processing"
NO to "Enable TCP/IP Networking"
YES to "Enable Strict Mode"
"Standard Character Set"

And of course, I set up a root password. Alright. NOW....

I am totally lost. lol. I can see that there is apparently no GUI, just a command line, and I guess I could be ok with that if I had a clue as to what I was doing. SO, here's the info you wanted.

The only two factors taken into effect for spawning in the creatures is terrain type and creatures CR.

I already have NWNx installed, though I don't have the MySQL plugin set up, but I'll do that right now. Shouldn't be too hard.
Back to top
View user's profile Send private message
FunkySwerve



Joined: 02 Jun 2005
Posts: 377

PostPosted: Thu Apr 30, 2009 6:27    Post subject: Reply with quote

Well, there have to be more variables in play than that. You'll be calling up creature resrefs, correct? Anything else you want to associate with the creatures? Day/night encounters? Number or range per resref to spawn? Etc. Spend some time thinking about it. Mysql makes combining all these factors child's play. As for commands, I'll give you the link to the handbook, though much of it you'll learn by reading code. You might start by looking at the SQL statements in aps_include. Once you understand those, and how they relate to the default database, you'll be well on your way to using MySQL towards whatever ends you want. I'll be happy to write the SQL code in question for this script, but I urge you to consider any other factors you might want down the line.

Funky
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    nwnx.org Forum Index -> Scripts and Modules All times are GMT + 2 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group