Jump to content

If I Have A Victimguid Why Don't I Have A Victim?


Recommended Posts

Posted

Greetings,

Putting the finishing touches on my improved PetAI when I ran into a strange issue.

In PetAI::UpdateAI I have the following code to determine if a pet on "stay" can

still attack it's victim:

if(i_victimGuid)
{
       // Pet has a target
       if( !i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY) )
               bShouldAttack = true;
       else if( i_pet.canReachWithAttack(i_pet.getVictim()))
               bShouldAttack = true;
}

You'll notice this calls the following function (which I did not write):

bool Unit::canReachWithAttack(Unit *pVictim) const
{
       assert(pVictim);
       float reach = GetFloatValue(UNIT_FIELD_COMBATREACH);
       if( reach <= 0.0f )
               reach = 1.0f;
       return IsWithinDistInMap(pVictim, reach);
}

It is in this function that pVictim is sometimes NULL (0x00000000). What I'm confused

about is if my pet has a victimGuid and a victim (i_pet.GetVictim()) then why / how does

pVictim go out of scope in "canReachWithAttack"?

I have a fix for it but I'm just not seeing why it's happening in the first place. I'm thinking

it's because the victim has died and my pet's victimGuid hasn't been cleared but the victim

no longer exists.

I plan to replace the "assert(pVictim)" with an "if(pVictim)". The function will still work and it

won't halt the server if the victim is invalid.

Your input would be appreciated.

Thanks

Posted

Ok well this is confusion for many Script developers so I explain here:

Units are removed from all threat lists the moment they die. For example

void UpdateAI(uint32 diff)
{
       if (!m_creature->getVictim())
                return;

    m_creature->getVictim()->DealDamage(m_creature->getVictim(), m_creature->getVictim()->GetMaxHealth(), bla bla bla);

    m_creature->getVictim()->GetHealth();    //CRASH!
}

getVictim() returned NULL at the line that says crash. This is because Victim is no longer valid. 99.9% of the the time you will not have a problem with this because all spells are actually casted on NEXT world update tick. The only exception to this is Triggered spells. Triggered spells are cast right then and there.

Example

void UpdateAI(uint32 diff)
{
       if (!m_creature->getVictim())
                return;

    m_creature->CastSpell(m_creature->getVictim(), 5, false);

    m_creature->getVictim()->GetHealth();    //Safe
}

void UpdateAI(uint32 diff)
{
       if (!m_creature->getVictim())
                return;

    m_creature->CastSpell(m_creature->getVictim(), 5, true);

    m_creature->getVictim()->GetHealth();    //Crash
}

The method the original MaNGOS developers chose was to simply assert if the target was somehow removed from threat list while CreatureAI::Update() was still in progress. Personally with ScriptDev2 I have had zero problems with simply adding Null pointer checks.

On related note this crash also occurs with ThreatList Iteration if unit is removed from threatlist during the iteration but that is another story for another time.

Anyway as far as I am concerned I'd say simply remove the assert and add a Null pointer check (if (!Victim) return;).

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. Privacy Policy Terms of Use