Author Topic: Randomly Generated Placeables  (Read 589 times)

Legacy_SKIPPNUTTZ

  • Full Member
  • ***
  • Posts: 148
  • Karma: +0/-0
Randomly Generated Placeables
« on: November 21, 2011, 09:04:54 am »


               Here is something I started writing earlier tonight. What I have is a pretty empty forest tileset, with cliffs as the perimeter. The cliffs have around 24 doors that will link to the next map.

What I am trying to accomplish, is creating random trees from the bioware blueprints, since the CEP naming conventions aren't nearly as organized. To avoid the engine creating objects at completely random vectors, subsequently in undesired locations (in front of doors), I figured I would just place around 100+ waypoints on the map with random facing directions and positions.

Keep in mind I am a novice scripter, so the code below may be very inefficient and not work at all '<img'>

What I would like to know is, what would be the most efficient, laggfree way to accomplish this?
And the estimated execute time to pass through all 100 placed objects.

So if I draw 100 waypoints, named "wp_skip_bf1_001" through "wp_skip_bf1_100", this script should have a 50% chance of creating a random Large sized tree at that waypoint. IIRC there is about 20 trees to pick from, if I only used "x3_plc_treel".

I appreciate any help with accomplishing this effectively '<img'> Might even help give some other builders ideas for their maps.


----------

void main()
{

    //Random Tree, last 3 characters will define the random blueprint
    string sTree = GetStringRight("x3_plc_treel", 3);
    //Random Waypoint, last 3 characters will define the random waypoint
    string sWP = GetStringRight("wp_skip_bf1_", 3);
    object oWP = GetObjectByTag(sWP);
    //Random Location for trees based off waypoints drawn on map
    location lWP = GetLocation(oWP);
    //Max amount of trees to create per map
    int nMAX = 50;
    //Count of how many trees have been created, incrementing to  nMAX...
    int nCount = 0;
    //Chance to spawn a tree
    int nChance = 50;

    object oTarget = GetFirstObjectInArea(OBJECT_SELF);


       //Perform if its one of the random waypoints
       if(GetObjectType(oTarget) == OBJECT_TYPE_WAYPOINT && oTarget == oWP)
       {
           //nChance% to spawn a tree per random waypoint
           if(d100() >= nChance)
           {
                //Perform if there hasnt been nMAX trees spawned
                if(nCount < nMAX )
                {
                //Create random tree at the waypoint
                CreateObject(OBJECT_TYPE_PLACEABLE, sTree, lWP);
                //Increment the count by 1
                nCount++;
                }
           }
       }

    //Get the next waypoint
    oTarget = GetNextObjectInArea(OBJECT_SELF);

}

               
               

               


                     Modifié par SKIPPNUTTZ, 21 novembre 2011 - 09:08 .
                     
                  


            

Legacy_Xardex

  • Sr. Member
  • ****
  • Posts: 414
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #1 on: November 21, 2011, 11:40:23 am »


               Since you look for specifically tagged waypoints there is no need to check for ObjectType. Amusingly enough you forgot the loop from your script, if that's not intentional. '<img'>

As for performance... Having too many placeables in one area is of course a recipe for lag, but I won't even guess how many placeables "too many" is. One thing you might want to consider is avoiding placing the waypoints near tile edges, since placeables near tile edges sometimes mess up pathfinding. (or so I have heard, never experienced this myself)

Another way of doing this would be to create triggers where inside the trees would be randomly created, instead of tossing around hundreds of waypoints.
               
               

               
            

Legacy_Kato -

  • Hero Member
  • *****
  • Posts: 747
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #2 on: November 21, 2011, 04:22:25 pm »


               That would do it assuming that you have 100 waypoints tagged wp_skip_bf1_001 and so on, but although this is fast the lag cannot be avoided, and for a simple reason: The placeables you're creating are very large. Try this as a proof: Place a single large tree like the ones you wish to create anywhere in your testing area and place your starting point far enough so that you don't see the tree upon logging, then move toward the tree. When the tree appears in your line of sight, ouch! The bigger the tree, the bigger the impact, even if the tree is static, and even if there is only one. A lot of small trees would not even create as much lag(up to a limit, of course).


const string TREE_REF = "x3_plc_treel"; // the trees base ref
const string WP_REF = "wp_skip_bf1_"; // the waypoints base ref
const int MAX_TREES = 50; // the max number of spawns

void main()
{
    int nWP = 0, nSpawned = 0;
    while(++nWP <= 100 && nSpawned < MAX_TREES) // replace 100 with the number of waypoints if different
    {       
        if(Random(2)) 
        {
            string sWP = IntToString(nWP);
            string sTree = IntToString(Random(19)); // 19 existing large tree refs from 0 to 18 
            switch(GetStringLength(sWP))
           {
               case 1: sWP = "00" + sWP; break;
               case 2: sWP = "0" + sWP; break;
              default: break;
           }
           switch(GetStringLength(sTree))
           {
              case 1: sTree = "00" + sTree; break;
              case 2: sTree = "0" + sTree; break;
              default: break;
           }
          object oWP = GetObjectByTag(WP_REF + sWP);
          CreateObject(OBJECT_TYPE_PLACEABLE, TREE_REF + sTree, GetLocation(oWP));
          nSpawned++; 
        }
    }
}


Kato
               
               

               


                     Modifié par Kato_Yang, 21 novembre 2011 - 04:59 .
                     
                  


            

Legacy_SKIPPNUTTZ

  • Full Member
  • ***
  • Posts: 148
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #3 on: November 21, 2011, 08:57:58 pm »


               Thanks a lot for your input guys. And AV3 says hello, Kato.

Now I have 2 maps that will use this system, and ideally I would not have to make a completely different script just to change:
const string WP_REF = "wp_skip_bf1_";

to

const string WP_REF = "wp_skip_bf2_";

-----

I guess I should use:
const string WP_REF1 = "wp_skip_bf1_";
const string WP_REF2= "wp_skip_bf2_";

object oWP;
if(area = map1)//forgot how to check this
{oWP =  GetObjectByTag(WP_REF1 + sWP);}
else if (area = map2)
{oWP =  GetObjectByTag(WP_REF2 + sWP);}

-----

I guess if I really wanted to make it even more random, i could replace "x3_plc_treel" with "x3_plc_tree", that would randomize the actual size of the trees too. I would have to check through all the blueprints first to make sure there isn't any that I don't want to create, like snow covered branches etc. Ideally I would have a lot more than 100 wps to really make it more randomized, but thats time consuming '<img'>

This is obviously an onenter script, just want to make sure the routine will be fast enough so that the trees will all be placed before the loading screen fades out.

As for performance... Having too many placeables in one area is of course a recipe for lag, but I won't even guess how many placeables "too many" is.


Well anything will be a lot less laggy than the system the module admin put in place there. It uses some weird random dungeon creator, that creates a maze of "cages", literally creating over 1000 placeables every time someone zones into those maps. As well as having to delete all of the 1000+ cages on the previous map. It brings the server to a halt, and causes hardware lag on individual clients having that many placeables it seems (at least for my crappy computer '<img'>). Experiment gone bad.

I'm guessing for the OnExitArea event I should just use a similar object loop like the one in my original post to delete all objects with the tag "x3_plc_tree" so that it will be a fresh map when players return to it.

-----

Now what would be really cool, is a way to manipulate random tiles/terrain for maps OnEnter.... haven't found a way to do this yet:p
               
               

               


                     Modifié par SKIPPNUTTZ, 21 novembre 2011 - 10:02 .
                     
                  


            

Legacy_Kato -

  • Hero Member
  • *****
  • Posts: 747
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #4 on: November 21, 2011, 10:07:50 pm »


               Since you call it from the OnEnter it would rather look like this, checking the presence of other PCs in the area before doing anything. It also randomizes on tree size, yet I have excluded none.

const string TREE_REF = "x3_plc_tree"; // the trees base ref
const string WP_REF = "wp_skip_bf1_"; // the waypoints base ref
const int MAX_TREES = 50; // the max number of spawns

void main()
{
     object oEntering = GetEnteringObject();
     if(!GetIsPC(oEntering) || GetIsDM(oEntering)) return;
     int nNumPCs = GetLocalInt(OBJECT_SELF, "NumPCs")+1;
     SetLocalInt(OBJECT_SELF, "NumPCs", nNumPCs);
     if(nNumPCs > 1) return;
     int nWP = 0, nSpawned = 0;
     while(++nWP <= 100 && nSpawned < MAX_TREES) // replace 100 with the number of waypoints if different
     {
         if(Random(2))
         {
            string sWP = IntToString(nWP);
            string sTree = IntToString(Random(19)); // 19 existing tree refs from 0 to 18 for each size
            int nRandSize = Random(3)+1;
            string sSize = "l";
            if(nRandSize == 1) sSize = "s";
            else if(nRandSize == 2) sSize = "m";
            switch(GetStringLength(sWP))
            { 
                case 1: sWP = "00" + sWP; break;
                case 2: sWP = "0" + sWP; break;
               default: break;
            }
            switch(GetStringLength(sTree))
            {
               case 1: sTree = "00" + sTree; break;
               case 2: sTree = "0" + sTree; break;
               default: break;
            }
            object oWP = GetObjectByTag(WP_REF + sWP);
            CreateObject(OBJECT_TYPE_PLACEABLE, TREE_REF + sSize + sTree, GetLocation(oWP));
            nSpawned++;
         }
    }
}


And then you would need to put this in the OnExit event handler of your area(s):

void main()
{
   object oExiting = GetExitingObject();
   if(!GetIsPC(oExiting) || GetIsDM(oExiting)) return;
   int nNumPCs = GetLocalInt(OBJECT_SELF, "NumPCs")-1;
   if(nNumPCs < 1) 
   {
      object oTree = GetFirstObjectInArea(OBJECT_SELF);
      while(oTree != OBJECT_INVALID) 
      {
          if(FindSubString(GetResRef(oTree), "x3_plc_tree") > -1) DestroyObject(oTree);
          oTree = GetNextObjectInArea(OBJECT_SELF);
      }
      DeleteLocalInt(OBJECT_SELF, "NumPCs");
   }
   else SetLocalInt(OBJECT_SELF, "NumPCs", nNumPCs);
}
               
               

               


                     Modifié par Kato_Yang, 21 novembre 2011 - 10:13 .
                     
                  


            

Legacy_SKIPPNUTTZ

  • Full Member
  • ***
  • Posts: 148
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #5 on: November 21, 2011, 10:22:02 pm »


               yep you are right, i was going to do that last. Already have a system in place similar to that to reset encounter triggers if there are no players on the last map. However it looks like you missed my last edit. I have two areas total that will use this script. So if i change const string WP_REF = "wp_skip_bf1_"
to
const string WP_REF1 = "wp_skip_bf1_";
const string WP_REF2 = "wp_skip_bf2_";

and then use this later on in the script:

object oWP;
if( GetTag(OBJECT_SELF) == "skip_bforest1" )
{oWP = GetObjectByTag(WP_REF1 + sWP);}

else if( GetTag(OBJECT_SELF) == "skip_bforest2" )
{oWP = GetObjectByTag(WP_REF2 + sWP);}

That should use different sets of waypoint strings depending on the area right? just want to limit the amount of scripts that our module has to have. AV3 is ginormous for only 1 server.

Thanks again Kato :-)

Final Script looks like this.
-----

const string TREE_REF = "x3_plc_tree"; // the trees base ref
const string WP_REF1 = "wp_skip_bf1_"; // the waypoints base ref for map 1
const string WP_REF2 = "wp_skip_bf2_"; // the waypoints base ref for map 2
const int MAX_TREES = 50; // the max number of spawns

void main()
{
     object oEntering = GetEnteringObject();
     if(!GetIsPC(oEntering) || GetIsDM(oEntering)) return;
     int nNumPCs = GetLocalInt(OBJECT_SELF, "NumPCs")+1;
     SetLocalInt(OBJECT_SELF, "NumPCs", nNumPCs);
     if(nNumPCs > 1) return;
     int nWP = 0, nSpawned = 0;
     while(++nWP <= 100 && nSpawned < MAX_TREES) // replace 100 with the number of waypoints if different
     {
         if(Random(2))
         {
            string sWP = IntToString(nWP);
            string sTree = IntToString(Random(19)); // 19 existing tree refs from 0 to 18 for each size
            int nRandSize = Random(3)+1;
            string sSize = "l";
            if(nRandSize == 1) sSize = "s";
            else if(nRandSize == 2) sSize = "m";
            switch(GetStringLength(sWP))
            {
                case 1: sWP = "00" + sWP; break;
                case 2: sWP = "0" + sWP; break;
               default: break;
            }
            switch(GetStringLength(sTree))
            {
               case 1: sTree = "00" + sTree; break;
               case 2: sTree = "0" + sTree; break;
               default: break;
            }

          object oWP;
          if( GetTag(OBJECT_SELF) == "skip_bforest1" )
          {oWP = GetObjectByTag(WP_REF1 + sWP);}

          else if( GetTag(OBJECT_SELF) == "skip_bforest2" )
          {oWP = GetObjectByTag(WP_REF2 + sWP);}
            CreateObject(OBJECT_TYPE_PLACEABLE, TREE_REF + sSize + sTree, GetLocation(oWP));
            nSpawned++;
         }
    }
}


This is the current OnExit script to handle resetting the encounters
-----
void main()
{
  object oPC = GetExitingObject();
  object oArea = OBJECT_SELF;
  int iSomeoneThere = FALSE;
  object oInArea = GetFirstObjectInArea(oArea);

  //Only reset the encounters if the area has no players in it
  while ( oInArea != OBJECT_INVALID)
  {
    if ( GetIsPC(oInArea) & oInArea != oPC)
    {
      iSomeoneThere = TRUE;
    }
    oInArea = GetNextObjectInArea(oArea);
  }

  if (!iSomeoneThere)
  {

    string sAreaNo = GetStringRight(GetTag(oArea), 2);
    int i;
    for(i = 1; i < 6; i++)
    {
    string sSpwnNo = "0" + IntToString(i);
    object oSpawn = GetObjectByTag( "skip_bfspawn" + sAreaNo + sSpwnNo );
    SetLocalInt(oSpawn, "iCanTrigger", 1);
    }
  }

}
               
               

               


                     Modifié par SKIPPNUTTZ, 21 novembre 2011 - 10:33 .
                     
                  


            

Legacy_Kato -

  • Hero Member
  • *****
  • Posts: 747
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #6 on: November 21, 2011, 10:45:23 pm »


               In that case, you can simply call GetNearestObjectByTag() with oEntering as the source location, so that only the waypoints of the current area are searched. Thus you don't need to create new waypoint sets, they are useable everywhere.

const string TREE_REF = "x3_plc_tree"; // the trees base ref
const string WP_REF = "wp_skip_bf1_"; // the waypoints base ref
const int MAX_TREES = 50; // the max number of spawns

void main()
{
     object oEntering = GetEnteringObject();
     if(!GetIsPC(oEntering) || GetIsDM(oEntering)) return;
     int nNumPCs = GetLocalInt(OBJECT_SELF, "NumPCs")+1;
     SetLocalInt(OBJECT_SELF, "NumPCs", nNumPCs);
     if(nNumPCs > 1) return;
     int nWP = 0, nSpawned = 0;
     while(++nWP <= 100 && nSpawned < MAX_TREES) // replace 100 with the number of waypoints if different
     {
         if(Random(2))
         {
            string sWP = IntToString(nWP);
            string sTree = IntToString(Random(19)); // 19 existing tree refs from 0 to 18 for each size
            int nRandSize = Random(3)+1;
            string sSize = "l";
            if(nRandSize == 1) sSize = "s";
            else if(nRandSize == 2) sSize = "m";
            switch(GetStringLength(sWP))
            { 
                case 1: sWP = "00" + sWP; break;
                case 2: sWP = "0" + sWP; break;
               default: break;
            }
            switch(GetStringLength(sTree))
            {
               case 1: sTree = "00" + sTree; break;
               case 2: sTree = "0" + sTree; break;
               default: break;
            }
            object oWP = GetNearestObjectByTag(WP_REF + sWP, oEntering);
            CreateObject(OBJECT_TYPE_PLACEABLE, TREE_REF + sSize + sTree, GetLocation(oWP));
            nSpawned++;
         }
    }
}


Kato
               
               

               


                     Modifié par Kato_Yang, 21 novembre 2011 - 10:49 .
                     
                  


            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #7 on: November 21, 2011, 11:02:28 pm »


               Question?  Instead of throwing down a bunch of  numbered waypoints.  Why not just  give all the waypoints the same name.      Tag then something like AreaName+"_Tree"    Then you can just use something like   GetObjectByTag(GetName(OBJECT_SELF)+"_Tree", nWP);
               
               

               
            

Legacy_SKIPPNUTTZ

  • Full Member
  • ***
  • Posts: 148
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #8 on: November 21, 2011, 11:23:13 pm »


               I guess I'll keep this in mind for the future, though it only took like 25 minutes to place all the waypoints. Gonna have to see how it turns out with only 100 random locations, should be sufficient.

However I might want to use other blueprints besides just trees if I cant find a way to manipulate the terrain of certain tiles. randomly placed rocks and other wilderness objects would certainly break up the monotony some.
               
               

               
            

Legacy_Kato -

  • Hero Member
  • *****
  • Posts: 747
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #9 on: November 21, 2011, 11:25:54 pm »


               @Lightfoot8 : Nicer indeed. Giving the waypoints an area identity and using the faster function. '<img'>
               
               

               


                     Modifié par Kato_Yang, 21 novembre 2011 - 11:34 .
                     
                  


            

Legacy_Rolo Kipp

  • Hero Member
  • *****
  • Posts: 4349
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #10 on: November 22, 2011, 12:23:24 am »


               <slippping in a side bar...>

SKIPPNUTTZ wrote...
What I am trying to accomplish, is creating random trees from the bioware blueprints, since the CEP naming conventions aren't nearly as organized. To avoid the engine creating objects at completely random vectors, subsequently in undesired locations (in front of doors), I figured I would just place around 100+ waypoints on the map with random facing directions and positions.

Just a side note, as I'm contemplating something tangential to this... If you *did* want to spawn something but wanted to make sure they *weren't* spawned in a certain area (or that they *were*), you could lay down a trigger defining the ex(in)clusion area and loop through the objects within the trigger to be sure no "_tree" or whatever was within with GetFirst(next)InPersistentObject.

I.e. Lay down a tr_doorway trigger and randomly spawn your whatever, then check to see if it fell into the trigger (blocking a doorway). If it did, destroy and respawn.

Not directly applicable to where you're going, but an alternative to keep in mind.

<...and getting wasted>
               
               

               
            

Legacy_SKIPPNUTTZ

  • Full Member
  • ***
  • Posts: 148
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #11 on: November 22, 2011, 12:44:03 am »


               heh what's up Rolo. My last name is Kipp =D

Anyways, that sounds like an interesting approach. To make sure I understand,  I could have just made a giant trigger theoretically, that didnt intrude on the doors that are along the perimeter of the forest. And made 50 or so trees spawn at random vectors only within that trigger via persistent object? That would be sooooo much easier lol.
-----

Just tested this out, and it seems to be working fine (thanks everyone). However I think it might be better to add a slight delay on the onexit event to prevent all of this being executed at the same time if there is a person soloing in these areas.

-----

Side note: I have 5 different elemental mobs, with 4 varying abilities of their respective elemental type, (Fire gets aura of fire, bolt of fire, pulse of fire, etc.) Seems like with this default AI, they will only spam Bolt of XXX.

//------------------------------------------------------------------------------
// * Setting this variable on a spellcaster creature will make its spelluse a
// * bit more random, but their spell selection may not always be appropriate
// * to the situation anymore.
//------------------------------------------------------------------------------
const string CREATURE_VAR_RANDOMIZE_SPELLUSE = "X2_SPELL_RANDOM";


If I set the string named: CREATURE_VAR_RANDOMIZE_SPELLUSE on all the creatures. Would I use X2_SPELL_RANDOM as the value, or would I use TRUE.

I have only recently discovered this switch and never used it.
               
               

               
            

Legacy_Taino

  • Sr. Member
  • ****
  • Posts: 268
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #12 on: November 22, 2011, 12:57:29 am »


               If your using CEP 2.3 or 2.4 why not just use the aiss [Aerisarn's Spawn System] function? Where you can just place var in each area and be done with it.
               
               

               
            

Legacy_SKIPPNUTTZ

  • Full Member
  • ***
  • Posts: 148
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #13 on: November 22, 2011, 01:52:01 am »


               

Taino wrote...

If your using CEP 2.3 or 2.4 why not just use the aiss [Aerisarn's Spawn System] function? Where you can just place var in each area and be done with it.


Well because it has been years since I worked on this game, and didnt even know such a system existed. Thanks for pointing it out, I'll look into it.

-----

Turns out I had to replace:
CreateObject(OBJECT_TYPE_PLACEABLE, TREE_REF + sSize + sTree, GetLocation(oWP));

with

object oTree =  CreateObject(OBJECT_TYPE_PLACEABLE, TREE_REF + sSize + sTree, GetLocation(oWP));
SetPlotFlag(oTree, TRUE);

-----

It was possible for AOE spells to destroy the placeables, and I wanted them to be totally noninteractive.
               
               

               
            

Legacy_Taino

  • Sr. Member
  • ****
  • Posts: 268
  • Karma: +0/-0
Randomly Generated Placeables
« Reply #14 on: November 22, 2011, 03:22:34 am »


               Yea the aiss is pretty basic.. If you look in your NWN> docs>cep you will find the aiss pdf. Easy to use and simple.

Just thought to add this additional info for you.