Author Topic: Mathematical problem with script.  (Read 348 times)

Legacy_Fester Pot

  • Hero Member
  • *****
  • Posts: 1698
  • Karma: +0/-0
Mathematical problem with script.
« on: July 16, 2013, 08:33:57 pm »


               This little script is driving me nuts. It works, but its calculation is incorrect.

An item with the tag GP_MED_10GP can be placed into a container to make a donation. If the donation equals a specific amount, the player receives a spell cast upon them. Only the special tagged item will result in this, but there are more than one a player can find.

In testing with only one special item, it's creating gold on the container as if there is more than one special item placed into the container.

With the script posted below, and my testing, it creates 30gp on the container, rather than 10gp as I am expecting.

void main()
{

object oPC = GetFirstPC();

object oContainer = OBJECT_SELF;

   // ITEM WORTH 10GP
   string nGPM1 = "GP_MED_10GP";  // TAG OF SPECIAL ITEM

   object oItems = GetFirstItemInInventory(oContainer);

   int nNumberM1 = 0; // STACK count

   while(GetIsObjectValid(oItems))
      {

       if(GetTag(oItems) == nGPM1)
        {

          SetPlotFlag(oItems,FALSE);

          nNumberM1 += GetNumStackedItems(oItems);

          string sString = IntToString(nNumberM1);

          DestroyObject(oItems);

          SendMessageToPC(GetFirstPC(), "DEBUG: Fired 10*nNumberM1.");

          SendMessageToPC(GetFirstPC(), "This container found this many stacked items: " +sString);

          //GiveGoldToCreature(oContainer,10*nNumberM1); // PLACEABLE CONTAINER, SO THIS DOES NOT WORK

          CreateItemOnObject("NW_IT_GOLD001", oContainer, 10*nNumberM1);
          // VALUE OF SPECIAL ITEMS IS 10GP, SO CREATE GOLD AMOUNT BASED ON STACK SIZE
          // 1 = 10gp
          // 2 = 20gp

         }

        oItems=GetNextItemInInventory(oContainer);

    } // While loop

if ( (GetGold(oContainer) >=1) && (GetGold(oContainer) <=8) )
{
AssignCommand(OBJECT_SELF, SpeakString("You make a small donation."));
DestroyObject(oItems);
return;
}

if ( (GetGold(oContainer) >=9) && (GetGold(oContainer) <=19) )
{

AssignCommand(OBJECT_SELF, ActionCastSpellAtObject(SPELL_DIVINE_FAVOR, oPC, METAMAGIC_ANY, TRUE, 2, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
AssignCommand(OBJECT_SELF, SpeakString("You make a small donation (>=9 && <=19)."));
return;
}

if ( (GetGold(oContainer) >=20) && (GetGold(oContainer) <=29) )
{

AssignCommand(OBJECT_SELF, ActionCastSpellAtObject(SPELL_BULLS_STRENGTH, oPC, METAMAGIC_ANY, TRUE, 2, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
AssignCommand(OBJECT_SELF, SpeakString("You make a small donation."));
return;
}

if (GetGold(oContainer) >=30)
{

AssignCommand(OBJECT_SELF, ActionCastSpellAtObject(SPELL_DIVINE_POWER, oPC, METAMAGIC_ANY, TRUE, 2, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
AssignCommand(OBJECT_SELF, SpeakString("You make a small donation."));
return;
}

}


Could someone point out what crazy mistake I've made? The debug notes I put in says it finds 2 stacked items, even though there is only one item placed into the container.

Thanks,
FP!
               
               

               


                     Modifié par Fester Pot, 16 juillet 2013 - 07:37 .
                     
                  


            

Legacy_AndarianTD

  • Hero Member
  • *****
  • Posts: 725
  • Karma: +0/-0
Mathematical problem with script.
« Reply #1 on: July 16, 2013, 09:07:39 pm »


               Could you be disrupting the behavior of the item inventory iterator (GetFirstItemInInventory / GetNextItemInInventory) by creating / destroying items in the inventory in mid-loop? I'm not sure how robust the iterator is supposed to be in the face of a dynamically changing inventory. You could check that by putting a short delay on the create and destroy commands, so that the loop finishes before any of the creates or destroys is actually processed.
               
               

               


                     Modifié par AndarianTD, 16 juillet 2013 - 08:43 .
                     
                  


            

Legacy_Squatting Monk

  • Hero Member
  • *****
  • Posts: 776
  • Karma: +0/-0
Mathematical problem with script.
« Reply #2 on: July 16, 2013, 09:29:48 pm »


               The problem is that the gold is created during the loop. When it is, the gold becomes the first item,  causing your specially tagged item to be the second one (so it gets picked up again). So the loop runs twice. The first time, it creates 10gp. The second time, it creates 20 gp (since it's detected a total of 2 items).

To solve this, create the gold after the loop has finished.

EDIT: Andarian is right, too. Deleting the item mid-loop is probably what's making the gold get listed as the first item.

EDIT 2: Here's the loop fixed up for you:


    while (GetIsObjectValid(oItems))
    {
        if (GetTag(oItems) == sGPM1)
        {
            SetPlotFlag(oItems, FALSE);
            nNumberM1 += GetNumStackedItems(oItems);

            string sString = IntToString(nNumberM1);

            DestroyObject(oItems);

            SendMessageToPC(GetFirstPC(), "DEBUG: Fired 10*nNumberM1.");
            SendMessageToPC(GetFirstPC(), "This container found this many stacked items: " +sString);
        }

        oItems=GetNextItemInInventory(oContainer);
    } // While loop

    if (nNumberM1 > 0)
    {
        //GiveGoldToCreature(oContainer,10*nNumberM1); // PLACEABLE CONTAINER, SO THIS DOES NOT WORK

        CreateItemOnObject("NW_IT_GOLD001", oContainer, 10*nNumberM1);
        // VALUE OF SPECIAL ITEMS IS 10GP, SO CREATE GOLD AMOUNT BASED ON STACK SIZE
        // 1 = 10gp
        // 2 = 20gp
    }

               
               

               


                     Modifié par Squatting Monk, 16 juillet 2013 - 08:38 .
                     
                  


            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
Mathematical problem with script.
« Reply #3 on: July 17, 2013, 01:53:41 am »


               

Squatting Monk wrote...

EDIT: Andarian is right, too. Deleting the item mid-loop is probably what's making the gold get listed as the first item.


I don't think that's a problem. The destroyobject doesn't destroy the object until the after the script completes. We do
that kind of loop all the time to, say, clear out the PCs inventory...

In fact, that's why your fix can help. The item doesn't get destroyed right away so can be picked up in the list still.
               
               

               
            

Legacy_Axe_Murderer

  • Full Member
  • ***
  • Posts: 199
  • Karma: +0/-0
Mathematical problem with script.
« Reply #4 on: July 17, 2013, 08:31:52 am »


               Use DelayCommand on the CreateItemOnObject call within the while loop (0.1 duration ought to do it). That should solve your issue I think. But when you do, all the GetGoldInContainer functions will start exhibiting inaccuracies because the gold won't go in until after the script ends. What you really need is to keep track in a counter of how much gold is put in the chest within the loop instead of actually creating it there. Then you can make one call to create it all after the loop ends and before the GetGoldInContainer's go off. Or just use your counter directly instead of calling those functions (assuming there was no gold in the chest to begin with, if so just initialize the counter using -GetGoldInContainer before the loop). Something like Monk posted.

Also, oItems must be invalid after the loop, or else it would not have ended. Thus the DestroyObject( oItems ) call in the first if-stmt following the loop is having no effect. So whatever it was you were thinking needed to get destroyed there isn't getting whacked. Might as well delete that line if you don't need it.

Another thing, gold heaps in inventory have a maximum stack limit. As long as you are working with small stack sized donations, which it looks like you're doing, then you'll be ok. But there will come a point where 10 *nNumberM1 will necessarily exceed that limit. The result will be that you will only get one heap of maximum size in the chest and lose all the excess.

If you wanted to handle that possibility, you could make a function that wraps the create call so it will loop and create multiple heaps in the chest until the full amount has been deposited there. Something like:

void CreateGoldInChest( object oChest, int nGold )
{ while( nGold > 0 )
    nGold -= GetItemStackSize( CreateItemOnObject( "NW_IT_GOLD001", oChest, nGold ));
}

Then DelayCommand( 0.1, CreateGoldInChest( oContainer, 10 *nNumberM1 )) in your loop.
But it looks like you probably need not worry about it.
               
               

               


                     Modifié par Axe_Murderer, 17 juillet 2013 - 10:07 .
                     
                  


            

Legacy_Squatting Monk

  • Hero Member
  • *****
  • Posts: 776
  • Karma: +0/-0
Mathematical problem with script.
« Reply #5 on: July 17, 2013, 11:29:27 am »


               Axe is back! Woohoo!
               
               

               
            

Legacy_Fester Pot

  • Hero Member
  • *****
  • Posts: 1698
  • Karma: +0/-0
Mathematical problem with script.
« Reply #6 on: July 18, 2013, 01:02:15 am »


               Alright good fellows, I've done as guided to do and things aren't all wonky anymore.

Thanks for the assistance!

FP!