diff --git a/src/game/Object/CreatureAI.cpp b/src/game/Object/CreatureAI.cpp index cb0d5864..47bfec5d 100644 --- a/src/game/Object/CreatureAI.cpp +++ b/src/game/Object/CreatureAI.cpp @@ -57,14 +57,14 @@ CanCastResult CreatureAI::CanCastSpell(Unit* pTarget, const SpellEntry* pSpell, if (pSpell->PreventionType == SPELL_PREVENTION_TYPE_PACIFY && m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED)) { return CAST_FAIL_STATE; } - if (!m_creature->IsWithinLOSInMap(pTarget)) - { return CAST_FAIL_NO_LOS; } - // Check for power (also done by Spell::CheckCast()) if (m_creature->GetPower((Powers)pSpell->powerType) < Spell::CalculatePowerCost(pSpell, m_creature)) { return CAST_FAIL_POWER; } } + if (!m_creature->IsWithinLOSInMap(pTarget)) + { return CAST_FAIL_NO_LOS; } + if (const SpellRangeEntry* pSpellRange = sSpellRangeStore.LookupEntry(pSpell->rangeIndex)) { if (pTarget != m_creature) @@ -137,26 +137,43 @@ bool CreatureAI::DoMeleeAttackIfReady() return m_creature->UpdateMeleeAttackingState(); } +void CreatureAI::AddCombatMovementFlags(uint32 cmFlags) +{ + if (!m_combatMovement) + { m_creature->clearUnitState(UNIT_STAT_NO_COMBAT_MOVEMENT); } + + m_combatMovement |= cmFlags; +} + +void CreatureAI::ClearCombatMovementFlags(uint32 cmFlags) +{ + m_combatMovement &= ~cmFlags; + + if (!m_combatMovement) + { m_creature->addUnitState(UNIT_STAT_NO_COMBAT_MOVEMENT); } +} + void CreatureAI::SetCombatMovement(bool enable, bool stopOrStartMovement /*=false*/) { - m_isCombatMovement = enable; - - if (enable) - { m_creature->clearUnitState(UNIT_STAT_NO_COMBAT_MOVEMENT); } - else - { m_creature->addUnitState(UNIT_STAT_NO_COMBAT_MOVEMENT); } - if (stopOrStartMovement && m_creature->getVictim()) // Only change current movement while in combat { MotionMaster* creatureMotion = m_creature->GetMotionMaster(); + creatureMotion->MovementExpired(false); if (enable) { - m_creature->CastStop(); - creatureMotion->MoveChase(m_creature->getVictim(), m_attackDistance, m_attackAngle); + switch(creatureMotion->GetCurrentMovementGeneratorType()) + { + case CHASE_MOTION_TYPE: + case FOLLOW_MOTION_TYPE: + creatureMotion->Clear(false); + break; + default: + break; + } + creatureMotion->MoveChase(m_creature->getVictim(), m_attackDistance, m_creature->GetAngle(m_creature->getVictim())); } else if (creatureMotion->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) { - creatureMotion->MovementExpired(); creatureMotion->Clear(false); m_creature->StopMoving(); creatureMotion->MoveIdle(); @@ -167,7 +184,9 @@ void CreatureAI::SetCombatMovement(bool enable, bool stopOrStartMovement /*=fals void CreatureAI::HandleMovementOnAttackStart(Unit* victim) { MotionMaster* creatureMotion = m_creature->GetMotionMaster(); - if (m_isCombatMovement) + creatureMotion->MovementExpired(false); + + if (IsCombatMovement()) { creatureMotion->MoveChase(victim, m_attackDistance, m_attackAngle); } // TODO - adapt this to only stop OOC-MMGens when MotionMaster rewrite is finished else if (creatureMotion->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE || creatureMotion->GetCurrentMovementGeneratorType() == RANDOM_MOTION_TYPE) diff --git a/src/game/Object/CreatureAI.h b/src/game/Object/CreatureAI.h index 6a298d57..565d901a 100644 --- a/src/game/Object/CreatureAI.h +++ b/src/game/Object/CreatureAI.h @@ -66,6 +66,14 @@ enum CastFlags CAST_AURA_NOT_PRESENT = 0x20, // Only casts the spell if the target does not have an aura from the spell }; +enum CombatMovementFlags +{ + COMBAT_MOVEMENT_SCRIPT = 0x01, // Combat movement enforced by script + COMBAT_MOVEMENT_LOS = 0x02, // Combat movement triggered by LoS issues + COMBAT_MOVEMENT_OOM = 0x04, // Combat movement triggered by power exhaustion + COMBAT_MOVEMENT_DISTANCE = 0x08 // Combat movement triggered by distance checks +}; + enum AIEventType { // Usable with Event AI @@ -103,16 +111,16 @@ class CreatureAI public: explicit CreatureAI(Creature* creature) : m_creature(creature), - m_isCombatMovement(true), + m_combatMovement(0), m_attackDistance(0.0f), m_attackAngle(0.0f) - {} + { AddCombatMovementFlags(COMBAT_MOVEMENT_SCRIPT); } virtual ~CreatureAI(); ///== Information about AI ======================== /** - * This funcion is used to display information about the AI. + * This function is used to display information about the AI. * It is called when the .npc aiinfo command is used. * Use this for on-the-fly debugging * @param reader is a ChatHandler to send messages to. @@ -312,10 +320,12 @@ class CreatureAI */ CanCastResult DoCastSpellIfCan(Unit* pTarget, uint32 uiSpell, uint32 uiCastFlags = 0, ObjectGuid OriginalCasterGuid = ObjectGuid()); - /// Set combat movement (on/off), also sets UNIT_STAT_NO_COMBAT_MOVEMENT + /// Combat movement functions void SetCombatMovement(bool enable, bool stopOrStartMovement = false); - bool IsCombatMovement() const { return m_isCombatMovement; } - + bool IsCombatMovement() const { return m_combatMovement != 0; } + void AddCombatMovementFlags(uint32 cmFlags); + void ClearCombatMovementFlags(uint32 cmFlags); + ///== Event Handling =============================== /* @@ -355,7 +365,7 @@ class CreatureAI Creature* const m_creature; /// Combat movement currently enabled - bool m_isCombatMovement; + uint32 m_combatMovement; /// How should an enemy be chased float m_attackDistance; float m_attackAngle; diff --git a/src/game/Object/CreatureEventAI.cpp b/src/game/Object/CreatureEventAI.cpp index dfac84ec..ae6d511f 100644 --- a/src/game/Object/CreatureEventAI.cpp +++ b/src/game/Object/CreatureEventAI.cpp @@ -63,7 +63,7 @@ int CreatureEventAI::Permissible(const Creature* creature) void CreatureEventAI::GetAIInformation(ChatHandler& reader) { reader.PSendSysMessage(LANG_NPC_EVENTAI_PHASE, (uint32)m_Phase); - reader.PSendSysMessage(LANG_NPC_EVENTAI_MOVE, reader.GetOnOffStr(m_isCombatMovement)); + reader.PSendSysMessage(LANG_NPC_EVENTAI_MOVE, reader.GetOnOffStr(IsCombatMovement())); reader.PSendSysMessage(LANG_NPC_EVENTAI_COMBAT, reader.GetOnOffStr(m_MeleeEnabled)); if (sLog.HasLogFilter(LOG_FILTER_EVENT_AI_DEV)) // Give some more details if in EventAI Dev Mode @@ -627,6 +627,8 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 { uint32 selectFlags = 0; uint32 spellId = 0; + uint32 cmFlags = 0; + if (!(action.cast.castFlags & (CAST_TRIGGERED | CAST_FORCE_CAST | CAST_FORCE_TARGET_SELF))) { spellId = action.cast.spellId; @@ -646,19 +648,40 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 switch (castResult) { case CAST_FAIL_POWER: - case CAST_FAIL_TOO_FAR: - case CAST_FAIL_NO_LOS: - { - // Melee current victim if flag not set - if (!(action.cast.castFlags & CAST_NO_MELEE_IF_OOM)) - { - SetCombatMovement(true,true); - } + cmFlags |= COMBAT_MOVEMENT_OOM; + break; + case CAST_FAIL_TOO_FAR: + cmFlags |= COMBAT_MOVEMENT_DISTANCE; + break; + case CAST_FAIL_NO_LOS: + cmFlags |= COMBAT_MOVEMENT_LOS; break; - } default: break; } + + // Melee current victim if flag not set + if (cmFlags) + { + if (!(action.cast.castFlags & CAST_NO_MELEE_IF_OOM)) + { + AddCombatMovementFlags(cmFlags); + SetCombatMovement(true,true); + } + } + else + { + if (m_combatMovement & (COMBAT_MOVEMENT_LOS | COMBAT_MOVEMENT_DISTANCE | COMBAT_MOVEMENT_OOM)) + { + ClearCombatMovementFlags(COMBAT_MOVEMENT_LOS | COMBAT_MOVEMENT_DISTANCE | COMBAT_MOVEMENT_OOM); + + if (!IsCombatMovement() && m_creature->IsNonMeleeSpellCasted(false) && m_creature->IsInCombat() && m_creature->getVictim()) + { + SetCombatMovement(false,true); + m_creature->SendMeleeAttackStop(m_creature->getVictim()); + } + } + } break; } case ACTION_T_SUMMON: //12 @@ -744,12 +767,21 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 break; case ACTION_T_COMBAT_MOVEMENT: //21 // ignore no affect case - if (m_isCombatMovement == (action.combat_movement.state != 0) || m_creature->IsNonMeleeSpellCasted(false)) - { return; } + if (IsCombatMovement() == (action.combat_movement.state != 0) || m_creature->IsNonMeleeSpellCasted(false)) + { + if (IsCombatMovement()) + { AddCombatMovementFlags(COMBAT_MOVEMENT_SCRIPT); } + return; + } - SetCombatMovement(action.combat_movement.state != 0, true); + if (action.combat_movement.state != 0) + { AddCombatMovementFlags(COMBAT_MOVEMENT_SCRIPT); } + else + { ClearCombatMovementFlags(COMBAT_MOVEMENT_SCRIPT); } + + SetCombatMovement(IsCombatMovement(), true); - if (m_isCombatMovement && action.combat_movement.melee && m_creature->IsInCombat() && m_creature->getVictim()) + if (IsCombatMovement() && action.combat_movement.melee && m_creature->IsInCombat() && m_creature->getVictim()) { m_creature->SendMeleeAttackStart(m_creature->getVictim()); } else if (action.combat_movement.melee && m_creature->IsInCombat() && m_creature->getVictim()) { m_creature->SendMeleeAttackStop(m_creature->getVictim()); } @@ -805,7 +837,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 m_attackDistance = (float)action.ranged_movement.distance; m_attackAngle = action.ranged_movement.angle / 180.0f * M_PI_F; - if (m_isCombatMovement) + if (IsCombatMovement()) { if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) { diff --git a/src/game/WorldHandlers/Spell.cpp b/src/game/WorldHandlers/Spell.cpp index cb36a1f1..b11c924a 100644 --- a/src/game/WorldHandlers/Spell.cpp +++ b/src/game/WorldHandlers/Spell.cpp @@ -2996,6 +2996,16 @@ void Spell::update(uint32 difftime) return; } + if (m_targets.getUnitTarget() && (m_targets.getUnitTarget() != m_caster) && IsSingleTargetSpell(m_spellInfo) && + !IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_IsTriggeredSpell) + { + if (!m_caster->IsWithinLOSInMap(m_targets.getUnitTarget())) + { + cancel(); + return; + } + } + // check if the player or unit caster has moved before the spell finished (exclude casting on vehicles) if (((m_caster->GetTypeId() == TYPEID_PLAYER || m_caster->GetTypeId() == TYPEID_UNIT) && m_timer != 0) && (m_castPositionX != m_caster->GetPositionX() || m_castPositionY != m_caster->GetPositionY() || m_castPositionZ != m_caster->GetPositionZ()) &&