diff options
| author | treeston <treeston.mmoc@gmail.com> | 2016-01-13 18:38:54 +0100 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2016-03-29 19:36:05 +0200 |
| commit | 009cabb4b9d60a02ed2f219127c0df293ea96580 (patch) | |
| tree | 8cf5a56aeee002e94329c339a1a934dae451b868 /src/server/game/Entities/Creature | |
| parent | 21371075ff4efc82a8567dfd604137577f4400e7 (diff) | |
Merge branch '3.3.5-spellfacing' into 3.3.5-base (PR #15641)
(cherry picked from commit 233297c5c8619af96e2aab4fa5ef6d205b5dc18a)
Diffstat (limited to 'src/server/game/Entities/Creature')
| -rw-r--r-- | src/server/game/Entities/Creature/Creature.cpp | 117 | ||||
| -rw-r--r-- | src/server/game/Entities/Creature/Creature.h | 6 |
2 files changed, 103 insertions, 20 deletions
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 47ef22b2df0..4c1e7af2615 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -203,6 +203,7 @@ m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo( TriggerJustRespawned = false; m_isTempWorldObject = false; _focusSpell = NULL; + _focusDelay = 0; } Creature::~Creature() @@ -1596,7 +1597,9 @@ void Creature::setDeathState(DeathState s) if (sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY) || isWorldBoss()) SaveRespawnTime(); - SetTarget(ObjectGuid::Empty); // remove target selection in any cases (can be set at aura remove in Unit::setDeathState) + ReleaseFocus(); // remove spellcast focus (this also clears unit target) + SetTarget(ObjectGuid::Empty); // drop target - dead mobs shouldn't ever target things + SetUInt64Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0); // if creature is mounted on a virtual mount, remove it at death @@ -2613,39 +2616,117 @@ void Creature::SetDisplayId(uint32 modelId) void Creature::SetTarget(ObjectGuid const& guid) { - if (!_focusSpell) + if (!IsFocusing()) SetGuidValue(UNIT_FIELD_TARGET, guid); } -void Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target) +bool Creature::FocusTarget(Spell const* focusSpell, WorldObject const* target) { // already focused if (_focusSpell) - return; + return false; + + if ((!target || target == this) && !focusSpell->GetCastTime()) // instant cast, untargeted (or self-targeted) spell doesn't need any facing updates + return false; _focusSpell = focusSpell; - SetGuidValue(UNIT_FIELD_TARGET, target->GetGUID()); - if (focusSpell->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_DONT_TURN_DURING_CAST) - AddUnitState(UNIT_STATE_ROTATING); - // Set serverside orientation if needed (needs to be after attribute check) - SetInFront(target); + // "instant" creature casts that require re-targeting will be delayed by a short moment to prevent facing bugs + bool shouldDelay = false; + + // set target, then force send update packet to players if it changed to provide appropriate facing + ObjectGuid newTarget = target ? target->GetGUID() : ObjectGuid::Empty; + if (GetGuidValue(UNIT_FIELD_TARGET) != newTarget) + { + SetGuidValue(UNIT_FIELD_TARGET, newTarget); + if (target) + SetFacingToObject(target); + + if ( // here we determine if the (relatively expensive) forced update is worth it, or whether we can afford to wait until the scheduled update tick + ( // only require instant update for spells that actually have a visual + focusSpell->GetSpellInfo()->SpellVisual[0] || + focusSpell->GetSpellInfo()->SpellVisual[1] + ) && ( + !focusSpell->GetCastTime() || // if the spell is instant cast + focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST) // client gets confused if we attempt to turn at the regularly scheduled update packet + ) + ) + { + const MapRefManager& mapPlayers = GetMap()->GetPlayers(); + for (MapRefManager::const_iterator it = mapPlayers.begin(); it != mapPlayers.end(); ++it) + if (Player* player = (*it).GetSource()) + { + // only update players that can both see us, and are actually in combat with us (this is a performance tradeoff) + if (player->CanSeeOrDetect(this, false, true) && IsInCombatWith(player)) + { + SendUpdateToPlayer(player); + shouldDelay = true; + } + } + if (shouldDelay) + shouldDelay = (!focusSpell->IsTriggered() && !focusSpell->GetCastTime()); + + } + } + + // tell the creature that it should reacquire its current target after the cast is done (this is handled in ::Attack) + MustReacquireTarget(); + + bool canTurnDuringCast = !focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST); + // Face the target - we need to do this before the unit state is modified for no-turn spells + if (target) + SetInFront(target); + else if (!canTurnDuringCast) + if(Unit* victim = GetVictim()) + SetInFront(victim); // ensure server-side orientation is correct at beginning of cast + + if (!canTurnDuringCast) + AddUnitState(UNIT_STATE_CANNOT_TURN); + + return shouldDelay; +} + +bool Creature::IsFocusing(Spell const* focusSpell, bool withDelay) +{ + if (!IsAlive()) // dead creatures cannot focus + { + ReleaseFocus(nullptr, false); + return false; + } + + if (focusSpell && (focusSpell != _focusSpell)) + return false; + + if (!_focusSpell) + { + if (!withDelay || !_focusDelay) + return false; + if (GetMSTimeDiffToNow(_focusDelay) > 1000) // @todo figure out if we can get rid of this magic number somehow + { + _focusDelay = 0; // save checks in the future + return false; + } + } + + return true; } -void Creature::ReleaseFocus(Spell const* focusSpell) +void Creature::ReleaseFocus(Spell const* focusSpell, bool withDelay) { + if (!_focusSpell) + return; + // focused to something else - if (focusSpell != _focusSpell) + if (focusSpell && focusSpell != _focusSpell) return; - _focusSpell = NULL; - if (Unit* victim = GetVictim()) - SetGuidValue(UNIT_FIELD_TARGET, victim->GetGUID()); - else - SetGuidValue(UNIT_FIELD_TARGET, ObjectGuid::Empty); + SetGuidValue(UNIT_FIELD_TARGET, ObjectGuid::Empty); + + if (_focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST)) + ClearUnitState(UNIT_STATE_CANNOT_TURN); - if (focusSpell->GetSpellInfo()->AttributesEx5 & SPELL_ATTR5_DONT_TURN_DURING_CAST) - ClearUnitState(UNIT_STATE_ROTATING); + _focusSpell = nullptr; + _focusDelay = withDelay ? getMSTime() : 0; // don't allow re-target right away to prevent visual bugs } void Creature::StartPickPocketRefillTimer() diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 96b04b32f84..73183b6f8dd 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -705,8 +705,9 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma // Handling caster facing during spellcast void SetTarget(ObjectGuid const& guid) override; - void FocusTarget(Spell const* focusSpell, WorldObject const* target); - void ReleaseFocus(Spell const* focusSpell); + bool FocusTarget(Spell const* focusSpell, WorldObject const* target); + bool IsFocusing(Spell const* focusSpell = nullptr, bool withDelay = false); + void ReleaseFocus(Spell const* focusSpell = nullptr, bool withDelay = true); CreatureTextRepeatIds GetTextRepeatGroup(uint8 textGroup); void SetTextRepeatId(uint8 textGroup, uint8 id); @@ -779,6 +780,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool TriggerJustRespawned; Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing + uint32 _focusDelay; CreatureTextRepeatGroup m_textRepeat; }; |
