diff --git a/src/game/AI/CreatureAI.cpp b/src/game/AI/CreatureAI.cpp index a506cf40c..1778b9201 100644 --- a/src/game/AI/CreatureAI.cpp +++ b/src/game/AI/CreatureAI.cpp @@ -185,13 +185,13 @@ void CreatureAI::SetSpellsTemplate(const CreatureSpellsTemplate *SpellsTemplate) void CreatureAI::DoSpellTemplateCasts(const uint32 uiDiff) { - bool bCasted = false; + bool bDontCast = false; for (auto & spell : m_CreatureSpells) { if (spell.cooldown <= uiDiff) { // Prevent casting multiple spells in the same update. Only update timers. - if (bCasted) + if (bDontCast) continue; if (m_creature->IsNonMeleeSpellCasted(false) && !(spell.castFlags & (CF_TRIGGERED | CF_INTERRUPT_PREVIOUS))) @@ -211,7 +211,7 @@ void CreatureAI::DoSpellTemplateCasts(const uint32 uiDiff) { case SPELL_CAST_OK: { - bCasted = true; + bDontCast = true; spell.cooldown = urand(spell.delayRepeatMin, spell.delayRepeatMax); if (spell.castFlags & CF_MAIN_RANGED_SPELL) @@ -228,9 +228,10 @@ void CreatureAI::DoSpellTemplateCasts(const uint32 uiDiff) m_creature->GetMap()->ScriptsStart(sCreatureSpellScripts, spell.scriptId, m_creature, pTarget); break; } + case SPELL_FAILED_FLEEING: case SPELL_FAILED_SPELL_IN_PROGRESS: { - // If we are casting, do nothing so it will try again on next update. + // Do nothing so it will try again on next update. break; } case SPELL_FAILED_TRY_AGAIN: @@ -408,6 +409,16 @@ void CreatureAI::SetCombatMovement(bool enabled) } } +void CreatureAI::MoveAwayFromTarget(Unit* pOwner, float distance) +{ + if (m_creature->hasUnitState(UNIT_STAT_NOT_MOVE | UNIT_STAT_CONFUSED | UNIT_STAT_LOST_CONTROL)) + return; + + m_creature->CastStop(); + + m_creature->GetMotionMaster()->MoveDistance(pOwner, distance); +} + void CreatureAI::OnCombatStop() { // Reset back to default spells template. This also resets timers. diff --git a/src/game/AI/CreatureAI.h b/src/game/AI/CreatureAI.h index 9572ab9d8..3b51510c2 100644 --- a/src/game/AI/CreatureAI.h +++ b/src/game/AI/CreatureAI.h @@ -228,6 +228,9 @@ class MANGOS_DLL_SPEC CreatureAI // Enables or disabled combat movement. void SetCombatMovement(bool enabled); + + // Try to get away from a target. + void MoveAwayFromTarget(Unit* pTarget, float distance); // Pointer to controlled by AI creature Creature* const m_creature; diff --git a/src/game/AI/CreatureEventAI.cpp b/src/game/AI/CreatureEventAI.cpp index fcfbf6a56..18bdf3bcc 100644 --- a/src/game/AI/CreatureEventAI.cpp +++ b/src/game/AI/CreatureEventAI.cpp @@ -366,6 +366,14 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction case EVENT_T_MAP_SCRIPT_EVENT: case EVENT_T_GROUP_MEMBER_DIED: break; + case EVENT_T_VICTIM_ROOTED: + { + if (!m_creature->getVictim() || !m_creature->getVictim()->hasUnitState(UNIT_STAT_ROOT)) + return false; + + pHolder.UpdateRepeatTimer(m_creature, event.victim_rooted.repeatMin, event.victim_rooted.repeatMax); + break; + } default: sLog.outErrorDb("CreatureEventAI: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); break; @@ -787,6 +795,7 @@ void CreatureEventAI::UpdateEventsOn_UpdateAI(const uint32 diff, bool Combat) case EVENT_T_TARGET_AURA: case EVENT_T_MISSING_AURA: case EVENT_T_TARGET_MISSING_AURA: + case EVENT_T_VICTIM_ROOTED: if (Combat) ProcessEvent(*i); break; diff --git a/src/game/AI/CreatureEventAI.h b/src/game/AI/CreatureEventAI.h index 9afe65ff6..5b53fab37 100644 --- a/src/game/AI/CreatureEventAI.h +++ b/src/game/AI/CreatureEventAI.h @@ -70,6 +70,7 @@ enum EventAI_Type EVENT_T_LEAVE_COMBAT = 30, // NONE EVENT_T_MAP_SCRIPT_EVENT = 31, // Param1 = EventID, Param2 = Data EVENT_T_GROUP_MEMBER_DIED = 32, // Param1 = CreatureId, Param2 = IsLeader + EVENT_T_VICTIM_ROOTED = 33, // RepeatMin, RepeatMax EVENT_T_END, }; @@ -237,6 +238,12 @@ struct CreatureEventAI_Event uint32 creatureId; uint32 isLeader; } group_member_died; + // EVENT_T_VICTIM_ROOTED = 33 + struct + { + uint32 repeatMin; + uint32 repeatMax; + } victim_rooted; // RAW struct { diff --git a/src/game/AI/CreatureEventAIMgr.cpp b/src/game/AI/CreatureEventAIMgr.cpp index 785d453af..b3360897f 100644 --- a/src/game/AI/CreatureEventAIMgr.cpp +++ b/src/game/AI/CreatureEventAIMgr.cpp @@ -291,6 +291,12 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Events() } break; } + case EVENT_T_VICTIM_ROOTED: + { + if (temp.victim_rooted.repeatMax < temp.victim_rooted.repeatMin) + sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); + break; + } default: sLog.outErrorDb("CreatureEventAI: Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp.creature_id, temp.event_id, i); break; diff --git a/src/game/Maps/ScriptCommands.cpp b/src/game/Maps/ScriptCommands.cpp index 77661f092..fb0181a84 100644 --- a/src/game/Maps/ScriptCommands.cpp +++ b/src/game/Maps/ScriptCommands.cpp @@ -755,6 +755,10 @@ bool Map::ScriptCommand_SetMovementType(const ScriptInfo& script, WorldObject* s if (pTarget) pSource->GetMotionMaster()->MoveCharge(pTarget, script.movement.intParam, script.movement.boolParam); break; + case DISTANCING_MOTION_TYPE: + if (pTarget && pSource->AI() && (!script.movement.intParam || (script.movement.intParam <= pSource->GetPowerPercent(POWER_MANA)))) + pSource->AI()->MoveAwayFromTarget(pTarget, script.x); + break; default: sLog.outError("SCRIPT_COMMAND_MOVEMENT (script id %u) call for an invalid motion type (MotionType: %u), skipping.", script.id, script.movement.movementType); return ShouldAbortScript(script); @@ -1434,6 +1438,9 @@ bool Map::ScriptCommand_DealDamage(const ScriptInfo& script, WorldObject* source return ShouldAbortScript(script); } + if (!pTarget->isAlive()) + return ShouldAbortScript(script); + uint32 damage = script.dealDamage.isPercent ? pTarget->GetMaxHealth()*(script.dealDamage.damage / 100.0f) : script.dealDamage.damage; pSource->DealDamage(pTarget, damage, nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); diff --git a/src/game/Movement/MotionMaster.cpp b/src/game/Movement/MotionMaster.cpp index a7bb2676c..91a16b98e 100644 --- a/src/game/Movement/MotionMaster.cpp +++ b/src/game/Movement/MotionMaster.cpp @@ -276,7 +276,7 @@ void MotionMaster::DirectExpire(bool reset) // also drop stored under top() targeted motions typedef std::list MvtGenList; MvtGenList mvtGensToFinalize; - while (!empty() && (top()->GetMovementGeneratorType() == CHASE_MOTION_TYPE || top()->GetMovementGeneratorType() == FOLLOW_MOTION_TYPE)) + while (!empty() && (top()->GetMovementGeneratorType() == CHASE_MOTION_TYPE || top()->GetMovementGeneratorType() == FOLLOW_MOTION_TYPE) && (curr->GetMovementGeneratorType() != DISTANCING_MOTION_TYPE)) { MovementGenerator *temp = top(); pop(); @@ -322,7 +322,7 @@ void MotionMaster::DelayedExpire(bool reset) // also drop stored under top() targeted motions typedef std::list MvtGenList; MvtGenList mvtGensToFinalize; - while (!empty() && (top()->GetMovementGeneratorType() == CHASE_MOTION_TYPE || top()->GetMovementGeneratorType() == FOLLOW_MOTION_TYPE)) + while (!empty() && (top()->GetMovementGeneratorType() == CHASE_MOTION_TYPE || top()->GetMovementGeneratorType() == FOLLOW_MOTION_TYPE) && (curr->GetMovementGeneratorType() != DISTANCING_MOTION_TYPE)) { MovementGenerator *temp = top(); pop(); @@ -605,8 +605,9 @@ void MotionMaster::Mutate(MovementGenerator *m) { // HomeMovement is not that important, delete it if meanwhile a new comes case HOME_MOTION_TYPE: - // DistractMovement interrupted by any other movement + // Distract and Distancing movememnt interrupted by any other movement case DISTRACT_MOTION_TYPE: + case DISTANCING_MOTION_TYPE: MovementExpired(false); default: break; @@ -720,6 +721,13 @@ void MotionMaster::MoveCharge(Unit* target, uint32 delay, bool triggerAutoAttack Mutate(new ChargeMovementGenerator(*(m_owner->ToCreature()), *target, delay, triggerAutoAttack)); } +void MotionMaster::MoveDistance(Unit* pTarget, float distance) +{ + float x, y, z; + pTarget->GetNearPoint(m_owner, x, y, z, 0, distance, pTarget->GetAngle(m_owner)); + Mutate(new DistancingMovementGenerator(x, y, z)); +} + void MotionMaster::ClearType(MovementGeneratorType moveType) { for (iterator it = begin(); it != end();) diff --git a/src/game/Movement/MotionMaster.h b/src/game/Movement/MotionMaster.h index e63677b5b..24d1a01b5 100644 --- a/src/game/Movement/MotionMaster.h +++ b/src/game/Movement/MotionMaster.h @@ -54,6 +54,7 @@ enum MovementGeneratorType EFFECT_MOTION_TYPE = 15, PATROL_MOTION_TYPE = 16, CHARGE_MOTION_TYPE = 17, + DISTANCING_MOTION_TYPE = 18, WAYPOINT_SPECIAL_REACHED = 256, // Only used in CreatureAI::MovementInform when a special waypoint is reached. The pathId >= 0 is added as additonal value WAYPOINT_SPECIAL_STARTED = 512, // Only used in CreatureAI::MovementInform when a special waypoint is started. The pathId >= 0 is added as additional value @@ -143,6 +144,7 @@ class MANGOS_DLL_SPEC MotionMaster : std::stack void MoveDistract(uint32 timeLimit); void MoveJump(float x, float y, float z, float horizontalSpeed, float max_height, uint32 id = 0); void MoveCharge(Unit* target, uint32 delay = 0, bool triggerAutoAttack = false); + void MoveDistance(Unit* target, float distance); MovementGeneratorType GetCurrentMovementGeneratorType() const; diff --git a/src/game/Movement/PointMovementGenerator.cpp b/src/game/Movement/PointMovementGenerator.cpp index 150c2230e..d91c2fe93 100644 --- a/src/game/Movement/PointMovementGenerator.cpp +++ b/src/game/Movement/PointMovementGenerator.cpp @@ -141,6 +141,33 @@ template void PointMovementGenerator::Reset(Creature&); template bool PointMovementGenerator::Update(Player &, const uint32 &diff); template bool PointMovementGenerator::Update(Creature&, const uint32 &diff); +bool DistancingMovementGenerator::Update(Creature &unit, const uint32 &diff) +{ + if (!&unit) + return false; + + if (unit.hasUnitState(UNIT_STAT_CAN_NOT_MOVE)) + { + unit.clearUnitState(UNIT_STAT_ROAMING_MOVE); + return false; + } + + unit.addUnitState(UNIT_STAT_ROAMING_MOVE); + + if (!unit.movespline->Finalized() && _recalculateSpeed) + { + _recalculateSpeed = false; + Initialize(unit); + } + return !unit.movespline->Finalized(); +} + +void DistancingMovementGenerator::MovementInform(Creature &unit) +{ + if (unit.AI()) + unit.AI()->DoSpellTemplateCasts(1); +} + void AssistanceMovementGenerator::Initialize(Creature& unit) { if (unit.hasUnitState(UNIT_STAT_CAN_NOT_REACT | UNIT_STAT_CAN_NOT_MOVE)) diff --git a/src/game/Movement/PointMovementGenerator.h b/src/game/Movement/PointMovementGenerator.h index 34765dc68..1dbe78ec8 100644 --- a/src/game/Movement/PointMovementGenerator.h +++ b/src/game/Movement/PointMovementGenerator.h @@ -37,9 +37,9 @@ class MANGOS_DLL_SPEC PointMovementGenerator virtual void Finalize(T &); void Interrupt(T &); void Reset(T &unit); - bool Update(T &, const uint32 &diff); + virtual bool Update(T &, const uint32 &diff); - void MovementInform(T &); + virtual void MovementInform(T &); void unitSpeedChanged() { _recalculateSpeed = true; } @@ -55,6 +55,20 @@ class MANGOS_DLL_SPEC PointMovementGenerator bool _recalculateSpeed; }; +class MANGOS_DLL_SPEC DistancingMovementGenerator +: public PointMovementGenerator +{ + public: + DistancingMovementGenerator(float _x, float _y, float _z) : + PointMovementGenerator(0, _x, _y, _z, MOVE_PATHFINDING | MOVE_RUN_MODE), m_bAborted(false) {} + + MovementGeneratorType GetMovementGeneratorType() const { return DISTANCING_MOTION_TYPE; } + bool Update(Creature&, const uint32 &diff) override; + void MovementInform(Creature &) override; + private: + bool m_bAborted; +}; + class MANGOS_DLL_SPEC AssistanceMovementGenerator : public PointMovementGenerator { diff --git a/src/game/Objects/Creature.cpp b/src/game/Objects/Creature.cpp index caa289739..128735a87 100644 --- a/src/game/Objects/Creature.cpp +++ b/src/game/Objects/Creature.cpp @@ -3597,7 +3597,6 @@ SpellCastResult Creature::TryToCast(Unit* pTarget, const SpellEntry* pSpellInfo, if ((uiCastFlags & CF_AURA_NOT_PRESENT) && pTarget->HasAura(pSpellInfo->Id)) return SPELL_FAILED_AURA_BOUNCED; - // Can't cast while fleeing. if (GetMotionMaster()->GetCurrentMovementGeneratorType() == TIMED_FLEEING_MOTION_TYPE) return SPELL_FAILED_FLEEING; @@ -3610,12 +3609,22 @@ SpellCastResult Creature::TryToCast(Unit* pTarget, const SpellEntry* pSpellInfo, return SPELL_FAILED_TOO_CLOSE; // This spell should only be cast when we cannot get into melee range. - if ((uiCastFlags & CF_TARGET_UNREACHABLE) && (CanReachWithMeleeAttack(pTarget) || (GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE) || !(hasUnitState(UNIT_STAT_NOT_MOVE) || !GetMotionMaster()->GetCurrent()->IsReachable()))) + if ((uiCastFlags & CF_TARGET_UNREACHABLE) && (CanReachWithMeleeAttack(pTarget) || (GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE) || !(hasUnitState(UNIT_STAT_ROOT) || !GetMotionMaster()->GetCurrent()->IsReachable()))) return SPELL_FAILED_MOVING; // Custom checks if (!(uiCastFlags & CF_FORCE_CAST)) { + // Can't cast while fleeing. + switch (GetMotionMaster()->GetCurrentMovementGeneratorType()) + { + case TIMED_FLEEING_MOTION_TYPE: + return SPELL_FAILED_FLEEING; + case DISTANCING_MOTION_TYPE: + if (!hasUnitState(UNIT_STAT_CAN_NOT_MOVE)) + return SPELL_FAILED_FLEEING; + } + // If the spell requires to be behind the target. if (pSpellInfo->Custom & SPELL_CUSTOM_FROM_BEHIND && pTarget->HasInArc(M_PI_F, this)) return SPELL_FAILED_UNIT_NOT_BEHIND; diff --git a/src/game/ScriptMgr.cpp b/src/game/ScriptMgr.cpp index 590a18f68..684c611a3 100644 --- a/src/game/ScriptMgr.cpp +++ b/src/game/ScriptMgr.cpp @@ -567,6 +567,7 @@ void ScriptMgr::LoadScripts(ScriptMapMap& scripts, const char* tablename) case DISTRACT_MOTION_TYPE: case FOLLOW_MOTION_TYPE: case CHARGE_MOTION_TYPE: + case DISTANCING_MOTION_TYPE: break; default: {