Author Topic: 2 Bugs within DeterminCombatRound  (Read 2032 times)

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #15 on: October 29, 2011, 05:37:48 am »


               You might have to make a wrapper function for GetNearestCreature to more easily allow checking for plot flag in the targetting routine.
 The changes withing the vanilla OnPysicalAttacked and OnDamaged routines are easier, just put an exception in so that if the attacker/damager is plot flagged don't switch targets.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #16 on: October 29, 2011, 08:54:45 am »


               

ShaDoOoW wrote...

i already seen this code in other topic and re-scripted it myself and it didnt worked and it cant by default because there is an target switch in OnAttack script, to make it work and ignore BBotD there must be some code added either to this event or into DetermineCombatRound (which is where I did it) and run bkAcquireTarget if the oIntruder is BBotD, that way it works.

It works just fine, as I've said. You need to include it in nw_i0_generic and x0_inc_generic, which contain, among other things, subfunctions of DCR and DCR iteself, which uses those functions. It replaces the following:


// This function returns the target for this combat round.
// Normally, this will be the same target as the last round.
// The only time this changes is if the target is gone/killed
// or they are in dying mode.
object bkAcquireTarget()
{
    object oLastTarget = GetAttackTarget();

    // * for now no 'target switching' other
    // * than what occurs in the OnDamaged and OnPerceived events
    // * (I may roll their functionality into this function
    if (GetIsObjectValid(oLastTarget) == TRUE
        && !GetAssociateState(NW_ASC_MODE_DYING, oLastTarget))
    {
        return oLastTarget;
    } else {
        oLastTarget = ChooseNewTarget();
    }

    // * If no valid target it means no enemies are nearby, resume normal behavior
    if (! GetIsObjectValid(oLastTarget)) {
        // * henchmen should only equip weapons based on what you tell them
        if (GetIsObjectValid(GetMaster(OBJECT_SELF)) == FALSE) {
            // * if no ranged weapon this function should
            // * automatically be melee weapon
            ActionEquipMostDamagingRanged();
        }
    }

    // valid or not, return it
    return oLastTarget;
}


// Choose a new nearby target. Target must be an enemy, perceived,
// and not in dying mode. If possible, we first target members of
// a class we hate.
object ChooseNewTarget(object oTargetToExclude=OBJECT_INVALID)
{
    int nHatedclass = GetLocalInt(OBJECT_SELF, "NW_L_BEHAVIOUR1") - 1;

    // * if the object has no hated class, then assign it
    // * a random one.
    // * NOTE: classes are off-by-one
    if (nHatedclass == -1)
    {
        bkSetupBehavior(1);
        nHatedclass = GetLocalInt(OBJECT_SELF, "NW_L_BEHAVIOUR1") - 1;
    }

    //MyPrintString("I hate " + IntToString(nHatedclass));

    // * First try to attack the class you hate the most
    object oTarget = GetNearestPerceivedEnemy(OBJECT_SELF, 1,
                                              CREATURE_TYPE_class,
                                              nHatedclass);

    if (GetIsObjectValid(oTarget) && !GetAssociateState(NW_ASC_MODE_DYING, oTarget))
        return oTarget;

    // If we didn't find one with the criteria, look
    // for a nearby one
    // * Keep looking until we find a perceived target that
    // * isn't in dying mode
    oTarget = GetNearestPerceivedEnemy();
    int nNth = 1;
    while (GetIsObjectValid(oTarget)
           && GetAssociateState(NW_ASC_MODE_DYING, oTarget))
    {
        nNth++;
        oTarget = GetNearestPerceivedEnemy(OBJECT_SELF, nNth);
    }

    return oTarget;
}

Past that, just recompile all your scripts using DCR and it should work just fine. DCR is called from default5, so I don't see why you would single out that event.

Funky
               
               

               


                     Modifié par FunkySwerve, 29 octobre 2011 - 07:57 .
                     
                  


            

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #17 on: October 29, 2011, 10:43:30 am »


               

while (GetIsObjectValid(oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
oSource, nNth++,
CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN,
CREATURE_TYPE_DOES_NOT_HAVE_SPELL_EFFECT, SPELL_ETHEREALNESS)))


I'm curious about that part of your script, Funky.  Doesn't NWN remove a creature from your visibility list if they have etherealness cast and you don't have the counter for it?
  It seems just looking at it, as if you're needlessly checking the spell effects, potentially of every creature in the area, if the etherealness effect is already handled under the visibility check for PERCEPTION_SEEN.
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #18 on: October 29, 2011, 11:08:30 am »


               Funky, I did that exactly and then I went playtesting. I had an creature that couldnt see me (i was outside of his visual range) and then I cast BBoD into his vision range, this creature then attacked BBoD and doesnt changed target even after I came closer. Then I tried to come closer in order this npc would attack me and then I casted BBoD into his path, the BBoD then attacked him and since I was farther than BBoD creature switched target in OnAttack script to BBoD and then bkAcquireTarget WASNT called at all!

I tracked down every bits and then I rewritten it. I took the BBoD part from bkAcquireTarget and put it into DetermineCombatRound where I checked if oIntruder (object passed into function which if valid has greater priority than new target from bkAcquireTarget) is BBoD and in case if it is && creature is smart enough, then she try to dispell it immediately or if no such talent possible, then it switch target if there is a no-BBoD target in his vision range. That worked just fine and without problem.

Failed.Bard wrote...

while (GetIsObjectValid(oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
oSource, nNth++,
CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN,
CREATURE_TYPE_DOES_NOT_HAVE_SPELL_EFFECT, SPELL_ETHEREALNESS)))


I'm curious about that part of your script, Funky.  Doesn't NWN remove a creature from your visibility list if they have etherealness cast and you don't have the counter for it?
  It seems just looking at it, as if you're needlessly checking the spell effects, potentially of every creature in the area, if the etherealness effect is already handled under the visibility check for PERCEPTION_SEEN.

It does but HG might use some plugin that reenables it etc. A safety check is never wrong even almost unused. This two extra parameters have no effect on CPU/TMI anything. If you will go into default AI you will see lots of things like this, while I could also rewriten them, I rather kept them in order to make the changes I did easier to compare with default version.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #19 on: October 29, 2011, 05:16:10 pm »


               

Failed.Bard wrote...

while (GetIsObjectValid(oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
oSource, nNth++,
CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN,
CREATURE_TYPE_DOES_NOT_HAVE_SPELL_EFFECT, SPELL_ETHEREALNESS)))


I'm curious about that part of your script, Funky.  Doesn't NWN remove a creature from your visibility list if they have etherealness cast and you don't have the counter for it?
  It seems just looking at it, as if you're needlessly checking the spell effects, potentially of every creature in the area, if the etherealness effect is already handled under the visibility check for PERCEPTION_SEEN.


It's to remedy bugs in behavior caused by engine-level errors, which would cause creatures to be attacked inappropriately - see, e.g., this bug report. The vision code is some of the buggiest - it's actually home to one of the few remaining crash bugs in nwserver. There's also an issue with attacks of opportunity against Sanc'd targets, though I don't remember if they're at all related.

Funky
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #20 on: October 29, 2011, 06:55:00 pm »


               

ShaDoOoW wrote...

Funky, I did that exactly and then I went playtesting. I had an creature that couldnt see me (i was outside of his visual range) and then I cast BBoD into his vision range, this creature then attacked BBoD and doesnt changed target even after I came closer. Then I tried to come closer in order this npc would attack me and then I casted BBoD into his path, the BBoD then attacked him and since I was farther than BBoD creature switched target in OnAttack script to BBoD and then bkAcquireTarget WASNT called at all!


You're right about it not working when oIntruder is passed into DCR. I guess we haven't noticed any issues from that due to style of play (not doing what you did in your test) and pre-existing code in events where that happens. Here, for example, is our dialogue script:


#include "nw_i0_generic"

void main () {
    object oSelf = OBJECT_SELF;

    if (GetSpawnInCondition(NW_FLAG_ON_DIALOGUE_EVENT))
        SignalEvent(oSelf, EventUserDefined(EVENT_DIALOGUE));

    int nUptime = GetLocalInt(GetModule(), "uptime");
    int nEvtime = GetLocalInt(oSelf, "AI_Uptime_Dialogue");

    if (nUptime > 0 && nUptime == nEvtime)
        return;

    SetLocalInt(oSelf, "AI_Uptime_Dialogue", nUptime);
    DelayCommand(1.0, SetLocalInt(oSelf, "AI_Uptime_Dialogue", -1));

    object oSpeaker = GetLastSpeaker();

    if (!GetIsPC(oSpeaker) && GetListenPatternNumber() == 1) {
        int bDCR = FALSE;
        object oTarget, oHostile = GetLastHostileActor(oSpeaker);

        if (GetIsObjectValid(oHostile)) {
            int bIgnoreBBoD = Random(4);

            oTarget = GetAttemptedAttackTarget();
            if (!GetIsObjectValid(oTarget) || (bIgnoreBBoD && GetResRef(oTarget) == "x2_s_bblade")) {
                oTarget = GetAttemptedSpellTarget();

                if (!GetIsObjectValid(oTarget) || (bIgnoreBBoD && GetResRef(oTarget) == "x2_s_bblade")) {
                    bDCR    = TRUE;
                    oTarget = GetLastHostileActor(oSelf);

                    if (!GetIsObjectValid(oTarget) || (bIgnoreBBoD && GetResRef(oTarget) == "x2_s_bblade"))
                        SetLastHostileActor(oSelf, oHostile);
                }
            }
        } else {
            if (!GetIsObjectValid(GetAttemptedAttackTarget()) &&
                !GetIsObjectValid(GetAttemptedSpellTarget()))
                bDCR = TRUE;
        }

        if (bDCR)
            DetermineCombatRound(oHostile);
    }
}



And, searching by that resref, we have a LOT of pre-existing bbod-handling code:



abo_endcombat (245):     if (GetLocalInt(oSelf, "NoBBoD") && GetResRef(oPC) == "x2_s_bblade") {
aby_boss_heart (426):                     if (GetIsObjectValid(oTarget = GetNearestObjectByTag("x2_s_bblade", oSelf)))
aby_boss_heart (487):                     if (GetIsObjectValid(oTarget = GetNearestObjectByTag("x2_s_bblade", oSelf)))
aby_boss_heart (527):                     if (GetIsObjectValid(oTarget = GetNearestObjectByTag("x2_s_bblade", oSelf)))
aby_boss_heart (589):                     if (GetIsObjectValid(oTarget = GetNearestObjectByTag("x2_s_bblade", oSelf)))
aby_boss_heart (720):                     if (GetIsObjectValid(oTarget = GetNearestObjectByTag("x2_s_bblade", oSelf)))
aby_endcombat (147):     } else if (GetResRef(oTarget) == "x2_s_bblade") {
aby_endcombat (874):     if (GetLocalInt(oSelf, "NoBBoD") && GetResRef(oPC) == "x2_s_bblade")
aby_endcombat (3394):                 if (GetResRef(oPC) == "x2_s_bblade") {
aby_enter (1683):     if (GetIsDM(oPC) || !(GetIsPC(oPC) || GetIsPC(GetMaster(oPC))) || GetResRef(oPC) == "x2_s_bblade")
aby_onhit (206):                 if (GetResRef(oTarget) == "x2_s_bblade")
dul_endcombat (178):     if (GetLocalInt(oSelf, "NoBBoD") && GetResRef(oPC) == "x2_s_bblade")
ele_endcombat (105):     if (GetLocalInt(oSelf, "NoBBoD") && GetResRef(oPC) == "x2_s_bblade")
ely_endcombat (156):             if (GetResRef(oPC) == "x2_s_bblade") {
fey_endcombat (82):     if (GetLocalInt(oSelf, "NoBBoD") && GetResRef(oPC) == "x2_s_bblade")
fky_3endcmb_hell (366):                 if (GetResRef(oPC) == "x2_s_bblade")
fky_3endcmb_hell (439):                 if (GetResRef(oPC) == "x2_s_bblade")
fky_3endcmb_hell (501):                 if (GetResRef(oPC) == "x2_s_bblade")
fky_3endcmb_hell (621):                 if (GetResRef(oPC) == "x2_s_bblade") {
fky_3endcmb_hell (1544):                 if (GetResRef(oPC) == "x2_s_bblade") {
fky_ai_4dialogue (28):             if (!GetIsObjectValid(oTarget) || (bIgnoreBBoD && GetResRef(oTarget) == "x2_s_bblade")) {
fky_ai_4dialogue (31):                 if (!GetIsObjectValid(oTarget) || (bIgnoreBBoD && GetResRef(oTarget) == "x2_s_bblade")) {
fky_ai_4dialogue (35):                     if (!GetIsObjectValid(oTarget) || (bIgnoreBBoD && GetResRef(oTarget) == "x2_s_bblade"))
fky_ai_target (13):         } else if (GetResRef(oLastTarget) == "x2_s_bblade") {//skip bbod if boss or smart enough, unless caster with Mord
fky_ai_target (162):         if (sRes == "x2_s_bblade") {//skip bbod if boss or smart enough, unless caster with Mord
gen_endcombat (185):     if (GetLocalInt(ei.self, "NoBBoD") && GetResRef(ei.opp) == "x2_s_bblade")
had_enter (274):     if (GetIsDM(oPC) || GetLocalInt(oPC, "PlotSummons") || GetResRef(oPC) == "x2_s_bblade")
had_enter (298):     if (GetIsDM(oPC) || !(GetIsPC(oPC) || GetIsPC(GetMaster(oPC))) || GetResRef(oPC) == "x2_s_bblade")
hellenter (96):         if (GetResRef(oPC) == "x2_s_bblade") {
hellenter (131):     if (GetIsDM(oPC) || !(GetIsPC(oPC) || GetIsPC(GetMaster(oPC))) || GetResRef(oPC) == "x2_s_bblade")
hell_boss_spawn (18):     AssignCommand(oArea, DelayCommand(6.0, DestroyObjectsByTag(oNear, "x2_s_bblade")));
hg_area_exit (154):     if (GetResRef(oPC) != "x2_s_bblade" && !GetLocalInt(oPC, "FKY_CHAT_INVULN") && !GetLocalInt(oPC, "PlotSummons"))
hg_attack_inc (769):         if (GetResRef(oTarget) == "x2_s_bblade") {
hiv_endcombat (167):     if (GetLocalInt(oSelf, "NoBBoD") && GetResRef(oPC) == "x2_s_bblade")
loc_endcombat (112):     if (GetLocalInt(oSelf, "NoBBoD") && GetResRef(oPC) == "x2_s_bblade")
loc_endcombat (403):             } else if (GetResRef(oPC) == "x2_s_bblade") {
mol_endcombat (153):     if (GetLocalInt(oSelf, "NoBBoD") && GetResRef(oPC) == "x2_s_bblade") {
toy_endcombat (124):     if (GetLocalInt(oSelf, "NoBBoD") && GetResRef(oPC) == "x2_s_bblade")
toy_onhit (17):             if (GetResRef(oTarget) == "x2_s_bblade")
x2_inc_spellhook (223):         if(GetTag(oAssoc) == "x2_s_bblade") // black blade of disaster
x2_s0_blckblde (10):     if (GetResRef(oSummon) == "x2_s_bblade") {
x2_s0_blckblde (35):         effect eSummon = EffectSummonCreature("x2_s_bblade");

Nothing in onattacked, though. I guess that scenario just isn't coming up much. Surveying the default ai scripts, it looks like it's occurences are limited to onspellcastat, ondialogue, and ondamaged.


nw_c2_default2 (39):            DetermineCombatRound();
nw_c2_default2 (58):                     DetermineCombatRound();
nw_c2_default3 (23):        DetermineCombatRound();
nw_c2_default3 (28):     }else{DetermineCombatRound();}
nw_c2_default5 (39):                             DetermineCombatRound();
nw_c2_default6 (72):                         DetermineCombatRound();
nw_c2_default6 (79):                                 ActionDoCommand(DetermineCombatRound());
nw_c2_default6 (102):                         DetermineCombatRound(oAttacker);
nw_c2_default8 (28):             DetermineCombatRound(oTarget);
nw_c2_default9 (64):     DetermineCombatRound();
nw_c2_defaultb (21):                 DetermineCombatRound(oCaster);
And, surveying my ai, which is mostly based on the default (with the x2 revisions), it comes up even less:

fky_3endcmb_hell (2368):         DetermineCombatRound();
fky_ai_2percept (27):         DetermineCombatRound();
fky_ai_4dialogue (46):             DetermineCombatRound(oHostile);
fky_ai_5attacked (15):         DetermineCombatRound();
fky_ai_5spellat (18):         DetermineCombatRound();
fky_ai_6ondamage (380):         DetermineCombatRound(oDamager);
fky_ai_6ondamage (385):                 ActionDoCommand(DetermineCombatRound());
fky_ai_9spawn (37):     DetermineCombatRound();
though my ai includes mw_c2_default8 (ondisturbed), which does pass oIntruder.

I have to wonder if simply removing the check for oIntruder's validity and running bkAcq all the time isn't the best solution. Here's my onspellcastat (replacing defaultb) , by way of comparison:


#include "nw_i0_generic"

void main() {
    object oSelf = OBJECT_SELF;

    if (GetSpawnInCondition(NW_FLAG_SPELL_CAST_AT_EVENT))
        SignalEvent(oSelf, EventUserDefined(EVENT_SPELL_CAST_AT));

    if (!GetLastSpellHarmful())
        return;

    if (GetIsObjectValid(GetAttemptedAttackTarget()) || GetIsObjectValid(GetAttemptedSpellTarget()))
        return;

    object oHostile = GetLastSpellCaster();

    if (GetArea(oHostile) == GetArea(oSelf)) {
        DetermineCombatRound();

        if (!GetIsObjectValid(GetLastHostileActor(oSelf)))
            SetLastHostileActor(oSelf, oHostile);
        SpeakString("NW_I_WAS_ATTACKED", TALKVOLUME_SILENT_TALK);
    }
}
}

Thoughts? I ran your test with spawing out of sight and got the same result, by the way, save that the creature switched to me as soon as I attacked it with a sling, despite being much further away than the BBoD. The script events on the creature in question:

OnBlocked=nw_c2_defaulte
OnDamaged=fky_ai_6ondamage
OnDeath=fky_ai_7death
OnConversation=fky_ai_4dialogue
OnDisturbed=nw_c2_default8
OnCombatRoundEnd=fky_3endcmb_hell
OnHeartbeat=fky_1heart_hellb
OnPhysicalAttacked=fky_ai_5attacked
OnPerception=fky_ai_2percept
OnRested=nw_c2_defaulta
OnSpawn=fky_ai_9spawn
OnSpellCast=fky_ai_5spellat
OnUserDefined=


Kinda tempted to throw in debug to see just how often DCR fires in various scenarios.

Funky
               
               

               


                     Modifié par FunkySwerve, 29 octobre 2011 - 05:56 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #21 on: October 29, 2011, 10:25:20 pm »


               Yea when I attacked, creature tried to switch me but since BBoD attacked again in the same time or few seconds later creature stayed. Except that what Im trying to handle is a tactic used on my PW where you summon BBoD outside of corner, lure hard creatures to attack it, then you tell BBoD to come to you into your corner, creatures come with it. And then you tell BBoD to stop and you can easily avoid fighting and run away. And unlock door and continue further inside dungeon.

Or at least not allowing to use BBoD to acquire some time to heal/resurrect another player etc in situations like very tough boss who just killed tank.
               
               

               


                     Modifié par ShaDoOoW, 29 octobre 2011 - 09:25 .
                     
                  


            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #22 on: October 30, 2011, 04:31:32 am »


               

ShaDoOoW wrote...
And unlock door and continue further inside dungeon.

This part doesn't require AI scripting.  You can simply cause the door (I assume it is plot) to relock itself and send a message to the players (like "You will need to kill the creatures in this area before proceeding onward") if the guardian creatures are still alive.
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #23 on: October 30, 2011, 05:27:28 am »


               I dont want them to kill guardians.
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #24 on: November 09, 2011, 02:53:16 pm »


               

Failed.Bard wrote...

while (GetIsObjectValid(oTarget = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
oSource, nNth++,
CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN,
CREATURE_TYPE_DOES_NOT_HAVE_SPELL_EFFECT, SPELL_ETHEREALNESS)))


I'm curious about that part of your script, Funky.  Doesn't NWN remove a creature from your visibility list if they have etherealness cast and you don't have the counter for it?
  It seems just looking at it, as if you're needlessly checking the spell effects, potentially of every creature in the area, if the etherealness effect is already handled under the visibility check for PERCEPTION_SEEN.

BTW, I reliazed this later but then I implemented it into my patch and latest test confirmed it works.

This code actually fixes inv+GS bug thought only for monsters as player will be able to attack you anyway. Also sometimes the GS works only for the monsters who are around when you cast it. This is also fixed by this snippet.

To resurrect the thread, I have found at least three new issues with invisibility code within AI.
1) once the invisibility is cast, creature cast defensively although that her target might get see insivisibility/true seeing already
2) casters with HIPS don't cast spells and attacks melee
3) creature which entered invisibility tries to heal herself no matter that she is missing only 1% of hitpoints and she may waste a fullheal trying to cure this loss
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #25 on: November 12, 2011, 06:09:23 pm »


               

ShaDoOoW wrote...

1) once the invisibility is cast, creature cast defensively although that her target might get see insivisibility/true seeing already
2) casters with HIPS don't cast spells and attacks melee
3) creature which entered invisibility tries to heal herself no matter that she is missing only 1% of hitpoints and she may waste a fullheal trying to cure this loss

I fixed all three issues + I rewritted TalentHealingSpell which is whole wrong or at least insuitable and works only by chance. This allows casters with HIPS to HIPS-cast, and it allows undead to use any other inflict spells not only harm-self.

I will add these fixes in next patch 1.70 version, sorry but I wont waste whole day to explain how I did it, as its not so hard to find out if you download Patch's builders resources where are also all changed include scripts and then you can compare it via tools like BeyondCompare to the 1.69 default.
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #26 on: November 14, 2011, 06:51:59 pm »


               

ShaDoOoW wrote...

But if your clerics just stop attacking I think that its not this issue, this issue will send casters into melee in case that AI found talent that is useless in current situation (drown spell against constructs etc). If they stop attack, then they most probably used some feat that has bad impact on AI (horse menu) or cannot be used in certain situation (rapid shot with melee weapon) as I already pointed in my first posts.


It was the issue. We were getting this with pretty much every active combat freat, including the Power Attack on the Summon Creature III Dire Wold, which I used as my test bed. See bug report here. I traced the ai all the way down from its DCR call, and found what I believed to be the problem call, near the end of Talent MeleeAttack in x0_i0_talent. This edit made the Dire Wolf no longer hang after power attacking:


    if(nDiff < 10)
    {
        ClearActions(CLEAR_X0_I0_TALENT_MeleeAttack1);
        // * this function will call the BK function
        EquipAppropriateWeapons(oTarget);
        tUse = GetCreatureTalent(TALENT_CATEGORY_HARMFUL_MELEE,
                                     GetCRMax());
        //MyPrintString("Melee Talent Valid = "+ IntToString(GetIsTalentValid(tUse)));
        //MyPrintString("Feat ID = " + IntToString(GetIdFromTalent(tUse)));

        if(GetIsTalentValid(tUse)
           && VerifyDisarm(tUse, oTarget)
           && VerifyCombatMeleeTalent(tUse, oTarget))
        {
            //MyPrintString("TalentMeleeAttack: Talent Use Successful");
            // February 6 2003: Did not have a clear all actions before it
            [color="#ff0000"]if (bkTalentFilter(tUse, oTarget))//edited from simple command to conditional, made TRUE return contingent on it as test fix for ai 11/14/2011 Fky[/color]
                [color="#ff0000"]return TRUE;[/color]
        }
        else
        {
            //MyPrintString("TalentMeleeAttack Successful Exit");
            WrapperActionAttack(oTarget);
            return TRUE;
        }
    }
    else
    {
        //MyPrintString("TalentMeleeAttack Successful Exit");
        ClearActions(CLEAR_X0_I0_TALENT_MeleeAttack2);
        // * this function will call the BK function
        EquipAppropriateWeapons(oTarget);
        WrapperActionAttack(oTarget);
        return TRUE;
    }

    //MyPrintString("TALENT MELEE ATTACK FAILURE EXIT - THIS IS VERY BAD");
    return FALSE;
}

My assumption is that you're correct about ALL the calls to that function - whoever made those  edits seemed to think the return within it would return the functions it was called in as well - strange, to say the least. I wanted to keep my edits mimimalistic, however, as I don't think many of the calls are creating problems at present (if it ain't broke, don't fix it, etc.), despite being fundamentally incorrect.

Would you share your modified TalenHeal code, please? I'm considering a full ai rewrite at this point.

Thanks,
Funky
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #27 on: November 15, 2011, 02:01:20 am »


               very strange, I will look inside this again, but last time I looked I saw the potentional issue with exact modification you did and I adviced to keep this exact bit of code default to prevent TMI.

Because this is actually the last bktalentfilter call in the whole AI function. Now (in default code without modification) when the feat is bad, the bktalent filter tells the creature to perform ActionAttack. So unless the code is rewritten to the if(bkTalentFilter) return TRUE; the AI stops in the moment there is something inappropriate and perform basic melee attack. If we add the if, the talent where the bkfilter is used won't return TRUE and next talent tries bkFilter again, canceling ordered attack and trying next talent.

So, if your creature has no spells, then she shouldn't stop attacking unless she tries to use inappropriate feat. If the feat itself is removed by the AI code as inappropriate, AI performs single attack.

If you add the last if into this line in TalentMeleeAttack, in case the feat will be inappropriate, the ChooseTalent function will not return 99 but 1 instead and the DetermineCombatRound function start recurse with GetNearestCreature. This will be very probably the same target and very probably the AI code might repeate whole process with the same result -> if(nDiff < 10), random talent that is inappropriate because there are no other talents. (This might happen in these cases: creature with stunning fist or sap when target is undead/construct/elemental/vermin or smite when target is neutral/opposite alignment, and no other combat feats, the getcreaturetalent will always return the same feat which will circle AI)

Doesn't this wolf also have rapid shot feat? As thats what can cause this behavior and what I fixed in my modification as well. If there is rapid shot feat, the GetCreatureTalent can and will return it, bktalentfilter returns TRUE but creature that try to use it gets stuck. The same behavior might happen also with AA arrow feats (if you grant them 22category) if the creature doesnt use bow and ALSO when the feat triggers spell which has Area TargetType bit (I modified imbue arrow to be fired on area, PC can use it well, but monsters get stuck - so I didnt allowed AI to use it).

But, as I said I look again. I already realized one issue that still remains in my latest AI code (AA arrow feats might cause creature gets stuck if she hold inappropriate weaponEDIT: its ok) so there might be more of this.

Anyway if you could write which feats (if custom then I need to know if there is Category set and if the spell that gets triggered has Area bit in TargetType) this wolf have, it would make whole debugging easier.

You can get into my TalentHealingSelf code (I didn't changed the second healing talent, as I can also recommend the policy "don't change what works") in THIS archive under spell scripts and includes. If you will want some explanation why I did coded it like that, tell me, I had to adapt on some talent function related issues.
               
               

               


                     Modifié par ShaDoOoW, 15 novembre 2011 - 02:21 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #28 on: November 15, 2011, 04:18:42 am »


               The wolf is the default Summon Creature III dire wolf - one of the reasons I picked it to test was that you would be able to look at the exact critter yourself. If for some reason you can't, I'm happy to do a feat listing on it.

The script was default before I modified it, but I can't be certain if other scripts haven't been edited. One of our devs mentioned something about altering cases where IS_BUSY flags weren't getting turned off appropriately. I'll try to find out more.

Funky
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
2 Bugs within DeterminCombatRound
« Reply #29 on: November 15, 2011, 04:51:50 am »


               He'd thought he made an edit to x2's henchman ai include, but it was unmodded for me. Here's our dm_info_feat spew on the wolf:

[CHAT WINDOW TEXT] [Mon Nov 14 21:49:04] [Server]
Name: Summoned Dire Wolf
Resref: nw_s_wolfdire  Tag: NW_S_WOLFDIRE  Object: 13b44
Env: Town of Ascension (townofascension) [77.22, 43.82, 0.00 | 0]
Master: Refulgent Grace (FunkySwerve)

Cleave
Improved Critical (creature)
Keen Sense
Knockdown
Power Attack
Skill Focus (Hide)
Skill Focus (Listen)
Skill Focus (Spot)
Toughness
Weapon Focus (creature)
Weapon Proficiency (creature)
Weapon Specialization (creature)


Funky