Aller au contenu

Photo

Stubborn deer


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

#1
Rolo Kipp

Rolo Kipp
  • Members
  • 2 788 messages
 <refusing to be driven crazy...>

Background for those who haven't been following the bouncing ball: I'm incorporating Failed.Bard's dynamic forest system into the Regional Mod. A good part of that is the creation (and destruction) of the ambient vegetation, prey and predators (and possibly landmark features when I get the time).

The creation works the way I want... in poor nodes, starving, weak creatures are spawned. In rich nodes, powerful, healthy creatures are spawned. The creatures have default (yucky) scripting on them, but they work. And they can be killed. They spawn where and when I want them to, and in the proper number.

The problem: They refuse to be destroyed. Transition away and the "reg_node_sleep" script is dropped on the exiting area with a delay (currently set to 10 seconds for debugging). Reg_node_sleep check to be sure there are no PCs in the area (not in use) then destroys all the node-specific ambients (tagged reg_veg, reg_pry and reg_prd) and unsets the isNode identifier (freeing the area for reuse).

It all works except the destroy part.

The destroy part should be as simple as:

DestroyObjectsInAreaByTag( "reg_pry", OBJECT_SELF );

Except it doesn't work.

Hmmm... Ok. Perhaps the objects aren't coming back as valid for some reason?

Tried all kinds of loops with all kinds of delays.  Here is the latest:

  oObject = GetFirstObjectInArea( OBJECT_SELF);

//   DestroyObjectsInAreaByTag( "reg_veg", OBJECT_SELF );

// Loop through all "reg_pry" tags in area - Destroy  
iIndex = 0;  
oTarget = GetNearestObjectByTag( "reg_pry", oObject);  
while (oTarget != OBJECT_INVALID) {     
   if (GetIsObjectValid(oTarget)) {
       sMessage = "Pry "+IntToString(iIndex)+" is a valid object type "+IntToString(GetObjectType(oTarget))+ " (creature is type "+IntToString(OBJECT_TYPE_CREATURE)+")";
       SendMessageToPC( oPC, sMessage );
   }      
   nPC = 0;  // Just reusing var as counter      
   while (oTarget != OBJECT_INVALID ) { 
      sMessage = IntToString(nPC++)+" Trying to destroy pry " + IntToString( iIndex);
      SendMessageToPC( oPC, sMessage );   
      DestroyObject( oTarget, 0.01f );    
   }      
   oTarget = GetNearestObjectByTag( "reg_pry", OBJECT_SELF, iIndex++ );
}

And a dump of the debug messages...

 Pry Table is pry_tempforest
 Max Pry # 5
 Stage is 6
 Pry chain is 1
 Alpha Prey is reg_deer_06
 Beta is reg_deer_04
 Beta is reg_deer_04
 Beta is reg_deer_04
 Beta is reg_deer_04
 Beta is reg_deer_04
 Beta is reg_deer_04
 Stage is 6
 Pry chain is 4
 Alpha Prey is reg_deer_06
 Beta is reg_deer_04
 Beta is reg_deer_04
 Beta is reg_deer_04
 Beta is reg_deer_04
 Beta is reg_deer_04
 Beta is reg_deer_04
 [Server] You are now in a No PVP area.
 [Server] You are now in a No PVP area.
 Putting node to sleep...
 Pry 0 is a valid object type 1 (creature is type 1)
 0 Trying to destroy pry 0
 1 Trying to destroy pry 0
 2 Trying to destroy pry 0
 ...
 < snip >
 ...
 6234 Trying to destroy pry 0
 Script reg_node_sleep, OID: 800000ee, Tag: , ERROR: TOO MANY INSTRUCTIONS
 Script reg_area_exit, OID: 800000ee, Tag: , ERROR: TOO MANY INSTRUCTIONS

Note that the object is valid and a creature. The only creatures in that area are ambients with the tag "reg_pry". So it is getting the right object, the object is valid... but it never destroys it. 
And I have tried it with various delays...

Those stubborn deer have stumped me. 

Edit: And yes, I realize the base object oObject will be destroyed in the middle of that loop, self destructing the whole thing. But "Destroy" has to work before that can happen :-P 
Then I can use "DestroyObjectsInAreaByTag" ;-P

<...'cuz he can walk from here>

Modifié par Rolo Kipp, 22 novembre 2012 - 04:25 .


#2
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
You may have made the creatures undestroyable by either leaving a lootable corpse or making them raisable. 

Try:
...
AssignCommand(oTarget,SetIsDestroyable(TRUE));
DestroyObject( oTarget, 0.01f );
... 

 
EDIT:

Edit: And yes, I realize the base object oObject will be destroyed in the middle of that loop


False,  Objects are not destroyed untill after the script finishes.  

Modifié par Lightfoot8, 22 novembre 2012 - 04:38 .


#3
Rolo Kipp

Rolo Kipp
  • Members
  • 2 788 messages
<pulling hard...>

The blueprint is standard creature (not lootable, no inventory, etc...) but...
Ok.
And...
No change. Same result.

Edit: Added:

   while (GetObjectType(oObject) != OBJECT_TYPE_WAYPOINT ) {
       oObject = GetNextObjectInArea( OBJECT_SELF );
   }

So loop doesn't self destruct. Same result.

<...on his least favorite pipe>

Modifié par Rolo Kipp, 22 novembre 2012 - 04:47 .


#4
Rolo Kipp

Rolo Kipp
  • Members
  • 2 788 messages
<The light dawns...>

Lightfoot8 wrote...
False,  Objects are not destroyed untill after the script finishes.  

Ah-HA!

And the script wont end until the objects are destroyed! Got it.

*Knew* it was something simple and stupid :-/

Thanks, LF. Luv ya, man! Er, Kitty. Grumpy kitty.

<...dim and flickering, but it dawns>

#5
henesua

henesua
  • Members
  • 3 858 messages
Are the scripts that destroy the animal running on the animal? What event calls them? Or are they called by OnAreaExit? Etc...

I ran into a problem when depending on Creature AI scripts after all PC's left the area. The rate of execution goes way way down, and this appears to be a hardcoded feature.

Anyway... just somewhere to look.

Another Idea....
Use OTR's deer. :D

Modifié par henesua, 22 novembre 2012 - 04:52 .


#6
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
Yep,  you found it.   Just so everyone else can see it. 

void main()
{
  oObject = GetFirstObjectInArea( OBJECT_SELF);
  //   DestroyObjectsInAreaByTag( "reg_veg", OBJECT_SELF );
  // Loop through all "reg_pry" tags in area - Destroy
  iIndex = 0;
  oTarget = GetNearestObjectByTag( "reg_pry", oObject);
  while (oTarget != OBJECT_INVALID)
  {
    if (GetIsObjectValid(oTarget))
    {
       sMessage = "Pry "+IntToString(iIndex)
         +" is a valid object type "
         +IntToString(GetObjectType(oTarget))
         + " (creature is type "+IntToString(OBJECT_TYPE_CREATURE)+")";
       SendMessageToPC( oPC, sMessage );
    }
    nPC = 0;  // Just reusing var as counter
    while (oTarget != OBJECT_INVALID )
    {
      sMessage = IntToString(nPC++)
       +" Trying to destroy pry "
       + IntToString( iIndex);
      SendMessageToPC( oPC, sMessage );
      DestroyObject( oTarget, 0.01f );
    }

   oTarget = GetNearestObjectByTag( "reg_pry", OBJECT_SELF, iIndex++ );
  }

An endless loop was created on the first object since it was not destroyed during the script and was always valid.

#7
Rolo Kipp

Rolo Kipp
  • Members
  • 2 788 messages
<sighing...>

Nope. Destroyed them all (back to "DestroyAllHumans...er, ObjectsInAreaByTag") and let the script end... and they're still there. Doubled in fact, since they respawn when returning to the node...

<...a vast sigh of the put-upon>

Modifié par Rolo Kipp, 22 novembre 2012 - 05:07 .


#8
Rolo Kipp

Rolo Kipp
  • Members
  • 2 788 messages
<trying to make up in quantum physics...>

henesua wrote...
Are the scripts that destroy the animal running on the animal? What event calls them? Or are they called by OnAreaExit? Etc...

I ran into a problem when depending on Creature AI scripts after all PC's left the area. The rate of execution goes way way down, and this appears to be a hardcoded feature.

The area onExit executes the reg_node_sleep script on the exiting area after a delay. That part works fine. I get starting and ending messages letting me know the script fired and completed (now that it's not an endless loop).

Anyway... just somewhere to look.

Another Idea....
Use OTR's deer. :D

I thought about it (and certainly will for Amethyst!) But this is for the CCC and I really don't want to include other, previously released content to bloat the hak :-P So all the blueprints are simply modified bioware original. Makes finding diversity a real challenge for the biomass chains :-/

Trust me, better trees, prey animals, predators (and especially the alpha creatures!) are a must with this.

<...what he lacks in quality>

Modifié par Rolo Kipp, 23 novembre 2012 - 11:24 .


#9
henesua

henesua
  • Members
  • 3 858 messages
Did you put a debug notifier in their death script to let you know when they died?
I usually do something like print out "DEATH" + GetName + Object ToString

-- and you should rename them "Mule Deer" in  stubborness.

Modifié par henesua, 22 novembre 2012 - 05:05 .


#10
Rolo Kipp

Rolo Kipp
  • Members
  • 2 788 messages
<tying notes...>

henesua wrote...
Did you put a debug notifier in their death script to let you know when they died?
I usually do something like print out "DEATH" + GetName + Object ToString

-- and you should rename them "Mule Deer" in  stubborness.

Good ideas, both...
brb

<...to the legs of frisky deer>

#11
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
It will not solve the current problem, But I did notice that your loop is getting the first creature twice. 

iIndex++  will return the curent value of  iIndex to a calling function and increase its value after.
++iIndex will increase the value first and return the new value.

so change :  oTarget = GetNearestObjectByTag( "reg_pry", OBJECT_SELF, iIndex++ );


To:  oTarget = GetNearestObjectByTag( "reg_pry", OBJECT_SELF, ++iIndex );

#12
Rolo Kipp

Rolo Kipp
  • Members
  • 2 788 messages
<willing to be proved...>

Lightfoot8 wrote...
It will not solve the current problem, But I did notice that your loop is getting the first creature twice.  

iIndex++  will return the curent value of  iIndex to a calling function and increase its value after.
++iIndex will increase the value first and return the new value.
...

I may be wrong, but I call it first outside the loop with the first object (index 0).
This gives it a valid object for the loop.
Operations are done on object 0, and object 0 is the object handed off to "Destroy". *Then* iIndex is incremented to object 1 .... Ah! *after* the GetObject grabs object 0 again. I see :-)

Thanks :-)

Ah-HA! again... Inside the loop I use OBJECT_SELF (area) to get next object... should be oObject.
And... it works :-)

<...slightly off kilter>

#13
Zarathustra217

Zarathustra217
  • Members
  • 221 messages
GetNearestObjectByTag takes 1 as the nearest object, so by...

oTarget = GetNearestObjectByTag( "reg_pry", oObject, iIndex++ );

... I imagine you'd get an OBJECT_INVALID, ending the loop after first run. Using ++iIndex instead would indeed solve part of it, but you still have to have your iIndex initialize as 1.

Alternatively, since GetFirstObjectInArea might be one of your deers, you could pass that in as the first target,

replacing the

object oTarget = GetNearestObjectByTag( "reg_pry", oObject);

with:

object oTarget = oObject;

Then you can initialize iIndex as 0 or keep iIndex++ rather than ++iIndex.

Modifié par Zarathustra217, 24 novembre 2012 - 08:56 .


#14
Zarathustra217

Zarathustra217
  • Members
  • 221 messages
Hmm, realising I might have come off a bit confusing, let me try to show how I propose to change the code:

object oObject = GetFirstObjectInArea( OBJECT_SELF);

// Loop through all "reg_pry" tags in area - Destroy 
iIndex = 1; 
oTarget = oObject; //First loop, we'll use the first object in the area.
while (oTarget != OBJECT_INVALID) {     
   if (GetIsObjectValid(oTarget)) {
       sMessage = "Pry "+IntToString(iIndex)+" is a valid object type "+IntToString(GetObjectType(oTarget))+ " (creature is type "+IntToString(OBJECT_TYPE_CREATURE)+")";
       SendMessageToPC( oPC, sMessage );
   }     
   nPC = 0;  // Just reusing var as counter     
   //Z217 - not really sure why you want to have the nPC variable here?
   sMessage = IntToString(nPC++)+" Trying to destroy pry " + IntToString( iIndex);
   SendMessageToPC( oPC, sMessage );   
   DestroyObject( oTarget, 0.0 );   //Z217 removed delay, unneeded, DestroyObject is always executed after the script ends.
   oTarget = GetNearestObjectByTag( "reg_pry", oObject, iIndex++ );
}


#15
Rolo Kipp

Rolo Kipp
  • Members
  • 2 788 messages
<grabbing a towel...>

The nPC was just a reused variable left over from the endless loop LF highlighted in red above.
Likewise the delay was a legacy of me going nuts trying to figure out why they weren't destroyed.

And, actually, the original question remains unanswered. Although I got the loop-through to destroy what I want when I want, why didn't DestroyObjectsInAreaByTag() work? It takes the same area (OBJECT_SELF) and the same tag "reg_pry" I used in the loop...

The final version (in use now) is:
Edit: oObject is initialized to the first WP in the area. It's just a base from which to count.
But you're right about the index. Was 1. Why'd I set it to 0? *shakes head sadly*

// Loop through all "reg_pry" tags in area - Destroy
iIndex = 1;
oTarget = GetNearestObjectByTag( "reg_pry", oObject);
while (oTarget != OBJECT_INVALID) {
   sMessage = "Destroying pry " + GetName(oTarget)+" "+IntToString( iIndex);
   SendMessageToPC( oPC, sMessage );
   DestroyObject( oTarget );
   oTarget = GetNearestObjectByTag( "reg_pry", oObject, ++iIndex );
}


<...to clean up the mess>

Modifié par Rolo Kipp, 24 novembre 2012 - 04:31 .


#16
Zarathustra217

Zarathustra217
  • Members
  • 221 messages
Where did you pull the DestroyObjectsInAreaByTag from?

#17
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages

Rolo Kipp wrote...

 
 And, actually, the original question remains unanswered. Although I got the loop-through to destroy what I want when I want, why didn't DestroyObjectsInAreaByTag() work? It takes the same area (OBJECT_SELF) and the same tag "reg_pry" I used in the loop...



It is hard to answer the original question,  The code posted in the original post did not work because of the TMI.   Nothing happened in the script except in the first itteration of the loop where you hit your TMI on the inner loop. 

After that I never seen any Non working code posted, So it is hard to answer the question.

#18
Rolo Kipp

Rolo Kipp
  • Members
  • 2 788 messages
<a bigger fan...>

Zarathustra217 wrote...
Where did you pull the DestroyObjectsInAreaByTag from?

From x0_io_destroy (SoU):

//:://////////////////////////////////////////////////
//:: X0_I0_DESTROY
/*
Small include library to handle destroying multiple objects
with the same tag. Also has some utility functions for
handling multiple objects with the same tag.
 */

//:://////////////////////////////////////////////////
//:: Copyright (c) 2002 Floodgate Entertainment
//:: Created By: Naomi Novik
//:: Created On: 11/11/2002
//:://////////////////////////////////////////////////


/**********************************************************************
 * CONSTANTS
 **********************************************************************/

string INVISIBLE_OBJECT_RESREF = "plc_invisobj";

/**********************************************************************
 * FUNCTION PROTOTYPES
 **********************************************************************/

// Destroy all the objects in the area with a given tag.
// If nLimit is > 0, this will be the maximum number of things
// destroyed with the given tag.
void DestroyObjectsInAreaByTag(string sTag, object oCenter=OBJECT_SELF, int nLimit=0);

/**********************************************************************
 * FUNCTION DEFINITIONS
 **********************************************************************/

// Destroy all the objects in the area with a given tag.
// If nLimit is > 0, this will be the maximum number of things
// destroyed with the given tag.

void DestroyObjectsInAreaByTag(string sTag, object oCenter=OBJECT_SELF, int nLimit=0)
{
    object oDestroyer = CreateObject(OBJECT_TYPE_PLACEABLE,
                                     INVISIBLE_OBJECT_RESREF,
                                     GetLocation(oCenter));
    int nObjects = CountAllObjectsInAreaByTag(sTag, oDestroyer);
    if (nLimit == 0) {
        nLimit == nObjects;
    }
    int i=0;
    float fDelay=0.0;
    for (i=0; i < nObjects && i < nLimit; i++) {
        fDelay += 0.2;
        AssignCommand(oDestroyer,
                      DestroyNearestObjectByTag(sTag, oDestroyer, fDelay));
    }
    fDelay = 1.0 * nObjects;
    AssignCommand(oDestroyer, DestroyObject(oDestroyer, fDelay));
}

And the called functions are in there to (count & destroy nearest).
Just don't know why it didn't work the simple way :-P

@ LF: I mentioned it in the OP:

The destroy part should be as simple as:

DestroyObjectsInAreaByTag( "reg_pry", OBJECT_SELF );

Except it doesn't work.


<...of her books>

Modifié par Rolo Kipp, 25 novembre 2012 - 03:04 .


#19
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
My misunderstanding.  I thought DestroyObjectsInAreaByTag  was the function you where writing.

So the question is then what was OBJECT_SELF when you called the function?  (or oCenter)

DestroyObjectsInAreaByTag( "reg_pry", OBJECT_SELF );

The first line in the function attempts to create a Placable at the location of oCenter.  If oCenter is the Area or ANY OBJECT NOT IN AN AREA the location will be invalid and then function will fail.

#20
Rolo Kipp

Rolo Kipp
  • Members
  • 2 788 messages
<shakes head sadly...>

Of course. And since I pass the tag and the area (Script is run on the area), it doesn't work.

So DestroyObjectsInAreaByTag does *not* work if you do not seed it with an object in the area.
Should have been written to get the first object in area if oCenter was an area. My opinion...

Well. Good to know.

Thanks for the dissection, LF :-) It was bugging me.

<...and waggles a finger at naomi>

#21
Zarathustra217

Zarathustra217
  • Members
  • 221 messages
I'd honestly recommend you didn't use that function since cycling by GetNearestObjectByTag is (particularly) slow when you have a lot of objects with the same tag in an area. Based on my own tests, I believe you would get the most effective solution by using GetFirst/NextObjectInShape that covers the entire area.

Code for such a function would be:


void DestroyAllObjectsInAreaByTag(object oArea, string sTag, int nObjectFilter=OBJECT_TYPE_CREATURE)
{
    location lCenter=Location(oArea, Vector(), 0.0);
    object oTemp=GetFirstObjectInShape(SHAPE_CUBE, 400.0, lCenter, nObjectFilter);
    while(oTemp!=OBJECT_INVALID)
    {
        if(GetTag(oTemp)==sTag)
        {
            DestroyObject(oTemp);
        }
        oTemp=GetNextObjectInShape(SHAPE_CUBE, 400.0, lCenter, nObjectFilter);
    }
}



#22
Rolo Kipp

Rolo Kipp
  • Members
  • 2 788 messages
<scribbling madly...>

I like it, thanks Z :-)
Hmmm... That 400.0... meters? "half the length of the sides of the cube".. A cube 800m on a side? Overkill?

And did anyone actually do any profiling on GetObjectInShape vs. Get ObjectInArea? I saw Funky state an opinion on it, but I don't remember anyone actually looking at the numbers...

<...with invisible ink>

#23
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
I myself would just use GetFirst/NextObjectInArea.

1) No distance checks
2) no checks to see if it in in the shape.

Draw back. There will be more VM instructions, since you will have to Filter for the Object type in the script.

With your system what you are really looking at is the trade off of number of VM instructions vs. speed.

What I really do not like about the function from the bioware Include is the fact that it counts all of the objects in the area, Most likely using GetNeareastObject in the iCountAllObjectsInAreaByTag function. This basicly means that the function uses the slowest function twice on all objects with that tag. A major waste in my book.

#24
Zarathustra217

Zarathustra217
  • Members
  • 221 messages
Well, I profiled it at some point and it turned out that if you were looking for just one object type, using the GetFirst/NextObjectInShape was faster than cycling with GetFirst/NextObjectInArea. The difference was rather marginal though, but my main point is really that I think it's easy to overrate how much performance is lost in simple geometrical tasks in NWN.

But then, we are splitting hairs here. To Rolo, I simply used 400.0 meters to make sure every object in the area gets included. I expect that the GetFirst/NextObjectInShape function doesn't work by drawing out any actual cube but calculate per object if it'll be within the range.

#25
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages
First I am not arguing, Just giving my opinion.   Since I am not going to the length of looking at the functions, nothing is definitive. 

[quote]Zarathustra217 wrote...

Well, I profiled it at some point and it turned out that if you were looking for just one object type, using the GetFirst/NextObjectInShape was faster than cycling with GetFirst/NextObjectInArea. The difference was rather marginal though,... [/quote]
I can see it being both slower and faster.  It is all dependent on how many items are in the area and the mix/ratio of object types.  Both functions are itterators so will not have the same major drag as GetNearestObject.  The main advantage of  GetFirst/NextObjectInShape is the "type filter" as you stated.  Being handled by the function in bulding the list allows it to happen much faster then the VM instruction equivalent.  I myself would go with First/NextInArea but that is just my prefferance.



[/quote] ...but my main point is really that I think it's easy to overrate how much performance is lost in simple geometrical tasks in NWN.[/quote]
I agree with that 100%,  I myself would still use GetNearestObjectByTag, If I was just looking for the closest single object.  I would not however use it to itterare through objects since it builds and destroys its distance chart every time the function runs. ( I know we all agree on that one.)   The problem with that function is not that it does a simple geometrical tasks, It is that it does a simple geometrical tasks way to often, along with allocating/deallocating memory on every itteration just to rebuild the same list again.. and again. ( Again Im beating a dead horse.)  

[quote]But then, we are splitting hairs here. To Rolo, I simply used 400.0 meters to make sure every object in the area gets included. I expect that the GetFirst/NextObjectInShape function doesn't work by drawing out any actual cube but calculate per object if it'll be within the range.[/quote]

I would guess you are right.   I view the function as a wrapper around GetFirst/NextInArea with a distance Location check. does not really matter how it was done though since it is an itterator,  Meaning that the Checks only have to be done once per object,  reguardless of the number of itterations.  

P.S. and after reading the mess i wrote above, I should have just said; Apples and Oranges, Both functions will work. Just pick one.