Author Topic: Animal Companion AI Scripts  (Read 1272 times)

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #15 on: October 29, 2013, 08:14:29 pm »


               Yes, in the Animal Companion summon script I put

SetLocalString(oAnimal,"X2_SPECIAL_COMBAT_AI_SCRIPT", "sh_commandcheck" );

(oAnimal has been verified to be the correct target)

and then "sh_commandcheck" is a new script that is


void main()
{
         // Don't do anything if we have have been recently commanded
         if (GetLocalInt(OBJECT_SELF, "commandstatus"))
         {
                 SetLocalInt(OBJECT_SELF,"X2_SPECIAL_COMBAT_AI_SCRIPT_OK",TRUE);
         }
}

With the idea that as long as commandstatus is considered TRUE that the rest of the AI would be suppressed.
               
               

               


                     Modifié par MagicalMaster, 29 octobre 2013 - 08:15 .
                     
                  


            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #16 on: October 29, 2013, 08:28:50 pm »


               I'm not sure what ShaDoOoW is seeing as more efficient in using the AI script.  Your original idea to suppress the AI by disabling event calls if certain criteria is met decreases overhead so long as DetermineCombatRound() isn't called elsewhere (besides from any PC initiated commands).  Waiting for the special AI to trigger means more exposure to scripted checks and changing tasks.  On the other hand, DetermineCombatRound() does do a good job at clearing the action queue, however, this is not always enforced if it is cut off with special AI scripts.
               
               

               
            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #17 on: October 29, 2013, 10:22:27 pm »


               

WhiZard wrote...

Waiting for the special AI to trigger means more exposure to scripted checks and changing tasks.  On the other hand, DetermineCombatRound() does do a good job at clearing the action queue, however, this is not always enforced if it is cut off with special AI scripts.


Yeah, my code literally looks something like this...

// On Attacked/Perceived/Whatever

int main()
{
    if (Busy)
    {
        return;
    }

     <Actual AI stuff for the script goes here>
}

Also, whenever my command script gives a new order it clears all actions first to prevent any kind of action queue pile-up.  And then the AI proceeds normally after a set time passes (9 seconds by default) without the PC giving an order.
               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #18 on: October 30, 2013, 12:40:00 am »


                AS far as the sp.AI script.   I think I would try something like this.   


#include "x2_inc_switches"
#include "nw_i0_generic"
void main()
{
    if ( GetCreatureOverrideAIScriptTarget()== OBJECT_INVALID)
    {
        string sScript =GetLocalString(OBJECT_SELF,CREATURE_VAR_CUSTOM_AISCRIPT);
        SetCreatureOverrideAIScript(OBJECT_SELF,"");
        DetermineCombatRound(GetAttackTarget());
        SetCreatureOverrideAIScript(OBJECT_SELF,sScript);
        SetCreatureOverrideAIScriptFinished();
    }
}

To answer the question on efficiency,  I believe you method is more efficient.  I myself however like everything going through a single combat function.  But it does not sound like you are blocking the combat AI anyway. You are just blocking the other events from firing.
               
               

               
            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #19 on: October 30, 2013, 01:10:25 am »


               Yeah, I'm blocking events that fire the combat AI, in effect.  Basically telling the mobs being controlled "Do NOT think for yourselves."

Also, any ideas on what might seem to randomly cause this or good ways to debug it?  This never happens without an animal companion but I *sometimes* see it with one -- it doesn't even happen every fight.

'Posted

Trying to check exactly when AC5 (OnAttacked/OnDamaged) is firing to see if it gets triggered a bunch in a split second or something but figured I could ask here as well.
               
               

               


                     Modifié par MagicalMaster, 30 octobre 2013 - 01:13 .
                     
                  


            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #20 on: October 30, 2013, 01:51:04 am »


               Okay...it appears somehow AC5 is getting triggered like 50 times simultaneously or something.  Pet entered an acid cloud of boss and instantly claimed AC5 was triggered like 15 times (cloud only ticks once per second) and I got the instructions error.

Here's the code running for the acid cloud ("sh_blank" is simply blank and that's what the persistent AoE scripts are designated so there's no persistent AoE scripts running):

void Spit2(location lTarget, int nDur, int nDam)
{
    int nAdjust;

    object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 4.0, lTarget);
    while (GetIsObjectValid(oTarget))
    {
        if (GetIsEnemy(oTarget))
        {
            nAdjust = AdjustDamage(nDam, oTarget);

            ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nAdjust), oTarget);
        }
        oTarget = GetNextObjectInShape(SHAPE_SPHERE, 4.0, lTarget);
    }

    if (!GetIsDead(OBJECT_SELF) && nDur > 0)
    {
        nDam = (3*nDam)/2;
        if (nDam > 1000)
         {
            nDam = 1000;
        }
        DelayCommand(1.0, Spit2(lTarget, --nDur, nDam));
    }

}

Seems pretty basic -- loop through a 4.0 meter circle, apply damage if it's an enemy, adjust the damage for the next tick, then call itself again as long there is duration remaining.
               
               

               


                     Modifié par MagicalMaster, 30 octobre 2013 - 01:52 .
                     
                  


            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #21 on: October 30, 2013, 04:40:48 am »


               Okay.  So I added some debugging strings (again) to try to get to the root of this (and have been unsuccessful so far).

Ths is the acid cloud code.
void Spit2(location lTarget, int nDur, int nDam)
{
    int nAdjust;

    object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 4.0, lTarget);
    while (GetIsObjectValid(oTarget))
    {
        if (GetIsEnemy(oTarget))
        {
            nAdjust = AdjustDamage(nDam, oTarget);

            AssignCommand(oTarget, SpeakString("Took damage from cloud"));
            ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nAdjust), oTarget);
        }
        oTarget = GetNextObjectInShape(SHAPE_SPHERE, 4.0, lTarget);
    }


    if (!GetIsDead(OBJECT_SELF) && nDur > 0)
    {
        nDam = (3*nDam)/2;
        if (nDam > 1000)
        {
            nDam = 1000;
        }

        DelayCommand(1.0, Spit2(lTarget, --nDur, nDam));
    }

}

This is the code at the top of AC5:

void main()
{
    // Don't do anything if we have have been recently commanded
    if (GetLocalInt(OBJECT_SELF, "commandstatus"))
    {
        return;
    }

    AdjustLocalInt(OBJECT_SELF, "test", 1);
    SpeakString("OnAttacked: " + IntToString(GetLocalInt(OBJECT_SELF, "test")));

So, in essence, every time the creature gets attacked or damage it says "On Attacked: x" where "x" is the number of times this event has occcurred.

It took me several tries to reproduce this bug and I'm still not sure what's triggering it -- doesn't happen every time.'Posted

'Posted

You can see how the cloud damaged the bear 14 times *instantly* before the AssignCommand could even make the bear say it took damage form the cloud (then it overloaded).

But how in the world does that loop hit ANYTHING 14+ times instantly?
               
               

               


                     Modifié par MagicalMaster, 30 octobre 2013 - 04:42 .
                     
                  


            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #22 on: October 30, 2013, 02:34:56 pm »


               I have seen this phenoma happen many times with standard creatures.  I have had wall of flame, cause up to six applications of damage to a creature that was trying to attack me, as his AI made him retreat slightly back each time he hit the AoE and took damage.  Do a check in the AoE scripts to see how many times the OnEnter and OnExit scripts are running.
               
               

               
            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #23 on: October 30, 2013, 03:40:44 pm »


               There *are* no OnEnter or OnExit scripts -- they're simply blank (using the conveniently named "sh_blank" script).  Literally just

int main (void)
{
        return;
}

The only thing actually doing damage is the script I posted which does an AoE check each second -- so even if the creature enters and leaves the area the area is only checked every second.  I'll try putting a debug string in blank when I can to see if anything is weird but the only script DOING anything is the one posted.
               
               

               


                     Modifié par MagicalMaster, 30 octobre 2013 - 03:41 .
                     
                  


            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #24 on: October 30, 2013, 05:37:40 pm »


               Yeah, it's definitely not sh_blank -- that's only being called a few times and exactly when expected.

Half thinking I should just set a local int on the first loop and then remove it with a second loop to see if that helps.

But I don't see why I should *need* to do this.
               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #25 on: October 30, 2013, 05:43:57 pm »


               

MagicalMaster wrote...

Yeah, it's definitely not sh_blank -- that's only being called a few times and exactly when expected.

Half thinking I should just set a local int on the first loop and then remove it with a second loop to see if that helps.

But I don't see why I should *need* to do this.


Could you post the contents within the void main() declaration which references the function in "sh_vrocklord" and in "sh_hungerer" as well as noting what causes the void main() in those scripts to fire?  It seems odd to have a scripting error both in the web aplication and in the bear's AI.
               
               

               


                     Modifié par WhiZard, 30 octobre 2013 - 05:47 .
                     
                  


            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #26 on: October 30, 2013, 05:58:29 pm »


               Going to just do "sh_hungerer" for now because I've had better luck reproducing the bug with the acid cloud but I still can't do it most of the time.  And it doesn't seem odd to me -- somehow an infinite loop seems to be occurring (bear gets hit with acid damage 14+ times in a split second) which both causes an error in the script controlling the acid damage and the script firing when the acid damage is taken.

Editing/modifying some stuff which isn't relevant to the acid cloud itself...

#include "sh_library"
#include "nw_i0_2q4luskan"

void Hungerer();

void main()
{
    AdjustLocalInt(OBJECT_SELF, "siphon", 1);
    AdjustLocalInt(OBJECT_SELF, "global", 1);
    DelayCommand(6.0, AdjustLocalInt(OBJECT_SELF, "global", -1));
    SpeakString("*Chitter chitter*");
    Hungerer();
}

void Hungerer()
{
    [Makes sure boss isn't dead]
    else
    {
        [Do some stuff on the first call]
        else if (!GetLocalInt(OBJECT_SELF, "global") && !GetLocalInt(OBJECT_SELF, "spit"))
        {
            Spit(25, 6.0, 18.0);
        }

        DelayCommand(1.0, Hungerer());
        AdjustLocalInt(OBJECT_SELF, "timer", 1);
    }
}

"sh_library" has the Spit functions which are...

//// - Spits acid on the ground, dealing increasing damage to anything in
////   it every second for 30 seconds
void Spit(int nDam, float fGlobal, float fCooldown)
{
    AdjustLocalInt(OBJECT_SELF, "spit", 1);
    DelayCommand(fCooldown, AdjustLocalInt(OBJECT_SELF, "spit", -1));
    AdjustLocalInt(OBJECT_SELF, "global", 1);
    DelayCommand(fGlobal, AdjustLocalInt(OBJECT_SELF, "global", -1));

     // Get the nearest PC within 30 meters
    object oTarget = GetNearestPCTarget();

    if (!GetIsObjectValid(oTarget))
    {
        return;
    }

    SpeakString("*" + GetName(OBJECT_SELF, TRUE) + " SPITS ACID*");

    location lTarget = GetLocation(oTarget);

    ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectAreaOfEffect(AOE_PER_FOGACID, "sh_blank", "sh_blank", "sh_blank"), lTarget, 36.0);

    Spit2(lTarget, 36, nDam);
}

void Spit2(location lTarget, int nDur, int nDam)
{
    int nAdjust;

    object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 4.0, lTarget);
    while (GetIsObjectValid(oTarget))
    {
        if (GetIsEnemy(oTarget))
        {
            nAdjust = AdjustDamage(nDam, oTarget);

            AssignCommand(oTarget, SpeakString("Took damage from cloud"));
            ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nAdjust), oTarget);
        }
        oTarget = GetNextObjectInShape(SHAPE_SPHERE, 4.0, lTarget);
    }

    if (!GetIsDead(OBJECT_SELF) && nDur > 0)
    {
        nDam = (3*nDam)/2;
        if (nDam > 1000)
        {
            nDam = 1000;
        }

        DelayCommand(1.0, Spit2(lTarget, --nDur, nDam));
    }

}

I can post the whole code if desired but that's the stuff that has any relation to the acid cloud.
               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #27 on: October 30, 2013, 06:07:02 pm »


               If I am reading this right the main declaration is causing a call each second to spit, spit calls spit2, and spit2 repeats itself every second.  So each consecutive second gives an increased number of times spit2 is called.  This does not look like an effect overload, but rather a script execution overload (too many scripts running at the same time, so the game cuts-off all scripts).
               
               

               


                     Modifié par WhiZard, 30 octobre 2013 - 06:08 .
                     
                  


            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #28 on: October 30, 2013, 06:22:29 pm »


               No, because the "global" variable prevents any ability from occurring more than once every six seconds and the "spit" variable prevents spit from occurring more than once every 18 seconds.

The Hungerer function checks every second whether it is allowed to use an ability but most of the time it cannot.

On top of that, at most 36 spit2 calls could be occurring at the same time and they're all within the same script (note that this doesn't actually happen and at most 2 spit2 calls are active).  Other scripts continue to function as well after the sh_hungerer and AC5 scripts overload.

Here's a video showing what actually happens in the fight:


               
               

               


                     Modifié par MagicalMaster, 30 octobre 2013 - 06:29 .
                     
                  


            

Legacy_MagicalMaster

  • Hero Member
  • *****
  • Posts: 2712
  • Karma: +0/-0
Animal Companion AI Scripts
« Reply #29 on: October 30, 2013, 06:33:44 pm »


               Also, I want to point out that until it gets hit 14 times instantly and overloading that it is only getting hit once per second -- and it only sometimes bugs out.  This has also NEVER happened without the animal companion there -- works perfectly otherwise.
               
               

               


                     Modifié par MagicalMaster, 30 octobre 2013 - 06:49 .