Aller au contenu

Photo

Queue/Stack of Objects - Possible in NwN?


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

#1
Tarot Redhand

Tarot Redhand
  • Members
  • 2 677 messages

I have a while loop. In that loop, a function is called that deletes the object passed to it. With the function, the script crashes with a "too many instructions" error, iterating through the while loop 1400+ times. If I comment out the function call the while loop only iterates 30 times which is what is supposed to happen.

 

I figure that if I can separate the gathering of the objects from the deletion of same I can eliminate the crash. i.e.

  1. create a list of valid objects.
  2. step through the list, deleting as I go.

For this I would need a more complex data structure than the simple variables provided by vanilla NwN. Does anyone know how to go about this?

 

Thanks in advance for any help.

 

TR



#2
meaglyn

meaglyn
  • Members
  • 808 messages

You have to fake it. There are a number of list implementations. One simple one can be found in Paul Speed's z-dialog package. You only need the pg_lists_i.nss file from it. But  you could easily roll your own for what you are doing. e.g.  list_object_0, list_object_1 ...   just keep a count and walk back through the variables in a for loop or some such.



#3
Lightfoot8

Lightfoot8
  • Members
  • 2 535 messages

Hmm,   I am not 100% sure what you are trying to do.    But I can see where trying to maintain a list of object,   regardless of sorting them or not,  can lead to a TMI error. 

 

The only Idea I can think of would be to create a  empty store or other container object to hold you list as actual objects.  Not necessarily the actual object.  any miscellaneous Small item would do.   Give the item a useful  Tag to your situation and a LocalObject Var to point to the item it represents in your list. 

 

That is the best I can think of without seeing your original script. 

 

 

Example:

 

object oListContainer =  GetObjectByTag( " bla bla");
 
void AddItemToList ( object oItem)
{
 
   SetLocalObject
     (
       CreateItemOnObject("gem001",oListContainer,1,ObjectToString(oItem)),
       "Me",
       oItem
      );
 
}


#4
Proleric

Proleric
  • Members
  • 2 352 messages
Without seeing the script, it's hard to be certain why you're getting a TMI. DestroyObject is normally safe in loops, because it doesn't occur until end of script.

If you need to make a list, though, as Meaglyn says, pseudoarrays are good. In this case, it would be a set of local objects, to be destroyed in a subsequent loop.

#5
WhiZard

WhiZard
  • Members
  • 1 204 messages

TMI is based on instruction count, not on script completion time.  If your script takes too long, then there are other problems, such as lag.  DestroyObject(), like any other instruction, can contribute to a TMI.

 

What appears to be the issue is that when you are removing an element from the list, you are reindexing the entire list to preserve order and reference.  What might be better is to allow null entries when an element is removed.  Another method, similar to Lightfoot's, is to use items for each list element, but instead of storing a tag for reference, simply reference the newest to oldest element by GetFirst/NextItemInInventory().  However, reindexing usually should not cause more instruction problems than one or two loops through every object would.



#6
meaglyn

meaglyn
  • Members
  • 808 messages

Hhm... WhiZard and Lightfoot, I think I read that differently. I don't believe TR has a list implementation but is asking about switching to one. I suspect he's hitting TMI because he's doing something in the loop that is messing up the iterator state. Although, as Proleric said, DestroyObject by itself would generally not do that. Maybe he's got nested GetFirst/NextItem or something.



#7
WhiZard

WhiZard
  • Members
  • 1 204 messages

Yes, DestroyObject() does not interfere with the loop iterator, but CopyObject() and similar commands would.  If TR is trying to replace a number of objects in a loop, this could be the problem.



#8
Tarot Redhand

Tarot Redhand
  • Members
  • 2 677 messages

Thanks guys. While I fixed the TMI issue (adding a DelayCommand cured it), it looks like I'll have to go down the pseudo stack route anyway. Here is the problem section of code.

 

void ChangeWindow(object oOldObject, string sNewLetter)
{
    string sResRef;
    location lHere;
 
    sResRef = GetResRef(oOldObject);
    lHere = GetLocation(oOldObject);
    sResRef = GetStringLeft(sResRef, GetStringLength(sResRef) - 1) + sNewLetter;
 
    if(sResRef == sOldResRef)
        return;
    else
        sOldResRef = sResRef;
 
    debug(sResRef);
    DestroyObject(oOldObject);
    DelayCommand(0.1f, CreateObjectVoid(OBJECT_TYPE_PLACEABLE, sResRef, lHere));
}
 
void ChangeAllWindows(string sNewLetter)
{
    int iIndex = 0;
    string sLastChar;
    object oPC = GetFirstPC();
    object oWindow = GetNearestObjectByTag("ES_SG_Win", oPC, iIndex);
 
    while(oWindow != OBJECT_INVALID)
    {
        sLastChar = GetStringRight(GetResRef(oWindow), 1);
 
        if(sLastChar != sNewLetter)
            ChangeWindow(oWindow, sNewLetter);
 
        iIndex++;
        oWindow = GetNearestObjectByTag("ES_SG_Win", oPC, iIndex);
    }
}
 

It is part of an OnEnter script for the management of a set of placeables that I have created. This section of code is supposed to

 

step through all of the placeables in an area and identify those that need changing and change each one as identified.

(changing involves destroying a placeable and creating a new one in the old ones exact location)

 

Unfortunately the above code is only mostly successful. This section of code

 

    if(sResRef == sOldResRef)
        return;
    else
        sOldResRef = sResRef;
 
was my last attempt to fix it and it almost worked too. sOldResRef is a global variable used to hold the value of a variable from the previous call to the function. Cest la vie.
 
As usual all comments welcome.
 
TR
 
PS What the bleeps happened to the code/end code thingy. It is now wiping out everything after it. This is the third attempt to write this reply! 


#9
meaglyn

meaglyn
  • Members
  • 808 messages

Yep. It's the create of the new object that was messing it up.  If  "if(sLastChar != sNewLetter)" is enough to be sure you want to change that window then you could just do the delaycommand on the call the to subroutine instead.

  if(sLastChar != sNewLetter)
            DelayCommand(0.1, ChangeWindow(oWindow, sNewLetter));

That way you would not need the delay in the subroutine and the creation of the new object would not interfere with your iterator. And you could probably remove the sOldResRef hackery.



#10
Tarot Redhand

Tarot Redhand
  • Members
  • 2 677 messages
Well I have now rewritten the above section of code so that it now uses a pseudo list. However, I still have the same problem - one of the placeables is not being updated. There is some weird bug in the following code.
 
void ChangeAllWindows(string sNewLetter)
{
    int iIndex = 1;
    int iMatchedCount = 0;
    string sLastChar;
    object oPC = GetFirstPC();
    object oWindow = GetNearestObjectByTag("ES_SG_Win", oPC, iIndex);
 
    while(oWindow != OBJECT_INVALID)
    {
        sLastChar = GetStringRight(GetResRef(oWindow), 1);
 
        debug("Index = " + IntToString(iIndex) + " - " + GetResRef(oWindow) + " - " + GetName(oWindow));
 
        if(sLastChar != sNewLetter)
        {
            PushDetails(oWindow, sNewLetter, iMatchedCount);
            iMatchedCount++;
        }
        iIndex++;
        oWindow = GetNearestObjectByTag("ES_SG_Win", oPC, iIndex);
    }
 
2 things to note. iIndex is initialised to 1 in preparation for it to be used as parameter nNth as per the lexicon. I have a very small debug function that sends messages to both the pc and the log file. The call to it in the above code snippet produced the following output (with 1 instance of each of the 27 placeables in the area). 
 
Index =  1 - es_sg_window05l - ES SG Window 05 (Kynareth) Light
Index =  2 - es_sg_window06l - ES SG Window 06 (Mara) Light
Index =  3 - es_sg_window04l - ES SG Window 04 (Julianos) Light
Index =  4 - es_sg_window05e - ES SG Window 05 (Kynareth) Dusk
Index =  5 - es_sg_window06e - ES SG Window 06 (Mara) Dusk
Index =  6 - es_sg_window07l - ES SG Window 07 (Stendarr) Light
Index =  7 - es_sg_window04e - ES SG Window 04 (Julianos) Dusk
Index =  8 - es_sg_window03l - ES SG Window 03 (Dibella) Light
Index =  9 - es_sg_window07e - ES SG Window 07 (Stendarr) Dusk
Index = 10 - es_sg_window05d - ES SG Window 05 (Kynareth) Dark
Index = 11 - es_sg_window06d - ES SG Window 06 (Mara) Dark
Index = 12 - es_sg_window06d - ES SG Window 06 (Mara) Dark
Index = 13 - es_sg_window04d - ES SG Window 04 (Julianos) Dark
Index = 14 - es_sg_window08l - ES SG Window 08 (Talos) Light
Index = 15 - es_sg_window07d - ES SG Window 07 (Stendarr) Dark
Index = 16 - es_sg_window02l - ES SG Window 02 (Arkay) Light
Index = 17 - es_sg_window08e - ES SG Window 08 (Talos) Dusk
Index = 18 - es_sg_window03d - ES SG Window 03 (Dibella) Dark
Index = 19 - es_sg_window02e - ES SG Window 02 (Arkay) Dusk
Index = 20 - es_sg_window08d - ES SG Window 08 (Talos) Dark
Index = 21 - es_sg_window09l - ES SG Window 09 (Zenithar) Light
Index = 22 - es_sg_window09e - ES SG Window 09 (Zenithar) Dusk
Index = 23 - es_sg_window02d - ES SG Window 02 (Arkay) Dark
Index = 24 - es_sg_window01l - ES SG Window 01 (Akatosh) Light
Index = 25 - es_sg_window01e - ES SG Window 01 (Akatosh) Dusk
Index = 26 - es_sg_window09d - ES SG Window 09 (Zenithar) Dark
Index = 27 - es_sg_window01d - ES SG Window 01 (Akatosh) Dark
 
The question is "What is causing the duplication on the 11th and 12th passes and why is the object with the resref of es_sg_window03e being missed? I cannot see any errors in the blueprints either. TBH I am stumped. Ideas anyone?
 
TR


#11
Proleric

Proleric
  • Members
  • 2 352 messages

What is PushDetails doing? What is the value of sNewLetter in this example?

 

I guess you've checked that you haven't accidentally duplicated es_sg_window06d instead of placing es_sg_window03e?



#12
Tarot Redhand

Tarot Redhand
  • Members
  • 2 677 messages

Here is the code for PushDetails and a little function it calls

 

string TwoStringDigits(int iInValue)
{
    string sReturnMe;
 
    if(iInValue > 9)
        sReturnMe = IntToString(iInValue);
    else
        sReturnMe = "0" + IntToString(iInValue);
 
    return sReturnMe;
}
 
void PushDetails(object oOldObject, string sNewLetter, int iIndex)
{
    string sVarO = "oStore" + TwoStringDigits(iIndex);
    string sVarL = "lStore" + TwoStringDigits(iIndex);
    string sVarR = "rStore" + TwoStringDigits(iIndex);
    string sResRef;
    location lHere;
 
    SetLocalObject(OBJECT_SELF, sVarO, oOldObject);
 
    lHere = GetLocation(oOldObject);
    SetLocalLocation(OBJECT_SELF, sVarL, lHere);
 
    sResRef = GetResRef(oOldObject);
    sResRef = GetStringLeft(sResRef, GetStringLength(sResRef) - 1) + sNewLetter;
    SetLocalString(OBJECT_SELF, sVarR, sResRef);
}
 
It simply stores the details of the object itself, its location and the resref of the object that will replace it  later on, in a pseudo list without doing anything to the object at this point.
 
The value of sNewLetter in this example is "d", although I positioned the call to debug so that I could check every object that GetNearestObjectByTag found.  
 
I have double checked through all the blueprint resrefs but I thought that the toolset wouldn't allow 2 blueprints with the same resref anyway.
 
TR


#13
meaglyn

meaglyn
  • Members
  • 808 messages

It won't. Proleric is asking if you have accidentally painted es_sg_window06d twice in the area. And not painted es_sg_window03e at all.



#14
Proleric

Proleric
  • Members
  • 2 352 messages

True, you can't have two blueprints with the same resref, but you can accidentally have two instances of the same blueprint in an area.

 

Edit : what meaglyn said.



#15
Tarot Redhand

Tarot Redhand
  • Members
  • 2 677 messages

Nope, Just 1 instance of each one - I double checked that at the same time.

 

TR



#16
Tarot Redhand

Tarot Redhand
  • Members
  • 2 677 messages

Having given up on being able to get this working 100%, I decided that with all the work I had put into it, I would post it (including a warning about the mystery bug) anyway. Having decided this, I set about taking some screen shots. As I tried and failed to get all the placeables into a single picture, I enlarged the area and moved the PC's start position a little (~ 2 - 3 game meters) further away from the placeables. I first got my shot in the toolset. I then pressed F9 for an in-game shot. To cut a long story (of experimentation) short... Having left the debug code in place I discovered that it now appears to be working 100%. It would appear that either standing this close (see pic)

 

Too%20Close.gif

 

was the problem or the area was too small. In other words the blueprints are fine, and the script is ok as well. My guess is that there is something in the algorithm of the GetNearestObjectByTag function that was falling down. Has anyone else come across this before?

 

BTW, if you are interested in what this project is look in this thread.

 

TR



#17
WhiZard

WhiZard
  • Members
  • 1 204 messages

Bear in mind that a script that creates objects can interfere with the indexing for looping through GetNearestObjectByTag().



#18
Tarot Redhand

Tarot Redhand
  • Members
  • 2 677 messages

Which is why I used a pseudo stack and 3 separate loops for detection, destruction and creation. What you see above is only the detection part of the code, that stores the information on the pseudo stack.

 

TR



#19
Proleric

Proleric
  • Members
  • 2 352 messages
As a rule-of-thumb, I avoid distance functions if there is an alternative e.g. GetObjectByTag.

The only science behind this is that distance is miscalculated on entry (because creature position is null for an appreciable time) and slightly variable when creatures get bumped, so I can't explain your result.

Some will say that blanket avoidance is unnecessary, which is true if you have a memory for fine detail, but as a more conceptual thinker I prefer to keep it simple.

#20
WhiZard

WhiZard
  • Members
  • 1 204 messages

The only thing GetNearestObjectByTag() would be efficient for is if you had different effects for the closest, next closest, etc.  If you are just looking for what is within a certain distance, then GetObjectByTag() accompanied by a distance check will be much more efficient processing-wise and does not run the risk of duplicating when an object is created.



#21
Tarot Redhand

Tarot Redhand
  • Members
  • 2 677 messages

I chose GetNearestObjectByTag  because it simplified the script due to it only getting matching objects in the current area.

 

TR



#22
WhiZard

WhiZard
  • Members
  • 1 204 messages

 

The question is "What is causing the duplication on the 11th and 12th passes and why is the object with the resref of es_sg_window03e being missed? I cannot see any errors in the blueprints either. TBH I am stumped. Ideas anyone?
 
TR

 

 

I have another thought. How repeatable was this duplication and omission?  Could it be a rare occurrence when the two objects specified were the exact same distance from the PC's location?



#23
Tarot Redhand

Tarot Redhand
  • Members
  • 2 677 messages

That is my guess. But I have now finished with the project that it was for. I have learned some new techniques and if anyone comes across this problem in that project I will offer what help I can. Finally, if I have a similar project in the future I will avoid GetNearestObjectByTag  and use GetObjectByTag  even if it does mean I have another level of complexity in my code.

 

TR