Multiple fixes (#116)

* Fix usage of Gnomish Universal Remote (ItemID: 7506)

https://classic.wowhead.com/item=7506/gnomish-universal-remote
Will now apply correct random spells :
Spells :  8345 - Control the machine | 8346 = Malfunction the machine (root) | 8347 = Taunt/enrage the machine

* Fix Spells "Hate to Zero" 


https://classic.wowhead.com/spell=9204/hate-to-zero#see-also-other
SpellIDs : 9204 | 20538  | 26569 | 26637

* Upgrade Unit::RemoveAllAurasOnEvade method

As specific list of spell used when evading to remove all auras except some special auras

* Fix npc_escortAI - Properly despawn pets that act as escorts

Thanks to caa548ca7d

* Fix autoshot not reinstating 0.5 sec cooldown on stopping (adapt Unit::IsNonMeleeSpellCasted method)

Source : 20602b3ead
This fix needed to rewrite and add more stuff to be fully ported from CMangos.

* Fix build compile

Implement "IsClientControlled()" missing
Rename some defines to match CMangos ones which are more relevant and would facilitate backports.
This commit is contained in:
Elmsroth 2020-11-02 22:44:10 +01:00 committed by GitHub
parent efcac00443
commit b4e54bc4de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 218 additions and 42 deletions

View File

@ -131,11 +131,18 @@ bool TargetedMovementGeneratorMedium<T, D>::Update(T& owner, const uint32& time_
}
// prevent movement while casting spells with cast time or channel time
if (owner.IsNonMeleeSpellCasted(false, false, true))
if (owner.IsNonMeleeSpellCasted(false, false, true, true))
{
if (!owner.IsStopped())
if (!owner.movespline->Finalized())
{
owner.StopMoving();
if (owner.IsClientControlled())
{
owner.StopMoving(true);
}
else
{
owner.InterruptMoving();
}
}
return true;
}

View File

@ -454,7 +454,7 @@ void FlightPathMovementGenerator::Finalize(Player& player)
player.clearUnitState(UNIT_STAT_TAXI_FLIGHT);
player.Unmount();
player.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
player.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CLIENT_CONTROL_LOST | UNIT_FLAG_TAXI_FLIGHT);
if (player.m_taxi.empty())
{
@ -482,7 +482,7 @@ void FlightPathMovementGenerator::Reset(Player& player)
{
player.GetHostileRefManager().setOnlineOfflineState(false);
player.addUnitState(UNIT_STAT_TAXI_FLIGHT);
player.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
player.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CLIENT_CONTROL_LOST | UNIT_FLAG_TAXI_FLIGHT);
Movement::MoveSplineInit init(player);
uint32 end = GetPathAtMapEnd();

View File

@ -2920,7 +2920,7 @@ void Player::InitStatsForLevel(bool reapplyMods)
// cleanup unit flags (will be re-applied if need at aura load).
RemoveFlag(UNIT_FIELD_FLAGS,
UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 |
UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_CLIENT_CONTROL_LOST | UNIT_FLAG_NOT_ATTACKABLE_1 |
UNIT_FLAG_OOC_NOT_ATTACKABLE | UNIT_FLAG_PASSIVE | UNIT_FLAG_LOOTING |
UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED |
UNIT_FLAG_STUNNED | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED |
@ -19555,7 +19555,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
return false;
}
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CLIENT_CONTROL_LOST))
{
return false;
}

View File

@ -360,6 +360,103 @@ inline bool IsAreaEffectTarget(Targets target)
return false;
}
inline bool IsSpellRemovedOnEvade(SpellEntry const* spellInfo)
{
//TODO: search for potential correct case for Classic
/*if (IsSpellHaveAura(spellInfo, SPELL_AURA_FLY))
return false; */
switch (spellInfo->Id)
{
case 588: // Inner Fire (Rank 1)
case 3235: // Rancid Blood
case 3284: // Violent Shield
case 3417: // Thrash
case 3418: // Improved Blocking
case 3616: // Poison Proc
case 3637: // Improved Blocking III
case 5111: // Living Flame Passive
case 5301: // Defensive State (DND)
case 5680: // Torch Burn
case 6718: // Phasing Stealth
case 6752: // Weak Poison Proc
case 6947: // Curse of the Bleakheart Proc
case 7090: // Bear Form (Shapeshift)
case 7165: // Battle Stance (Rank 1)
case 7276: // Poison Proc
case 8247: // Wandering Plague
case 8279: // Stealth Detection
case 8393: // Barbs
case 8599: // Enrage
case 8601: // Slowing Poison
case 8876: // Thrash
case 9205: // Hate to Zero (Hate to Zero)
case 9460: // Corrosive Ooze
case 9941: // Spell Reflection
case 10022: // Deadly Poison
case 10072: // Splintered Obsidian
case 10074: // Spell Reflection
case 10095: // Hate to Zero (Hate to Zero)
case 11838: // Hate to Zero (Hate to Zero)
case 11919: // Poison Proc
case 11966: // Fire Shield
case 11984: // Immolate
case 12099: // Shield Spike
case 12246: // Infected Spine
case 12529: // Chilling Touch
case 12539: // Ghoul Rot
case 12546: // Spitelash (Spitelash)
case 12556: // Frost Armor
case 12627: // Disease Cloud
case 12787: // Thrash
case 12898: // Smoke Aura Visual
case 13299: // Poison Proc
case 13616: // Wracking Pains Proc
case 13767: // Hate to Zero (Hate to Zero)
case 14178: // Sticky Tar
case 15088: // Flurry
case 15097: // Enrage
case 15876: // Ice Blast
case 16140: // Exploding Cadaver (Exploding Cadaver)
case 16563: // Drowning Death
case 16577: // Disease Cloud
case 16592: // Shadowform
case 17327: // Spirit Particles
case 17467: // Unholy Aura
case 18148: // Static Field
case 18268: // Fire Shield
case 18943: // Double Attack
case 18968: // Fire Shield
case 19030: // Bear Form (Shapeshift)
case 18950: // Invisibility and Stealth Detection
case 19194: // Double Attack
case 19195: // Hate to 90% (Hate to 90%)
case 19396: // Incinerate (Incinerate)
case 19626: // Fire Shield (Fire Shield)
case 19640: // Pummel (Pummel)
case 19817: // Double Attack
case 19818: // Double Attack
case 20514: // Ruul Snowhoof Shapechange (DND)
case 21061: // Putrid Breath
case 21857: // Lava Shield
case 22128: // Thorns
case 22578: // Glowy (Black)
case 22735: // Spirit of Runn Tum
case 22781: // Thornling
case 22788: // Grow
case 22856: // Ice Lock (Guard Slip'kik ice trap in Dire Maul)
case 25592: // Hate to Zero (Hate to Zero)
case 26341: // Saurfang's Rage
case 27987: // Unholy Aura
case 28126: // Spirit Particles (purple)
case 29526: // Hate to Zero (Hate to Zero)
return false;
default:
return true;
}
}
inline bool IsAreaOfEffectSpell(SpellEntry const* spellInfo)
{
if (IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetA[EFFECT_INDEX_0])) || IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetB[EFFECT_INDEX_0])))
@ -485,7 +582,6 @@ inline bool IsNeedCastSpellAtOutdoor(SpellEntry const* spellInfo)
return (spellInfo->HasAttribute(SPELL_ATTR_OUTDOORS_ONLY) && spellInfo->HasAttribute(SPELL_ATTR_PASSIVE));
}
inline bool NeedsComboPoints(SpellEntry const* spellInfo)
{
return spellInfo->HasAttribute(SPELL_ATTR_EX_REQ_TARGET_COMBO_POINTS) || spellInfo->HasAttribute(SPELL_ATTR_EX_REQ_COMBO_POINTS);

View File

@ -3703,33 +3703,73 @@ void Unit::FinishSpell(CurrentSpellTypes spellType, bool ok /*= true*/)
spell->finish(ok);
}
bool Unit::IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled, bool skipAutorepeat) const
bool Unit::IsClientControlled(Player const* exactClient /*= nullptr*/) const
{
// Severvide method to check if unit is client controlled (optionally check for specific client in control)
// Applies only to player controlled units
if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED))
return false;
// These flags are meant to be used when server controls this unit, client control is taken away
if (HasFlag(UNIT_FIELD_FLAGS, (UNIT_FLAG_CLIENT_CONTROL_LOST | UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING)))
return false;
// If unit is possessed, it has lost original control...
if (ObjectGuid const& guid = GetCharmerGuid())
{
// ... but if it is a possessing charm, then we have to check if some other player controls it
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED) && guid.IsPlayer())
return (exactClient ? (exactClient->GetObjectGuid() == guid) : true);
return false;
}
// By default: players have client control over themselves
if (GetTypeId() == TYPEID_PLAYER)
return (exactClient ? (exactClient == this) : true);
return false;
}
bool Unit::IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled, bool skipAutorepeat, bool forMovement, bool forAutoIgnore) const
{
// We don't do loop here to explicitly show that melee spell is excluded.
// Maybe later some special spells will be excluded too.
// generic spells are casted when they are not finished and not delayed
if (m_currentSpells[CURRENT_GENERIC_SPELL] &&
(m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) &&
(withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED))
if (Spell const* genericSpell = m_currentSpells[CURRENT_GENERIC_SPELL])
{
if (genericSpell->getState() != SPELL_STATE_FINISHED)
{
bool specialResult = true;
if (forMovement) // mobs can move during spells without this flag
specialResult = genericSpell->m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT;
bool isAutoNonInterrupting = forAutoIgnore && genericSpell->m_spellInfo->HasAttribute(SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS);
if (!isAutoNonInterrupting && specialResult && (withDelayed || genericSpell->getState() != SPELL_STATE_TRAVELING))
return true;
}
}
// channeled spells may be delayed, but they are still considered casted
else if (!skipChanneled && m_currentSpells[CURRENT_CHANNELED_SPELL] &&
(m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED))
if (!skipChanneled)
{
if (Spell const* channeledSpell = m_currentSpells[CURRENT_CHANNELED_SPELL])
{
bool attributeResult = false;
if (!forMovement)
attributeResult = channeledSpell->m_spellInfo->HasAttribute(SPELL_ATTR_EX4_CAN_CAST_WHILE_CASTING);
bool isAutoNonInterrupting = forAutoIgnore && channeledSpell->m_spellInfo->HasAttribute(SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS);
if (!isAutoNonInterrupting && !attributeResult && !channeledSpell->IsTriggered() && (channeledSpell->getState() != SPELL_STATE_FINISHED))
return true;
}
}
// autorepeat spells may be finished or delayed, but they are still considered casted
else if (!skipAutorepeat && m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
{
if (!skipAutorepeat && m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
return true;
}
return false;
return forAutoIgnore;
}
void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id)
@ -4902,12 +4942,19 @@ void Unit::RemoveAllAurasOnDeath()
void Unit::RemoveAllAurasOnEvade()
{
// used when evading to remove all auras except some special auras
// Linked and flying auras should not be removed on evade
// Fly should not be removed on evade - neither should linked auras
// Some cosmetic script auras should not be removed on evade either
for (SpellAuraHolderMap::iterator iter = m_spellAuraHolders.begin(); iter != m_spellAuraHolders.end();)
{
SpellEntry const* proto = iter->second->GetSpellProto();
if (IsSpellRemovedOnEvade(proto))
{
RemoveSpellAuraHolder(iter->second, AURA_REMOVE_BY_DEFAULT);
iter = m_spellAuraHolders.begin();
}
else
++iter;
}
if ((GetTypeId() == TYPEID_UNIT) && HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED))
{

View File

@ -508,7 +508,7 @@ enum UnitFlags
UNIT_FLAG_NONE = 0x00000000,
UNIT_FLAG_UNK_0 = 0x00000001,
UNIT_FLAG_NON_ATTACKABLE = 0x00000002, ///< not attackable
UNIT_FLAG_DISABLE_MOVE = 0x00000004,
UNIT_FLAG_CLIENT_CONTROL_LOST = 0x00000004, // Generic unspecified loss of control initiated by server script, movement checks disabled, paired with loss of client control packet.
UNIT_FLAG_PVP_ATTACKABLE = 0x00000008, ///< allow apply pvp rules to attackable state in addition to faction dependent state, UNIT_FLAG_UNKNOWN1 in pre-bc mangos
UNIT_FLAG_RENAME = 0x00000010, ///< rename creature
UNIT_FLAG_RESTING = 0x00000020,
@ -537,7 +537,7 @@ enum UnitFlags
UNIT_FLAG_DISARMED = 0x00200000, ///< disable melee spells casting..., "Required melee weapon" added to melee spells tooltip.
UNIT_FLAG_CONFUSED = 0x00400000,
UNIT_FLAG_FLEEING = 0x00800000,
UNIT_FLAG_PLAYER_CONTROLLED = 0x01000000, ///< used in spell Eyes of the Beast for pet... let attack by controlled creature
UNIT_FLAG_POSSESSED = 0x01000000, ///< used in spell Eyes of the Beast for pet... let attack by controlled creature |// Unit is under remote control by another unit, movement checks disabled, paired with loss of client control packet. New master is allowed to use melee attack and can't select this unit via mouse in the world (as if it was own character).
UNIT_FLAG_UNK_28 = 0x10000000,
UNIT_FLAG_UNK_29 = 0x20000000 ///< used in Feign Death spell
};
@ -3383,10 +3383,12 @@ class Unit : public WorldObject
void InterruptSpell(CurrentSpellTypes spellType, bool withDelayed = true);
void FinishSpell(CurrentSpellTypes spellType, bool ok = true);
bool IsClientControlled(Player const* exactClient = nullptr) const;
// set withDelayed to true to account delayed spells as casted
// delayed+channeled spells are always accounted as casted
// we can skip channeled or delayed checks using flags
bool IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled = false, bool skipAutorepeat = false) const;
bool IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled = false, bool skipAutorepeat = false, bool forMovement = false, bool forAutoIgnore = false) const;
// set withDelayed to true to interrupt delayed spells too
// delayed+channeled spells are always interrupted

View File

@ -366,7 +366,7 @@ enum SpellAttributesEx2
SPELL_ATTR_EX2_UNK14 = 0x00004000, // 14
SPELL_ATTR_EX2_UNK15 = 0x00008000, // 15 not set in 2.4.2
SPELL_ATTR_EX2_UNK16 = 0x00010000, // 16
SPELL_ATTR_EX2_UNK17 = 0x00020000, // 17 suspend weapon timer instead of resetting it, (?Hunters Shot and Stings only have this flag?)
SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS = 0x00020000, // 17 suspend weapon timer instead of resetting it, (?Hunters Shot and Stings only have this flag?)
SPELL_ATTR_EX2_UNK18 = 0x00040000, // 18 Only Revive pet - possible req dead pet
SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT = 0x00080000, // 19 does not necessary need shapeshift (pre-3.x not have passive spells with this attribute)
SPELL_ATTR_EX2_UNK20 = 0x00100000, // 20
@ -428,7 +428,7 @@ enum SpellAttributesEx4
SPELL_ATTR_EX4_UNK4 = 0x00000010, // 4 This will no longer cause guards to attack on use??
SPELL_ATTR_EX4_UNK5 = 0x00000020, // 5
SPELL_ATTR_EX4_NOT_STEALABLE = 0x00000040, // 6 although such auras might be dispellable, they can not be stolen
SPELL_ATTR_EX4_UNK7 = 0x00000080, // 7
SPELL_ATTR_EX4_CAN_CAST_WHILE_CASTING = 0x00000080, // 7 In theory, can use this spell while another is channeled/cast/autocast
SPELL_ATTR_EX4_STACK_DOT_MODIFIER = 0x00000100, // 8 no effect on non DoTs?
SPELL_ATTR_EX4_UNK9 = 0x00000200, // 9
SPELL_ATTR_EX4_SPELL_VS_EXTEND_COST = 0x00000400, // 10 Rogue Shiv have this flag

View File

@ -4224,7 +4224,7 @@ void Spell::SendChannelUpdate(uint32 time)
if (possessed)
{
possessed->clearUnitState(UNIT_STAT_CONTROLLED);
possessed->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
possessed->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED);
possessed->SetCharmerGuid(ObjectGuid());
// TODO - Requires more specials for target?

View File

@ -197,8 +197,11 @@ enum SpellState
SPELL_STATE_CREATED = 0, // just created
SPELL_STATE_PREPARING = 1, // cast time delay period, non channeled spell
SPELL_STATE_CASTING = 2, // channeled time period spell casting state
SPELL_STATE_FINISHED = 3, // cast finished to success or fail
SPELL_STATE_DELAYED = 4 // spell casted but need time to hit target(s)
SPELL_STATE_DELAYED = 3, // spell is delayed (cast time pushed back) TODO: need to be implemented properly
SPELL_STATE_TRAVELING = 4, // spell casted but need time to hit target(s)
SPELL_STATE_LANDING = 5, // processing the effects
SPELL_STATE_CHANNELING = 6, // channeled time period spell casting state
SPELL_STATE_FINISHED = 7, // cast finished to success or fail
};
enum SpellTargets
@ -384,6 +387,8 @@ class Spell
Item* m_CastItem;
SpellCastTargets m_targets;
bool IsTriggered() const {return m_IsTriggeredSpell;}
int32 GetCastTime() const { return m_casttime; }
uint32 GetCastedTime() { return m_timer; }
bool IsAutoRepeat() const { return m_autoRepeat; }

View File

@ -2399,7 +2399,7 @@ void Aura::HandleModPossess(bool apply, bool Real)
{
target->addUnitState(UNIT_STAT_CONTROLLED);
target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED);
target->SetCharmerGuid(p_caster->GetObjectGuid());
target->setFaction(p_caster->getFaction());
@ -2458,7 +2458,7 @@ void Aura::HandleModPossess(bool apply, bool Real)
target->DeleteThreatList();
target->GetHostileRefManager().deleteReferences();
target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED);
target->SetCharmerGuid(ObjectGuid());
@ -2517,7 +2517,7 @@ void Aura::HandleModPossessPet(bool apply, bool Real)
p_caster->SetClientControl(pet, 1);
((Player*)caster)->SetMover(pet);
pet->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
pet->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED);
pet->StopMoving();
pet->GetMotionMaster()->Clear(false);
@ -2541,7 +2541,7 @@ void Aura::HandleModPossessPet(bool apply, bool Real)
pet->clearUnitState(UNIT_STAT_CONTROLLED);
pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
pet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED);
pet->AttackStop();

View File

@ -487,6 +487,25 @@ void Spell::EffectDummy(SpellEffectIndex eff_idx)
m_caster->CastSpell(m_caster, spell_id, true, NULL);
return;
}
case 8344: // Gnomish Universal Remote (ItemID: 7506)
{
if (m_CastItem && unitTarget)
{
// 8345 - Control the machine | 8346 = Malfunction the machine (root) | 8347 = Taunt/enrage the machine
const uint32 spell_list[3] = { 8345, 8346, 8347 };
m_caster->CastSpell(unitTarget, spell_list[urand(0, 2)], true, m_CastItem);
}
return;
}
case 9204: // Hate to Zero
case 20538:
case 26569:
case 26637:
{
m_caster->GetThreatManager().modifyThreatPercent(unitTarget, -100);
return;
}
case 9976: // Polly Eats the E.C.A.C.
{
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
@ -4982,7 +5001,7 @@ void Spell::EffectSummonPossessed(SpellEffectIndex eff_idx)
spawnCreature->SetCharmerGuid(m_caster->GetObjectGuid());
spawnCreature->SetCreatorGuid(m_caster->GetObjectGuid());
spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
spawnCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
spawnCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_POSSESSED);
spawnCreature->SetLevel(m_caster->getLevel());

@ -1 +1 @@
Subproject commit 8fe5663e1f7346aa76ce396d13ba52ade2566ad6
Subproject commit 189eb90ae21fc96557015c8ffe15fb08d5e49dfa