Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Commit

Permalink
Core/AI: further fine tuning to TotemAI:
Browse files Browse the repository at this point in the history
* expose spellInfo and spell range to derived classes
* put target searching while not being able to find one behind a timer to reduce overzealous object accessor usage
  • Loading branch information
Ovahlord committed Oct 25, 2023
1 parent 891f545 commit dc691de
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 22 deletions.
25 changes: 18 additions & 7 deletions src/server/game/AI/CoreAI/TotemAI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include "GridNotifiersImpl.h"
#include "CellImpl.h"

static constexpr uint32 TargetSearchInterval = 400; // let's mimic one retail AI tick

int32 TotemAI::Permissible(Creature const* creature)
{
if (creature->IsSummon() && creature->IsTotem())
Expand All @@ -32,7 +34,7 @@ int32 TotemAI::Permissible(Creature const* creature)
return PERMIT_BASE_NO;
}

TotemAI::TotemAI(Creature* creature) : NullCreatureAI(creature), _spellInfo(nullptr), _spellRange(0.f), _victimGUID() { }
TotemAI::TotemAI(Creature* creature) : NullCreatureAI(creature), _spellInfo(nullptr), _spellRange(0.f), _targetSearchTimer(0) { }

void TotemAI::JustAppeared()
{
Expand All @@ -48,7 +50,7 @@ void TotemAI::JustAppeared()
_spellRange = _spellInfo->GetMaxRange(false);
}

void TotemAI::UpdateAI(uint32 /*diff*/)
void TotemAI::UpdateAI(uint32 diff)
{
// Passive aura totems do not need any further casting
if (!_spellInfo || _spellInfo->IsPassive())
Expand All @@ -57,12 +59,21 @@ void TotemAI::UpdateAI(uint32 /*diff*/)
if (!me->IsAlive() || me->IsNonMeleeSpellCast(false))
return;

Unit* target = SelectTotemTarget();
if (_targetSearchTimer <= diff)
{
Unit* target = SelectTotemTarget();

if ((!target && !_spellInfo->NeedsExplicitUnitTarget()) || target)
me->CastSpell(target, _spellInfo->Id);
if (target)
_victimGUID = target->GetGUID();
if ((!target && !_spellInfo->NeedsExplicitUnitTarget()) || target)
{
me->CastSpell(target, _spellInfo->Id);
if (target)
_victimGUID = target->GetGUID();
}
else
_targetSearchTimer = TargetSearchInterval;
}
else
_targetSearchTimer -= diff;
}

Unit* TotemAI::SelectTotemTarget()
Expand Down
5 changes: 3 additions & 2 deletions src/server/game/AI/CoreAI/TotemAI.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ class Totem;
class TC_GAME_API TotemAI : public NullCreatureAI
{
public:

explicit TotemAI(Creature* creature);

void AttackStart(Unit* /*victim*/) override {}
Expand All @@ -40,9 +39,11 @@ class TC_GAME_API TotemAI : public NullCreatureAI

static int32 Permissible(Creature const* creature);

private:
protected:
SpellInfo const* _spellInfo;
float _spellRange;
private:
ObjectGuid _victimGUID;
uint32 _targetSearchTimer;
};
#endif
54 changes: 43 additions & 11 deletions src/server/game/Entities/Unit/Unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2428,11 +2428,14 @@ float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) con
}
else
{
chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
if (skillDifference <= 10)
chance = 5.f + skillDifference * 0.1f;
else
chance = 6.f + (skillDifference - 10) * 0.1f;
if (!victim->IsTotem())
{
chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
if (skillDifference <= 10)
chance = 5.f + skillDifference * 0.1f;
else
chance = 6.f + (skillDifference - 10) * 0.1f;
}
}

// Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE
Expand Down Expand Up @@ -2474,7 +2477,7 @@ float Unit::GetUnitParryChance(WeaponAttackType attType, Unit const* victim) con
else
{
// Allow parries for creatures only if it's not a totem, does have a virtual item equipped and does not have CREATURE_FLAG_EXTRA_NO_PARRY
if (victim->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0) || victim->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1) &&
if (!victim->IsTotem() && (victim->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0) || victim->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1)) &&
!(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY))
{
chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
Expand Down Expand Up @@ -2536,7 +2539,7 @@ float Unit::GetUnitBlockChance(Unit const* victim) const
}
else
{
if (!(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK))
if (!victim->IsTotem() && !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK))
{
chance = 5.0f;
chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT);
Expand Down Expand Up @@ -3236,6 +3239,13 @@ void Unit::_UnapplyAura(AuraApplicationMap::iterator& i, AuraRemoveFlags removeM
// all effect mustn't be applied
ASSERT(!aurApp->GetEffectMask());

// Remove totem at next update if totem loses its aura
if (aurApp->GetRemoveMode().HasFlag(AuraRemoveFlags::Expired) && GetTypeId() == TYPEID_UNIT && IsTotem())
{
if (ToTotem()->GetSpell() == aura->GetId() && ToTotem()->GetTotemType() == TOTEM_PASSIVE)
ToTotem()->setDeathState(JUST_DIED);
}

// Remove aurastates only if were not found
if (!auraStateFound)
ModifyAuraState(auraState, false);
Expand Down Expand Up @@ -5731,9 +5741,13 @@ void Unit::SetCharm(Unit* charm, bool apply)
// Hook for OnHeal Event
sScriptMgr->OnHeal(healer, victim, (uint32&)gain);

if (healer)
Unit* unit = healer;
if (healer && healer->GetTypeId() == TYPEID_UNIT && healer->IsTotem())
unit = healer->GetOwner();

if (unit)
{
if (Player* player = healer->ToPlayer())
if (Player* player = unit->ToPlayer())
{
if (!healInfo.GetSpellInfo() || !healInfo.GetSpellInfo()->HasAttribute(SPELL_ATTR7_DO_NOT_COUNT_FOR_PVP_SCOREBOARD))
if (Battleground* bg = player->GetBattleground())
Expand Down Expand Up @@ -5917,6 +5931,11 @@ int32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, int3
return int32(std::max(float(pdamage + DoneTotal) * DoneTotalMod, 0.0f));
}

// Totems get their bonus damage from their owner
if (IsCreature() && IsTotem())
if (Unit* creator = ObjectAccessor::GetUnit(*this, GetCreatorGUID()))
return creator->SpellDamageBonusDone(victim, spellProto, pdamage, damagetype, effIndex, stack, spell, aurEff);

DoneTotalMod = SpellDamagePctDone(victim, spellProto, damagetype);

// done scripted mod (take it from owner)
Expand Down Expand Up @@ -6067,6 +6086,10 @@ float Unit::SpellDamagePctDone(Unit* victim, SpellInfo const* spellProto, Damage
if (spellProto->HasAttribute(SPELL_ATTR6_IGNORE_CASTER_DAMAGE_MODIFIERS))
return 1.0f;

// For totems pct done mods are calculated when its calculation is run on the player in SpellDamageBonusDone.
if (GetTypeId() == TYPEID_UNIT && IsTotem())
return 1.0f;

// Done total percent damage auras
float DoneTotalMod = 1.0f;

Expand Down Expand Up @@ -6664,6 +6687,11 @@ float Unit::SpellCritChanceTaken(Unit const* caster, SpellInfo const* spellInfo,

int32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, int32 healamount, DamageEffectType damagetype, uint8 effIndex, uint32 stack /*= 1*/, Spell* spell /*= nullptr*/, AuraEffect const* aurEff /*= nullptr*/) const
{
// For totems get healing bonus from owner (statue isn't totem in fact)
if (GetTypeId() == TYPEID_UNIT && IsTotem())
if (Unit* creator = ObjectAccessor::GetUnit(*this, GetCreatorGUID()))
return creator->SpellHealingBonusDone(victim, spellProto, healamount, damagetype, effIndex, stack, spell, aurEff);

// Some spells don't benefit from done mods
if (spellProto->HasAttribute(SPELL_ATTR3_IGNORE_CASTER_MODIFIERS))
return healamount;
Expand Down Expand Up @@ -6774,6 +6802,10 @@ int32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, int

float Unit::SpellHealingPctDone(Unit* victim, SpellInfo const* spellProto) const
{
// For totems pct done mods are calculated when its calculation is run on the player in SpellHealingBonusDone.
if (GetTypeId() == TYPEID_UNIT && IsTotem())
return 1.0f;

// No bonus healing for potion spells
if (spellProto->SpellFamilyName == SPELLFAMILY_POTION)
return 1.0f;
Expand Down Expand Up @@ -9983,7 +10015,7 @@ Unit* Unit::SelectNearbyTarget(Unit* exclude, float dist) const
// remove not LoS targets
for (std::list<Unit*>::iterator tIter = targets.begin(); tIter != targets.end();)
{
if (!IsWithinLOSInMap(*tIter) || (*tIter)->IsSpiritService() || (*tIter)->IsCritter())
if (!IsWithinLOSInMap(*tIter) || (*tIter)->IsTotem() || (*tIter)->IsSpiritService() || (*tIter)->IsCritter())
targets.erase(tIter++);
else
++tIter;
Expand Down Expand Up @@ -10471,7 +10503,7 @@ void Unit::PlayOneShotAnimKitId(uint16 animKitId)
}

// Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim
if (attacker && attacker->IsGuardian())
if (attacker && (attacker->IsPet() || attacker->IsTotem()))
{
// proc only once for victim
if (Unit* owner = attacker->GetOwner())
Expand Down
4 changes: 2 additions & 2 deletions src/server/scripts/World/npcs_special.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3084,9 +3084,9 @@ struct npc_shaman_searing_totem : public TotemAI
{
// The Searing Totem prefers targets afflicted by its creator's Flameshock and Stormstrike ability
std::list<Unit*> targets;
Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, Coalesce<Unit const>(me->GetCreator(), me), 15.f);
Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, Coalesce<Unit const>(me->GetCreator(), me), _spellRange);
Trinity::UnitListSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> searcher(me, targets, u_check);
Cell::VisitAllObjects(me, searcher, 15.f);
Cell::VisitAllObjects(me, searcher, _spellRange);

if (!targets.empty())
{
Expand Down

0 comments on commit dc691de

Please sign in to comment.