/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "StdAfx.h"
AIInterface::AIInterface()
{
m_waypoints=NULL;
m_canMove = true;
m_destinationX = m_destinationY = m_destinationZ = 0;
m_nextPosX = m_nextPosY = m_nextPosZ = 0;
UnitToFollow = NULL;
FollowDistance = 0.0f;
m_fallowAngle = M_PI/2;
m_timeToMove = 0;
m_timeMoved = 0;
m_moveTimer = 0;
m_WayPointsShowing = false;
m_WayPointsShowBackwards = false;
m_currentWaypoint = 0;
m_moveBackward = false;
m_moveType = 0;
m_moveRun = false;
m_moveSprint = false;
m_moveFly = false;
m_creatureState = STOPPED;
m_canCallForHelp = false;
m_hasCalledForHelp = false;
m_fleeTimer = 0;
m_FleeDuration = 0;
m_canFlee = false;
m_hasFleed = false;
m_canRangedAttack = false;
m_FleeHealth = m_CallForHelpHealth = 0.0f;
m_AIState = STATE_IDLE;
m_updateAssist = false;
m_updateTargets = false;
m_updateAssistTimer = 1;
m_updateTargetsTimer = TARGET_UPDATE_INTERVAL;
m_nextSpell = NULL;
m_nextTarget = NULL;
totemspell = NULL;
m_Unit = NULL;
m_PetOwner = NULL;
m_aiCurrentAgent = AGENT_NULL;
m_moveSpeed = 0.0f;
UnitToFear = NULL;
firstLeaveCombat = true;
m_outOfCombatRange = 2500;
tauntedBy = NULL;
isTaunted = false;
m_AllowedToEnterCombat = true;
m_totalMoveTime = 0;
m_lastFollowX = m_lastFollowY = 0;
m_FearTimer = 0;
m_WanderTimer = 0;
m_totemspelltime = 0;
m_totemspelltimer = 0;
m_formationFollowAngle = 0.0f;
m_formationFollowDistance = 0.0f;
m_formationLinkTarget = 0;
m_formationLinkSqlId = 0;
m_currentHighestThreat = 0;
disable_melee = false;
next_spell_time = 0;
m_hasWaypointEvents = false;
waiting_for_cooldown = false;
m_AIState_backup = m_AIState;
UnitToFollow_backup = NULL;
m_isGuard = false;
m_fastMove = false;
}
void AIInterface::Init(Unit *un, AIType at, MovementType mt)
{
ASSERT(at != AITYPE_PET);
m_AIType = at;
m_MovementType = mt;
m_AIState = STATE_IDLE;
m_MovementState = MOVEMENTSTATE_STOP;
m_Unit = un;
m_moveSpeed = m_Unit->m_runSpeed*0.001f;
/*if(!m_DefaultMeleeSpell)
{
m_DefaultMeleeSpell = new AI_Spell;
m_DefaultMeleeSpell->entryId = 0;
m_DefaultMeleeSpell->spellType = 0;
m_DefaultMeleeSpell->agent = AGENT_MELEE;
m_DefaultSpell = m_DefaultMeleeSpell;
}*/
m_sourceX = un->GetPositionX();
m_sourceY = un->GetPositionY();
m_sourceZ = un->GetPositionZ();
m_guardTimer = getMSTime();
}
AIInterface::~AIInterface()
{
}
void AIInterface::Init(Unit *un, AIType at, MovementType mt, Unit *owner)
{
ASSERT(at == AITYPE_PET || at == AITYPE_TOTEM);
m_AIType = at;
m_MovementType = mt;
m_AIState = STATE_IDLE;
m_MovementState = MOVEMENTSTATE_STOP;
m_Unit = un;
m_PetOwner = owner;
m_moveSpeed = m_Unit->m_runSpeed*0.001f;
m_sourceX = un->GetPositionX();
m_sourceY = un->GetPositionY();
m_sourceZ = un->GetPositionZ();
}
void AIInterface::HandleEvent(uint32 event, Unit* pUnit, uint32 misc1)
{
if(!pUnit || !m_Unit)
return;
if(m_AIState != STATE_EVADE)
{
switch(event)
{
case EVENT_ENTERCOMBAT:
{
/* send the message */
if(m_Unit->GetTypeId() == TYPEID_UNIT)
{
if(((Creature*)m_Unit)->has_combat_text)
objmgr.HandleMonsterSayEvent(((Creature*)m_Unit), MONSTER_SAY_EVENT_ENTER_COMBAT);
CALL_SCRIPT_EVENT(m_Unit, OnCombatStart)(pUnit);
ScriptSystem->OnCreatureEvent(((Creature*)m_Unit), pUnit, CREATURE_EVENT_ON_ENTER_COMBAT);
}
// Stop the emote
m_Unit->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
m_returnX = m_Unit->GetPositionX();
m_returnY = m_Unit->GetPositionY();
m_returnZ = m_Unit->GetPositionZ();
m_moveRun = true; //run to the target
// dismount if mounted
m_Unit->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
if(m_AIState != STATE_ATTACKING)
StopMovement(0);
m_AIState = STATE_ATTACKING;
firstLeaveCombat = true;
if(pUnit && pUnit->GetInstanceID() == m_Unit->GetInstanceID())
{
m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, pUnit->GetGUID());
}
if(m_Unit->GetMapMgr()->GetMapInfo() && m_Unit->GetMapMgr()->GetMapInfo()->type == INSTANCE_RAID)
{
if(m_Unit->GetTypeId() == TYPEID_UNIT)
{
if(static_cast(m_Unit)->GetCreatureName() && static_cast(m_Unit)->GetCreatureName()->Rank == 3)
{
m_Unit->GetMapMgr()->AddCombatInProgress(m_Unit->GetGUID());
}
}
}
}break;
case EVENT_LEAVECOMBAT:
{
// restart emote
if(m_Unit->GetTypeId() == TYPEID_UNIT)
{
if(static_cast(m_Unit)->original_emotestate)
m_Unit->SetUInt32Value(UNIT_NPC_EMOTESTATE, static_cast(m_Unit)->original_emotestate);
ScriptSystem->OnCreatureEvent(((Creature*)m_Unit), pUnit, CREATURE_EVENT_ON_LEAVE_COMBAT);
}
//reset ProcCount
//ResetProcCounts();
if(m_AIType == AITYPE_PET)
{
m_AIState = STATE_FOLLOWING;
UnitToFollow = m_PetOwner;
FollowDistance = 3.0f;
m_lastFollowX = m_lastFollowY = 0;
if(m_Unit->GetGUIDHigh() == HIGHGUID_PET)
((Pet*)m_Unit)->SetPetAction(PET_ACTION_FOLLOW);
HandleEvent(EVENT_FOLLOWOWNER, 0, 0);
}
else
{
CALL_SCRIPT_EVENT(m_Unit, OnCombatStop)(UnitToFollow);
m_AIState = STATE_EVADE;
m_Unit->setAttackTarget(NULL);
UnitToFollow = NULL;
FollowDistance = 0.0f;
m_lastFollowX = m_lastFollowY = 0;
}
m_aiTargets.clear();
m_fleeTimer = 0;
m_hasFleed = false;
m_hasCalledForHelp = false;
m_nextSpell = NULL;
SetNextTarget(NULL);
firstLeaveCombat = false;
// Scan for a new target before moving back on waypoint path
Unit * Target = FindTarget();
if(Target != NULL)
AttackReaction(Target, 1, 0);
else
{
firstLeaveCombat = true;
if(m_isGuard)
{
m_Unit->m_runSpeed *= 2.0f;
m_fastMove = true;
}
}
/*SpellEntry* spell = getSpellEntry(2054);
Affect* aff = new Affect(spell, 6000, m_Unit->GetGUID());
aff->SetHealPerTick(uint16(m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH)/4), 2000);
m_Unit->AddAffect(aff);*/
if(m_Unit->GetMapMgr()->GetMapInfo() && m_Unit->GetMapMgr()->GetMapInfo()->type == INSTANCE_RAID)
{
if(m_Unit->GetTypeId() == TYPEID_UNIT)
{
if(static_cast(m_Unit)->GetCreatureName() && static_cast(m_Unit)->GetCreatureName()->Rank == 3)
{
m_Unit->GetMapMgr()->RemoveCombatInProgress(m_Unit->GetGUID());
}
}
}
// Remount if mounted
if(m_Unit->GetTypeId() == TYPEID_UNIT)
{
if(((Creature*)m_Unit)->proto)
m_Unit->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, ((Creature*)m_Unit)->proto->MountedDisplayID);
}
}break;
case EVENT_DAMAGETAKEN:
{
CALL_SCRIPT_EVENT(m_Unit, OnDamageTaken)(pUnit, misc1);
if(!modThreatByPtr(pUnit, misc1))
{
m_aiTargets.insert(TargetMap::value_type(pUnit, misc1));
}
}break;
case EVENT_FOLLOWOWNER:
{
m_AIState = STATE_FOLLOWING;
if(m_Unit->GetGUIDHigh() == HIGHGUID_PET)
((Pet*)m_Unit)->SetPetAction(PET_ACTION_FOLLOW);
UnitToFollow = m_PetOwner;
m_lastFollowX = m_lastFollowY = 0;
FollowDistance = 4.0f;
m_aiTargets.clear();
m_fleeTimer = 0;
m_hasFleed = false;
m_hasCalledForHelp = false;
m_nextSpell = NULL;
SetNextTarget(NULL);
}break;
case EVENT_FEAR:
{
CALL_SCRIPT_EVENT(m_Unit, OnFear)(pUnit, 0);
m_AIState_backup = m_AIState;
m_AIState = STATE_FEAR;
StopMovement(1);
UnitToFollow_backup = UnitToFollow;
UnitToFollow = NULL;
m_lastFollowX = m_lastFollowY = 0;
FollowDistance_backup = FollowDistance;
FollowDistance = 0.0f;
m_aiTargets.clear();
m_fleeTimer = 0;
m_hasFleed = false;
m_hasCalledForHelp = false;
m_moveRun = true;
// update speed
m_Unit->m_runSpeed /= 2;
getMoveFlags();
m_nextSpell = NULL;
SetNextTarget(NULL);
}break;
case EVENT_UNFEAR:
{
// update speed
m_Unit->m_runSpeed *= 2;
getMoveFlags();
// m_AIState = STATE_IDLE;
//maybe we were not idle before fear. Like a guardian could have been doing something
m_AIState = m_AIState_backup;
UnitToFollow = UnitToFollow_backup;
FollowDistance = FollowDistance_backup;
}break;
case EVENT_WANDER:
{
//CALL_SCRIPT_EVENT(m_Unit, OnWander)(pUnit, 0); FIXME
m_AIState_backup = m_AIState;
m_AIState = STATE_WANDER;
StopMovement(1);
UnitToFollow_backup = UnitToFollow;
UnitToFollow = NULL;
m_lastFollowX = m_lastFollowY = 0;
FollowDistance_backup = FollowDistance;
FollowDistance = 0.0f;
//comented by Zack : why do we need to forget our old targets ?. Maybe this was put here with a reason
// m_aiTargets.clear();
m_fleeTimer = 0;
m_hasFleed = false;
m_hasCalledForHelp = false;
m_moveRun = true;
// update speed
m_Unit->m_runSpeed /= 2;
getMoveFlags();
m_nextSpell = NULL;
SetNextTarget(NULL);
//m_Unit->setAttackTarget(NULL);
}break;
case EVENT_UNWANDER:
{
// update speed
m_Unit->m_runSpeed *= 2;
getMoveFlags();
UnitToFollow = UnitToFollow_backup;
FollowDistance = FollowDistance_backup;
m_AIState = m_AIState_backup;
}break;
default:
{
}break;
}
}
if(event != EVENT_UNITDIED)
m_Unit->setAttackTarget(m_nextTarget);
//Should be able to do this stuff even when evading
switch(event)
{
case EVENT_UNITDIED:
{
CALL_SCRIPT_EVENT(m_Unit, OnDied)(pUnit);
ScriptSystem->OnCreatureEvent(((Creature*)m_Unit), pUnit, CREATURE_EVENT_ON_DIED);
m_AIState = STATE_IDLE;
StopMovement(0);
m_aiTargets.clear();
UnitToFollow = NULL;
m_lastFollowX = m_lastFollowY = 0;
UnitToFear = NULL;
FollowDistance = 0.0f;
m_fleeTimer = 0;
m_hasFleed = false;
m_hasCalledForHelp = false;
m_nextSpell = NULL;
SetNextTarget(NULL);
//reset ProcCount
//ResetProcCounts();
//reset waypoint to 0
m_currentWaypoint = 0;
// There isn't any need to do any attacker checks here, as
// they should all be taken care of in DealDamage
//removed by Zack : why do we need to go to our master if we just died ? On next spawn we will be spawned near him after all
/* if(m_AIType == AITYPE_PET)
{
SetUnitToFollow(m_PetOwner);
SetFollowDistance(3.0f);
HandleEvent(EVENT_FOLLOWOWNER, m_Unit, 0);
}*/
if(m_Unit->GetMapMgr() && m_Unit->GetMapMgr()->GetMapInfo() && m_Unit->GetMapMgr()->GetMapInfo()->type == INSTANCE_RAID || m_Unit->GetMapMgr()->GetMapInfo() && m_Unit->GetMapMgr()->GetMapInfo()->type == INSTANCE_MULTIMODE)
{
if(m_Unit->GetTypeId() == TYPEID_UNIT && !m_Unit->IsPet())
{
if(static_cast(m_Unit)->GetCreatureName() && static_cast(m_Unit)->GetCreatureName()->Rank == 3)
{
m_Unit->GetMapMgr()->RemoveCombatInProgress(m_Unit->GetGUID());
sInstanceSavingManager.SaveObjectStateToInstance(m_Unit);
m_Unit->GetMapMgr()->SavePlayersToInstance();
}
else if(static_cast(m_Unit)->proto && static_cast(m_Unit)->proto->boss && m_Unit->GetMapMgr()->iInstanceMode == MODE_HEROIC)
{
sInstanceSavingManager.SaveObjectStateToInstance(m_Unit);
m_Unit->GetMapMgr()->SavePlayersToInstance();
}
}
}
}break;
}
}
void AIInterface::Update(uint32 p_time)
{
if(m_AIType == AITYPE_TOTEM)
{
assert(totemspell != 0);
if(p_time >= m_totemspelltimer)
{
Spell * pSpell = new Spell(m_Unit, totemspell, true, 0);
SpellCastTargets targets(0);
if(!m_nextTarget ||
(m_nextTarget &&
(!m_Unit->GetMapMgr()->GetUnit(m_nextTarget->GetGUID()) ||
!m_nextTarget->isAlive() ||
!IsInrange(m_Unit,m_nextTarget,pSpell->GetRadius(0)) ||
!isAttackable(m_Unit, m_nextTarget))))
{
//something happend to our target, pick another one
pSpell->GenerateTargets(&targets);
if(targets.m_targetMask & TARGET_FLAG_UNIT)
m_nextTarget = m_Unit->GetMapMgr()->GetUnit(targets.m_unitTarget);
else m_nextTarget=NULL;//sorry but could not find a target
}
if(m_nextTarget)
{
SpellCastTargets targets(m_nextTarget->GetGUID());
pSpell->prepare(&targets);
// need proper cooldown time!
m_totemspelltimer = m_totemspelltime;
}
else delete pSpell;
// these will *almost always* be AoE, so no need to find a target here.
// SpellCastTargets targets(m_Unit->GetGUID());
// Spell * pSpell = new Spell(m_Unit, totemspell, true, 0);
// pSpell->prepare(&targets);
// need proper cooldown time!
// m_totemspelltimer = m_totemspelltime;
}
else
{
m_totemspelltimer -= p_time;
}
return;
}
_UpdateTimer(p_time);
_UpdateTargets();
if(m_Unit->isAlive() && m_AIState != STATE_IDLE
&& m_AIState != STATE_FOLLOWING && m_AIState != STATE_FEAR
&& m_AIState != STATE_WANDER && m_AIState != STATE_SCRIPTMOVE)
{
if(m_AIType == AITYPE_PET )
{
if(!m_Unit->bInvincible && m_Unit->GetGUIDHigh() == HIGHGUID_PET)
{
Pet * pPet = static_cast(m_Unit);
if(pPet->GetPetAction() == PET_ACTION_ATTACK || pPet->GetPetState() != PET_STATE_PASSIVE)
{
_UpdateCombat(p_time);
}
}
//we just use any creature as a pet guardian
else if(m_Unit->GetGUIDHigh() != HIGHGUID_PET)
{
_UpdateCombat(p_time);
}
}
else
{
_UpdateCombat(p_time);
}
}
_UpdateMovement(p_time);
if(m_AIState == STATE_EVADE)
{
if(m_creatureState != MOVING)
{
if((m_AIType == AITYPE_PET) && m_PetOwner != NULL)
{
m_returnX = m_PetOwner->GetPositionX()+(3*(cosf(m_fallowAngle+m_PetOwner->GetOrientation())));
m_returnY = m_PetOwner->GetPositionY()+(3*(sinf(m_fallowAngle+m_PetOwner->GetOrientation())));
m_returnZ = m_PetOwner->GetPositionZ();
}
if(m_returnX != 0.0f && m_returnY != 0.0f && m_returnZ != 0.0f)
{
//return to last position before attacking
MoveTo(m_returnX,m_returnY,m_returnZ,m_Unit->GetOrientation());
Creature *aiowner = static_cast(m_Unit);
if(aiowner)
{
//clear tagger.
aiowner->Tagged = false;
aiowner->TaggerGuid = 0;
aiowner->SetUInt32Value(UNIT_DYNAMIC_FLAGS,aiowner->GetUInt32Value(UNIT_DYNAMIC_FLAGS) & ~(U_DYN_FLAG_TAGGED_BY_OTHER |U_DYN_FLAG_LOOTABLE));
}
}
}
//else
//{
if(m_returnX !=0.0f && m_returnY != 0.0f)
{
if(m_Unit->GetDistanceSq(m_returnX,m_returnY,m_returnZ) < 4.0f/*2.0*/)
{
m_AIState = STATE_IDLE;
m_returnX = m_returnY = m_returnZ = 0.0f;
if(hasWaypoints())
{
if(m_moveBackward)
{
if(m_currentWaypoint != GetWayPointsCount()-1)
m_currentWaypoint++;
}
else
{
if(m_currentWaypoint != 0)
m_currentWaypoint--;
}
}
// Set health to full if they at there last location before attacking
if(m_AIType != AITYPE_PET)
//if(m_Unit->GetMapId() > 1 && m_Unit->GetMapId() != 530)
m_Unit->SetUInt32Value(UNIT_FIELD_HEALTH,m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH));
}
}
//}
}
if(m_fleeTimer)
{
if(m_fleeTimer > p_time)
{
m_fleeTimer -= p_time;
_CalcDestinationAndMove(m_nextTarget, 5.0f);
}
else
{
m_fleeTimer = 0;
SetNextTarget(FindTargetForSpell(m_nextSpell));
}
}
//Pet Dismiss after a certian ditance away
/*if(m_AIType == AITYPE_PET && m_PetOwner != NULL)
{
float dist = (m_Unit->GetInstanceID() == m_PetOwner->GetInstanceID()) ?
m_Unit->GetDistanceSq(m_PetOwner) : 99999.0f;
if(dist > 8100.0f) //90 yard away we Dismissed
{
DismissPet();
return;
}
}*/
}
void AIInterface::_UpdateTimer(uint32 p_time)
{
if(m_updateAssistTimer > p_time)
{
m_updateAssistTimer -= p_time;
}else
{
m_updateAssist = true;
m_updateAssistTimer = TARGET_UPDATE_INTERVAL * 2 - m_updateAssistTimer - p_time;
}
if(m_updateTargetsTimer > p_time)
{
m_updateTargetsTimer -= p_time;
}else
{
m_updateTargets = true;
m_updateTargetsTimer = TARGET_UPDATE_INTERVAL * 2 - m_updateTargetsTimer - p_time;
}
}
void AIInterface::_UpdateTargets()
{
if(m_Unit->IsPlayer() || (m_AIType != AITYPE_PET && disable_melee))
return;
if(((Creature*)m_Unit)->GetCreatureName() && ((Creature*)m_Unit)->GetCreatureName()->Type == CRITTER)
return;
AssistTargetSet::iterator i, i2;
TargetMap::iterator itr, itr2;
// Find new Assist Targets and remove old ones
if(m_AIState == STATE_FLEEING)
{
FindFriends(100.0f/*10.0*/);
}
else if(m_AIState != STATE_IDLE && m_AIState != STATE_SCRIPTIDLE)
{
FindFriends(16.0f/*4.0f*/);
}
if(m_updateAssist)
{
m_updateAssist = false;
//modified for vs2005 compatibility
for(i = m_assistTargets.begin(); i != m_assistTargets.end();)
{
i2 = i;
++i;
if(m_Unit->GetDistanceSq((*i2)) > 2500.0f/*50.0f*/ || !(*i2)->isAlive() || !(*i2)->isInCombat())
{
m_assistTargets.erase(i2);
}
}
}
if(m_updateTargets)
{
m_updateTargets = false;
//modified for vs2005 compatibility
for(itr = m_aiTargets.begin(); itr != m_aiTargets.end();)
{
itr2 = itr;
++itr;
//if(!itr->target) // we shouldnt get to here, i'm guessing.
//continue;
if(!itr2->first->isAlive() || m_Unit->GetDistanceSq(itr2->first) >= 6400.0f/*80.0f*/)
{
m_aiTargets.erase(itr2);
}
}
if(m_aiTargets.size() == 0
&& m_AIState != STATE_IDLE && m_AIState != STATE_FOLLOWING
&& m_AIState != STATE_EVADE && m_AIState != STATE_FEAR
&& m_AIState != STATE_WANDER && m_AIState != STATE_SCRIPTIDLE)
{
if(firstLeaveCombat)
{
Unit* target = FindTarget();
if(target)
{
AttackReaction(target, 1, 0);
}else
{
firstLeaveCombat = false;
}
}
/*else
{
HandleEvent(EVENT_LEAVECOMBAT, m_Unit, 0);
}*/
}
else if(m_aiTargets.size() == 0 && (m_AIType == AITYPE_PET && (m_Unit->GetGUIDHigh() == HIGHGUID_PET && static_cast(m_Unit)->GetPetState() == PET_STATE_AGGRESSIVE) || (m_Unit->GetGUIDHigh() != HIGHGUID_PET && disable_melee==false)))
{
Unit* target = FindTarget();
if(target)
{
AttackReaction(target, 1, 0);
}
}
}
// Find new Targets when we are ooc
if((m_AIState == STATE_IDLE || m_AIState == STATE_SCRIPTIDLE) && m_assistTargets.size() == 0)
{
Unit* target = FindTarget();
if(target)
{
AttackReaction(target, 1, 0);
}
}
}
///====================================================================
/// Desc: Updates Combat Status of m_Unit
///====================================================================
void AIInterface::_UpdateCombat(uint32 p_time)
{
if(m_AIType != AITYPE_PET && disable_melee)
return;
//just make sure we are not hitting self. This was reported as an exploit.Should never ocure anyway
if(m_nextTarget==m_Unit)
m_nextTarget=GetMostHated();
uint16 agent = m_aiCurrentAgent;
// If creature is very far from spawn point return to spawnpoint
// If at instance dont return -- this is wrong ... instance creatures always returns to spawnpoint, dunno how do you got this ideia.
if( m_AIType != AITYPE_PET
&& (m_outOfCombatRange && m_Unit->GetDistanceSq(m_returnX,m_returnY,m_returnZ) > m_outOfCombatRange)
&& m_AIState != STATE_EVADE
&& m_AIState != STATE_SCRIPTMOVE)
{
HandleEvent(EVENT_LEAVECOMBAT, m_Unit, 0);
}
else if(!m_nextTarget && m_AIState != STATE_FOLLOWING && m_AIState != STATE_SCRIPTMOVE)
{
// SetNextTarget(FindTargetForSpell(m_nextSpell));
m_nextTarget=GetMostHated();
if(!m_nextTarget)
{
HandleEvent(EVENT_LEAVECOMBAT, m_Unit, 0);
}
}
bool cansee;
if(m_nextTarget && m_nextTarget->GetInstanceID() == m_Unit->GetInstanceID())
{
if(m_Unit->GetTypeId() == TYPEID_UNIT)
cansee=((Creature*)m_Unit)->CanSee(m_nextTarget);
else
cansee = ((Player*)m_Unit)->CanSee(m_nextTarget);
}
else
cansee=false;
if(cansee && m_nextTarget && m_nextTarget->isAlive() && m_AIState != STATE_EVADE && !m_Unit->isCasting())
{
if(agent == AGENT_NULL || (m_AIType == AITYPE_PET && !m_nextSpell)) // allow pets autocast
{
if(!m_nextSpell)
m_nextSpell = this->getSpell();
if(!m_nextSpell && waiting_for_cooldown)
{
/* don't start running to the target for melee if we're waiting for a cooldown. */
return;
}
if(m_canFlee && !m_hasFleed
&& ((m_Unit->GetUInt32Value(UNIT_FIELD_HEALTH) / m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH)) < m_FleeHealth ))
agent = AGENT_FLEE;
else if(m_canCallForHelp
&& !m_hasCalledForHelp
&& (m_CallForHelpHealth > (m_Unit->GetUInt32Value(UNIT_FIELD_HEALTH) / (m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH) > 0 ? m_Unit->GetUInt32Value(UNIT_FIELD_MAXHEALTH) : 1))))
agent = AGENT_CALLFORHELP;
else if(m_nextSpell)
{
if(m_nextSpell->agent != AGENT_NULL)
{
agent = m_nextSpell->agent;
}
else
{
agent = AGENT_MELEE;
}
}
else
{
agent = AGENT_MELEE;
}
}
if(agent == AGENT_RANGED || agent == AGENT_MELEE)
{
if(m_canRangedAttack)
{
agent = AGENT_MELEE;
if(m_nextTarget->GetTypeId() == TYPEID_PLAYER)
{
float dist = m_Unit->GetDistanceSq(m_nextTarget);
if(((Player*)m_nextTarget)->m_currentMovement == MOVE_ROOT || dist >= 64.0f)
{
agent = AGENT_RANGED;
}
}
else if(m_nextTarget->m_canMove == false || m_Unit->GetDistanceSq(m_nextTarget) >= 64.0f)
{
agent = AGENT_RANGED;
}
}
else
{
agent = AGENT_MELEE;
}
}
if(this->disable_melee && agent == AGENT_MELEE)
agent = AGENT_NULL;
switch(agent)
{
case AGENT_MELEE:
{
float combatReach[2]; // Calculate Combat Reach
float distance = m_Unit->CalcDistance(m_nextTarget);
combatReach[0] = 0.0f;
combatReach[1] = _CalcCombatRange(m_nextTarget, false);
if(
// distance >= combatReach[0] &&
distance <= combatReach[1]) // Target is in Range -> Attack
{
#ifdef ENABLE_CREATURE_DAZE
//now if the target is facing his back to us then we could just cast dazed on him :P
//as far as i know dazed is casted by most of the creatures but feel free to remove this code if you think otherwise
if(!(m_Unit->m_factionDBC->RepListId == -1 && m_Unit->m_faction->FriendlyMask==0 && m_Unit->m_faction->HostileMask==0) /* neutral creature */
&& m_nextTarget->IsPlayer() && !m_Unit->IsPet() && Rand(CREATURE_CHANCE_TO_DAZE))
{
float our_facing=m_Unit->calcRadAngle(m_Unit->GetPositionX(),m_Unit->GetPositionY(),m_nextTarget->GetPositionX(),m_nextTarget->GetPositionY());
float his_facing=m_nextTarget->GetOrientation();
if(fabs(our_facing-his_facing)HasNegativeAura(CREATURE_SPELL_TO_DAZE))
{
SpellEntry *info = sSpellStore.LookupEntry(CREATURE_SPELL_TO_DAZE);
Spell *sp = new Spell(m_Unit, info, false, NULL);
SpellCastTargets targets;
targets.m_unitTarget = m_nextTarget->GetGUID();
sp->prepare(&targets);
}
}
#endif
if(UnitToFollow != NULL)
{
UnitToFollow = NULL; //we shouldn't be following any one
m_lastFollowX = m_lastFollowY = 0;
m_Unit->setAttackTarget(NULL); // remove ourselves from any target that might have been followed
}
FollowDistance = 0.0f;
m_moveRun = false;
//FIXME: offhand shit
if(m_Unit->isAttackReady(false) && !m_fleeTimer)
{
m_creatureState = ATTACKING;
bool infront = m_Unit->isInFront(m_nextTarget);
if(!infront) // set InFront
{
//prevent mob from rotating while stunned
if(!m_Unit->IsStunned ())
{
setInFront(m_nextTarget);
infront = true;
}
}
if(infront)
{
m_Unit->setAttackTimer(0, false);
m_Unit->Strike(m_nextTarget, (agent==AGENT_MELEE)?0:2,NULL,0,0,0, false);
}
}
}
else // Target out of Range -> Run to it
{
//calculate next move
float dist = combatReach[1]-PLAYER_SIZE;
if(dist < PLAYER_SIZE)
dist = PLAYER_SIZE; //unbelievable how this could happen
m_moveRun = true;
_CalcDestinationAndMove(m_nextTarget, dist);
}
}break;
case AGENT_RANGED:
{
float combatReach[2]; // Calculate Combat Reach
float distance = m_Unit->CalcDistance(m_nextTarget);
combatReach[0] = 8.0f;
combatReach[1] = 30.0f;
if(distance >= combatReach[0] && distance <= combatReach[1]) // Target is in Range -> Attack
{
if(UnitToFollow != NULL)
{
UnitToFollow = NULL; //we shouldn't be following any one
m_lastFollowX = m_lastFollowY = 0;
m_Unit->setAttackTarget(NULL); // remove ourselves from any target that might have been followed
}
FollowDistance = 0.0f;
m_moveRun = false;
//FIXME: offhand shit
if(m_Unit->isAttackReady(false) && !m_fleeTimer)
{
m_creatureState = ATTACKING;
bool infront = m_Unit->isInFront(m_nextTarget);
if(!infront) // set InFront
{
//prevent mob from rotating while stunned
if(!m_Unit->IsStunned ())
{
setInFront(m_nextTarget);
infront = true;
}
}
if(infront)
{
m_Unit->setAttackTimer(0, false);
SpellEntry *info = sSpellStore.LookupEntry(SPELL_RANGED_GENERAL);
if(info)
{
Spell *sp = new Spell(m_Unit, info, false, NULL);
SpellCastTargets targets;
targets.m_unitTarget = m_nextTarget->GetGUID();
sp->prepare(&targets);
//Lets make spell handle this
//m_Unit->Strike(m_nextTarget, (agent==AGENT_MELEE)?0:2,NULL,0,0,0);
}
}
}
}
else // Target out of Range -> Run to it
{
//calculate next move
float dist = 1.0f;
if(distance < combatReach[0])// Target is too near
dist = 9.0f;
else
dist = 20.0f;
m_moveRun = true;
_CalcDestinationAndMove(m_nextTarget, dist);
}
}break;
case AGENT_SPELL:
{
if(!m_nextSpell || !m_nextTarget)
return; // this shouldnt happen
/* stop moving so we don't interrupt the spell */
//this the way justly suggested
// if(m_nextSpell->spell->CastingTimeIndex != 1)
//do not stop for instant spells
SpellCastTime *sd = sCastTime.LookupEntry(m_nextSpell->spell->CastingTimeIndex);
if(GetCastTime(sd) != 0)
StopMovement(0);
float distance = m_Unit->GetDistanceSq(m_nextTarget);
if((distance <= powf(m_nextSpell->maxrange,2) && distance >= powf(m_nextSpell->minrange,2)) || m_nextSpell->maxrange == 0) // Target is in Range -> Attack
{
SpellEntry* spellInfo = m_nextSpell->spell;
/* if(m_nextSpell->procCount)
m_nextSpell->procCount--;*/
SpellCastTargets targets = setSpellTargets(spellInfo, m_nextTarget);
CastSpell(m_Unit, spellInfo, targets);
AddSpellCooldown(spellInfo, m_nextSpell);
//add pet spell after use to pet owner with some chance
if(m_Unit->GetGUIDHigh() == HIGHGUID_PET && m_PetOwner->IsPlayer())
{
Pet * pPet = static_cast(m_Unit);
if(pPet && Rand(10))
pPet->AddPetSpellToOwner(spellInfo->Id);
}
m_nextSpell = NULL;
}
else // Target out of Range -> Run to it
{
//calculate next move
m_moveRun = true;
if(m_nextSpell->maxrange < 5.0f)
_CalcDestinationAndMove(m_nextTarget, 0.0f);
else
_CalcDestinationAndMove(m_nextTarget, m_nextSpell->maxrange - 5.0f);
/*Destination* dst = _CalcDestination(m_nextTarget, dist);
MoveTo(dst->x, dst->y, dst->z,0);
delete dst;*/
}
}break;
case AGENT_FLEE:
{
//float dist = 5.0f;
m_moveRun = false;
if(m_fleeTimer == 0)
m_fleeTimer = m_FleeDuration;
/*Destination* dst = _CalcDestination(m_nextTarget, dist);
MoveTo(dst->x, dst->y, dst->z,0);
delete dst;*/
_CalcDestinationAndMove(m_nextTarget, 5.0f);
if(!m_hasFleed) // to avoid lua excuting spam
CALL_SCRIPT_EVENT(m_Unit, OnFlee)(m_nextTarget);
m_AIState = STATE_FLEEING;
//removed by Zack : somehow creature starts to attack sefl. Just making sure it is not this one
// m_nextTarget = m_Unit;
// m_Unit->SetUInt64Value(UNIT_FIELD_TARGET, 0);
SetNextTarget(NULL);
m_Unit->setAttackTarget(0);
m_Unit->clearAttackers(true);
WorldPacket data(SMSG_MESSAGECHAT, 100);
string msg = "%s attempts to run away in fear!";
data << (uint8)CHAT_MSG_MONSTER_EMOTE;
data << (uint32)LANG_UNIVERSAL;
data << (uint32)(strlen(((Creature*)m_Unit)->GetCreatureName()->Name) + 1);
data << ((Creature*)m_Unit)->GetCreatureName()->Name;
data << (uint64)0;
data << (uint32)(msg.size() + 1);
data << msg;
data << uint8(0);
m_Unit->SendMessageToSet(&data, false);
//m_Unit->SendChatMessage(CHAT_MSG_MONSTER_EMOTE, LANG_UNIVERSAL, msg);
//sChatHandler.FillMessageData(&data, CHAT_MSG_MONSTER_EMOTE, LANG_UNIVERSAL, msg, m_Unit->GetGUID());
m_hasFleed = true;
}break;
case AGENT_CALLFORHELP:
{
FindFriends(400.0f/*20.0f*/);
m_hasCalledForHelp = true; // We only want to call for Help once in a Fight.
if(m_Unit->GetTypeId() == TYPEID_UNIT)
objmgr.HandleMonsterSayEvent(((Creature*)m_Unit), MONSTER_SAY_EVENT_CALL_HELP);
CALL_SCRIPT_EVENT(m_Unit, OnCallForHelp)();
}break;
}
}
else if(!m_nextTarget || m_nextTarget->GetInstanceID() != m_Unit->GetInstanceID() || !m_nextTarget->isAlive() || !cansee)
{
SetNextTarget(NULL);
// no more target
//m_Unit->setAttackTarget(NULL);
}
}
void AIInterface::DismissPet()
{
/*
if(m_AIType != AITYPE_PET)
return;
if(!m_PetOwner)
return;
if(m_PetOwner->GetTypeId() != TYPEID_PLAYER)
return;
if(m_Unit->GetUInt32Value(UNIT_CREATED_BY_SPELL) == 0)
((Player*)m_PetOwner)->SetFreePetNo(false, (int)m_Unit->GetUInt32Value(UNIT_FIELD_PETNUMBER));
((Player*)m_PetOwner)->SetPet(NULL);
((Player*)m_PetOwner)->SetPetName("");
//FIXME:Check hunter pet or not
//FIXME:Check enslaved creature
m_PetOwner->SetUInt64Value(UNIT_FIELD_SUMMON, 0);
WorldPacket data;
data.Initialize(SMSG_PET_SPELLS);
data << (uint64)0;
((Player*)m_PetOwner)->GetSession()->SendPacket(&data);
sEventMgr.RemoveEvents(((Creature*)m_Unit));
if(m_Unit->IsInWorld())
{
m_Unit->RemoveFromWorld();
}
//setup an event to delete the Creature
sEventMgr.AddEvent(((Creature*)this->m_Unit), &Creature::DeleteMe, EVENT_DELETE_TIMER, 1, 1);*/
}
void AIInterface::AttackReaction(Unit* pUnit, uint32 damage_dealt, uint32 spellId)
{
if(m_AIState == STATE_EVADE || m_fleeTimer != 0 || !pUnit
|| m_Unit->IsPacified() || m_Unit->IsStunned() || !m_Unit->isAlive())
{
return;
}
if(m_Unit == pUnit)
{
return;
}
if(m_AIState == STATE_IDLE || m_AIState == STATE_FOLLOWING)
{
WipeTargetList();
HandleEvent(EVENT_ENTERCOMBAT, pUnit, 0);
}
HandleEvent(EVENT_DAMAGETAKEN, pUnit, _CalcThreat(damage_dealt, spellId, pUnit));
}
bool AIInterface::HealReaction(Unit* caster, Unit* victim, uint32 amount)
{
if(!caster || !victim)
{
printf("!!!BAD POINTER IN AIInterface::HealReaction!!!\n");
return false;
}
int casterInList = 0, victimInList = 0;
if(m_aiTargets.find(caster) != m_aiTargets.end())
casterInList = 1;
if(m_aiTargets.find(victim) != m_aiTargets.end())
victimInList = 1;
/*for(i = m_aiTargets.begin(); i != m_aiTargets.end(); i++)
{
if(casterInList && victimInList)
{ // no need to check the rest, just break that
break;
}
if(i->target == victim)
{
victimInList = true;
}
if(i->target == caster)
{
casterInList = true;
}
}*/
if(!victimInList && !casterInList) // none of the Casters is in the Creatures Threat list
{
return false;
}
if(!casterInList && victimInList) // caster is not yet in Combat but victim is
{
// get caster into combat if he's hostile
if(isHostile(m_Unit, caster))
{
//AI_Target trgt;
//trgt.target = caster;
//trgt.threat = amount;
//m_aiTargets.push_back(trgt);
m_aiTargets.insert(TargetMap::value_type(caster, amount));
return true;
}
return false;
}
else if(casterInList && victimInList) // both are in combat already
{
// mod threat for caster
modThreatByPtr(caster, amount);
return true;
}
else // caster is in Combat already but victim is not
{
modThreatByPtr(caster, amount);
// both are players so they might be in the same group
if(caster->GetTypeId() == TYPEID_PLAYER && victim->GetTypeId() == TYPEID_PLAYER)
{
if(((Player*)caster)->GetGroup() == ((Player*)victim)->GetGroup())
{
// get victim into combat since they are both
// in the same party
if(isHostile(m_Unit, victim))
{
m_aiTargets.insert(TargetMap::value_type(victim, 1));
return true;
}
return false;
}
}
}
return false;
}
void AIInterface::OnDeath(Object* pKiller)
{
if(pKiller->GetTypeId() == TYPEID_PLAYER || pKiller->GetTypeId() == TYPEID_UNIT)
HandleEvent(EVENT_UNITDIED, static_cast(pKiller), 0);
else
HandleEvent(EVENT_UNITDIED, m_Unit, 0);
}
Unit* AIInterface::FindTarget()
{// find nearest hostile Target to attack
if( !m_AllowedToEnterCombat )
return NULL;
Unit* target = NULL;
Unit* critterTarget = NULL;
float distance = 999999.0f; // that should do it.. :p
float crange;
float z_diff;
std::set