From b4e54bc4de3464555d8a5d02ab6300e93a1df832 Mon Sep 17 00:00:00 2001 From: Elmsroth Date: Mon, 2 Nov 2020 22:44:10 +0100 Subject: [PATCH] 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 https://github.com/cmangos/mangos-classic/commit/caa548ca7df83c3aebb2ba66fb62fce431151012 * Fix autoshot not reinstating 0.5 sec cooldown on stopping (adapt Unit::IsNonMeleeSpellCasted method) Source : https://github.com/cmangos/mangos-classic/commit/20602b3eadd2dd6cd293c471ec6323cf147335ac 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. --- .../TargetedMovementGenerator.cpp | 13 ++- .../WaypointMovementGenerator.cpp | 4 +- src/game/Object/Player.cpp | 4 +- src/game/Object/SpellMgr.h | 98 ++++++++++++++++++- src/game/Object/Unit.cpp | 81 +++++++++++---- src/game/Object/Unit.h | 8 +- src/game/Server/SharedDefines.h | 4 +- src/game/WorldHandlers/Spell.cpp | 2 +- src/game/WorldHandlers/Spell.h | 15 ++- src/game/WorldHandlers/SpellAuras.cpp | 8 +- src/game/WorldHandlers/SpellEffects.cpp | 21 +++- src/modules/SD3 | 2 +- 12 files changed, 218 insertions(+), 42 deletions(-) diff --git a/src/game/MotionGenerators/TargetedMovementGenerator.cpp b/src/game/MotionGenerators/TargetedMovementGenerator.cpp index 43ed975b..c8069a72 100644 --- a/src/game/MotionGenerators/TargetedMovementGenerator.cpp +++ b/src/game/MotionGenerators/TargetedMovementGenerator.cpp @@ -131,11 +131,18 @@ bool TargetedMovementGeneratorMedium::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; } diff --git a/src/game/MotionGenerators/WaypointMovementGenerator.cpp b/src/game/MotionGenerators/WaypointMovementGenerator.cpp index 8b2518a4..e5667ec3 100644 --- a/src/game/MotionGenerators/WaypointMovementGenerator.cpp +++ b/src/game/MotionGenerators/WaypointMovementGenerator.cpp @@ -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(); diff --git a/src/game/Object/Player.cpp b/src/game/Object/Player.cpp index 9d01cce5..4f55f595 100644 --- a/src/game/Object/Player.cpp +++ b/src/game/Object/Player.cpp @@ -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 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; } diff --git a/src/game/Object/SpellMgr.h b/src/game/Object/SpellMgr.h index 61027b4d..bf66f183 100644 --- a/src/game/Object/SpellMgr.h +++ b/src/game/Object/SpellMgr.h @@ -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); diff --git a/src/game/Object/Unit.cpp b/src/game/Object/Unit.cpp index a1e30552..a3980f4b 100644 --- a/src/game/Object/Unit.cpp +++ b/src/game/Object/Unit.cpp @@ -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. + // 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)) + // generic spells are casted when they are not finished and not delayed + if (Spell const* genericSpell = m_currentSpells[CURRENT_GENERIC_SPELL]) + { + if (genericSpell->getState() != SPELL_STATE_FINISHED) { - return true; + 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) { - return true; + 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,11 +4942,18 @@ 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();) { - RemoveSpellAuraHolder(iter->second, AURA_REMOVE_BY_DEFAULT); - iter = m_spellAuraHolders.begin(); + 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)) diff --git a/src/game/Object/Unit.h b/src/game/Object/Unit.h index 4d7ca8b9..a184f77b 100644 --- a/src/game/Object/Unit.h +++ b/src/game/Object/Unit.h @@ -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 diff --git a/src/game/Server/SharedDefines.h b/src/game/Server/SharedDefines.h index 0a3baaca..abe83e93 100644 --- a/src/game/Server/SharedDefines.h +++ b/src/game/Server/SharedDefines.h @@ -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 diff --git a/src/game/WorldHandlers/Spell.cpp b/src/game/WorldHandlers/Spell.cpp index 1906bb34..b2435f74 100644 --- a/src/game/WorldHandlers/Spell.cpp +++ b/src/game/WorldHandlers/Spell.cpp @@ -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? diff --git a/src/game/WorldHandlers/Spell.h b/src/game/WorldHandlers/Spell.h index 2c843f98..ab9cf0cc 100644 --- a/src/game/WorldHandlers/Spell.h +++ b/src/game/WorldHandlers/Spell.h @@ -194,11 +194,14 @@ inline ByteBuffer& operator>> (ByteBuffer& buf, SpellCastTargetsReader const& ta 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_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_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; } diff --git a/src/game/WorldHandlers/SpellAuras.cpp b/src/game/WorldHandlers/SpellAuras.cpp index 4f8ef8cc..4f6f6c09 100644 --- a/src/game/WorldHandlers/SpellAuras.cpp +++ b/src/game/WorldHandlers/SpellAuras.cpp @@ -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(); diff --git a/src/game/WorldHandlers/SpellEffects.cpp b/src/game/WorldHandlers/SpellEffects.cpp index 3bda51cd..adf65813 100644 --- a/src/game/WorldHandlers/SpellEffects.cpp +++ b/src/game/WorldHandlers/SpellEffects.cpp @@ -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()); diff --git a/src/modules/SD3 b/src/modules/SD3 index 8fe5663e..189eb90a 160000 --- a/src/modules/SD3 +++ b/src/modules/SD3 @@ -1 +1 @@ -Subproject commit 8fe5663e1f7346aa76ce396d13ba52ade2566ad6 +Subproject commit 189eb90ae21fc96557015c8ffe15fb08d5e49dfa