Aller au contenu

Photo

Random number generator inside While loop


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

#1
Ivanovich

Ivanovich
  • Members
  • 74 messages
Hi folks, I'm trying to insert a random number integer inside a "while" loop, but it continues to give me the same number each and every time (the maxinteger).  It's maddening.  I've tried it with the random function, and with the d4() function, but I always get "3".  Here is the code.  Any help is appreciated.


void main()
{
object oCenter = GetObjectByTag("guardbeacon");
string sWay = "Guard";
string sGuard;
object oArea = GetArea(GetEnteringObject());
string sArea = GetTag(oArea);
string sTarget;
int nCount = 1;
object oTarget;
location lTarget;
int iRand = Random(4);
string sRand;

if (sArea == "AmaristanCoastwood")
{
        if (GetLocalString(oArea, "Guards") != "yes")
        {
            SetLocalString(oArea, "Guards", "yes");
            sGuard = "amarioutcast001";

            oTarget = GetNearestObjectByTag(sWay, oCenter, nCount);
            lTarget = GetLocation(oTarget);

            while (GetIsObjectValid(oTarget))
            {
            CreateObject(OBJECT_TYPE_CREATURE, sGuard, lTarget, FALSE);
            nCount = nCount + 1;
            oTarget = GetNearestObjectByTag(sWay, oCenter, nCount);
            lTarget = GetLocation(oTarget);
            iRand = Random(4);
                if (iRand = 0)
                {
                sGuard = "amarioutcast001";
                }
                if (iRand = 1)
                {
                sGuard = "amarioutcast";
                }
                if (iRand = 2)
                {
                sGuard = "arakirirebel";
                }
                if (iRand = 3)
                {
                sGuard = "dhazaarioutcast";
                }
             sRand = IntToString(iRand);
             SendMessageToPC(GetEnteringObject(), "iRand is " + sRand);
            }
        }
}

}

#2
Shadooow

Shadooow
  • Members
  • 4 465 messages
Im using Random(Random(Random(7))); in module onload and randoms number sequence was always different. Also in PW enviroment it will be different. If that won't work, try use Random(GetTimeMiliseconds()); in each area OnEnter, that must work

Modifié par ShaDoOoW, 28 juillet 2010 - 04:29 .


#3
420

420
  • Members
  • 190 messages
The random function isn't truly random unfortunately and will always return the same sequence of numbers unless you first mix it up a bit.

Try putting a line like this in the OnClientEnter event of the Module Properties:
Random(GetTimeMillisecond());

The other problem is that you are using a while statement but you aren't cycling through any objects using GetFirst and GetNext functions. An if statement would be more appropriate than the while.

Lastly, instead of doing a bunch of if statements you may want to use a switch/case:
iRand = Random(4);
switch(iRand);
    {
    case 0:
        sGuard = "amarioutcast001";
        break;
    case 1:
        sGuard = "amarioutcast";
        break;
    case 2:
        sGuard = "arakirirebel";
        break;
    case 3:
        sGuard = "dhazaarioutcast";
        break;
    }

-420

#4
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
IMO Random works just fine. I think your main problem is that these:

if (iRand = 0)

should be:

if (iRand == 0)

If you want to see the randomness this is how I tested it. I took a part of what you had there and stuck it in a while loop to run 100 times. You will see that you get a pretty even mix. Here's the script:

void main()
{
int iLoops = 100;
int iGuard1;
int iGuard2;
int iGuard3;
int iGuard4;
string sGuard;

while (iLoops >0)
    {
    int iRand = Random(4);
    if (iRand == 0)
        {
        sGuard = "amarioutcast001";
        iGuard1++;
        }
    if (iRand == 1)
        {
        sGuard = "amarioutcast";
        iGuard2++;
        }
    if (iRand == 2)
        {
        sGuard = "arakirirebel";
        iGuard3++;
        }
    if (iRand == 3)
        {
        sGuard = "dhazaarioutcast";
        iGuard4++;
        }
    iLoops--;
    }

string sGuard1 = IntToString(iGuard1);
string sGuard2 = IntToString(iGuard2);
string sGuard3 = IntToString(iGuard3);
string sGuard4 = IntToString(iGuard4);
//sRand = IntToString(iRand);
//SendMessageToPC(GetEnteringObject(), "iRand is " + sRand);
SendMessageToPC(GetEnteringObject(), "Guard1 =" + sGuard1 +
                                     "\\\\\\\\nGuard2 =" + sGuard2 +
                                     "\\\\\\\\nGuard3 =" + sGuard3 +
                                     "\\\\\\\\nGuard4 =" + sGuard4);
}

Hope this helps. Good luck.

EDIT: Please note that for some reason when you put a "\\" in a post here it adds three more to it. There should only be one in front of the "n"s  where it says "nGuard2", "nGuard3" and "nGuard4". Not sure why this happens but it can be very problematic to some scripts if people don't catch it.

Modifié par GhostOfGod, 28 juillet 2010 - 06:44 .


#5
GhostOfGod

GhostOfGod
  • Members
  • 863 messages
Oh for crying out loud. Ok it added more of those when i edited it. This is something that needs to be fixed. But anyway there should only be one just in front of the "n"s. Sorry bout this second post but i didn't want a bunch more on there.

#6
TheSpiritedLass

TheSpiritedLass
  • Members
  • 765 messages
*groans* Nice catch Ghost. classic problem.



The NWN compilier should have thrown a total fit over that logic issue, Ivanovich. Not that I just did that myself earlier this week to know that. Nope, not me. Uh uh. *whistles innocently*

#7
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
umm sorry was trying to post a single slash.  I faild.  Back to the drawing board. 




 

Modifié par Lightfoot8, 28 juillet 2010 - 09:24 .


#8
Ivanovich

Ivanovich
  • Members
  • 74 messages
I appreciate all the feedback. It would seem I have a problem even before I get to the random number issue. I put the above script in the OnEnter part of an area. It locks up and does not load the module when this is the case - regardless of whether or not I am starting in that area. I've narrowed it down to the "CreateObject" inside the script.



All I'm trying to do is - when a PC enters an area - have guards spawn on waypoints already layed out in the module. I've tried the PersistentObject function, to no avail (it did not like the area as the persistent object.



if the creature is "creature001" and the waypoint is "guard" and there are 3 waypoints (but the number could vary in different zones), how on God's green Earth can I simply get the guards to spawn on variable number of waypoints named "Guard" as the PC enters the zone?



This really shouldn't be that difficult, yet I seem to hit a wall each time.

#9
Ivanovich

Ivanovich
  • Members
  • 74 messages
This is the script I'm using. I've commented out the createobject because if I do not, the module doesn't load. At all.



I've read the response from "420" above, but I cannot figure how else to create the loop without a "while".


#10
Ivanovich

Ivanovich
  • Members
  • 74 messages
void main()

{

object oPC = GetEnteringObject();

object oCenter = GetNearestObjectByTag("x3_plc_evlaltar", oPC);

string sWay = "Guard";

string sGuard;

object oArea = OBJECT_SELF;

string sArea = GetTag(oArea);

string sTarget;

int nCount = 1;

object oTarget;

location lTarget;

int iRand = Random(4);

string sRand;







if (sArea == "AmaristanTownofAbbala")

{

sGuard = "arakhirisoldier1";



oTarget = GetNearestObjectByTag(sWay, oCenter, nCount);

lTarget = GetLocation(oTarget);



if (GetLocalString(oArea, "Guards") != "yes")

{

while (GetIsObjectValid(oTarget))

{

SendMessageToPC(oPC, "Pass");

//CreateObject(OBJECT_TYPE_CREATURE, sGuard, lTarget, FALSE);

nCount = nCount + 1;

oTarget = GetNearestObjectByTag(sWay, oCenter, nCount);

lTarget = GetLocation(oTarget);

}

}



SetLocalString(oArea, "Guards", "Yes");

}

}

#11
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
Your problem is simple. The OnEnter Event for the area Fires every time something enters the area, Not just a PC. So when the script first runs and creates another object in the area, this new object entering the area also fires the Event and creates another object entering the area. around and around we go. The loop never stops and your module locks up.

To fix put a check at the top of the script to exit the script if the Entering object is not a PC.

object oPC = GetEnteringObject();

If (!GetIsPC(oPC)) return;

Modifié par Lightfoot8, 30 juillet 2010 - 08:29 .


#12
Ivanovich

Ivanovich
  • Members
  • 74 messages
Here's a new script for you:



string sMan = "The Man"

object oHelper = LastForumHelper();



SendMessageToForumHelper(oHelper, "You = " + sMan + "!! Thanks!");




#13
420

420
  • Members
  • 190 messages
Good catch Lightfoot8, sometimes it's the simplest solutions that are the hardest to see.

Ivanovich wrote...

I've read the response from "420" above, but I cannot figure how else to create the loop without a "while".

What I meant was that if you want to use a while loop the format should look something like this:
void main()
{
object oTarget = GetFirstObjectInArea(OBJECT_SELF);
while(GetIsObjectValid(oTarget))
    {
    if(GetTag(oTarget) == "TargetTag")
        {
        //Do something to the target then break out of the loop
        break;
        }
    oTarget = GetNextObjectInArea(OBJECT_SELF);
    }
}

There are a bunch of helpful GetFirst/GetNext related functions:
GetFirstEffect()
GetFirstFactionMember() [Used to cycle through party members]
GetFirstInPersistentObject() [Used to cycle through objects in a trigger or an AoE]
GetFirstItemInInventory()
GetFirstItemProperty()
GetFirstObjectInArea()
GetFirstObjectInShape() [Also used for auras]
GetFirstPC()

In addition you can always use the iLoops method posted by GhostOfGod if you are working with a static number of objects.

-420

Modifié par 420, 30 juillet 2010 - 10:19 .


#14
Ivanovich

Ivanovich
  • Members
  • 74 messages
I stayed away from the GetFirstObjectInArea because of the amount of objects in the area. It might be a cumbersome script on the server.




#15
Ivanovich

Ivanovich
  • Members
  • 74 messages
Here's an additional question. On Exiting the area, I've got this:

object oPC = GetExitingObject();
object oCenter = GetNearestObjectByTag("guardbeacon", oPC);

but oCenter is always blank, as if it's not picked up. The item "guardbeacon" is in the area the PC just exited. But it seems that if I put this script in the OnExit part of the area, it never finds the "guardbeacon" with this function. Thoughts?

I also tried it with

object oCenter = GetNearestObjectByTag("guardbeacon", OBJECT_SELF);

But the area doesn't get it either.

Modifié par Ivanovich, 31 juillet 2010 - 12:52 .


#16
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages

Ivanovich wrote...


I stayed away from the GetFirstObjectInArea because of the amount of objects in the area. It might be a cumbersome script on the server.

*************

Here's an additional question. On Exiting the area, I've got this:

object oPC = GetExitingObject();
object oCenter = GetNearestObjectByTag("guardbeacon", oPC);

but oCenter is always blank, as if it's not picked up. The item "guardbeacon" is in the area the PC just exited. But it seems that if I put this script in the OnExit part of the area, it never finds the "guardbeacon" with this function. Thoughts?

I also tried it with

object oCenter = GetNearestObjectByTag("guardbeacon", OBJECT_SELF);

But the area doesn't get it either.


The responce to bout of your last posts are linked.

Once the PC leaves the Area he is no longer in an area. His location in Invalid therefore you can not find any objects that are close to him. Or even get a location for him. You would be best to use GetFirstObjectInArea/GetNextObject in area.

420 is also correct. If you only have 4 waypoints and you are wanting a loop to get all of them you are better off with the GetFirst/GetLast functions. These functions have the advantage if being iterators. This means that every time you call the GetNext function it does not have to start over from scratch and can just start the search back up where the previous function lest off.
The Get Nearest functions are not iterators so every time the function is called it has no ground work laid for it. It will search through every object in the area. then have to calculate which one is closer. There is a lot more over head to this function, It probable even calls the GetFirstObjectInArea/Next functions every tine you use it. unless you actually need the closest x number of objects, you are better off looping through the Objects one time as opposed to x times. without all the distance calculations also.

Modifié par Lightfoot8, 31 juillet 2010 - 01:44 .


#17
Ivanovich

Ivanovich
  • Members
  • 74 messages
Excellent.  Got it to work with the "First Object in Area" function.

Your help was sincerely appreciated.  Cheers.

Modifié par Ivanovich, 01 août 2010 - 09:33 .


#18
Ivanovich

Ivanovich
  • Members
  • 74 messages
Whoops, works now. Had too much data inside the if statement.



Thanks for all your help.


#19
Ivanovich

Ivanovich
  • Members
  • 74 messages
Ok, so here's adding a little complexity into the issue. Now that the script works, and it removes the guards once the PC leaves the area, what is the best way to assure the script only runs when there are no more PCs in the area? For example, if a PC leaves the area, but other PCs are there, the guards remain. If not, and the zone is empty of all PCs, the script executes and the guards in the zone are removed.



Would it be to cycle through all objects with the GetFirst/NextInArea function again? Or is there a simpler way?

#20
Shadooow

Shadooow
  • Members
  • 4 465 messages
This is one possible way to do this.

int GetNumPCsInArea(object oArea)
{
 if(!GetIsObjectValid(oArea) || GetArea(oArea) != oArea)
 {
 return 0;
 }
object oPC = GetFirstPC();
int nCount;
 while(GetIsObjectValid(oPC))
 {
  if(GetArea(oPC) == oArea)
  {
  nCount++;
  }
 oPC = GetNextPC();
 }
return nCount;
}

Just place this function at top of your script and then in your script use:

if(GetNumPCsInArea(oArea) == 0)

Another method using GetNearestCreature, and that method is more efficient, but when we are speaking about such small codes, it don't matter really.

BTW you might consider to postphone the cleaning area to 2minutes after last PC exit area. Quite often, some player enters area again until that, so cleaning could be quite ineffective. (If you delay cleaning, just must then re-check PC count in area!)

Also, good idea is to reset all encounters when you clean monsters, to ensure players won't exploit cleaning area in order to get safely through area to lower floor.

Modifié par ShaDoOoW, 02 août 2010 - 08:14 .


#21
Ivanovich

Ivanovich
  • Members
  • 74 messages
Thanks, m8. Not going to do this with monsters, only guards in the cities. How do you suggest waiting the 2 minutes? Just with the DelayCommand function?

#22
Shadooow

Shadooow
  • Members
  • 4 465 messages
This might give you idea.

void CleanArea()
{
object oArea = OBJECT_SELF;
 if(GetNumPCsInArea(oArea) > 0)
 {
 return;
 }
SetLocalInt(oArea,"CLEANED",1);//im checking this when someone enter area to know if I should spawn npcs
object oFirst = GetFirstObjectInArea(oArea);
int nTh = 1;
object oNPC = GetObjectType(oFirst) == OBJECT_TYPE_CREATURE ? oFirst : GetNearestObject(OBJECT_TYPE_CREATURE,oFirst,nTh++);
 while(GetIsObjectValid(oNPC))
 {
  if(GetLocalInt(oNPC,"CLEANME") != -1)
  {
  DestroyObject(oNPC);
  }
 oNPC = GetNearestObject(OBJECT_TYPE_CREATURE,oFirst,nTh++);
 }
}

void main()
{
object oArea = OBJECT_SELF;
 if(GetNumPCsInArea(oArea) > 0)
 {
 return;
 }
DelayCommand(120.0,CleanArea());
}


Modifié par ShaDoOoW, 02 août 2010 - 08:43 .


#23
Ivanovich

Ivanovich
  • Members
  • 74 messages
Great, so the DelayCommand. Ok, thanks, mate. And thanks to the others that assisted with this thread!

#24
Ivanovich

Ivanovich
  • Members
  • 74 messages
Here's a question for anyone still reading... :)

When you script the "GetItemActivatedTargetLocation" function, and you've created an item to use a unique power with the same name as used in the script, the process goes as such:

1. You use the item.
2. You target something.
3. If the "something" is greater than a pre-defined distance away, you first MOVE towards that something before your next action.

Is there a way to change that predefined distance to longer? Ie, if I target something on the other side of the screen, can I make it accept the target without moving half the way there? Or is that hardcoded into the engine?

Thx!

Modifié par Ivanovich, 04 août 2010 - 12:07 .


#25
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
Here is the relevelent information from spells.2da.

 
2DA V2.0
      
         Label                                Range       Name    TargetType  
386   ACTIVATE_ITEM                   S            6802      0x7f                       
413   ACTIVATE_ITEM_SELF          S            6802      0x01                       
428   ACTIVATE_ITEM_SELF2        S           6802       0x09                       
697   ACTIVATE_ITEM_L                L            83622     0x7f                       
795   ACTIVATE_ITEM_T               T           86781      0x7f                       

 

The range collunm can have values of:


Range of spell
P = personal
T = touch
S = short
M = medium
L = long

Target types are as follows.
self = 0x01
creature = 0x02
area = 0x04
items = 0x08
door = 0x10
placeable = 0x20
trigger = 0x40

Modifié par Lightfoot8, 05 août 2010 - 08:56 .